I am building a grpc server in Go. Right now the server provides three methods:
- SubmitJob
- CancelJob
- GetJobStatus
I am using Datadog to log few metrics e.g. request count, duration etc. The SubmitJob method looks like:
func (s *myServer) submitJob(ctx context.Context, request *submitJobRequest) (*submitJobResponse, error) {
s.dd_client.LogRequestCount("SubmitJob")
start_time := time.Now()
defer s.dd_client.LogRequestDuration("SubmitJob", time.Since(start_time))
resp := someFunc()
return resp, nil
}
The logging code is pretty much the same among those three different server methods. Thus, I wonder how to avoid such duplication.
I noticed that golang grpc has the concept ofinterceptor. Basically I can define a interceptor function, and use that to do pre/post processing of server method call.
Follow the documentation, I wrote an interceptor like:
func unaryInterceptor(ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler,
) (interface{}, error) {
dd_client := NewDatadogClient()
defer dd_client.Close()
dd_client.LogRequestCount(info.FullMethod)
start_time := time.Now()
resp, err := handler(ctx, req)
dd_client.LogRequestDuration(info.FullMethod, time.Since(start_time))
return resp, err
}
The problem is that, everytime when the interceptor is called, it will create and destory a new Datadog client. I feel this is unnecessary. But since unaryInterceptor is just a method instead of a class, I do not see a way to create the Datadog client once and reuse it later? Is there a way to fulfill what I need?
CodePudding user response:
Yes, instead of creating it on spot, move that to a different file / package, lets call it DataDog.go
On it you need to declar it as a singleton so it's only 1 pointer across all usages something like....
(im using the type 'DataDogType' to refer the data type return by the function 'NewDatadogClient', pls specify the package and version to give a more precise sample code)
//your package code....
var dataDog DataDogType
//access it through a wrapper, the wrapper will init the var the first time,
//the remaining times it will be reused
func GetDataDog()(*DataDogType) {
if dataDog == nil {
dataDog = NewDatadogClient()
}
return dataDog
}
Now depending of your use case, if you return it as a pointer and close it you will close it for all usages, so return the pointer it self and omit the part of closing it, or return the value so its a copy of the original
btw could you share the dataDog version and specific package that you are using?