张胖子所在的公司这几年发展得还不错,业务激增,人员迅速膨胀。一转眼,张胖子就成了公司的“高级”员工。更重要的是,经过多年的不懈努力,他终于坐上了建筑师的宝座。不过很快胖子就发现,这个建筑师还真的不好当。技术选型、架构设计,尤其是大家无法处理的技术难点,最后还得自己来进行。沟通、说服、妥协,甚至争吵都是家常便饭,这比我刚做开发的时候难多了。公司的IT系统已经从单机向分布式转变,分布式系统带来了巨大的挑战。周一刚上班,张胖子的邮箱里已经塞满了紧急邮件。1小亮的邮件小亮的邮件提到了一个RPC调用的问题。本来公司的架构组自己开发了一个RPC框架给每个团队使用,但是每个开发团队都抱怨:这个RPC框架不支持动态服务注册。并发现。张胖子一看这张图就明白是怎么回事了。为了支持高并发,OrderService是四副本部署的,每个客户端保存一个服务提供者列表,但是这个列表是静态的(在配置文件中如果服务的提供者发生变化,比如有的机器宕机了,或者添加了一个OrderService的实例,客户端根本不知道,可能还在傻傻地尝试那些已经坏掉的实例!如果想得到最流行的服务商的URL列表,就得手动更新配置文件,实在是太不方便了。对于这样的问题,胖子马上意识到,这就是客户端和服务提供者之间的紧耦合,如果要去掉这种耦合,就必须加一个中间层!张胖子认为应该有一个注册中心,首先给这些服务起个名字(比如orderService),然后那些OrderServices就可以在这里注册,客户端就可以来这里查询了,给个名字orderService就可以了,d注册中心可以给Create一个可用的url,再也不用担心服务商的动态增减。不知道是不是下意识的行为,张大发把这个注册中心的数据结构设计成树状结构:/orderService表达了一个服务的概念,下面的每个节点代表一个服务的实例。比如/orderService/node2代表的订单服务的第二个实例,可以在每个节点上记录这个实例的url,以便于查询。当然,注册中心必须能够与每个服务实例通信。如果一个服务实例不幸挂掉了,它在树结构中对应的节点也必须被删除,这样客户端就无法查询到它。那么,你可以直接在注册中心和各个服务实例建立会话,让各个服务实例周期性的发送心跳。如果一定时间后还没有收到心跳,则认为服务实例已经死了,会话就过期了。将其从树中删除从结构中删除。张胖子把自己的想法回复给了小梁,接着看了小王的邮件。2小王的Master选举小王的邮件中提到了三个BatchJob的协调。这三个BatchJob分别部署在三台机器上,但是三个BatchJob一次只能运行一个。如果一个不幸宕机,剩下的两个需要被选举出来,被选中的BatchJob需要“继承遗产”继续工作。其实这是选主子的事情,张大发一眼就看出了本质。光是选举Master,这三个BatchJob都需要相互通信,相互协调,麻烦啊!如何获取数据库表?利用数据库表主键不能冲突的特性,让三个BatchJobs向同一张表插入相同的数据,谁先成功谁就是Master!但是如果抢到Master的BatchJob死了,其他人就永远抢不到了!因为记录已经存在,其他BatchJobs无法插入数据!那么,我们必须添加一个定期更新机制。如果一段时间没有更新,Master就被认为死了,其他BatchJob可以继续抢……但是这样做很麻烦!换个思路,让他们也去一个登记处喊:“我做主!”谁的声音最大,谁就是大师。事实上,这不是喊叫。三个BatchJob启动后,都去注册中心争先恐后地创建一个树节点(比如/master)。请求失败),另外两个BatchJob会监控这个节点。如果删除该节点,将开始新一轮创建/master节点的竞争。什么时候删除节点?是的,现在的Master的机器宕机了!显然,注册表还需要与每台机器进行通信以查看它们是否存活。等等,这里还有一个复杂的情况。如果机器1没死,但是长时间连接不上注册中心,注册中心会发现session超时,会删除机器1创建的/master,让机器2和机器3竞争。如果机器3成为master,开始运行BatchJob,而机器1并不知道自己已经被解除Master的位置,还在努力运行BatchJob,这就是冲突!看来机器1肯定能感知到与注册中心的连接断开了,需要停止batchjob。重新连接注册中心后,就会发现自己不再是master了。老实等待下一次机会。.不管是什么方案,实现起来都非常麻烦,这该死的分布式!我先回复小王。然后看小蔡的邮件。3小蔡的分布式锁小蔡邮件中提到的问题比较麻烦。有多个不同的系统(当然是分布在不同的机器上!),它们需要对同一个资源进行操作。如果这是在一台机器上,可以用某种语言内置的锁,比如Java中的synchronized,但是现在是分布式的,程序在不同的机器上运行在不同的进程中,所以synchcronized根本没用!这是一个分布式锁问题!能不能考虑一下Master选举问题中的方法,让大家去抢?谁能先在注册中心创建一个/distribute_lock节点,就说明他抢到了锁,然后再读写资源。读写完毕,删除/distribute_lock节点,大家再抢。但是在这种情况下,某个系统可能会多次抢夺,这是不公平的。如果让这些系统在注册表的/distribute_lock下创建子节点,然后给每个系统一个编号,它会是这样的:然后每个系统检查自己的编号,谁的编号小就认为谁持有锁.例如系统1。系统1持有锁,可以操作共享资源。操作完成后删除process_01节点,新建节点(编号变为process_04):其他系统看到编号01被删除。看看谁最小,是process_02,那么就认为系统2持有锁,可以操作共享资源。操作完成后,process_02节点也应该被删除,并创建一个新节点。此时process_03最小,可以持有锁。如此循环下去……分布式锁就可以实现了!看,我设计的集中式树??结构很好,可以解决各种问题!张胖子不禁得意起来。好吧,先把这个想法告诉小蔡,下午我们开会讨论具体的实施细节。4当Zookeeper正要回复小菜的时候,胖子突然意识到自己漏掉了很重要的一点,那就是注册中心的高可用。如果注册中心只有一台机器,一旦挂掉,整个系统就完了。这个注册中心也必须有多台机器来保证高可用性。自己比较得意的树结构,也需要在多台机器之间进行同步。机器挂了怎么办?通讯超时怎么办?树状结构的数据如何保证机器间的强一致性?小王、小梁、小蔡原来的问题还没有解决,光是注册中心就致命了。以我们公司的技术实力,搞出这样的注册中心简直是不可能的任务!大胖赶紧上网搜索一下,看看有没有类似的解决办法。幸运的是,有一个叫做Zookeeper的!Zookeeper使用的树结构和我想象的很像。更重要的是,人们已经实现了树结构数据在多台机器之间的可靠复制,实现了多台机器之间数据的一致性。而如果这多台机器中有一部分因为网络原因挂掉/或无法连接,整个系统仍然可以工作。胖子,赶紧看看Zookeeper的关键概念和API:1.Session:表示一个客户端系统(比如BatchJob)与ZooKeeper的连接会话。BatchJob连接到ZooKeeper后,会定时发送心跳信息。如果Zookeeper在一定时间内没有收到心跳,就会认为BatchJob已经挂掉,Session就会结束。2.znode:树状结构中的每个节点称为一个znode,按类型分为三种:绝对znode(除非主动删除,否则会一直存在)、临时znode(会被删除会话结束后)和顺序znode(即小菜分布式锁中的process_01、process_02.....)。3.Watch:客户端系统(如BatchJob)可以监控znode,以及znode节点(删除,数据修改等)可以通知BatchJob,让BatchJob做出相应的动作,比如争先创建节点。嗯,这些概念和接口应该符合我们的要求,就这样吧,我们调用下午开会,开始学习Zookeeper。后记:本文从用户的角度描述了Zookeeper的用处。至于它内部是怎么工作的,那就是另一个Big话题了,以后再说。【本文为专栏作家“刘欣”原创稿件,转载请通过作者微信获取授权公众号coderising】点此查看该作者更多好文
