I am building an API using gRPC and in server side, I want to receive a notification when a client disconnects, identify it and perform some tasks based on that.
So far, I was able to detect client disconnection using grpc.StatsHandler
method HandleConn
. I tried passing values using context, but they can't be accessed from server side.
Client side:
conn, err := grpc.DialContext(
context.WithValue(context.Background(), "user_id", 1234),
address,
grpc.WithInsecure(),
)
Server side:
// Build stats handler
type serverStats struct {}
func (h *serverStats) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
return ctx
}
func (h *serverStats) HandleRPC(ctx context.Context, s stats.RPCStats) {}
func (h *serverStats) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
return context.TODO()
}
func (h *serverStats) HandleConn(ctx context.Context, s stats.ConnStats) {
fmt.Println(ctx.Value("user_id")) // Returns nil, can't access the value
switch s.(type) {
case *stats.ConnEnd:
fmt.Println("client disconnected")
break
}
}
// Build server
s := grpc.NewServer(grpc.StatsHandler(&serverStats{}))
I want to access the value passed from client side in server side. What is the right way to do it, or is there any other way to identify the client that has disconnected?
I don't think you may pass that value when you are dialling, but you may tag the connection and than all next client requests will have the same value in its context:
In your serverStats implementation:
func (h *serverStats) TagConn(ctx context.Context, info *stats.ConnTagInfo) context.Context {
return context.WithValue(ctx, "user_counter", userCounter++)
}
In your service implementation
func (s *service) SetUserId(ctx context.Context, v MyMessage) (r MyResponse, err Error) {
s.userIdMap[ctx.Value("user_counter")] = v.userId
...
}
In the other methods:
func (s *service) AnotherMethod(ctx context.Context, v MyMessage) (r MyResponse, err Error) {
userId := s.userIdMap[ctx.Value("user_counter")]
...
}
func (h *serverStats) HandleConn(ctx context.Context, s stats.ConnStats) {
switch s.(type) {
case *stats.ConnEnd:
fmt.Printf("client %d disconnected", s.userIdMap[ctx.Value("user_counter")])
break
}
}
Please let me know if you find a way to pass values in the dialling step.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With