
zookeeper是什么ZooKeeper是一个分布式的开放源码的分布式应用程序协调服务是Google的Chubby一个开源的实现它是集群的管理者监视着集群中各个节点的状态根据节点提交的反馈进行下一步合理操作。最终将简单易用的接口和性能高效、功能稳定的系统提供给用户Zookeeper工作原理Zookeeper 的核心是原子广播这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式它们分别是恢复模式选主和广播模式同步。当服务启动或者在领导者崩溃后Zab就进入了恢复模式当领导者被选举出来且大多数Server完成了和 leader的状态同步以后恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。为了保证事务的顺序一致性zookeeper采用了递增的事务id号zxid来标识事务。所有的提议proposal都在被提出的时候加上了zxid。实现中zxid是一个64位的数字它高32位是epoch用来标识leader关系是否改变每次一个leader被选出来它都会有一个新的epoch标识当前属于那个leader的统治时期。低32位用于递增计数。服务端角色Zookeeper中一共有以下角色Leader负责进行投票的发起和决议更新状态和数据Follower用于接收客户端请求并向客户端返回结果在选择Leader时会进行投票Observer一种功能和Follower相同但是它不参与投票过程。它主要是为了扩展系统提高读取速度ZooKeeper 默认只有 Leader 和 Follower 两种角色没有 Observer 角色。为了使用 Observer 模式在任何想变成Observer的节点的配置文件中加入:peerTypeobserver 并在所有 server 的配置文件中配置成 observer 模式的 server 的那行配置追加 :observerFollower 和 Observer 都能提供读服务不能提供写服务。两者唯一的区别在于 Observer机器不参与 Leader 选举过程也不参与写操作的『过半写成功』策略因 此 Observer 可以在不影响写性能的情况下提升集群的读性能。leader选举当leader崩溃或者leader失去大多数的follower这时候zk进入恢复模式恢复模式需要重新选举出一个新的leader让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种一种是基于basic paxos实现的另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。fast paxos流程是在选举过程中某Server首先向所有Server提议自己要成为leader当其它Server收到提议以后解决epoch和 zxid的冲突并接受对方的提议然后向对方发送接受提议完成的消息重复这个流程最后一定能选举出Leader。选完Leader以后zk就进入状态同步过程1. Leader等待server连接2 .Follower连接leader将最大的zxid发送给leader3 .Leader根据follower的zxid确定同步点4 .完成同步后通知follower 已经成为uptodate状态5 .Follower收到uptodate消息后又可以重新接受client的请求进行服务了节点ZNode每个子目录项如 NameService 都被称作为 znode(目录节点)和文件系统一样我们能够自由的增加、删除znode在一个znode下增加、删除子znode唯一的不同在于znode是可以存储数据的。有四种类型的znodePERSISTENT-持久化目录节点客户端与zookeeper断开连接后该节点依旧存在PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点客户端与zookeeper断开连接后该节点依旧存在只是Zookeeper给该节点名称进行顺序编号EPHEMERAL-临时目录节点客户端与zookeeper断开连接后该节点被删除EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点客户端与zookeeper断开连接后该节点被删除只是Zookeeper给该节点名称进行顺序编号客户端注册监听它关心的目录节点当目录节点发生变化数据改变、被删除、子目录节点增加删除时zookeeper会通知客户端。客户端SessionSession 是指客户端会话在讲解客户端会话之前我们先来了解下客户端连接。在ZooKeeper 中一个客户端连接是指客户端和 ZooKeeper 服务器之间的TCP长连接。ZooKeeper 对外的服务端口默认是2181客户端启动时首先会与服务器建立一个TCP连接从第一次连接建立开始客户端会话的生命周期也开始了通过这个连接客户端能够通过心跳检测和服务器保持有效的会话也能够向 ZooKeeper 服务器发送请求并接受响应同时还能通过该连接接收来自服务器的 Watch 事件通知。Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时只要SessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器那么之前创建的会话仍然有效。Watcher是 ZooKeeper 中一个很重要的特性。ZooKeeper允许用户在指定节点上注册一些 Watcher 并且在一些特定事件触发的时候ZooKeeper 服务端会将事件通知到感兴趣的客户端上去。该 机制是 ZooKeeper 实现分布式协调服务的重要特性。import java.util.concurrent.CountDownLatch; import org.apache.zookeeper.WatchedEvent; import org.apache.zookeeper.Watcher; import org.apache.zookeeper.Watcher.Event.EventType; import org.apache.zookeeper.Watcher.Event.KeeperState; import org.apache.zookeeper.ZooKeeper; import org.apache.zookeeper.data.Stat; /** * 分布式配置中心demo * author * */ public class ZooKeeperProSync implements Watcher { private static CountDownLatch connectedSemaphore new CountDownLatch(1); private static ZooKeeper zk null; private static Stat stat new Stat(); public static void main(String[] args) throws Exception { //zookeeper配置数据存放路径 String path /username; //连接zookeeper并且注册一个默认的监听器 zk new ZooKeeper(192.168.31.100:2181, 5000, // new ZooKeeperProSync()); //等待zk连接成功的通知 connectedSemaphore.await(); //获取path目录节点的配置数据并注册默认的监听器 System.out.println(new String(zk.getData(path, true, stat))); Thread.sleep(Integer.MAX_VALUE); } public void process(WatchedEvent event) { if (KeeperState.SyncConnected event.getState()) { //zk连接成功通知事件 if (EventType.None event.getType() null event.getPath()) { connectedSemaphore.countDown(); } else if (event.getType() EventType.NodeDataChanged) { //zk目录节点数据变化通知事件 try { System.out.println(配置已修改新值为 new String(zk.getData(event.getPath(), true, stat))); } catch (Exception e) { } } } } }使用场景服务发现以Dubbo注册中心为例这个功能类似阿里内部的HSF使用的ConfigServerDubbo是集团开源的分布式服务框架致力于提供高性能和透明化的远程服务调用解决方案和基于服务框架展开的完整SOA服务治理方案。其中服务自动发现是最核心的模块之一该模块提供基于注册中心的目录服务使服务消费方能够动态的查找服务提供方让服务地址透明化同时服务提供方可以平滑的对机器进行扩容和缩容其注册中心可以基于提供的外部接口来实现各种不同类型的注册中心例如数据库、ZK和Redis等。接下来看一下基于ZK实现的Dubbo注册中心。/dubbo: 这是Dubbo在ZK上创建的根节点。/dubbo/com.foo.BarService: 这是服务节点代表了Dubbo的一个服务。/dubbo/com.foo.BarService/Providers: 这是服务提供者的根节点其子节点代表了每个服务的真正提供者。/dubbo/com.foo.BarService/Consumers: 这是服务消费者的根节点其子节点代表了没一个服务的真正消费者Dubbo基于ZK实现注册中心的工作流程服务提供者在初始化启动的时候首先在/dubbo/com.foo.BarService/Providers节点下创建一个子节点同时写入自己的url地址代表这个服务的一个提供者。服务消费者在启动的时候读取并订阅ZooKeeper上/dubbo/com.foo.BarService/Providersz节点下的所有子节点并解析所有提供者的url地址类作为该服务的地址列表开始发起正常调用。同时在Consumers节点下创建一个临时节点写入自己的url地址代表自己是BarService的一个消费者监控中心监控中心是Dubbo服务治理体系的重要一部分它需要知道一个服务的所有提供者和订阅者及变化情况。监控中心在启动的时候会通过ZK的/dubbo/com.foo.BarService节点来获取所有提供者和消费者的url地址并注册Watcher来监听其子节点变化情况。所有服务提供者在ZK上创建的节点都是临时节点利用的是临时节点的生命周期和客户端会话绑定的特性一旦提供者机器挂掉无法对外提供服务时该临时节点就会从ZK上摘除这样服务消费者和监控中心都能感知到服务提供者的变化。配置管理中心数据发布与订阅即所谓的配置中心顾名思义就是发布者将数据发布到 ZooKeeper 节点上,供订阅者进行数据订阅进而达到动态获取数据的目的实现配置信息的集中式管理和动态更新。对于数据量通常比较小。数据内容在运行时动态变化。集群中各机器共享配置一致。这样的全局配置信息就可以发布到 ZooKeeper上让客户端集群的机器去订阅该消息。发布/订阅系统一般有两种设计模式分别是推Push和拉Pull模式。- 推模式服务端主动将数据更新发送给所有订阅的客户端- 拉模式客户端主动发起请求来获取最新数据通常客户端都采用定时轮询拉取的方式ZooKeeper 采用的是推拉相结合的方式客户端想服务端注册自己需要关注的节点一旦该节点的数据发生变更那么服务端就会向相应的客户端发送Watcher事件通知客户端接收到这个消息通知后需要主动到服务端获取最新的数据Master选举针对 Master 选举的需求通常情况下我们可以选择常见的关系型数据库中的主键特性来实现希望成为 Master 的机器都向数据库中插入一条相同主键ID的记录数据库会帮我们进行主键冲突检查也就是说只有一台机器能插入成功——那么我们就认为向数据库中成功插入数据的客户端机器成为Master。依靠关系型数据库的主键特性确实能够很好地保证在集群中选举出唯一的一个Master。但是如果当前选举出的 Master 挂了那么该如何处理谁来告诉我 Master 挂了呢显然关系型数据库无法通知我们这个事件。但是ZooKeeper 可以做到利用 ZooKeepr 的强一致性能够很好地保证在分布式高并发情况下节点的创建一定能够保证全局唯一性即 ZooKeeper 将会保证客户端无法创建一个已经存在的 数据单元节点。也就是说如果同时有多个客户端请求创建同一个临时节点那么最终一定只有一个客户端请求能够创建成功。利用这个特性就能很容易地在分布式环境中进行 Master 选举了。成功创建该节点的客户端所在的机器就成为了 Master。同时其他没有成功创建该节点的客户端都会在该节点上注册一个子节点变更的 Watcher用于监控当前 Master 机器是否存活一旦发现当前的Master挂了那么其他客户端将会重新进行 Master 选举。这样就实现了 Master 的动态选举。分布式锁分布式锁是控制分布式系统之间同步访问共享资源的一种方式。如果不同系统或同一系统不同机器之间共享了同一资源那访问这些资源时通常需要一些互斥手段来保证一致性这种情况下就需要用到分布式锁了。使用关系型数据库是一种简单、广泛的实现方案但大多数大型分布式系统中数据库已经是性能瓶颈了如果再给数据库添加额外的锁会更加不堪重负另外使用数据库做分布式锁当抢到锁的机器挂掉的话如何释放锁也是个头疼的问题。接下来看下使用ZK如何实现排他锁。排他锁的核心是如何保证当前有且只有一个事务获得锁并且锁被释放后所有等待获取锁的事务能够被通知到。和Master选举类似在需要获取排他锁时所有客户端都会试图在/exclusive_lock下创建临时子节点/exclusive_lock/lock最终只有一个客户端能创建成功该客户端就获取到了锁。同时没有获取到锁的客户端需要到/exclusive_lock节点上注册一个子节点变更的Watcher监听用于实时监听lock节点的变更情况。/exclusive_lock/lock是一个临时节点在一下两种情况下都有可能释放锁当获取锁的客户端挂掉ZK上的该节点会被删除正常执行完业务逻辑之后客户端会主动将自己创建的临时节点删除。无论在什么情况下删除了lock临时节点ZK都会通知在/exclusive_lock节点上注册了子节点变更Watcher监听的客户端重新发起锁的获取。感兴趣可以看看阿里巴巴为什么不用 ZooKeeper 做服务发现_阿里巴巴_坤宇_InfoQ精选文章