分析服务端keepalive实现
概览
如下是服务端keepalive配置
1 | var kaep = keepalive.EnforcementPolicy{ |
服务端分为两部分配置,一部分为keepalive.EnforcementPolicy,有两个配置参数:
- MinTime:如果客户端两次ping的间隔小于5s,中止连接
- PermitWithoutStream:即使没有active stream,也允许ping
另一部分为keepalive.ServerParameters,五个配置参数:
- MaxConnectionIdle:如果一个client空闲超过15s,发送一个GOAWAY,为了防止同一时间发送大量GOAWAY,会在15s时间间隔上下浮动15*10%,即15+1.5或者15-1.5
- MaxConnectionAge:如果任意连接存活时间超过30s,发送一个GOAWAY
- MaxConnectionAgeGrace:在强制关闭连接之间,允许有5s的时间完成pending的rpc请求
- Time:如果一个clinet空闲超过5s,则发送一个ping请求
- Timeout:如果ping请求1s内未收到回复,则认为该连接已断开
keepalive策略相关代码
1 | func (t *http2Server) handlePing(f *http2.PingFrame) { |
判断是否违反两条策略,如果违反则将pingStrikes++,当违反次数大于maxPingStrikes(2)时,打印一条错误日志并且发送一个goAway包.
keepalive参数相关代码
1 | func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) { |
新建一个HTTP2 server的时候会启动一个单独的goroutine处理keepalive.
1 | func (t *http2Server) keepalive() { |
启动三个定时器,分别处理maxIdle,maxAge,keepAlive相关事件:
- maxIdle:判断client空闲时间是否超出配置时间,如果超时,则调用t.drain,该方法会发送一个GOAWAY包
- maxAge:触发之后首先调用t.drain发送GOAWAY包,接着重置定时器,时间设置为MaxConnectionAgeGrace,再次触发后调用t.Close()直接关闭
- keepalive:首先判断activity是否为1,如果不是则置pingSent为true,并且发送ping包,接着重置定时器时间为Timeout,再次触发后如果activity不为1(即未收到ping的回复)并且pingSent为true,则调用t.Close()关闭连接
小结
keepalive配置及实现通过client和server两端来实现.借助于go的timer和goroutine可以实现的相当简洁易懂