在前面的文章中押桃,我們分析了Cluster模式的啟動(dòng)過程况毅,以及Zab的實(shí)現(xiàn).
在這篇文章中,我們會(huì)詳細(xì)介紹异吻,ZooKeeper是如何處理每個(gè)請求的.
過程
在ZooKeeper源碼解析(3)-Cluster啟動(dòng)過程解析這篇文章中裹赴,我們介紹了,Cluster模式在啟動(dòng)時(shí)诀浪,會(huì)創(chuàng)建一個(gè)用戶創(chuàng)建ServerCnxn的ServerCnxnFactory.默認(rèn)情況下是NIOServerCnxnFactory.
這不棋返,在這里,我們再一次用到了NIOServerCnxnFactory.
在NIOServerCnxnFactory的run()方法中雷猪,
我們可以看到懊昨,NIOServerCnxnFactory會(huì)根據(jù)不同的事件來做不同的事:
- 如果是SelectionKey.OP_ACCEPT事件,代表客戶端請求建立一個(gè)新的連接春宣,那么就創(chuàng)建一個(gè)NIOServerCnxn并且將其注冊到Selector中
- 如果是SelectionKey.OP_READ | SelectionKey.OP_WRITE事件,那么就找到對應(yīng)的NIOServerCnxn并執(zhí)行其doIO()方法.
我們來具體看一下NIOServerCnxn的doIO()方法的實(shí)現(xiàn).
在這個(gè)方法中嫉你,會(huì)判斷目前發(fā)生的到底是SelectionKey.OP_READ事件還是SelectionKey.OP_WRITE事件.
我們先看一下月帝,當(dāng)發(fā)生的是SelectionKey.OP_READ事件時(shí),會(huì)如何處理.
其實(shí)這里還是蠻難理解的.特別是readLength()這個(gè)函數(shù).
其實(shí)這段代碼的功能幽污,我在注釋里已經(jīng)寫的很清楚了.
這段代碼的功能嚷辅,總的來說,就是:
- 判斷客戶端執(zhí)行的是不是monitor command
- 如果是monitor command距误,那么在readLength()函數(shù)中就進(jìn)行處理
- 如果是其他的命令簸搞,那么就處理用戶請求
這里,我們需要清楚什么是monitor command.
monitor command就是監(jiān)控命令准潭,就是能夠查看Server此時(shí)狀態(tài)的一些命令.這些命令都是四個(gè)字符的.
從ZooKeeper Administrator's Guide中趁俊,我們可以看到,有這么一些Monitor Command:
全部的Monitor Command請參考這個(gè)列表.
我們可以看到刑然,其中有兩個(gè)很重要的變量寺擂,一個(gè)是incomingBuffer,一個(gè)是lenBuffer.
我們看一下其定義:
我們可以看到泼掠,它們開始都是一個(gè)capacity為4的ByteBuffer.
我們可以看到怔软,當(dāng)有讀事件時(shí),先將數(shù)據(jù)的前四個(gè)字節(jié)讀入incomingBuffer.
我們可以看到择镇,下面有一個(gè)if block挡逼,判斷語句為是否incomingBuffer == lenBuffer,而從上面的定義中腻豌,我們看到家坎,incomingBuffer就是== lenBuffer.
其實(shí)這里的重點(diǎn)是readLength()方法.
在readLength()方法中嘱能,我們可以看到,如果前面的條件都通過乘盖,那么最后會(huì)重新初始化incomingBuffer.
其實(shí)這段代碼焰檩,只要記住我上面說的功能,就很容易理解了.
這里也吐槽一下订框,起的名字這么難理解析苫,而且感覺簡單的事情讓它給搞復(fù)雜了.
如果是Monitor Command,那么在readLength()方法以及后續(xù)的方法中穿扳,就對它進(jìn)行處理了.
而如果不是Monitor Command衩侥,就要進(jìn)入到readPayload()方法中處理.
readPayload()方法的主體如下:
我們可以看到,進(jìn)行如下處理:
- 如果此時(shí)Server還未初始化完成矛物,那么就調(diào)用readConnectRequest()方法對ConnectRequest進(jìn)行處理茫死,然后將initialized設(shè)置為true.當(dāng)然,這些都是readConnectRequest()方法內(nèi)部做的事情.
- 如果Server已經(jīng)初始化完成履羞,那么就調(diào)用readRequest()方法進(jìn)行處理
我們可以看到峦萎,在Server未初始化完成之前,還是可以處理ConnectRequest的.
這里我們不深入去看readConnectRequest()的源碼忆首,請讀者自行查看源碼來理解.
我們重點(diǎn)關(guān)注readRequest()方法.
在這個(gè)方法內(nèi)部爱榔,會(huì)調(diào)用ZooKeeperServer的processPacket()方法.
而在processPacket()方法內(nèi)部,會(huì)調(diào)用submitRequest()方法糙及,讓ZooKeeperServer中的RequestProcessor對請求進(jìn)行處理.
我們看一下ZooKeeperServer的submitRequest()方法的主體:
那么详幽,RequestProcessor是什么時(shí)候被配置的呢?
它是在啟動(dòng)ZooKeeperServer的時(shí)候浸锨,配置的.
我們看一下ZooKeeper默認(rèn)配置的RequestProcessor都有哪些.
我們可以看到唇聘,其中并沒有用于進(jìn)行Zab算法提議的RequestProcessor.
還記得我們在ZooKeeper源碼解析(3)-Cluster啟動(dòng)過程解析這篇文章中,提到的柱搜,Server在發(fā)現(xiàn)自己是Leader之后迟郎,會(huì)啟動(dòng)一個(gè)LeaderZooKeeperServer嗎?
沒錯(cuò)聪蘸,就是這里.
我們看看谎亩,LeaderZooKeeperServer都設(shè)置了哪些RequestProcessor.
我們可以看到,其中就有用于Zab算法的提議的RequestProcessor.
關(guān)于不同的RequestProcessor的介紹宇姚,我們會(huì)在下一篇文章中介紹.
我們這里主要關(guān)注PreRequestProcessor.
我們可以看到匈庭,它的processRequest()方法的實(shí)現(xiàn),特別簡單:
就是非常簡單的添加到submittedRequests這個(gè)集合中.
那么何時(shí)來處理呢浑劳?
其實(shí)PreRequestProcessor是一個(gè)線程阱持,在它啟動(dòng)后,它就會(huì)不斷從submittedRequest中讀取數(shù)據(jù)魔熏,并處理:
主要的處理函數(shù)就是pRequest()方法衷咽,其實(shí)現(xiàn)如下:
上面我們主要介紹了讀事件的處理過程鸽扁,那么寫事件呢?
過程也很簡單镶骗,就是添加一些監(jiān)控信息桶现,然后將數(shù)據(jù)發(fā)送給客戶端就好了.