cancelling context
2019/06/12 11:33:01 error on Echo: rpc error: code = Canceled desc = context canceled
exit status 1
msg from client: echo from client
start sleep
context done, return error
syntax = "proto3"; | |
// EchoRequest is the request for echo. | |
message EchoRequest { | |
string message = 1; | |
} | |
// EchoResponse is the response for echo. | |
message EchoResponse { | |
string message = 1; | |
} | |
service NonStreamingCancel { | |
rpc Echo(EchoRequest) returns(EchoResponse) {} | |
} |
package main | |
import ( | |
"context" | |
"flag" | |
"fmt" | |
"log" | |
"time" | |
"google.golang.org/grpc" | |
pb "grpc/examples/proto/echo" | |
) | |
var addr = flag.String("addr", "localhost:50051", "the address to connect to") | |
func main() { | |
flag.Parse() | |
// Set up a connection to the server. | |
conn, err := grpc.Dial(*addr, grpc.WithInsecure()) | |
if err != nil { | |
log.Fatalf("did not connect: %v", err) | |
} | |
defer conn.Close() | |
c := pb.NewNonStreamingCancelClient(conn) | |
// Initiate the stream with a context that supports cancellation. | |
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | |
go func() { | |
fmt.Println("cancelling context") | |
time.Sleep(1 * time.Second) | |
cancel() | |
}() | |
res, err := c.Echo(ctx, &pb.EchoRequest{Message: "echo from client"}) | |
if err != nil { | |
log.Fatalf("error on Echo: %v", err) | |
} | |
fmt.Printf("response ------- %+v\n", res) | |
} |
package main | |
import ( | |
"context" | |
"flag" | |
"fmt" | |
"google.golang.org/grpc/codes" | |
"google.golang.org/grpc/status" | |
"log" | |
"net" | |
"time" | |
"google.golang.org/grpc" | |
pb "grpc/examples/proto/echo" | |
) | |
var port = flag.Int("port", 50051, "the port to serve on") | |
type server struct{} | |
func (s *server) Echo(ctx context.Context, in *pb.EchoRequest) (*pb.EchoResponse, error) { | |
fmt.Println("msg from client: ", in.Message) | |
done := make(chan struct{}) | |
go func() { | |
fmt.Println("start sleep") | |
time.Sleep(5 * time.Second) | |
done <- struct{}{} | |
fmt.Println("push to done channel, end sleep") | |
}() | |
select { | |
case <- ctx.Done(): | |
fmt.Println("context done, return error") | |
return nil, status.Error(codes.Canceled, "context canceled on server side") | |
case <- done: | |
fmt.Println("get data from done channel") | |
return &pb.EchoResponse{Message: "echo from server"}, nil | |
} | |
} | |
func main() { | |
flag.Parse() | |
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) | |
if err != nil { | |
log.Fatalf("failed to listen: %v", err) | |
} | |
fmt.Printf("server listening at port %v\n", lis.Addr()) | |
s := grpc.NewServer() | |
pb.RegisterNonStreamingCancelServer(s, &server{}) | |
s.Serve(lis) | |
} |
Base on log content, we can conclude that when the context in the client side is canceled, the context in server side is canceled, too.
In short, the flow is like below:
Canceled
code to the server.You can dig deeper into the source code by starting at https://github.com/grpc/grpc-go/blob/684ef046099f3f1c4b1fe762e5826a2f7bc6e27f/internal/transport/http2_client.go#L975