rpc-codec
rpc-codec copied to clipboard
measure runtime by method
Hi all,
i would like to measure the runtimes each call is taking and group it by the jsonrpc method using the following code:
func handleRequest(ctx context.Context, rc io.ReadCloser) bytes.Buffer {
var res bytes.Buffer
codec := jsonrpc2.NewServerCodecContext(ctx, struct {
io.ReadCloser
io.Writer
}{
rc,
&res,
}, nil)
err := rpc.ServeRequest(codec)
if err != nil {
log.WithError(err).Error("rpc.ServeRequest")
}
fmt.Printf("%+v\n", codec)
return res
}
the data i am looking for is already in the "codec" variable after rpc.ServeRequest:
&{encmutex:{state:0 sema:0} dec:0xc00157fce0 enc:0xc004b47540 c:{ReadCloser:{Reader:0xc00137b8c0} Writer:0xc00137b8f0} srv:0xc0000ec140 ctx:0xc00137b7d0 req:{Version:2.0 Method:System.Hello Params:0xc0053867e0 ID:
but i am unable to access it due to "req" not being exported.
Is there really no other way but to decode the json a second time just to get the method name?
Many thanks in advance!
Do you really need to implement it this way? Did you consider other ways to do this?
I mean, hook at low level (io.Reader/[]byte) means need in double-decoding JSON and also supporting protocol details good enough to handle batch requests, request multiplexing, etc. All of this is bad for performance and very error-prone. Because of this I usually implement this as a wrapper/middleware on methods which handle requests - these methods anyway often needs some sort of such wrapper/middleware to handle common tasks like checking auth, setup request-scooped logger, etc. - so any sort of metrics and statistics are usually naturally fit too.
E.g. if we've RPC method Something(arg SomethingReq, resp *SomethingResp) error and wanna wrap it by some middlewares then we can just rename it to doSomething and use code-generation tools like https://github.com/cheekybits/genny to auto-generate method Something which will call doSomething in the middle, wrapped by required middlewares.
Example:
- middleware https://github.com/powerman/go-monolith-example/blob/master/ms/example/internal/api/wrap.go (auto-generated code for other handlers: https://github.com/powerman/go-monolith-example/blob/master/ms/example/internal/api/gen.wrap.go)
- RPC handlers https://github.com/powerman/go-monolith-example/blob/master/ms/example/internal/api/handlers.go
- arg/result type definitions https://github.com/powerman/go-monolith-example/blob/master/proto/rpc-example/proto.go
P.S. To use genny for code generation it's important to make sure arg/resp types of all RPC methods are named in same way and contain method name as part of type name (like in my example).