点赞再看,养成习惯,微信搜一搜【一角钱小助手】 关注更多原创技术文章。
本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。
前言
对于做Java开发而言,面试过程中肯定有聊到分布式系统,面试官跟你聊完 dubbo 相关一些问题之后,已经确认你对分布式服务框架/RPC框架都有一些认知了,那么他很有可能开始要跟你聊分布式相关的其他问题了。比如分布式锁这个东西,很常用的。你做Java系统开发,分布式系统可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
因为 zookeeper 是分布式系统中很常见的一个基础系统,而且面试常问的就是说 zookeeper 的使用场景是什么?看你知不知道这一些基本的使用场景。但其实 zookeeper 深挖可以问的很深很深的。基于此我们来深入学习下 zookeeper,开始来写 zookeeper 系列文章了,本篇重点介绍下 zookeeper 的特性和节点说明。
Zookeeper 背景
分布式
互联网技术的发展,导致大型系统/网站需要的计算能力和存储能力越来越高。系统/网站架构逐渐从集中式转变成分布式。
什么是分布式
把一个计算任务分解为若干个计算单元,并分派到若干个不同的计算机中去执行,然后再汇总计算结果。
分布式的工作方式有点类似于团队合作。当有一项任务分配到某个团队之后,团队内部的成员开始各司其职,然后把工作结果统一汇总给团队主管,由团队主管再整理团队的工作成果汇报给公司。
分布式存在的问题
虽然分布式和集中式系统相比有很多优势,比如能提供更强的计算、存储能力,避免单点故障等问题。但是由于采用分布式部署的方式,就经常会出现网络故障等问题,并且如何在分布式系统中保证数据的一致性和可用性也是一个比较关键的问题。
比如在集中式系统中,有一些关键的配置信息,可以直接保存在服务器的内存中,但是在分布式系统中,如何保存这些配置信息,又如何保证所有机器上的配置信息都保持一致,又如何保证修改一个配置能够把这次修改同步到所有机器中呢?
再比如,在集中式系统中,进行一个同步操作要写同一个数据的时候,可以直接使用事务+锁来管理保证数据的ACID。但是,在分布式系统中如何保证多台机器不会同时写同一条数据呢?
Zookeeper产生背景
项目从单体到分布式转变之后,将会产生多个节点之间协同的问题。如:
- 每天的定时任务由谁哪个节点来执行?
- RPC调用时的服务发现?
- 如何保证并发请求的幂等
- …
这些问题可以统一归纳为多节点协调问题,如果靠节点自身进行协调这是非常不可靠的,性能上也不可取。必须由一个独立的服务做协调工作,它必须可靠,而且保证性能。
Zookeeper 介绍
Zookeeper 的由来
2006年的时候Google出了Chubby来解决分布一致性的问题(distributed consensus problem),所有集群中的服务器通过Chubby最终选出一个Master Server ,最后这个Master Server来协调工作。简单来说其原理就是:在一个分布式系统中,有一组服务器在运行同样的程序,它们需要确定一个Value,以那个服务器提供的信息为主/为准,当这个服务器经过n/2+1的方式被选出来后,所有的机器上的Process都会被通知到这个服务器就是主服务器 Master服务器,大家以他提供的信息为准。很想知道Google Chubby中的奥妙,可惜人家Google不开源,自家用。
但是在2009年3年以后沉默已久的Yahoo在Apache上推出了类似的产品ZooKeeper,并且在Google原有Chubby的设计思想上做了一些改进,因为ZooKeeper并不是完全遵循Paxos协议,而是基于自身设计并优化的一个2 phase commit的协议。
下面这段内容摘自《从Paxos到Zookeeper 》第四章第一节的某段内容,推荐大家阅读以下:
Zookeeper最早起源于雅虎研究院的一个研究小组。在当时,研究人员发现,在雅虎内部很多大型系统基本都需要依赖一个类似的系统来进行分布式协调,但是这些系统往往都存在分布式单点问题。所以,> **雅虎的开发人员就试图开发一个通用的无单点问题的分布式协调框架,以便让开发人员将精力集中在处理业务逻辑上。**关于“ZooKeeper”这个项目的名字,其实也有一段趣闻。在立项初期,考虑到之前内部很多项目都是使用动物的名字来命名的(例如著名的Pig项目),雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家RaghuRamakrishnan开玩笑地说:“在这样下去,我们这儿就变成动物园了!”此话一出,大家纷纷表示就叫动物园管理员吧一一一因为各个以动物命名的分布式组件放在一起,> 雅虎的整个分布式系统看上去就像一个大型的动物园了,而Zookeeper正好要用来进行分布式环境的协调一一于是,Zookeeper的名字也就由此诞生了。
ZooKeeper 概览
ZooKeeper 是一个开源的分布式协调服务,ZooKeeper 框架最初是在“Yahoo!"上构建的,用于以简单而稳健的方式访问他们的应用程序。
后来,Apache ZooKeeper 成为 Hadoop,HBase 和其他分布式框架使用的有组织服务的标准。
例如,Apache HBase 使用 ZooKeeper 跟踪分布式数据的状态。
ZooKeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
原语: 操作系统或计算机网络用语范畴。它是由若干条指令组成的,用于完成一定功能的一个过程。具有不可分割性,即原语的执行必须是连续的,在执行过程中不允许被中断。
ZooKeeper 是一个典型的分布式数据一致性解决方案,分布式应用程序可以基于 ZooKeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
ZooKeeper 一个最常用的使用场景就是用于担任服务生产者和服务消费者的注册中心。
服务生产者将自己提供的服务注册到 ZooKeeper 中心,服务的消费者在进行服务调用的时候先到 ZooKeeper 中查找服务,获取到服务生产者的详细信息之后,再去调用服务生产者的内容与数据。
如下图所示,在 Dubbo 架构中 ZooKeeper 就担任了注册中心这一角色。
- Provider: 暴露服务的提供方,可以通过jar或者容器的方式启动服务
- Consumer:调用远程服务的服务消费方。
- Registry: 服务注册中心和发现中心。
- Monitor: 统计服务和调用次数,调用时间监控中心。(dubbo的控制台页面中可以显示,目前只有一个简单版本)
- Container:服务运行的容器。
znode 节点
Zookeeper 中数据基本单元叫节点,节点之下可包含子节点,最后以树级方式程现。每个节点拥有唯一的路径path。客户端基于PATH上传节点数据,Zookeeper 收到后会实时通知对该路径进行监听的客户端。
部署与常规配置
Zookeeper 基于JAVA开发,下载后只要有对应JVM环境即可运行。其默认的端口号是2181运行前得保证其不冲突。
版本说明
2020年9月9日发行的3.6.2版是目前最新的一个稳定版本。
另外在2020年9月9日发行的3.5.5是3.5分支的第一个稳定版本。此版本被认为是3.4稳定分支的后续版本,可以用于生产。基于3.4它包含以下新功能
- 动态重新配置
- 本地会议
- 新节点类型:容器,TTL
- 原子广播协议的SSL支持
- 删除观察者的能力
- 多线程提交处理器
- 升级到Netty 4.1
- Maven构建
另请注意:建议的最低JDK版本为1.8
文件说明:
- apache-zookeeper-xxx-tar.gz 代表源代码
- apache-zookeeper-xxx-bin.tar.gz 运行版本
下载地址:https://zookeeper.apache.org/releases.html#download
具体部署流程:
#下载
wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
#解压
tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz
#拷贝默认配置
cd {zookeeper_home}/conf
cp zoo_sample.cfg zoo.cfg
#启动
./zkServer.sh start
#关闭服务器
./zkServer.sh stop
#启动客户端
./zkCli.sh -server 127.0.0.1:2181
常规配置文件说明
# zookeeper时间配置中的基本单位 (毫秒)
tickTime=2000
# 允许follower初始化连接到leader最大时长,它表示tickTime时间倍数 即:initLimit*tickTime
initLimit=10
# 允许follower与leader数据同步最大时长,它表示tickTime时间倍数
syncLimit=5
#zookeper 数据存储目录
dataDir=/tmp/zookeeper
#对客户端提供的端口号
clientPort=2181
#单个客户端与zookeeper最大并发连接数
maxClientCnxns=60
# 保存的数据快照数量,之外的将会被清除
autopurge.snapRetainCount=3
#自动触发清除任务时间间隔,小时为单位。默认为0,表示不自动清除。
autopurge.purgeInterval=1
客户端命令
系统命令
配置命令
节点命令
配额命令
权限命令
监视器命令
node数据的增删改查
# 列出子节点
ls /
#创建节点
create /hjh "hjh is good man"
# 查看节点
get /hjh
# 创建子节点
create /hjh/sex "man"
# 删除节点
delete /hjh/sex
# 删除所有节点 包括子节点
deleteall /hjh
Zookeeper节点介绍
Zookeeper 中节点叫 znode 存储结构上跟文件系统类似,以树级结构进行存储。不同之外在于znode没有目录的概念,不能执行类似cd之类的命令。znode结构包含如下:
- path:唯一路径
- childNode:子节点
- stat:状态属性
- type:节点类型
节点类型
- PERSISTENT(持久节点)
持久化保存的节点,也是默认创建的
#默认创建的就是持久节点
create /test
- PERSISTENT_SEQUENTIAL(持久序号节点)
创建时zookeeper 会在路径上加上序号作为后缀,。非常适合用于分布式锁、分布式选举等场景。创建时添加 -s 参数即可。
#创建序号节点
create -s /test
#返回创建的实际路径
Created /test0000000001
create -s /test
#返回创建的实际路径2
Created /test0000000002
- EPHEMERAL(临时节点)
临时节点会在客户端会话断开后自动删除。适用于心跳,服务发现等场景。创建时添加参数-e 即可。
#创建临时节点, 断开会话 在连接将会自动删除
create -e /temp
- EPHEMERAL_SEQUENTIAL(临时序号节点)
与持久序号节点类似,不同之处在于EPHEMERAL_SEQUENTIAL是临时的会在会话断开后删除。创建时添加 -e -s
create -e -s /seq
节点属性
# 查看节点属性
stat /test
其属性说明如下表:
#创建节点的事物ID
cZxid = 0x8
#创建时间
ctime = Sat Oct 03 22:20:27 CST 2020
#修改节点的事物ID
mZxid = 0x8
#最后修改时间
mtime = Sat Oct 03 22:20:27 CST 2020
# 子节点变更的事物ID
pZxid = 0x8
#这表示对此znode的子节点进行的更改次数(不包括子节点)
cversion = 0
# 数据版本,变更次数
dataVersion = 0
#权限版本,变更次数
aclVersion = 0
#临时节点所属会话ID
ephemeralOwner = 0x0
#数据长度
dataLength = 0
#子节点数(不包括子子节点)
numChildren = 0
节点的监听
客户添加 -w 参数可实时监听节点与子节点的变化,并且实时收到通知。非常适用保障分布式情况下的数据一致性。其使用方式如下:
acl权限设置
ACL全称为Access Control List(访问控制列表),用于控制资源的访问权限。ZooKeeper使用ACL来控制对其znode的防问。基于 scheme:id:permission
的方式进行权限控制。scheme表示授权模式、id模式对应值、permission即具体的增删改权限位。
scheme:认证模型
permission权限位
acl 相关命令
world权限示例
语法: setAcl world:anyone:<权限位>
注:world模式中anyone是唯一的值,表示所有人
- 查看默认节点权限:
#创建一个节点
create -e /testAcl
#查看节点权限
getAcl /testAcl
#返回的默认权限表示 ,所有人拥有所有权限。
'world,'anyone: cdrwa
- 修改默认权限为 读写
#设置为rw权限
setAcl /testAcl world:anyone:rw
# 可以正常读
get /testAcl
# 无法正常创建子节点
create -e /testAcl/t "hi"
# 返回没有权限的异常
Authentication is not valid : /testAcl/t
IP权限示例
语法: setAcl ip:<ip地址|地址段>:<权限位>
auth模式示例
语法:
- setAcl auth:<用户名>:<密码>:<权限位>
- addauth digest <用户名>:<密码>
digest 权限示例
语法:
- setAcl digest :<用户名>:<密钥>:<权限位>
- addauth digest <用户名>:<密码>
注1:密钥 通过sha1与base64组合加密码生成,可通过以下命令生成
echo -n <用户名>:<密码> | openssl dgst -binary -sha1 | openssl base64
注2:为节点设置digest 权限后,访问前必须执行addauth,当前会话才可以防问。
- 设置digest 权限
#先 sha1 加密,然后base64加密
echo -n hjh:123456 | openssl dgst -binary -sha1 | openssl base64
#返回密钥
Td6f6IYc3Kjci9RQP1A6WzjkZv8=
#设置digest权限
setAcl /hjh digest:hjh:Td6f6IYc3Kjci9RQP1A6WzjkZv8=:cdrw
- 查看节点将显示没有权限
#查看节点
get /hjh
#显示没有权限访问
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /hjh
- 给当前会话添加认证后再次查看
#给当前会话添加权限帐户
addauth digest hjh:123456
#在次查看
get /hjh
#获得返回结果
hjh is good man
ACL的特殊说明:
权限仅对当前节点有效,不会让子节点继承。如限制了IP防问A节点,但不妨碍该IP防问A的子节点 /A/B。
Zookeeper 系列文章,敬请关注,下篇继续!
部分图片来源于网络,版权归原作者,侵删。