Zookeeper服务端安装操作

1.zookeeper的安装与启动(构建注册中心)

准备虚拟机镜像

安装在Linux服务器里面(本次演示安装在虚拟机Linux系统里面)
需要jdk环境,把网络设置成为仅主机,然后网段设置为25网段(是因为我们后面要去做图片的上传服务器,这个服务器就是25网段,是固定的了,为了让我们服务器处在同一个局域网里面,所以品优购的镜像也设置为25网段就好了)

加载Linux虚拟机镜像, 网段设置为仅主机模式和25网段
E:\新班就业资料\项目一\资源\Linux镜像\品优购服务器 从这里里面找,复制到你要存放的位置,点击下面东西加载镜像


Zookeeper实操[老的有时间整理一下] - 图1
点击我已移动该虚拟机 它会用原来的ip地址
如果点我已复制该虚拟机 它会基于当前网络重新分配一个ip地址
网段如果设置完了,你点哪个都可以

登录
用户名是root 密码是itcast
Zookeeper实操[老的有时间整理一下] - 图2
然后需要固定ip地址防止每次启动虚拟机的时候ip地址会变,项目ip设置也会变,具体看 我虚拟机使用的笔记.

安装zookeeper软件

E:\新班就业资料\项目一\资源\配套软件\Dubbox 下找zookeeper-3.4.6.tar.gz上传到你的Linux服务器里面
用CRT连接虚拟机
Zookeeper实操[老的有时间整理一下] - 图3
输入 rz 回车
开始上传
Zookeeper实操[老的有时间整理一下] - 图4
再按 rz 回车就开始上传

等会儿输入 ll (LL 的小写) 查看
Zookeeper实操[老的有时间整理一下] - 图5
已经进去了
开始解压软件
tar -zxvf XXXXXXX (注意tar后面有英文空格)
Zookeeper实操[老的有时间整理一下] - 图6解压完成
需要创建目录存放数据(因为zookeeper是注册中心,需要存放数据)
cd zookeeper的文件里面
Zookeeper实操[老的有时间整理一下] - 图7
mkdir data
创建完了文件后,ll 查看一下
Zookeeper实操[老的有时间整理一下] - 图8

创建完了还需要配置一下这个目录(只是创建完了 zookeeper不知道这个文件夹)
输入:cd data //进入data目录
再输入: pwd // 显示当前用户的绝对路径
屏幕显示:

/root/zookeeper-3.4.6/data 复制一下这个绝对路径留着,(需要找一个配置文件配置目录用到这个路径)

cd ../conf //进入conf文件夹里面
ll //找到zoo_sample.cfg 这个就是zookeeper的配置文件,但是需要把下划线的 sample去掉
Zookeeper实操[老的有时间整理一下] - 图9
输入: mv zoo_sample.cfg zoo.cfg //改名
输入 vim zoo.cfg ///编辑文件
Zookeeper实操[老的有时间整理一下] - 图10

输入 i 进入编辑模式,把这个路径修改一下
Zookeeper实操[老的有时间整理一下] - 图11
修改后的
点击esc 退出编辑 输入 :wq 保存

启动zookeeper软件

zookeeper也是一个项目,所以我们需要先进入bin目录下,
从头开始进入项目
先ll 查看根文件夹所有东西
Zookeeper实操[老的有时间整理一下] - 图12
再cd zookeeper-3.4.6 进入zookeeper目录
再ll 一下能看到里面有个bin文件夹,
cd bin 进入bin文件夹之后 ll 一下查看所有文件,能看到一个 zkServer.sh 这时候在当前文件夹启动zookeeper 即可:
输入: ./zkServer.sh start 命令
看到下面说明启动成功:
Zookeeper实操[老的有时间整理一下] - 图13
启动成功

查看是否启动zookeeper

注意ps 和-ef之间有个空格

ps -ef | grep zookeeper 能看到进程号之类的就是启动成功了

有进程就启动成功
Zookeeper实操[老的有时间整理一下] - 图14

2.开启关闭查看Zookeeper服务(在linux系统下)

直接在linux黑窗口里面输入命令
启动zookeeper服务(进入zookeeper目录然后) zkServer.sh start

[root@localhost ~]# sh zookeeper-3.4.5/bin/zkServer.sh start
出现下面儿字眼表示启动成功
JMX enabled by default
Using config: /root/zookeeper-3.4.5/bin/../conf/zoo.cfg
Starting zookeeper … STARTED

停止zookeeper服务(进入zookeeper目录然后) zkServer.sh stop
[root@localhost ~]# sh zookeeper-3.4.5/bin/zkServer.sh stop
出现下面的表示停止成功
JMX enabled by default
Using config: /root/zookeeper-3.4.5/bin/../conf/zoo.cfg
Stopping zookeeper … STOPPED
查看zookeeper的启动状态
Zookeeper实操[老的有时间整理一下] - 图15

3.管理中心的部署

是用来查看注册了哪些服务的, 是web项目.部署到服务器里面的tomcat里面

去找这个管理中心:
E:\新班就业资料\项目一\资源\配套软件\Dubbox 下的dubbox-master.zip

解压后找到
E:\新班就业资料\项目一\资源\配套软件\Dubbox\dubbox-master\dubbo-admin
Zookeeper实操[老的有时间整理一下] - 图16
在这个目录下面打包
按住 shift 在此处打开命令窗口
输入 mvn package -Dmaven.skip.test=true //打包并且跳过测试文件

上传tomcat和服务war包

rz 去找tomcat压缩包 .gz结尾的
然后解压, 进入到 tomcat文件夹里面的webapps
从里面上传dubbo-admin.war 到webapps里面

启动Tomcat:

cd ../
cd bin/
ll //看看目录
./startup.sh //启动tomcat服务器

Tom启动成功
Zookeeper实操[老的有时间整理一下] - 图17

访问服务中心

访问项目在本地浏览器输入 ip地址端口号加项目名
http://192.168.25.144:8080/dubbo-admin/ 回车
如果出错了检查 端口号和ip地址 还有项目名

Zookeeper实操[老的有时间整理一下] - 图18
用户名和密码都是 root

Zookeeper实操[老的有时间整理一下] - 图19


(二)管理端的使用

192.168.25.144 是部署的linux的主机地址
访问网站http://192.168.25.144:8080/dubbo-admin/
用户名: root
密码: root



命令行

(一)服务端常用命令


在准备好相应的配置之后,可以直接通过zkServer.sh 这个脚本进行服务的相关操作
启动ZK服务: sh bin/zkServer.sh start 
查看ZK服务状态: sh bin/zkServer.sh status 
停止ZK服务: sh bin/zkServer.sh stop 
重启ZK服务: sh bin/zkServer.sh restart

(二)客户端常用命令



使用 zkCli.sh -server 127.0.0.1:2181 连接到 ZooKeeper 服务,连接成功后,系统会输出 ZooKeeper 的相关环境以及配置信息。 命令行工具的一些简单操作如下:

l 显示根目录下、文件: ls / 使用 ls 命令来查看当前 ZooKeeper 中所包含的内容
l 显示根目录下、文件: ls2 / 查看当前节点数据并能看到更新次数等数据
l 创建文件,并设置初始内容: create /zk “test” 创建一个新的 znode节点“ zk ”以及与它关联的字符串 [-e] [-s] 【-e 零时节点】 【-s 顺序节点】
l 获取文件内容: get /zk 确认 znode 是否包含我们所创建的字符串 [watch]【watch 监听】
l 修改文件内容: set /zk “zkbak” 对 zk 所关联的字符串进行设置 
l 删除文件: delete /zk 将刚才创建的 znode 删除,如果存在子节点删除失败
l 递归删除:rmr /zk将刚才创建的 znode 删除,子节点同时删除
l 退出客户端: quit 
l 帮助命令: help

1.ACL命令常用命令


再回过头来看下ACL权限

Zookeeper的ACL(Access Control List),分为三个维度:scheme、id、permission
通常表示为:scheme:id:permission
l schema:代表授权策略
l id:代表用户
l permission:代表权限

Scheme

world:
默认方式,相当于全世界都能访问
auth:
代表已经认证通过的用户(可以通过addauth digest user:pwd 来添加授权用户)
digest:
即用户名:密码这种方式认证,这也是业务系统中最常用的
ip:
使用Ip地址认证

id

id是验证模式,不同的scheme,id的值也不一样。

scheme为auth时:
username:password
scheme为digest时:
username:BASE64(SHA1(password))
scheme为ip时:
客户端的ip地址。
scheme为world时 anyone。

Permission

CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)
CREATE(c):创建子节点的权限
DELETE(d):删除节点的权限
READ(r):读取节点数据的权限
WRITE(w):修改节点数据的权限
ADMIN(a):设置子节点权限的权限

ACL命令

①getAcl
获取指定节点的ACL信息

create /testDir/testAcl deer # 创建一个子节点
getAcl /testDir/testAcl # 获取该节点的acl权限信息

②setAcl
设置指定节点的ACL信息

setAcl /testDir/testAcl world:anyone:crwa # 设置该节点的acl权限
getAcl /testDir/testAcl # 获取该节点的acl权限信息,成功后,该节点就少了d权限

create /testDir/testAcl/xyz xyz-data # 创建子节点
delete /testDir/testAcl/xyz # 由于没有d权限,所以提示无法删除

③addauth
注册会话授权信息
1)Auth
addauth digest user1:123456 # 需要先添加一个用户
setAcl /testDir/testAcl auth:user1:123456:crwa # 然后才可以拿着这个用户去设置权限

getAcl /testDir/testAcl # 密码是以密文的形式存储的

create /testDir/testAcl/testa aaa
delete /testDir/testAcl/testa # 由于没有d权限,所以提示无法删除

退出客户端后:
ls /testDir/testAcl #没有权限无法访问
create /testDir/testAcl/testb bbb #没有权限无法访问

addauth digest user1:123456 # 重新新增权限后可以访问了
2)Digest
auth与digest的区别就是,前者使用明文密码进行登录,后者使用密文密码进行登录

create /testDir/testDigest data
addauth digest user1:123456
setAcl /testDir/testDigest digest:user1:HYGa7IZRm2PUBFiFFu8xY2pPP/s=:crwa # 使用digest来设置权限

注意:这里如果使用明文,会导致该znode不可访问

通过明文获得密文
shell>
java -Djava.ext.dirs=/soft/zookeeper-3.4.12/lib -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar org.apache.zookeeper.server.auth.DigestAuthenticationProvider deer:123456

deer:123456->deer:ACFm5rWnnKn9K9RN/Oc8qEYGYDs=


④acl命令行ip
create /testDir/testIp data
setAcl /testDir/testIp ip:192.168.30.10:cdrwa

getAcl /testDir/testIp

2.常用四字命令

ZooKeeper 支持某些特定的四字命令字母与其的交互。用来获取 ZooKeeper 服务的当前状态及相关信息。可通过 telnet 或 nc 向 ZooKeeper 提交相应的命令 :

当然,前提是安装好了nc

yum install nc


echo stat|nc 127.0.0.1 2181 来查看哪个节点被选择作为follower或者leader 
使用echo ruok|nc 127.0.0.1 2181 测试是否启动了该Server,若回复imok表示已经启动。 
echo dump| nc 127.0.0.1 2181 ,列出未经处理的会话和临时节点。 
echo kill | nc 127.0.0.1 2181 ,关掉server 
echo conf | nc 127.0.0.1 2181 ,输出相关服务配置的详细信息。 
echo cons | nc 127.0.0.1 2181 ,列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息 
echo envi |nc 127.0.0.1 2181 ,输出关于服务环境的详细信息(区别于 conf 命令)。 
echo reqs | nc 127.0.0.1 2181 ,列出未经处理的请求。 
echo wchs | nc 127.0.0.1 2181 ,列出服务器 watch 的详细信息。 
echo wchc | nc 127.0.0.1 2181 ,通过 session 列出服务器 watch 的详细信息,它的输出是一个与 watch 相关的会话的列表。 
echo wchp | nc 127.0.0.1 2181 ,通过路径列出服务器 watch 的详细信息。它输出一个与 session 相关的路径。

3.ZooKeeper 日志可视化

前面以及讲了两个非常重要的配置一个是dataDir,存放的快照数据,一个是dataLogDir,存放的是事务日志文件

java -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar:/soft/zookeeper-3.4.12/lib/slf4j-api-1.7.25.jar org.apache.zookeeper.server.LogFormatter log.1

java -cp /soft/zookeeper-3.4.12/zookeeper-3.4.12.jar:/soft/zookeeper-3.4.12/lib/slf4j-api-1.7.25.jar org.apache.zookeeper.server.SnapshotFormatter log.1

(三)操作数据模型命令


Zookeeper实操[老的有时间整理一下] - 图20

Java客户端框架

(一)有三种客户端


详情见ZJJ_Zookeeper项目代码



1. Zookeeper原生客户端

zookeeper官方提供的java客户端API;
重点掌握这个,原因是ZkClient和Curator客户端都是基于Zookeeper原生客户端封装的.,底层都是Zookeeper原生客户端,如果你原生的客户端理解了,你想了解下面的二次封装客户端更容易一些.

原生客户端的问题:
1. 会话连接是异步的
2. Watch需要重复注册,就是使用的时候,需要重新注册(Watch机制为什么是一次性的,设计者为了性能考虑,没有考虑到用户的可用性)
3. Session的重连机制,也是不完善的
4. 开发的复杂程度偏高的


2.ZkClient

开源的zk客户端,在原生API基础上封装,是一个更易于使用的zookeeper客户端;
一般公司用ZkClient的多

3. Curator

这种用起来最爽,包括分布式锁,但是用起来太重太麻烦了,可以不看这个,除非公司用这个客户端
开源的zk客户端,在原生API基础上封装,apache顶级项目;

Curator客户端

创建节点

创建一个节点,初始内容为空
client.create().forPath(path);
注意,如果没有设置节点属性,那么Curator默认创建的是持久节点,内容默认是空。这里的client是指一个已经完成会话创建并启动的Curator客户端实例,即CuratorFramework对象实例。
创建一个节点,附带初始内容
client.create.forPath(path, “init”.getBytes());
也可以在创建节点的时候写入初始节点内容。和ZkClient不同的是,Curator仍然是按照ZooKeeper原生API的风格,使用byte[]作为方法参数。
创建一个临时节点,初始内容为空
client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
创建一个临时节点,并自动递归创建父节点
client.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL).forPath(path);
这个接口非常有用,在使用ZooKeeper的过程中,开发人员经常会碰到NoNodeException异常,其中一个可能的原因就是试图对一个不存在的父节点创建子节点。因此,开发人员不得不在每次创建节点之前,都判断一下该父节点是否存在——这个处理通常让人厌恶。在使用Curator之后,通过调用creatingParentsIfNeeded接口,Curator就能够自动的递归创建所有需要的父节点。
同时要注意的一点是,由于在ZooKeeper中规定了所有非子节点必须为持久节点,调用上面这个API之后,只有path参数对应的数据节点是临时节点,其父节点均为持久节点。

删除节点

删除一个节点
client.delete().forPath(path);
注意,使用该接口,只能删除叶子节点。
删除一个节点,并递归删除其所有子节点
client.delete().deletingChildrenIfNeeded().forPath(path);
删除一个节点,强制指定版本进行删除
client.delete().withVersion(version).forPath(path);
删除一个节点,强制保证删除
client.delete().guaranteed().forPath(path);
注意,guaranteed()接口是一个保障措施,只要客户端会话有效,那么Curator会在后台持续进行删除操作,直到节点删除成功。

读取数据


读取一个节点的数据内容
client.getData().forPath(path);
注意,该接口调用后的返回值是byte[]。
读取一个节点的数据内容,同时获取到该节点的stat
client.getData().storingStatIn(stat).forPath(path);
Curator通过传入一个旧的stat变量的方式来存储服务端返回的最新的节点状态信息。

更新数据

更新一个节点的数据内容
client.setData().forPath(path);
调用该接口后,会返回一个stat对象。
更新一个节点的数据内容,强制指定版本进行更新
client.setData().withVersion(version).forPath(path);
注意,withVersion接口就是用来实现CAS(Compare and Swap)的,version(版本信息)通常是从一个旧的stat对象中获取到的。

分布式锁源码分析

虽然zookeeper原生客户端暴露的API已经非常简洁了,但是实现一个分布式锁还是比较麻烦的…我们可以直接使用curator这个开源项目提供的zookeeper分布式锁实现。

curator分布式锁实现代码

https://www.yuque.com/docs/share/6e0cc000-9713-4921-9c50-1e899e999a16#



Zookeeper集群操作

(一)Zookeeper集群配置

  1. 需要先安装jdk环境,,

    3.下载zookeeper安装包
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz


4.解压Zookeeper安装包

tar -zxvf zookeeper-3.4.10.tar.gz


5.修改Zookeeper文件夹名称

重命名: mv zookeeper-3.4.10 zookeeper


6. 修改zoo_sample.cfg配置文件

需要修改 dataDir 原因是防止zookeeper清空临时信息清空内容.
一定要修改cfg名字 为zoo.cfg,不然启动会报错.

cd /usr/local/zookeeper/conf
mv zoo_sample.cfg zoo.cfg修改conf: vi zoo.cfg 修改两处
(1) dataDir=/usr/local/zookeeper/data(注意同时在zookeeper创建data目录)



搭建集群
前面 server.0 server.1 server.2 是服务器id,代表着我给服务器起了个id,一定是int类型的,
第二部分三个zookeeper所在服务器的端口,

server.0=192.168.3.101:2888:3888
server.1=192.168.3.102:2888:3888
server.2=192.168.3.103:2888:3888


这个是总配置文件的信息

# 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/zookeeper/data
# the port at which the clients will connect
clientPort=2181
# 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.0=192.168.3.101:2888:3888
server.1=192.168.3.102:2888:3888
server.2=192.168.3.103:2888:3888



7. 创建服务器标识
/root/zookeeper/data
创建文件myid并填写内容为0

其它的zookeeper也创建文件夹/root/zookeeper/data

在文件夹里面创建文件myid填写内容为1 和2 , 具体填写什么数据需要看你配置文件里面的配置信息.


启动zookeeper

cd 到bin目录下 sh zkServer.sh start 启动命令, 别忘了三个服务器的zookeeper一起起来.



(二)Zookeeper集群解析



Zookeeper实操[老的有时间整理一下] - 图21

1.集群中的角色


如果所有的请求是 读请求 的时候,找的是Follower,Follower会把请求返回给你,如果你是写的请求,Follower也能接收写请求,但是不会去处理,会给这个写请求发送给Leader.,Leader处理好了再转发回去.

这个架构其实就是读写分离的架构.Follower(从节点)可以负责读,但是不能写,Leader(主节点)可以写也可以读.


Leader 集群工作机制中的核心

Leader是整个集群的老大,是所有的工作的一个核心,一个集群只有一个Leader,不可能有两个Leader
事务请求的唯一调度和处理者,保证集群事务处理的顺序性
集群内部个服务器的调度者(管理follower,数据同步,)
follower启动的时候都会从Leader里面把数据给取出存入自己的机器.这就是数据同步.

Follower 集群工作机制中的跟随者


处理非事务请求(就是查询操作,不涉及增删改),转发事务请求(增删改操作)给Leader
参与事务请求proposal投票(保证分布式事务写数据是成功的)
参与leader选举投票(刚启动集群的时候是没有Leader的,Leader是整个集群同时选举的,通过一个算法策略来选举,就好比上学时候选班干部)

Observer 观察者


3.30以上版本提供,和follower功能相同,但不参与任何形式投票,目的是提高集群非事务处理能力

处理非事务请求,转发事务请求给Leader

配置观察者方式是在zoo.cfg文件里面
peerType=observer
server.3=192.168.0.102:2888:3888:observer

2.Zookeeper集群特点

顺序一致性
客户端的更新顺序与它们(客户端)被发送的顺序相一致。
原子性 更新操作要么整个集群成功要么失败,没有第三种结果(第三结果就是成功一半儿失败一半儿)。
单一视图
虽然整个集群分为Leader和Follower等等角色,无论客户端连接到哪一个服务器,客户端将看到同一个的 ZooKeeper 视图。
可靠性 一旦一个更新操作被应用,那么在客户端再次更新它之前,它的值将不会改变。
实时性 连接上一个服务端数据修改,所以其他的服务端都会实时的跟新,不算完全的实时,有一点延时的
角色轮换避免单点故障 当leader出现问题的时候,会选举从follower中选举一个新的leader



3.Zookeeper集群配置


1.安装jdk运行jdk环境

上传jdk1.8安装包


2.安装jdk1.8环境变量

vi /etc/profile

export JAVA_HOME=/usr/local/jdk1.8.0_181
export ZOOKEEPER_HOME=/usr/local/zookeeper
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$JAVA_HOME/bin:$ZOOKEEPER_HOME/bin:$PATH



刷新profile文件
source /etc/profile


关闭防火墙


3.下载zookeeper安装包

wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz


4.解压Zookeeper安装包

tar -zxvf zookeeper-3.4.10.tar.gz


5.修改Zookeeper文件夹名称

重命名: mv zookeeper-3.4.10 zookeeper


8. 修改zoo_sample.cfg文件
需要给zoo_sample.cfg文件名改成zoo.cfg名字

clientPort=2181
initLimit=5
syncLimit=2
peerType=observer
server.ID1=IP1:2888:3888
server.ID2=IP2:2888:3888
server.ID3=IP3:nnnn:mmmm:observer

说明
IDn: server id,集群中机器序号
IPn: 机器ip地址
nnnn: 同步端口
mmmm: 选举端口
observer: 是否为观察者

需要注意的是 Zookeeper集群机器的zoo.cfg文件必须是一致的,但是peerType=observer这个属性别的机器可以没有,只要当前机器是观察者角色,就在当前机器的zoo.cfg里面才配置peerType=observer


cd /usr/local/zookeeper/conf
mv zoo_sample.cfg zoo.cfg修改conf: vi zoo.cfg 修改两处
(1) dataDir=/usr/local/zookeeper/data(注意同时在zookeeper创建data目录,不然会报错)
(2)最后面添加server.0=192.168.212.154:2888:3888
server.1=192.168.212.156:2888:3888
server.2=192.168.212.157:2888:3888




9. 创建服务器标识
zoo.cfg文件里面的,在dataDir所配置的目录下,创建一个名为myid的文件,在该文件的第一行写上一个数字,和zoo.cfg中当前机器的编号对应上

假如dataDir=/usr/local/zookeeper/data ,那么就需要在data里面创建名字为myid的文件

8.复制zookeeper
进行复制zookeeper目录到node1和node2还有/etc/profile文件
把node1、 node2中的myid文件里的值修改为1和2路径(vi /usr/local/zookeeper/data/myid)
9启动zookeeper启动zookeeper:
路径: /usr/local/zookeeper/bin
执行: zkServer.sh start
(注意这里3台机器都要进行启动)状态: zkServer.sh
status(在三个节点上检验zk的mode,一个leader和俩个follower)

scp -r /soft root@zk2:/
scp -r /soft root@zk3:/




4.Java客户端连接集群

Zookeeper实操[老的有时间整理一下] - 图22

ZK连接集群很简单,只需要把连接地址用逗号分隔就好。


5.为什么最好使用奇数台服务器构成 ZooKeeper 集群?

1.zookeeper集群的写操作,由leader节点负责,它会把通知所有节进行写入操作,只有收到半数以上节点的成功反馈,才算成功。如果是部署2个节点的话,那就必须都成功。
2.zookeeper的选举策略也是需要半数以上的节点同意才能当选leader,如果是偶数节点可能导致票数相同的情况
3.只有当半数以上的节点存活时 zookeeper集群才能对外服务,维持正常状态,如果是2个节点,只要其中一个挂掉,那么剩下的1个并不满足半数以上规则。

zookeeper有这样一个特性:集群中只要有过半的机器是正常工作的,那么整个集群对外就是可用的。也就是说如果有2个zookeeper,那么只要有1个死了zookeeper就不能用了,因为1没有过半,所以2个zookeeper的死亡容忍度为0;
三台机器如果死两台就不符合过半儿了,四台机器死两台也不符合过半儿了,五台机器死三台不符合过半儿,六台机器死三台也不符合过半儿.



分布式锁


为什么需要分布式锁

1. 多任务环境中才需要,同时多任务都需要对同一共享资源进行写操作;
2. 对资源的访问是互斥的(就是线程1访问资源的时候,不允许其它线程访问资源,只能等线程1访问结束后,其它线程才能访问资源)

Tips:任务通过竞争获取锁才能对该资源进行操作(①竞争锁);当有一个任务在对资源进行更新时(②占有锁),其他任务都不可以对这个资源进行操作(③任务阻塞),直到该任务完成更新(④释放锁);

(一)分布式锁实现方式比较

直接看这个链接
https://www.yuque.com/docs/share/92326f61-69ca-4b38-b44d-91d7ab6bcbce#


利用mysql的实现方案

实现思路:利用数据库自身提供的锁机制实现,要求数据库支持行级锁;

优点: 实现简单,稳定可靠

缺点:
1.性能差,无法适应高并发场景(数据库是有瓶颈的,瓶颈是不到1000);
2.容易出现死锁的情况;(假如说获取锁的时候,同时在数据库里面插入标记,插入失败,此时就会形成死锁,只能程序员去手动的解决)
3.无法优雅的实现阻塞式锁:是用线程休眠10毫秒.

利用redis的实现方案

实现思路:使用Setnx和lua脚本机制实现,保证对缓存操作序列的原子性;

优点:性能好

缺点:实现相对较复杂
有出现死锁的可能性;
无法优雅的实现阻塞式锁;


利用zookeeper的实现方案

实现思路:基于zk的节点特性以及watch机制实现;

优点:性能好,稳定可靠性高,能较好的实现阻塞式锁;用临时节点可以防止死锁(假如zookeeper死机了,临时节点会自动删除的,所以就能防止死锁,假如释放锁的时候删除节点失败了也不会死锁,因为只有服务器挂了才会造成删除节点失败.)

缺点:实现相对复杂

(二)Zookeeper实现分布式锁

案例:ZJJ_Zookeeper_2019/10/27_19:39:57_akh1w

在描述算法流程之前,先看下zookeeper中几个关于节点的有趣的性质:

1. 有序节点:假如当前有一个父节点为/lock,我们可以在这个父节点下面创建子节点;zookeeper提供了一个可选的有序特性,例如我们可以创建子节点“/lock/node-”并且指明有序,那么zookeeper在生成子节点时会根据当前的子节点数量自动添加整数序号,也就是说如果是第一个创建的子节点,那么生成的子节点为/lock/node-0000000000,下一个节点则为/lock/node-0000000001,依次类推。
2. 临时节点:客户端可以建立一个临时节点,在会话结束或者会话超时后,zookeeper会自动删除该节点。
3. 事件监听:在读取数据时,我们可以同时对节点设置事件监听,当节点数据或结构变化时,zookeeper会通知客户端。当前zookeeper有如下四种事件:1)节点创建;2)节点删除;3)节点数据修改;4)子节点变更。

下面描述使用zookeeper实现分布式锁的算法流程,假设锁空间的根节点为/lock:

1. 客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推。
2. 客户端获取/lock下的子节点列表,判断自己创建的子节点是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听/lock的子节点变更消息,获得子节点变更通知后重复此步骤直至获得锁;
3. 执行业务代码;
4. 完成业务流程后,删除对应的子节点释放锁。

步骤1中创建的临时节点能够保证在故障的情况下锁也能被释放,考虑这么个场景:假如客户端a当前创建的子节点为序号最小的节点,获得锁之后客户端所在机器宕机了,客户端没有主动删除子节点;如果创建的是永久的节点,那么这个锁永远不会释放,导致死锁;由于创建的是临时节点,客户端宕机后,过了一定时间zookeeper没有收到客户端的心跳包判断会话失效,将临时节点删除从而释放锁。
另外细心的朋友可能会想到,在步骤2中获取子节点列表与设置监听这两步操作的原子性问题,考虑这么个场景:客户端a对应子节点为/lock/lock-0000000000,客户端b对应子节点为/lock/lock-0000000001,客户端b获取子节点列表时发现自己不是序号最小的,但是在设置监听器前客户端a完成业务流程删除了子节点/lock/lock-0000000000,客户端b设置的监听器岂不是丢失了这个事件从而导致永远等待了?这个问题不存在的。因为zookeeper提供的API中设置监听器的操作与读操作是原子执行的,也就是说在读子节点列表时同时设置监听器,保证不会丢失事件。
最后,对于这个算法有个极大的优化点:假如当前有1000个节点在等待锁,如果获得锁的客户端释放锁时,这1000个客户端都会被唤醒,这种情况称为“羊群效应”;在这种羊群效应中,zookeeper需要通知1000个客户端,这会阻塞其他的操作,最好的情况应该只唤醒新的最小节点对应的客户端。应该怎么做呢?在设置事件监听时,每个客户端应该对刚好在它之前的子节点设置事件监听,例如子节点列表为/lock/lock-0000000000、/lock/lock-0000000001、/lock/lock-0000000002,序号为1的客户端监听序号为0的子节点删除消息,序号为2的监听序号为1的子节点删除消息。

所以调整后的分布式锁算法流程如下:
1. 客户端连接zookeeper,并在/lock下创建临时的且有序的子节点,第一个客户端对应的子节点为/lock/lock-0000000000,第二个为/lock/lock-0000000001,以此类推;
2. 客户端获取/lock下的子节点列表,判断自己创建的子节点A是否为当前子节点列表中序号最小的子节点,如果是则认为获得锁,否则监听刚好在自己之前一位的子节点删除消息,获得子节点变更通知后重复此步骤直至获得锁;
3. 执行业务代码;
4. 完成业务流程后,删除对应的子节点A释放锁。
5. 比自己大一点的节点B监听到了这个节点A被删除了,说明锁被释放了,
6. 然后B节点获取到了锁,执行业务代码.

(三)脑裂问题分布式锁如何保证健壮性

假如A客户端网络有点问题,但是Zookeeper以为A客户端死掉了,就给Zookeeper里面的A用分布式锁的临时顺序节点给删了,其实A客户端还在基于获取到分布式锁的情况下执行了很多代码.

此时B客户端因为Zookeeper给A临时节点删除了,那么B客户端收到了watcher通知就认为自己加锁成功了,客户端B就会基于加锁的状态运行很多代码.这个时候就出现问题了.








服务注册与发现

微服务中每一个服务都是一个功能,符合单一职责原则,但是多个服务带来了新的问题,就是混乱,注册中心就是把这些服务登记起来,别的线程访问服务的时候先通过注册中心转发给当前线程需要访问的服务.

注册中心功能:

1. 服务注册:各个服务把自己地址信息(IP,端口等)注册到注册中心里面
2. 服务发现:业务线程调用服务先通过注册中心去找服务地址
3. 负载算法:业务线程从多个符合要求的服务中筛选一个去调用.


Zookeeper实操[老的有时间整理一下] - 图23

工作流程


1. 当前工作线程假如要下单订,需要先去注册中心里面去查询,从注册中心里面获取具体的服务列表,把服务列表下载到本地.
2. 然后通过负载均衡,会有一个随机算法,然后最终会获得一个IP加端口,
3. 通过这个IP加端口可以调用业务需要的服务