Zookeeper 是一個基於 Google Chubby 論文實現的一款解決分佈式數據一致性問題的開源實現,方便了依賴 Zookeeper 的應用實現 數據發佈 / 訂閱
、負載均衡
、服務註冊與發現
、分佈式協調
、事件通知
、集羣管理
、Leader 選舉
、 分佈式鎖和隊列
等功能php
通常的,在分佈式系統中,構成集羣的每一臺機器都有本身的角色,最爲典型的集羣模式就是 Master / Slave
主備模式。在該模式中,咱們把可以處理全部寫操做
的機器稱爲 Master
節點,並把全部經過異步複製
方式獲取最新數據、提供讀服務
的機器稱爲 Slave
節點html
而 Zookeeper 中,則是引入了 領導者(Leader)
、跟隨者(Follower)
、觀察者(Observer)
三種角色 和 領導(Leading)
、跟隨(Following)
、觀察(Observing)
、尋找(Looking)
等相應的狀態。在 Zookeeper 集羣中的經過一種 Leader 選舉
的過程,來選定某個節點做爲 Leader
節點,該節點爲客戶端提供讀
和寫
服務。而 Follower
和 Observer
節點,則都能提供讀
服務,惟一的區別在於,Observer
機器不參與 Leader 選舉
過程 和 寫操做
的"過半寫成功"
策略,Observer
只會被告知已經 commit 的 proposal。所以 Observer
能夠在不影響寫性能
的狀況下提高集羣的讀性能
(詳見下文 「性能優化 - 優化策略 - Observer 模式」 部分)java
Session 指客戶端會話。在 Zookeeper 中,一個客戶端會話
是指 客戶端
和服務器
之間的一個 TCP 長鏈接
。客戶端啓動的時候,會與服務端創建一個 TCP 鏈接
,客戶端會話的生命週期,則是從第一次鏈接創建開始算起。經過這個鏈接,客戶端可以經過心跳檢測
與服務器保持有效的會話,並向 Zookeeper 服務器發送請求並接收響應,以及接收來自服務端的 Watch 事件通知
node
Session 的 sessionTimeout 參數,用來控制一個客戶端會話的超時時間。當服務器壓力
太大 或者是網絡故障
等各類緣由致使客戶端鏈接斷開時,Client 會自動從 Zookeeper 地址列表中逐一嘗試重連(重試策略可以使用 Curator 來實現)。只要在 sessionTimeout 規定的時間內可以從新鏈接
上集羣中任意一臺服務器
,那麼以前建立的會話仍然有效。若是,在 sessionTimeout 時間外重連了,就會由於 Session 已經被清除了,而被告知 SESSION_EXPIRED
,此時須要程序去恢復臨時數據;還有一種 Session 重建後的在新節點上的數據,被以前節點上因網絡延遲
晚來的寫請求
所覆蓋的狀況,在 ZOOKEEPER-417 中被提出,並在該 JIRA 中新加入的 SessionMovedException
,使得 用同一個 sessionld/sessionPasswd
重建 Session 的客戶端能感知到,可是這個問題到 ZOOKEEPER-2219 仍然沒有獲得很好的解決linux
在 Zookeeper 中,節點
分爲兩類,第一類是指 構成集羣的機器
,稱之爲機器節點
;第二類則是指 數據模型中的數據單元
,稱之爲數據節點 ZNode
。Zookeeper 將全部數據存儲在內存中,數據模型的結構相似於樹(ZNode Tree),由斜槓(/)
進行分割的路徑,就是一個 ZNode
,例如 /foo/path1
。每一個 ZNode 上都會保存本身的數據內容 和 一系列屬性信息git
ZNode 能夠分爲持久節點(PERSISTENT)
和臨時節點(EPHEMERAL)
兩類。所謂持久節點
是指一旦這個 ZNode 被建立了,除非主動進行移除操做,不然這個節點將一直保存在 Zookeeper 上。而臨時節點
的生命週期,是與客戶端會話綁定的,一旦客戶端會話失效
,那麼這個客戶端建立的全部臨時節點
都會被移除
。在 HBase 中,集羣則是經過 **/hbase/rs/* 和 /hbase/master 兩個臨時節點,來監控 HRegionServer 進程的加入和宕機 和 HMaster 進程的 Active 狀態**github
另外,Zookeeper 還有一種 順序節點(SEQUENTIAL)
。該節點被建立的時候,Zookeeper 會自動在其子節點名上,加一個由父節點維護的、自增整數的後綴(上限:Integer.MAX_VALUE
)。該節點的特性,還能夠應用到 持久 / 臨時節點 上,組合成 持久順序節點(PERSISTENT_SEQUENTIAL)
和 臨時順序節點(EPHEMERAL_SEQUENTIAL)
算法
Zookeeper 的每一個 ZNode
上都會存儲數據,對應於每一個 ZNode
,Zookeeper 都會爲其維護一個叫作 Stat
的數據結構,Stat
中記錄了這個 ZNode
的三個數據版本,分別是 version
(當前 ZNode 數據內容的版本),cversion
(當前 ZNode 子節點的版本)和 aversion
(當前 ZNode 的 ACL 變動版本)。這裏的版本
起到了控制 Zookeeper 操做原子性
的做用(詳見下文 「源碼分析 - 落腳點 - Zookeeper 樂觀鎖」 部分)shell
若是想要讓寫入數據的操做支持 CAS,則能夠藉助 Versionable#withVersion
方法,在 setData()
的同時指定當前數據的 verison
。若是寫入成功,則說明在當前數據寫入的過程當中,沒有其餘用戶對該 ZNode 節點的內容進行過修改;不然,會拋出一個 KeeperException.BadVersionException
,以此能夠判斷本次 CAS 寫入是失敗的。而這樣作的好處就是,能夠避免 「併發局部更新 ZNode 節點內容」 時,發生相互覆蓋的問題數據庫
Watcher(事件監聽器)是 Zookeeper 提供的一種 發佈/訂閱
的機制。Zookeeper 容許用戶在指定節點上註冊一些 Watcher,而且在一些特定事件觸發
的時候,Zookeeper 服務端會將事件通知給訂閱
的客戶端。該機制是 Zookeeper 實現分佈式協調
的重要特性
相似於 Unix 文件系統,Zookeeper 採用 ACL(Access Control Lists)
策略來進行權限控制(使用方式,詳見下文 「經常使用命令 - 執行腳本 - zkCli - 節點操做」 部分;代碼實現,詳見 PrepRequestProcessor#checkACL)
Command | Comment |
---|---|
CREATE (c) | 建立子節點的權限 |
READ (r) | 獲取節點數據和子節點列表的權限 |
WRITE (w) | 更新節點數據的權限 |
DELETE (d) | 刪除當前節點的權限 |
ADMIN (a) | 管理權限,能夠設置當前節點的 permission |
Scheme | ID | Comment |
---|---|---|
world | anyone | Zookeeper 中對全部人有權限的結點就是屬於 world:anyone |
auth | 不須要 id | 經過 authentication 的 user 都有權限 |
(Zookeeper 支持經過 kerberos 來進行 authencation ,也支持 username /password 形式的 authentication) |
||
digest | username:BASE64 (SHA1(password)) | 須要先經過 username:password 形式的 authentication |
ip | id 爲客戶機的 IP 地址(或者 IP 地址段) | ip:192.168.1.0/14,表示匹配前 14 個 bit 的 IP 段 |
super | 對應的 id 擁有超級權限(CRWDA) |
@Before public void init() throws Exception { zoo = new ZooKeeper(HOST.concat(":" + CLIENT_PORT), TIME_OUT_MILLISECOND, null); acls = new ArrayList<>(); acls.add(new ACL(ZooDefs.Perms.ALL, new Id(IP, "10.24.40.178"))); acls.add(new ACL(ZooDefs.Perms.ALL, new Id(IP, "127.0.0.1"))); aclsNoAuth = new ArrayList<>(); aclsNoAuth.add(new ACL(ZooDefs.Perms.ALL, new Id(IP, "127.0.0.1"))); } @Test public void ipAcl() throws Exception { if (zoo.exists(IP_PATH, null) != null) zoo.delete(IP_PATH, -1); if (zoo.exists(IP_PATH_NO_AUTH, null) != null) zoo.delete(IP_PATH_NO_AUTH, -1); zoo.create(IP_PATH, IP.getBytes(), acls, CreateMode.PERSISTENT); assertEquals(IP, new String(zoo.getData(IP_PATH, false, null))); zoo.create(IP_PATH_NO_AUTH, IP.getBytes(), aclsNoAuth, CreateMode.PERSISTENT); try { zoo.getData(IP_PATH_NO_AUTH, false, null); } catch (KeeperException.NoAuthException e) { assertEquals("KeeperErrorCode = NoAuth for ".concat(IP_PATH_NO_AUTH), e.getMessage()); } }
Tips: Full code is here.
$ zkCli.sh -server localhost:2181 [zk: localhost:2181(CONNECTED) 16] ls / [leader, election, zookeeper, origin, ip, auth_test, benchmark] [zk: localhost:2181(CONNECTED) 17] ls /ip Authentication is not valid : /ip [zk: localhost:2181(CONNECTED) 18] getAcl /ip 'ip,'10.24.40.178 : cdrwa 'ip,'127.0.0.1 : cdrwa $ zkCli.sh -server 127.0.0.1:2181 [zk: 127.0.0.1:2181(CONNECTED) 1] ls /ip [] [zk: 127.0.0.1:2181(CONNECTED) 2] get /ip ip cZxid = 0x10000c43b ctime = Tue Aug 22 16:50:37 CST 2017 mZxid = 0x10000c43b mtime = Tue Aug 22 16:50:37 CST 2017 pZxid = 0x10000c43b cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 2 numChildren = 0
簡單易用,直接在物理層面,對用戶進行權限隔離;可是,若是不將 127.0.0.1
放入到 IP Acl 列表裏,會給服務端的運維帶來麻煩
@Before public void init() throws Exception { zoo = new ZooKeeper(HOST.concat(":" + CLIENT_PORT), TIME_OUT_MILLISECOND, null); zoo.addAuthInfo("digest", "yuzhouwan:com".getBytes()); zooNoAuth = new ZooKeeper(HOST.concat(":" + CLIENT_PORT), TIME_OUT_MILLISECOND, null); } @Test public void digestAcl() throws Exception { if (zoo.exists(AUTH_PATH_CHILD, null) != null) zoo.delete(AUTH_PATH_CHILD, -1); if (zoo.exists(AUTH_PATH, null) != null) zoo.delete(AUTH_PATH, -1); zoo.create(AUTH_PATH, bytes, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); try { zooNoAuth.create(AUTH_PATH_CHILD, bytes, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); } catch (KeeperException.InvalidACLException e) { assertEquals("KeeperErrorCode = InvalidACL for /auth_test/child", e.getMessage()); } zoo.create(AUTH_PATH_CHILD, bytes, ZooDefs.Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT); try { zooNoAuth.delete(AUTH_PATH_CHILD, -1); } catch (KeeperException.NoAuthException e) { assertEquals("KeeperErrorCode = NoAuth for /auth_test/child", e.getMessage()); } assertEquals(AUTH_PATH, new String(zoo.getData(AUTH_PATH, false, null))); }
Tips: Full code is here.
$ zkCli.sh -server localhost:2181 [zk: localhost:2181(CONNECTED) 5] ls / [leader, auth_test, election, zookeeper, benchmark, origin] [zk: localhost:2181(CONNECTED) 6] ls /auth_test Authentication is not valid : /auth_test [zk: localhost:2181(CONNECTED) 7] get /auth_test Authentication is not valid : /auth_test [zk: localhost:2181(CONNECTED) 8] getAcl /auth_test 'digest,'yuzhouwan:h/j+/wDlblTtA48jnbq8snP1glA= : cdrwa [zk: localhost:2181(CONNECTED) 9] addauth digest yuzhouwan:true [zk: localhost:2181(CONNECTED) 10] get /auth_test /auth_test cZxid = 0x10000c31e ctime = Tue Aug 22 15:26:27 CST 2017 mZxid = 0x10000c31e mtime = Tue Aug 22 15:26:27 CST 2017 pZxid = 0x10000c31e cversion = 0 dataVersion = 0 aclVersion = 0 ephemeralOwner = 0x0 dataLength = 10 numChildren = 0
能夠創建角色,按照用戶名、密碼進行權限控制;可是,想要修改某個用戶的密碼,須要對全部的 ACLs 作更改
$ cd ~/install/ $ wget http://archive.apache.org/dist/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz $ wget http://archive.apache.org/dist/zookeeper/zookeeper-3.4.10/zookeeper-3.4.10.tar.gz.md5 # 校驗 MD5 $ head -n 1 zookeeper-3.4.10.tar.gz.md5 e4cf1b1593ca870bf1c7a75188f09678 zookeeper-3.4.10.tar.gz $ md5sum zookeeper-3.4.10.tar.gz e4cf1b1593ca870bf1c7a75188f09678 *zookeeper-3.4.10.tar.gz # 對比 MD5 碼一致後進行解壓安裝 $ tar zxvf zookeeper-3.4.10.tar.gz -C ~/software/ $ cd ~/software $ ln -s zookeeper-3.4.10 zookeeper
cd zookeeper $ mkdir tmp $ cp conf/zoo_sample.cfg conf/zoo.cfg $ mkdir -p /home/zookeeper/data/zookeeper $ mkdir -p /home/zookeeper/logs/zookeeper # 更多配置,詳見下文 「經常使用配置」 部分 $ vim conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/home/zookeeper/data/zookeeper dataLogDir=/home/zookeeper/logs/zookeeper clientPort=2181
$ bin/zkServer.sh start $ bin/zkServer.sh status ZooKeeper JMX enabled by default Using config: /home/zookeeper/software/zookeeper/bin/../conf/zoo.cfg Mode: standalone $ bin/zkCli.sh
$ vim conf/zoo.cfg tickTime=2000 initLimit=10 syncLimit=5 dataDir=/home/zookeeper/data/zookeeper dataLogDir=/home/zookeeper/logs/zookeeper clientPort=2181 server.1=yuzhouwan01:2281:2282 server.2=yuzhouwan02:2281:2282 server.3=yuzhouwan03:2281:2282 # 在各個節點的 dataDir下建立 myid 文件,並對應 zoo.cfg中配置的 id [zookeeper@yuzhouwan01 ~] echo "1" > /home/zookeeper/data/zookeeper/myid [zookeeper@yuzhouwan02 ~] echo "2" > /home/zookeeper/data/zookeeper/myid [zookeeper@yuzhouwan03 ~] echo "3" > /home/zookeeper/data/zookeeper/myid
Command | Comment |
---|---|
conf | 輸出相關服務配置的詳細信息 |
cons | 列出全部鏈接到服務器的客戶端的徹底的鏈接 / 會話 的詳細信息 (包括「接受 / 發送」 的包數量、會話 id 、操做延遲、最後的操做執行等等信息) |
envi | 輸出關於服務環境的詳細信息 (區別於 conf 命令) |
dump | 列出未經處理的會話和臨時節點 |
stat | 查看哪一個節點被選擇做爲 Follower 或者 Leader |
ruok | 測試是否啓動了該 Server,若回覆 imok 表示已經啓動 |
mntr | 輸出一些運行時信息(latency / packets / alive_connections / outstanding_requests / server_state / znode + watch + ephemerals count …) |
reqs | 列出未經處理的請求 |
wchs | 列出服務器 watch 的簡要信息 |
wchc | 經過 session 列出服務器 watch 的詳細信息(輸出是一個與 watch 相關的會話的列表) |
wchp | 經過路徑列出服務器 watch 的詳細信息(輸出一個與 session 相關的路徑) |
srvr | 輸出服務的全部信息(能夠用來檢查當前節點同步完畢集羣數據,處於 Follower 狀態) |
srst | 重置服務器統計信息 |
kill | 關掉 Server |
Tips: <font color="bright turquoise">部分加粗命令對資源消耗比較大,生產環境慎用!</font>
# online $ yum install nc # offline $ uname -a Linux yuzhouwan 2.6.32-279.el6_sn.7.x86_64 #1 SMP Fri May 27 18:04:25 CST 2016 x86_64 x86_64 x86_64 GNU/Linux # 搜索rpm包 https://rpmfind.net/linux/rpm2html/search.php?query=nc&submit=Search+...&system=&arch=x86_64 $ wget ftp://rpmfind.net/linux/centos/6.9/os/x86_64/Packages/nc-1.84-24.el6.x86_64.rpm $ rpm -ivh nc-1.84-24.el6.x86_64.rpm
$ echo <four-letter command> | nc 127.0.0.1 2181
$ vim zoo.cfg # 4lw.commands.whitelist=* 4lw.commands.whitelist=stat, ruok, conf, isro
2017-06-13 23:05:01,998 [myid:5] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@197] - Accepted socket connection from /0:0:0:0:0:0:0:1:40986 2017-06-13 23:05:01,998 [myid:5] - INFO [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@827] - Processing srvr command from /0:0:0:0:0:0:0:1:40986 2017-06-13 23:05:02,000 [myid:5] - INFO [Thread-64778:NIOServerCnxn@1007] - Closed socket connection for client /0:0:0:0:0:0:0:1:40986 (no session established for client)
$ bin/zkServer.sh start # 查看狀態 $ bin/zkServer.sh status # 中止服務 $ bin/zkServer.sh stop # 重啓 $ bin/zkServer.sh restart
start-foreground
參數來排查失敗緣由$ bin/zkServer.sh start-foreground ZooKeeper JMX enabled by default Using config: /home/eagle/software/zookeeper/bin/../conf/zoo.cfg Error: Could not find or load main class org.apache.zookeeper.server.quorum.QuorumPeerMain # /home/eagle/software/zookeeper/zookeeper-3.4.10.jar 的問題,從新下載,校驗 md5 正確後,再次安裝便可
$ cd $ZOOKEEPER_HOME $ bin/zkCli -server <zk host>:2181,<zk host>:2181,<zk host>:2181
Command | Example | Comment |
---|---|---|
create | 建立一個節點 | |
ls | 查看當前節點數據 | |
ls2 | 查看當前節點數據,並能看到更新次數 等信息 |
|
set | 修改節點 | |
get | 獲得一個節點,包含數據 和更新次數 等信息 |
|
delete | 刪除一個節點 | |
rmr | 遞歸刪除 | |
history | 列出最近的歷史命令 | |
redo <command number: n> |
redo 1 | 重作第 n 步命令 |
stat | 打印節點狀態 | |
close | 關閉當前鏈接 | |
connect <host>:<port> |
connect localhost:2181 | 當 close 當前鏈接或者意外退出後,可在 zkCli 命令模式中重連 |
quit | 退出當前鏈接 | |
setAcl <path> <acl: scheme + id + permissions> |
setAcl /zk world:anyone:cdrw | 設置節點權限策略(詳見上文 「基本概念 - ACL」 部分) |
getAcl <path> |
獲取節點權限策略 | |
addauth <scheme> <auth> |
addauth digest username:password | 節點權限認證 |
setquota -n | -b val <path> |
|
listquota <path> |
listquota /zookeeper | 查看節點的配額 |
count=5, bytes=-1 | /zookeeper 節點個數限額爲 5,長度無限額 | |
delquota [-n or -b]``<path> |
||
sync <path> |
強制同步 | |
(因爲 「過半原則」,致使某些 Zookeeper Server 上的數據是舊的,用 sync 命令可強制同步全部的更新操做) |
||
printwatches on | off |
Command | Comment |
---|---|
get /consumers/<topic> /owners |
查看 Topic 實時消費的 Group ID |
get /consumers/<topic> /offsets/<group id> /<partitionor> |
查看 Offset 狀況(ctime:建立時間;mtime:修改時間) |
Zookeeper 保存服務器存儲快照文件的目錄,默認狀況,Zookeeper 將 寫數據的日誌文件
也保存在這個目錄裏(default:/tmp/zookeeper)
用來存儲服務器事務日誌
客戶端鏈接 Zookeeper 服務器的端口,Zookeeper 會監聽這個端口,接受客戶端的訪問請求(default:2181)
用來指示 服務器
之間或客戶端
與服務器
之間維護心跳
機制的 最小時間單元,Session
最小過時時間默認爲兩倍的 tickTime(default:2000
ms)
集羣中的 Leader
節點和 Follower
節點之間初始鏈接
時能容忍的最多心跳數(default:5 tickTime)
集羣中的 Leader
節點和 Follower
節點之間請求和應答
時能容忍的最多心跳數(default:2 tickTime)
默認分別是 2 tickTime ~ 20 tickTime,來用控制 客戶端設置的 Session 超時時間。若是超出或者小於,將自動被服務端強制設置爲 最大或者最小
配置 Zookeeper 集羣中的服務器節點 格式:`server.<myid>`=`<服務器地址>`:`<LF通信端口>`:`<選舉端口>` 樣例:`server.1=yuzhouwan:2888:3888`
$ vim zoo_replicated1.cfg tickTime=2000 dataDir=/zookeeper/data/zookeeper1 initLimit=5 syncLimit=2 dynamicConfigFile=/zookeeper/conf/zoo_replicated1.cfg.dynamic $ vim zoo_replicated1.cfg.dynamic server.1=125.23.63.23:2780:2783:participant;2791 server.2=125.23.63.24:2781:2784:participant;2792 server.3=125.23.63.25:2782:2785:participant;2793
Zookeeper 默認支持 JMX 鏈接,可是隻支持本地鏈接
$ vim bin/zkServer.sh # ZOOMAIN="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=$JMXLOCALONLY org.apache.zookeeper.server.quorum.QuorumPeerMain" ZOOMAIN="-Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false org.apache.zookeeper.server.quorum.QuorumPeerMain" </pre>
$ sudo tcpdump tcp port 2181 and host ! 127.0.0.1 # 下面抓包到的信息,是 Curator 遠程鏈接,並在 /children 命名空間下,建立臨時節點 /yuzhouwan,最終斷開 TCP 鏈接的過程 14:50:16.192736 IP 10.10.10.10.56342 > yuzhouwan01.eforward: Flags [S], seq 818611125, win 8192, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0 14:50:16.192765 IP yuzhouwan01.eforward > 10.10.10.10.56342: Flags [S.], seq 3867146147, ack 818611126, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0 14:50:16.193502 IP 10.10.10.10.56342 > yuzhouwan01.eforward: Flags [.], ack 1, win 256, length 0 14:50:16.196755 IP 10.10.10.10.56342 > yuzhouwan01.eforward: Flags [P.], seq 1:50, ack 1, win 256, length 49 14:50:16.196768 IP yuzhouwan01.eforward > 10.10.10.10.56342: Flags [.], ack 50, win 115, length 0 14:50:16.198618 IP yuzhouwan01.eforward > 10.10.10.10.56342: Flags [P.], seq 1:42, ack 50, win 115, length 41 14:50:16.214597 IP 10.10.10.10.56342 > yuzhouwan01.eforward: Flags [P.], seq 50:76, ack 42, win 256, length 26 14:50:16.215109 IP yuzhouwan01.eforward > 10.10.10.10.56342: Flags [P.], seq 42:62, ack 76, win 115, length 20
Tips: 比較關心的一個問題是,tcpdump
是否會對性能形成影響?答案是,會的。當過濾上千的 IP 時,已經會影響到服務器性能。主要瓶頸在 BPF Filter
,這是一個 O(n)O(n) 線性時間複雜度的算法,能夠考慮 HiPAC 多維樹匹配
替代
Metrics | Comment | Threshold |
---|---|---|
zk_version | 版本 | |
zk_avg_latency | 平均 響應延遲 | > 50ms,比上次統計增加超過 20ms,且上一次延遲不爲 0 |
zk_max_latency | 最大 響應延遲 | |
zk_min_latency | 最小 響應延遲 | |
zk_packets_received | 收包數 | |
zk_packets_sent | 發包數 | |
zk_num_alive_connections | 活躍鏈接數 | > 3000 |
zk_outstanding_requests | 堆積請求數 | 連續兩次大於 5(粒度:1min) |
zk_server_state | 主從狀態 | 由 Leader 變爲 Follower 或 由 Follower 變爲 Leader |
zk_znode_count | znode 數 | > 40000 |
zk_watch_count | watch 數 | > 50000 |
zk_ephemerals_count | 臨時節點數 | |
zk_approximate_data_size | 近似數據總和大小 | |
zk_open_file_descriptor_count | 打開 文件描述符 數 |
|
zk_max_file_descriptor_count | 最大 文件描述符 數 |
|
zk_followers | Follower 數 | |
zk_synced_followers | 已同步的 Follower 數 | 連續兩次檢測到未同步的 Follower 節點 (粒度:1min) |
zk_pending_syncs | 阻塞中的 sync 操做 |
Tips: 加粗指標,只有 Leader 節點纔會有
NuPIC(Numenta Platform for Intelligent Computing,Numenta智能計算平臺)
是一個不同凡響的開源人工智能平臺,它基於一種腦皮質學習算法
,即 「層級實時記憶」(Hierarchical Temporal Memory,HTM)。該算法旨在模擬新大腦皮層的工做原理,將複雜的問題轉化爲模式匹配與預測,而傳統的 AI 算法大可能是針對特定的任務目標而設計的
NuPIC 聚焦於分析實時數據流,能夠經過學習數據之間基於時間的狀態變化(而非閥值設置),對未知數據進行預測,並揭示其中的很是規特性。詳見個人另外一篇博客:人工智能
brownsys / zookeeper-benchmark (很難找到合適的開源項目,需本身編寫 Benchmark 工具)
數據的一致性
, ZooKeeper 在返回客戶端事務請求響應以前, 必需要將這次請求對應的事務日誌
刷入到磁盤中 [forceSync 參數控制,default:yes],因此事務日誌的寫入速度,直接決定了 Zookeeper 的吞吐率)指定清理頻率,單位爲小時(default:0 表示不開啓自動清理)
和上面 purgeInterval
參數配合使用,指定須要保留的文件數目(default:3)
$ vim conf/zoo.cfg autopurge.snapRetainCount=3 autopurge.purgeInterval=1 # 注意:Zookeeper 重啓會自動清除 zookeeper.out 日誌,若是有排錯須要,則應先備份好日誌文件 # 若是發現單事務日誌量過大,致使定時清理沒法及時處理,可使用 zkCleanup.sh 進行手動清除 $ cd ~/software/zookeeper1 $ zookeeper1/bin/zkCleanup.sh /home/zookeeper/logs/zookeeper1/version-2/ 3 Removing file: Aug 9, 2017 12:08:49 PM /home/zookeeper/logs/zookeeper1/version-2/log.1c00000001 Removing file: Aug 9, 2017 02:03:33 PM /home/zookeeper/data/zookeeper1/version-2/snapshot.1c0000ab90
$ cd $ZOOKEEPER_HOME $ vim conf/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 # 能夠調整爲 DaliyRollingFileAppender,天天滾動建立新的日誌文件 log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file} $ vim bin/zkServer.sh # 增長 ZOO_LOG_DIR 配置 ZOO_LOG_DIR=$ZOOBINDIR/../log4j $ vim bin/zkEnv.sh # if [ "x${ZOO_LOG4J_PROP}" = "x" ] # then # ZOO_LOG4J_PROP="INFO,CONSOLE" # fi if [ "x${ZOO_LOG4J_PROP}" = "x" ] then ZOO_LOG4J_PROP="INFO,ROLLINGFILE" fi
經過增長更多的 Observer
,能夠接收更多的讀請求
流量,卻不會犧牲寫操做
的吞吐量(寫操做的吞吐量取決於 quorum
法定人數的個數)
若是增長更多的 Server 進行投票,Quorum 會變大,這會下降寫操做
的吞吐量
然而增長 Observer
並不會徹底沒有損耗,新的 Observer
在提交一個事務後收到一條額外的 INFORM
消息。這個損耗比加入 Follower
進行投票來講會小不少
(圖片來源:Observers: core functionality)</center>
把 participant
分散到多個數據中心,可能會由於數據中心之間的網絡延遲,致使系統被拖慢
使用 Observer
的話,更新操做都在單獨的數據中心來處理,再發送到其餘數據中心,讓 Client
消費數據(分佈式數據庫 [中美異地機房] 同步系統 Otter 就使用該模式)
$ vim conf/zoo.cfg peerType=observer server.1:localhost:2181:3181:observer # 其餘須要擴展成 Observer 的 Server 都須要加上 `:observer` 後綴
由於 Observer 不參與到 ZAB 選舉中,因此 Leader 節點不會發送 proposal 給 Observer,只會發送一條包含已經經過選舉的 zxid
的 INFORM 消息。這裏,參與 ZAB 選舉的 Leader、Follower 節點稱之爲 PARTICIPANT
Server,而 Observer 則屬於 OBSERVER
Server
$ cd $ZOOKEEEPER_HOME # 常駐進程,須要避免 swapping 損害性能 # 臨時生效 $ echo 0 > /proc/sys/vm/swappiness # 永久生效 $ vim /etc/sysctl.conf vm.swappiness=0 # memory first # 設置 `-XX:+AlwaysPreTouch` 參數,在進程啓動的時候,讓 jvm 經過 demand-zeroed 方式將內存一次分配到位(ES #16937 / ZK #301) # 使用 CMS 垃圾回收器(「jdk7 + 內存使用很少」 的緣故,能夠暫不考慮 G1GC) $ vim conf/java.env export JVMFLAGS="-Xms3G -Xmx3G -Xmn1G -XX:+AlwaysPreTouch -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC" # 若是須要打印 GC 日誌,則多增長一些 flag export JVMFLAGS="-Xms3G -Xmx3G -Xmn1G -XX:+AlwaysPreTouch -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:-PrintGCTimeStamps -Xloggc:/home/zookeeper/logs/zookeeper_`date '+%Y%m%d%H%M%S'`.gc -XX:-UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=64M" # 須要注意的是,若是不但願 zkCli 等命令建立 gc 日誌文件,須要把 JVMFLAGS 改爲 SERVER_JVMFLAGS # 更進一步,若是有四字命令在作監控,則建議,直接修改 zkServer.sh,不然由於 zookeeper_`date '+%Y%m%d%H%M%S'`.gc 的存在,致使每次四字命令執行,會有不少小日誌被建立(ZK#302 已解決,待分析) $ vim bin/zkServer.sh start) # ... START_SERVER_JVMFLAGS="-Xms3G -Xmx3G -Xmn1G -XX:+AlwaysPreTouch -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -XX:-PrintGCTimeStamps -Xloggc:/home/zookeeper/logs/zookeeper_`date '+%Y%m%d%H%M%S'`.gc -XX:-UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=64M" nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \ "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \ -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \ -cp "$CLASSPATH" $JVMFLAGS $START_SERVER_JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null & # 堆大小的最終肯定,須要在 benchmark 結果的基礎之上,再作調整 # 另外,一旦建立完該文件,Zookeeper 進程會自動加載,所以,須要確保無誤以後,再創建 java.env 文件
爲什麼建議升級 JDK8 呢?由於 Zookeeper 裏面不少關鍵的功能點,都用到了 Atomic 類,而該類在 JDK8 中作了一次升級,性能提高了 6x 倍(JDK8 中加入了 Unsafe.getUnsafe().getAnd[Add|Set][Int|Long|Object]
一系列方法對 Atomic 類作了加強,因爲沒法看到 Oracle JDK 裏 Unsafe 的相關實現,有興趣能夠參考 OpenJDK 源碼。目前,存在一種比較靠譜的猜想是,compare-and-swap
被替換成系統底層的 fetch-and-add
,後者用 lock xadd 替代了 lock cmpxchg 來實現原子操做。其中 指令前綴 lock 用來鎖定指令涉及的存儲區域,xadd 指令做用是 交換兩個操做數的值,再進行加法操做,cmpxchg比較交換指令,第一操做數先和 AL/AX/EAX
比較,若是相等 ZF
置 1,第二操做數賦給第一操做數,不然 ZF
清 0,第一操做數賦給 AL/AX/EAX
。因而可知,xadd 指令實現的 FAA 和 cmpxchg 指令實現的 CAS 相比,並無自旋,所以不用擔憂循環時間過長以後 CPU 資源消耗過大,而且也沒有了 CAS 中 ABA 之類的問題 [該問題在 AtomicStampedReference 中,經過增長版本號解決了])
原文地址:https://blog.csdn.net/oASiDuoFu/article/details/80525870