rpc-codec icon indicating copy to clipboard operation
rpc-codec copied to clipboard

measure runtime by method

Open jackfreed opened this issue 5 years ago • 1 comments

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:} mutex:{state:0 sema:0} seq:1 pending:map[]

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!

jackfreed avatar May 26 '20 11:05 jackfreed

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).

powerman avatar May 26 '20 13:05 powerman