Netty到底是什麼
從HTTP說起
有了Netty,你可以實現自己的HTTP伺服器,FTP伺服器,UDP伺服器,RPC伺服器,WebSocket伺服器,Redis的Proxy伺服器,MySQL的Proxy伺服器等等。
我們回顧一下傳統的HTTP伺服器的原理
1、創建一個ServerSocket,監聽並綁定一個埠
2、一系列客戶端來請求這個埠
3、伺服器使用Accept,獲得一個來自客戶端的Socket連接對象
4、啟動一個新線程處理連接
4.1、讀Socket,得到位元組流
4.2、解碼協議,得到Http請求對象
4.3、處理Http請求,得到一個結果,封裝成一個HttpResponse對象
4.4、編碼協議,將結果序列化位元組流 寫Socket,將位元組流發給客戶端
5、繼續循環步驟3
HTTP伺服器之所以稱為HTTP伺服器,是因為編碼解碼協議是HTTP協議,如果協議是Redis協議,那它就成了Redis伺服器,如果協議是WebSocket,那它就成了WebSocket伺服器,等等。 使用Netty你就可以定製編解碼協議,實現自己的特定協議的伺服器。
NIO
上面是一個傳統處理http的伺服器,但是在高並發的環境下,線程數量會比較多,System load也會比較高,於是就有了NIO。
他並不是Java獨有的概念,NIO代表的一個辭彙叫著IO多路復用。它是由操作系統提供的系統調用,早期這個操作系統調用的名字是select,但是性能低下,後來漸漸演化成了Linux下的epoll和Mac裏的kqueue。我們一般就說是epoll,因為沒有人拿蘋果電腦作為伺服器使用對外提供服務。而Netty就是基於Java NIO技術封裝的一套框架。為什麼要封裝,因為原生的Java NIO使用起來沒那麼方便,而且還有臭名昭著的bug,Netty把它封裝之後,提供了一個易於操作的使用模式和介面,用戶使用起來也就便捷多了。
說NIO之前先說一下BIO(Blocking IO),如何理解這個Blocking呢?
客戶端監聽(Listen)時,Accept是阻塞的,只有新連接來了,Accept才會返回,主線程才能繼
讀寫socket時,Read是阻塞的,只有請求消息來了,Read才能返回,子線程才能繼續處理
讀寫socket時,Write是阻塞的,只有客戶端把消息收了,Write才能返回,子線程才能繼續讀取下一個請求
傳統的BIO模式下,從頭到尾的所有線程都是阻塞的,這些線程就乾等著,佔用系統的資源,什麼事也不幹。
那麼NIO是怎麼做到非阻塞的呢。它用的是事件機制。它可以用一個線程把Accept,讀寫操作,請求處理的邏輯全乾了。如果什麼事都沒得做,它也不會死循環,它會將線程休眠起來,直到下一個事件來了再繼續幹活,這樣的一個線程稱之為NIO線程。用偽代碼表示:
while true {
events = takeEvents(fds) // 獲取事件,如果沒有事件,線程就休眠
for event in events { if event.isAcceptable {
doAccept() // 新鏈接來了
} elif event.isReadable {
request = doRead() // 讀消息
if request.isComplete() {
doProcess()
}
} elif event.isWriteable {
doWrite() // 寫消息
}
}
}
Reactor線程模型
Reactor單線程模型
一個NIO線程+一個accept線程: