分布式协调服务Zookeeper

分布式系统介绍

分布式系统的定义
《分布式系统原理和范型》一书中定义:分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像是单个相关系统。

从进程角度看,两个程序分别运行在两台主机的进程上,它们互相协作最终完成同一个服务或者功能,那么理论上这个两个程序所组成的系统,也可以称作是"分布式系统"。

分布式系统遇到的挑战

  • 1、分布式session
    集群中的各机器之间如何保证Session同步?
  • 2、分布式配置中心
    如何在不停应用功能集群的情况下,调整整个集群运行时的行为特征?
  • 3、分布式事务
    分布式事务解决的用户最本质诉求是数据一致性。
  • 4、分布式锁
    如何解决分布式场景中的数据一致性?需要用到分布式锁。
  • 5、分布式定时任务

分布式系统问题的本质

分布式各系统之间都需要进行网络通信,所以本来在单一架构中能保证的数据一致性,升级为分布式系统后数据的一致性就难以保证,而Zookeeper的诞生就可以解决这个本质问题:数据一致性,再加上Zookeeper的其他特性还可以解决分布式锁,分布式定时任务等场景问题。

Zookeeper简介

Apache Zookeeper是Apache软件基金会的一个软件项目,用于为分布式系统提供分布式配置、命名注册、分布式同步以及组服务。

下载与安装

下载地址

下载后解压缩,这里下载的事3.4.13版本,进入zookeeper根目录
zookeeper
conf文件夹下是存放配置文件的地方,复制一份zoo_sample.cfg,名称改为zoo.cfg,这个文件就是zookeeper默认使用的配置文件。

下面看下zoo.cfg配置文件中的各配置项

  • tickTime: Zookeeper服务器之间或客户端与服务器之间维持心跳的时间间隔,以毫秒为单位。Zookeeper中最小的session过期时间是tickTime的两倍。
  • initLimit: Follower在启动过程中,会从Leader同步所有最新数据,然后确定自己能够对外服务的起始状态。initLimit用于配置这个同步时间,默认是10倍的tickTime
  • syncLimit: 在发送请求和获得确认之间的时间间隔。比如,Leader发送心跳包后,如果超过了sycnLimit指定的时间间隔后,还没有收到Follower发回的响应,则认为这个Follower已经不在线了。
  • dataDir:
    存储快照文件snapshot的目录。默认情况下,事务日志也会存储在这里。建议同时配置参数dataLogDir,事务日志的写性能直接影响zk性能。
  • clientPort: 客户端连接服务器的端口
基础命令
  • 服务端启动: bin/zkServer.sh start xxxx ,其中xxxx代表配置文件路径(默认使用zookeeper/conf/zoo.cfg),其中start表示启动,选项包括(start|start-foreground|stop|restart|status|upgrade|print-cmd)。

  • 客户端连接ZK: bin/zkCli.sh -server ip:portip:port默认使用localhost:2181

  • help查看可用的执行命令

  • ls /列出根目录下的内容

  • 创建节点
    create /zk_test my_data 在根目录下创建节点,节点名称 zk_test,节点中保存的数据my_data。可以使用flag指定创建的节点是临时的,持久的还是顺序的。不填写代表默认持久。

    • 临时节点: create -e /zk_ephemeral,客户端断开连接时,临时节点会被清除。
    • 顺序节点: create -s /zk_seq,zookeeper会向节点路径填充10为序列号,节点/zk_seq将转换为/zk_seq0000000001,下一个序列号是/xxxx0000000002(xxxx代表输入的节点名称),顺序递增。
  • 获取数据:
    get /zk_test 获取zk_test节点中的数据

  • 设置数据:
    set /zk_test my_data2 更新/zk_test节点数据为my_data2

  • 删除数据:
    delete /zk_test 删除zk_test节点,有子节点则无法删除

  • 递归删除数据:

  • rmr /zk_test 删除zk_test节点,包含子节点

  • 检查状态:

  • stat /zk_test,获取指定znode的元数据,包含时间戳、版本号、ACL、数据长度和znode等细项

更多信息可参考ZooKeeper Programmer’s Guide

zk操作1zk操作2

zookeeper集群搭建

集群的角色
  • 领导者Leader: 选举产生,领导者不接收client的请求,负责进行投票的发起和决议,最终更新状态。
  • 跟随者Follower: 用于接收客户请求并返回结果,参与Leader发起的投票。 过多会导致选举算法耗时较多。
  • 观察者Observer: 可以接收客户端连接,只接受读请求,将写请求转发给Leader节点,Observer不参与投票,只是同步Leader的状态。
  • 学习者Learner: 和Leader进行状态同步的server统称为Learner,上述Follower和Observer都是Learner。

为了方便模拟集群,在单台机器上搭建一套包含4个实例的zookeeper集群(其中包含一个observer),生产环境中一般在不同的机器上安装实例,并且参加选举的ZK实例数最好是单数。

第一步:复制4个配置文件

端口号分别设为2181、2182、2183、2184

新增数据存放位置,zkjdata文件夹下分别设为 data1、data2、data3、data4

其中zkjqdata,是在zookeeper根目录下新建的文件夹,由于存放集群数据,可根据实际情况自行指定。

第二步:配置文件中增加集群配置

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

其中localhost后面的第一个端口用于同步,第二个端口用于选举Leader。

observer标识该实例的角色是观察者,另外observer所在的节点配置中还需要增加peerType=observer

peerType=observer 这个选项更多是为了方便运维人员识别哪台机器是Observer,底层并不根据这个配置来判定谁是Observer角色,所以也可以不配

四个配置文件内容如下:
zoo1.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zookeeper-3.4.13/zkjqdata/data1
clientPort=2181

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

zoo2.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zookeeper-3.4.13/zkjqdata/data2
clientPort=2182

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

zoo3.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zookeeper-3.4.13/zkjqdata/data3
clientPort=2183

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

zoo4.cfg

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/zookeeper-3.4.13/zkjqdata/data4
clientPort=2184
peerType=observer

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

第三步:新增myid文件
在第一步创建的每个数据文件夹下新增myid文件,文件的内容要和第二步中server.X中".后面的X值对应

比如第一个实例的配置文件内容如下:

tickTime=2000
initLimit=10
syncLimit=5
dataDir=/iflytek/zookeeper-3.4.13/zkjqdata/data1
clientPort=2181

server.1=localhost:2887:3887
server.2=localhost:2888:3888
server.3=localhost:2889:3889
server.4=localhost:2890:3890:observer

那么需要在data1文件夹下新增myid文件,内容为1。

第四步:指定配置文件启动实例
bin/zkServer.sh start /zookeeper-3.4.13/conf/zoo1.cfg
bin/zkServer.sh start /zookeeper-3.4.13/conf/zoo2.cfg
bin/zkServer.sh start /zookeeper-3.4.13/conf/zoo3.cfg
bin/zkServer.sh start /zookeeper-3.4.13/conf/zoo4.cfg

然后使用bin/zkCli -server localhost:2181bin/zkCli -server localhost:2182bin/zkCli -server localhost:2183bin/zkCli -server localhost:2184,分别登录系统看实例是否能成功连上ZK Server。

测试:
1、在某个ZK Server下执行新建命令,看看其它ZK Server是否同步到数据。
2、停止某个ZK Server Leader,看下新选举出来的Leader是哪一个。
3、参与选举的ZK Server 停掉过半,整个集群会处于无法使用状态。
4、停掉observer所在Server,会发现对集群没有影响。

以上请自行测试,这里不再演示。

Observer存在的意义

Zookeeper服务中的每个Server可服务于多个Client,并且Client可连接到ZK服务中的任一台Server来提交请求。若是读请求,则由每台Server的本地副本数据库直接响应。若是改变Server状态的写请求,需要通过一致性协议(Zab协议)来处理。

简单来说,Zab协议规定: 来自Client的所有写请求,都要转发给ZK服务集群中唯一的Leader,由Leader根据该请求发起一个Proposal,然后,其他的Server对该Proposal进行投票。之后,Leader对投票进行收集,当投票数量过半时Leader会向所有的Server发送一个通知消息。最后,当Client所连接的Server收到该消息时,会把该操作更新到内存中并对Client写请求做出回应。

Zookeeper服务器在上述协议中实际扮演了两个职能。一方面从客户端接收连接与操作请求,另一方面对操作结果进行投票。这两个职能在Zookeeper集群扩展的时候彼此制约。例如,当我们希望增加ZK服务中CLient数量的时候,那么我们需要增加Server的数量。然而,从Zab协议对写请求的处理过程中我们可以发现,增加服务器的数量,会增加对协议中投票过程的压力。因为Leader节点必须等待集群中过半Server响应投票,于是节点的增加拖慢整个投票过程的可能性随之增加,导致写操作随之下降。

为此我们引入了不参与投票的服务器,Observer可以接受客户端的连接,并将写请求转发给Leader节点。但是Leader节点不会要求Observer参加投票。相反,Observer不参与投票过程,只接受投票结果。但这种方式也存在一定问题,因为协议中的通知阶段,仍然与服务器的数量呈线性关系。但是这里的串行开销很低,我们可以认为这个阶段的开销不会成为主要瓶颈。

Zookeeper的应用场景
  • 统一命名服务
    在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务地址,提供者等信息。比如分布式服务框架中的服务地址列表,在Zookeeper中,能够很容易创建一个全局唯一的path,这个path就可以作为一个名称。

  • 配置中心
    分布式系统的配置项可以交给Zookeeper管理,将配置信息保存在ZK的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到ZK的通知,然后从ZK中获取新的配置信息应用到系统中。

  • 集群管理和Master选举
    利用Zookeeper的两个特性,可以实现对集群机器存活性的监控:

    • 1.客户端在某个节点x上注册一个Watch,那么如果x的子节点发生变化,会通知该客户端
    • 2.创建EPHEMERAL类型的节点,一旦客户端和服务器的回话结束或郭琦,那么该节点就会消失。

    在分布式系统中,有些业务逻辑往往只需要整个集群中的某一台机器执行,其余机器可以共享这个结果,master选举便是这种场景下需要解决的问题。

  • 分布式锁
    分布式锁主要得益于Zookeeper为我们保证了数据的强一致性。锁服务可以分为两类:

    • 保持独占
      所有试图来获取这个锁的客户端,最终只有一个可以成功获取。通常的做法是把ZK上的一个znode看做是一把锁,通过create znode的方式来实现。所有客户端都去创建/distribute_lock节点,最终成功创建的客户端也即拥有了这把锁。

    • 控制时序
      所有试图来获取这个锁的客户端,最终都会被安排执行,只是有个全局时序了。做法和保持独占基本类似,只是这里/distribute_lock已经预先存在,客户端在它下面创建临时有序节点。父节点/distribute_lock维持一份sequence,保证子节点创建的时序性,从而形成了每个客户端的全局时序。

Zookeeper中的CAP

Zookeeper满足CAP定理中的CP原则。如果Leader服务器挂掉,需要重新进行选举,在选举过程中,集群是不可用的,牺牲了可用性。


------------本文结束感谢您的阅读------------
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值