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

Zookeeper入门看这篇文章就够了

时间:2023-03-22 17:29:35 科技观察

介绍Zookeeper是一个分布式开源协调服务,用于分布式应用。它是ApacheHadoop的一个子项目,主要用于解决分布式应用中经常遇到的一些数据管理问题,例如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。Zookeeper的原理ZooKeeper的核心是原子广播。这种机制保证了服务器之间的同步。实现这种机制的协议称为Zab协议。Zab协议有两种模式,分别是“recoverymode&broadcastmode”。恢复模式Zab协议会让ZK集群进入崩溃恢复模式如下:(1)服务框架在启动过程中(2)当leaderserver出现网络中断、崩溃退出重启等异常时情况。(3)当集群中超过半数的服务器不再与Leader服务器保持正常通信时。从所有跟随者服务器中选出一个作为领导者。当leader选举出来后,集群中的大部分服务都会在与新leader的状态同步完成后退出recovery模式,以保证至少一半的follower能够与leader保持数据。一致,当大部分follower集群与leader数据一致时,就会进入消息广播模式。状态同步确保Leader和Server具有相同的系统状态。所谓状态同步,其实就是数据同步。一旦领导者与大多数追随者同步了状态,它就开始广播消息并进入广播模式。这时,当一个server加入Zookeeper服务后,它会以recovery模式启动,发现leader,并与leader进行状态同步,同步结束后,也参与消息广播,Zookeeper服务一直保持Broadcast状态,直到leader崩溃或leader失去大部分followers的支持。BroadcastmodeMessage广播模式,Zab协议消息广播过程采用原子广播协议,类似于两阶段提交,但又有点不同。并不是所有的follower节点都需要返回ack来完成一致的交易。超过大多数人会这样做。对于每一个client的交易请求,leaderserver都会为其生成一个对应的交易Proposal,并发送给集群中的所有其他机器,然后收集各自的投票,最后提交交易。leader收到消息请求后,会为消息分配一个全局唯一的64位自增Id,我们通常称之为zxid,通过比较zxid的大小可以实现有序的特性。Leader通过队列保证发送顺序,将带zxid的消息作为提案(proposal)分发给所有follower。follower收到proposal后,先将proposal写入本地事务日志,事务写入成功后,再返回ACK给leader。确认当leader收到最多ack确认时,leader会向所有follower发送commit命令,同意在本地执行该消息。当follower收到消息commit命令后,就会执行消息。消息广播方式的流程图如下:首先,客户端会轮询Zookeeper集群中的各个节点。当轮询一个节点是follower时,如果是读请求,follower会返回请求结果。如果是增删改操作,follower会向leader发送交易请求,对于client的交易请求,leader会为此生成相应的交易提议,然后发送给所有follower服务器集群,收集各自的选票,最后提交交易。Zab协议的两阶段提交去掉了在提交过程中中断提交过程的操作。对于Zookeeper集群来说,超过半数的反馈Ack确认代表交易成功。这种方式无法完成所有节点的事务一致性,因此Zab协议采用了恢复模型来解决数据不一致的问题。消息广播协议是基于具有FIFO特性的TCP协议进行通信的,因此可以保证消息广播过程中接收和发送的顺序。事务ID为了保证事务的顺序一致性,Zookeeper使用递增的事务ID号(zxid)来标识事务。所有的操作(提议)在被提议时都会加上zxid。zxid是一个64位数字。这32位被epoch用来标识leader关系是否发生了变化。Wheneveranewleaderiselected,therewillbeanewepoch,whichidentifiesthecurrentleader.对于Zookeeper来说,每一次改变都会产生一个唯一的事务id,zxid(ZooKeeperTransactionId)可以通过zxid来决定更新操作的顺序。如果zxid1小于zxid2,则表示zxid1出现在zxid之前。Zookeeper模型Zookeeper是一个目录树结构,其名称是一系列由斜杠(/)分隔的路径元素。ZooKeeper命名空间中的每个节点都由路径标识。ZooKeeper分层树结构的根节点/包含两个节点(/model1&/module2),其中节点/module1包含三个子节点(/module/app1&/module1/app2&/module1/app3),在Zookeeper中,该节点用绝对路径表示,没有相对路径,除根节点外,其他节点不能以/结尾。特性资源共享:如存储空间、计算能力、数据和服务等可扩展性:从软件和硬件上增加系统的规模并发性:多个用户同时访问性能:保证当负载增加时,系统需要时间不会影响容错:虽然部分组件暂时不可用,但整个系统仍然可用API抽象:系统的独立组件对用户隐藏,只暴露服务Zookeeper的作用。Leader(领导者):负责投票发起和决议,更新系统状态Learner:包括follower和observer,follower用于接受client请求并返回结果给client,在选择master的过程中参与投票Observer:可以接受客户端连接,将写请求转发给leader,但观察者不参与投票过程,只同步leader的状态。观察者的目的是扩展系统,提高读取速度客户端(client):请求发起者保证顺序一致性:客户端更新将按照发送的顺序应用。原子性:更新成功或失败,没有部分结果。统一视图:无论服务端连接到哪个服务器,客户端看到的服务视图都是一样的。也就是说,即使客户端故障转移到具有相同会话的不同服务器,客户端也永远看不到系统的旧视图。可靠性:应用更新后,它将一直持续到客户端覆盖更新为止。时效性:保证系统对客户的看法在一定时间范围内是最新的。Znode节点有两种类型:持久节点和临时节点。Znode的类型创建后不能修改。临时节点当客户端会话结束时,Zookeeper会删除临时节点(znode),临时节点不能有子节点。利用临时节点的特点,我们可以利用临时节点来管理集群,发现服务上线下线。创建临时节点命令:create-e/module1/app1app1创建临时节点为“/module1/app1”,数据为“app1”。持久节点不依赖于客户端会话。只有当客户端明确要删除持久节点(znode)时才会在创建临时节点命令时删除:create/module1module1创建一个临时节点为“/module1”,数据为“module1”序列节点。ZooKeeper还提供了一种顺序节点类型,每次创建排序节点时,ZooKeeper都会自动给路径后面的数据加10。比如0000000001计数器在同一个父节点下会保证唯一。创建节点时,会添加顺序,常见的是分布式锁。顺序节点只是节点的一个特性,也就是说持久节点和临时节点都可以设置为顺序节点,所以Znode类型可以理解为四种类型:持久节点临时节点持久顺序节点临时顺序节点createsequentialnode命令(加“-s”参数):create-s/module1/appapp我们会看到Created/module1/app0000000001,也就是说我们创建了一个持久化的时序节点“/module1/app0000000001”如果再执行上面的命令,节点会生成“/module1/app0000000002”,同理,如果我们在create-s后加上-e参数,就表示我们创建了一个临时节点。节点数据在创建节点时,我们可以指定节点存储的数据。ZooKeeper可以保证读写都是原子操作,每次读写操作都是对数据的一次完整的读或写,不提供数据的部分读或写操作。ZooKeeper虽然提供了节点存储数据的功能,但是我们不能把它当作一个数据库。重点是不要把Zookeeper当作数据库使用,因为Zookeeper规定一个节点的数据大小不能超过1M,所以我们不能在节点上存储太多。数据,尽量保持数据量小,因为数据太大,会导致ZK的性能下降。如果确实需要存储大量数据,一般可以将这部分数据保存在分布式数据库或者Redis中,然后在Znode中保留数据库中的索引。Zookeeper单机模式安装java环境配置JAVA环境,测试环境java-version下载安装Zookeeperorg/zookeeper/zookeeper-3.7.0/apache-zookeeper-3.7.0-bin.tar.gztar-zxvfapache-zookeeper-3.7.0-bin.tar.gzcdzookeeper-3.7.0/重命名配置文件zoo_sample.cfgcpconf/zoo_sample.cfgconf/zoo.cfg启动ZK./bin/zkServer.shstart连接ZK客户端./bin/zkCli.sh当我们看到下图的信息,就说明我们已经成功启动了Zookeeper命令基本命令create:在树的某处创建一个节点delete:删除一个节点exists:测试某处是否存在该节点getdata:从某处读取数据nodesetdata:向一个节点写入数据getchildren:获取一个节点的childrenlistsync:等待数据传播完毕,操作Zookeeper查看keyls/新建Zookeeper中包含的Znode创建成功后,我们可以使用ls/查看我们创建的内容create/zkMxnmuxiaoonongls/[zkMxn,zookeeper]get命令获取创建的Znode内容get/zkMxnset命令设置zk关联的字符串set/zkMxnmxn666deleteZnodelete/zkMxnJavaApi操作ZK1。导入Jar包org.apache.zookeeperzookeeper3.6.3<!--junit单元测试-->junitjunit4.13.2runtime2。API操作Zookeeper创建一个Zookeeper对象publicZooKeeper(StringconnectString,intsessionTimeout,Watcherwatcher)throwsIOException{this(connectString,sessionTimeout,watcher,false);}connectString:连接的地址,包括主机名和端口号,如果多个分开通过逗号开启sessionTimeout:等待客户端通信的最长时间。如果客户端在这段时间后没有与服务器通信,则认为客户端已经终止。一般设置值为5-10秒,单位为毫秒。watcher:Listener,用于接收session事件的接口需要自己定义,实现process()方法连接ZookeeperZookeeperzkClient="";StringconnectStr="192.168.2.1:2181";zkClient=newZooKeeper(connectStr,5000,newWatcher(){@Overridepublicvoidprocess(WatchedEventwatchedEvent){}});创建节点publicZooKeeper(StringconnectString,intsessionTimeout,Watcherwatcher)throwsIOException{this(connectString,sessionTimeout,watcher,false);}path:节点路径data:节点数据acl:节点权限,例如:ZooDefs.Ids.OPEN_ACL_UNSAFEOPENACLUNSAFE:完全开发,使用world验证模式,由于每个ZK连接都有一个world验证模式,所以当我们的节点设置这个参数时,就会对所有连接开放CREATORALLACL:Znode连接的创建者拥有所有权限,这里是auth验证方式,使用sessionID进行验证,如果设置了这个参数,只有创建到Znode节点的连接才能对这个Znode进行任何操作所有连接都可以读取这个znodecreateMode:节点类型,例如:CreateMode.PERSISTENTPERSISTENT:持久节点PERSISTENTSEQUENTIAL:持久有序节点EPHEMERAL:临时节点EPHEMERALSEQUENTIAL:临时有序节点完整APIDemo:importlombok.extern.slf4j.Slf4j;importorg.apache.zookeeper.*;importorg.apache.zookeeper.data.Stat;importjava.io.IOException;importjava.io.UnsupportedEncodingException;importjava.util.concurrent.CountDownLatch;/**@Authormxn*@Description//TODOZooKeeperJavaAPI测试*@Date10:222021/9/29*@Param*@return**/@Slf4jpublicclassZookeeperTest{//IP和端口privatefinalstaticStringipAddress="192.168.2.123:2181";publicstaticvoidmain(String[]args){ZookeeperTesttest=newZookeeperTest();Stringkey="/zkMxn";Stringvalue="woismuxiaoonong";//创建Znodetest.add(key,value);//获取节点数据//test.get(key);//修改节点数据//test.modify(key,"woiszhuzhuxia");//删除节点//test.delete(key);}/***@return*@Authormxn*@Description//TODO获取ZooKeeper连接*@Date10:222021/9/29*@Param**/publicstaticZooKeepergetConntection(){ZooKeeperzooKeeper=null;try{finalCountDownLatchcountDownLatch=newCountDownLatch(1);//watch机制(回调),监控zooKeeper是否连接成功=newZooKeeper(ipAddress,5000,newWatcher(){@Overridepublicvoidprocess(WatchedEventwatchedEvent){if(Event.KeeperState.SyncConnected==watchedEvent.getState()){//如果收到服务器的响应事件,则连接成功countDownLatch.countDown();}}});countDownLatch.await();log.info("zookeeperstate:{}",zooKeeper.getState());//CONNECTED}catch(IOExceptione){e.printStackTrace();}catch(InterruptedExceptione){e.printStackTrace();}returnzooKeeper;}/**@Authorlyy*@Description//TODO关闭ZooKeeper连接*@Date14:572021/9/29*@Param*@return**/publicstaticvoidcloseConnection(动物园管理员rzooKeeper){try{//zooKeeper.close();}catch(Exceptione){e.printStackTrace();}}/**@Authorlyy*@Description//TODO添加节点*@Date13:362021/9/29*@Param*@return**/publicvoidadd(Stringkey,Stringvalue){ZooKeeperzooKeeper=ZookeeperTest.getConntection();try{//参数类型//1.key//2.价值//3。对应的ACL,当前节点的权限控制//4.设置当前节点类型zooKeeper.create(key,value.getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);ZookeeperTest.closeConnection(zooKeeper);}catch(KeeperExceptione){e.printStackTrace();}catch(InterruptedExceptione){e.printStackTrace();}}/**@Authorlyy*@Description//TODO获取节点信息*@Date14:572021/9/29*@Param*@return**/publicvoidget(Stringkey){ZooKeeperzooKeeper=ZookeeperTest.getConntection();Statstat=newStat();Stringdata=null;try{byte[]bytes=zooKeeper.getData(key,null,stat);data=newString(bytes,"gbk");log.info("当前节点信息:{}",data);ZookeeperTest.closeConnection(zooKeeper);}catch(KeeperExceptione){e.printStackTrace();}catch(InterruptedExceptione){e.printStackTrace();}catch(UnsupportedEncodingExceptione){e.printStackTrace();}}/**@Authorlyy*@Description//TODO修改节点信息*@Date14:572021/9/29*@Param*@return**/publicvoidmodify(Stringkey,StringnewValue){ZooKeeperzooKeeper=ZookeeperTest.getConntection();Statstat=newStat();//版本乐观锁概念,版本信息需要这里获取,需要先获取节点信息try{//获取节点(修改需要版本信息)zooKeeper.getData(key,null,stat);//修改zooKeeper.setData(key,newValue.getBytes(),stat.getVersion());ZookeeperTest.closeConnection(zooKeeper);}catch(KeeperExceptione){e.printStackTrace();}catch(InterruptedExceptione){e.printStackTrace();}}/**@Authorlyy*@Description//TODO删除节点*@Date14:572021/9/29*@Param*@return**/publicvoiddelete(Stringkey){ZooKeeperzooKeeper=ZookeeperTest.getConntection();Statstat=newStat();try{//获取节点(delete需要版本信息)zooKeeper.getData(key,null,stat);//删除节点zooKeeper.delete(key,stat.getVersion());ZookeeperTest.closeConnection(zooKeeper);}catch(KeeperExceptione){e.printStackTrace();}catch(InterruptedExceptione){e.printStackTrace();}}}综上所述,我们对Zookeeper的了解大概是入门级的,但是Zookeeper的功能比我们在这里描述的要多得多。如何使用Zookeeper实现集群管理和分布式锁,Queue等待,小农会落后文中讲解,关注我,后续精彩内容第一时间推送

最新推荐
猜你喜欢