GRPC 中 context的应用!
求取消和超时控制
context.WithTimeout
gRPC上下文包含一个取消通知,当客户端取消请求或者请求超时时,可以通过上下文来通知服务器端停止处理该请求。
syntax = "proto3";
option go_package = "./demo"; // go_package 指定了编译后生成文件的路径,也对应了生成后的包名!
service DemoService {
rpc ProcessRequest(RequestMessage) returns (ResponseMessage);
}
message RequestMessage {
string data = 1;
}
message ResponseMessage {
string result = 1;
}
实现写服务端 ProcessRequest服务方法(客户端传递过来的是一个context.WithTimeout
的context
,在指定时间会调用Done
)
func (d DemoService) ProcessRequest(ctx context.Context, req *demo.RequestMessage) (*demo.ResponseMessage, error) {
time.Sleep(3 * time.Second) // 模拟服务端处理业务3s
select {
case <-ctx.Done():
// 请求已取消或超时
fmt.Println("超时了!")
return nil, ctx.Err()
default:
// 处理请求
result := "Processed: " + req.GetData()
return &demo.ResponseMessage{Result: result}, nil
}
}
上面的代码我们使用 Sleep 模拟了业务的处理时间!如果超过了客户端context指定的超时时间,就会返回超时的错误!
客户端代码实现
func main() {
conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
if err != nil {
log.Fatalf("Failed to connect: %v", err)
}
defer conn.Close()
client := demo.NewDemoServiceClient(conn)
// 客户端2s后就会执行cancel!
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
req := &demo.RequestMessage{
Data: "Hello, gRPC!",
}
res, err := client.ProcessRequest(ctx, req)
if err != nil {
log.Fatalf("Request error: %v", err)
}
log.Printf("Response: %s", res.GetResult())
}
上面的客户端代码!就是在调用服务端方法的时候,传入了一个2s 超时的 TimeoutContext
,上面服务端的业务逻辑处理了三秒,那么此时!服务端就会收到从context.done
然后返回err给客户端!!
传递元数据 METADAT
传递元数据:上下文可以用于传递请求相关的元数据,比如身份验证凭据、请求标识符等。服务器端可以通过上下文来访问这些元数据并根据需要进行处理。
GRPC客户端发送metadata
metadata.Pairs("key", "val")
创建 Meta data- 这里使用
metadata.NewOutgoingContext
创建一个 context!传入我们的Meta data
md := metadata.Pairs("token", "1111") // metadata.Pairs 设置元数据信息!
ctxMD := metadata.NewOutgoingContext(context.Background(), md)
client := demo.NewDemoServiceClient(conn)
// 客户端2s后就会执行cancel!
ctx, cancel := context.WithTimeout(ctxMD, 2*time.Second)
defer conn.Close()
defer cancel()
req := &demo.RequestMessage{
Data: "Hello, gRPC!",
}
res, err := client.ProcessRequest(ctx, req)
if err != nil {
log.Fatalf("Request error: %v", err)
}
服务端通过 metadata.FromIncomingContext(ctx)
接受 ,从 context 获取 Metadata
// UnaryInterceptor 实现拦截器接口的方法
func (i *interceptor) UnaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
// 在请求到达处理程序之前执行的代码
log.Println("Before handling request")
if outgoingContext, b := metadata.FromIncomingContext(ctx); b {
fmt.Println(outgoingContext.Get("token"))
} else {
fmt.Println("no metadata token!")
}
// 调用实际的处理程序
resp, err = handler(ctx, req)
// 在处理程序完成后执行的代码
log.Println("After handling request")
return resp, err
}
评论区