1. Zookeeper概述
1.1 概述:
Zookeeper : 它服务的不是商家和用户,而是应用程序 。应用程序注入到 Zookeeper中后,Zookeeper就可以为这些应用程序提供服务
- 淘宝、饿了么、美团等等诸多应用都是
Zookeeper的现实版本,是一个服务平台,以淘宝为例:- 商家必须入驻淘宝,这样淘宝用户才能看到店铺
- 客户看商品,下订单购买,付款完成交易,一整套流程都是在淘宝这个服务平台上完成的。( 和商家实际上接触并不多 )
-
Zookeeper是一个 开源 、 分布式 ( 多台服务器干一件事 ) ,并且为分布式应用 提供协调服务 的Apache项目
1.2 工作机制
-
从设计模式角度上来看:zookeeper是基观 察者模式 设计的分布式服务管理框架
- 观察者模式:简单来说,就是一个监督者监视所有对象,当一个对象被修改时,则会自动通知依赖它的对象
- Zookeeper:简单来说,Zookeeper对于每一个入驻的服务/程序进行 监听 ,如果哪一个服务下线/上线,将通知客户端应用程序
-
负责 存储 、 管理 ,一些大家都关心的数据。然后接受
观察者(类似淘宝客户) 的注册,一旦这些数据发生变化,Zookeeper将 通知 已经注册的那些观察者做出相应的反应。从而实现集群中类似的 主从管理模式 -
Zookeeper = 文件系统 + 通知机制
1.3 特点
-
分布式和集群:-
无论分布式还是集群,都是有很多 人(很多台服务器) 再做事情,
-
例如:饭店招聘
-
分布式:
招了1个厨师,3个服务员,1个前台。虽然工作不一样,但是都是为这个饭店的运转工作
-
集群:
招聘3个服务员,但是这3个人的工作是一样的,这就是集群
-
-
实际运用:
- 淘宝 = 订单模块 + 用户模块 + 商品模块 + …,这就是分布式。
- 双十一,订单量巨大,就会新增很多处理订单模块的服务器。这些,新增的服务器都是用处理订单的,就是集群
-
Feature:
-
zookeeper是 一个leader、多个follower 组成的集群
-
Leader是通过内部选举机制临时产生
-
集群中,只要有半数以上的节点存活,Zookeeper就能正常工作,比如:
假设有10台服务器:
- 挂了4台能够正常工作
- 挂了5台及以上,Zookeeper服务就停止了
-
Zookeeper中的数据,具有 全局一致性 ,每台服务器都保存一分相同的数据副本。无论,客户端连接哪台服务器,数据都是一致的
-
数据的跟新,具有 原子性 。要么成功,要么失败
-
实时性,在一定的时间范围内,客户端都能读取到最新的数据
-
更新的请求 按照顺序执行,会按照发向服务器请求的顺序,逐一执行
1.4 数据结构
- 整体,可以看做是
树状结构,每一个节称为:Znode ( ZookeeperNode ) - 每个Znode默认能够存储
1MB的元数据,每个Znode的路径都是唯一的- 元数据:Metadata,又称中介数据、中继数据,是一种
描述数据的数据。简单来说,就是记录数据信息的数据
- 元数据:Metadata,又称中介数据、中继数据,是一种
1.5 应用场景
Zookeeper提供了 统一命名服务 , 统一配置管理 , 统一集群管理 , 服务器节点动态上下线 , 软负载均衡 等
1.5.1 统一命名服务
-
分布式环境下,需要对应用或者服务进行统一命名,便于识别
-
例如:服务器IP地址不易记住,但是域名很容易记住
通过域名去访问服务器
1.5.2 统一配置管理
- 分布式环境下,每台服务器的 配置文件同步 是必须的。(如果服务器一多,不可能一台一台的改)
- 通过Zookeeper进行统一配置管理:
- 能够将配置信息,写入到Zookeeper的某个节点上
- 每个客户端都会监听这个节点
- 一旦节点中的数据被修改,Zookeeper服务会通知没改客户端服务器,进行告知
1.5.3 服务器节点动态上下线
- 当服务器入驻到Zookeeper后,Zookeeper会对其进行监听。客户端能够实时获取服务器的上下线变化
1.5.4 软负载均衡
- Zookeeper会 记录 每台服务器的 访问次数 ,让访问量最小的服务器去处理最新的客户端请求
1.6 下载
2. 安装:
2.1 本地安装
现将Zookeeper的文件上传到虚拟机的 /opt 目录
2.1.1 解压
# 解压安装包
tar -zxvf apache-zookeeper-3.6.0-bin.tar.gz
# 查看
ll
# 解压后的文件名太长,改名为:zookeeper
mv apache-zookeeper-3.6.0-bin zookeeper
# 查看目录
ll
2.1.2修改配置文件
# 进入到配置文件夹中
cd /opt/zookeeper/conf
修改 zoo_sample.cfg 文件,但是不直接修改,复制一份取名为: zoo.cfg ,在此文件上修改
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ohAKhhP-1617932299530)(F:\TyporaNotes\picture\image-20210406150817929.png)]
# 复制 zoo_sample.cfg文件
cp zoo_sample.cfg zoo.cfg
# 打开 zoo.cfg
vim zoo.cfg
打开 zoo.cfg 后,修改配置:
# 存放数据的目录
dataDir=/opt/zookeeper/zkData
# 存放日志的目录
dataLogDir=/opt/zookeeper/zkLog
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJbMQAs9-1617932299532)(F:\TyporaNotes\picture\image-20210406152327672.png)]
然后 ESC , :wq 保存退出
注意 :配置后,要在 opt/zookeeper/ 这个目录下,创建 zkData 和 zkLog 这两个目录
cd /opt/zookeeper
# 创建目录
mkdir zkData
mkdir zkLog
安装完成
2.2操作Zookeeper
# 进入 Zookeeper安装目录下的 `bin` 目录
cd /opt/zookeeper/bin
-
启动Zookeeper
# 启动服务 ./zkServer.sh start -
查看当前 Zookeeper的启动状态
./zkServer.sh status -
查看进程是否启动
jps
# 只要有 QuorumPeerMain ,说明Zookeeper启动成功
-
启动客户端
./zkCli.sh # 进入客户端后 # 查看 所有Zookeeper的所有节点 ls / # 退出客户端窗口 quit -
停止Zookeeper
# 停止服务 ./zkServer.sh stop
2.3 Zookeeper配置文件的参数详解
进入配置文件目录: /opt/zookeeper/conf
找 zoo_sample.cfg , 但是我们复制了一份: zoo.cfg ,来修改
-
查看配置文件
vim zoo.cfg-
tickTime = 2000
- 单位
毫秒,是Zookeeper服务器和客户端之间的心跳时间。 - tickTime = 2000 ,即每隔 2s客户端就往服务器发送一次请求,证明还是联通状态
- 单位
-
initLimit = 10
-
(集群中的)learder 和 follower 的 初始通信时限 。
-
集群启动时 能容忍的最多心跳数
-
initLimit = 10,即 tickTime * initLimit = 10*2000 ms= 20s,也就是说 20s之内,没有心跳发出,那么视为失效的连接,leader 和 follower 彻底断开
-
-
syncLimit = 5
-
learder 和 follower 的 同步通信时限 。
-
集群启动后,learder 和 follower 的最大响应时间
-
如果超过
syncLimit * tickTime=5*2= 10s,那么 leader认为 follower已经挂掉,leader会将follower从服务器列表删除
-
-
dataDir = /opt/zookeeper/zkData
- 存放Zookeeper数据的目录
-
dataLogDir = /opt/zookeeper/zkLog
- 存放日志文件的目录
-
clientPort = 2181
- 客户端需要访问Zookeeper的端口号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-271829qy-1617932299535)(F:\TyporaNotes\picture\image-20210406162336132.png)]
-
3. Zookeeper内部原理
3.1 选举机制 ( 面试重点 )
-
半数机制:
集群中, 半数以上 的服务器存活,集群可用。所以,Zookeeper适合安装奇数台服务器
-
Leader的选举机制:
3.2 节点类型
-
持久型:
-
持久化目录节点:客户端和服务器断开连接后,创建的节点依然存在 -
持久化顺序编号目录节点:客户端和服务器断开连接后,创建的节点依然存在。创建Znode时会设置顺序标识,即在Znode名后附加一个值来标识顺序,例如:Znode001,Znode002…
-
-
短暂型:
-
临时目录节点:客户端和服务器断开连接后,创建的节点会自动删除 -
零时顺序编号目录节点:客户端和服务器断开连接后,创建的节点会自动删除。创建Znode时会设置顺序标识,即在Znode名后附加一个值来标识顺序,例如:Znode001,Znode002…
注意:顺序标识的序号,呈 i ++ 的增长。和数据库中自增类似
-
3.3 监听器原理( 面试重点 )
- 在main方法中,创建两个线程,一个负责
网络通信,一个负责监听 - 监听事件会通
过网络通信线程,发送给 Zookeeper - Zookeeper获得注册的监听事件后,立刻将
监听事件添加到监听列表中 - 当zookeeper监听到
数据变化或者路径变化就会将这个消息发送监听线程 - 就客户端接收到,数据有变后,监听线程就会在 内部调用 process方法,做出相应操作 ( process的方法需要根据需求,自己实现 )
- 常见的监听:
- 监听:子节点增减的变化
- 监听:节点数据的变化
3.4 写数据的流程
- 客户端发送一个 写入数据的请求(想在 Zookeeper的
server1上写入数据) - 如果
server1不是Leader,那么server1会把请求进一步转发给Leader - 这个
Leader会将写请求的操作,广播给每个server,server写数据操作执行成功后会通知Leader -
Leader接到 半数以上 的 server 写数据成功的通知,那么写入数据的操作成功 - 然后,
Leader会告诉server1写入数据成功 -
server1通知 客户端,写入数据成功,整个流程结束
4. Zookeeper 实战( 开发重点 )
4.1 分布式安装部署
集群思路 :先搞定一台服务器,再克隆出两台,形成3台服务器的集群(3台是最低标准)
4.1.1 安装Zookeeper
略…( 上文有介绍 )
4.1.2 配置服务器编号 - myid
-
在
/opt/zookeeper/zkData中创建myid文件cd /opt/zookeeper/zkData/ # 创建myid vim myid # i键进行编辑 (实际上是添加该服务器对应的编号:1 ) 1 # Esc :WQ # 查看myid文件 cat myid
4.1.3 修改配置文件
上文是介绍了,配置文件是复制一份并且重名为 zoo.cfg
-
打开配置文件
# 进入bin目录 cd /opt/zookeeper/conf # 打开配置文件 vim zoo.cfg -
修改配置文件:
# ---------cluster(集群)----------- server.1=192.168.2.17:2888:3888 server.2=192.168.2.18.82:2888:3888 server.3=192.168.2.19:2888:3888参数解读 : server.A = B : C : D
-
A:我们之前配置的服务器编号(/opt/zookeeper/zkData/myid中的值) -
B:该服务器的IP地址 -
C:与集群中 Leader 服务器交换信息的端口 -
D:选举机制的专用端口 (万一leader服务器宕机,需要该端口来重新选举一个服务器作为 Leader)。该端口在选举时,进行服务器之间的相互通信
-
-
查看配置文件是否修改成功:
cat /opt/zookeeper/conf/zoo.cfg
4.1.4 配置其余两台服务器
-
找到存放虚拟机数据的目录 (
D:\Program Files\vm)
-
在
D:\Program Files\vm目录下创建zk02文件夹 -
将我们刚刚配置好的服务器数据目录中 所有的
.vmx文件和.vmdk文件分别拷贝到zk02文件夹
-
打开
虚拟机—>文件—>打开( 选择zk02下的 .vmx文件 ) -
开启虚拟机后,弹出对话框,选择
我已复制该虚拟机 -
进入系统后:
- 修改Linux中的 IP地址
- 修改
/opt/zookeeper/zkData/myid为 2 - 测试网是否能通:
ping www.baidu.com
-
同理,按照相同的操作配置另外的服务器
4.1.5 集群操作
-
关闭每台服务器的防火墙
-
防火墙的状态:
-
Active: active (running):开启 -
Active: inactive (dead):关闭
-
-
常用命令:
# 查看防火墙 systemctl status firewalld # 开启防火墙 systemctl start firewalld.service # 关闭防火墙 systemctl stop firewalld.service # 设置开机自动启动防火墙 systemctl enable firewalld.service # 关闭开机制动启动防火墙 systemctl disable firewalld.service # 在不改变状态的条件下重新加载防火墙 firewall-cmd --reload
-
-
关闭防火墙之后,启动第一台服务器
# 进入bin目录 cd /opt/zookeeper/bin/ # 启动服务 ./zkServer.sh start -
查看启动状态
cd /opt/zookeeper/bin/ # 查看状态 ./zkServer.sh status会发现有一个 Error
-
注意:当启动两台服务器时,查看状态:
- 第一台:
mode: follower - 第二台:
mode: leader
- 第一台:
4.2 客户端命令行
-
启动客户端 ( Server1: 服务器编号为1 )
cd /opt/zookeeper/bin # 启动 ./zkCli.sh -
显示所有操作命令:
help -
查看Znode中的所有内容:
ls + path# 查看根目录下有哪些Znode ls / -
查看当前节点的详细数据:
ls -s + path# 查看根节点的详细数据 ls -s / -
创建普通节点:
create# 在根目录下创建USA create /USA create /UK -
在更目录
/下创建,俄罗斯节点,并保存“普京” 数据到节点上create /Ru "pujing" -
创建多级节点:
- 在日本节点下,创建 东京节点(这时候日本节点必须存在,提前创建好,不然报错)
# 先创建日本节点 create /japan # 再在日本节点下, 创建东京节点 create /japan/Tokyo -
获得节点的值:
getget /Ru -
创建临时节点(断开连接后,删除):
create -ecreate -e /YiLaKe -
创建带序号的节点:
create -s# 创建持久的顺序节点 create -s /USA/city # 执行3次 # 创建临时的顺序节点 create -s -e /USA/city -
修改节点中的值:
setset /japan/Tokyo-1 -
监听节点的值变化 或 子节点变化
-
在server3服务器上,注册监听
/USA的变化addwatch /USA -
在Server1 主机上修改
/USA的数据set /USA "hot" -
Server3会立刻响应:
WatchedEvent state:SyncConnected type:NodeDataChanged path:/USA -
如果在Server1 的
/USA下创建子节点NewYork,Server3会立刻响应WatchedEvent state:SyncConnected type:NodeCreated path:/USA/NewYork
-
-
删除:
delete -
递归参数:
deletealldeleteall /USA
4.3 API应用
4.3.1 IDEA环境的搭建
-
创建maven项目
-
导入依赖
<dependencies> <!-- org.apache.logging.log4j/log4j-core --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.3</version> </dependency> <!-- org.apache.zookeeper/zookeeper --> <dependency> <groupId>org.apache.zookeeper</groupId> <artifactId>zookeeper</artifactId> <version>3.6.0</version> </dependency> <!-- junit/junit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> -
在resource目录下创建
log4j.properties
4.3.2 创建Zookeeper对象
其实就是创建Zookeeper的客户端
public ZooKeeper(
String connectString,
int sessionTimeout,
Watcher watcher
)
参数详解:
-
connectString: ip:port 这种格式,多个的话用逗号,隔开(不能有空格) -
sessionTimeout:- 一般设置60*1000,就是60s
- 一定不能太小。因为Zookeeper加载集群环境,会因为性能等原因而延迟过高。如果设置过小,在没有创建好客户端,就会操作节点,发生 空指针异常
-
watcher:- Watcher是一个监听器接口,但是这里需要的是 对象,所以用匿名内部类
- 要重写process方法:客户端收到监听信息后,会在内部调用process方法,做出相应操作
public class TestZK {
private String connectString =
"192.168.2.17:2181," +
"192.168.2.18:2181," +
"192.168.2.19:2181";
private int sessionTimeout = 120 * 1000;
/**
* 客户端对象
*/
private ZooKeeper zkClient;
@Test
public void init() throws Exception{
zkClient=new ZooKeeper(connectString, sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
System.out.println("监听到,并且正在处理");
}
});
}
}
4.3.3 创建节点
// create方法, 客户端对象调用
public String create(
String path,
byte[] data,
List<ACL> acl,
CreateMode createMode
)
参数详解:
-
path:节点路径 - data: 节点存储的内容 (byte字节的数组,可以把
String用getBytes方法转换) -
acl:节点的权限- 一个ACL就是一个
id-permission对,类似于key-value键值对 -
permission:是一个int表示的位码,每一位代表一种操作对应的允许状态,类似于Linux的文件权限,不同的是共有5种操作:CREATE、READ、WRITE、DELETE、ADMIN(admin 对应更改ACL权限)- OPEN_ACL_UNSAFE :创建开放节点,允许任意操作 ( 常用 )
- READ_ACL_UNSAFE :创建自读节点
- CREATOR_ALL_ACL :创建者才有全部权限
- 一个ACL就是一个
-
createMode:创建节点的类型,比如:持久型的,还是临时节点- CreateMode. PERSISTENT :持久节点
- CreateMode. PERSISTENT_SEQUENTIA :持久带序号的节点
- CreateMode. EPHEMERAL :临时节点
- CreateMode. EPHEMERAL_SEQUENTIAL :临时带序号的节点
// 创建节点
String str = zkClient.create(
"/usa",
"要存储的数据".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.PERSISTENT
);
然后在服务端查看:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mLYiEgJY-1617932299543)(F:\TyporaNotes\picture\image-20210407180633868.png)]
节点创见成功
4.3.4 获取节点数据
public byte[] getData(
String path,
boolean watch,
Stat stat
)
参数详解:
-
String path:想要获取数据的节点的路径 -
boolean watch:是用来注册的通知接口,如果节点发生变更,就会通过当前的接口通知客户端。 -
Stat stat:是用来描述该数据节点的状态信息
// 获取数据
byte[] data = zkClient.getData("/usa", false, new Stat());
// 将获取到的数据,转为String
String str = new String(data);
4.3.4 修改节点:
public Stat setData(
String path,
byte[] data,
int version
)
参数详解:
-
String path:要修改的目标节点 -
byte[] data:修改的内容 -
int version:版本号 (在服务器上用:ls -s查看)
// 修改 /usa 节点上的数据
Stat stat = zkClient.setData(
"/usa",
"usa节点的值已修改".getBytes(),
0
);
4.3.5 删除节点
public void delete(String path, int version)
参数详解:
-
String path:要删除的目标节点 -
int version:版本号 (在服务器上用:ls -s查看)
zkClient.delete("/usa", 1);
4.3.6 获取子节点
public List<String> getChildren(String path, boolean watch)
参数详解:
-
String path:目标节点路径 -
boolean watch: 监听还是不监听
返回值:
-
List<String>:目标节点的所有子节点集合
List<String> list = zkClient.getChildren("/USA", false);
4.3.7 节点的监听
和获取字节点的方法一样,值时参数 watch 为true,变为了监听状态
// 监听根节点
List<String> list = zkClient.getChildren("/", true);
如果 / 目录下,有节点发生变化,就会被监听到,调用内部的 process 方法。 process 方法在我们创建 Zookeeper对象 时就会定义
4.3.8 节点的判断
public Stat exists(String path, boolean watch)
例子:
Stat stat = zkClient.exists("/YiLaKe", false);
// 判断
if (stat==null){
System.out.println("节点不存在");
}else {
System.out.println("节点存在");
}
4.4 实战模拟 — 美团商家的上下线
4.4.1 需求分析
-
模拟美团的服务平台
- 商家营业通知
- 商家打烊通知
-
提前在更节点下,创建好
/meituan节点
4.4.2 代码
1. pom文件的配置
同 4.3.1…
2. 商家服务类
…待跟新
3. 客户类
…待跟新