Mina 源碼閱讀:Server端基于NIO的處理流程

jopen 9年前發布 | 15K 次閱讀 NIO 網絡工具包
 

源碼面前,了無秘密。繼之前閱讀了Prototype、Spring、Tomcat、以及JDK的部分、Digester等等源碼之后,學習一門 技術,了解源碼成了必備流程。也深深的感受到了源碼面前,了無秘密的含義,同時也體會到它給我帶來的好處。同時,也希望所有開發者,不論前端后端,如果有 時間的話,都盡量看看源碼吧。

接下來進入正題,這里要對Mina流程做一個分析。因為是指對NIO流程做了分析,所以這里說的也是NIO的執行流程。

先看一下Mina中主要類的大致結構:

Mina 源碼閱讀:Server端基于NIO的處理流程

接下來看看Mina的整個生命周期:

1、NioSocketAdaptor初始化分析

初始化時,設置相關依賴,例如:listeners, sessionconfig,handler, executor等,這個過程沒什么可說的。

2、啟動并接收請求

啟動的入口是bind(SocketAddress),先來看看這部分的序列圖:

Mina 源碼閱讀:Server端基于NIO的處理流程

這一階段,

2.1 將ServerSocketChannel交由Selector來管理。對應序列圖中的regiesterHandlers()操作。

2.2 接收SocketChanel

1)使用ServerSocketChannel接收SocketChannel。

2)將接收到的SocketChannel封裝成IoSession,

3)添加到newSessions隊列中。

此外,IoSession會以attachment的方式捆綁到與SocketChannel關聯的SelectionKey中。這樣做的目的是在進行Selector.select()之后,可以根據key直接拿到IoSession。

對應于序列圖中的processHandler()操作。

3、處理請求分析

處理請求是由IoProcessor完成的,先來看看這部分的序列圖:

Mina 源碼閱讀:Server端基于NIO的處理流程

這一階段:

3.1 調用selector.select(),查看是否有可處理的SocketChannel

如果沒有,就結束本輪處理。如果有,才進行后續操作。

3.2處理最新添加的session,方法是handlernewsessions()。過程是:

1)  從newSessions隊列中取出IoSession,

2)  設置為非阻塞,并以 READ方式注冊到Selector中,這才由Selector來接管這些SocketChannel。

3)為IoSession創建FilterChain

4)將Session放到managedSession集合中。

5)觸發Session創建完畢事件。

3.3 交通管制處理。

本過程對應操作是updateTrafficMask()。

有時,我們需要在程序中控制是否進行寫讀操作,或者暫停讀操作。要實現這個功能,可以使用IoSession的resumeRead()或者 supendRead()。然而,程序中雖然調用了這兩種方法,但是要想真正的對IO進行控制,還得通過注冊感興趣的操作來完成。這一點程序中并沒有完 成。

這一過程中,其實就是完成幫助應用程序完成未完成的操作。

3.4 根據興趣事件進行相應處理。

這一過程對應序列圖中的process()操作。

1)  拿到所有的通過selector.select(TIME_OUT)篩選出來的可以處理的SelectionKey

2)  根據SelectionKey,取到與之捆綁的IoSession。

3)  處理感興趣的事件。

如果對讀感興趣,進入讀取數據、業務處理流程。從序列圖中,可以看出從SocketChannel讀取數據,然后經過FilterChain,最后到達IoHandler。

如果對寫感興趣,進入屬性數據處理流程。對于所有的對寫感興趣的IoSession,然后放到flushingSessions隊列中。

4)對每一個Session處理完興趣操作后,都有從3.1選擇到的Set<SelectionKey>中移除關聯的SelectionKey。

3.5 處理對寫事件感興趣的Session。

這一過程對應的是序列圖中的flush()操作。

如果應用程序中使用了session.write()操作,例如示例程序。該session就會被放到flushingSessions隊列中。

該過程操作是:

1)  就是將flushingSessions隊列中的session取出來

2)  清除寫興趣

3)  處理session中的寫請求隊列(writeRequestQueue),也就是將writeRequestQueue中的每一個寫請求中數據寫到SocketChannel中。

4)  如果沒有寫完畢,還有剩余,就再次注冊寫興趣。

5)  如果沒有寫完畢,就再放到flushingSeesions隊列中。

3.6 清理需要移除的Session

這個過程對應序列圖中的removeSessions()。

在3.2到3.5過程中,如果哪個過程出現了Exception,或者與客戶端的連接斷開了等情況下,相關聯的Session都會被放到removingSessions隊列中。

這個過程的處理是:

1)從removingSessions隊列中取出每一個Session

2)如果Session處于處理階段,清理writeRequestQueue,調用SelectionKey.cancel()來取消Selector對它的管理。

3)如果Session還處于未處理階段,也就是也存在于newSessions隊列中,就從newSessions隊列移除之。

3.7 做空閑處理

對應于序列圖中的notifyIdleSessions()操作。

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!