在前面的文章中,我們分析了Cluster模式的啟動過程,以及Zab的實現(xiàn).
在這篇文章中,我們會詳細介紹,ZooKeeper是如何處理每個請求的.
過程
在ZooKeeper源碼解析(3)-Cluster啟動過程解析這篇文章中,我們介紹了,Cluster模式在啟動時,會創(chuàng)建一個用戶創(chuàng)建ServerCnxn的ServerCnxnFactory.默認情況下是NIOServerCnxnFactory.
這不,在這里,我們再一次用到了NIOServerCnxnFactory.
在NIOServerCnxnFactory的run()方法中,


我們可以看到,NIOServerCnxnFactory會根據(jù)不同的事件來做不同的事:
- 如果是SelectionKey.OP_ACCEPT事件,代表客戶端請求建立一個新的連接,那么就創(chuàng)建一個NIOServerCnxn并且將其注冊到Selector中
- 如果是SelectionKey.OP_READ | SelectionKey.OP_WRITE事件,那么就找到對應的NIOServerCnxn并執(zhí)行其doIO()方法.
我們來具體看一下NIOServerCnxn的doIO()方法的實現(xiàn).
在這個方法中,會判斷目前發(fā)生的到底是SelectionKey.OP_READ事件還是SelectionKey.OP_WRITE事件.
我們先看一下,當發(fā)生的是SelectionKey.OP_READ事件時,會如何處理.


其實這里還是蠻難理解的.特別是readLength()這個函數(shù).
其實這段代碼的功能,我在注釋里已經(jīng)寫的很清楚了.
這段代碼的功能,總的來說,就是:
- 判斷客戶端執(zhí)行的是不是monitor command
- 如果是monitor command,那么在readLength()函數(shù)中就進行處理
- 如果是其他的命令,那么就處理用戶請求
這里,我們需要清楚什么是monitor command.
monitor command就是監(jiān)控命令,就是能夠查看Server此時狀態(tài)的一些命令.這些命令都是四個字符的.
從ZooKeeper Administrator's Guide中,我們可以看到,有這么一些Monitor Command:

全部的Monitor Command請參考這個列表.
我們可以看到,其中有兩個很重要的變量,一個是incomingBuffer,一個是lenBuffer.
我們看一下其定義:

我們可以看到,它們開始都是一個capacity為4的ByteBuffer.
我們可以看到,當有讀事件時,先將數(shù)據(jù)的前四個字節(jié)讀入incomingBuffer.
我們可以看到,下面有一個if block,判斷語句為是否incomingBuffer == lenBuffer,而從上面的定義中,我們看到,incomingBuffer就是== lenBuffer.
其實這里的重點是readLength()方法.

在readLength()方法中,我們可以看到,如果前面的條件都通過,那么最后會重新初始化incomingBuffer.
其實這段代碼,只要記住我上面說的功能,就很容易理解了.
這里也吐槽一下,起的名字這么難理解,而且感覺簡單的事情讓它給搞復雜了.
如果是Monitor Command,那么在readLength()方法以及后續(xù)的方法中,就對它進行處理了.
而如果不是Monitor Command,就要進入到readPayload()方法中處理.
readPayload()方法的主體如下:

我們可以看到,進行如下處理:
- 如果此時Server還未初始化完成,那么就調(diào)用readConnectRequest()方法對ConnectRequest進行處理,然后將initialized設置為true.當然,這些都是readConnectRequest()方法內(nèi)部做的事情.
- 如果Server已經(jīng)初始化完成,那么就調(diào)用readRequest()方法進行處理
我們可以看到,在Server未初始化完成之前,還是可以處理ConnectRequest的.
這里我們不深入去看readConnectRequest()的源碼,請讀者自行查看源碼來理解.
我們重點關(guān)注readRequest()方法.
在這個方法內(nèi)部,會調(diào)用ZooKeeperServer的processPacket()方法.
而在processPacket()方法內(nèi)部,會調(diào)用submitRequest()方法,讓ZooKeeperServer中的RequestProcessor對請求進行處理.

我們看一下ZooKeeperServer的submitRequest()方法的主體:

那么,RequestProcessor是什么時候被配置的呢?
它是在啟動ZooKeeperServer的時候,配置的.

我們看一下ZooKeeper默認配置的RequestProcessor都有哪些.

我們可以看到,其中并沒有用于進行Zab算法提議的RequestProcessor.
還記得我們在ZooKeeper源碼解析(3)-Cluster啟動過程解析這篇文章中,提到的,Server在發(fā)現(xiàn)自己是Leader之后,會啟動一個LeaderZooKeeperServer嗎?
沒錯,就是這里.
我們看看,LeaderZooKeeperServer都設置了哪些RequestProcessor.

我們可以看到,其中就有用于Zab算法的提議的RequestProcessor.
關(guān)于不同的RequestProcessor的介紹,我們會在下一篇文章中介紹.
我們這里主要關(guān)注PreRequestProcessor.
我們可以看到,它的processRequest()方法的實現(xiàn),特別簡單:

就是非常簡單的添加到submittedRequests這個集合中.
那么何時來處理呢?
其實PreRequestProcessor是一個線程,在它啟動后,它就會不斷從submittedRequest中讀取數(shù)據(jù),并處理:

主要的處理函數(shù)就是pRequest()方法,其實現(xiàn)如下:

上面我們主要介紹了讀事件的處理過程,那么寫事件呢?
過程也很簡單,就是添加一些監(jiān)控信息,然后將數(shù)據(jù)發(fā)送給客戶端就好了.