如果客户端因网络错误而断开连接,则服务器必须在我的情况下关闭pub / sub连接。我知道ctx.Done()
函数,但在我的情况下不知道如何正确使用它。有人可以解释一下吗?
grpc-go:1.7.0
go go go.8.4.4
func (a *API) Notifications(in *empty.Empty, stream pb.Service_NotificationsServer) error {
ctx := stream.Context()
_, ok := user.FromContext(ctx)
if !ok {
return grpc.Errorf(codes.Unauthenticated, "user not found")
}
pubsub := a.redisClient.Subscribe("notifications")
defer pubsub.Close()
for {
msg, err := pubsub.ReceiveMessage()
if err != nil {
grpclog.Warningf("Notifications: pubsub error: %v", err)
return grpc.Errorf(codes.Internal, "pubsub error %v", err)
}
notification := &pb.Notification{}
err = json.Unmarshal([]byte(msg.Payload), notification)
if err != nil {
grpclog.Warningf("Notifications: parse error: %v", err)
continue
}
if err := stream.Send(notification); err != nil {
grpclog.Warningf("Notifications: %v", err)
return err
}
grpclog.Infof("Notifications: send msg %v", notification)
}
}
答案 0 :(得分:1)
您可以使用select
。而不是正常从函数获取数据,使用通道获取数据和go例程来处理它。
有点像这样:
func (a *API) Notifications(in *empty.Empty, stream
pb.Service_NotificationsServer) error {
ctx := stream.Context()
_, ok := user.FromContext(ctx)
if !ok {
return grpc.Errorf(codes.Unauthenticated, "user not found")
}
pubsub := a.redisClient.Subscribe("notifications")
defer pubsub.Close()
// I can not build the code, so I assume the msg in your code Message struct
c := make(chan Message)
go func() {
for {
msg, err := pubsub.ReceiveMessage()
if err != nil {
grpclog.Warningf("Notifications: pubsub error: %v", err)
close(c)
return grpc.Errorf(codes.Internal, "pubsub error %v", err)
}
c<- msg
}
}()
for {
select {
case msg, ok := <-c:
if !ok {
// channel is closed handle it
}
notification := &pb.Notification{}
err = json.Unmarshal([]byte(msg.Payload), notification)
if err != nil {
grpclog.Warningf("Notifications: parse error: %v", err)
continue
}
if err := stream.Send(notification); err != nil {
grpclog.Warningf("Notifications: %v", err)
return err
}
grpclog.Infof("Notifications: send msg %v", notification)
case <- ctx.Done():
// do exit logic. some how close the pubsub, so next
// ReceiveMessage() return an error
// if forget to do that the go routine runs for ever
// until the end of main(), which I think its not what you wanted
pubsub.Close() // Its just pseudo code
return
}
}
}
从频道中读取消息(我假设类型为Message),并使用select
的幂。
在这种情况下另外两件事:
确保完成此功能后,go例程结束。我无法猜测,因为我不知道代码,但我认为有Close()
方法可以关闭pubsub
,以便下一个ReceiveMessage
返回错误。 (我看到推迟做我希望的工作)
如果ReceiveMessage
之前ctx.Done
出现错误,您可以关闭频道然后中断循环。
答案 1 :(得分:0)
您应该从调用者函数(或可以访问上下文的任何位置)取消上下文,并对Done()
检查select
语句执行适当的操作。
完成提供用于选择语句
当代表此上下文完成的工作应该被取消时,完成返回已关闭的频道。如果永远不能取消此上下文,则可以返回nil。对Done的连续调用返回相同的值。
和
WithCancel返回父级的副本,其中包含新的完成频道。当返回的取消函数被调用或父上下文的完成频道关闭时(以先发生者为准),返回的上下文的完成频道将被关闭。
取消此上下文会释放与其关联的资源,因此代码应在此上下文中运行的操作完成后立即调用cancel。
go func() {
for {
select {
case <-ctx.Done():
return // returning not to leak the goroutine
case dst <- n:
n++
}
}
}()