1. 同步阻塞BIO
-
serversocket負責綁定IP和Port,socket負責發(fā)起連接操作。連接成功后,通過輸入/輸出流進行同步阻塞式通信。 - 傳統(tǒng)請求-應答模型,Acceptor線程每接受一個請求,創(chuàng)建一個線程處理請求并返回。
2. 偽異步IO
- 后來考慮到高性能/高并發(fā)場景,演進了用線程池/消息隊列實現(xiàn)1個或多個線程處理N個客戶端。但底層通信機制仍是同步阻塞IO,故稱作“偽異步”。線程池中的線程數(shù)量和隊列大小可控,因此不會導致資源耗盡和宕機。
- 弊端:JDK的API文檔中,對于InputStream和OutputStream中的read(byte[] b)和write(byte[] b)操作,都是同步阻塞的,亦即一直阻塞直到發(fā)生如下事件:
- 有數(shù)據(jù)可讀。
- 讀到數(shù)據(jù)末尾。
- 發(fā)生空指針或IO異常。
如果大量連接上來,前端只有一個Acceptor線程接入請求,那么,之前的請求在線程池中的隊列(阻塞隊列實現(xiàn))中排隊,隊列滿,入隊操作阻塞,新請求將被拒絕。破解這一難題,NIO入場。
3. NIO編程
官方
New IO,更多人接受Non-block IO.與Socket和ServerSocket類似,NIO提供了SocketChannel和ServerSocketChannel套接字通道實現(xiàn),且支持阻塞和非阻塞模式。
3.1. 緩沖區(qū)Buffer
在NIO中,所有的數(shù)據(jù)操作都是面對緩沖區(qū)的,從緩沖區(qū)讀,往緩沖區(qū)寫。實現(xiàn)原理是數(shù)組。
3.2. 通道Channel
通道與流的不同,在于通道是雙向的,而流是單向的。通道可用于讀、寫、同時讀寫。同時支持阻塞和非阻塞模式。
3.3. 多路復用器Selector
多路選擇器提供了選擇已經(jīng)就緒的任務的能力。Selector會不斷輪詢注冊其上的Channel,如果某個Channel上有新的TCP連接、讀、寫事件,這個Channel就處于就緒狀態(tài),會被Selector輪詢出來。JDK使用了epoll()代替了select()實現(xiàn),沒有最大連接句柄數(shù)1024/2048的限制,一般1GB內(nèi)存支持100 000個連接。
4. AIO 編程
NIO2.0引入新的異步通道的概念,并提供了異步文件通道和異步套接字通道的實現(xiàn)。是真正的異步非阻塞IO,對應unix的事件驅(qū)動IO(AIO),不需要Selector對其輪詢即可實現(xiàn)異步讀寫。
5. 總結(jié)
5.1. 不選擇原生NIO編程原因
- NIO類庫和API非常負責,使用麻煩。
- 需具備其他技能做鋪墊,如Java多線程編程,Reactor模式等。
- 可靠性不夠,如客戶端斷連重連,網(wǎng)絡閃斷,半包讀寫,失敗緩存等。
- JDK的NIO bug,如epoll bug,它會導致Selector空輪詢,導致CPU使用率100%。
5.2. 為什么選擇Netty
- API使用簡單,開發(fā)門檻低。
- 功能強大,預置了多種編解碼功能,支持主流協(xié)議。
- 定制能力強。
- 性能高。
- 成熟穩(wěn)定,修復了已發(fā)現(xiàn)的所有NIO bug。
- 社區(qū)活躍,版本迭代周期短。
- 經(jīng)過了大規(guī)模的商用考驗。