(2020-3-15)Zookeeper学习之Zookeeper概览以及部署

Posted Mr. Dreamer Z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(2020-3-15)Zookeeper学习之Zookeeper概览以及部署相关的知识,希望对你有一定的参考价值。

Zookeeper学习之Zookeeper概览以及部署

1.初识Zookeeper

1.1 什么是zookeeper

zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目。

zookeeper官网地址

它又被叫做动物管理员,是因为我们在工作中用到的很多产品都是动物标志,比如tomcat,Hadoop等。zookeeper的诞生正是为了来进行分布式管理、协调各服务,因此zookeeper是分布式的基石。

1.2 zookeeper的特点

  • zookeeper是由一个领导者(leader),多个跟随者(follower)组成的集群。

  • 集群中只要有一半以上节点存活的话,zookeeper就能正常运行。(优点是节约资源)

  • 全局数据的一致性:每个server保存一份相同的数据副本,client无论连接到哪个server,数据都是一致的。

  • 更新请求顺序执行,来自同一个client的更新请求按其发送顺序依次执行。

  • 数据更新原子性:一次数据更新要么成功,要么失败。 实时性:在一定时间范围内,client能读取到最新数据。

1.3 zookeeper的数据结构

zookeeper数据模型的结构和Unix文件系统很类似,整体可以看作是一棵树,每个节点称作一个ZNode。每一个ZNode默认能够存储1MB的数据,每一个ZNode都可以通过其路径唯一标识。

2. zookeeper的应用场景

zookeeper基本都是应用在分布式场景下。提供的服务包括:统一命令服务,统一配置管理服务,统一集群管理服务,服务器节点动态上下线,软负载均衡等。

2.1 统一命令服务

在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。比如说:IP不容易记住,而域名容易记住


这样,就可以通常服务名直接访问。

2.2 统一管理配置

在分布式环境下,配置文件同步非常常见:

  1. 一般要求在一个集群中,所有节点的配置信息是一致的。比如kafka
  2. 对配置文件修改之后,需要能够快速同步到各个节点上

zookeeper就可以做这样的事情:

  1. 可将配置文件写入zookeeper上一个ZNode。
  2. 各个客户端服务器监听这个ZNode。
  3. 一旦ZNode中的数据被修改,zookeeper将通知各个客户端服务器

2.3 统一集群管理

分布式环境中,实时掌握每个节点的状态是必要的。可以根据节点的状态进行一些调整。

zookeeper可以实现实时监控节点状态的变化:

  1. 将节点信息写入zookeeper上的一个ZNode。
  2. 监听这个ZNode可获取它的实时状态变化。

2.4 服务器动态上下线

服务端实时洞察到服务器上下线的变化。
其余还是那个原理:

  1. 将在线的服务注册到一个ZNode节点上并创建对应的值,创建的时候选择zookeeper自带的暂时节点Mode。
  2. 由于这个服务下线之后,zookeeper会自动删除该节点的值。所以,创建直接监听即可。
    下面会以代码的形式讲解~

2.5 软负载均衡

在zookeeper中记录每台服务器的访问数,能访问最少的服务器去处理最新的客户端请求。

3.zookeeper内部原理

3.1 选举机制

半数机制:集群中半数以上的机器存货,集群可用。所以zookeeper适合安装奇数台服务器。

zookeeper虽然在配置文件中并没有指定Master和Slave。但是zookeeper工作时,是有一个节点为leader,其他节点为Follower。Leader是通过内部选举机制临时产生的。

下面简单的举一个例子来说明一下:

如图,有5台服务器部署了zookeeper进行集群。按照刚才说的,zookeeper最好部署奇数台服务。然后开始进行选举。

如上图所示,人性都是自私的,第一台服务器server1直接就选自己了,然后发投票信息。由于此时,其他服务器还没有启动,所以它收不到反馈的消息。server1处于Looking(选举状态)。


如上图,此时,server2启动了。刚刚也说了,人性是自私的,它还是投给自己。同时,与之前的server1交换结果,由于server2的编号大于server1,所以server2胜出。server1变成小弟,它把票重新投给server2。but,由于此时投票数据没有大于半数,所以两个服务器依然处理选举状态—Looking。(这里大家可以想成编号大的,级别更高)


如上图,server3启动,还是给自己投票。同时和之前启动的server1,server2交换信息。刚刚也说了,由于server3的编号是最大的,所以server1和server2将票投给server3。又因为,此时server3的票数刚好大于半数了(总共5票),所以server3成为Leader,server1和server2成为小弟Follower。

后面server4和server5陆续启动,按理说,它们应该和之前的操作情况一样,得到之前所有的票数。但是,由于server3已经胜出了,server3已经得到了半数以上的票数,它已经变成老大了。所以server4和server5都变成小弟。

3.2 节点类型

持久节点:客户端和服务端断开后,创建的节点不删除。
短暂节点:客户端和服务端断开后,创建的节点自动删除。

3.3 zookeeper的角色

zookeeper有三种角色,leader,follower,observer。

  • Leader:作为整个zookeeper集群的主节点,负责相应所有对zookeeper状态变更的请求。它会将每个状态更新请求进行排序和编号,比便保证整个集群内部消息处理的FIFO,写操作都走leader。
  • Follower:除了相应本服务器的读请求之外,它还有处理leader的提议,并且在leader提交该提议时在本地也进行提交。leader和follower构成ZooKeeper集群的法定人数,也就是说,只有他们才参与新leader的选举、响应leader的提议。
  • Observer:如果ZooKeeper集群的读取负载很高,或者客户端多到跨机房,可以设置一些observer服务器,以提高读取的吞吐量。Observer和Follower比较相似,只有一些小区别:首先observer不属于法定人数,即不参加选举也不响应提议;其次是observer不需要将事务持久化到磁盘,一旦observer被重启,需要从leader重新同步整个名字空间。

4. zookeeper伪集群部署

由于只有一台服务器,那就搞一个伪集群吧(3台)

4.1 包下载

进入Zookeeper官网进行最新稳定包下载(本次实验版本为 3.6.0),地址为:http://zookeeper.apache.org/releases.html#download

4.2 包解压

执行命令进行包解压:tar zxvf zookeeper-3.6.0.tar.gz, 并更名为zookeeper-server1。

4.3 配置修改

进入Zookeeper根目录中的conf目录下输入以下命令复制配置文件 (注意:Zookeeper默认会使用配置文件名为zoo.cfg的配置文件)

4.4 配置文件说明

进入配置文件,可以看到

# The number of milliseconds of each tick
tickTime=2000
# The number of ticks that the initial 
# synchronization phase can take
initLimit=10
# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
syncLimit=5
# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
dataDir=/root/apache-zookeeper-3.6.2/zookeeper-server2/zkData
dataLogDir=/root/apache-zookeeper-3.6.2/zookeeper-server2/logs
# the port at which the clients will connect
clientPort=2182
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
# server.1=ip:port1(服务端通讯端口):port2(服务之间选举端口)
server.1=localhost:2287:3386
server.2=localhost:2288:3387
server.3=localhost:2289:3388


## Metrics Providers
#
# https://prometheus.io Metrics Exporter
#metricsProvider.className=org.apache.zookeeper.metrics.prometheus.PrometheusMetricsProvider
#metricsProvider.httpPort=7000
#metricsProvider.exportJvmInfo=true

配置文件说明如下:

  • tickTime:Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个tickTime时间就会发送一个心跳。tickTime以毫秒为单位,默认2000。
  • syncLimit:Leader和follower之间的通讯时长 最长不能超过initLimt*ticktime
  • initLimt:接受客户端链接zk初始化的最长等待心跳时长 initLimt*ticktime
  • dataDir:Zookeeper保存数据的目录,默认情况下,Zookeeper将写数据的日志文件也保存在这个目录里。
  • dataLogDir:日志文件
  • clientPort:客户端链接服务端端口号
  • Server.A=B:C:D  A:第几号服务器;B:服务IP;C:代表Leader和follower通讯端口;D:备用选leader端口

4.5 点我开始详细操作

将之前的配置文件粘贴到对应的zoo.cfg文件中,请填写自己的data路径和log路径。

4.5.1 修改zookeeper文件名

在此之前,我们在对应的路径下创建一个zookeeper的文件夹,用于存放我们需要的3个zookeeper包。

mkdir apache-zookeeper-3.6.2

4.5.2 复制对应的文件夹

进入之前安装的zookeeper文件夹下面,然后将之前的zookeeper丢到它下面,然后改名 。

mv apache-zookeeper  /root/apache-zookeeper-3.6.2
mv apache-zookeeper zookeeper-server1

接着,拷贝两份zookeeper-server1

cp -a zookeeper-server1 zookeeper-server2
cp -a zookeeper-server1 zookeeper-server3

之后可以看到已经创建成功

4.5.3 修改对应的配置文件

创建成功之后,修改对应文件下面的配置文件。conf下面的zoo.cfg文件:

将整个地方的路径和你创建的文件夹名对应起来:zookeeper-serverX

4.5.4 zookeeper启动

进入对应bin目录下分别执行以下命令启动Zookeeper:

./zkServer.sh start ../conf/zoo.cfg

然后执行 ./zkServer.sh status 命令查看集群状态,如下图:


按照顺序展示出来的zookeeper-server,可以发现zookeeper-server2成为了Leader,这也正好对应起来了我们刚刚说的半数机制。

4.5.5 zookeeper连接客户端

进入bin目录下,

./zkCli.sh

OK,大功告成!!!

4.5.6 zookeeper伪集群踩坑

我在安装的时候,出现了一些问题,导致没办法启动。

  1. 随意修改zoo.cfg的名称,导致启动时找不到。网上有很多教程上面修改了zoo.cfg的名称,改成了zoo1.cfg。不要去改,就使用zoo.cfg。zookeeper会默认使用叫zoo.cfg的配置文件。
  2. dataDir和dataLogDir配置有问题:这个地方一定要配置自己全路径。
  3. 由于data和logs文件中有version文件夹,导致无法启动:这个问题我在网上找了很久才发现。如果不能启动,那么请进入自己的data或者logs文件删除对应的version文件夹。

5. zookeeper简单代码示例

服务端:

public class DistributeServer 

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException 
        DistributeServer server = new DistributeServer();
        //1.连接zookeeper集群
        server.getConnect();
        //2.注册节点
        server.register("zoo1"); //设置对应服务器的主题名称
        //3.业务逻辑处理
        server.bussiness();
    

    private void bussiness() throws InterruptedException 
        Thread.sleep(Long.MAX_VALUE);

    

    private void register(String hostName) throws KeeperException, InterruptedException 
        String path = zkClient.create("/servers/server", hostName.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);//暂时的是因为服务器下线之后要删除对应的节点
        System.out.println(hostName+" is online");
    

    private String connectingString = "xxx:2181,xxx:2182,xxx:2183";//集群地址,改成你自己服务器的地址。
    private int sessionTimeout = 2000;
    private ZooKeeper zkClient;

    private void getConnect() throws IOException 
        zkClient = new ZooKeeper(connectingString, sessionTimeout, new Watcher() 
            @Override
            public void process(WatchedEvent watchedEvent) 
                //监听。如果程序不结束会一直监听你配置的ip和对应路径
            
        );
    

客户端:

public class DistributeClient 

    public static void main(String[] args) throws IOException, KeeperException, InterruptedException 
        DistributeClient client = new DistributeClient();
        //获取zookeeper连接
        client.getConnect();
        //注册监听
        client.getChild();

        //业务逻辑处理
        client.business();
    

    private void business() throws InterruptedException 
        Thread.sleep(Long.MAX_VALUE);
    

    private void getChild() throws KeeperException, InterruptedException 
        List<String> children = zk.getChildren("/servers", true);
        //存储服务器节点主机名称集合
        ArrayList<Object> arrayList = new ArrayList<>();
        for (String child : children) 
            byte[] data = zk.getData("/servers/" + child, true, null);
            arrayList.add(new String(data));
        

        //将所有在线主机名称打印到控制台
        System.out.println(arrayList);
    

    private String connectingString = "";//TODO 集群地址
    private int sessionTime = 2000;//超时设置
    private ZooKeeper zk;

    private void getConnect() throws IOException 
        zk = new ZooKeeper(connectingString, sessionTime, new Watcher() 
            @SneakyThrows
            @Override
            public void process(WatchedEvent watchedEvent) 
                //监听。如果程序不结束会一直监听你配置的ip和对应路径
                getChild();
            
        );
    

OK。搞定

怕就输一辈子!!!先做起来再说

以上是关于(2020-3-15)Zookeeper学习之Zookeeper概览以及部署的主要内容,如果未能解决你的问题,请参考以下文章

zookeeper学习之原理

ZooKeeper 学习之 安装 部署

Java学习之Dubbo+ZooKeeper分布式服务Demo

Zookeeper学习之Jute序列化以及通信协议详解

Zookeeper学习之Zab一致性协议

Zookeeper学习之:paxos算法