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(),因为需要用俩写多重响应。尽管每一端总是能获取到按照他们写顺序的消息,但客户端和服务端都还能不根据顺序来读去消息,流的操作是完全独立的。
网友评论