上一篇文章,我们讲解了 ZooKeeper 入门知识,这篇文章主要讲解下 ZooKeeper 的架构,理解 ZooKeeper 的架构可以帮助我们更好地设计协同服务。

首先我们来看下 ZooKeeper 的总体架构图。

ZooKeeper 总体架构

应用使用 ZooKeeper 客户端库来使用 ZooKeeper 服务,ZooKeeper 客户端会和集群中某一个节点建立 session, ZooKeeper 客户端负责和 ZooKeeper 集群的交互。 ZooKeeper 集群可以有两种模式:standalone 模式和 quorum 模式。处于 standalone 模式的 ZooKeeper 集群还有一个独立运行的 ZooKeeper 节点,standalone 模式一般用来开发。实际生产环境 ZooKeeper 一般处于 quorum 模式,在 quorum 模式下 ZooKeeper 集群包换多个 ZooKeeper 节点。

Session

Session 是 ZooKeeper 客户端的一个重要概念,ZooKeeper 客户端库和 ZooKeeper 集群中的节点创建一个 session。客户端可以主动关闭 session。另外如果 ZooKeeper 节点没有在 session 关联的 timeout 时间内收到客户端的数据的话,ZooKeeper 节点也会关闭 session。另外 ZooKeeper 客户端库如果发现连接的 ZooKeeper 出错,会自动的和其他 ZooKeeper 节点建立连接。

下图展示了 ZooKeeper 客户端是如何进行重连的?

刚开始 ZooKeeper 客户端和 ZooKeeper 集群中的节点 1 建立的 session,在过了一段时间后,ZooKeeper 节点 1 失败了,ZooKeeper 客户端就自动和 ZooKeeper 集群中的节点 3 重新建立了 session 连接。

Quorum 模式

处于 Quorum 模式的 ZooKeeper 集群包含多个 ZooKeeper 节点。 下图的 ZooKeeper 集群有 3 个节点,其中节点 1 是 leader 节点,节点 2 和节点 3 是 follower 节点。集群中只能有一个 leader 节点,可以有多个 follower 节点,leader 节点可以处理读写请求,follower 只可以处理读请求。follower 在接到写请求时会把写请求转发给 leader 来处理。

下面来说下 ZooKeeper 保证的数据一致性:

数据一致性

  • 可线性化(Linearizable)写入:先到达 leader 的写请求会被先处理,leader 决定写请求的执行顺序。
  • 客户端 FIFO 顺序:来自给定客户端的请求按照发送顺序执行。

为了让大家更好地理解 Quorum 模式,下面会配置一个 3 节点的 Quorum 模式的 ZooKeeper 集群。

搭建 3 节点 ZooKeeper 集群

首先需要准备 3 个配置文件,dataDir 和 clientPort 配置项要配置不同的值。3 个配置文件的 server.n 部分都是一样的。在 server.1=127.0.0.1:3333:3334,其中 3333 是 quorum 之间的通信的端口号,3334 是用于 leader 选举的端口号。

还需要在每个节点的 dataDir 目录下创建 myid 文件,里面内容为一个数字,用来标识当前主机,配置文件中配置的 server.n 中 n 为什么数字,则 myid 文件中就输入这个数字。

如下是第 1 个节点的配置文件,其中目录是 node1,端口号用的是 2181,另外两个节点的目录分别是 node2 和 node3,端口号分别为 2182 和 2183,最下面的三行都是一样的:

# 心跳检查的时间 2秒
tickTime=2000
# 初始化时 连接到服务器端的间隔次数,总时间10*2=20秒
initLimit=10
# ZK Leader 和follower 之间通讯的次数,总时间5*2=10秒
syncLimit=5
# 存储内存中数据库快照的位置,如果不设置参数,更新事务日志将被存储到默认位置。
dataDir=/data/zk/quorum/node1
# ZK 服务器端的监听端口
clientPort=2181 server.1=127.0.0.1:3333:3334
server.2=127.0.0.1:4444:4445
server.3=127.0.0.1:5555:5556

下面来启动这个集群,首先启动第一个节点,启动命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node1.cfg

注:start-foreground 选项 zkServer.sh 在前台运行,把日志直接打到 console。如果把日志打到文件的话,这三个 zkServer.sh 会把日志打到同一个文件。

在启动第一个节点后日志中会出现如下:

2019-12-29 13:14:35,758 [myid:1] - WARN  [WorkerSender[myid=1]:QuorumCnxManager@679] - Cannot open channel to 2 at election address /127.0.0.1:4445
java.net.ConnectException: Connection refused (Connection refused)
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:589)
at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:650)
at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectOne(QuorumCnxManager.java:707)
at org.apache.zookeeper.server.quorum.QuorumCnxManager.toSend(QuorumCnxManager.java:620)
at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.process(FastLeaderElection.java:477)
at org.apache.zookeeper.server.quorum.FastLeaderElection$Messenger$WorkerSender.run(FastLeaderElection.java:456)
at java.lang.Thread.run(Thread.java:748)

原因是配置文件中配置的为 3 个节点,但是只启动了 1 个节点,目前他和其他另外两个节点建立不了连接,所以报这个问题。

接下来启动第 2 个节点,执行命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node2.cfg

启动后,我们可以在节点 2 的日志中发现这么一行:

2019-12-29 13:15:13,699 [myid:2] - INFO  [QuorumPeer[myid=2](plain=/0.0.0.0:2182)(secure=disabled):Leader@464] - LEADING - LEADER ELECTION TOOK - 41 MS

这说明节点 2 成为了 leader 节点,同样可以在节点 1 的日志中发现如下一行日志,说明了节点 1 成为了 follower 节点。

2019-12-29 13:15:13,713 [myid:1] - INFO  [QuorumPeer[myid=1](plain=/0.0.0.0:2181)(secure=disabled):Follower@69] - FOLLOWING - LEADER ELECTION TOOK - 61 MS

因为对于一个三节点集群来说两个就代表了多数,就形成了 Quorum 模式,接下来启动第 3 个节点,执行命令如下:

zkServer.sh start-foreground /usr/local/apache-zookeeper-3.5.6-bin/conf/zoo-quorum-node3.cfg

启动后在日志中也有如下一行,说明第 3 个节点也加入这个集群,并且是作为 follower 节点加入的。

2019-12-29 13:15:52,440 [myid:3] - INFO  [QuorumPeer[myid=3](plain=/0.0.0.0:2183)(secure=disabled):Follower@69] - FOLLOWING - LEADER ELECTION TOOK - 15 MS

下面来启动客户端来使用这个三节点集群,在命令中加了 -server 选项,后面指定的是三个节点的主机名和端口号,命令如下:

zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
2019-12-29 13:45:44,982 [myid:127.0.0.1:2181] - INFO  [main-SendThread(127.0.0.1:2181):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2181, sessionid = 0x101fff740830000, negotiated timeout = 30000

通过启动日志可以看到客户端和端口号为 2181 的节点建立了连接,也就是和第 1 个节点建立了连接。

我们执行 ls -R / 命令看下这个集群中的 znode 数据。

[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 1] ls -R /
/
/zookeeper
/zookeeper/config
/zookeeper/quota

下面我们杀掉一个 ZooKeeper 节点,看客户端是否能进行重连。现在我们连的节点 1,我们来把节点 1 杀掉,可以在客户端的日志中发现客户端和端口号为 2183 的节点重新建立了连接,也就是和节点 3 建立了连接。

2019-12-29 14:03:31,392 [myid:127.0.0.1:2183] - INFO  [main-SendThread(127.0.0.1:2183):ClientCnxn$SendThread@1394] - Session establishment complete on server localhost/127.0.0.1:2183, sessionid = 0x101fff740830000, negotiated timeout = 30000

然后我们再看下客户端能否正常使用,执行 ls -R / ,可以发现能够正常返回数据,说明客户端是能够正常使用的。

[zk: 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183(CONNECTED) 4] ls -R /
/
/zookeeper
/zookeeper/config
/zookeeper/quota

总结

这篇文章主要讲解了 ZooKeeper 架构,以及怎样配置一个三节点的 ZooKeeper 集群。

一篇文章带你了解 ZooKeeper 架构的更多相关文章

  1. MYSQL(进阶篇)——一篇文章带你深入掌握MYSQL

    MYSQL(进阶篇)--一篇文章带你深入掌握MYSQL 我们在上篇文章中已经学习了MYSQL的基本语法和概念 在这篇文章中我们将讲解底层结构和一些新的语法帮助你更好的运用MYSQL 温馨提醒:该文章大 ...

  2. 一篇文章带你掌握主流数据库框架——MyBatis

    一篇文章带你掌握主流数据库框架--MyBatis MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射. 在之前的文章中我们学习了MYSQL和JDBC,但是这些东西远远不 ...

  3. 一篇文章带你掌握主流基础框架——Spring

    一篇文章带你掌握主流基础框架--Spring 这篇文章中我们将会介绍Spring的框架以及本体内容,包括核心容器,注解开发,AOP以及事务等内容 那么简单说明一下Spring的必要性: Spring技 ...

  4. 一篇文章带你掌握主流办公框架——SpringBoot

    一篇文章带你掌握主流办公框架--SpringBoot 在之前的文章中我们已经学习了SSM的全部内容以及相关整合 SSM是Spring的产品,主要用来简化开发,但我们现在所介绍的这款框架--Spring ...

  5. MYSQL(基本篇)——一篇文章带你走进MYSQL的奇妙世界

    MYSQL(基本篇)--一篇文章带你走进MYSQL的奇妙世界 MYSQL算是我们程序员必不可少的一份求职工具了 无论在什么岗位,我们都可以看到应聘要求上所书写的"精通MYSQL等数据库及优化 ...

  6. 一篇文章带你掌握主流服务层框架——SpringMVC

    一篇文章带你掌握主流服务层框架--SpringMVC 在之前的文章中我们已经学习了Spring的基本内容,SpringMVC隶属于Spring的一部分内容 但由于SpringMVC完全针对于服务层使用 ...

  7. 一篇文章带你掌握MyBatis简化框架——MyBatisPlus

    一篇文章带你掌握MyBatis简化框架--MyBatisPlus 我们在前面的文章中已经学习了目前开发所需的主流框架 类似于我们所学习的SpringBoot框架用于简化Spring开发,我们的国人大大 ...

  8. 一篇文章带你了解网页框架——Vue简单入门

    一篇文章带你了解网页框架--Vue简单入门 这篇文章将会介绍我们前端入门级别的框架--Vue的简单使用 如果你以后想从事后端程序员,又想要稍微了解前端框架知识,那么这篇文章或许可以给你带来帮助 温馨提 ...

  9. 一篇文章带你了解热门版本控制系统——Git

    一篇文章带你了解热门版本控制系统--Git 这篇文章会介绍到关于版本控制的相关知识以及版本控制神器Git 我们可能在生活中经常会使用GitHub网页去查询一些开源的资源或者项目,GitHub就是基于G ...

随机推荐

  1. MUI - 关于百度定位

    关于百度定位 这是官方定位的解释:geolocation 地图插件配置 我在问答里面找到了这位童鞋的百度定位,地址变更提醒 *** 不过,童鞋倒是给具体的示例啊,木有~~~~(>_<)~~ ...

  2. qt 在ui界面添加控件后在cpp文件中无法调用?

    问题:qt 在ui界面添加控件后在cpp文件中无法调用? 解决方法:在build选项中选择“重新build项目”,再次在cpp中调用添加的控件发现可以调用了. 还有一种情况导致添加控件后无法调用,就是 ...

  3. oracle函数 ABS(x)

    [功能]返回x的绝对值 [参数]x,数字型表达式 [返回]数字 [示例] select abs(100),abs(-100) from dual; sign(x) [功能]返回x的正负值 [参数]x, ...

  4. python起个简单web服务器

    在 Linux 服务器上或安装了 Python 的机器上,Python自带了一个WEB服务器 SimpleHTTPServer. 我们可以很简单的使用  python -m SimpleHTTPSer ...

  5. ThinkPHP URL 路由简介

    简单的说,URL 路由就是允许你在一定规则下定制你需要的 URL 样子,以达到美化 URL ,提高用户体验,也有益于搜索引擎收录的目的. 例子 原本的 URL 为: http://www.5idev. ...

  6. Editplus配置java编译运行环境

    1.进入配置环境界面 首先,从菜单"工具(Tools)"->"配置用户工具..."进入用户工具设置. 在类别里展开"工具"树形菜单-& ...

  7. Python--day47--内容回顾

    1.什么是数据库

  8. Js中“==”和“===”的区别

    从字面上来讲,‘==’代表相等,‘===’代表严格相等. 具体来讲,比较过程如下: 比较过程: ‘==’:      1. 首先判断两个值的类型是否相同,如果相同,进行‘===’判断.      2. ...

  9. [Atom 编辑器 ] Packages

    Atom包      https://atom.io/packages 常用包整理: atom-chinese-menu   中文插件 atom-ternjs   对 es5,es6的语法支持 ato ...

  10. java 文件过滤器 java.io.FilenameFilter

    File 类里有方法: String[] list(FilenameFilter filter)    返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录. File ...