引言
前不久看到一篇文章,如何做一个靠谱的发号器,觉得是个挺好的设计,于是使用Java实现了一番。
Fly
发号器,取名fly的意思是,像有无数飞虫一样,不断产生,取之不尽。查看项目地址。
持久化
UID的生成记录会写入ZooKeeper,服务初始化/重启时从ZooKeeper读取配置信息。使用初始化作为持久化的原因是,ZooKeeper是强一致的,在全局单调ID的生成模式下,如果主服务器宕机,可以切换备服务器工作,备服务器从ZooKeeper读取配置,保证发出的ID是唯一的。
如果采用MySQL和Redis产生ID的实现,由于MySQL和Redis的复制机制无法保证强一致,当MySQL或Redis发生主备切换,备机尚未完全同步的话,还是会发出重复的ID。
通信协议
实现TLV格式的协议。
高可用方案设计
对于全局单调ID的生成,需要使用主备模式;对于全局唯一ID的生成,主备模式和负载均衡模式均可实现。在实际使用时,我们倾向于全局单调ID使用主备模式,全局唯一ID使用负载均衡模式部署服务。
主备模式
架构图
选主
部署服务的实例会在zk进行master节点抢占,抢占成功的实例会成为master,执行id的生成和分配。其他服务器会成为备服务器,执行客户端请求的转发,保障高可用。
ID的生成规则
全局单调性,目前实现是一个不断递增的整数。
主备切换
当前运行的服务端会写到zk里,如果异常/下线将触发事件通知其他运行实例进行抢占执行。注意,因为服务端实现了转发,对客户端来说是无感知的,当客户端连接原master异常时,会自动尝试连接其他服务地址。
缺点
服务非负载均衡。
负载均衡模式
架构图
部署
所有服务实例都是平等的对外提供服务。没有主从之分。
ID的生成规则
基于Twitter的SnowFlake算法,具体规则:参考
服务宕机
客户端自动尝试连接其他服务地址。
缺点
UUID的取用非全局单调。
主要代码
FlyService
接收处理客户端请求,默认端口是8888。对于当前非master的服务实例,将执行转发请求到master服务实例。
ForwardService
提供转发服务。具体实现是维护到master的连接池,同时当有请求发出的时候,将异步结果获取转为同步。
StateMachine
维护当前服务实例的状态,是否是master,是否avaliable等。当有master状态转变时,需要实时更新资源。
FlyManager
负责与zk的交互,执行master抢占和监听。
TODO
- 考虑到如果zk服务出现异常,我们可以降级到通过数据库实现UUID生成。
- 转发协议,压测转发处理的性能,以及连接池的优化;客户端连接管理。
- 完成客户端实现。