最近,我司在进行某机房的断网演练,我在使用ETCD中间件时,发现了一个平时不太关注的问题——ETCD client的生命周期。即ETCD集群中涉及到节点切换,或者机器宕机或者断网的情况下,ETCD Client无法快速重连到可用的ETCD节点,导致client端不可用。
当然,上面的问题可以通过临时创建一个新的ETCD clinet或重启操作,但每次遇到断网错误或者断网时间较长,那么这段时间内所有的请求都要重新创建一个新的ETCD client来重启吗?频繁创建甚至重启ETCD client对系统有什么影响?本文我们好好的探讨下这个问题,同时引出ETCD client的endPoint生命周期管理比较合理的方式。
在项目中,我们使用的ETCD集群版本是3.5,ETCD client是基于gRPC实现的,什么是gRPC呢?gRPC是谷歌开源的一个RPC框架,面向移动和HTTP2设计的,与其他RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的API服务。gRPC彩的是HTTP2协议,在传输层的协议是TCP,如果对gRPC的Client的生命周期设置的非常短,则TCP连接资源转化成了短连接,因此,在实际使用过程中,为了充分复用多路复用,降低对TCP资源的管理成本,我在项目中全局定义了一个Client变量,相当于Client的生命周期等同于进程。
节点初始连接
gRPC的负载均衡是由客户端实现的,即客户端从可用的后端服务节点列表中根据自己的负载均衡策略选择一个节点直连后端服务器。gRPC提供了许多LB策略,如pick_first
(默认,传说最新的LB策略已经换到round_robin了
)、round_robin
和 grpclb。Client通过客户端选择一个gRPC Server进行连接,如果一个节点连接成功后,就会一直用这个连接,直到把它用坏了为止,这主要也是基于节省TCP连接考虑。
模拟节点宕机
mac在配置文件/etc/pf.conf添加block drop from any to x.y.z.z1,执行pfctl -ef /etc/pf.conf命令,然后ping该ip就timeout了。
linux中使用iptables就行
- endPoints连接域名,域名无VIP
在模拟的过程中,gRPC有主动重试的功能。但无论是主动创建一个新的client还是依赖gRPC进行自动重试,与etcd建立连接的节点都保持不变,这与预想的不一致。这也与前面所说的ETCD client长连接遥相呼应了,那么在节点故障或者网关等问题时,确保ETCD client连接到其他可用的集群节点上呢?
当然,上面的endPoints是通过域名连接的,如果是通过ip列表连接,则不会存在如上的情况的,那么通过域名如何解决呢?
这是由于缓存域名解析后ip存在本地内存中,不重启java程序会一直拿着这个域名对应的缓存,而我的域名解析是没有VIP层的。
- endPoints连接域名,域名添加VIP层
域名会解析到VIP层,在VIP层有效的情况下,继续模拟ETCD server多数节点不可用及添加新节点两种场景。
当大部分节点不可用时,客户端与VIP建立正常的连接,但是会出现如下错误:
io.grpc.StatusRuntimeException: UNKNOWN: context deadline exceeded
-->io.grpc.StatusRuntimeException: UNAVAILABLE: etcdserver: request timed out
当然,当etcd节点达到大多数可用时,整个集群又变得可用了。
- endPoints连接ip
如果客户端的LB策略为round_robin,则客户端将与endPoint中的ip列表都将建立TCP连接
如果客户端的LB策略为pick_first(默认策略),则客户端只与endPint中的一个ip节点建立连接。当然,无论以上两种策略,如果某一个服务端节点不可用时,客户端会出现『连接超时』的异常,然后按照LB策略自动重试列表中的其他节点。
总结
由上面的分析可知,ETCD client与gRPC server建立连接时,由于etcd的负载均衡是客户端实现的,为了追求极致的性能,它会将endPoints中的节点缓存到客户端本地内存(如果是域名通过dns解析)中,客户端本身有错误重试功能,无论客户端的LB(如pick_first, round_robin还是自定义的)如何,endPoints列表的范围是固定的,在不重启客户端节点时,与gRPC server建立的连接节点范围都是不变的。如无VIP层的域名连接且endPoints层没有多列表,如果对应的server实例挂掉了,则在最大重试次数前,都只会与该server节点尝试SYNC_SEND建立连接。
那如何构建高可用的endPoints呢?
为所有的gRPC server节点对应的域名增加VIP层,VIP建议至少两个(有条件可以异地双机房部署)
客户端建立端点连接时,将域名,及对应的VIP列表添加到endPoints列表中
通过上述方案,则可保证在gRPC server网络不通/宕机等极端 情况下,ETCD client在不重新部署下能平滑的与ETCD集群建立友好的连接(毕竟所有VIP同时不可用是非常非常小的概率)
上述博文中分享了ETCD client建立的端点连接生命周期,及如何构建client与server的高可用连接。