zookeeper08-ZooKeeper的重配置

1、为什么不使用简单的扩容

  • 现在有三台机器组成的ZooKeeper集群。但是一两个月后,你会发现使用ZooKeeper的客户端越来越多,并且成为一个关键的服务,因此你想要把服务器扩容到五台,没什么大不了的,是嘛?你可以在深夜停止集群,重新配置所有服务器,并在不到一分钟的时间里恢复服务。如果你的应用程序恰当处理了Disconnected事件,用户可能不会感知到服务中断。我们在刚开始开发ZooKeeper时也是这么想的,但事实证明,情况要复杂得多。
  • 请看图10-1中的场景,三个服务器(A、B、C)组成了整个集群。服务器C因网络拥塞稍稍落后于整个集群,因此服务器C刚刚了解事务到(1,3)(其中1为时间戳,3为对应该时间戳的事务标识)。因为服务器A和B的通信良好,所以服务器C稍稍落后并不会导致整个系统变慢,因此服务器A和B已经提交事务到(1,6)。

zookeeper08-ZooKeeper的重配置

  • 现在,假设我们将所有zookeeper服务停止,把服务器D和E添加到集群中,当然这两台新服务器并不存在任何状态信息。我们重新配置A、B、C、D和E服务器,使它们成为一个更大的集群,然后重新启动服务。因为我们现在有五台服务器,因此至少需要三台服务器组成法定人数。三台服务器C、D和E就能够构成法定人数,因此当这些服务器(图10-2)构成法定人数并开始同步时都会发生什么。这个场景可以简单重现,如果服务器A和B的启动慢一些,比如服务器A和B比其他三个服务器的启动晚一些。一旦新的法定人数开始同步,服务器D和E就会与服务器C进行同步,因为法定人数中服务器C的状态为最新状态,法定人数的三个成员服务器会同步到最后的事务(1,3),而不会同步(1,4)、(1,5)和(1,6)这三个事务,因为服务器A和B并不是构成法定人数的成员。

zookeeper08-ZooKeeper的重配置

  • 因为已经构成一个活跃的法定人数,这些服务器可以开始提交新事务,我们假设有两个事务:(2,1)和(2,2)。如图10-3所示,当服务器A和B启动后连接到服务器C后,服务器C作为群首欢迎其加入到集群之中,并在收到事务(2,1)和(2,2)后立即告知服务器A和B删除事务(1,4)、(1,5)和(1,6)。

zookeeper08-ZooKeeper的重配置

  • 这个结果非常糟糕,我们丢失了某些状态信息,而且状态副本与客户端所看到的(1,4)、(1,5)、(1,6)也不再一致。为了避免这个问题,ZooKeeper提供了重配置操作,这意味着运维人员并不需要手工进行重配置操作,也不会破坏状态,而且也不需要停止任何服务。

2、ZooKeeper的重配置

  • 重配置不仅可以让我们改变集群成员配置,还可以修改网络参数配置,因为ZooKeeper中配置信息的变化,需要将重配置参数与静态的配置文件分离,单独保存为一个配置文件并自动更新该文件。dynamicConfigFile参数将两个文件链接在一起。
  • 在使用动态配置之前,回顾一下之前的配置文件:
]# grep '^[^#]' /usr/local/apache-zookeeper-3.5.9-bin/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper/data
clientPort=2181
dataLogDir=/tmp/zookeeper/log
server.1=10.1.1.10:2888:3888
server.2=10.1.1.11:2888:3888
server.3=10.1.1.12:2888:3888
  • 现在,我们将配置文件修改为动态配置方式:
--从配置文件中删除了clientPort
]# vim /usr/local/apache-zookeeper-3.5.9-bin/conf/zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/tmp/zookeeper/data
dataLogDir=/tmp/zookeeper/log
dynamicConfigFile=./conf/dyn.cfg

--dyn.cfg文件只由服务器条目组成
]# vim /usr/local/apache-zookeeper-3.5.9-bin/conf/dyn.cfg
server.1=10.1.1.10:1188:1288:participant;2181
server.2=10.1.1.11:2188:2288:participant;2182
server.3=10.1.1.12:3188:3288:participant;2183
  • 注意,我们已经从配置文件中删除了clientPort参数。现在,dyn.cfg文件只由服务器条目组成,每个条目都是一样的:
    • server.id=host:n:n[:role];[client_address:]client_port
      • 与正常的配置文件一样,每个服务器都列出了主机名和分别用于仲裁、leader选举的端口号。
      • role:其值必须为participant或observer。默认值是participant。
      • client_port:服务器监听的端口,用于客户端连接的服务器端口号。
      • client_address:绑定服务器的特定网络接口地址。
  • 我们使用重配置之前必须先创建这些文件,一旦这些文件就绪,我们就可以通过reconfg操作来重新配置一个集群,该操作可以增量或全量(整体)地进行更新操作。
  • 增量的重配置操作有两个列表:待删除的服务器列表,待添加的服务器项的列表。待删除的服务器列表仅是使用逗号分隔的服务器ID列表,待添加的服务器项列表是使用逗号分隔的服务器项列表,每个服务器项的形式为动态配置文件中所定义的形式。例如:
[zk: localhost:2181(CONNECTED) 0] reconfig -remove 2,3 -add server.4=10.1.1.14:4188:4288:participant;2184,server.5=10.1.1.15:5188:5288:participant;2185
  • 该命令将会从集群中删除服务器2和3,添加服务器4和5。该操作想要成功执行还需要满足一些条件,首先,与其他Zookeeper操作一样,原配置中法定人数必须处于活动状态;其次新的配置文件中构成的法定人数也必须处于活动状态。
  • 我们还可以使用-file参数来指定一个新的成员配置文件来进行一次全量更新。例如:reconfig -file newconf命令会产生如上面命令一样的增量操作结果, newcon文件为:
server.1=10.1.1.10:1188:1288:participant;2181
server.4=10.1.1.14:4188:4288:participant;2184
server.5=10.1.1.15:5188:5288:participant;2185
  • 最后,所有形式的reconfig都可以是有条件的。如果通过-v参数提供了配置版本号,reconfig命令会在执行前确认配置文件当前的版本号是否匹配,只有匹配才会成功执行。你可以通过读取/zookeeper/config来获取当前配置的版本号,或通过zkCli工具来调用config获取配置版本号信息。 
[zk: localhost:2181(CONNECTED) 0] get /zookeeper/config 
server.1=10.1.1.10:2888:3888:participant
server.2=10.1.1.11:2888:3888:participant
server.3=10.1.1.12:2888:3888:participant
version=0

3、管理客户端的连接串

  • 我们已经讨论了ZooKeeper服务器配置,但是客户端也有一些相关的配置:连接串。客户端连接串通常表示为由逗号分隔的host:port对,其中host为主机名或IP地址。使用主机名可以在服务器的实际IP地址和用于访问服务器的标识符之间建立一个间接层。例如,运维人员可以替换ZooKeeper服务为另一个,而不需要改变客户端的配置。
  • 不过,这种灵活性有一定限制,运维人员可以改变组成集群的服务器机器,但不能改变客户端正在使用的服务器。例如,如图10-4所示,ZooKeeper可以通过重配置很简单地将集群从三个服务器扩展到五个服务器,但客户端仍然使用三个服务器,而不是五个。

zookeeper08-ZooKeeper的重配置

  • 还有一种方法可以在不改变客户端配置的情况下,使ZooKeeper在服务器数量方面更具弹性。一个主机名解析为单个IP地址是很自然的,但实际上一个主机名可以解析为多个地址。如果一个主机名解析为多个IP地址, 客户端可以连接到其中任何一个IP地址。在图10-4中,假设服务器zk-a、zk-b和zk-c分别解析为三个独立的IP地址10.0.0.1、10.0.0.2和10.0.0.3。现在,假设您使用DNS配置一个主机名zk,并解析为这三个IP地址,你只需要修改DNS解析的地址数量,之后启动的任何客户端就可以访问这五个服务器,如图10-5所示。

zookeeper08-ZooKeeper的重配置

  • 在使用主机名解析为多个地址方式时,有一些注意事项。首先,所有的服务器必须使用相同的端口号;其次,解析主机名只有在创建连接时才会发生,所以已经连接的客户端无法知道最新的名称解析,只能对新创建的客户端生效。
  • 客户端的连接还可以包含路径信息,此路径指示解析路径名时要使用的根路径,其行为类似于Unix中的chroot命令,而且在ZooKeeper社区中你也会经常听到人们以"chroot"来称呼这个功能。例如,如果客户端的连接串为zk:222/app/superApp,当客户端连接并执行getData("/a.dat",...)操作时,实际客户端会得到/app/superApp/a.dat节点的数据信息(注意,连接串中指示的路径必须存在,因为连接串不会为你创建该路径)
  • 在连接串中添加路径信息的动机在于一个ZooKeeper集群可以为多个应用程序提供服务,这样不需要要求每个应用程序添加其路径的前缀信息。每个应用程序都可以使用ZooKeeper,就像它是唯一使用集合的应用程序一样,运维人员可以按自己的意愿来划分命名空间。图10-6的示例展示了不同的连接串可以为客户端应用程序提供不同的名称空间。

zookeeper08-ZooKeeper的重配置

  • 连接串的重叠
    • 当管理客户端连接串时,注意一个客户端的连接串永远不要包含两个不同的Zookeeper集群的主机名,这是导致脑裂最快速也是最简单方式。
上一篇:idea 本地啓動 zookeeper


下一篇:zookeeper06-ZooKeeper内部原理