java大数据之zookeeper+springboot

1. zookeeper简介

java大数据之zookeeper+springboot

1.1. ZooKeeper 的由来

Zookeeper最早起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。所以,雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。

关于"ZooKeeper"这个项目的名字,其实也有一段趣闻。在立项初期,考虑到之前内部很多项目都是使用动物的名字来命名的(例如著名的Pig项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家RaghuRamakrishnan(罗摩克里希纳)开玩笑地说:"在这样下去,我们这儿就变成动物园了!"此话一出,大家纷纷表示就叫动物园管理员吧一一一因为各个以动物命名的分布式组件放在一起,雅虎的整个分布式系统看上去就像一个大型的动物园了,而Zookeeper正好要用来进行分布式环境的协调一一于是,Zookeeper的名字也就由此诞生了。

1.2. ZooKeeper 概览

java大数据之zookeeper+springboot

Zookeeper 一个最常用的使用场景就是用于担任服务生产者和服务消费者的注册中心。 服务生产者将自己提供的服务注册到Zookeeper中心,服务的消费者在进行服务调用的时候先到Zookeeper中查找服务,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据。如下图所示,在 Dubbo架构中 Zookeeper 就担任了注册中心这一角色。

java大数据之zookeeper+springboot

2. zookeeper安装配置

1. 文件准备

将下载下来的Zookeeper 的配置文件进行解压 在linux上输入:

tar -xvf zookeeper-3.4.10.tar.gz

然后移动到/opt/zookeeper里面,没有就新建,然后将文件夹重命名为zookeeper3.4 输入

mkdir /opt/zookeeper

mv zookeeper-3.4.10 /opt/zookeeper/zookeeper3.4

2. 环境配置(省略,前期已经做过了)

编辑 /etc/profile 文件 输入:

export ZK_HOME=/opt/zookeeper/zookeeper3.4

export PATH=.:${JAVA_HOME}/bin:${SCALA_HOME}/bin:${SPARK_HOME}/bin:${ZK_HOME}/bin:$PATH

输入:

source /etc/profile

使配置生效

3. 修改配置文件

3.1. 创建文件和目录

在集群的服务器上都创建这些目录

mkdir /opt/zookeeper/data

mkdir /opt/zookeeper/dataLog

并且在/opt/zookeeper/data目录下创建myid文件 输入:

touch myid

创建成功之后,更改myid文件。 我这边为了方便,将master、slave1、slave2的myid文件内容改为1,2,3

3.2. 新建zoo.cfg

切换到/opt/zookeeper/zookeeper3.4/conf 目录下 如果没有 zoo.cfg 该文件,就复制zoo_sample.cfg文件并重命名为zoo.cfg。 修改这个新建的zoo.cfg文件

dataDir=/opt/zookeeper/data

dataLogDir=/opt/zookeeper/dataLog

server.1=master:2888:3888

server.2=slave1:2888:3888

server.3=slave2:2888:3888

配置说明

client port,顾名思义,就是客户端连接zookeeper服务的端口。这是一个TCP port。

dataLogDir里是放到的顺序日志(WAL)。而dataDir里放的是内存数据结构的snapshot,便于快速恢复。为了达到性能最大化,一般建议把dataDir和dataLogDir分到不同的磁盘上,这样就可以充分利用磁盘顺序写的特性。dataDir和dataLogDir需要自己创建,目录可以自己制定,对应即可。

server.1中的这个1需要和master这个机器上的dataDir目录中的myid文件中的数值对应。server.2中的这个2需要和slave1这个机器上的dataDir目录中的myid文件中的数值对应。server.3中的这个3需要和slave2这个机器上的dataDir目录中的myid文件中的数值对应。当然,数值你可以随便用,只要对应即可。2888和3888的端口号也可以随便用,因为在不同机器上,用成一样也无所谓。

其他配置说明:1.tickTime:CS通信心跳数 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。tickTime以毫秒为单位。 tickTime=2000 2.initLimit:LF初始通信时限 集群中的follower服务器(F)与leader服务器(L)之间初始连接时能容忍的最多心跳数(tickTime的数量)。 initLimit=10 3.syncLimit:LF同步通信时限 集群中的follower服务器与leader服务器之间请求和应答之间能容忍的最多心跳数(tickTime的数量)。 syncLimit=5

依旧将zookeeper传输到其他的机器上,记得更改 /opt/zookeeper/data 下的myid,这个不能一致。 输入:

scp -r /opt/zookeeper root@slave1:/opt

scp -r /opt/zookeeper root@slave2:/opt

修改myid

4. 启动zookeeper

因为zookeeper是选举制,它的主从关系并不是像hadoop那样指定的,具体可以看官方的文档说明。 成功配置zookeeper之后,

在每台机器上启动zookeeper。 切换到zookeeper目录下

cd /opt/zookeeper/zookeeper3.4/bin

输入:

zkServer.sh start

成功启动之后 查看状态输入:

zkServer.sh status

可以查看各个机器上zookeeper的leader和follower ,只能一个是主,随机选择,入下图:

java大数据之zookeeper+springboot

3. zookeeper结构和命令

3.1. zookeeper特性

1、Zookeeper:一个leader,多个follower组成的集群

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

3、分布式读写,更新请求转发,由leader实施

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

5、数据更新原子性,一次数据更新要么成功,要么失败

6、实时性,在一定时间范围内,client能读到最新数据

3.2. zookeeper数据结构

1、层次化的目录结构,命名符合常规文件系统规范(见下图)

2、每个节点在zookeeper中叫做znode,并且其有一个唯一的路径标识

3、节点Znode可以包含数据和子节点

3.3. 数据结构的图

java大数据之zookeeper+springboot

3.4. 节点类型

1、Znode有两种类型:

短暂(ephemeral)(断开连接自己删除)

持久(persistent)(断开连接不删除)

2、Znode有四种形式的目录节点(默认是persistent )

PERSISTENT

PERSISTENT_SEQUENTIAL(持久序列/test0000000019 )

EPHEMERAL

EPHEMERAL_SEQUENTIAL

3、创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护

4、在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号推断事件的顺序

ZooKeeper的数据模型是内存中的一个ZNode数,由斜杠(/)进行分割的路径,就是一个ZNode,每个ZNode上除了保存自己的数据内容,还保存一系列属性信息;

ZooKeeper中的数据节点分为两种:持久节点和临时节点。

所谓的持久节点是指一旦这个ZNode创建成功,除非主动进行ZNode的移除操作,节点会一直保存在ZooKeeper上;而临时节点的生命周期是跟客户端的会话相关联的,一旦客户端会话失效,这个会话上的所有临时节点都会被自动移除

3.5. zookeeper命令行操作

运行 zkCli.sh –server 进入命令行工具

在linux cli输入:./zkCli.sh -server hostIP/hostName

[zk: master(CONNECTED) 0]说明已经进入zookeeper命令行

java大数据之zookeeper+springboot

1、使用 ls 命令来查看当前 ZooKeeper 中所包含的内容:

ls /

java大数据之zookeeper+springboot

2、创建一个新的 znode ,使用 create /zk myData 。这个命令创建了一个新的 znode 节点" zk "以及与它关联的字符串:

create /zk "myData"

java大数据之zookeeper+springboot

3、我们运行 get 命令来确认 znode 是否包含我们所创建的字符串:

get /zk

java大数据之zookeeper+springboot

· czxid. 节点创建时的zxid.

· mzxid. 节点最新一次更新发生时的zxid.

· ctime. 节点创建时的时间戳.

· mtime. 节点最新一次更新发生时的时间戳.

· pZxid 这个节点就和子节点有关啦!是与 该节点的子节点(或该节点)的最近一次 创建 / 删除 的时间戳对应

·

java大数据之zookeeper+springboot

· dataVersion. 节点数据的更新次数.

· cversion. 其子节点的更新次数.

· aclVersion. 节点ACL(授权信息)的更新次数.

· ephemeralOwner. 如果该节点为ephemeral节点, ephemeralOwner值表示与该节点绑定的session id. 如果该节点不是ephemeral节点, ephemeralOwner值为0x0. 至于什么是ephemeral节点, 请看后面的讲述.

· dataLength. 节点数据的字节数.

· numChildren. 子节点个数.

#监听这个节点的变化,当另外一个客户端改变/zk时,它会打出下面的

#WATCHER::

#WatchedEvent state:SyncConnected type:NodeDataChanged path:/zk

get /zk watch

4、下面我们通过 set 命令来对 zk 所关联的字符串进行设置:

set /zk "aaa"

5、下面我们将刚才创建的 znode 删除:

delete /zk

java大数据之zookeeper+springboot

6、删除节点(包含子节点):rmr

rmr /zk

java大数据之zookeeper+springboot

7,短暂节点ephemeral和持久节点persistent的区别:

创建短暂节点:

java大数据之zookeeper+springboot

然后使用close断开连接

java大数据之zookeeper+springboot

发现在其他机器上,close之前可以正常获取节点信息,但是close之后无法获取节点信息。如下图:

java大数据之zookeeper+springboot

3.6. zookeeper-api应用

3.6.1. 基本使用

org.apache.zookeeper.Zookeeper是客户端入口主类,负责建立与server的会话

它提供了表 1 所示几类主要方法 :

3.6.2. 增删改查(springboot)

java大数据之zookeeper+springboot

 package com.aaa.qy87.zookeeper;

import org.apache.zookeeper.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;

/**
* @Author: 陈建
* @Date: 2019/2/18 0018 15:50
* @Version 1.0
* 测试zookeeperAPI
*/
public class MyZookeeper {
//zookeeper实例
ZooKeeper zk=null;
//sessionTimeout连接超时时间
int sessionTimeout=30000;
Watcher wc= new Watcher() {
@Override
public void process(WatchedEvent event) {

}
};

/**
* 初始化zookeeper实例
*/
@Before
public void initZookeeperConnect() throws IOException {
//String connectString, int sessionTimeout, Watcher watcher
zk= new ZooKeeper("192.168.153.101:2181",sessionTimeout,wc);
}

/**
* 创建节点
*/
@Test
public void createNode() throws KeeperException, InterruptedException {
//第一个参数path:the path for the node节点的路径(名称)
//第二个参数data:the initial data for the node(节点值)
//第三个参数acl:the acl for the node(节点的访问控制。权限)

//第四个参数createMode:specifying whether the node to be created is ephemeral and/or sequential
// 节点的类型,短暂还是持久
zk.create("/newNode","hello".getBytes(),ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
/**
* 查看节点
*/
@Test
public void getNodeInfo() throws KeeperException, InterruptedException {
//String path 节点路径, boolean watch是否监控, Stat stat节点状态
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回结果:"+result);
}
/**
* 修改节点
*/
@Test
public void setNodeInfo() throws KeeperException, InterruptedException {
// String path 节点路径, byte data[] 节点值, int version 版本号 默认-1
zk.setData("/newNode","xxx".getBytes(),-1);
//String path 节点路径, boolean watch是否监控, Stat stat节点状态
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回结果:"+result);
}
/**
* 删除节点
*/
@Test
public void deleteNodeInfo() throws KeeperException, InterruptedException {
// String path 节点路径, int version 版本号 默认-1
zk.delete("/newNode",-1);
//String path 节点路径, boolean watch是否监控, Stat stat节点状态
byte[] zkData = zk.getData("/newNode", false, null);
String result=new String(zkData);
System.out.println("返回结果:"+result);
}

/**

* 关闭zookeeper连接
* @throws InterruptedException
*/
@After
public void closeZookeeper() throws InterruptedException {
zk.close();
}

}
java大数据之zookeeper+springboot

4. zookeeper原理

Zookeeper虽然在配置文件中并没有指定master和slave

但是,zookeeper工作时,是有一个节点为leader,其他则为follower

Leader是通过内部的选举机制临时产生的

4.1. zookeeper全新集群的选举机制

java大数据之zookeeper+springboot

以一个简单的例子来说明整个选举的过程.假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的.假设这些服务器依序启动,来看看会发生什么.1) 服务器1启动,此时只有它一台服务器启动了,它发出去的报没有任何响应,所以它的选举状态一直是LOOKING状态2) 服务器2启动,它与最开始启动的服务器1进行通信,互相交换自己的选举结果,由于两者都没有历史数据,所以id值较大的服务器2胜出,但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是3),所以服务器1,2还是继续保持LOOKING状态.3) 服务器3启动,根据前面的理论分析,服务器3成为服务器1,2,3中的老大,而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的leader.4) 服务器4启动,根据前面的分析,理论上服务器4应该是服务器1,2,3,4中最大的,但是由于前面已经有半数以上的服务器选举了服务器3,所以它只能接收当小弟的命了.5) 服务器5启动,同4一样,当小弟.

4.2. 非全新集群的选举机制(数据恢复)

那么,初始化的时候,是按照上述的说明进行选举的,但是当zookeeper运行了一段时间之后,有机器down掉,重新选举时,选举过程就相对复杂了。

需要加入数据id、leader id和逻辑时钟。

数据id:数据新的id就大,数据每次更新都会更新id。

Leader id:就是我们配置的myid中的值,每个机器一个。

逻辑时钟:这个值从0开始递增,每次选举对应一个值,也就是说: 如果在同一次选举中,那么这个值应该是一致的 ; 逻辑时钟值越大,说明这一次选举leader的进程更新.

选举的标准就变成:

1、逻辑时钟小的选举结果被忽略,重新投票

2、统一逻辑时钟后,数据id大的胜出

3、数据id相同的情况下,leader id大的胜出

根据这个规则选出leader。


分享到:


相關文章: