Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go gRPC Stream Scope

Tags:

go

grpc

TLDR; If my Go gRPC bi-directional streaming server is looping on Recv/Send infinitely, how can I close the connection when my client's stream falls out of scope and gets GC'd?


Let's say I am using Go to do a gRPC bi-directional streaming call:

syntax = "proto3";

package mystical;

service Unicorn {
    rpc RainbowStream(stream Rainbow) returns (stream Rainbow);
}

I have a server implementation that I will register with my gRPC server:

type Server struct {}

func (serv *Server) RainbowStream(stream proto.Unicorn_RainbowStreamServer) error {
    // Stub implementation
    // Just sends the rainbows back
    for {
        msg, err := stream.Recv()
        if err != nil {
            return err
        }
        if err := stream.Send(msg); err != nil {
            return err
        }
    }
}

Then I have a client, eliding some of the gRPC nitty-gritty:

type Client struct {
    proto.UnicornClient
}

func (c *Client) TastyColors() (stream proto.Unicorn_RainbowStreamClient, err error) {
    return c.RainbowStream()
}

And I do this:

func somewhereDeepInMyCode() {

    stream, err := aClient.TastyColors()
    // ...

    go func() {
       stream.Send(msg)
       // ...
       // eventually this goroutine ends
       // and `stream` would get GC'd
    }
}

When stream falls out of scope, does the gRPC streaming call clean itself up? Normally, from what I understand, the server has to return on its RainbowStream call to close the connection. If it doesn't clean itself up, how can I achieve this?

like image 548
Matt Mc Avatar asked Nov 25 '25 13:11

Matt Mc


1 Answers

Use a cancel (https://golang.org/pkg/context/#WithCancel) context to cancel from the client side:

func somewhereDeepInMyCode() {
    ctx, cancel := context.WithCancel(context.Background())
    stream, err := aClient.TastyColors(ctx)
    // ...

    go func() {
        defer cancel()
        stream.Send(msg)
        // ...
        // eventually this goroutine ends
        // and `stream` would get GC'd
    }
}

The server can handle client-side cancellation for cleanup as needed:

for {
    select {
    ...
    case <-stream.Context().Done():
    // cleanup
    ...
    }
}
like image 93
Chad Maine Avatar answered Nov 28 '25 02:11

Chad Maine



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!