zookeeper 分布式过程协同技术详解.pdf_Zookeeper分布式过程协同 part III

Zookeeper Part III ,这次直接开始使用Zookeeper.(月更很愧疚) 具体的安装介绍就不赘述了,网上有关zookeeper安装的教程有很多.

开始之前,让我们再次确认一下zookeeper到底是做什么工作,它能做些什么。

zookeeper是分布式协调通知工作的一致性框架,就是多个服务模块在分布式系统中,多个系统之间的的通知调度,需要有一个协调调度的通知机制,保证全部的提供一执行服务的软件,这就是zookeeper,类似班主任。

班主任手中有一个花名册,册中记录了每位同学的信息。假设你向班任请求寻找靠近后门的倒数第三排的第6位同学,此时班主任也未必能想到那个人到底是谁。

但班任手中有一份服务的注册列表,班中的每位同学都在这个花名册中进行了注册,每个注册的同学都相当于一个系统中的服务,这样保证了之后的互相调用,达到通信的效果。

设计模式来理解Zookeeper:Zookeeper是一个基于观察者模式设计的分布式服务管理框架,负责存储和管理重要的数据,然后接受观察者的注册,一旦被记录的数据状态发生了变化,Zookeeper就负责通知那些在Zookeeper上注册观察者们作出相应的反应,实现类似集群中类似 Master/Slave 管理模式。

ea9fa45cfc4f62280591c7ce663ac1de.png文件系统

Zookeeper也是一个倒装的树形结构文件系统(同理linux文件系统)

Zookeeper能做什么:

  1. 命名服务:Zookeeper的每个节点就是一个服务。

  2. 配置维护:要修改90个系统的90个jdbc.properties中的网段,也就是修改jdbc.properties中的url地址,Zookeeper一次修改,处处生效。

  3. 集群管理:类似redis的三个集群(主-从、Sentinel、cluster),Zookeeper也有自己的集群管理。

  4. 分布式消息同步和协调机制:举个例子,用户下订单,会影响:用户customer模块、库存管理、积分、产品模块,订单生成,库存减少,积分上升。什么时候将这四个模块联动?四个模块会监控Zookeeper中树形节点的变化,节点的变化就是消息同步的“绿灯”。

  5. 负载均衡:虽然能做,但多用nginx(专人的人做专业的事)。

  6. 对Dubbo支持。

  7. Zookeeper提供了很好的分布式集群管理机制,它这种基于层次型的目录树的数据结构,并对树中的节点进行有效管理,从而可以设计出多样的分布式数据管理模型,实现分布式系统的沟通调度。

ea9fa45cfc4f62280591c7ce663ac1de.png第一个Zookeeper会话

检查zookeper是否连接成功:

9d2b6f6ad787270eaee79cd318faf1cc.png

ca8969939b7f948a36b4c9d8eae0d1ca.png

92d099eded25c916b6fc81d7a4fb0b51.png

输入./zkCli.sh命令之后,命令行会发生改变。(使用help命令查看操作命令

找到根节点[zookeeper]

ls /

79d03f51cdf94c10eb96cf64b53a95e7.png

发现无法通过cd命令进入,它其实是一个服务节点,而不是文件夹。

(CONNECTED) 后面的数是为了回退的时候方便,所以每回车一次就增加1。

这样就知道了,最左边的节点挂着一个节点,而zookeeper节点后又挂着quota节点。

之后的服务节点过来注册,也就是去quota上注册。(类似linux的树形菜单结构)

服务器四大操作:启动、连接、断开、关闭。

ade97b6838ad286b27932e26a5a5608c.png

373cfa174156b1c340d442c9cfee1f58.png

使用get命令获取testNode节点的data

ceee0b0095c5c2c1932c67e6965ccfe4.png

使用delete命令删除testNode节点

fac26b783b02b1a74666a6036d4e0767.png

Znode维护了一个stat结构,stat包含数据变化的版本号、访问控制列表变化、时间戳等。版本号和时间戳一起让Zookeeper验证缓存和协调更新。

adb993c472cb665b1bc403074c1780e8.png

9f2e6e7eeccddb89e7badd5e6a9d4776.png

-s 代表附带序列号的节点-e 代表临时节点-p 持久节点(默认)

78beeb9b18071c741c01c68efb546854.png

ea9fa45cfc4f62280591c7ce663ac1de.png zkCli常用命令

zkCli的命令和操作redis的KV键值对类似,区别是zookeeper中的key是一个路径节点的值,value是一个data。

ea9fa45cfc4f62280591c7ce663ac1de.png 独立模式运行Zookeeper

以独⽴模式运⾏ZooKeeper并创建⼀个会话。要做到这⼀点,使⽤ZooKeeper发⾏包中bin/⽬录下的zkServer和zkCli⼯具。有经验的管理员常常使⽤这两个⼯具来进⾏调试和管理,同时也⾮常适合初学者熟悉和了解ZooKeeper。

假设你已经下载并解压了ZooKeeper发⾏包,进⼊shell,变更⽬录(cd)到项⽬根⽬录下,重命名配置⽂件:

1c4bb384c3093fc742ada34773a28a62.png

①客户端启动程序来建立⼀个会话。②客户端尝试连接到localhost/127.0.0.1:2181。③客户端连接成功,服务器开始初始化这个新会话。④会话初始化成功完成。⑤服务器向客户端发送⼀个SyncConnected事件。

来看⼀看这些输出。有很多⾏告诉我们各种各样的环境变量的配置以及客户端使⽤了什么JAR包。我们在例⼦中忽略这些信息,关注会话的建⽴,但可以花时间来分析所有屏幕的输出信息。

在输出的结尾,我们看到会话建⽴的⽇志消息。

第⼀处提到“Initiating client connection”。消息本⾝说明到底发⽣了什么,⽽额外的重要细节说明了客户端尝试连接到客户端发送的连接串localhost/127.0.0.1:2181中的⼀个服务器。这个例⼦中,字符串只包含了localhost,因此指明了具体连接的地址。之后我们看到关于SASL的消息,我们暂时忽略这个消息,随后⼀个确认信息说明客户端与本地的ZooKeeper服务器建⽴了TCP连接。后⾯的⽇志信息确认了会话的建⽴,并告诉我们会话ID为:0x100002965fb0002。最后客户端库通过SyncConncted事件通知了应⽤。应⽤需要实现Watcher对象来处理这个事件。

为了更加了解ZooKeeper,让我们列出根(root)下的所有znode,然后创建⼀个znode。⾸先我们要确认此刻znode树为空,除了节点/zookeeper之外,该节点内标记了ZooKeeper服务所需的元数据树。

我们执⾏" ls / "后看到这⾥只有/zookeeper节点。现在我们创建⼀个名为/workers的znode,确保如下所⽰:

为了完成这个练习,删除znode,然后退出:

观察到znode /workers已经被删除,并且会话现在也关闭。为了完成最后的清理,退出ZooKeeper服务器:

bb13bd183d0eb160bfbc56210ad5dcc8.png

ea9fa45cfc4f62280591c7ce663ac1de.png 会话状态和生命周期

会话的⽣命周期(lifetime)是指会话从创建到结束的时期,⽆论会话正常关闭还是因超时⽽导致过期。

为了讨论在会话中发⽣了什么,我们需要考虑会话可能的状态,以及可能导致会话状态改变的事件。

⼀个会话的主要可能状态⼤多是简单明了的:CONNECTING、CONNECTED、CLOSED和NOT_CONNECTED。状态的转换依赖于发⽣在客户端与服务之间的各种事件。

51b5227ce406da4732cb0e57c22e9826.png

⼀个会话从NOT_CONNECTED状态开始,当ZooKeeper客户端初始化后转换到CONNECTING状态(箭头1)。正常情况下,成功与ZooKeeper服务器建⽴连接后,会话转换到CONNECTED状态(箭头2)。当客户端与ZooKeeper服务器断开连接或者⽆法收到服务器的响应时,它就会转换回CONNECTING状态(箭头3)并尝试发现其他ZooKeeper服务器。如果可以发现另⼀个服务器或重连到原来的服务器,当服务器确认会话有效后,状态又会转换回CONNECTED状态。否则,它将会声明会话过期,然后转换到CLOSED状态(箭头4)。应⽤也可以显式地关闭会话(箭头4和箭头5)。

注意:发⽣⽹络分区时等待CONNECTING

如果⼀个客户端与服务器因超时⽽断开连接,客户端仍然保持CONNECTING状态。如果因⽹络分区问题导致客户端与ZooKeeper集合被隔离⽽发⽣连接断开,那么其状态将会⼀直保持,直到显式地关闭这个会话,或者分区问题修复后,客户端能够获悉ZooKeeper服务器发送的会话已经过期。发⽣这种⾏为是因为ZooKeeper集合对声明会话超时负责,⽽不是客户端负责。直到客户端获悉ZooKeeper会话过期,否则客户端不能声明自⼰的会话过期。然⽽,客户端可以选择关闭会话。

创建⼀个会话时,你需要设置会话超时这个重要的参数,这个参数设置了ZooKeeper服务允许会话被声明为超时之前存在的时间。如果经过时间t之后服务接收不到这个会话的任何消息,服务就会声明会话过期。⽽在客户端侧,如果经过t/3的时间未收到任何消息,客户端将向服务器发送⼼跳消息。在经过2t/3时间后,ZooKeeper客户端开始寻找其他的服务器,⽽此时它还有t/3时间去寻找。

注意:客户端会尝试连接哪⼀个服务器?

在仲裁模式下,客户端有多个服务器可以连接,⽽在独立模式下,客户端只能尝试重新连接单个服务器。在仲裁模式中,应用需要传递可用的服务器列表给客户端,告知客户端可以连接的服务器信息并选择⼀个进⾏连接。

当尝试连接到⼀个不同的服务器时,⾮常重要的是,这个服务器的ZooKeeper状态要与最后连接的服务器的ZooKeeper状态保持最新。客户端不能连接到这样的服务器:它未发现更新⽽客户端却已经发现的更新。ZooKeeper通过在服务中排序更新操作来决定状态是否最新。ZooKeeper确保每⼀个变化相对于所有其他已执⾏的更新是完全有序的。因此,如果⼀个客户端在位置i观察到⼀个更新,它就不能连接到只观察到i'

下图描述了在重连情况下事务标识符(zkid)的使⽤。当客户端因超时与s1断开连接后,客户端开始尝试连接s2,但s2延迟于客户端所知的变化。然⽽,s3对这个变化的情况与客户端保持⼀致,所以s3可以安全连接。

ce47ea81df0204cb7f05b2f7db2c098a.png

ea9fa45cfc4f62280591c7ce663ac1de.pngZookeeper与仲裁模式

到⽬前为⽌,我们⼀直基于独⽴模式配置的服务器端。如果服务器启动,服务就启动了,但如果服务器故障,整个服务也因此⽽关闭。这⾮常不符合可靠的协作服务的承诺。出于可靠性,我们需要运⾏多个服务器。

幸运的是,我们可以在⼀台机器上运⾏多个服务器。我们仅仅需要做的便是配置⼀个更复杂的配置⽂件。

为了让服务器之间可以通信,服务器间需要⼀些联系信息。理论上,服务器可以使⽤多播来发现彼此,但我们想让ZooKeeper集合⽀持跨多个⽹络⽽不是单个⽹络,这样就可以⽀持多个集合的情况。

为了完成这些,我们将要使⽤以下配置⽂件:

af40f76932e1769e249df72e84dc071b.png

当服务器启动时,服务器通过配置⽂件中的dataDir参数来查找data⽬录的配置。它通过mydata获得服务器ID,之后使⽤配置⽂件中server.n对应的项来设置端⼜并监听。当在不同的机器上运⾏ZooKeeper服务器进程时,它们可以使⽤相同的客户端端⼜和相同的配置⽂件。但对于这个例⼦,在⼀台服务器上运⾏,我们需要⾃定义每个服务器的客户端端⼜。

因此,⾸先使⽤本章之前讨论的配置⽂件,创建z1/z1.cfg。之后通过分别改变客户端端⼜号为2182和2183,创建配置⽂件z2/z2.cfg和z3/z3.cfg。

因此,⾸先使⽤本章之前讨论的配置⽂件,创建z1/z1.cfg。之后通过分别改变客户端端⼜号为2182和2183,创建配置⽂件z2/z2.cfg和z3/z3.cfg。

现在可以启动服务器,让我们从z1开始:

6445d1f0ed825ea0179b887eac37f62e.png

ed948b371794b261d2cdf5653a6c246d.png

现在来启动另外一个服务器,可以构成仲裁的法定⼈数:

6bb01d2bdfb9c0832daf44b39d46a279.png

观察z1目录下面的zookeeper.out。服务器1作为服务器2的追随者被激活。我们现在具有了符合法定仲裁(三分之⼆)的可⽤服务器。

094820eecc6a4cae0404d35d3f5704f9.png

cat一下z2目录下面的zookeeper.out。

该⽇志指出服务器2已经被选举为leader。

05658328ee8c4902aab518492feaee56.png

同理启动第三个服务器。

37ddf5269f05081d09c46d3ca355176e.png

37ddf5269f05081d09c46d3ca355176e.png

在此刻服务开始可⽤。我们现在需要配置客户端来连接到服务上。连接字符串需要列出所有组成服务的服务器host:port对。

对于这个例⼦,连接串为"127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"(我们包含第三个服务器的信息,即使我们永远不启动它,因为这可以说明ZooKeeper⼀些有⽤的属性)。

994cbf8f4969f5d629764eb52c1cb6cd.png

注意:简单的负载均衡

客户端以随机顺序连接到连接串中的服务器。这样可以用ZooKeeper来实现⼀个简单的负载均衡。不过,客户端⽆法指定优先选择的服务器来进⾏连接。例如,如果我们有5个ZooKeeper服务器的⼀个集合,其中3个在美国西海岸,另外两个在美国东海岸,为了确保客户端只连接到本地服务器上,我们可以使在东海岸客户端的连接串中只出现东海岸的服务器,在西海岸客户端的连接串中只有西海岸的服务器。

这个连接尝试说明如何通过运⾏多个服务器来达到可靠性(当然,在⽣产环境中,你需要在不同的主机上进⾏这些操作)。对于本书⼤部分,包括后续⼏章,我们⼀直以独⽴模式的服务器进⾏开发,因为启动和管理多个服务器⾮常简单,实现这个例⼦也⾮常简单。除了连接串外,客户端不⽤关⼼ZooKeeper服务由多少个服务器组成,这也是ZooKeeper的优点之⼀。

ea9fa45cfc4f62280591c7ce663ac1de.png通过Zookeeper实现锁

关于ZooKeeper的功能,⼀个简单的例⼦就是通过锁来实现临界区域。我们知道有很多形式的锁(如:读/写锁、全局锁),通过ZooKeeper来实现锁也有多种⽅式。这⾥讨论⼀个简单的⽅式来说明应⽤中如何使⽤ZooKeeper,我们不再考虑其他形式的锁。

假设有⼀个应⽤由n个进程组成,这些进程尝试获取⼀个锁。再次强调,ZooKeeper并未直接暴露原语,因此我们使⽤ZooKeeper的接⼜来管理znode,以此来实现锁。为了获得⼀个锁,每个进程p尝试创建znode,名为/lock。如果进程p成功创建了znode,就表⽰它获得了锁并可以继续执⾏其临界区域的代码。不过⼀个潜在的问题是进程p可能崩溃,导致这个锁永远⽆法释放。在这种情况下,没有任何其他进程可以再次获得这个锁,整个系统可能因死锁⽽失灵。为了避免这种情况,我们不得不在创建这个节点时指定/lock为临时节点。

其他进程因znode存在⽽创建/lock失败。因此,进程监听/lock的变化,并在检测到/lock删除时再次尝试创建节点来获得锁。当收到/lock删除的通知时,如果进程p还需要继续获取锁,它就继续尝试创建/lock的步骤,如果其他进程已经创建了,就继续监听节点。

ea9fa45cfc4f62280591c7ce663ac1de.png一个主-从模式例子的实现

本节中我们通过zkCli⼯具来实现主-从⽰例的⼀些功能。这个例⼦仅⽤于教学⽬的,我们不推荐使⽤zkCli⼯具来搭建系统。使⽤zkCli的⽬的仅仅是为了说明如何通过ZooKeeper来实现协作菜谱,从⽽撇开在实际实现中所需的⼤量细节。我们将在下⼀章中进⼊实现的细节。主-从模式的模型中包括三个⾓⾊:

主节点:主节点负责监视新的从节点和任务,分配任务给可⽤的从节点。

从节点:从节点会通过系统注册⾃⼰,以确保主节点看到它们可以执⾏任务,然后开始监视新任务。

客户端:客户端创建新任务并等待系统的响应。现在探讨这些不同的⾓⾊以及每个⾓⾊需要执⾏的确切步骤。

ea9fa45cfc4f62280591c7ce663ac1de.png主节点角色

主节点:端口2181

因为只有⼀个进程会成为主节点,所以⼀个进程成为ZooKeeper的主节点后必须锁定管理权。为此,进程需要创建⼀个临时znode,名为/master:

a331ee0c1e9c8961ea23103b4575f72c.png

①创建主节点的znode,以便获得管理权。使用-e标志来表示创建的znode为临时性的。②列出ZooKeeper树的根。③获取/master znode的元数据和数据。

刚刚发⽣了什么?⾸先创建⼀个临时znode/master。我们在znode中添加了主机信息,以便ZooKeeper外部的其他进程需要与它通信。添加主机信息并不是必需的,但这样做仅仅是为了说明我们可以在需要时添加数据。为了设置znode为临时性的,需要添加-e标志。记得,⼀个临时节点会在会话过期或关闭时⾃动被删除。现在让我们看下我们使⽤两个进程来获得主节点⾓⾊的情况,尽管在任何时刻最多只能有⼀个活动的主节点,其他进程将成为备份主节点。假如其他进程不知道已经有⼀个主节点被选举出来,并尝试创建⼀个/master节点。让我们看看会发⽣什么:

c088700fb701f968249af06c35ed75d2.png

ZooKeeper告诉我们⼀个/master节点已经存在。这样,第⼆个进程就知道已经存在⼀个主节点。然⽽,⼀个活动的主节点可能会崩溃,备份主节点需要接替活动主节点的⾓⾊。为了检测到这些,需要在/master节点上设置⼀个监视点,操作如下:

6eaaf991b31db788e4f05b6e4931f777.png

stat命令可以得到⼀个znode节点的属性,并允许我们在已经存在的znode节点上设置监视点。通过在路径后⾯设置参数true来添加监视点。当活动的主节点崩溃时,我们会观察到以下情况:

d083f6401b25785997844866349747dc.png

在输出的最后,我们注意到NodeDeleted事件。这个事件指出活动主节点的会话已经关闭或过期。同时注意,/master节点已经不存在了。现在备份主节点通过再次创建/master节点来成为活动主节点。

ea9fa45cfc4f62280591c7ce663ac1de.png从节点的角色、任务和分配

主节点:端口2182 从节点:端口2181 客户端:端口2183

讨论从节点和客户端所采取的步骤之前,让我们先创建三个重要的⽗/znode,/workers、/tasks/assign

81f9c65944a7b64ce9e4c838b81afbce.png

这三个新的znode为持久性节点。本例中,通过使⽤这些znode可以告诉我们哪个从节点当前有效,还告诉我们当前有任务需要分配,并向从节点分配任务。

在真实的应⽤中,这些znode可能由主进程在分配任务前创建,也可能由⼀个引导程序创建,不管这些节点是如何创建的,⼀旦这些节点存在了,主节点就需要监视/workers/tasks的⼦节点的变化情况:

1c41f0e2abb26dd014f76b50bb7ef6d0.png

请注意,在主节点上调⽤stat命令前,我们使⽤可选的true参数调⽤ls命令。通过true这个参数,可以设置对应znode的⼦节点变化的监视点。

ea9fa45cfc4f62280591c7ce663ac1de.png从节点角色

从节点⾸先要通知主节点,告知从节点可以执⾏任务。从节点通过在/workers⼦节点下创建临时性的znode来进⾏通知,并在⼦节点中使⽤主机名来标识⾃⼰:

13801ac1e74a16a9bb0cda66c14412cb.png

注意,输出中,ZooKeeper确认znode创建完成。之前主节点已经监视了/workers的⼦节点变化情况。⼀旦从节点在/workers下创建了⼀个znode,主节点就会观察到以下通知信息:

3b7b6a3217a21ed33f9a0438f65bc725.png

下⼀步,从节点需要创建⼀个⽗znode /assign/worker1.example.com来接收任务分配,并通过第⼆个参数为true的 ls 命令来监视这个节点的变化,以便等待新的任务。

800ca72be84f0d5cec11fc9faea0ecb6.png

从节点现在已经准备就绪,可以接收任务分配。之后,我们通过讨论客户端⾓⾊来看⼀下任务分配的问题。

ea9fa45cfc4f62280591c7ce663ac1de.png客户端角色

客户端向系统中添加任务。在本⽰例中具体任务是什么并不重要,我们假设客户端请求主从系统来运⾏cmd命令。为了向系统添加⼀个任务,客户端执⾏以下操作:

我们需要按照任务添加的顺序来添加znode,其本质上为⼀个队列。客户端现在必须等待任务执⾏完毕。执⾏任务的从节点将任务执⾏完毕后,会创建⼀个znode来表⽰任务状态。客户端通过查看任务状态的znode是否创建来确定任务是否执⾏完毕,因此客户端需要监视状态znode的创建事件:

7790676505678d724292df1182bac105.png

执⾏任务的从节点会在/tasks/task-0000000002节点下创建状态znode节点,所以我们需要⽤ls命令来监视/tasks/task-0000000002的⼦节点。⼀旦创建任务的znode,主节点会观察到以下事件:

c3043dacf37ee41bf198eefd203f6f00.png

主节点之后会检查这个新的任务,获取可⽤的从节点列表,之后分配这个任务给worker1.example.com

b5d07c665622e1be1412724ce8d5c35d.png

从节点接收到新任务分配的通知:

97593db77c11eacf001cf9d1a0fd48be.png

从节点之后便开始检查新任务,并确认该任务是否分配给⾃⼰:

5cda3e03b99a0fd0d1849cddcfef0ae8.png

⼀旦从节点完成任务的执⾏,它就会在/tasks中添加⼀个状态znode:

5f695ad5daf6eae05d615ffa2d9e378a.png

之后,客户端接收到通知,并检查执⾏结果:

13a06af71a173f5ccc73162ec7d12dd8.png

客户端检查状态znode的信息,并确认任务的执⾏结果。本例中,我们看到任务成功执⾏,其状态为“done”。当然任务也可能⾮常复杂,甚⾄涉及另⼀个分布式系统。最终不管是什么样的任务,执⾏任务的机制与通过ZooKeeper来传递结果,本质上都是⼀样的。

ea9fa45cfc4f62280591c7ce663ac1de.png总结

我们了解了许多基础的ZooKeeper概念,我们看到了ZooKeeper通过其API提供的基本功能,还探讨了其架构中的⼀些重要概念,如通过仲裁理论进⾏复制。此时此刻,最重要的并不是了解ZooKeeper的复制协议是如何⼯作的,最重要的是明⽩仲裁理论的概念,因为你在部署ZooKeeper时需要指定服务器的数量。讨论的另⼀个重要概念便是会话。会话的语义对ZooKeeper的保障⾮常关键,因为它们常常会涉及会话。

为了对如何使⽤ZooKeeper提供初步的了解,我们使⽤zkCli⼯具来访问ZooKeeper服务器,并执⾏请求。我们展⽰了使⽤该⼯具在主-从模式例⼦中的主要操作。当实现⼀个真实的ZooKeeper应⽤时,你不应该使⽤这个⼯具;这个⼯具更多地⽤于调试和监控⽬的。你需要使⽤ZooKeeper提供的某⼀语⾔套件。

38e7cfbb6d8a0a1305229b46d83883f3.pngfbf1f705fcc350a50993a9a7246fdb0f.png

本期推荐内容:

MSVL统一模型验证器安装及使用手册

https://blog.csdn.net/qq_38340135/article/details/107584329

Golang之kafka知识点

https://blog.csdn.net/qq_38871968/article/details/106320372

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值