背景
我们的服务器大致架构是一个接入服mixer,mixer会连后台的服务backend,两者之间通过GRPC通信(长连接)。backend可能有多实例,所以为了负载均衡,mixer维护了一个连接池,连接池会定期进行刷新(2min一次)。
问题
当某一个backend实例重启时,mixer并不能立即感知到,而要等到下一次刷新连接池才会更新连接。这样在backend重启到mixer下次刷新期间的请求如果被路由到正在重启的backend实例,就会一直等到超时返回。
解决
我们是通过kubernetes管理服务,backend重启时,k8s会先启动一个新的实例,等新的实例ready for serve,就发送SIGTERM给旧的实例。在接收到SIGTERM后,backend就进入关闭过程,这时是无法保证正常返回的。
所以在backend程序中加了对系统SIGTERM信号的处理:当接收到SIGTERM信号,会设置一个全局变量为true,标识服务器正在关闭中。当其他线程接收到mixer的信号,会去读这个全局变量,发现自己已经在关闭过程中,就会给mixer返回一个特定的消息。
那么mixer接收到这个消息应该如何做呢?
1)接收到这个消息代表某个backend实例即将失效,应该对连接池中所有指向这个实例的连接进行刷新。
2)接收到这个消息的请求应该换一个有效的连接进行重试。
但是这里有个问题就是我们用的是GRPC,进行连接的是一个k8s service的地址,而不是每个instance的真实地址,所以我们维护的连接池中的连接,是不知道自己连向哪一个backend实例的,所以1)和2)都会有问题,如下:
1)无法区分哪些连接是指向即将关闭的实例,所以就无法确定该刷新哪些连接。
2)无法知道哪个连接是指向即将关闭的实例,所以无法选出有效的连接。
针对1),最简单的办法就是在收到SIGTERM时刷新全部连接,但代价太高,会阻塞mixer的全部服务。
针对2),如何选到一个有效的连接呢?由于待重启的实例在接收到SIGTERM信号时,k8s已经将其从service的列表中摘除,那么刷新后的连接就肯定不是连向即将关闭的实例,问题解决。
网友评论