首页 > 代码库 > zookeeper入门

zookeeper入门

1.Zookeeper是什么?

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务。ZooKeeper的设计非常易于编程,它使用的是类似于文件系统那样的树形数据结构他的数据都存在内存中,所以速度非常快,当然为了避免内存数据的丢失,所以数据也可以同时保存在硬盘上。

2.Zookeeper能做什么?

场景一:有一组服务器向客户端提供某种服务(例如:我前面做的分布式网站的服务端,就是由四台服务器组成的集群,向前端集群提供服务),我们希望客户端每次请求服务端都可以找到服务端集群中某一台服务器,这样服务端就可以向客户端提供客户端所需的服务。对于这种场景,我们的程序中一定有一份这组服务器的列表,每次客户端请求时候,都是从这份列表里读取这份服务器列表。那么这分列表显然不能存储在一台单节点的服务器上,否则这个节点挂掉了,整个集群都会发生故障,我们希望这份列表时高可用的。高可用的解决方案是:这份列表是分布式存储的,它是由存储这份列表的服务器共同管理的,如果存储列表里的某台服务器坏掉了,其他服务器马上可以替代坏掉的服务器,并且可以把坏掉的服务器从列表里删除掉,让故障服务器退出整个集群的运行,而这一切的操作又不会由故障的服务器来操作,而是集群里正常的服务器来完成。这是一种主动的分布式数据结构,能够在外部情况发生变化时候主动修改数据项状态的数据机构。Zookeeper框架提供了这种服务。这种服务名字就是:统一命名服务,它和javaEE里的JNDI服务很像。

  场景二:分布式锁服务。当分布式系统操作数据,例如:读取数据、分析数据、最后修改数据。在分布式系统里这些操作可能会分散到集群里不同的节点上,那么这时候就存在数据操作过程中一致性的问题,如果不一致,我们将会得到一个错误的运算结果,在单一进程的程序里,一致性的问题很好解决,但是到了分布式系统就比较困难,因为分布式系统里不同服务器的运算都是在独立的进程里,运算的中间结果和过程还要通过网络进行传递,那么想做到数据操作一致性要困难的多。Zookeeper提供了一个锁服务解决了这样的问题,能让我们在做分布式数据运算时候,保证数据操作的一致性。

  场景三:配置管理。在分布式系统里,我们会把一个服务应用分别部署到n台服务器上,这些服务器的配置文件是相同的(例如:我设计的分布式网站框架里,服务端就有4台服务器,4台服务器上的程序都是一样,配置文件都是一样),如果配置文件的配置选项发生变化,那么我们就得一个个去改这些配置文件,如果我们需要改的服务器比较少,这些操作还不是太麻烦,如果我们分布式的服务器特别多,比如某些大型互联网公司的hadoop集群有数千台服务器,那么更改配置选项就是一件麻烦而且危险的事情。这时候zookeeper就可以派上用场了,我们可以把zookeeper当成一个高可用的配置存储器,把这样的事情交给zookeeper进行管理,我们将集群的配置文件拷贝到zookeeper的文件系统的某个节点上,然后用zookeeper监控所有分布式系统里配置文件的状态,一旦发现有配置文件发生了变化,每台服务器都会收到zookeeper的通知,让每台服务器同步zookeeper里的配置文件,zookeeper服务也会保证同步操作原子性,确保每个服务器的配置文件都能被正确的更新。

  场景四:为分布式系统提供故障修复的功能。集群管理是很困难的,在分布式系统里加入了zookeeper服务,能让我们很容易的对集群进行管理。集群管理最麻烦的事情就是节点故障管理,zookeeper可以让集群选出一个健康的节点作为master,master节点会知道当前集群的每台服务器的运行状况,一旦某个节点发生故障,master会把这个情况通知给集群其他服务器,从而重新分配不同节点的计算任务。Zookeeper不仅可以发现故障,也会对有故障的服务器进行甄别,看故障服务器是什么样的故障,如果该故障可以修复,zookeeper可以自动修复或者告诉系统管理员错误的原因让管理员迅速定位问题,修复节点的故障。大家也许还会有个疑问,master故障了,那怎么办了?zookeeper也考虑到了这点,zookeeper内部有一个“选举领导者的算法”,master可以动态选择,当master故障时候,zookeeper能马上选出新的master对集群进行管理。

3.我所在的项目用到了zookeeper去管理配置文件。把zookeeper当成一个高可用的配置存储器,几乎所有的配置文件在zookeeper上都保存一份,当需要去配置文件里面的参数的时候会首先到zookeeper的节点上读取,如果没有读到就到本地去读取。很简单的一个功能但是感觉很有用。下面就是我自己参考zookeeper文档写的一份上传配偶properties文件到zookeeper上,以及从zookeeper上读取文件。

3.1 下载zookeeper服务器端。http://zookeeper.apache.org/
3.2 解压到一个目录去,conf目录中添加一个zoo.cfg文件
里面写上
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/home/liubin/zookeeper-3.4.6/data
clientPort=2181
3.3 启动zookeeper   
$:bin/zkServer.sh start(启动)

$:bin/zkServer.sh stop(关闭)  $:bin/zkServer.sh restart(重启)  

3.4测试zookeeper是否能连接

$ bin/zkCli.sh -server localhost:2181

若进入[zk: localhost:2181(CONNECTED) 0] 则说明客户端连接成功

[zk: localhost:2181(CONNECTED) 0] ls /             ----查看节点
[ss, test, zookeeper]
[zk: localhost:2181(CONNECTED) 1] create /mytest mydata   <span style="font-family: Arial, Helvetica, sans-serif;">  ----创建节点</span>
Created /mytest
[zk: localhost:2181(CONNECTED) 2] ls /
[ss, mytest, test, zookeeper]

[zk: localhost:2181(CONNECTED) 3] delete /mytest    ---删除节点
[zk: localhost:2181(CONNECTED) 5] ls /
[ss, test, zookeeper]
3.5java中的操作

ZookeeperUtil类

public class ZookeeperUtil {<span style="white-space:pre">	</span>//根据Key获取Value值<span style="white-space:pre">	</span>public String getConfigValue(String fileName, String key) throws IOException, KeeperException, InterruptedException {<span style="white-space:pre">		</span>String zkPath = "/test/configuration/" + fileName;<span style="white-space:pre">		</span>//创建zookeeper的连接<span style="white-space:pre">		</span>ZooKeeper zk = new ZooKeeper("localhost:2181", 2000, new Watcher() {<span style="white-space:pre">			</span>@Override<span style="white-space:pre">			</span>public void process(WatchedEvent event) {<span style="white-space:pre">			</span>}<span style="white-space:pre">		</span>});<span style="white-space:pre">		</span>//读取节点上的数据,并将数据转化成properties对象。<span style="white-space:pre">		</span>InputStream is = new ByteArrayInputStream(zk.getData(zkPath, false, null));<span style="white-space:pre">		</span>Properties properties = new Properties();<span style="white-space:pre">		</span>properties.load(is);<span style="white-space:pre">		</span>zk.close();<span style="white-space:pre">		</span>//获取对应与key的value<span style="white-space:pre">		</span>return properties.getProperty(key);<span style="white-space:pre">	</span>}<span style="white-space:pre">	</span>//保存配置文件到ZK服务器上<span style="white-space:pre">	</span>public boolean savePropertyToZK(String localPropertyPath, String fileName) throws IOException, KeeperException,<span style="white-space:pre">			</span>InterruptedException {<span style="white-space:pre">		</span>//创建zookeeper的连接<span style="white-space:pre">		</span>ZooKeeper zk = new ZooKeeper("localhost:2181", 2000, new Watcher() {<span style="white-space:pre">			</span>@Override<span style="white-space:pre">			</span>public void process(WatchedEvent event) {<span style="white-space:pre">			</span>}<span style="white-space:pre">		</span>});<span style="white-space:pre">		</span>String zkPath = "/test/configuration/" + fileName;<span style="white-space:pre">		</span>//把配置文件转化为byte[]<span style="white-space:pre">		</span>InputStream is = new FileInputStream(localPropertyPath);<span style="white-space:pre">		</span>byte[] data = http://www.mamicode.com/StreamToByte(is);>
pro1.properties文件

name=Lubby
username=admin
password=123456

测试

public static void main(String[] args) {
		ZookeeperUtil zkUtil = new ZookeeperUtil();
		try {
			System.out.println(zkUtil.savePropertyToZK("src/com/lubby/test/pro1.properties", "pro1"));

			System.out.println(zkUtil.getConfigValue("pro1", "name"));
			System.out.println(zkUtil.getConfigValue("pro1", "username"));
			System.out.println(zkUtil.getConfigValue("pro1", "password"));
		} catch (IOException | KeeperException | InterruptedException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

结果

true
Lubby
admin
123456