gRPC阅读日记(四)
Client-side streaming RPC
今天介绍的是比较复杂的RPC了,Client-side streaming 方法RecordRoute,服务端处理来自客户端的流式请求,这次,client会发送连续的Point消息请求,然后服务端返回单次响应RouteSummary,响应带有行程中的信息。从下方的例子中可以看到,这次方法不需要请求参数,而是用了RouteGuide_RecordRouteServer流,可以同时读写消息,并且可以通过Recv()来接收客户端消息,然后返回单个响应通过SendAndClose()方法
func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { var pointCount, featureCount, distance int32 var lastPoint *pb.Point startTime := time.Now() for { point, err := stream.Recv() if err == io.EOF { endTime := time.Now() return stream.SendAndClose(&pb.RouteSummary{ PointCount: pointCount, FeatureCount: featureCount, Distance: distance, ElapsedTime: int32(endTime.Sub(startTime).Seconds()), }) } if err != nil { return err } pointCount++ for _, feature := range s.savedFeatures { if proto.Equal(feature.Location, point) { featureCount++ } } if lastPoint != nil { distance += calcDistance(lastPoint, point) } lastPoint = point } }
在方法中可以看到,RouteGuide_RecordRouteServer的Recv()方法一直重复阅读来自客户端的请求,在具体一点,Recv()一直获取来自请求流中的单个point消息直到读取不到更多的消息。服务端必须检查Recv()方法的error,如果error等于nil,证明仍然需要读取消息,如果error等于io.EOF证明已经读取完毕了。服务器就可以返回RouteSummary。如果error既不是读取完毕io.EOF也不是nil则返回该错误,因为gRPC层会将它翻译成RPC状态并返回给客户端。
Bidirectional streaming RPC
来看最后一种双向流RPC的实现RouteChat()
func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { for { in, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } key := serialize(in.Location) ... // look for notes to be sent to client for _, note := range s.routeNotes[key] { if err := stream.Send(note); err != nil { return err } } } }
这次使用了RouteGuide_RouteChatServer流,在上一个客户端请求流式用例中,可以用来读写信息,这次我们通过方法的流来返回值的同时,客户端也仍然在像流中写消息。
从语法上也跟client-streaming方法很相似,除了服务端需要使用流的send()方法,而不是SendAndClose(),因为需要用俩写多重响应。尽管每一端总是能获取到按照他们写顺序的消息,但客户端和服务端都还能不根据顺序来读去消息,流的操作是完全独立的。