一、zookeeper概述
ZooKeeper是Apache软件基金会的一个开源的软件项目,是一种 为分布式应用所设计的高可用、高性能 且一致的 开源协调服务,它提供了一项基本服务:分布式锁服务。由于ZooKeeper的开源特性,后来的开发者在分布式锁的基础上,摸索了出了其他的使用方法:配置维护、组服务、分布式消息队列、分布式通知/协调等。 在分布式协调技术方面做得比较好的就是Google的Chubby还有Apache的ZooKeeper都是分布式锁的实现者。Chbby是非开源的,Google自家用。后来雅虎模仿Chubby开发出了ZooKeeper,也实现了类似的分布式锁的功能,并且将ZooKeeper作为一种开源的程序捐献给了Apache。注意:ZooKeeper性能上的特点决定了它能够用在大型的、分布式的系统中。可靠性方面来,它并不会因为一个节点的错误 而崩溃。除此之外,它严格的序列访问控制 意味着复杂的控制原语 可以应用在客户端上。ZooKeeper在一致性、可用性、容错性的保证,也是ZooKeeper的成功之处,它获得的一切成功都与它采用的协议——Zab协议是密不可分的。 ZooKeeper在实现这些服务(分布式锁、配置维护、组服务等)时,首先它设计一种新的数据结构——Znode,然后在该数据结构的基础上定义了一些原语,也就是一些关于该数据结构的一些操作。有了这些数据结构和原语还不够,因为ZooKeeper是工作在一个分布式的环境下,服务是通过消息以网络的形式发送给分布式应用程序,所以还需要一个通知机制——Watcher机制。那么总结一下,ZooKeeper所提供的服务主要是通过:数据结构+原语+watcher机制,三个部分来实现的。
二、应用场景
-
命名服务
命名服务就是指通过指定的名字来获取资源或者服务的地址。Zookeeper会在自己的文件系统上(树结构的文件系统)创建一个以路径为名称的节点,它可以指向提供的服务的地址,远程对象等。简单来说使Zookeeper做命名服务就是用路径作为名字,路径上的数据就是其名字指向的实体,类似于JAVA的 JNDI (java name directory interface) 都是将有层次的目录结构关联到一定资源上,但是 Zookeeper 的命名服务更加是广泛意义上的关联,也许你并不需要将名称关联到特定资源上,你可能只需要一个不会重复名称,就像数据库中产生一个唯一的主键一样。
-
配置管理
配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中
-
集群管理
Master启动 在引入了Zookeeper以后启动两个主节点,"主节点-A"和"主节点-B" 启动以后,都向ZooKeeper去注册一个节点(znode)。假设"主节点-A"锁注册znode是"master-00001", "主节点-B"注册的节点是"master00002",注册完以后进行选举,编号最小的节点将在选举中获胜获得锁成为主节点,也就是"主节点-A"将会获得锁成为主节点,然后"主节点-B"将被阻塞成为一个备用节点。通过这种方式就完成了对两个Master进程的调度。 Master故障 如果"主节点-A"挂了,此时它所注册的节点将被自动删除,ZooKeeper会自动感知节点的变化,然后再次发出选举,此时”主节点-B"将在选举中获胜,替代"主节点-A"成为主节点。恢复了,它会再次向ZooKeeper注册一个节点(znode),此时它注册的znode将会是"master-00003",ZooKeeper会感知节点的变化再次发动选举,这时候"主节点-B"在选举中会再次获胜继续担任"主节点","主节点-A"会担任备用节点
-
分布式锁服务
类似进程互斥 不允许两个以上的共享该资源的并发进程同时进入临界区:(访问公用数据的那段程序)先来回顾一下多线程中的锁控制。开启了五个线程,每个线程通过plus()方法对静态变量counter分别进行20次累加,预期counter最后会变成100。运行程序:可以发现,五个线程执行完毕之后,counter并没有变成100。plus()方法涉及到对公共资源的改动,但是并没有对它进行同步控制,可能会造成多个线程同时对公共资源发起改动,进而出现并发问题。问题的根源在于,上例中没有保证同一时刻只能有一个线程可以改动公共资源。给plus()方法加上synchronized关键字
synchronized关键字的作用是对plus()方法加入锁控制,一个线程想要执行该方法,首先需要获得锁(锁是唯一的),执行完毕后,再释放锁。如果得不到锁,该线程会进入等待池中等待,直到抢到锁才能继续执行。这样就保证了同一时刻只能有一个线程可以改动公共资源,避免了并发问题。共享锁在同一个进程中很容易实现,可以靠Java本身提供的同步机制解决,但是在跨进程或者在不同 Server 之间就不好实现了,这时候就需要一个中间人来协调多个Server之间的各种问题,比如如何获得锁/释放锁、谁先获得锁、谁后获得锁等,借助Zookeeper 可以实现这种分布式锁:需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren()方法获取列表中最小的目录节点,如果最小节点就是自己创建的目录节点,那么它就获得了这个锁,如果不是那么它就调用 exists() 方法并监控前一节点的变化,一直到自己创建的节点成为列表中最小编号的目录节点,从而获得锁。释放锁很简单,只要删除它自己所创建的目录节点就行了
-
分布式队列
分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。 如果你试图向一个 已经满了的阻塞队列中添加一个元素或者是从一个空的阻塞队列中移除一个元索,将导致线程阻塞.在多线程进行合作时,阻塞队列是很有用的工具。工作者线程可以定期地把中间结果存到阻塞队列中而其他工作者线程把中间结果取出并在将来修改它们。很多单机上很平常的事情,放在集群环境中都会发生质的变化。在跨进程或者分布式环境下呢?比如,一台机器运行生产者程序,另一台机器运行消费者程序,代表邮筒的有序队列无法跨机器共享,但是两者需要随时了解邮筒的状态(是否已满、是否已空)以及保证信件的有序(先到达的先发送)。这种情况下,可以借助ZooKeeper实现一个分布式队列。新建一个“/mailBox”节点代表邮筒。一旦有信件到达,就在该节点下创建PERSISTENT_SEQUENTIAL类型的子节点,当子节点总数达到上限时,阻塞生产者,然后使用getChildren(String path, Watcher watcher)方法监控子节点的变化,子节点总数减少后再回复生产;而消费者每次选取序号最小的子节点进行处理,然后删除该节点,当子节点总数为0时,阻塞消费者,同样设置监控,子节点总数增加后再回复消费。
-
讲解zookeeper
zookeeper是Apache软件基金会的一款开源的软件项目,是一种为分布式应用设计的高可用、高性能且一致的开源协调服务,主要用来解决分布式集群中应用系统的一致性问题。
***----应用场景:
1 统一命名服务
2 配置管理
3 集群管理
4 分布式锁服务
5 分布式队列