官方设计文档
https://github.com/grpc/grpc-go/blob/master/Documentation/keepalive.md
gRPC在传输上发送http2 ping以检测连接是否断开。如果在一定时间内对方未确认ping,将关闭连接。请注意,仅当连接上没有活动时才需要执行ping操作。
Keepalive对于检测TCP级别的连接故障很有用。一种特殊情况是TCP连接丢失数据包(包括FIN)。需要使用系统TCP超时(可能是30分钟)来检测此故障。Keepalive将允许gRPC更快地检测到这个故障。
另一种用法(顾名思义)是保持连接处于活动状态。例如,L4代理被配置为杀死“空闲”连接。发送ping将使连接不“空闲”。
有关如何配置Keepalive的信息,请参见 https://godoc.org/google.golang.org/grpc/keepalive。
我应该设置什么?
对于大多数用户而言,将客户端参数设置为拨号选项就足够了。
会发生什么?
(此处描述的行为特定于gRPC-go,在其他语言中可能会稍有不同。)
当连接上没有活动时(请注意,当没有消息发送时,正在进行的流将导致 没有活动),之后Time
,客户端将发送ping命令,而服务器在收到ping命令后将发送ping ack。客户端将等待Timeout
,并检查在此期间连接上是否有任何活动(Ping ack是一项活动)。
服务器端呢?
服务器也有类似的Time
并Timeout
设置为客户端。服务器还可以配置连接最大寿命。有关 详细信息,请参见服务器参数。
执法政策
强制策略是服务器端的一项特殊设置,用于保护服务器免受恶意或行为不正常的客户端的侵害。
服务器发送ENHANCE_YOUR_CALM发送GOAWAY并在检测到不良行为时关闭连接:
- 客户端发送太频繁的ping
- 客户端在没有流的情况下发送ping命令,而服务器配置不允许这样做
type ClientParameters
ClientParameters用于在客户端设置keepalive参数。这些配置了客户端如何主动探测连接何时断开并发送ping信号,以便中介体能够感知连接的活动。确保这些参数是与服务器上的keepalive策略协调设置的,因为不兼容的设置可能导致关闭连接。
type ClientParameters struct {
//在一段时间后,如果客户端没有看到任何活动,它ping服务器,看看传输是否仍然是活动的。
//如果设置小于10s,则使用最小值10s
Time time.Duration // The current default value is infinity.
//ping ack 20s之内未返回则认为连接已断开
Timeout time.Duration // The current default value is 20 seconds.
//如果是,客户端发送keepalive ping,即使没有活动的rpc。如果为false,当没有活动的rpc时,
//时间和超时将被忽略,并且不会发送keepalive ping。
PermitWithoutStream bool // false by default.
}
type EnforcementPolicy
用于在服务器端设置keepalive实施策略。服务器将与违反此策略的客户端关闭连接。
type EnforcementPolicy struct {
// 客户端每次发送keepalive ping之间应该等待的最短时间
MinTime time.Duration // The current default value is 5 minutes.
// 如果是,服务器允许保持活的ping信号,即使没有活动的流(rpc)。
//如果为false,客户端在没有活动流时发送ping,服务器将发送GOAWAY并关闭连接。
PermitWithoutStream bool // false by default.
}
type ServerParameters
在服务器端设置keepalive和max-age参数
服务端对每条链接执行的keepalive策略,因此如果建立很多的conn,且没有主动关闭,会导致server端大量goroutine堵塞在keepalive逻辑里
https://github.com/grpc/grpc-go/issues/1269
type ServerParameters struct {
//在这段时间之后,一个空闲连接将通过发送GoAway关闭。
// 闲置时间是指最近一次未完成的rpc数量变为0或连接建立之后的时间。
MaxConnectionIdle time.Duration // The current default value is infinity.
// 连接在发送GoAway关闭之前,可能存在的最大时间长度
MaxConnectionAge time.Duration // The current default value is infinity.
// MaxConnectionAgeGrace是MaxConnectionAge之后的一个附加周期,在此之后连接将被强制关闭。
MaxConnectionAgeGrace time.Duration // The current default value is infinity.
// 在一段时间之后,如果服务器没有看到任何活动,它将ping客户机以查看传输是否仍然是活动的。
// 如果设置在1s以下,则使用1s的最小值。
Time time.Duration // The current default value is 2 hours.
// ping ack 20s之内未返回则认为连接已断开
// After having pinged for keepalive check, the server waits for a duration
// of Timeout and if no activity is seen even after that the connection is
// closed.
Timeout time.Duration // The current default value is 20 seconds.
}
遇到 grpc 客户端报错 rpc error: code = Unavailable desc = transport is closing
,原因是连接长时间没有使用,被服务端断开,这种情况通过简单粗暴的重试策略可以解决,更加优雅的解决方案是增加保持连接策略
这个错误意味着RPC正在使用的连接被关闭,有很多可能的原因,包括:
- 传输凭据配置错误,在握手时连接失败
- 字节中断,可能是由于中间的代理
- 服务器关闭
- Keepalive参数会导致连接关闭,例如,如果您已将服务器配置为定期终止连接以触发DNS查找。如果是这种情况,您可能需要增加MaxConnectionAgeGrace,以允许更长的RPC调用完成。
client
keep := keepalive.ClientParameters{
Time:20*time.Second,
Timeout:20*time.Second,
PermitWithoutStream:true,
}
conn, err := grpc.Dial(address, grpc.WithInsecure(),grpc.WithKeepaliveParams(keep))
server
var kaep = keepalive.EnforcementPolicy{
MinTime: 5 * time.Second,
PermitWithoutStream: true,
}
var kasp = keepalive.ServerParameters{
MaxConnectionIdle: 15 * time.Second,
MaxConnectionAge: 30 * time.Second,
MaxConnectionAgeGrace: 5 * time.Second,
Time: 5 * time.Second,
Timeout: 1 * time.Second,
}
s := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))