一、配置 ZooKeeper 服务器

ZooKeeper 服务器在启动时从一个名为 zoo.cfg 的配置文件读取所有选项,多个服务器如果角色相似,同时基本配置信息一样,就可以共享一个文件。
data 目录下的 myid 文件用于区分各个服务器,对每个服务器来说,data 目录是唯一的,因此这个目录可以更加方便地保持一些差异化文件。服务器ID 将 myid 文件作为一个索引引入到配置文件中,一个特定的 ZooKeeper 服务器可以知道如何配置自己参数。
配置参数常常通过配置文件的方式进行设置,本节后续部分,通过列表方式列出了这些参数。很多参数也可以通过Java的系统属性传递,其形式通常为zookeeper.propertyName,在启动服务器时,通过-D选项设置这些属性。不过,系统属性所对应的一个特定参数对服务来说是插入的配置,配置文件中的配置参数优先于系统属性中的配置。

1、基本配置

  • clientPort
    客户端所连接的服务器所监听的TCP端口,默认情况下,服务端会监听在所有的网络连接接口的这个端口上,除非设置了clientPortAddress参数。客户端端口可以设置为任何值,不同的服务器也可以监听在不同的端口上。默认端口号为2181。
  • dataDir 和 dataLogDir

    • dataDir:用于配置内存数据库保存的模糊快照的目录,如果某个服务器为集群中的一台,id文件也保存在该目录下。dataDir并不需要配置到一个专用存储设备上,快照将会以后台线程的方式写入,且并不会锁定数据库,而且快照的写入方式并不是同步方式,直到写完整快照为止。
    • dataLogDir:事务日志对该目录所处的存储设备上的其他活动更加敏感,服务端会尝试进行顺序写入事务日志,以为服务端在确认一个事务前必须将数据同步到存储中,该设备的其他活动(尤其是快照的写入)可能导致同步时磁盘过于忙碌,从而影响写入的吞吐能力。因此,最佳实践是使用专用的日志存储设备,将dataLogDir的目录配置指向该设备。
  • tickTime
    tick的时长单位为毫秒,tick为ZooKeeper使用的基本的时间度量单位,在9.7节已经介绍过,该值还决定了会话超时的存储器大小。Zookeeper集群中使用的超时时间单位通过tickTime指定,也就说,实际上tickTime设置了超时时间的下限值,因为最小的超时时间为一个tick时间,客户端最小会话超时事件为两个tick时间。
    tickTime的默认值为3000毫秒,更低的tickTime值可以更快地发现超时问题,但也会导致更高的网络流量(心跳消息)和更高CPU使用率(会话存储器的处理)。

2、存储配置

  • preAllocSize
    用于设置预分配的事务日志文件(zookeeper.preAllocSize)的大小值,以KB为单位。
    当写入事务日志文件时,服务端每次会分配preAllocSize值的KB的存储大小,通过这种方式可以分摊文件系统将磁盘分配存储空间和更新元数据的开销,更重要的是,该方式也减少了文件寻址操作的次数。

    默认情况下preAllocSize的值为64MB,缩小该值的一个原因是事务日志永远不会达到这么大,因为每次快照后都会重新启动一个新的事务日志,如果每次快照之间的日志数量很小,而且每个事务本身也很小,64MB的默认值显然就太大了。例如,如果我们每1000个事务进行一次快照,每个事务的平均大小为100字节,那么100KB的preAllocSize值则更加合适。默认的preAllocSize值的设置适用于默认的snapCount值和平均事务超过512字节的情况。

  • snapCount
    指定每次快照之间的事务数(zookeeper.snapCount)。
    当Zookeeper服务器重启后需要恢复其状态,恢复时两大时间因素分别是为恢复状态而读取快照的时间以及快照启动后所发生的事务的执行时间。执行快照可以减少读入快照文件后需要应用的事务数量,但是进行快照时也会影响服务器性能,即便是通过后台线程的方式进行写入操作。
    snapCount的默认值为100000,因为进行快照时会影响性能,所以集群中所有服务器最好不要在同一时间进行快照操作,只要仲裁服务器不会一同进行快照,处理时间就不会受影响,因此每次快照中实际的事务数为一个接近snapCount值的随机数。
    注意,如果snapCount数已经达到,但前一个快照正在进行中,新的快照将不会开始,服务器也将继续等到下一个snapCount数量的事务后再开启一个新的快照。
  • autopurge.snapRetainCount
    当进行清理数据操作时,需要保留在快照数量和对应的事务日志文件数量。
    ZooKeeper将会定期对快照和事务日志进行垃圾回收操作,autopurge.snapRetainCount值指定了垃圾回收时需要保留的快照数,显然,并不是所有的快照都可以被删除,因为那样就不可能进行服务器的恢复操作。autopurge.snapRetainCount的最小值为3,也是默认值的大小。
  • autopurge.purgeInterval
    对快照和日志进行垃圾回收(清理)操作的时间间隔的小时数。如果设置为一个非0的数字,autopurge.purgeInterval指定了垃圾回收周期的时间间隔,如果设置为0,默认情况下,垃圾回收不会自动执行,而需要通过ZooKeeper发行包中的zkCleanup.sh脚本手动运行。
  • fsync.warningthresholdms
    触发警告的存储同步时间阀值(fsync.warningthresholdms),以毫秒为单位。
    ZooKeeper服务器在应答变化消息前会同步变化情况到存储中。如果同步系统调用消耗了太长时间,系统性能就会受到严重影响,服务器会跟踪同步调用的持续时间,如果超过fsync.warningthresholdms只就会产生一个警告消息。默认情况下,该值为1000毫秒。
  • weight.x=n
    该选项常常以一组参数进行配置,该选项指定组成一个仲裁机构的某个服务器的权重为n,其权重n值指示了该服务器在进行投票时的权重值。在ZooKeeper中一些部件需要投票值,比如群首选举中和原子广播协议中。默认情况下,一个服务器的权重值为1,如果定义的一组服务器没有指定权重,所有服务器的权重值将默认分配为1。
  • traceFile
    持续跟踪ZooKeeper的操作,并将操作记录到跟踪日志中,跟踪日志的文件名为traceFile.year.month.day。除非设置了该选项(requestTraceFile),否则跟踪功能将不会启用。
    该选项用来提供ZooKeeper所进行的操作的详细视图。不过,要想记录这些日志,ZooKeeper服务器必须序列化操作,并将操作写入磁盘,这将争用CPU和磁盘的时间。如果你使用了该选项,请确保不要将跟踪文件放到日志文件的存储设备中。还需要知道,跟踪选项还可能影响系统运行,甚至可能会很难重现跟踪选项关闭时发生的问题。另外还有个有趣的问题,traceFile选项的Java系统属性配置中不含有zookeeper前缀,而且系统属性的名称也与配置选项名称不同,这一点请小心。

3、网络配置

这些配置参数可以限制服务器和客户端之间的通信,超时选项也在该节进行讨论:
  • globalOutstandingLimit
    ZooKeeper中待处理请求的最大值(zookeeper.globalOutstandingLimit)。
    ZooKeeper客户端提交请求比ZooKeeper服务端处理请求要快很多,服务端将会对接收到的请求队列化,最终(也许几秒之内)可能导致服务端的内存溢出。为了防止发生这个问题,ZooKeeper服务端中如果待处理请求达到globalOutstandingLimit值就会限制客户端的请求。但globalOutstandingLimit值并不是硬限制,因为每个客户端至少有一个待处理请求,否则会导致客户端超时,因此,当达到globalOutstandingLimit值后,服务端还会继续接收客户端连接中的请求,条件是这个客户端在服务器中没有任何待处理的请求。
    为了确定某个服务器的全局限制值,我们只是简单地将该参数值除以服务器的数量,目前还没有更智能的方式去实现全局待处理操作数量的计算,并强制采用该参数所指定的限制值,因此,该限制值为待处理请求的上限值,事实上,服务器之间完美的负载均衡解决方案还无法实现,所以某些服务器运行得稍缓慢一点,或者处于更高的负载中,即使最终没有达到全局限制值也可能被限制住吞吐量。
    该参数的默认值为1000个请求,你可能并不会修改该参数值,但如果你有很多客户端发送大数据包请求可能就需要降低这个参数值,但我们在实践中还未遇到需要修改这个参数的情况。
  • maxClientCnxns
    允许每个IP地址的并发socket连接的最大数量。Zookeeper通过流量控制和限制值来避免过载情况的发生。一个连接的建立所使用的资源远远高于正常操作请求所使用的资源。我们曾看到过某些错误的客户端每秒创建很多ZooKeeper连接,最后导致拒绝服务(DoS),为了解决这个问题,我们添加了这个选项,通过设置该值,可以在某个IP地址已经有maxClientCnxns个连接时拒绝该IP地址新的连接。该选项的默认值为60个并发连接。
    注意,每个服务器维护着这个连接的数量,如果有一个5个服务器的集群,并且使用默认的并发连接数60,一个欺诈性的客户端会随机连接到这5个不同的服务器,正常情况下,该客户端几乎可以从单个IP地址上建立300个连接,之后才会触发某个服务器的限制。
  • clientPortAddress
    限制客户端连接到指定的接收信息的地址上。默认情况下,一个ZooKeeper服务器会监听在所有的网络接口地址上等待客户端的连接。
    有些服务器配置了多个网络接口,其中一个网络接口用于内网通信,另一个网络接口用于公网通信,如果你并不希望服务器在公网接口接受客户端的连接,只需要设置clientPortAddress选项为内网接口的地址。
  • minSessionTimeout
    最小会话超时时间,单位为毫秒。当客户端建立一个连接后就会请求一个明确的超时值,而客户端实际获得的超时值不会低于minSessionTimeout的值。
    minSessionTimeout的默认值为tickTime值的两倍。配置该参数值过低可能会导致错误的客户端故障检测,配置该参数值过高会延迟客户端故障的检测时间。
  • maxSessionTimeout
    会话的最大超时时间值,单位为毫秒。当客户端建立一个连接后就会请求一个明确的超时值,而客户端实际获得的超时值不会高于maxSessionTimeout的值。
    虽然该参数并不会影响系统的性能,但却可以限制一个客户端消耗系统资源的时间,默认情况下maxSessionTimeout的时间为tickTime的20倍。

4、集群配置

当以一个集群来构建ZooKeeper服务时,需要为每台服务器配置正确的时间和服务器列表信息,以便服务器之间可以互相建立连接并进行故障监测,在ZooKeeper的集群中,这些参数的配置必须一致:
  • initLimit
    对于追随者最初连接到群首时的超时值,单位为tick值的倍数。
    当某个追随者最初与群首建立连接时,它们之间会传输相当多的数据,尤其是追随者落后整体很多时。配置initLimit参数值取决于群首与追随者之间的网络传输速度情况,以及传输的数据量大小,如果ZooKeeper中保存的数据量特别大(即存在大量的znode节点或大数据集)或者网络非常缓慢,就需要增大initLimit值,因为该值取决于环境问题,所有没有默认值。需要为该参数配置适当的值,以便可以传输所期望的最大快照,也许有时你需要多次传输,你可以配置initLimit值为两倍你所期望的值。如果配置initLimit值过高,那么首次连接到故障的服务器就会消耗更多的时间,同时还会消耗更多的恢复时间,因此最好在你的网络中进行追随者与群首之间的网络基准测试,以你规划所使用的数据量来测试出你所期望的时间。
  • syncLimit
    对于追随者与群首进行sync操作时的超时值,单位为tick值的倍数。
    追随者总是会稍稍落后于群首,但是如果因为服务器负载或网络问题,就会导致追随者落后群首太多,甚至需要放弃该追随者,如果群首与追随者无法进行sync操作,而且超过了syncLimit的tick时间,就会放弃该追随者。与initLimit参数类似,syncLimit也没有默认值,与initLimit不同的是,syncLimit并不依赖于ZooKeeper中保存的数据量大小,而是依赖于网络的延迟和吞吐量。在高延迟网络环境中,发送数据和接收响应包会耗费更多时间,此时就需要调高syncLimit值。即使在相对低延迟的网络中,如果某些相对较大的事务传输给追随者需要一定的时间,你也需要提高syncLimit值。
  • leaderServes
    配置值为“yes”或“no”标志,指示群首服务器是否为客户端提供服务(zookeeper.leaderServes)。
    担任群首的ZooKeeper服务器需要做很多工作,它需要与所有追随者进行通信并会执行所有的变更操作,也就意味着群首的负载会比追随者的负载高,如果群首过载,整个系统可能都会受到影响。
    该标志位如果设置为“no”就可以使群首除去服务客户端连接的负担,使群首将所有资源用于处理追随者发送给它的变更操作请求,这样可以提高系统状态变更操作的吞吐能力。换句话说,如果群首不处理任何与其直连的客户端连接,追随者就会有更多的客户端,因为连接到群首的客户端将会分散到追随者上,尤其注意在集群中服务器数量比较少的时候。默认情况下,leaderServes的值为“yes”。
  • server.x=[hostname]:n:n[:observer]
    服务器x的配置参数。
    ZooKeeper服务器需要知道它们如何通信,配置文件中该形式的配置项就指定了服务器x的配置信息,其中x为服务器的ID值(一个整数)。当一个服务器启动后,就会读取data目录下myid文件中的值,之后服务器就会使用这个值作为查找server.x项,通过该项中的数据配置服务器自己。如果需要连接到另一个服务器y,就会使用server.y项的配置信息来与这个服务器进行通信。
    其中hostname为服务器在网络n中的名称,同时后面跟了两个TCP的端口号,第一个端口用于事务的发送,第二个端口用于群首选举,典型的端口号配置为2888:3888。如果最后一个字段标记了observer属性,服务器就会进入观察者模式。
    注意,所有的服务器使用相同的server.x配置信息,这一点非常重要,否则的话,因服务器之间可能无法正确建立连接而导致整个集群无法正常工作。
  • cnxTimeout
    在群首选举打开一个新的连接的超时值(zookeeper.cnxTimeout)。
    ZooKeeper服务器在进行群首选举时互相之间会建立连接,该选项值确定了一个服务器在进行重试前会等待连接成功建立的时间为多久,9.2节介绍了该超时的用途。默认的超时时间为5秒,该值足够大,也许你并不需要修改。
  • electionAlg
    选举算法的配置选项。
    为了整个配置的完整性,我们也列入了该选项。该选项用于选择不同的群首选举算法,但除了默认的配置外,其他算法都已经弃用了,所以并不需要配置这个选项。

5、认证和授权选项

该节中包括认证和授权相关的选型配置。对于Kerberos相关的配置选项信息,请参考6.1.2节:
zookeeper.DigestAuthenticationProvider.superDigest(只适用于Java系统属性)该系统属性指定了“super”用户的密码摘要信息(该功能默认不启用),以super用户认证的客户端会跳过所有ACL检查。该系统属性的值形式为super:encoded_digest。为了生成加密的摘要,可以使用org.apache.zookeeper.server.auth.DigestAuthenticationProvider工具,使用方式如下:
java -cp $ZK_CLASSPATH \ org.apache.zookeeper.server.auth.DigestAuthenticationProvider super:asdf

通过命令行工具生成一个 asdf 密码的加密摘要信息:
super:asdf->super:T+4Qoey4ZZ8Fnni1Yl2GZtbH2W4=

为了在服务器启动中使用该摘要,可以通过以下命令实现:
export SERVER_JVMFLAGS
SERVER_JVMFLAGS=-Dzookeeper.DigestAuthenticationProvicder.superDigest=
    suer:T+4Qoey4ZZ8Fnni1Yl2GZtbH2W4=
./bin/zkServer.sh start

现在通过 zkCli 进行连接:
[zk: localhost:2181(CONNECTED) 0] addauth digest super:asdf
[zk: localhost:2181(CONNECTED) 1]
此时,已经以super用户的身份被认证,现在不会被任何ACL所限制。

【注意:不安全连接】

ZooKeeper客户端与服务器之间的连接并未加密,因此不要在不可信的链接中使用super的密码,使用super密码的安全方式是在ZooKeeper服务器本机上使用super密码运行客户端。

6、非安全配置

  • forceSync
    通过“yes”或“no”选项可以控制是否将数据信息同步到存储设备上(zookeeper.forceSync)。
    默认情况下,forceSync配置yes时,事务只有在同步到存储设备后才会被应答,同步系统调用的消耗很大,而且也是事务处理中最大的延迟原因之一。如果forceSync配置为no,事务会在写入到操作系统后就立刻被应答,在将事务写入磁盘之前,这些事务常常缓存于内存之中,配置forceSync为no可以提高性能,但代价是服务器崩溃或停电故障时可恢复性。
  • jute.maxbuffer(仅适用于Java系统属性)
    一个请求或响应的最大值,以字节为单位。该选项只能通过Java的系统属性进行配置,并且选项名称没有zookeeper前缀。
    ZooKeeper中内置了一些健康检查,其中之一就是对可传输的znode节点数据的大小的检查,ZooKeeper被设计用于保存配置数据,配置数据一般由少量的元数据信息(大约几百字节)所组成。默认情况下,一个请求或响应消息如果大于1M字节,就会被系统拒绝,可以使用该属性来修改健康检查值,调小检查值,或者真的确认要调大检查值。

【注意:修改健康检查值】
虽然通过jute.maxbuffer指定的限制值可以进行大块数据的写入操作,但获取一个znode节点的子节点,而同时该节点有很多子节点时就会出现问题。如果一个znode节点含有几十万个子节点,每个子节点的名字长度平均为10个字符,在试着返回子节点列表时就会命中默认最大缓冲大小检查,此时就会导致连接被重置。

  • skipACL
    跳过所有ACL检查(zookeeper.skipACL)。
    处理ACL检查会有一定的开销,通过该选项可以关闭ACL检查功能,这样做可以提高性能,但也会将数据完全暴露给任何一个可以连接到服务器的客户端。
  • readonlymode.enabled(仅适用于Java系统属性)
    将该配置设置为true可以启用服务器只读模式功能,客户端可以以只读模式的请求连接服务器并读取信息(可能是已过期的信息),即使该服务器在仲裁中因分区问题而被分隔。为了启用只读模式,客户端需要配置canBeReadOnly为true。
    该功能可以使客户端即使在网络分区发生时也能读取(不能写入)ZooKeeper的状态,在这种情况下,被分区而分离的客户端依然可以继续取得进展,并不需要等待分区问题被修复。特别注意,一个与集群中其他服务器失去连接ZooKeeper也许会终止以只读模式提供过期的数据服务。

7、日志

ZooKeeper采用SLF4J库(JAVA简易日志门面)作为日志的抽象层,默认使用Log4J进行实际的日志记录功能。
Log4J的配置文件为log4j.properties,系统会从classpath中加载这个文件,对于Log4J比较失望的是,如果对应路径不存在log4j.properties文件,我们会看到以下输出信息:
log4j:WARN No appenders could be found for logger (org.apache.zookeeper.serv ...
log4j:WARN Please initialize the log4j system properly.

一般,log4j.properties会保存到classpath中的conf目录下,在ZooKeeper中的log4j.properties文件的主要部分:
zookeeper.root.logger=INFO, CONSOLE ①
zookeeper.console.threshold=INFO
zookeeper.log.dir=.
zookeeper.log.file=zookeeper.log
zookeeper.log.threshold=DEBUG
zookeeper.tracelog.dir=.
zookeeper.tracelog.file=zookeeper_trace.log
log4j.rootLogger=${zookeeper.root.logger} ②
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender ③
log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} ④
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout ⑤
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] -
...
log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender ⑥
log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} ⑦
log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file}
log4j.appender.ROLLINGFILE.MaxFileSize=10MB
log4j.appender.ROLLINGFILE.MaxBackupIndex=10
log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] -
①第一组配置中,所有配置项均以zookeeper.开头,配置了该文件的默认值,这些配置项实际上是系统属性配置,可以通过java命令行指定-D参数来覆盖JVM的配置。第一行的日志配置中,默认配置了日志消息的级别为INFO,即所有低于INFO级别的日志消息都会被丢弃,使用的appender为CONSOLE。你可以指定多个appender,例如,你如果想将日志信息同时输出到CONSOLE和ROLLINGFILE时,可以配置zookeeper.root.logger项为INFO,CONSOLE,ROLLINGFILE。
②rootLogger指定了处理所有日志消息的日志处理器,因为我们并不需要其他日志处理器。
③该行配置以CONSOLE名称定义了一个类,该类会处理消息的输出。在这里使用的是ConsoleAppender类。
④在appender的定义中也可以过滤消息,该行配置了这个appender会忽略所有低于INFO级别的消息,因为zookeeper.root.logger中定义了全局阀值为INFO。
⑤appender使用的布局类对输出日志在输出前进行格式化操作。我们通过布局模式定义了输出日志消息外还输出日志级别、时间、线程信息和调用位置等信息。
⑥RollingFileAppender实现了滚动日志文件的输出,而不是不断地输出到一个单个日志文件或控制台。除非ROLLINGFILE被rootLogger引用,否则该appender会被忽略。
⑦定义ROLLINGFILE的输出级别为DEBUG,因为rootLogger过滤了所有低于INFO级别的日志,所以,你如果你想看DEBUG消息,就必须将zookeeper.root.logger项的配置从INFO修改为DEBUG。

8、专用资源

当考虑在服务器上运行ZooKeeper如何配置时,服务器本身的配置也很重要。为了达到所期望的性能,可以考虑使用专用的日志存储设备,就是说日志目录处于专属的硬盘上,没有其他进程使用该硬盘资源,甚至周期性的模糊快照也不会使用该硬盘。

二、配置 ZooKeeper 集群

仲裁(quorum)的概念:该概念深深贯穿于ZooKeeper的设计之中。在复制模式下处理请求时以及选举群首时都与仲裁的概念有关,如果ZooKeeper集群中存在法定人数的服务器已经启动,整个集群就可以继续工作。
观察者(observer):与集群一同工作,接收客户端请求并处理服务器上的状态变更,但是群首并不会等待观察者处理请求的响应包,同时集群在进行群首选举时也不会考虑观察者的通知消息。

1、多数原则

当集群中拥有足够的 ZooKeeper 服务器来处理请求时,称这组服务器的集合为仲裁法定人数。
当配置多个服务器来组成ZooKeeper集群时,我们默认使用多数原则作为仲裁法定人数。ZooKeeper会自动监测是否运行于复制模式,从配置文件读取时确定是否拥有多个服务器的配置信息,并默认使用多数原则的仲裁法定人数。

2、法定人数的可配置性

关于法定人数的一个重要属性:
如果一个法定人数解散了,集群中另一个法定人数形成,这两个法定人数中至少有一个服务器必须交集。
ZooKeeper也允许灵活的法定人数配置,这种特殊方案就是对服务器进行分组配置时,会将服务器分组成不相交的集合并分配服务器的权重,通过这种方案来组成法定人数,需要使多数组中的服务器形成多数投票原则。
例如,有三个组,每个组中有三个服务器,每个服务器的权重值为1,在这种情况下,需要四个服务器来组成法定人数:某个组中的两个服务器,另一组中的两台服务器。
总之,其数学逻辑归结为:如果有G个组,所需要的服务器为一个G组的服务器,满足|G|>|G|/2,同时对于G组中的服务器集合g,还需要集合g中的集合g满足集合g的所有权重值之和W'不小于集合g的权重值之和(如:W>W/2)。
通过以下配置选项可以创建一个组:
group.x=n[:n]
启用法定人数的分层构建方式。x为组的标识符,等号后面的数字对应服务器的标识符,赋值操作符右侧为冒号分隔的服务器标识符的列表。注意,组与组之间不能存在交集,所有组的并集组成了整个ZooKeeper集群,换句话说,集群中的每个服务器必须在某个组中被列出一次。

【下面的示例说明了9个服务器被分为3组的情况:】

group.1=1:2:3
group.2=4:5:6
group.3=7:8:9
这个例子中,每个服务器的权重都一样,为了构成法定人数,需要两个组及这两个组中各取两个服务器,也就是总共4个服务器。但根据法定人数多数原则,至少需要5个服务器来构成一个法定人数。注意,不能从任何子集形成法定人数的4个服务器,不过,一个组全部服务器加上另一个组的一个单独的服务器并不能构成法定人数。
当在跨多个数据中心部署ZooKeeper服务时,这种配置方式有很多优点。例如,一个组可能表示运行于不同数据中心的一组服务器,即使这个数据中心崩溃,ZooKeeper服务也可以继续提供服务。
在跨三个数据中心部署的这种方式可以容忍某个数据中心的故障问题,可以在两个数据中心中每一个部署三个服务器,而只在第三个数据中心部署一个服务器,通过这种方式来使用多数原则,这样,如果某个数据中心不可用,其他两个数据中心还能组成法定人数。这种配置方式的优点是这七个服务器中的任何四个都构成一个法定人数,而缺点是一旦某个数据中心不可用,其他数据中心中任何服务器的崩溃都无法容忍。
如果只有两个数据中心可用,可以使用【权重值】来表示优先权,例如,基于每个数据中心中的客户端数量来配置权重值。只有两个数据中心时,如果每个服务器的权重值都一样,就无法容忍任何一个数据中心的失效,但是如果对某个服务器分配了更高的权重值,就可以容忍这两个数据中心中某个数据中心的失效。假设,在每个数据中心中分配三个服务器,并且将这些服务器均放到同一组中:
group.1=1:2:3:4:5:6
因为所有的服务器默认情况下权重值都一样,因此只要6个服务器中有4个服务器有效时就可以构成法定人数的服务器。当然,这也意味着如果某个数据中心失效,就不能形成法定人数,即使另一个数据中心的三个服务器均有效。

【为了给服务器分配不同的权重值,可以通过以下选型进行配置:】

weight.x=n
与group选项一起配合使用,通过该选项可以为某个服务器形成法定人数时分配一个权重值为n。其值n为服务器投票时的权重,ZooKeeper中群首选举和原子广播协议中均需要投票。默认情况下,服务器的权重值为1,如果配置文件中定义了组的选项,但为指定权重值,所有的服务器均会被分配权重值1。

假设,某个数据中心只要其所有服务器均可用,即使在其他数据中心失效时,这个数据中心也可以提供服务,我们暂且称该数据中心为D1,此时,可以为D1中的某个服务器分配更高权重值,以便可以更容易与其他服务器组成法定人数。

【假设D1中有服务器1、2和3,通过以下方式为服务器1分配更高的权重值:】

weight.1=2
通过以上配置,就有了7个投票,在构成法定人数时,只需要4个投票。如果没有weight.1=2参数,任何服务器都需要与其他三个服务器来构成法定人数,但有了这个参数配置,服务器1与两个服务器就可以构成法定人数。因此,只要D1可用,即使其他数据中心发生故障,服务器1、2和3也能构成法定人数并继续提供服务。

通过以上不同的法定人数配置的若干示例,看到该配置对部署的影响。提供的分层方案非常灵活,通过不同的权重值和组的管理可以提供不同的分层配置。

3、观察者

观察者(observer)为ZooKeeper服务器中不参与投票但保障状态更新顺序的特殊服务器。

【配置ZooKeeper集群使用观察者,需要在观察者服务器的配置文件中添加以下行】:

peerType=observer

【同时,还需要在所有服务器的配置文件中添加该服务器的:observer定义。如下】:

server.1:localhost:2181:3181:observer

三、重配置

图10-1的场景,三个服务器(A、B、C)组成了整个集群,服务器C因某些网络拥塞问题稍稍落后于整个集群,因此服务器C刚刚了解事务到<1,3>(其中1为时间戳,3为对应该时间戳的事务标识,但因为服务器A和B的通信良好,所以服务器C稍稍落后并不会导致整个系统变慢,服务器A和B可以提交事务到<1,6>)。

【图10-1:含有3个服务器的集群将要扩展到5个】
clipboard.png

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

【图10-2:5个服务器的集群的法定人数为3】
clipboard.png

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

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

【重配置】不仅可以改变集群成员配置,还可以修改网络参数配
置,因为ZooKeeper中配置信息的变化,需要将重配置参数与静态的配置文件分离,单独保存为一个配置文件并自动更新该文件。dynamicConfigFile参数和链接这两个配置文件。

【使用动态配置之前,回顾一下之前的配置文件:】

tickTime=2000
initLimit=10
syncLimit=5
dataDir=./data
dataLogDir=./txnlog
clientPort=2182
server.1=127.0.0.1:2222:2223
server.2=127.0.0.1:3333:3334
server.3=127.0.0.1:4444:4445

【现在,将配置文件修改为动态配置方式:】

tickTime=2000
initLimit=10
syncLimit=5
dataDir=./data
dataLogDir=./txnlog
dynamicConfigFile=./dyn.cfg

【注意】,甚至从配置文件中删除了clientPort参数配置,在dyn.cfg文件由服务器项的配置组成,同时还多了一些配置,服务器项的配置形式如下:

server.id=host:n:n[:role];[client_address:]client_port

与正常的配置文件一样,列出了每个服务器的主机名和端口号用于法定人数和群首选举消息。

  • role选项:必须为participant或observer,如果忽略role选项,默认为participant
  • client_port:还指定了client_port(用于客户端连接的服务器端口号),以及该服务器需要绑定的特定网络接口地址,因为从静态配置文件中删除了clientPort参数,所以在这里添加该配置。

【最终的dyn.cfg配置文件如下所示】:

server.1=127.0.0.1:2222:2223:participant;2181
server.2=127.0.0.1:3333:3334:participant;2182
server.3=127.0.0.1:4444:4445:participant;2183

使用重配置之前必须先创建这些文件,一旦这些文件就绪,就可以通过reconfig操作来重新配置一个集群,该操作可以增量或全量(整体)地进行更新操作。

增量的重配置操作将会形成两个列表:

  • 待删除的服务器列表
    待删除的服务器列表仅仅是一个逗号分隔服务器ID列表
  • 待添加的服务器项的列表
    待添加的服务器项列表为逗号分隔的服务器项列表,每个服务器项的形式为动态配置文件中所定义的形式。例如:
reconfig -remove 2,3 -add \
server.4=127.0.0.1:5555:5556:participant;2184,\
server.5=127.0.0.1:6666:6667:participant;2185

该命令将会删除服务器2和3,添加服务器4和5。该操作成功执行还需要满足某些条件:

  • 首先,与其他ZooKeeper操作一样,原配置中法定人数必须处于活动状态;
  • 其次,新的配置文件中构成的法定人数也必须处于活动状态。

【注意:通过重配置从一个服务器到多个服务器】

当只有一个单独的ZooKeeper服务器,该服务器以独立模式运行,这种情况稍微复杂一些,因为重配置不仅改变了法定人数组成的元素,同时还会切换原来的服务器模式从独立模式到仲裁模式,所以,不允许以独立模式运行重配置操作,只有在仲裁模式时才可以使用重配置功能。
ZooKeeper一次允许一个配置的变更操作请求,当然,配置操作会非常快地被处理,而且重新配置也很少发生,所以并发的重配置操作应该不是什么问题。

还可以使用-file参数来指定一个新的成员配置文件来进行一次全量更新。例如:reconfig-file newconf命令会产生如上面命令一样的增量操作结果,newconf文件为:

server.1=127.0.0.1:2222:2223:participant;2181
server.4=127.0.0.1:5555:5556:participant;2184
server.5=127.0.0.1:6666:6667:participant;2185
通过-members参数,后跟服务器项的列表信息,可以代替-file参数进行全量更新配置操作。
最后【-v】,所有形式的reconfig的为重新配置提供了条件,如果通过-v参数提供了配置版本号,reconfig命令会在执行前确认配置文件当前的版本号是否匹配,只有匹配才会成功执行。可以通过读取zookeeper/config节点来获取当前配置的版本号,或通过zkCli工具来调用config获取配置版本号信息。

客户端连接串的管理

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

【图10-4:集群从三个到五个服务器时,客户端的重配置】
clipboard.png

另一种方式可以使ZooKeeper的服务器数量更具弹性,而不需要改变客户端的配置。对主机名很自然地想到可以解析为一个IP地址,但实际上,一个主机名可以解析为多个地址,如果主机名解析为多个IP地址,ZooKeeper就可以连接到其中的任何地址,在图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所示。

【图10-5:集群从三个到五个服务器时,使用DNS对客户端的重配置】
clipboard.png

在使用主机名解析为多个地址方式时,还有一些注意事项:

  • 首先,所有的服务器必须使用相同的客户端端口号;
  • 其次,主机名解析只有在创建连接时才会发生,所以已经连接的客户端无法知道最新的名称解析,只能对新创建的ZooKeeper客户端生效。
【路径信息】:
客户端的连接还可以包含路径信息,该路径指示了解析路径名称时的根路径,其行为与UNIX系统中的chroot命令相似,而且在ZooKeeper社区中也会经常听到人们以“chroot”来称呼这个功能。
例如,如果客户端的连接串为zk:2222/app/superApp,当客户端连接并执行getData("/a.dat",...)操作时,实际客户端会得到/app/superApp/a.dat节点的数据信息(注意,连接串中指示的路径必须存在,而不会创建连接串中所指示的路径)。

在连接串中添加路径信息的动机在于一个ZooKeeper集群为多个应用程序提供服务,这样不需要要求每个应用程序添加其路径的前缀信息。每个应用程序可以类似名称独享似的使用ZooKeeper集群,运维人员可以按他们的期望来划分命名空间。
图10-6的示例展示了不同的连接串可以为客户端应用程序提供不同的根入口点。

【图10-6:通过连接串指定ZooKeeper客户端的根节点】
clipboard.png

【注意:连接串的重叠】
当管理客户端连接串时,注意一个客户端的连接串永远不要包含两个不同的ZooKeeper集群的主机名,这是最快速也是最简单导致脑裂问题的方式。

四、配额管理

ZooKeeper的另一个可配置项为配额,ZooKeeper初步提供了znode节点数量和节点数据大小的配额管理的支持。可以通过配置来指定某个子树的配额,该子树就会被跟踪,如果该子树超过了配额限制,就会记录一条警告日志,但操作请求还是可以继续执行。此时,ZooKeeper会检测是否超过了某个配额限制,但不会阻止处理流程。

配额管理的跟踪功能通过/zookeeper子树完成,所以应用程序不能在这个子树中存储自己的数据,这个子树只应该保留给ZooKeeper使用,而 /zookeeper/quota 节点就是ZooKeeper管理配额的节点。为了对应用程序/application/superApp创建一个配额项,需要在application/superApp节点下创建两个子节点zookeeper_limits和zookeeper_stats。

对于znode节点数量的限制称之为count,而对于节点数据大小的限制则为bytes。在zookeeper_limits和zookeeper_stats节点中通过count=n,bytes=m来指定配额,其中n和m均为整数,在zookeeper_limits节点中,n和m表示将会触发警告的级别(如果配置为-1就不会触发警告信息),在zookeeper_stats借点中,n和m分别表示当前子树中的节点数量和子树节点的数据信息的当前大小。

【注意:对元数据的配额跟踪】

对于子树节点数据的字节数配额跟踪功能,并不会包含每个znode节点的元数据的开销,元数据的大小大约100字节,所以如果每个节点的数据大小都比较小,跟踪znode节点的数量比跟踪znode数据的大小更加实用。

可以使用zkCli来创建/application/superApp节点,并配置配额限制:

[zk: localhost:2181(CONNECTED) 2] create /application ""
Created /application
[zk: localhost:2181(CONNECTED) 3] create /application/superApp super
Created /application/superApp
[zk: localhost:2181(CONNECTED) 4] setquota -b 10 /application/superApp
Comment: the parts are option -b val 10 path /application/superApp
[zk: localhost:2181(CONNECTED) 5] listquota /application/superApp
absolute path is /zookeeper/quota/application/superApp/zookeeper_limits
Output quota for /application/superApp count=-1,bytes=10
Output stat for /application/superApp count=1,bytes=5
创建了/application/superApp节点,且该节点的数据为5个字节(一个单词“super”),之后为/application/superApp节点设置了配额限制为10个字节,当列出/application/superApp节点配置限制是,发现数据大小的配额还有5个字节的余量,而并未对这个子树设置znode节点数量的配额限制,因为配额中count的值为-1。

如果发送命令get/zookeeper/quota/application/superApp/zookeeper_stats,可以直接访问该节点数据,而不需要使用zkCli工具,事实上,可以通过创建或删除这些节点来创建或删除配额配置。如果运行以下命令:

create /application/superApp/lotsOfData ThisIsALotOfData
就会在日志中看到如下信息:
Quota exceeded: /application/superApp bytes=21 limit=10

五、多租赁配置

配额,提供了配置选项中的某些限制措施,而ACL策略更值得我们考虑如何使用ZooKeeper来服务于多租赁(multitenancy)情况。满足多租赁的一些令人信服的原因如下:

  • 为了提供可靠的服务器,ZooKeeper服务器需要运行于专用的硬件设备之上,跨多个应用程序共享这些硬件设备更容易符合资本投资的期望。
  • 发现,在大多数情况下,ZooKeeper的流量非常具有突发性:配置或状态的变化的突发操作会导致大量的负载,从而导致服务长时间的不可用。如果是没有什么关联的应用程序的突发操作,将这些应用程序共享这个服务器更能有效利用硬件资源。不过还是要注意失联事件发生时所产生的峰值,某些写得不太规范的应用程序在处理Disconnected事件时,产生的负载高于其所需要的资源。
  • 对于硬件资源的分摊,可以获得更好的故障容错性:如果两个应用程序,从之前各自三个服务器的集群中转移到一个由5台服务器组成的集群,总量上所使用的服务器更少了,对ZooKeeper也可以容忍两台服务器的故障,而不是之前的只能容忍一个服务器故障。

当服务于多租赁的情况下时,运维人员一般会将数据树分割为不同的子树,每个子树为某个应用程序所专用。开发人员在设计应用程序时可以考虑在其所用的znode节点前添加前缀,但还有一个更简单的方法来隔离各个应用程序:在连接串中指定路径部分,10.3节中介绍了这方式。每个应用程序的开发人员在进行应用程序的开发时,就像使用专用的ZooKeeper服务一样。如果运维人员决定将应用程序部署到根路径/application/newapp之下,应用程序可以使用host:port/application/newapp连接串,而不仅仅是host:port,通过这种方式,对应用程序所呈现的犹如使用专用服务一样,与此同时,运维人员还可以为/application/newapp节点配置配额限制,以便跟踪应用程序的空间使用情况。

六、文件系统布局和格式

数据存储有两类:事务日志文件和快照文件。
这些文件均以普通文件的形式保存到本地文件系统中,在进行关键路径的事务处理时就会写入事务日志文件,所以强烈建议将这些文件保存到一个专用存储设备上

快照文件将会被写入到DataDir参数所指定的目录中,而事务日志文件将会被写入到DataLogDir参数所指定的目录中。

首先,看一下事务日志目录中的文件,如果列出该目录的信息,会发现只有一个子目录,名为version-2,对日志和快照的格式只做出了一次重大改进,当改变其格式后,发现,将数据通过文件版本进行分离,对于处理版本间的数据迁移是非常有用的。

1、事务日志

在运行一些小测试后的目录内容,有两个事务日志文件:

-rw-r--r-- 1 breed 67108880 Jun 5 22:12 log.100000001
-rw-r--r-- 1 breed 67108880 Jul 15 21:37 log.200000001
可以仔细观察这些文件信息。首先,考虑到测试很少,而这些文件却非常大(每个都超过6MB);其次这些文件名的后缀中均有一个很大数字。

ZooKeeper为文件预分配大的数据块,来避免每次写入所带来的文件增长的元数据管理开销,如果通过对这些文件进行十六进制转储打印,会发现这些文件中全部以null字符(0)填充,只有在最开始部分有少量的二进制数据,服务器运行一段时间后,其中的null字符逐渐被日志数据替换。

日志文件中包含事务标签zxid,但为了减轻恢复负载,而且为了快速查找,每个日志文件的后缀为该日志文件中第一个zxid的十六进制形式。通过十六进制表示zxid的一个好处就是可以快速区分zxid中时间戳部分和计数器部分,所以在之前例子中的第一个文件的时间戳为1,而第二个文件的时间戳为2。

不过,还想继续看一看文件中保存了什么内容,对于问题诊断也非常有帮助。有时,开发人员宣称ZooKeeper丢失了某些znode节点信息,此时只有通过查找事务日志文件才可以知道客户端具体删除过哪些节点。

可以通过一下命令来查看第二个日志文件:
java -cp $ZK_LIBS org.apache.zookeeper.server.LogFormatter version-2 log.200000001
这个命令的输出信息如下:
7/15/13... session 0x13...00 cxid 0x0 zxid 0x200000001 createSession 30000
7/15/13... session 0x13...00 cxid 0x2 zxid 0x200000002 create
'/test,#22746573746 ...
7/15/13... session 0x13...00 cxid 0x3 zxid 0x200000003 create
'/test/c1,#6368696c ...
7/15/13... session 0x13...00 cxid 0x4 zxid 0x200000004 create
'/test/c2,#6368696c ...
7/15/13... session 0x13...00 cxid 0x5 zxid 0x200000005 create
'/test/c3,#6368696c ...
7/15/13... session 0x13...00 cxid 0x0 zxid 0x200000006 closeSession null
每个日志文件中的事务均以可读形式一行行地展示出来。因为只有变更操作才会被记录到事务日志,所以在事务日志中不会看到任何读事务操作。

2、快照

快照文件的命名规则与事务日志文件的命名规则相似,以下为之前例子中的服务器的快照列表信息:
-rw-r--r-- 1 br33d 296 Jun 5 07:49 snapshot.0
-rw-r--r-- 1 br33d 415 Jul 15 21:33 snapshot.100000009
快照文件并不会被预分配空间,所以文件大小也更加准确地反映了其中包含的数据大小。其中后缀表示快照开始时当时的zxid值,之前已经介绍过,快照文件实际上为一个模糊快照,直到事务日志重现之后才会成为一个有效的快照文件。因此在恢复系统时,必须从快照后缀的zxid开始重现事务日志文件,甚至更早的zxid开始重现事务。

快照文件中保存的模糊快照信息同样为二进制格式,因此,可以通过另一个工具类来检查快照文件的内容:

java -cp ZK_LIBS org.apache.zookeeper.server.SnapshotFormatter version-2 /snapshot.100000009
这个命令的输出信息如下:
----
/
cZxid = 0x00000000000000
ctime = Wed Dec 31 16:00:00 PST 1969
mZxid = 0x00000000000000
mtime = Wed Dec 31 16:00:00 PST 1969
pZxid = 0x00000100000002
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 0
----
/sasd
cZxid = 0x00000100000002
ctime = Wed Jun 05 07:50:56 PDT 2013
mZxid = 0x00000100000002
mtime = Wed Jun 05 07:50:56 PDT 2013
pZxid = 0x00000100000002
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x00000000000000
dataLength = 3
----
....
只有每个节点的元数据被转储打印出来,这样,运维人员就可以知道一个znode节点何时发生了变化,以及哪个znode节点占用了大量内存。很遗憾,数据信息和ACL策略并没有出现在输出中,因此,在进行问题诊断时,记住将快照中的信息与日志文件的信息结合起来分析问题所在。

3、时间戳文件

ZooKeeper的持久状态由两个小文件构成,它们是两个时间戳文件,其文件名为acceptedEpoch和currentEpoch,之前已经讨论过时间戳的概念,而这两个文件则反映了某个服务器进程已接受的和正在处理的信息。虽然这两个文件并不包含任何应用数据信息,但对于数据一致性却至关重要,所以如果你在备份一个ZooKeeper服务器的原始数据文件时,不要忘了这两个文件。

4、已保存的ZooKeeper数据的应用

ZooKeeper数据存储的一个优点是,不管独立模式的服务器还是集群方式的服务器,数据的存储方式都一样。之前已经介绍过通过事务日志和快照的合并可以得到准确的数据视图,可以将事务日志文件和快照文件拷贝到另一台设备上进行这些操作(例如在你的便携电脑中),将这些文件放到一个独立模式的服务器下空白的data目录下,然后启动服务,该服务就会真实反映出你所拷贝的那个服务器上的状态信息。这项技术可以从生产环境拷贝服务器的状态信息,用于稍后的复查等用途。

同时也意味着,只需要简单地将这些数据文件进行备份,就可以轻易地完成ZooKeeper服务器的备份,如果采用这种方式进行备份,还需要注意一些问题。首先,ZooKeeper为复制服务,所以系统中存在冗余信息,如果进行备份操作,只需要备份其中一台服务器的数据信息。

当ZooKeeper服务器认可了一个事务,从这时起它就会承诺记录下该状态信息,一定要记住这一点,这一点非常重要。因此如果使用旧的备份文件恢复一个服务器,就会导致服务器违反其承诺。如果刚刚遭遇了所有服务器的数据丢失的情况,这可能不是什么大问题,但如果你的集群在正常工作中,而你将某个服务器还原为旧的状态,你的行为可能会导致其他服务器也丢失了某些信息。

如果要对全部或大多数服务器进行数据丢失的恢复操作,最好的办法是使用最新抓取的状态信息(从最新的存活服务器中获取的备份文件),并在启动服务器之前将状态信息拷贝到其他所有服务器上。

七、四字母命令

四字母命令提供的这一简单方法,可以让对系统进行各种各样的检查。四字母命令的主要目标就是提供一个非常简单的协议,使使用简单的工具,如telnet或nc,就可以完成系统健康状况检查和问题的诊断。为简单起见,四字母命令的输出也是可读形式,使得更容易使用这些命令。

对服务器添加一个新的命令也很容易,命令列表也就会增长。本节中将会介绍一些常用的命令,对于最新的全部命令列表信息,请参考ZooKeeper文档。

  • ruok
    提供(有限的)服务器的状态信息。如果服务器正在运行,就会返回imok响应信息。事实上“OK”状态只是一个相对的概念,例如,服务器运行中,虽无法与集群中其他服务器进行通信,然而该服务器返回的状态仍然是“OK”。对于更详细信息及可靠的健康状态检查,需要使用stat命令。
  • stat
    提提供了服务器的状态信息和当前活动的连接情况,状态信息包括一些基本的统计信息,还包括该服务器当前是否处于活动状态,即作为群首或追随者,该服务器所知的最后的zxid信息。某些统计信息为累计值,可以使用srst命令进行重置。
  • srvr
    提供的信息与stat一样,只是忽略了连接情况的信息。
  • dump
    提供会话信息,列出当前活动的会话信息以及这些会话的过期时间。该命令只能在群首服务器上运行。
  • conf
    列出该服务器启动运行所使用的基本配置参数。
  • envi
    列出各种各样的Java环境参数。
  • mntr
    提供了比stat命令更加详细的服务器统计数据。每行输出的格式为key<tab>value。(群首服务器还将列出只用于群首的额外参数信息)。
  • wchs
    列出该服务器所跟踪的监视点的简短摘要信息。
  • wchc
    列出该服务器所跟踪的监视点的详细信息,根据会话进行分组。
  • wchp
    列出该服务器所跟踪的监视点的详细信息,根据被设置监视点的

znode节点路径进行分组。

  • cons,crst
    cons命令列出该服务器上每个连接的详细统计信息,crst重置这些连接信息中的计数器为0。

八、通过JMX进行监控

四字母命令可以用于系统的监控,但却没有提供系统控制和修改的方法,ZooKeeper通过标准Java管理协议,JMX(Java管理扩展),提供了更强大的监控和管理功能。

本节中,将会使用一个简单的管理控制台工具jconsole来探索通过JMX管理ZooKeeper功能。

jconsole为Java中自带的工具,实际上,类似jconsole这样的JMX工具常常用于监控远程的ZooKeeper服务器,但出于说明的目的,将会在ZooKeeper服务所运行的设备运行该工具。

首先、启动第二个ZooKeeper服务器(即ID为2的服务器),之后,只需要在命令行中简单的输入jconsole命令就可以启动jconsole工具,jconsole启动后,就会看到类似图10-7中所示的窗口。

注意到其中带有“zookeeper”名称的进程,对于本地进程,jconsole会自动发现可连接的进程。
【图10-7:jconsole启动界面】
clipboard.png

现在,只需要在列表中双击该进程就可以连接到ZooKeeper进程上,因为没有启用SSL,此时会提示关于非安全连接的选项,单击非安全连接按钮,之后屏幕中就会出现图10-8所示的窗口。
【图10-8:进程管理的第一个窗口】
clipboard.png

从这个界面中看到,可以通过该工具获取关于ZooKeeper服务器的各种各样有趣的统计信息。JMX支持通过MBean(托管Bean)来将自定义信息暴露给远程管理者,虽然名字听起来比较笨拙,但却是暴露信息和操作的一个非常灵活的方式。jconsole会在最右侧的信息标签中列出进程暴露的所有MBean信息,如图10-9所示。
【图10-9:jconsole中MBean信息】
clipboard.png

在MBean列表中可以看到ZooKeeper所使用且暴露出来的组件信息,比较关心ZooKeeperService的信息,因此双击该列表项,将会看到一个分级的列表副本以及这些副本的信息,如果打开某些列表中的子项,会看到图10-10所示的信息。
【图10-10:jconsole中关于服务器2的信息】
clipboard.png

通过浏览replica.2的信息,注意到这些信息中还包括其他副本
的信息,但只是一些通信信息,因为服务器2对其他副本所知信息并不多,所以服务器2无法展示更多其他副本信息,而服务器2很了解自己的信息,所以它可以暴露更多的信息。

当启动服务器1,服务器2就可以与服务器1构成一个法定人数,此时就可以看到服务器2的更多信息。启动服务器1,之后再次通过jconsole检查服务器2信息。图10-11展示了通过JMX暴露的一些额外信息,可以看到服务器2当前角色为追随者,还可以看到数据数的信息。

图10-11还展示了服务器1的一些信息,看到,服务器1的角色为群首角色,同时还有一些额外信息,仅在群首服务器中,FollowerInfo中还会展示追随者的列表。当点击该按钮,会看到连接到服务器1的其他ZooKeeper服务器的原始列表信息。
【图10-11:jconsole中关于服务器1的信息】
clipboard.png

到目前为止,看到相比四字母命令,通过JMX所看到的信息更加优美直观,但是还没有看到有什么新功能,现在看看JMX可以做到而四字母命令无法做到的功能。启动zkCli脚本工具,连接到服务器1,之后运行以下命令:

create -e /me "foo"

通过该命令,会创建一个临时性的znode节点,图10-11所示的服务器1的JMX信息中,可以看到出现了一个关于连接的新信息项,连接的属性中列出了各种各样的信息,这些信息对于调试运行问题非常有用。这个视图中,还看到两个有意思的操作:terminateSession和terminateConnection。

terminateConnection操作会关闭ZooKeeper客户端到服务器之间的连接,而会话依然处于活跃状态,所以此时客户端还可以重新连接到另一个服务器,客户端会收到失去连接的事件,但可以轻易恢复。

与之相反,terminateSession操作会声明会话已经死亡,客户端与服务器之间的连接将会被关闭,且会话也将因过期而中止,客户端也不能再使用这个会话与其他服务器建立连接。因此,使用terminateSession操作时需要小心,因为该操作会在会话超时之前就导致会话过期,所以在该进程自己发现过期前,其他进程可能已经发现了该进程的会话死亡的情况。

远程连接

JMX代理器运行于ZooKeeper服务器的JVM之中,如果连接远程的ZooKeeper服务器,需要配置好JMX代理器。对与远程连接的JMX协议有若干参数需要配置,本节中,展示了一种JMX的配置方式,来看看JMX提供了什么样功能。如果在生产环境使用JMX,可能需要使用另一种JMX配置——具体参考如何配置更高级的安全功能。

对JMX的配置可以通过系统属性的方式进行配置,在用于启动ZooKeeper服务器的zkServer.sh脚本中,使用SERVER_JVMFLAGS环境变量来配置这些系统属性。

例如,以下面的配置启动服务,就可以远程连接服务器3的55555端口。

SERVER_JVMFLAGS="-Dcom.sun.management.jmxremote.password.file=passwd \
-Dcom.sun.management.jmxremote.port=55555 \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.access.file=access"
 _path_to_zookeeper_/bin/zkServer.sh start   _path_to_server3.cfg_

系统属性参数中用到了密码文件和访问控制文件,这些文件的格式非常简单。首先,创建passwd文件,方式如下:

# user password
admin <password>

注意,密码文件中的密码信息为明文保存,因此只能给密码文件的所有者分配读写权限,如果不这样做,Java就无法启动服务。同时,关闭了SSL功能,这意味着密码信息在网络上会以明文的方式进行传输,如果需要更强的安全级别,JMX也提供了更强有力的选项,但这些已经超出本书所涉及的范围。

对于访问控制文件,将会给admin用户赋予readwrite权限,创建该文件的方式如下:

admin readwrite

九、工具

在ZooKeeper发行包的contrib目录中,可以找到一些软件,这些软件可以帮你集成ZooKeeper到其他的检测系统中去。列出了一部分最受欢迎的软件或工具:

  • 通过C绑定实现的Perl和Python语言的绑定库。
  • ZooKeeper日志可视化的软件。
  • 一个基于网页的集群节点浏览和ZooKeeper数据修改功能的软件。
  • ZooKeeper中自带的zktreeutil和guano均可以从GitHub下载。这些软件可以对ZooKeeper的数据进行导入和导出操作。
  • zktop,也可以从GitHub下载,该软件监控ZooKeeper的负载,并提供Unix的top命令类似的输出。
  • ZooKeeper冒烟测试,可以从GitHub上下载。该软件对ZooKeeper集群提供了一个简单的冒烟测试客户端,这个工具对于开发人员熟悉ZooKeeper非常不错。

总有刁民想害朕
111 声望9 粉丝