1. 程式人生 > >zookeeper-如何修改原始碼-《每日五分鐘搞定大資料》

zookeeper-如何修改原始碼-《每日五分鐘搞定大資料》

本篇文章僅僅是起一個拋磚迎玉的作用,舉一個如何修改原始碼的例子。文章的靈感來自 ZOOKEEPER-2784

提一個問題先

之前的文章講過zxid的設計,我們先複習下:

zxid有64位,分成兩部分: 高32位是Leader的epoch:選舉時鐘,每次選出新的Leader,epoch累加1 低32位是在這輪epoch內的事務id:對於使用者的每一次更新操作叢集都會累加1。

這麼設計會存在什麼問題?

Zookeeper 的事務 ID 有可能會超過 32 位。

epoch增長非常慢,超過32位需要非常久的時間,幾乎可以忽略這個問題,但是事務 ID 似乎不行。我們來算下。

如果我們每秒鐘操作1000次 Zookeeper ,即 1k/s ops,那麼

2^32/(86400∗1000) ≈ 49.7

49.7天后,事務 ID 就將溢位,那溢位會發生什麼,看程式碼:

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);
        }
        

從上面的程式碼可以看到,

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 (and double type the same) in JVM, is divided into high 32bit and low 32bit part of the operation, and because the ZXID variable is not modified with volatile 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大家還有什麼想知道的可以留言,我覺得有價值的話會再更新些新的文章。

推薦閱讀

歡迎關注大叔據