Hadoop之ZooKeeper

x33g5p2x  于2020-10-30 发布在 Zookeeper  
字(13.5k)|赞(0)|评价(0)|浏览(847)

1、ZooKeeper概述

Google的三篇论文影响了很多人,也影响了很多系统,这三篇论文一直是分布式领域传阅的经典。根据MapReduce,于是我们有了Hadoop;根据GFS(Google File System),于是我们有了HDFS(Hadoop Distributed FileSystem);根据BigTable,于是我们有了HBase。在这三篇论文里面都提到了Google的一个Lock Service——Chubby,于是我们有了ZooKeeper。

那么ZooKeeper到底是什么?ZooKeeper又可以用来做什么?我们又该怎么使用ZooKeeper?ZooKeeper又是如何实现的呢?

ZooKeeper是一个分布式的、开放源代码的分布式应用程序协调服务,是Google的Chubby的一个开源实现,是Storm和HBase等的重要组件。ZooKeeper包含一个简单的原语集,分布式应用程序可以基于它实现同步服务、配置维护和命名服务等。在分布式应用程序中,由于工程师不能很好地使用锁机制,并且基于消息的协调机制不适合在某些应用中使用,因此需要有一种可靠的、可扩展的、分布式的、可配置的协调机制来统一管理系统的状态。ZooKeeper的目的就在于此,其目标是封装好复杂易出错的关键服务,将简单易用的接口和性能高效、功能稳定的系统提供给用户。

伴随着ZooKeeper有两篇论文,一篇是关于Zab,它是介绍ZooKeeper背后运用的一致性协议(即ZooKeeper Atomic Broadcastprotocol),另一篇是介绍ZooKeeper本身的。在这两篇文章中都提到ZooKeeper是一个分布式协调服务(即a service for coordinatingprocess of distributed applications)。那分布式协调服务又是个什么东东呢?

在一个并发的环境里,为了避免不同处理单元对共享数据同时进行修改,造成数据的损坏,我们必须依赖像锁这样的协调机制,让有的线程先操作这些资源,而其他线程等待。对于进程内的锁来讲,各种语言平台都已经给出了多种方案,比如Java有最普通的同步方法和同步块。运用这种方式后,多个线程对同步方法进行操作时,就会协调好顺序,不会对共享方法中的资源进行破坏,从而产生不一致的情况。在进程内进行协调可以使用语言、平台、操作系统等提供的机制,那么在分布式环境中呢?也就是程序运行在不同的机器上,这些机器还可能位于不同的机架、不同的机房甚至不同的数据中心。在这样的环境中,要实现协调又该如何呢?这就是分布式协调服务需要干的事情。

在分布式系统中,所有同一个进程内的任何假设都是不存在的,因为网络是不可靠的。比如,在同一个进程内,对一个方法的调用如果成功,那就是成功,如果调用失败,如抛出异常那就是调用失败。在同一个进程内,如果这个方法先调用先执行,那就是先执行。但是在分布式环境中呢?由于网络的不可靠,对一个服务的调用失败了并不表示一定是失败的,可能是执行成功了,但是响应返回的时候失败了。还有,比如A和B都去调用C服务,在时间上A先调用,B后调用,那最后的结果是不是一定A的请求就先于B到达呢?答案是显然的。这些原本在同一个进程内的种种假设,都需要重新进行考虑,同时,在分布式环境中为了提高可靠性,往往会部署多套服务,这又得考虑如何在多套服务中达到一致性。所以分布式协调远远比同一个进程内的协调复杂,因而类似ZooKeeper这类基础服务便应运而生。这些服务在各个系统中久经考验,它们的可靠性和可用性都是经过理论和实践的验证的,在构建分布式系统时合理使用这些服务将会事半功倍。

2**、ZooKeeper可以做什么?**

官方对ZooKeeper的解释是:ZooKeeper is a centralizedservice for maintaining configuration information,name,providing distributed synchronization and providing group services。这句英文充分描述了ZooKeeper主要可以做的事情,即配置管理、名字服务,提供分布式同步以及集群管理。那这些服务到底是什么?我们为什么需要这些服务?使用ZooKeeper如何实现这些服务,它又有何优势?下面将进行简单介绍。

A**、配置管理**

通常情况下,在我们的应用程序中除了代码,还有一些配置文件,比如数据库的连接等。当只有一种配置,只有一台服务器,并且不需要经常修改的时候,使用配置文件是一个很好的方式。但如果配置项非常多,而且很多服务器都需要使用这个配置,甚至还可能是动态配置项,那使用配置文件就不是一个很好的方式了。这个时候就需要寻找一种集中管理配置的方式,只要在这个集中的地方修改配置,所有对这个配置有需求的应用都可以获得更新。举个例子,把配置信息存放到数据库中,然后所有需要这些配置的服务都去这个数据库中读取配置,但是,由于很多服务的正常运行都十分依赖这些配置,所以需要这个集中提供配置的服务具备非常高的可靠性。

一般情况下,可以用一个集群来提供这个配置服务,但是使用集群提高了可靠性,那如何保证配置在集群中的一致性呢?这时候就需要使用一种实现了一致性协议的服务。ZooKeeper就是这样一种服务,它使用Zab一致性协议来实现一致性,现在有很多开源项目都使用ZooKeeper来维护配置,比如在HBase中,客户端就是连接一个ZooKeeper,获得必要的HBase集群的配置信息,然后才可以进行下一步的操作。还有在开源的消息队列Kafka中,也使用ZooKeeper来维护broker的信息;在Alibaba开源的SOA框架Dubbo中,也广泛使用ZooKeeper来管理一些配置,实现服务治理。

B**、名字服务**

为了通过网络访问一个系统,我们得知道对方机器的IP地址,可是IP地址对人非常不友好,这个时候就需要使用域名来访问,但是计算机又不能是别域名,这该怎么办呢?如果每台机器里都备有一份域名到IP地址的映射,这个到是能解决一部分问题,但是如果域名对应的IP地址发生了变化又该怎么办呢?针对这种情况,出现了DNS这个东西,只需要访问一个大家熟知的点,它会告诉你这个域名对应的IP地址是什么。

在应用中也会存在很多类似的情况,特别是在服务很多的时候,如果在本地保存服务的地址,将非常不方便,但是如果只需要访问一个大家熟知的点,这里提供统一的入口,那么维护起来将方便得多。比如在日常开发中,常常会遇到这样的场景:服务A(开发者:张三)需要访问服务B(开发者:李四),但是服务B还在开发过程中(未完成),那么服务A(此时已经完成)就不知道如何获取服务B的访问路径了(假设使用的是http服务),张三还有其他工作要做,该怎么办呢?使用ZooKeeper的名字服务就可以简单解决:张三和李四约定好,服务B部署成功后,先到ZooKeeper注册服务(即在ZooKeeper上添加节点/service/B和节点数据)。服务A开发结束后,部署到服务器,然后服务A监控ZooKeeper服务节点/service/B,如果发现节点数据,那么服务A就可以访问服务B了。

C**、分布式锁**

前面已经提到ZooKeeper是一个分布式协调服务,可以利用它来协调多个分布式进程之间的活动,比如在一个分布式环境中,为了提高可靠性,集群的每台服务器上都部署着同样的服务。但是,同样一件事情,如果集群中的每个服务器都进行处理的话,那相互之间就要协调,编程起来将非常复杂。而如果只让一个服务进行操作,那又存在单点故障的问题。通常还有一种做法就是使用分布式锁,在某个时刻只让一台机器去干活,当这台机器出问题的时候便释放锁,立即fail over到其他机器上。

在很多分布式系统中都是这么做的,这种设计有一个更好听的名字叫Leader Election(leader选举)。比如HBase的Master就是采用的这种机制,需要注意的是,分布式锁与同一个进程的锁是有很大区别的,所以使用的时候要比在同一个进程里的锁更谨慎。那么如何使用ZooKeeper实现分布式锁呢?在描述算法流程之前,先看下ZooKeeper中几个关于节点的性质:

有序节点:假如当前有一个父节点为/lock,我们可以在这个父节点下面创建子节点;ZooKeeper提供了一个可选的有序特性,例如我们可以创建子节点“/lock/node-”,并且指明有序,那么ZooKeeper在生成子节点时会根据当前的子节点数量自动添加整数序号,也就是说如果是第一个创建的子节点,那么生成的子节点为“/lock/node-0000000000“”,下一个节点则为“/lock/node-0000000001“”,依次类推;
*
临时节点:客户端可以建立一个临时节点,在会话结束或者会话超时后,ZooKeeper会自动删除该节点;
*
事件监听:在读取数据时,我们可以同时对节点设置事件监听,当节点数据或结构变化时,ZooKeeper会通知客户端。当前ZooKeeper有如下四种事件:1)节点创建;2)节点删除;3)节点数据修改;4)子节点变更。

下面介绍使用ZooKeeper实现分布式锁的算法流程,假设锁空间的根节点为“/lock”:

步骤一:客户端连接ZooKeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,依次类推;
*
步骤二:客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后,重复此步骤,直至获得锁;
*
步骤三:执行业务代码;
*
步骤四:完成业务流程后,删除对应的子节点释放锁。

针对上面几个步骤,这里做几点说明:

(1)步骤1中创建的临时节点能够保证在故障的情况下锁也能被释放,考虑这么个场景:假如客户端a当前创建的子节点为序号最小的节点,获得锁之后客户端所在机器宕机了,客户端没有主动删除子节点;如果创建的是永久性的节点,那么这个锁永远不会释放,导致死锁;由于创建的是临时节点,客户端宕机后,过了一定时间ZooKeeper没有收到客户端的心跳信息,判断会话失效,将临时节点删除,从而释放锁;

(2)在步骤2中,获取子节点列表与设置监听这两步操作的原子性问题,考虑这么个场景:客户端a对应子节点为/lock/lock-0000000000,客户端b对应子节点为/lock/lock-0000000001,客户端b获取子节点列表时发现自己不是序号最小的,但是在设置监听器前客户端a完成业务流程并删除了子节点/lock/lock-0000000000,客户端b设置的监听器岂不是丢失了这个事件,从而导致永远等待了?这个问题是不存在的,因为ZooKeeper提供的API中,设置监听器的操作与读操作是原子执行的,也就是说在读子节点列表时同时设置监听器,保证不会丢失事件;

(3)对于这个算法有个极大的优化点:假如当前有1000个节点在等待锁,如果获得锁的客户端释放锁时,这1000个客户端都会被唤醒,这种情况称为“羊群效应”;在这种羊群效应中,ZooKeeper需要通知1000个客户端,这会阻塞其他的操作,最好的情况应该是只唤醒新的最小节点对应的客户端。应该怎么做呢?在设置事件监听时,每个客户端应该对刚好在它之前的子节点设置事件监听,例如子节点列表为/lock/lock-0000000000、/lock/lock-0000000001、/lock/lock-0000000002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的客户端监听序号为1的子节点删除消息。所以按照这个优化策略,上述算法的步骤二需要修改为:客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁。

D**、集群管理**

在分布式集群中,经常会由于各种原因,例如硬件故障,软件故障,网络问题等,有些节点会进进出出,有新的节点加入进来,也有老的节点退出集群。这个时候,集群中其他机器需要感知到这些变化,然后根据这些变化做出对应的决策。比如一个分布式存储系统,有一个中央控制节点负责存储的分配,当有新的存储进来的时候,需要根据集群当前的状态来分配存储节点,这个时候就需要动态感知集群当前的状态。

比如在一个分布式的SOA架构中,服务是由一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现现在有哪些节点可以提供该服务(这也被叫做服务发现,比如Alibaba开源的SOA框架Dubbo,就采用了ZooKeeper作为服务发现的底层机制)。还有开源的Kafka队列,也采用了ZooKeeper作为Consumer的上下线管理。

ZooKeeper很容易便能实现这个功能,比如在ZooKeeper服务器端有一个节点叫/service,那么集群中每一个机器启动的时候都去这个节点下创建一个临时性的节点,比如server1创建/service/server1(可以使用IP,保证不重复),server2创建/service/server2,然后server1和server2都watch / service这个父节点,那么当这个父节点下数据或者子节点发生变化时,都会通知对该节点进行watch的客户端。因为临时的节点有一个很重要的特性,就是客户端和服务器端连接断掉或者session过期就会使节点消失,那么在某一个机器挂掉或者断连的时候,其对应的节点就会消失,然后集群中所有对/service节点进行watch的客户端都会收到通知,然后取得最新列表即可。

3**、**ZooKeeperClient

ZooKeeper的client是通过ZooKeeper类提供的,ZooKeeper给使用者提供的是一个类似操作系统的文件结构,只不过这个结构是分布式的,可以理解为一个分布式的文件系统。我们可以通过ZooKeeper来访问这个分布式的文件系统。ZooKeeper的client API给我们提供了如下这些方法:

A**、**create

在给定的path上创建一个znode节点(注意必须要有父节点),这个path就像文件系统的路径,比如/myapp/data/1,在创建节点的时候还可以指定节点的类型,如永久性节点,永久性顺序节点,临时性节点,临时性顺序节点等。永久性节点一经创建就永久保留,就像我们在文件系统上创建一个普通文件,这个文件的生命周期跟创建它的应用没有任何关系。而临时性节点呢,当创建这个临时性节点的应用与ZooKeeper之间的会话过期之后,就会被ZooKeeper自动删除,这个特性是实现很多功能的关键。

比如我们做集群感知,应用启动的时候将自己的IP地址作为临时节点创建在某个节点下面,当应用由于某些异外,如网络断线或者机器宕机,它与ZooKeeper的会话就会过期,过期后这个临时性节点就会被删除。如此一来,我们就可以通过这个特性来感知到我们服务的集群中还有哪些机器是活着的。

那么顺序节点又是怎么回事呢?如果我们在指定的path上创建节点,如果这个节点已经被创建,则会抛出一个NodeExistsException异常。如果我们在指定的路径上创建顺序节点,则ZooKeeper会自动在我们给定的path上加上一个顺序编号,这个特性就是实现分布式锁的关键。

假如我们有几个节点共享一个资源,这几个节点都争着用这个资源,那我们就让这几个节点都向某个路径下创建临时顺序节点,然后顺序最小的那个节点就获得锁,如果某个节点释放了锁,那按顺序第二小的那个节点就获得锁,依次类推。这样,一个分布式的公平锁就实现了,除此之外,在每个节点上还可以保存一些数据。

B**、**delete

删除给定的znode节点(注意该znode不能有任何子节点),删除节点的时候必须给定被更新znode的版本号version参数(可以通过exists操作获得),只有路径和version参数都匹配的时候,节点才会被删除。有了这个version,在分布式环境中,我们就可以使用乐观锁来确保一致性。比如我们先读取一下节点,获得了节点的version,然后删除,如果删除成功了,则说明在这之前没有人操作过这个节点,否则就是并发冲突了。

C**、**exists

测试一个znode节点是否存在并且查询它的元数据,这个方法会返回一个Stat对象,如果给定的path不存在的话,则返回null。这个方法有一个关键参数,可以提供一个Watcher对象,Watcher是ZooKeeper强大功能的源泉,可以把Watcher理解成是一个事件处理器,或者是一个回调。比如这个exists方法调用后,如果别人对这个path上的节点进行操作,如创建、删除或设置数据,这个Watcher都会接收到对应的通知。

D**、**setData/getData

设置或获取一个znode节点所保存的数据,getData也可以设置Watcher。

E**、**getChildren

获取一个znode节点的子节点列表,同样可以设置Watcher。

F**、**sync

将客户端的znode节点视图与ZooKeeper同步。Sync操作和POSIX文件系统中的fsync()操作是不同的,ZooKeeper中的写操作具有原子性,一个成功的写操作会保证将数据写到ZooKeeper服务器的持久性存储介质中。然而,ZooKeeper允许客户端读到的数据滞后于ZooKeeper服务的最新状态,因此客户端可以使用sync操作来获取数据的最新状态。

4**、安装和配置****ZooKeeper**

ZooKeeper在大数据分布式系统中使用非常广泛,诸如HDFS、HBase以及Storm等组件要实现HA(High Availability,即高可用性),都离不开它。ZooKeeper继承了Hadoop的传统,其安装模式也包括三种,即单机模式、伪集群模式以及集群模式,总体上来说,这些模式的安装都非常简单,下面将进行详细介绍。

A**、单机模式**

单机模式的特点是,ZooKeeper只运行在一台机器上,安装过程简便,适合测试环境。下面我们就在主机hadoop221上安装ZooKeeper的单机模式。首先,将ZooKeeper安装包ZooKeeper-3.4.10.tar.gz上传到/root/tools目录下,运行命令tar -zxvf ZooKeeper-3.4.10.tar.gz-C /root/training/,将ZooKeeper解压到/root/training目录下;然后,配置ZooKeeper的环境变量,运行命令vi /root/.bash_profile编辑.bash_profile文件,在末尾添加如下代码:

ZOOKEEPER_HOME=/root/training/ZooKeeper-3.4.10

export ZOOKEEPER_HOME

PATH=$ZOOKEEPER_HOME/bin:$PATH

export PATH

保存退出后,运行命令source /root/.bash_profile使环境变量生效。接下来配置ZooKeeper的核心配置文件zoo.cfg,进入到/root/training/ZooKeeper-3.4.10/conf目录,由于ZooKeeper安装目录事先未提供zoo.cfg文件,但提供了模板配置文件zoo_sample.cfg,运行命令cp zoo_sample.cfg zoo.cfg,如此便得到zoo.cfg文件,运行命令vi zoo.cfg进行编辑,修改第12行的配置,将dataDir=/tmp/ZooKeeper更改为/root/training/ZooKeeper-3.4.10/tmp(由于/tmp/是Linux系统的临时目录,重启系统会导致数据丢失,故一定注意进行修改),并在末尾添加server.1=hadoop221:2888:3888(配置ZooKeeper节点的主机名及端口号,2888是通信端口,3888是选举端口),保存退出后,运行命令mkdir /root/training/ZooKeeper-3.4.10/tmp,创建刚刚在zoo.cfg文件中配置的目录;最后,还需要在/root/training/ZooKeeper-3.4.10/tmp目录创建一个节点文件,与zoo.cfg文件中的server.1=hadoop221:2888:3888相对应,进入到目录/root/training/ZooKeeper-3.4.10/tmp,运行命令vi myid,并输入1,保存退出即可。到这里,ZooKeeper的单机模式配置完成。

现在,我们来验证下ZooKeeper的安装是否成功。运行命令zkServer.sh start启动ZooKeeper,并运行命令zkServer.sh status查看ZooKeeper的运行模式,如下图所示:

从上图可以看到,当前ZooKeeper运行的模式为standalone模式。通常情况下,ZooKeeper会保存很多的状态信息,因此可以把它看作为一个“数据库“,可以往其中保存一些简单的信息。运行命令zkCli.sh登录到ZooKeeper的客户端,运行ls /(/代表ZooKeeper的根目录,)查看ZooKeeper中的目录信息,运行命令create /node1 helloworld,往ZooKeeper中创建节点node1并赋值helloworld,运行命令get /node1,查看节点/node1的信息,如下图所示:

B**、伪集群模式**

ZooKeeper不但可以在单机上运行单机模式,而且可以在单机上模拟集群模式的运行,也就是将不同节点运行在同一台机器上。在伪集群模式下对ZooKeeper的操作和在集群模式下没有本质的区别。这样一来,伪集群模式为我们体验ZooKeeper和做一些尝试性的实验提供了很大的便利,比如先使用少量数据在伪集群模式下进行测试,当测试可行时,再将数据迁移到集群模式下进行真实的数据实验,这样不但保证了它的可行性,同时大大提供了实验的效率。这种搭建方式非常简便,成本也很低,适合测试和学习,如果手头机器数量不足,就可以在一台机器上部署多个server。

需要注意的是,在伪集群模式下,我们使用的每个配置文件模拟一台机器,但是必须保证每个配置文件的各个端口号不能冲突,除了clientPort不同之外,dataDir也不能相同,另外还需要在dataDir对应的目录下创建myid节点文件配置指定的ZooKeeper服务器实例。总结如下:

clientPort端口:如果在1台机器上部署多个server,那么每台机器都要配置不同的clientPort,比如server1是2181,server2是2182,server3是2183;
*
dataDir和dataLogDir:dataDir和dataLogDir也需要区分下,将数据文件和日志文件分开存放,同时每个server的这两个变量对应的路径都是不同的;
*
server.X和myid:server.X这个数字就是对应data/myid文件中的数字,在3个server的myid文件中分别写入1,2,3,那么每个server中的zoo.cfs文件分别配置server.1,server.2,server3即可,因为在同一台机器上,后面连着的2个端口,3个server都不能一样,否则端口会产生冲突。

前面已经安装和配置了ZooKeeper的单机模式,现在在其基础之上来配置伪集群模式,运行命令cp zoo.cfg zoo1.cfg、cp zoo.cfg zoo2.cfg cpzoo.cfg zoo3.cfg,得到三个ZooKeeper实例的核心配置文件,需要修改的内容对应如下:

zoo1.cfg:

dataDir=/root/training/ZooKeeper-3.4.10/tmp1

clientPort=2181

dataLogDir=/root/training/ZooKeeper-3.4.10/logs1

server.1=hadoop221:2888:3888

server.2= hadoop221:2887:3887

server.3= hadoop221:2886:3886

zoo2.cfg:

dataDir=/root/training/ZooKeeper-3.4.10/tmp2

clientPort=2182

dataLogDir=/root/training/ZooKeeper-3.4.10/logs2

server.1=hadoop221:2888:3888

server.2= hadoop221:2887:3887

server.3= hadoop221:2886:3886

zoo3.cfg:

dataDir=/root/training/ZooKeeper-3.4.10/tmp3

clientPort=2183

dataLogDir=/root/training/ZooKeeper-3.4.10/logs3

server.1=hadoop221:2888:3888

server.2= hadoop221:2887:3887

server.3= hadoop221:2886:3886

创建目录/root/training/ZooKeeper-3.4.10/tmp1和/root/training/ZooKeeper-3.4.10/logs1:在/root/training/ZooKeeper-3.4.10/tmp1目录下创建文件myid,输入1;

创建目录/root/training/ZooKeeper-3.4.10/tmp2和/root/training/ZooKeeper-3.4.10/logs2:在/root/training/ZooKeeper-3.4.10/tmp2目录下创建文件myid,输入2;

创建目录/root/training/ZooKeeper-3.4.10/tmp3和/root/training/ZooKeeper-3.4.10/logs3:在/root/training/ZooKeeper-3.4.10/tmp3目录下创建文件myid,输入3;

至此,ZooKeeper的伪集群模式配置完成。依次运行命令zkServer.sh start conf/zoo1.cfg、zkServer.sh start conf/zoo2.cfg、zkServer.sh start conf/zoo3.cfg,便会启动三个ZooKeeper实例,如下图所示:

C**、集群模式**

为了获得可靠的ZooKeeper服务,在条件允许的情况下,应该在一个机群之上部署ZooKeeper集群服务。只要集群上大多数的ZooKeeper服务启动了,那么总的ZooKeeper服务将是可用的。ZooKeeper集群模式的配置方式和前两种类似,同样需要进行环境变量的配置,在每台机器上的conf/zoo.cfg文件中配置相关参数,下面就在主机hadoop222、hadoop223和hadoop224上进行ZooKeeper的集群模式的安装和配置。

我们先在主机hadoop222上进行安装和配置,最后再将配置好的ZooKeeper目录拷贝到其它两台机器上。在hadoop222上解压安装ZooKeeper,运行命令tar -zxvf ZooKeeper-3.4.10.tar.gz-C /root/training/,在三台机器上按照完全相同的操作配置环境变量,vi /root/.bash_profile,末尾添加如下内容:

ZOOKEEPER_HOME=/root/training/ZooKeeper-3.4.10

export ZOOKEEPER_HOME

PATH=$ZOOKEEPER_HOME/bin:$PATH

export PATH

保存退出后,运行命令source /root/.bash_profile,使环境变量生效。进入到/root/training/ZooKeeper-3.4.10/conf目录,拷贝生成zoo.cfg文件,编辑修改如下:

dataDir=/root/training/ZooKeeper-3.4.10/tmp

server.1=hadoop222:2888:3888

server.2= hadoop222:2888:3888

server.3= hadoop222:2888:3888

在/root/training/ZooKeeper-3.4.10/目录下创建目录tmp,再在/root/training/ZooKeeper-3.4.10/tmp目录下创建文件myid,在myid文件中输入1。至此,hadoop222主机上ZooKeeper的安装配置完成,然后依次运行scp -r /root/training/ZooKeeper-3.4.10/ root@hadoop223:/root/training、scp -r /root/training/ZooKeeper-3.4.10/ root@hadoop224:/root/training,将配置好的ZooKeeper目录通过网络拷贝到主机hadoop223和hadoop224上,最后修改hadoop223上文件myid的内容为2,hadoop224上文件myid内容为3即可。

到这里,ZooKeeper的集群模式安装和配置完成。在三台机器上分别运行命令zkServer.sh start,启动ZooKeeper,运行命令zkServer.sh status,查看各主机上ZooKeeper的状态,可以看到其中有一台主机上的ZooKeeper的状态为leader,其余两台状态为follower;在主机hadoop222上运行命令zkCli.sh,登录到其客户端,运行命令create /node1 helloworld,创建/node1节点并赋值helloworld,运行命令get /node1,查看节点/node1的数据信息,如下图所示:

分别在主机hadoop223和hadoop224上运行命令zkCli.sh,登录到ZooKeeper客户端,查看节点/node1的数据信息,如下图所示:

在我本地主机hadoop224上的ZooKeeper运行状态为leader,运行命令zkServer.sh stop,退出ZooKeeper,如下图所示:

此时,再去查看主机hadoop222和hadoop223上ZooKeeper的运行状态,可以看到主机hadoop223上ZooKeeper的运行状态变为了leader,如下图所示:

经过上面的实验,验证了ZooKeeper的数据同步功能(ZooKeeper集群模式下,所有ZooKeeper从节点follower实例保存的数据信息跟主节点leader实例一致,同步更新),以及选举功能(当ZooKeeper主节点leader实例异常退出后,ZooKeeper集群会从剩下的follower实例中重新选举产生出新leader实例)

5、分布式锁程序实例(使用ZooKeeper实现秒杀系统)

现在国内互联网上非常火爆的618,双11等购物活动,其电商平台就是一个典型的秒杀系统,简单解释就是,在一段时间内,仅允许部分用户抢购数量有限的某些商品。这类秒杀系统是一种分布式系统,其中便使用到了分布式锁技术,以实现共享资源的互斥访问。下面就使用Java程序实例,来模拟在不使用分布式锁以及使用分布式锁情况下,程序的运行效果。

A**、不使用ZooKeeper分布式锁**

下面的程序代码没有使用到ZooKeeper,其实它就是一个普通的Java程序,其中定义了一个共享资源(就是一个整数),然后启动了10个线程来访问和操作该共享资源,以模拟分布式访问的情形。

程序运行结果如下图所示,可以看到,程序输出的结果是混乱的,共享资源(整型数字)有些重复输出,有些未输出,这便是未使用分布式锁导致的结果。

B**、使用ZooKeeper分布式锁**

下面的程序使用了ZooKeeper分布式锁,在每个线程访问业务方法之前,都需要去申请获取该分布式锁,只有成功获取到才继续执行;否则,就会按照重试策略进行多次尝试。

程序运行结果如下图所示,可以很明显地看到,这次10个线程的输出结果平稳有序,这便是使用分布式锁的功劳。

到这里为止,ZooKeeper的介绍就告一段落,本文并没有涉及到太多底层原理的东西,仅是带你简单地对ZooKeeper进行了解和使用。如需进一步深入了解,还请自行查阅相关资料进行学习。

参考文献

——《CSDN博客》

——《潭州大数据课程课件》

——《微信公众号:Java知音》

转自https://mp.weixin.qq.com/s/OAehif-FSrK_S40pD9QGuA

相关文章