本篇文章僅僅是起一個拋磚迎玉的做用,舉一個如何修改源碼的例子。文章的靈感來自 ZOOKEEPER-2784。html
以前的文章講過zxid的設計,咱們先複習下:java
zxid有64位,分紅兩部分:
高32位是Leader的epoch:選舉時鐘,每次選出新的Leader,epoch累加1
低32位是在這輪epoch內的事務id:對於用戶的每一次更新操做集羣都會累加1。git
Zookeeper 的事務 ID 有可能會超過 32 位。github
epoch增加很是慢,超過32位須要很是久的時間,幾乎能夠忽略這個問題,可是事務 ID 彷佛不行。咱們來算下。apache
若是咱們每秒鐘操做1000次 Zookeeper ,即 1k/s ops,那麼併發
2^32/(86400∗1000) ≈ 49.7
49.7天后,事務 ID 就將溢出,那溢出會發生什麼,看代碼:ide
src/java/main/org/apache/zookeeper/server/quorum/Leader.java line1037大數據
/** * create a proposal and send it out to all the members * * @param request * @return the proposal that is queued to send to all the members */ public Proposal propose(Request request) throws XidRolloverException { /** * Address the rollover issue. All lower 32bits set indicate a new leader * election. Force a re-election instead. See ZOOKEEPER-1277 */ if ((request.zxid & 0xffffffffffL) == 0xffffffffffL) { String msg = "zxid lower 32 bits have rolled over, forcing re-election, and therefore new epoch start"; shutdown(msg); throw new XidRolloverException(msg); }
從上面的代碼能夠看到,atom
Zookeeper 的 Leader 節點會throw new XidRolloverException(msg) 強制進行 re-election從新選舉,操作系統
即服務會中止一段時間,在一些場景下,這種狀況過於頻繁是不能容忍的,那咱們來看看如何解決。
上面說了epoch增加速度慢到能夠忽略它溢出的問題,那麼能夠從新設計 ZXID,
設計成高 24 位用於 epoch,低 40 位用於 事務 ID 增加。
咱們再來算一下:
2^40/(86400∗1000) ≈ 12725.8 即 12725.8/365 ≈ 34.9 年
1k/s ops 的狀況下, 34.9 年以後纔會進行一次強制選舉。
設想不錯,能夠解決咱們的問題,那咱們繼續。
從操做系統的底層來講,對於32位操做系統,單次操做能處理的最長長度爲32bit,而long類型8字節64bit,因此對long的讀寫都要兩條指令才能完成(即每次讀寫64bit中的32bit)。
爲何說這個,由於也許有人會把這個和 ZXID 的設計聯想起來,上面的 ZOOKEEPER-2784裏面也提到了這個問題。
However, i thought the ZXID is
long
type, reading and writing the long type (anddouble
type the same) in JVM, is divided into high 32bit and low 32bit part of the operation, and because theZXID
variable is not modified withvolatile
and is not boxed for the corresponding reference type (Long
/Double
), so it belongs to [non-atomic operation]
我大概翻譯一下:
ZXID 是 long 類型,32 bit 的 JVM 在對 long 讀寫時(和 double 類型同樣),是分爲高 32 位和 低 32 位兩部分進行操做的,因爲 ZXID 變量沒有用 volatile 修飾,且也沒有裝箱爲對應的引用類型(Long / Double),屬於非原子操做。
這位老哥擔憂對 ZXID 從新設計時把高 32 位和 低 32 位改爲高 24 位和 低 40 位,可能會存在併發的問題。
會不會有這個問題,咱們先來看看源碼:
Iterator<Integer> iterator = servers.iterator(); long zxid = Long.valueOf(m.group(2)); int count = (int)zxid;// & 0xFFFFFFFFL; int epoch = (int)Long.rotateRight(zxid, 32);// >> 32;
注意這個& 0xFFFFFFFFL,實際上後面的代碼還有不少這種按位與的操做,就不貼出來了。
翻了這一塊的源碼就能夠知道,這個擔憂是多餘的,關於ZXID的全部操做都是位操做而不是「=」的賦值操做,它不會形成JVM級別的併發問題。
接下來咱們就用源碼中「位與」的方式,把 32 爲改爲 40 位。
即:zxid按位於(&)0xffffffffffL(40位)得到zxid的後40位。
注意要把count以前的int類型改成long類型,由於int爲32bit,long爲64bit,此時count有40位因此換成long。
Iterator<Integer> iterator = servers.iterator(); long zxid = Long.valueOf(m.group(2)); // int count = (int)zxid;// & 0xFFFFFFFFL; // int epoch = (int)Long.rotateRight(zxid, 32);// >> 32; long count = zxid & 0xffffffffffL; int epoch = (int)Long.rotateRight(zxid, 40);// >> 40;
後面還有多處相似的地方要修改,就不一一列出來了,有興趣的能夠看這裏github
zookeeper篇到這裏就完結了,關於zookeeper你們還有什麼想知道的能夠留言,我以爲有價值的話會再更新些新的文章。