ZooKeeper 的原理与实战

您所在的位置:网站首页 zookeeper和chubby ZooKeeper 的原理与实战

ZooKeeper 的原理与实战

2023-04-08 16:25| 来源: 网络整理| 查看: 265

1. 学习目标1.1 基础介绍Zookeeper的安装配置,Zookeeper的基础概念和zkCli.sh的使用,为后面的学习打好基础。1.2 开发介绍Zookeeper API 的使用。首先会对Zookeeper核心API做一个讲解,然后结合具体示例讲解如何使用Zookeeper API 进行协同服务的开发。1.3 运维介绍Zookeeper生产环境的安装配置和Zookeeper的监控。监控部分除了介绍Zookeeper自带的监控工具,还会介绍和其他监控系统的集成。1.4 开发进阶一个使用Zookeeper来实现服务发现的实战项目。讲解Apache Kafka是如何使用Zookeeper的。1.5 比较Zookeeper、etcd和Chubbyetcd和Chubby是和Zookeeper类似的系统。这一章会对etcd和Chubby做一个简单的介绍,并把他们和Zookeeper做一个比较。1.6 实现原理和源码解读

1. 结合Zookeeper的Paper讲解一下Zookeeper的设计实现原理,并带着大家把Zookeeper的核心模块的代码阅读一下。在讲解Zookeeper原理的时候会把相关的计算机理论知识点讲一下。

2. 什么是Zookeeper2.1 什么是Zookeeper?开源的分布式协同服务系统。目标是将复杂且容易出错的分布式协同服务封装起来,抽象出一个高效可靠的原语集,并以一系列简单的接口提供给用户使用。2.2 发展历史起源于雅虎研究小组。雅虎的开发人员就开发了一个通用的无单点问题的分布式协同服务系统。有3个著名开源项目使用Zookeeper:Hadoop:使用Zookeeper做Namenode的高可用HBase: 保证集群中只有一个master,保存集群中的RegionServer列表,保存hbase:meta表的位置Kafka:集群成员管理,controller节点选举2.3 应用场景

典型应用场景:

配置管理(configuration management)DNS服务组成员管理(group membership)各种分布式锁

Zookeeper适用于存储和协同相关的关键数据,不适合用于大数据量存储。

3. 提供什么服务?3.1 服务的使用使用Zookeeper客服端库使用Zookeeper服务。Zookeeper客户端负责和Zookeeper集群的交互。3.2 数据模型文件系统的树形模型和key-value模型是两种主流的数据模型。Zookeeper使用文件系统模型主要基于两点考虑:文件系统的树形结构便于表达数据之间的层次的关系文件系统的树形结构便于为不同的应用分配独立的命名空间。Zookeeper的层次模型称作data tree。Data tree的每个节点叫作znode.(所有的znode都有数据)不同于文件系统,每个节点都可以保存数据,每个znode都有一个版本,版本从0开始计数。3.3 data tree 接口

1. Zookeeper对外提供一个用来访问data tree的简化文件系统API:

使用Unix风格的路径名来定位znode,例如/A/X表示znode A的子节点X。znode的数据只支持全量写入和读取,没有像通用文件系统那样支持部分写入和读取。data tree 的所有API 都是wait-free的.正在执行中的API调用不会影响其他API的完成。data tree的API都是对文件系统的wait-free操作,不直接提供锁这样的分布式协同机制。但是data tree的API非常强大,可以用来实现多种分布式协同机制。

2. 一个znode可以是持久性的,也可以是临时性的:

持久性的znode(PERSISTENT): 这样的节点在创建之后,即使发生Zookeeper集群宕机或者client宕机也不会丢失。临时性的znode(EPHEMERAL):clent宕机或者client在指定的timeout时间内没有给Zookeeper集群发消息,这样的节点就会消失。

znode节点也可以是顺序性的。每个顺序性的znode关联一个唯一的单调递增整数。

这个单调递增整数是znode名字的后缀。如果上面两种znode具备顺序性,又有以下两种znode:

3. 持久顺序性的znode(PERSISTENT_SEQUENTIAL):znode除了具备持久性znode的特点之外,znode的名字具备顺序性。

4. 临时顺序性的znode(EPHEMERAL_SEQUENTIAL): znode除了具备临时性znode的特点之外,znode的名字具备顺序性。

Zookeeper主要有以上4种znode。

4. 开始使用Zookeeper4.1 使用Zookeeper入门安装配置:

最重要的配置,就是:端口、日志存储位置。

在~/.bash_profile中,配置 export ZOOKEEPER_HOME = "$HOME/tools/apache-zookeeper-3.7.1-bin" export PATH="$ZOOKEEPER_HOME/bin:$PATH" 然后,source 一下该配置文件。 cd 进入,创建 conf/zoo.cfg 启动 zk

2. 启动Zookeeper

使用zkServer.sh start 启动 Zookeeper服务检查Zookeeper日志是否有出错信息检查Zookeeper数据文件检查Zookeeper是否在2181端口上监听。// 检查 是否带exception、error的报错信息 cd logs grep -E -i "((exception)|(error))" * // 查看存储的文件的树结构 cd /temp/zookeeper // 查看是否有日志 tree . ├── version-2 │ └── snapshot.0 └── zookeeper_server.pid // 查看端口号是否在监听 netstat -tunlp | grep 2181 // 使用zkCli.sh 然后 使用 help // R代表递归向下查找 ls -R / create /app2 create /app1 create /app1/p_1 create /app1/p_2 create /app1/p_3

3. 如何使用zkCli实现一个锁(要保证分布式锁,如果crash了,释放掉锁)

// 终端1 zkCli.sh create -e /lock quit // 终端2 zkCli.sh create -e /lock stat -w /lock // 显示如下: [cZxid = 0x8 ctime = Tue Jan 10 16:06:43 CST 2023 mZxid = 0x8 mtime = Tue Jan 10 16:06:43 CST 2023 pZxid = 0x8 cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x1002c8896f40001 dataLength = 0 numChildren = 0] create -e /lock

4.2 master-worker架构

master-worker广泛使用在分布式架构中。一个master负责监控worker的状态:

在任何时刻,系统中最多只能有一个master,不可以出现两个master的情况,多个master共存会导致脑裂。系统中除了处于active状态的master还有一个backup master,如果active master失败了,backup master可以很快的进入active状态。master实时监控worker的状态,能够及时收到worker成员变化的通知。master在收到worker成员变化的时候,通常重新进行任务的重新分配。

master-worker架构示例1-HBase

master-worker架构示例2-kafka

master-worker架构示例3-HDFS

如何使用Zookeeper实现master-worker集群服务

使用zkCli.sh 演示如何实现master-worker

首先:使用create /workers 创建/workers znode

// 创建临时节点 使用-e create -e /master "m1:2223" // 再次 create -e /master "m2:2223" 此时,m2 处于backup 状态 // -w 相当于加个 watch stat -w /master // 当m1 quit失败了,那么在m2就会检查到 // 此时,m2再次创建就会成功 create -e /master "m2:2223" // ls 监控目录下面节点的变化 ls -w /workers // 通过master去观察其他worker的目录情况 ls -w /workers [w1, w2] // 如果w2失败,quit,master再去查,只有w1 [w1] // 也就是说,master,及时去观察,worker的情况,再次去分配。4.3 Zookeeper的架构一般情况下,使用quorum模式。standalone的节点只有一个,quorum节点有多个。

2. Session

Zookeeper客户端库和Zookeeper集群中的某个节点创建一个session。

客户端可以主动关闭session。如果Zookeeper节点没有在session关联的timeout时间内收到客户端的消息的话,Zookeeper节点也会关闭session。Zookeeper客户端库如果发现连接的Zookeeper出错,会自动的和其他Zookeeper节点建立连接。

3. Quorum 模式

处于Quorum模式的Zookeeper集群包含多个Zookeeper节点。

下图有1个leader节点,节点2、3是follower节点。leader节点可以处理读写请求,follower只可以处理读请求。follower在接到写请求时会把请求转发给leader来处理。4.4 数据一致性全局可线性化(Linearizable)写入:先到达leader的写请求会被先处理,leader决定写请求的执行顺序客户端FIFO顺序:来自给定客户端的请求按照发送顺序执行

3. 配置文件

需要准备3个配置文件,dataDir和clientPort配置项要配置不同的值。

3个配置文件的server.n 部分都是一样的。

在server.1 = 127.0.0.1:3333:3334中,3333是用于quorum通信的端口,3334是用于leader选举的端口

vi src/main/resources/quorum/zoo-quorum-node1.cfg vi src/main/resources/quorum/zoo-quorum-node2.cfg vi src/main/resources/quorum/zoo-quorum-node3.cfg // 先看端口 netstat -tunlp | grep 2181 // 杀掉后,重新启动,在前台运行start-foreground, 可以把日志打到console zkServer.sh start-foreground src/main/resources/quorum/zoo-quorum-node1.cfg // 需要新建myid文件 /data/zk/quorum/node1/myid 内容为1 /data/zk/quorum/node2/myid 内容为2 /data/zk/quorum/node3/myid 内容为3 // -server 指定主机名:端口号 zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 ls -R /4.5 APIZookeeper Java 代码 主要使用org.apache.zookeeper.Zookeeper这个类使用Zookeeper服务。Zookeeper(connectString,sessionTimeout,watcher)connectString:使用逗号分隔的列表,每个Zookeeper节点是一个 host:port对,host是机器名或者IP地址,port是Zookeeper节点对客户端提供服务的端口号。客户端会任意选取connectString中的一个节点建立连接。sessionTimeout:session timeout 时间watcher:用于接收到来自Zookeeper集群的事件。

2. Zookeeper 主要方法

create(path,data,flags): 创建一个给定路径的znode,并在znode保存data[]的数据,flags指定znode的类型。flags指定,持久化还是临时delete(path,version): 如果给定path上的znode的版本和给定的version匹配,删除znodeexists(path,watch):判断给定path上的znode是否存在,并在znode设置一个watchgetData(path,watch): 返回给定path上的znode数据,并在znode设置一个watch获取数据setData(path, data, version): 如果给定path上的znode的版本和给定的version匹配,设置znode数据设置数据,watch参数用来做版本匹配getChildren(path, watch):返回给定path上的znode的孩子znode名字,并在znode设置一个watchsync(path): 把客户端session连接节点和leader节点进行同步。

3. 方法说明

所有读取znode数据的API都可以设置一个watch用来监控znode的变化所有更新znode数据的API都有两个版本:无条件更新版本和条件更新版本。如果version为-1,更新为无条件更新。否则只有给给定的version和znode当前的version一样,才会进行更新,这样的更新是条件更新。匹配更新所有的方法都有同步和异步两个版本。同步版本的方法发送请求给Zookeeper并等待服务器的响应。异步版本把请求放入客户端的请求队列,然后马上返回。异步版本通过callback来接受来自服务端的响应。

4. Zookeeper代码异常处理

所有同步执行的API方法都有可能抛出以下两个异常:

KeeperException: 表示Zookeeper服务端出错。KeeperException的子类ConnectionLossException表示客户端和当前连接的Zookeeper节点断开了连接。网络分区和Zookeeper节点失败都会导致这个异常出现。发生次异常的时机可能是Zookeeper节点处理客户端请求之前,也可能是在Zookeeper节点处理客户端请求之后。出现ConnectionLossException异常之后,客户端会进行自动重新连接,但是我们必须要检查我们以前的客户端请求是否被成功执行。InterruptException:表示方法被中断了。我们可以使用Tread.interrupt() 来中断API的执行。

5. 数据读取API示例-getData

有以下三个获取znode数据的方法:

byte[] getData(String path, boolean watch, Stat stat)同步方法。如果watch为true,该znode的状态变化会发送给构建Zookeeper是指定的watcher。void getData(String path, boolean watch, DataCallback cb, Object ctx)异步方法。cb是一个callback,用来接收服务端的响应。ctx 是提供给cb的context。watch 参数的含义和方法1相同。void getData(String path, Watcher watcher, DataCallback cb, Object ctx)异步方法。watcher用来接收该znode的状态变化。可以指定watcher

6.数据写入API示例-setData

Stat setData(String path, byte[] data, int version)同步版本。如果version是-1,做无条件更新。如果version是非负整数,做条件更新。void setData(String path, byte[] data, int version, StatCallback cb, Object ctx)异步版本

6. watch

watch提供一个让客户端获取最新数据的机制。如果没有watch机制,客户端需要不断的轮询Zookeeper来查看是否有数据更新,这在分布式环境中是非常耗时的。客户端可以在读取数据的时候设置一个watcher,这样在数据更新时,客户端就会收到通知。

需要加 - w 选项, -v 版本

7. 条件更新

设想用znode/c 实现一个counter,使用set命令来实现自增1操作。条件更新场景:

客户端1把/c 更新到版本1,实现/c的自增1客户端2把/c 更新到版本2,实现/c的自增1客户端1不知道/c已经被客户端2更新过了,还用过时的版本1是去更新/c,更新失败。如果客户端1使用的是无条件更新,/c就会更新为2,没有实现自增1

使用条件更新可以避免对数据过于过期的数据进行数据更新操作。

javadoc 演示

4.4 开发使用API设置CLASSPATH使用Zookeeper java代码需要使用依赖的jar包,下面的命名把Zookeeper依赖的JAR加到CLASSPATH环境变量:ZOOBINDIR = "/bin" . "$ZOOBINDIR" /zkENV.sh

2. 启动java实例:

gradle compileJava ./scripts/executor.sh localhost:2181 /watch data/znode-data scripts/seq.sh5.分布式5.1 分布式队列

git clone https://github.com/apache/zookeeper.git

yum install ant

ant eclipse

导入到ide

设计

/queue代表znode下的节点。

znode名字后缀数字越小,越靠前

2. offer 方法

在/queue下创建一个顺序znode。因为znode的后缀数字是/queue下面现有znode最大后缀数字加1,所以该znode对应的队列元素处于队尾。

3. element 方法

throw new NoSuchElementException(): 因为element方法读取到了队列为空的状态,所以抛出NoSuchElementException是正确的。return Zookeeper.getData(dir + "/"+headNode, false, null):childNames保存的是队列内容的一个快照。这个ruturn语句返回快照中还没出队的后缀数字最小的znode。如果队列快照的元素都出队了,重试。

4. remove方法

remove和element方法类似。值得注意的是getData的成功执行不意味着出队成功,原因是该队列元素可能会被其他用户出队。

// 编译 ant compile-test ant -Dtest .output=yes test5.2 分布式锁设计 :使用临时顺序 znode 来表示获取锁的请求,创建最小后缀数字znode 的用户成功拿到锁。Recipe说明:Locks

2. 避免羊群效应(herd effect)

把锁请求者按照后缀数字进行排队,后缀数字小的锁,请求者先获取锁。如果所有的锁请求者都watch锁持有者,当代表所请求者的znode被删除以后,所有的锁请求者都会通知到,但是只有一个锁请求者能拿到锁。这就是羊群效应。

设计:

为了避免羊群效应,每个锁请求者watch它前面的锁请求者。每次锁被释放,只会有一个锁请求者会被通知到。这样做还让锁的分配具有公平性,锁定的分配遵循先到先得的原则。

lock

unlock删除锁。

5.3 分布式选举设计

使用临时顺序znode来表示选举请求,创建最小后缀数字znode的选举请求成功。在协同设计上和分布式锁是一样的,不同之处在于具体实现。

不同于分布式锁,选举的具体实现对选举的各个阶段做了监控。

5.4 Apache Curator概述:

目的就是简化Zookeeper的API。例如:在以前的代码展示中,我们都要自己处理ConnectionLossException。另外,Curator为常见的分布式协同服务提供了高质量的实现。

Apache Curator最初是Netflix研发的,后来捐献了Apache基金会,目前是Apache的顶级项目。

2. Curator 技术栈

Client: 封装了Zookeeper类,管理和Zookeeper集群的连接,并提供了重建连接机制。Framework:为所有的Zookeeper操作提供了重试机制,对外提供了一个Fluent风格的APIRecipes:使用framework实现了大量的Zookeeper协同服务。Extensions:扩展模块

3. Fluent风格API

4. 配置Curator源码

5.

5.Curator Recipes 示例-选举

开始竞争,释放leadership

5.5 生产环境配置配置项

zookeeper的配置项在zoo.cfg配置文件中配置,另外有些配置项可以通过Java系统属性来进行配置。

clientPort:Zookeeper对客户端提供服务的端口dataDir: 用来保存快照文件的目录。如果没有设置dataLogDir,事务日志文件也会保存到这个目录。dataLogDir:用来保存事务日志文件的目录。因为Zookeeper在提交一个事务之前,需要保证事务日志记录的落盘,所以需要dataLogDir分配一个独占的存储设备

2. Zookeeper节点硬件要求

给Zookeeper分配独占的服务器,要给Zookeeper的事务日志分配独立的存储设备

内存:Zookeeper需要在内存中保存data tree。对于一般的Zookeeper应用场景,8G的内存足够了CPU: Zookeeper对CPU的消耗不高。只要保证Zookeeper能够有一个独占的CPU核即可。所以使用一个双核的CPU存储:因为存储设备的写延迟会直接影响事务提交的效率,建议为dataLogDir分配一个独占的SSD盘

3.日志配置文件

4. 安装配置步骤

5.6 监控

are you ok?

echo ruok | ncat localhost 2181 echo conf | ncat localhost 2181 echo done | ncat localhost 2181 // 返回临时节点的信息 echo wchc | ncat localhost 2181 // 查看work的命令

JMX 监控

返回更详细的Zookeeper信息。

Zookeeper很好的支持了JMX,大量的监控和管理工作都可以通过JMX来做。可以把Zookeeper的JMX数据集成到Prometheus,使用Prometheus来做Zookeeper的监控。

export JMXPORT = 8081

5.7 通过Zookeeper Observer实现跨区域部署什么是Observer?

2. 通过动态配置实现不中断服务的集群成员变更

手动集群成员调整

停止整个Zookeeper现有集群。更改配置文件zoo.cfg的server.n项启动新集群的Zookeeper节点

问题1:需要停止Zookeeper服务。

问题2: 可能会导致已经提交的数据写入被覆盖。

第一个是airport1事务的任期,第二个airport1事务提交。

在3.5.0新特性-dynamic reconfiguration

可以在不停机动态配置。

加入dynamic

5.8 节点是如何存储数据的?

查看快照

5.9 使用Zookeeper实现服务发现1服务发现

服务发现主要应用于微服务架构和分布式架构场景下。在这些场景下,一个服务通常需要松耦合的多个组件的协同才能完成。服务发现就是让组件发现相关的组件。服务发现要提供的功能有以下3点:

服务注册服务实例的获取服务变化的通知机制

Curator有一个扩展叫做curator-x-discovery. curator-x-discovery基于Zookeeper实现了服务发现。

2. curator-x-dicovery设计

使用一个 base path 作为整个服务发现的根目录。

在这个根目录下是各个服务的目录。服务目录下面是服务实例。实例是服务实例的JSON序列化数据。服务实例对应的znode节点可以根据需要设置成持久化、临时性、顺序性。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3