刚刚毕业进入工作岗位第一个任务是重构分布式缓存的console。不过重构是后面的事情,起因是对console的rebalance算法做简化。如果你有兴趣可以翻看我第一篇blog,里面解析了新rebalance算法。算法来自于豌豆荚的codis,为了搞懂他的思想,我看了codis rebalance的源码,并在github上跟向作者提问,终于完全搞懂了。下面会记录一下重构这个项目的过程。
首先说明一下缓存console的功能:
- console监听zk group节点的子节点数量,响应group数量的改变;
- 如果增加group,则计算出新的负载情况,即group -> buckets的对应关系,以二维表的形式存在zk上,二维表的行是group,列(index)是桶号;如果group减少,则出现宕机情况,更新二维表;
- 控制client能看到的二维表,下面会解释的
- 控制migration的结果和client读写权限
- 响应zk state change,即zk Disconnected SyncConnected状态,以及主备切换
console主要功能是控制。二维表是console的核心数据结构,重构前他的结构是第0行为缓存主机,第1行第一个备机,第二行是第二个备机,以此类推。纵向看index是桶号,0号桶的主备机,1号桶的主备机,以此类推。切换成组之后的不同是一个组包含了主机和备机,而对于缓存client来说他只能看到主机,所以,新的二维表只有一行,表中的内容从机器地址扩展为group对象,增加了字段和功能。
上面罗列的console功能中多次出现不同功能的二维表,在zk上有三张不同功能的二维表,他们共同为client、console、引擎提供组信息。第一张是c表,是client看到的组的分布情况;第二张是m表,存放“旧”数据;第三张d表存放“最终结果”数据。
新算法对增加组进行rebalance计算,若是宕机则需要人工介入,重启宕掉的组。作为组出现,即主备机同时宕掉的几率极低,如果主机宕掉,备机顶上来。新增组后,做rebalance,其算法思想很简单明了,很好理解。根据上来组的内存大小按比例分配多少个桶(bucket or slot)“落在”哪个组中。越大的组分配越多的桶。这个前提是认为各个桶的分配是均衡的。算法详情点击这里。
思路很清楚,需要新增两个zkChildListener,分别监控组和迁移结果的变化。通过对比二维表和zk上组路径下子节点的数量和子节点的内容,就可以判断有哪些组是新增的、哪些组宕掉了。如果当掉了,三张表中对应位置组置为0。client看到0会报错,console同时也会在宕机逻辑中报错,且还会有一个线程持续检查是否有宕机情况。组减少的处理情况很简单,不过也是最合理的。因为尽管宕机这种事情“必然”会发生,但频率很低,用人工介入代替复杂的算法逻辑是合理的。不过增加组的逻辑就稍微麻烦一些。
最正常的情况是新增组是三张表是相同的,没有宕机和迁移这两种情况。那么正常rebalance,将“最终结果”写入d表,m表中就变成了“旧”数据,将c表中迁移的源组置为只读。例如(1 : 192.168.0.1:33661, 2 : 192.168.0.2:33661....) : m表111222,增加3,d表变为113223,需要迁移的是2、5号桶、迁移的源和目的也能得出。
如果正在迁移,新增组这种情况不会发生,因为这里有其他模块控制这种异常情况,但确实是一种异常情况。这种异常很复杂,需要处理很多复杂逻辑。
如果是有宕机的情况,新增组,这种异常的思路是“填回去,不计算”。既然有空位为什么不直接填回去,干嘛还要rebalance,原先也一定是平衡状态。宕机也分为两种,一种是旧组宕掉,一种是新组宕掉。其区别是对不同的二维表做操作,原因是m、d表的数据不相同。
迁移就更简单明了,你也应该能感觉到,对比m、d表计算出需要迁移,监控migration节点子节点变化,引擎完成一个桶的迁移,写一条记录,触发一次计算。迁移结束更新三张表。
不过以上这些结论都是经过几次做错、再重构后得出的,毕竟架构师不会把太多的时间放在我身上。。。
关于第5点功能,我遇到了zk的bug,如果你也遇到过可以交流一下。console的主备是依靠注册到zk上的顺序决定的。会在一个临时节点中存master的值。但这个临时节点在zk server停掉后没有消失,随即报错。这个问题我还要测一下。。醉了,其他的都蛮简单的就略过了。。
----------------------------------------------------------------分割线--------------------------------------------------------------------
这个并不是zk的bug,测试时因为机器宕机,但在zk服务端,旧的session还没过期,session的过期时间是30s,造成临时节点没有释放。其实规避方法很明显有两种,一是重连间隔 > 30s,二是创建节点之前要判断节点是否exists。
由于是公司的项目需要保密,只能说一下思想,源码就不贴了,其实贴了也没太大的意义,除非把,,哦,对了,这个分布式缓存已经开源,但新console还没开源,希望感兴趣的同学能在github上跟主创交流,开源代码点击这里。