使用StreamClientInterceptor来确定RPC会话何时结束的最佳方法是什么?

huangapple go评论85阅读模式
英文:

What's the best way to determine when an RPC session ends using a StreamClientInterceptor?

问题

在编写StreamClientInterceptor函数时,确定调用者何时完成RPC的最佳方法是什么?对于一元拦截器或在服务器端,您会收到执行RPC的handler,这是很明显的。但是在客户端,您返回一个ClientStream,调用者与之交互,这时候该如何确定呢?

其中一个使用案例是在OpenTracing中进行仪表化,目标是在RPC的开始和结束时启动和完成一个span。

我正在研究的一种策略是让流拦截器返回一个装饰过的ClientStream。这个新的ClientStream会在任何接口方法HeaderCloseSendSendMsgRecvMsg返回错误或Context被取消时,将RPC视为已完成。此外,它还在RecvMsg方法中添加了以下逻辑:

func (cs *DecoratedClientStream) RecvMsg(m interface{}) error {
	err := cs.ClientStream.RecvMsg(m)
	if err == io.EOF {
		// 将RPC视为已完成
		return err
	} else if err != nil {
		// 将RPC视为已完成
		return err
	}
	if !cs.isResponseStreaming {
		// 将RPC视为已完成
	}
	return err
}

在大多数情况下,这种方法是有效的。但是我了解到,如果调用者知道结果将是io.EOF,它并不需要调用Recv(参见https://stackoverflow.com/questions/42915337/are-you-required-to-call-recv-until-you-get-io-eof-when-interacting-with-grpc-cl/42939914#42939914),所以它并不适用于所有情况。有没有更好的方法来实现这个目标?

英文:

When writing a StreamClientInterceptor function, what's the best way to determine when an invoker finishes the RPC? This is straightforward enough with unary interceptors or on the server-side where you're passed a handler that performs the RPC, but it's not clear how best to do this on the client-side where you return a ClientStream that the invoker then interacts with.

One use case for this is instrumenting OpenTracing, where the goal is to start and finish a span to mark the beginning and end of the RPC.

A strategy I'm looking into is having the stream interceptor return a decorated ClientStream. This new ClientStream considers the RPC to have completed if any of the interface methods Header, CloseSend, SendMsg, RecvMsg return an error or if the Context is cancelled. Additionally, it adds this logic to RecvMsg:

func (cs *DecoratedClientStream) RecvMsg(m interface{}) error {
	err := cs.ClientStream.RecvMsg(m)
	if err == io.EOF {
		// Consider the RPC as complete
		return err
	} else if err != nil {
		// Consider the RPC as complete
		return err
	}
	if !cs.isResponseStreaming {
		// Consider the RPC as complete
	}
	return err
}

It would work in most cases, but my understanding is that an invoker isn't required to call Recv if it knows the result will be io.EOF (See https://stackoverflow.com/questions/42915337/are-you-required-to-call-recv-until-you-get-io-eof-when-interacting-with-grpc-cl/42939914#42939914), so it wouldn't work in all cases. Is there a better way to accomplish this?

答案1

得分: 3

我遇到了一个非常类似的问题,我想要追踪流式的 gRPC 调用。除了像你自己提到的那样对流进行装饰之外,我没有找到一个好的方法来检测流的结束。直到我发现了 grpc-go 提供的 stats hooks(https://godoc.org/google.golang.org/grpc/stats)。尽管 stats API 的目的是收集有关 RPC 调用的统计信息,但它提供的 hooks 对于追踪也非常有帮助。

如果你仍然在寻找追踪流式调用的方法,我编写了一个使用 stats hooks 对 gRPC 进行 OpenTracing 仪器化的库:https://github.com/charithe/otgrpc。然而,请注意,这种方法可能不适用于创建长时间运行的流的系统。

英文:

I had a very similar issue where I wanted to trace streaming gRPC calls. Other than decorating the stream as you mentioned yourself, I was not able to find a good way to detect the end of streams. That is, until I came across the stats hooks provided by grpc-go (https://godoc.org/google.golang.org/grpc/stats). Even though the stats API is meant for gathering statistics about the RPC calls, the hooks it provides are very helpful for tracing as well.

If you're still looking for a way to trace streaming calls, I have written a library for OpenTracing instrumentation of gRPC, using the stats hooks:
https://github.com/charithe/otgrpc. However, please bear in mind that this approach is probably not suitable for systems that create long-lived streams.

huangapple
  • 本文由 发表于 2017年3月24日 06:48:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/42988396.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定