[英]Non-blocking socket client and selectors
由於我想嘗試實現與 Telegram 服務器的基本 TCP 連接(使用 MTProto),我開始閱讀有關 Java NIO 類的信息。 但是,我“卡住”了試圖理解Selector
對客戶的意義。
選擇器支持基於鍵的、非阻塞的、多路復用的 I/O。 換句話說,選擇器使您能夠通過多個通道執行 I/O。 ( Java - 完整參考)
由於 TCP 消息,作為 stream,始終是有序的,而且我只會打開單個套接字連接(單個SocketChannel
),使用Selector
有什么意義? 我認為沒有意義,對嗎?
如果我的自我回答是正確的,為什么不直接使用阻塞 I/O?
NIO
基本上是用在服務器端來處理大規模的。 我將嘗試解釋典型服務器的工作原理。
服務器有一個請求隊列,輪詢線程從該隊列中消耗連接作為blocking dequeue
操作(在 Java 中,請求隊列的默認長度為 50。這意味着如果您在請求隊列已滿時嘗試啟動第 51 個連接,你會得到ConnectionRefused
異常)。
一個典型的blocking
實現是這樣的:
服務器接受連接並將其放入requests queue
。
輪詢thread
使用來自隊列頭部的連接並將其分派到thread pool
。 如果thread-pool
隊列未滿,輪詢線程將變為空閑狀態並繼續使用queue
中的連接。
在某些時候,線程池中的所有線程都將變得忙碌,並且輪詢線程將在向池提交更多連接時被阻塞(因為線程池隊列是blocking queue
)。
requests queue
同時開始填滿。 在某些時候,它會完全充滿,服務器將不再接受任何連接。
在這一點上,我們的服務器無法再擴展。 請注意,池中的“忙碌”線程可能根本不忙,而只是被阻塞了——比如它們正在服務的相應套接字的InputStream
上的更多數據。
現在考慮這個設計:
輪詢線程使用請求隊列頭部的項目。
它把它放在一個無限列表中。
另一個線程不斷地遍歷這個列表並檢查套接字上是否發生了任何活動(讀到讀、准備寫等)。 如果有活動,則提供socket
。 請注意,這些sockets
在NIO
模式下運行。 也就是說,如果沒有活動,我們的線程不會被阻塞。
同時輪詢線程繼續向列表提交連接,因為列表是無限的。 它不會在任何地方被阻塞(除非它在請求隊列上等待新連接)。
在上述設計中,請注意,我們的規模僅受系統資源的限制——即list
擁有多少連接。 響應時間會受到影響,因為只有一個線程為所有連接提供服務。 由於盲目的迭代,您的 CPU 消耗將非常高。 但是您仍然可以連接到服務器,這與之前的設計不同。
NIO
基本上通過使用selectors
解決了這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.