汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪汪。
没保存啊,都是泪啊。
前言
心好累啊,又要写一遍。
从去年初次接触Zookeeper到现在,正好一年了,一直没总结,一方面也是因为自己对分布式系统理解很少,没有什么可说的,如果只是描述一下zk提供的几个原语实在没什么意义。(另一方面就是懒)。
感觉自己终于对分布式可以说是有一定的入门了,所以赶紧记录下自己的成长。
正文
我接触zk相对来说是挺缘分的,因为C++出身,对于Hadoop家族了解甚少,只是在项目中对于模糊的“分布式协同”而找到了zk。就像一场仓促的恋爱,摸不准对方的脾气, 闹出了很多尴尬(够了)。
明天继续。。。
什么是zk
zk是hadoop全家桶的一员,后来独立为apache的顶级项目,是google的chubby的开源实现。
很高大对吧。。。但是说了好像没说。。。
通俗的理解,在一个分布式系统的开发中,我们会面对很多共同的问题,比如leader选举/分布式锁,集群管理等等等等,就像后台开发,都会需要序列化/翻序列化,日志等功能,如果所有事情都自己去实现,那么不仅低效,而且可能会遇到很多坑。
zk就是一个解决分布式协同的轮子,让开发者能专注于业务逻辑的实现,同时避免了很多坑。
作为一个广泛使用的轮子,不光要能正确高效的使用,同时还可以通过它学习到很多知识。
zk能做什么
zk主要是用来进行分布式协同。在Hadoop项目中,有很多动物名字命名的项目,想要管理好这么多动物(进程),自然需要一个动物园管理者(zookeeper)。
这里的协同包括提供分布式锁, 崩溃检测, 元数据存储,服务发现等功能。
如果只单纯来看zk的API,可能会不知所云,像目录树一样的znode?创建节点?删除节点??而上述的功能没啥关系啊。。。
对client来说, chubby直接上菜,提供加锁,释放锁等api,而zk相当于提供了菜的原材料(create node, delete node等),由client自己组合为各种想要的功能,当然,对于一个会做菜的人来说,后者更加open,想吃啥自己做就行了。
但是对于分布式初学者/不会做菜的人来说,面对一堆原材料就很尴尬了。
当然,和现实,初学者需要一份菜谱来尝试做菜了,随便胡乱做菜。。。。小心炼出丹来。(为什么写这段时总想到桂纶镁 “要不要和我学做菜啊~” )
啥叫菜谱
官方给出了一系列功能的实现方法,称为菜谱(recipes)[1]。
推荐学习菜谱来感受做菜。。。啊不,zk。。。
当然,这里我们也举一个实际的例子来讲解菜谱。
菜谱之Leader选举
最简单的方法就是使用 SEQUENCE|EPHEMERAL(有序|短暂) 的znode来代表参与选举的client的proposals(这个词不翻译,相信我,只要你学分布式,这个词绝对会出现N次的)。具体步骤如下
1.所有参与选举的client在一个指定的目录下创建属性为SEQUENCE|EPHEMERAL的znode
2.seq最小的client即为leader,其他的client为follower,同时watch Leader的znode。
3.一旦Leader因某种原因挂了,其他的client会收到通知,开始新一轮的Leader选举。
然而这种方法并不完美。Why?
让我们明天继续~
转眼就是明天的明天的……好多个明天了..自己挖的坑还是要填上啊~
为什么不完美呢,让我们想一下上述操作的具体步骤是如何操作的.
对于每个客户端,逻辑如下
// .........
// 主函数
my_node = zk.create("/leader/clientId-", "SEQUENCE|EPHEMERAL")
all_nodes = zk.getChildren("/leader")
if(judgeMinSeq(my_node, all_nodes) == true)
doMaster();
else
doSlave();
// .........
void doSlave()
{
// 设置watch点
//slave正常逻辑
}
void watchpointCallback()
{
all_nodes = zk.getChildren("/leader")
if(judgeMinSeq(my_node, all_nodes) == true)
doMaster();
else
doSlave();
}
当Leader挂掉时,所有的slave都会收到通知,调用callback检查自己的znode的seq是否为最小可以当选为下一个Leader.
这就导致了 herd effect(牧群效应,个人感觉很类似于epoll的thundering herd).所有的slave都调用getChildren,当slave数量很多时,大大影响了整个集群的效率.而且这样并没有什么意义,毕竟顺序已经固定了.除了真正的下一任Leader外,其他的调用都是无用的.
zk的这种Leader选举方式,其实是带有隐含的继承顺序,换句话说并不是battle一轮决出C位,更像是皇位继承,要从太子…五阿哥….十三阿哥(你看,这也是带上了Seq)…按照顺序来选出继承者.
那么如何改进呢?
就像之前我们说的那样,对于太子来说,他需要关心的是皇上是否挂了,只有皇上挂了他才能登基,而对于后面的阿哥来说,只要太子没挂,即使皇上挂了,下一个皇位也轮不上自己,那么只需要关注比自己更年长的那个阿哥….
好,我知道把你绕晕了,对于每个slave来说,在第一次Leader选举getChildren时,只要关注刚好小于自己seq的znode就行了.
比如5节点
[0,1,2,3,4]
那么第一次选举之后
0为Leader
0<-1 1watch0,0挂了1就是leader
1<-2 同上
…剩下继续同上
当然,一旦触发事件(watch的znode挂了),并不是想当然的认为自己是Leader,仍要调用一次getChildren确认现有Leader是否存活(或者说确认自己的seq为最小)
可以看出zk的菜谱博大精深,我们这里拿出Leader选举作为最简单的例子来讲解,就有很多可以说的.在设计自己的应用时还是要多多用心,比如用zk实现一个分布式互斥锁也和Leader选举很像啊,你能将Leader选举这个菜谱改一改,改成你需要的锁吗?
和etcd的区别
注册中心??
参考
http://jm.taobao.org/2018/06/13/%E5%81%9A%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%EF%BC%9F/
https://yq.aliyun.com/articles/227260
http://www.infoq.com/cn/articles/background-architecture-and-solutions-of-service-discovery
http://zhangtielei.com/posts/blog-redlock-reasoning.html
我要是明天没写完我就是狗