13

I am using grpc go

i have an rpc which looks roughly like this

196 service MyService {
197   // Operation 1
198   rpc Operation1(OperationRequest) returns (OperationResponse) {
199       option (google.api.http) = {
200         post: "/apiver/myser/oper1"
201         body: "*"
202     };
203   }

Client connects by using grpc.Dial() method

When a client connects, the server does some book keeping. when the client disconnects, the bookkeeping needs to be removed.

is there any callback that can be registered which can be used to know that client has closed the session.

weima
  • 4,653
  • 6
  • 34
  • 55
  • I haven't used gRPC yet myself but I will be soon and during my research I've seen the server interface methods pass a `ctx context.Context` argument. I would like to assume this context will be cancelled if the connection with the client is closed. See [Go gRPC Basics](http://www.grpc.io/docs/tutorials/basic/go.html) – jmason Oct 05 '16 at 19:42
  • thanks for your comment!!. i have explored `ctx.Done()` option. this channel is closed per operation, on the same connection. thats not what i am looking for. i am looking for a channel to read connection closed event. – weima Oct 07 '16 at 05:42
  • In our rpc app we periodically poll with time.Ticker, calling a status function, and if that returns a connection error we mark the worker as dead from the master side. Not sure if this is good enough for your use case. – Slabgorb Apr 06 '18 at 19:13

3 Answers3

8

Based on your code, it's an unary rpc call, the client connect to server for only one time, send a request and get a response. The client will wait for the response until timeout.

In server side streaming, you can get the client disconnect from

<-grpc.ServerStream.Context.Done()

signal.

With that above, you can implement your own channel in a go routine to build your logic. Use select statement as:

select {
    case <-srv.Context().Done():
        return
    case res := <-<YOUR OWN CHANNEL, WITH RECEIVED RESQUEST OR YOUR RESPONSE>
        ....
}

I provide some detailed code here

In client streaming, besides the above signal, you can check whether the server can receive the msg:

req, err := grpc.ServerStream.Recv()
if err == io.EOF {
    break
} else if err != nil {
    return err
}
Franci
  • 2,157
  • 1
  • 21
  • 33
1

Assuming that the server is implemented in go, there's an API on the *grpc.ClientConn that reports state changes in the connection.

func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool

https://godoc.org/google.golang.org/grpc#ClientConn.WaitForStateChange

These are the docs on each of the connectivity.State

https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md

If you need to expose a channel that you can listen to for the client closing the connection then you could do something like this:

func connectionOnState(ctx context.Context, conn *grpc.ClientConn, states ...connectivity.State) <-chan struct{} {
    done := make(chan struct{})

    go func() {
        // any return from this func will close the channel
        defer close(done)

        // continue checking for state change 
        // until one of break states is found
        for { 
            change := conn.WaitForStateChange(ctx, conn.GetState())
            if !change {
                // ctx is done, return
                // something upstream is cancelling
                return
            }

            currentState := conn.GetState()

            for _, s := range states {
                if currentState == s {
                    // matches one of the states passed
                    // return, closing the done channel
                    return 
                }
            }
        }
    }()

    return done
}

If you only want to consider connections that are shutting down or shutdown, then you could call it like so:

// any receives from shutdownCh will mean the state Shutdown
shutdownCh := connectionOnState(ctx, conn, connectivity.Shutdown) 
Zak
  • 5,515
  • 21
  • 33
  • 8
    isn't your solution only for client-side? I thought the OP was asking how to know on the server-side. – codecraig Dec 12 '19 at 15:47
0

as the github issue:link you can do like this

err = stream.Context().Err()
if err != nil {
    break
}