前两天线上出现tfs客户端无法登陆rc server的情况,查看rc server的log发现,server的网络队列已经满了,重启rc服务,没有作用,很快网络队列又满了,通过pstack发现rc server上多个网络包处理线程都在等gethostbyname里的锁,有一个线程加锁成功,但gethostbyname的调用一直阻塞(阻塞的原因后来确认是DNS服务的问题)。
rc server是TFS用于管理应用、集群的服务,每个使用TFS的应用在使用时,会先登录rc server,获取给该应用配置的集群资源,由于后端集群的资源可能会发生变化,比如某个机房有问题时,需要将某些应用的访问切换到其他机房的集群,通过客户端与rc server周期性的keepalive,后端的配置一旦发生变化,rc server会将变化后的配置信息带给客户端。
应用信息、集群信息由TFS的运维人员配置在mysql数据库,rc server在启动时会先从数据库加载这些配置信息,并在数据库信息发生变更时重新加载,最新的配置通过keepalive带给客户端。如果rc server宕机,已经登录rc server获取到初始配置的客户端将不受影响(keepalive失败只会导致客户端拿不到最新的配置,但只要后端的配置没有发生变化,客户端是丝毫不受影响的);新启动的客户端则会因登录不到rc server而启动失败,所以对于rc server的服务,服务时可以短时间中断的,但可以容忍错误不代表不需要做容错,下面主要探讨下rcserver的容错机制。
rc server依赖数据存在可共享的mysql数据库里,多个rc server可以加载同一mysql的配置信息提供服务,客户端连接某一个rc server失败时,直接尝试其他的rc server即可,所以关键问题在于如何实现这种重试机制。
最简单的方式是借助DNS服务(使得服务依赖DNS服务,同时要求DNS服务支持健康检查机制),将多个rc server的ip地址映射到同一个域名上,客户端直接根据rc的域名来访问rc server的服务,客户端访问rc server解析ip地址时,借助DNS的健康检查机制,DNS服务会返回某个正常服务着的(服务监听的端口正常)rc server地址,故即使在有rc server宕机时,客户端也能正常访问到rc server的服务。
借助DNS的方式还有个缺点,其后端健康检查通过检查服务端口实现,对于那种服务进程仍然存活,但服务很繁忙的服务没办法检测出来(比如文章开头提到的网络队列满的情况),要想提供这种应用层面的容错,可以将rc server的ip地址列表给客户端,并在客户端实现主动failover,即在连接不上某个rc server(比如网络访问超时),尝试列表里的其他rc server来实现容错。