ZooKeeper-2.2 ZooKeeper架构- 高飞网

2.2 ZooKeeper架构

2018-05-28 11:12:44.0

zookeeper服务端运行于两种模式下:独立模式和仲裁模式。独立模式为一个单独的服务器,适用于测试环境,而仲裁模式是一个zk集合,适用于生产环境。

2.2.1 ZooKeeper仲裁

    在仲裁模式下,ZooKeeper复制集群中的所有服务器的数据树。但如果让一个客户端等待每个服务器完成数据保存后再继续,延迟问题将无法授受。因此为了使ZooKeeper工作,必须满足有效运行的服务器最小数量,例如有5个节点,则法定人数必须是3个。如果小于3个,如2个,可能会产生脑裂问题。

2.2.2 会话

    在对ZooKeeper集合执行任何请求前,一个客户端必须先与服务端建立会话。通过TCP协议与服务端进行通信。当会话无法与当前连接的服务器进行通信时,会话会透明地转移到另一个服务器上。

    会话提供了顺序保障,这意味着同一个会话中的请求会以FIFO(先进先出)顺序执行。通常一个客户端只打开一个会话。

    一个会话的主要可能状态大多是简单明了的:CONNECTING、CONNECTED、CLOSED和NET_CONNECTED。


    如果客户端与服务端的连接断开后,在仲裁模式下,会重新连接到下一个服务端,但这个服务端的状态必须是最新的,不能晚于客户端已知的状态。

2.3.3 ZooKeeper与仲裁模式

    仲裁模式的配置,第一个节点

tickTime=2000
initLimit=10
syncLimit=5
dataDir=./data
clientPort=2181
server.1=127.0.0.1:2222:2223
server.2=127.0.0.1:3333:3334
server.3=127.0.0.1:4444:4445
    主要关注下server.n=ip:port1:port2,表中:

ip:服务器n的IP地址或主机名;port1:仲裁通信的端口;port2:群首选举端口。

创建三个数据目录:

mkdir z1
mkdir z1/data
mkdir z2
mkdir z2/data
mkdir z3
mkdir z3/data

    当启动一个服务器时,需要知道启动的是哪个服务器(对应上面的server.n)。一个服务器通过读取data目录下的myid文件来获取服务端ID信息。

echo 1 > z1/data/myid
echo 2 > z2/data/myid
echo 3 > z3/data/myid
    当服务器启动时,通过配置文件中的dataDir参数来查找data目录;通过myid获得服务器ID,通过server.n中配置的端口来设置并监听。

    在data同级目录下创建三个配置文件,分别是z1/z1.cfg、z2/z2.cfg、z3/z3.cfg。配置就用上面讨论的配置,只需要修改其中zk监听的端口,第一个默认的2181,z2、z3分别是2182、2183.

    最后启动服务,从z1开始:

./bin/zkServer.sh start ./z1/z1.cfg && tail -f zookeeper.out 

日志中看到

2018-05-28 14:33:03,477 [myid:1] - INFO  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumPeer$QuorumServer@167] - Resolved hostname: 127.0.0.1 to address: /127.0.0.1
2018-05-28 14:33:03,479 [myid:1] - WARN  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumCnxManager@588] - Cannot open channel to 3 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:562)
	at org.apache.zookeeper.server.quorum.QuorumCnxManager.connectAll(QuorumCnxManager.java:614)
	at org.apache.zookeeper.server.quorum.FastLeaderElection.lookForLeader(FastLeaderElection.java:843)
	at org.apache.zookeeper.server.quorum.QuorumPeer.run(QuorumPeer.java:913)
    因为只启动了三个zk中的一个,所以整个服务还无法进行。这个服务器疯狂地连接到其他服务器,然后失败,如果启动了第二个z2服务器,就可以构成仲裁的法定人数。

    此时服务器z2被选中群首

2018-05-28 14:37:51,835 [myid:2] - INFO  [main:DatadirCleanupManager@78] - autopurge.snapRetainCount set to 3
2018-05-28 14:37:51,835 [myid:2] - INFO  [main:DatadirCleanupManager@79] - autopurge.purgeInterval set to 0
2018-05-28 14:37:51,835 [myid:2] - INFO  [main:DatadirCleanupManager@101] - Purge task is not scheduled.
2018-05-28 14:37:51,851 [myid:2] - INFO  [main:QuorumPeerMain@127] - Starting quorum peer
2018-05-28 14:37:51,880 [myid:2] - INFO  [main:NIOServerCnxnFactory@89] - binding to port 0.0.0.0/0.0.0.0:2182
2018-05-28 14:37:51,898 [myid:2] - INFO  [main:QuorumPeer@1134] - minSessionTimeout set to -1
2018-05-28 14:37:51,898 [myid:2] - INFO  [main:QuorumPeer@1145] - maxSessionTimeout set to -1
2018-05-28 14:37:51,899 [myid:2] - INFO  [main:QuorumPeer@1419] - QuorumPeer communication is not secured!
2018-05-28 14:37:51,899 [myid:2] - INFO  [main:QuorumPeer@1448] - quorum.cnxn.threads.size set to 20
2018-05-28 14:37:51,920 [myid:2] - INFO  [ListenerThread:QuorumCnxManager$Listener@739] - My election bind port: /127.0.0.1:3334
2018-05-28 14:37:51,929 [myid:2] - INFO  [QuorumPeer[myid=2]/0:0:0:0:0:0:0:0:2182:QuorumPeer@865] - LOOKING
2018-05-28 14:37:51,932 [myid:2] - INFO  [QuorumPeer[myid=2]/0:0:0:0:0:0:0:0:2182:FastLeaderElection@818] - New election. My id =  2, proposed zxid=0x100000008

    而第一个服务器z1作为追随者。

2018-05-28 14:37:52,151 [myid:1] - INFO  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:QuorumPeer@935] - FOLLOWING
2018-05-28 14:37:52,181 [myid:1] - INFO  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:ZooKeeperServer@173] - Created server with tickTime 2000 minSessionTimeout 4000 maxSessionTimeout 40000 datadir ./z1/data/version-2 snapdir ./z1/data/version-2
2018-05-28 14:37:52,182 [myid:1] - INFO  [QuorumPeer[myid=1]/0:0:0:0:0:0:0:0:2181:Follower@64] - FOLLOWING - LEADER ELECTION TOOK - 314159

最后,从客户端连接到服务器:

./bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

打印如下:

Connecting to 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
2018-05-28 14:50:30,694 [myid:] - INFO  [main:ZooKeeper@438] - Initiating client connection, connectString=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 sessionTimeout=30000 watcher=org.apache.zookeeper.ZooKeeperMain$MyWatcher@69d0a921
Welcome to ZooKeeper!
2018-05-28 14:50:30,737 [myid:] - INFO  [main-SendThread(127.0.0.1:2182):ClientCnxn$SendThread@1032] - Opening socket connection to server 127.0.0.1/127.0.0.1:2182. Will not attempt to authenticate using SASL (unknown error)
JLine support is enabled
2018-05-28 14:50:30,857 [myid:] - INFO  [main-SendThread(127.0.0.1:2182):ClientCnxn$SendThread@876] - Socket connection established to 127.0.0.1/127.0.0.1:2182, initiating session
2018-05-28 14:50:30,904 [myid:] - INFO  [main-SendThread(127.0.0.1:2182):ClientCnxn$SendThread@1299] - Session establishment complete on server 127.0.0.1/127.0.0.1:2182, sessionid = 0x263a577a6d70000, negotiated timeout = 30000

可见与z2建立了连接,多次ctrl+c关闭会话,创建会话时,会从这三个节点随机寻找作为服务端去连接。

2.3.4 实现一个原语:通过ZooKeeper实现锁

    这里讨论一个简单的方式来说明应用中如何使用ZooKeeper创建锁。假设有n个进程,其中一个进程尝试获取锁,例如在创建一个/lock节点,创建成功就代表获取了锁,否则创建失败,这个节点要是临时节点,这样在这个进程崩溃或关闭后,能够自动释放锁。