重要参数说明与配置
参数/参数类别 | 是否java环境变量 | 描述 | 默认值 |
---|
Minimum Configuration |
---|
clientPort | | 监听客户端端口 | | dataDir | | zk快照路径 | | tickTime | | 时间单元,以毫秒为单位,用于心跳和超时,举个例子最小session过期是2个ticks | | Advanced Configuration |
---|
dataLogDir | 无 | 事务日志路径 | | globalOutstandingLimit | zookeeper.globalOutstandingLimit | zk请求的缓存队列大小,避免zk oom | | preAllocSize | zookeeper.preAllocSize | snapshots预分配大小 | 64m | snapCount | zookeeper.snapCount | 记录snapCount条事务到日志文件以后开始创建新的快照和事物日志 | | traceFile | requestTraceFile | 当定义这个属性后会记录debug日志,但会影响性能 | 不设置 | maxClientCnxns | 无 | 限制每个ip的最大连接数,如不限制果设置为0代表不限制 | 60 | clientPortAddress | 无 | 暴露给客户端的端口 | | minSessionTimeout | 无 | session过期最小周期,与tickTime关联 | 2 | maxSessionTimeout | 无 | session过期最小周期,与tickTime关联 | 20 | fsync.warningthresholdms | fsync.warningthresholdms | 事务日志同步时间阀值,超出会有warn日志 | | autopurge.snapRetainCount | 无 | 保留最近数量的snapshots和transaction logs | | autopurge.purgeInterval | 无 | 清理任务触发的时间间隔,为正整数 | | syncEnabled | zookeeper.observer.syncEnabled | The observers now log transaction and write snapshot to disk by default like the participants. This reduces the recovery time of the observers on restart. Set to "false" to disable this feature. Default is "true" | | Cluster Options |
---|
electionAlg | 无 | 0:the original UDP-based version,udp基础版本 1:the non-authenticated UDP-based version of fast leader election,无认证fast-leader的udp版本 2:the authenticated UDP-based version of fast leader election,认证fast-leader的udp版本 3:TCP-based version of fast leader election,tcp?fast-leader基础版本 | 1 | initLimit | 无 | to allow followers to connect and sync to a leader,允许从连接主的最大时间 | | leaderServes | zookeeper.leaderServes | leader是否允许接受客户端连接 | yes | server.x=[hostname]:nnnnn[:nnnnn] | 无 | The first followers use to connect to the leader, and the second is for leader election, 第一个端口为从连接主的端口,第二个端口为选举端口 | | syncLimit | 无 | to allow followers to sync with ZooKeeper. If followers fall too far behind a leader, they will be dropped. 允许从同步主的最大时间,如果从远落后于主,会被提出集群 | | group.x=nnnnn[:nnnnn] | 无 | Enables a hierarchical quorum construction."x" is a group identifier and the numbers following the "=" sign correspond to server identifiers. The left-hand side of the assignment is a colon-separated list of server identifiers. Note that groups must be disjoint and the union of all groups must be the ZooKeeper ensemble. | | weight.x=nnnnn | 无 | Such a value corresponds to the weight of a server when voting,选举投票的权重 | 1 | cnxTimeout | zookeeper.cnxTimeout | Sets the timeout value for opening connections for leader election notifications. Only applicable if you are using electionAlg 3. 设置leader选举时建立连接的超时时间,只有electionAlg为3时生效 | |
服务端推荐配置
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/ccdata/zookeeper/data dataLogDir=/ccdata/zookeeper/logs clientPort=15311 server.1=ip:15312:15313 server.2=ip:15312:15313 server.3=ip:15312:15313 maxClientCnxns=100
zk运维
zk四字命令
四字命令 | 作用描述 | 执行示例 | |
---|
conf | 打印机器配置信息 | echo?conf?| nc 10.0.10.21 15311 | clientPort=15311 dataDir=/ccdata/zookeeper/data/version-2 dataLogDir=/ccdata/zookeeper/logs/version-2 tickTime=6000 maxClientCnxns=800 minSessionTimeout=12000 maxSessionTimeout=120000 serverId=1 initLimit=10 syncLimit=5 electionAlg=3 electionPort=15313 quorumPort=15312 peerType=0 | cons | 打印session和连接信息 | echo?cons?| nc 10.0.10.21 15311 | /10.0.10.61:52360[1](queued=0,recved=30228,sent=30270,sid=0x16f9daff9bc0b9e, lop=PING,est=1581665511287,to=60000,lcxid=0x216 ,lzxid=0xffffffffffffffff,lresp=1582266376218,llat=0,minlat=0,avglat=0,maxlat=303) | crst | 重置所有连接的连接/会话统计信息 | echo?crst?| nc 10.0.10.21 15311 | Connection stats reset. | dump | 打印session和临时节点信息,仅主节点生效 | echo?dump?| nc 10.0.10.21 15311 | SessionTracker dump: org.apache.zookeeper.server.quorum.LearnerSessionTracker@38ac995f ephemeral nodes dump: Sessions with Ephemerals (300): 0x36e450ca29e00fc: | envi | 打印环境变量 | echo?envi?| nc 10.0.10.21 15311 | Environment: zookeeper.version=3.4.8--1, built on 02/06/2016 03:18 GMT host.name=vpc-dev01-basic01 java.version=1.8.0_121 java.vendor=Oracle Corporation java.home=/opt/src/jdk1.8.0_121/jre | ruok | 检查机器运行是否正常,正常返回imok | echo?ruok?| nc 10.0.10.21 15311 | imok | srst | 重置服务统计信息 | echo?srst?| nc 10.0.10.21 15311 | Server stats reset. | srvr | 列出服务详细信息 | echo?srvr?| nc 10.0.10.21 15311 | Zookeeper version: 3.4.8--1, built on 02/06/2016 03:18 GMT Latency min/avg/max: 0/0/16 Received: 2206 Sent: 2244 Connections: 311 Outstanding: 0 Zxid: 0x24009ff0ac Mode: follower Node count: 127807 | stat | 列出服务和简要连接信息 | echo?stat?| nc 10.0.10.21 15311 | Zookeeper version: 3.4.8--1, built on 02/06/2016 03:18 GMT Clients: /10.0.10.61:52360[1](queued=0,recved=19,sent=19) /10.0.10.42:58180[1](queued=0,recved=38,sent=38) /10.0.10.53:55470[1](queued=0,recved=18,sent=18) | wchs | 列出服务器watches的简洁信息:连接总数、watching节点总数和watches总数 | echo?wchs?| nc 10.0.10.21 15311 | 207 connections watching 62843 paths Total watches:12988 | wchc | 列出每个session详细watches,注意 开销很大,慎用 | echo?wchc?| nc 10.0.10.21 15311 | 0x26e450c8abb4b75 /dubbo/com.caocao.api.service.DictionaryServiceApi/configurators /dubbo/com.caocao.authcenter.api.CityQueryApi/configurators /dubbo/com.caocao.api.service.BaseConfigApi/configurators /dubbo/com.caocao.api.service.RuleServiceApi/configurators /dubbo/com.caocao.bss.authserver.api.ExcelServiceApi/configurators | wchp | 列出每个路径的watches,慎用 | echo?wchp?| nc 10.0.10.21 15311 | /dubbo/com.caocao.dic.api.marketing.MarketingServiceApi/providers 0x16f9daff9bcbc44 0x26e450c8abb29c4 0x16f5cdede03000d | mntr | 列出zk的一些指标,可用来检查zk的健康情况 | echo?mntr?| nc 10.0.10.21 15311 | echo mntr | nc 10.0.10.21 15311 zk_version 3.4.8--1, built on 02/06/2016 03:18 GMT zk_avg_latency 11 zk_max_latency 3896 zk_min_latency 0 zk_packets_received 30504 zk_packets_sent 33556 zk_num_alive_connections 313 zk_outstanding_requests 0 zk_server_state follower zk_znode_count 127841 zk_watch_count 251483 zk_ephemerals_count 5294 zk_approximate_data_size 18371860 zk_open_file_descriptor_count 342 zk_max_file_descriptor_count 10240 |
需要避免的事项
一致性zk列表:
1)、客户端在使用的时候尽量写全整个zk集群列表,ps 如果只写一个子集也能正常使用,
2)、服务端的zk集群的各个服务配置必须保证一致
不正确的事务日志存放:
1)、尽量不要把事务日志放在io频繁的数据盘上,会影响zk的性能,有条件可以放多个数据盘
不合适的java堆大小:
不正确的堆内存设置
1)、如果机器只有4g,最大堆内存不要设置成4g或者更大,也许3g较合适,需要考虑系统对内存的使用情况,尤其是混部的时候
zk节点
Watches
zookeeper所有读操作(getData(),getChildren(),exists())具有设置watch的选项。 zookeeper watch的定义如下:watch事件是一次性触发器,当watch监视的数据发生变化时,通知设置了该watch的client,即watcher。
需要注意三点:
1.一次性触发器 client在一个节点上设置watch,随后节点内容改变,client将获取事件。当节点内容再次改变,client不会获取这个事件,除非它又执行了一次读操作并设置watch
2.发送至client,watch事件延迟 watch事件异步发送至观察者。比如说client执行一次写操作,节点数据内容发生变化,操作返回后,而watch事件可能还在发往client的路上。这种情况下,zookeeper提供有序保证:client不会得知数据变化,直到它获取watch事件。网络延迟或其他因素可能导致不同client在不同时刻获取watch事件和操作返回值。
3.设置watch的数据内容 涉及到节点改变的不同方式。比方说zookeeper维护两个watch列表:节点的数据watch和子节点watch。getData()和exists()设置了内容watch,getChildren()设置了子节点watch,操作返回的数据类型不同,前者是节点的内容,后者是节点的子节点列表。setData()触发内容watch,create()触发当前节点的"内容watch"和其父节点的"子节点watch",delete()同时触发"内容watch"和"子节点watch"(其子节点被全部删除),以及其父节点的"子节点watch"。说白了,对当前节点的操作,要考虑到对其父节点与子节点的影响。
watch在客户端所连接的服务端本地维护。watch的设置、维护、分发操作都很轻量级。当客户端连接到新的服务端,watch将被任一会话事件触发。与服务端断开连接时,不能获取watch事件。客户端重连后,之前注册的watch将被重新注册并在需要时触发。通常这一切透明地发生,用户不会察觉到。有一种情况watch可能丢失:之前对一个尚未建立的节点的设置了exists watch,如果断开期间该节点被建立或删除,那么此watch将丢失。
对于watch,zookeeper提供以下保证: 1.watch对于其他事件、watch、异步响应是有序的。zookeeper client library保证有序分发 2.客户端监视一个节点,总是先获取watch事件,再发现节点的数据变化。 3.watch事件的顺序对应于zookeeper服务所见的数据更新的顺序。
关于watch要记住的是: 1.watch是一次性触发的,如果获取一个watch事件并希望得到新变化的通知,需要重新设置watch 2.watch是一次性触发的并且在获取watch事件和设置新watch事件之间有延迟,所以不能可靠的观察到节点的每一次变化。要认识到这一点。 3.watch object只触发一次,比如,一个watch object被注册到同一个节点的getData()和exists(),节点被删除,仅对应于exists()的watch ojbect被调用 4.若与服务端断开连接,直到重连后才能获取watch事件。
Data Access
...
Ephemeral Nodes
zk临时节点,存活于session,当session断开,临时节点会被删除,且临时节点不允许有子节点
Sequence Nodes -- Unique Naming
zk序列节点,会在节点的尾部添加单调递增计数的路径,这个计数对于父节点是唯一的,10位数字,以0补齐
Time in ZooKeeper
zk多纬度跟踪时间,
zxid:zk事务id,任何zk状态变化都会更新,意味着zk变更的顺序
version number:每个节点的改变版本都会递增,共三个类型,version:节点的变更版本;cversion:自节点变更的版本;aversion:acl变更的版本
ticks:zk的时间单位,例如最小session过期时间是2个ticks
Real time:zk只在在zk stat structure用真实时间
ZooKeeper Stat Structure
-
czxid The zxid of the change that caused this znode to be created. -
mzxid The zxid of the change that last modified this znode. -
ctime The time in milliseconds from epoch when this znode was created. -
mtime The time in milliseconds from epoch when this znode was last modified. -
version The number of changes to the data of this znode. -
cversion The number of changes to the children of this znode. -
aversion The number of changes to the ACL of this znode. -
ephemeralOwner The session id of the owner of this znode if the znode is an ephemeral node. If it is not an ephemeral node, it will be zero. -
dataLength The length of the data field of this znode. -
numChildren The number of children of this znode. - 只有一种情况需要我们手动
session会话
使用客户端来创建一个和zk服务端连接的句柄,这就是一个会话(session)。Session一旦建立,状态就是连接中(CONNECTING)状态,然后客户端会尝试去连接zk服务端,连接成功之后状态变成已连接(CONNECTED)。一般正常情况下只会有这两个状态。不过,还是会发生一些无法恢复的错误/故障,比如:session过期,认证失败,或者客户端关闭连接,这种情况下,session状态会变成关闭(CLOSED)状态。下图给出了zk客户顿可能的状态转换情况:

以下内容是介绍zk的特性,不过很多是结合代码来描述
为了创建一个session,代码中必须要提供一个连接字符串,连接字符串由host:port组成,用,分割,例如127.0.0.1:4545,127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002。zk客户端会随意选择一个zk服务端来尝试连接。如果这个连接失败了,或者因为某种原因断开连接了,客户端会自动尝试下一个服务端,直到连接被成功建立。
3.2.0新特性:连接字符串增加了一个chroot后缀。意思就是所有操作的节点路径都是这个路径下的相对路径(类似unix的chroot命令)。举个例子,127.0.0.1:4545/app/a,127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002/app/a,这种情况下,你访问的/foo/bar节点,其实是zk服务端的/app/a/foo/bar节点。如果你的zk服务端是多应用共享的,这个特性应该会很适合你,因为可以很清晰的隔离开各个应用的数据。
当一个session建立的时候,zk服务端会生成一个64位的数字,也就是这个session的标识(姑且称之为session id吧),zk客户端会保存这个标识。如果zk客户端因为某种原因连接到了另一个zk服务端,他会把这个session id传给新的服务端。出于安全考虑,zk服务端在创建连接时,不仅仅生成一个session id,还会同时传给zk客户端一个密码,这个session id+密码是可以被任何一个zk服务端校验的。这样,每次zk客户端重连zk服务端的时候,会同时传递session id+密码,zk服务端校验通过了才会建立连接。
为什么需要密码呢? 随便举个例子,znode的权限是和session有关联的,如果任何客户端都可以伪造session id,那这种安全性就没有意义了。
使用zk客户端创建session的一个参数是session超时时间(毫秒)。但是请注意,这个session超时时间并不是客户端可以随意设置的。zk客户端会把这个session超时时间发给服务端,服务端会返回一个他可以接受的值给客户端。标准其实就是tickTime*2 <= session timeout <= tickTime*20。
如果zk客户端和zk服务端集群断开连接之后,在session超时时间之内,重新连接上了,那么session状态重新变为connected,如果在session超时时间之内没有连接上,那么session状态会变成expired。当session断开连接的时候,最好不用自己去建立一个新的session,因为zk客户端已经帮我做了这个工作,他会自动重连的。只有一种情况需要我们手动重新创建新的session,那就是明确知道session状态为过期状态(expiration)。
session是否过期是由zk服务端集群管理的,而不是zk客户端自己管理自己是否过期。zk服务端就是根据session过期时间来判断是否过期。当zk服务端超过一定的时间没有收到来自zk客户端的心跳,zk服务端就把这个session标记为过期,然后删除这个session创建的所有临时节点,并且立刻通知所有监听了这些节点的其他session。在这个时候,zk客户端处于断开连接的状态,一旦它重新连接成功了,他也会收到自己被标记为过期这一事件提醒;在还没有重新连接成功之前,这个zk客户端是不会收到过期的提醒的。
下面举个例子来形象的展示一下上面说的:
connected:session被创建,客户端和服务端正常通信。 ……客户端和服务端断开连接。 disconnected:客户端和服务端失去连接。 ……随着时间的流逝,超过了session的超时时间,客户端还没有重新连接成功。 ……随着时间的流逝,客户端重新连接上了服务端。 expired:最后客户端连接上了服务端,但是之前的session已经被过期了,客户端也会收到过期的事件提醒。 3.2.0 – SessionMovedException:这是一个内部异常,一般不会被应用程序接触到。这个异常发生的情况比较少见,举个例子吧,当一个客户端发一个心跳请求个服务端,但是网络延时,导致服务端没有收到,过一会后,客户端连接上了另一个新的服务端,在这之后,之前的心跳被旧的服务端收到了,这时候旧的服务端会被提醒,当前session已经被转移了,然后旧的服务端会关闭这个连接。客户端一般不会感知到这个异常,因为旧连接一般都会被关闭。但是还有一个特殊情况,两个客户端同时使用保存这的session id+密码来重新连接服务端,第一个连接成功,紧着第二个又连接成功,这会导致第一个连接被关闭,然后就是这两个客户端无限重连了。
更新服务器列表,zk客户端可以使用一个新的连接字符串来更新服务列表。这个机制会使用一个负载均衡算法来重新平衡各个客户端和服务端的连接情况,所以会导致部分客户端断开连接并重新连接到其他服务端。
举个例子,如果之前的连接字符串包含了3和host,新的连接字符串除了之前的3个host还多出了2个新的host,那么会有40%的客户端需要断开连接来连接新的3个服务端,以保持负载均衡。就意味着这40%的客户端需要断开重连。
同样的道理,如果原本有5个host,新的只有3个,也就是移除了2个,那么那移除的2个服务端对应的客户端都会被断开,需要重新连接到3个服务端上。
?
zk特性
Guarantees ZooKeeper is very fast and very simple. Since its goal, though, is to be a basis for the construction of more complicated services, such as synchronization, it provides a set of guarantees. These are: zk为构建一致性系统服务,例如同步系统
Sequential Consistency - Updates from a client will be applied in the order that they were sent. 顺序一致性,更新数据通过客户端发送请求的顺序
Atomicity - Updates either succeed or fail. No partial results. 原子性,更新要么成功要么失败
Single System Image - A client will see the same view of the service regardless of the server that it connects to. 单一系统镜像,客户端看到的同一个版本视图保证一致
Reliability - Once an update has been applied, it will persist from that time forward until a client overwrites the update. 可靠性,保证数据第一次写入到下次更新时数据保持不变
Timeliness - The clients view of the system is guaranteed to be up-to-date within a certain time bound. 及时性,保证系统的客户端视图在一定时间内是最新的
zk api
create creates a node at a location in the tree
delete deletes a node
exists tests if a node exists at a location
get data reads the data from a node
set data writes data to a node
get children retrieves a list of children of a node
sync waits for data to be propagated
implementation
all write requests from clients are forwarded to a single server, called the leader. The rest of the ZooKeeper servers, called followers, receive message proposals from the leader and agree upon message delivery leader接收写消息 follow接受leader的消息,并做消息传递
The programming interface to ZooKeeper is deliberately simple. With it, however, you can implement higher order operations, such as synchronizations primitives, group membership, ownership
官方对zk性能的评估
参考:http://zookeeper.apache.org/doc/r3.4.8/zookeeperOver.html#fg_zkPerfRW?
读:写 20% tps:2w 50% rps4w 80% tps:6w
当读写比在30%,当follwer挂掉后马上恢复,zk能恢复到之前的吞吐量 一般zk选举可以在200ms选出一个主
解决zk大量连接问题-zk观察者模式

观察者的设计是希望能动态扩展zookeeper集群又不会降低写性能。
虽然通过让客户端直接连接到集群的投票成员,ZooKeeper也表现得非常好,但是这种架构使得很难扩展到有大量的客户端情况。问题是,随着我们添加更多投票成员,写入性能也会随着下降。这是因为写操作需要(通常)需要集群中至少一半的节点投票达成一致,因此随着更多投票者的加入,投票的成本会显著增加。
这里引入一种新zookeeper节点类型,叫做观察者,观察者的引入帮助解决了上面的问题同时大大增加了zookeeper的动态扩展能力。观察者不参与投票,只听取投票结果。除了这个简单的区别,Observers的功能与Followers完全相同 - 客户端可以连接到它们并向它们发送读写请求。Observer会像follower一样将消息转发给leader,但是Observer只会听取投票结果,不参与投票。由于这点,我们可以增加任意数量的Observer,同时不会影响我们集群的性能。
Observer还有其它优点。因为他们不投票,所以他们不是ZooKeeper集群的重要组成部分。 因此,它们可以失败,或者与集群断开连接,而不会损害ZooKeeper服务的可用性。对用户的好处是Observer相比Follower来说更能通过不太可靠的网络链接进行连接。实际上,Observers可用于与另一个数据中心的ZooKeeper服务器通信。Observers的客户端可以快速读取,因为所有读取都在本地提供,并且写入消耗最小的网络流量,因为在没有投票协议的情况下所需的消息数量较少。
zk客户端使用代码
推荐使用:curator
创建连接:
CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy) ----------------- RetryPolicy retryPolicy =? new ?ExponentialBackoffRetry( 1000 ,? 3 ) CuratorFramework client = CuratorFrameworkFactory.newClient(zookeeperConnectionString, retryPolicy); client.start(); |
创建zk节点:
client.create().forPath( "/my/path" , myData) |
好处:Curator会管理zk连接和重试操作
分布式锁:
InterProcessMutex lock =? new ?InterProcessMutex(client, lockPath); if ?( lock.acquire(maxWait, waitUnit) ) { ???? try ???? { ???????? // do some work inside of the critical section here ???? } ???? finally ???? { ???????? lock.release(); ???? } } |
leader选举:
LeaderSelectorListener listener =? new ?LeaderSelectorListenerAdapter() { ???? public ?void ?takeLeadership(CuratorFramework client)? throws ?Exception ???? { ???????? // this callback will get called when you are the leader ???????? // do whatever leader work you need to and only exit ???????? // this method when you want to relinquish leadership ???? } } LeaderSelector selector =? new ?LeaderSelector(client, path, listener); selector.autoRequeue();?? // not required, but this is behavior that you will probably expect selector.start(); |
更多用法:
http://curator.apache.org/curator-examples/index.html
版本情况:
https://cwiki.apache.org/confluence/display/CURATOR/Releases
参考
zk管理
http://zookeeper.apache.org/doc/r3.4.8/zookeeperAdmin.html#sc_maintenance
watch:
http://zookeeper.apache.org/doc/r3.4.8/zookeeperProgrammers.html#ch_zkWatches
zk观察者:
http://zookeeper.apache.org/doc/r3.4.14/zookeeperObservers.html
https://zhuanlan.zhihu.com/p/42067231
curator:
http://curator.apache.org/getting-started.html
|