当前位置: 首页 > 科技观察

迄今为止最好的ZooKeeper入门文章

时间:2023-03-20 21:12:18 科技观察

大家好,今天我来说说zookeeper。其实很早就打算写一篇关于它的文章,但是由于种种原因,一直拖到了今天。熟悉《编程新论》公众号的读者都知道,账号主人在介绍一个东西的时候,是不会直接透露的。动物园管理员就是这样。相反,它会以类比的方式逐步、逐层发展。本文依然采用这种风格。大家坐好,让我们开始一场脑力之旅。边界诞生与突破不知不觉,孩子已经上小学了,前段时间我还参加了一次家长会。就拿大家耳熟能详的学校和会议来说吧。如果一个班级要开班会,可以随时召开,无需事先安排和通知,因为班级内部是一个整体,在班级内部,学生和老师可以自由交流,没有任何延误。任何障碍和障碍。一个班级对外是一个独立的实体,因为班级之间是完全独立的,所以一个班级的学生和老师不会随便去别的班级。这是因为有界线,阶级界线。正是这个阶级边界将阶级分开。界内的事情,比如班会,可以随便进行,因为与界外的一切都没有关系。但是到了越界,也就是越界的时候,问题就来了。比如学校要开全班会,肯定会提前安排好时间和地点,还有操场上每个班级的顺序,也会提前给出相应的通知。为什么一个班级的班会可以随时随地召开,而一般的班会却必须提前安排和通知?因为它跨越了类边界,是一个跨边界的问题。而且各个班级相互独立,彼此不熟悉,相互交流起来可能不太方便,所以需要提前安排。如何通知?可以让班级互相通知,比如班级通知班级二,班级通知班级二等。也可以由一个独立于所有班级的人轮流通知所有班级,比如教务处或学生处的人。这两种通知方式都是现实中使用的,所以没有绝对的好坏,看情况而定。读者需要明确,这两种方法代表了处理此类问题的两种方式,一种是通过独立个体之间的直接沟通来解决问题,另一种是通过第三方的介入来协调解决。在这里我们可以得出一个结论,边界的产生是一种自然现象,通常边界不会被打破或消失,但边界两边的事情可以通过其他方式进行沟通和协商,充其量是一种《突破》吧。计算机相关边界的创造与突破上一节的描述很简单,相信大家也能看懂。接下来说说计算机相关的边界。其实还有很多,我们就说一两个吧。操作系统中有内核空间和用户空间,它们之间是有界限的,但是它们之间还是可以通信的,因为操作系统的开发者已经准备好了通信方式。每个应用程序通常都是一个进程,因为应用之间通常会有很大的差异,还有其他的考虑,比如安全问题,所以进程之间是有边界的,也就是进程边界。操作系统按进程分配资源,因此进程内的线程共享这些资源。由于进程边界的存在,这些资源不能被其他进程使用。所以过程就像一个类。由于不同的进程通常不需要通信,就像类通常通信不多一样,所以默认情况下进程之间是不能通信的,这与操作系统的内核和用户空间不同。但总有特殊情况。如果进程之间需要通信怎么办?只能由开发者自己实现,比如通过Socket。这种情况在中间件中很常见,比如Nginx,涉及到多个进程。因为中间件开发人员一般都是好人,他们可以搞定。但问题是,绝大多数开发者从事的是业务开发,往往受限于能力、时间或金钱的限制,无法做出生产级的通信方式。但是有时候业务人员开发应用程序的过程也是需要交流的,就像开班会一样,举一反三,想办法解决。我们可以让进程直接相互通信,就像类相互通知一样。一方面,这对开发人员来说既费时又耗时。另一方面,当进程比较多的时候,它们之间的直接通信就变成了网状的,会很乱。为了说明这一点,让我们看一个简单的例子。假设张三、李四、王五是同事,他们周五下午下班时互相穿错了衣服。不幸的是,他们只有在晚上回到家时才会发现。第二天,也就是星期六,他们都想改。张三要找李四,李四要找王舞,王舞要找张三。假设他们都住得很远,这将是一个相当复杂的问题。如果有20个人都穿错了衣服,那问题就更复杂了。可见,如果个体直接相互交流,随着个体数量的增加,会变得极其混乱和复杂。可能大家已经想到了更好的解决方案。也就是约定一个合适的地方,比如一个公司,张三、李四、王舞都去那里,换好衣服之后就回家了。随着个体数量的增加,这种方法会越来越好。这种方式其实就是全班开会,第三方无关人员介入协调处理时通知的第二种方式。这个时候,第三方就是教务处或者学生处。多进程相互通信的解决方案也是如此。第三方无关进程介入以协调处理。这个时候第三方就是ZooKeeper。这种方法的另一个好处是,它在一定程度上降低了复杂性和对个人的要求,以及由此产生的额外问题。比如有的班主任脾气不好或者不善言辞,其他班的班主任都不愿意通知他。这个时候教务处的工作人员会通知他,就不会出现这个问题了。对于流程,降低了对业务开发人员的要求,不需要具备完整的进程间通信相关知识,同时降低了流程本身的复杂度,不需要支持完整的进程间通信,并且可能只需要支持客户端。能。这种方式还有一个好处就是可以抽象出来做成一个独立的中间件供大家使用。ZooKeeper就是这样。所以本质上,ZooKeeper是一个第三方,也叫中间人,搭建了一个平台,所有其他进程都可以通过这个平台进行间接通信。ZooKeeper的数据模型计算机实际上是用来处理或存储数据的,其上运行的大部分软件也是如此。zookeeper作为多进程的协调者,肯定跑不起来。存储数据和放置物品一样,不能随意乱扔,占地方,不好看,也不好找。所以必须有一定的等级制度。这是计算机专业课的数据结构。最简单的数据结构是数组或链表。它们被称为线性表,它们是一维的,具有线性关系,即前后顺序。优点是简单,缺点是不够强大。然后是树,可以认为是二维的,左右是兄弟,上下是父子,是从属关系。它是一个兼具功能和复杂性的结构。现实生活中的各种组织结构大多是树形的。比较复杂的是图,它是一个网络结构,可以认为是多维的。由于可以连接任何节点,因此表示多边关系。虽然功能强大,但它也很复杂。现实中,铁路网和人际关系网大多是网状的。当然,这是三大类的数据结构,每一类又可以分为很多种。例如,有许多种类的树。虽然都叫作树,但有些还是有很大区别的。ZooKeeper选择树作为存储数据的结构。其实和文件系统很像,如下图所示:说到数据,离不开增删改查。对于树来说,adding就是在树中增加新的节点,delete就是从树中删除一个节点,modify就是修改树中某个节点上存储的数据,search就是在树中找到一个节点读取存储在其上的数据。说白了,树表示就是一种结构,真正的数据放在节点上,要么是叶子节点,要么是非叶子节点。ZooKeeper应该具备的能力我们从最常见的场景入手,从宏观上了解ZooKeeper是如何使用的,它应该具备哪些能力。场景一:有两个应用进程A和B,A先处理数据,处理完后通知B,B继续处理。我们应该如何使用zookeeper来完成这个呢?我们一起来分析一下。首先,进程A连接到zookeeper,并在其上创建一个节点来代表自己的存在。假设节点名称是foo。然后在节点上设置一个数据叫doing,表示正在处理数据。稍等片刻,更新节点上的数据就完成了。这样,进程A的工作就结束了。但这对进程B有何影响?我们知道zookeeper完成的是进程间的间接通信,即进程之间不相遇。因此,只能使用这棵树中的节点。进程B也需要连接zookeeper,然后找到foo节点,看看上面的数据是否由doing变成了done。如果是,它将开始自行处理数据。如果没有,它将继续等待。问题是B进程不能自己一直盯着foo节点,太累太折腾了,还得干点别的。那么这件事情应该由谁来做呢?显然是动物园管理员。于是进程B对zookeeper说,你替我关注一下foo节点,完成了通知我,我就开始工作了。所以zookeeper需要有跟踪和通知其他进程的能力。这对应zookeeper中的一个专业术语叫Watch。Watch的作用和用法同上文所述。即进程B找到foo节点,并在其上放置一个Watch。这样zookeeper就知道进程B比较关注foo节点,于是zookeeper盯着foo节点,一旦有什么麻烦就第一时间通知进程B。备注:关于Watch的细节很多,这里就不说了。需要注意的是,这款手表是一次性的,即只能使用一次。也就是说,zookeeper通知进程B后,Watch用完了,以后不会再通知了。如果还需要通知进程B怎么办?很简单,在foo节点上新建一个Watch即可。长此以往,可以保证一直通知到。我觉得之所以把这个Watch设计成一次性的,是zookeeper不想让自己太累。睁着眼睛盯着太多东西看太久确实很累。另外,zookeeper在通知进程B时,也可以发送foo节点存储的数据。细心的朋友可能已经发现zookeeper可以主动给进程B发送通知或者推送数据,说明zookeeper和进程B之间需要一直保持连接。因为进程B的位置比较随意,本来就是一个业务进程。一旦断开连接,就像断线的风筝一样,Zookeeper就再也找不到进程B了。但是,Zookeeper的位置是固定的。一旦连接断开,进程B可以再次向zookeeper发起连接请求。如果断开连接的时间足够短,进程B应该能够检索它曾经在zookeeper上拥有的所有内容。这里涉及到session,所以zookeeper也需要有一定的session延续能力,这样在断线时间不长的时候,方便找回原来的session。所以zookeeper应该具备节点监控、通知进程、长连接维护、会话续传的能力。场景二:有时候为了高可用或者高性能,通常会运行一个应用的多个副本。如果跑四份,就有四个进程,分别是A、B、C、D,当一个call过来,发现A、B、C、D可以调整,那么就可以根据选择一个call到配置的负载均衡策略。假设D进程所在的机器不幸掉电了,实际上D挂了,那么此时再调用,会发现只能调整A、B、C,而D会自动消失。这其实是Dubbo的一部分功能,那么基于zookeeper如何实现呢?我们照例一起分析。由于zookeeper是基于树形数据结构的,所以还是要说说节点。进程A启动时,需要连接zookeeper,然后创建一个节点来代表自己。节点名称和节点存储的数据可以根据实际情况确定,至少包括运行进程的IP和端口信息。进程B、C和D做同样的事情。如果进程A、B、C、D的节点都位于同一个父节点下,那么调用时,只要找到父节点并读取其所有子节点,就可以调用所有进程信息了得到。如果在某个时候,进程D挂掉了,那么父节点下进程D对应的节点应该会被zookeeper自动删除。zookeeper中有一个术语叫做临时节点。那么对应的自然就是永久节点了。其实工作过程是这样的。业务流程启动后,与zookeeper建立连接,然后在zookeeper中创建一个临时节点,写入自己的相关信息。然后通过周期性的心跳保持与zookeeper的连接。一旦业务进程挂掉,zookeeper将收不到心跳,一定时间后,zookeeper会删除对应的临时节点,表示业务进程不再可用。Dubbo的做法是将接口名称、IP端口信息和我们设置的信息整合成一个类似URL的字符串,然后用这个字符串作为名字来创建一个临时节点。临时节点不允许有子节点,只有永久节点可以。这篇文章的内容非常简单易懂,所以即使你是刚刚接触zookeeper的朋友,看到这里也算是一个介绍吧。如果你想更深入,那就是细节。作者是一名码农,从业10余年,现为架构师。喜欢研究技术,崇尚简单快乐。追求用通俗易懂的语言讲解技术,希望所有读者都能理解和记住。