服务leader-slave切换
一个服务,如果很重要,一般需要对其进行主备部署,状态为主的节点对外提供服务,从节点时刻待命,等待主节点故障而取代成为主节点对外提供服务。如何高效准确的进行主从切换很重要。目前会发生主从切换的场景有:
- 主节点程序主动退出
- 主节点异常退出
- 主节点与etcd网络异常
主节点程序主动退出
这种情况好处理,节点在退出之前删除在etcd上注册的节点,从节点watch到注册节点的变化,竞争成为主节点
主节点异常退出
异常退出,程序不能正常删除在etcd上注册的节点,从节点就不能感知主节点异常。因此,节点在注册时,需要设置ttl时间,主节点与etcd进行keepalive保活,异常后超过ttl时间没有保活心跳,etcd就会主动删除注册节点,从节点就会感知从而成为主节点。
主节点与etcd网络异常
网络异常的情况最难处理,因为虽然设置了ttl,并定期保活心跳,这种方式可以通知到从节点竞争成为主节点。但是原先的主节点还是在运行,因为与etcd网络异常,该节点不知道当前的主节点信息。一个简单的解决方式是,在保活心跳时失败时,服务直接点退出为从节点,避免新的当选的主节点与原来网络异常的主节点同时工作,但这可能会导致服务非常不稳定,主从节点会更频繁的切换。
我们可以使用etcd对ttl封装一个组件lease,来实现这个功能。
func (clientv3.Lease).KeepAliveOnce(ctx context.Context, id clientv3.LeaseID) (*clientv3.LeaseKeepAliveResponse, error)
type LeaseKeepAliveResponse struct {
*pb.ResponseHeader
ID LeaseID
TTL int64
}
使用接口KeepAliveOnce来做保活,并返回该lease剩余的过期时间ttl,我们根据剩余ttl计算预期的过期时间,保存在本地,然后定时坚持当前时间是否超过预期的过期时间,这样我们就可以准确的知道lease何时过期,然后及时做主从切换,避免主从切换的步骤依赖于外部的etcd