11.gRPC重试机制
gRPC 中已经内置了 retry 功能,可以直接使用,不需要手动来实现,本文主要记录使用 gRPC 实现自动重试功能。
gRPC重试机制
示例代码基于 grpc-testing v0.0.1
服务端
为了模拟操作失败,在服务端增加一些处理逻辑
package main
import (
"app/grpc-testing-server/api/echo"
"context"
"fmt"
"log"
"net"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type EchoServer struct {
// 增加计数
count int
echo.UnimplementedEchoServer
}
func (e *EchoServer) Echo(ctx context.Context, par *echo.EchoRequest) (*echo.EchoResponse, error) {
fmt.Println("echo")
e.count++
// 模拟失败
if e.count%3 != 0 {
return nil, status.Error(codes.Unavailable, "Unavailable")
}
result := &echo.EchoResponse{
Msg: par.GetMsg(),
}
return result, nil
}
func main() {
log.Println("Go")
listen, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
echo.RegisterEchoServer(s, &EchoServer{})
if err := s.Serve(listen); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
客户端
package main
import (
"app/grpc-testing-client/api/echo"
"context"
"log"
"google.golang.org/grpc"
)
func main() {
// 通过 serviceConfig 配置服务 retry
scsource := `{
"methodConfig": [{
"name": [{"service": "echo.echo","method":"Echo"}],
"retryPolicy": {
"MaxAttempts": 4,
"InitialBackoff": ".01s",
"MaxBackoff": ".1s",
"BackoffMultiplier": 1.0,
"RetryableStatusCodes": [ "UNAVAILABLE" ]
}
}]}`
conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure(), grpc.WithBlock(), grpc.WithDefaultServiceConfig(scsource))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
for i := 0; i < 10; i++ {
c := echo.NewEchoClient(conn)
ctx, cancle := context.WithCancel(context.Background())
defer cancle()
r, err := c.Echo(ctx, &echo.EchoRequest{Msg: "hello"})
if err != nil {
log.Fatalf("echo failed :%#v", err)
}
log.Print(r.GetMsg())
}
}
ServiceConfig 的配置说明
name
指定下面的配置信息作用的 RPC 服务或方法
- service : 通过服务名匹配,语法为
package.service
。package
就是 proto 文件中指定的package
,service
是 proto 文件中指定的service
。 - method : 匹配具体方法,值为 proto 文件中定义的 RPC 名称。
gRPC 的重试机制使用了退避算法,即失败一次,等待一定时间后再次尝试,如果还是失败,则等待更久的时间再次尝试,直到达到最大尝试次数或者最大尝试时间。重试策略配置如下:
- MaxAttempts : 最大尝试次数
- InitialBackoff : 默认退避时间
- MaxBackoff : 最大退避时间
- BackoffMultiplier : 退避时间增加倍率
- RetryableStatusCodes : 服务端返回什么错误代码才重试
gRPC 内置错误代码
gRPC 内置的错误代码定义在 google.golang.org/grpc/codes
包中
OK Code = 0 成功返回
Canceled Code = 1 客户端取消,gRPC 框架生成此错误代码
Unknown Code = 2 未知的错误,场景: 一个 RPC 调用返回的错误信息是本系统不知道的错误,或者返回的错误信息不足。 以上两上场景 gRPC 框架自动生成此错误代码
InvalidArgument Code = 3 无效的参数,此错误代码不由 gRPC 框架生成
DeadlineExceeded Code = 4 RPC 调用超时,此错误代码由 gRPC框架生成
NotFound Code = 5 实体未找到,此错误代码不由 gRPC 框架生成
AlreadyExists Code = 6 创建实体已存在,此错误代码不由 gRPC 框架生成
PermissionDenied Code = 7 调用者没有操作权限,如果是用于认证失败,请使用 Unauthenticated ,此错误代码不由 gRPC 框架生成,但是 authentication 中间件使用了它
ResourceExhausted Code = 8 资源耗尽,此错误代码在 out of memory , 服务器超载,或者 RPC 消息大于配置的最大尺寸时生成。
FailedPrecondition Code = 9 表示操作请求因为系统不处于执行操作所需的状态而被拒绝。此错误代码不由 gRPC 框架生成
Aborted Code = 10 操作中止。此错误代码不由 gRPC 框架生成
OutOfRange Code = 11 操作尝试超过有效范围。此错误代码不由 gRPC 框架生成
Unimplemented Code = 12 此服务未实现或不支持/未启用。此错误代码将由 gRPC 框架生成。最常见的情况是,当服务器上缺少方法实现时。
Internal Code = 13 内部错误,此错误代码由 gRPC 框架生成
Unavailable Code = 14 服务当前不可用,此错误代码将在服务器过程或网络连接突然关闭期间由 gRPC 框架生成。
DataLoss Code = 15 无法恢复的数据丢失或损坏。此错误代码不由 gRPC 框架生成
Unauthenticated Code = 16 请求没有有效的操作身份验证凭据。