2017-10-10 70 views
2

對於練習,我們將實現一個服務器,該服務器具有偵聽連接的線程,接受它們並將套接字引入BlockingQueue。池中的一組工作線程然後通過隊列並處理通過套接字進入的請求。Java:使用隊列管理比線程更多的連接

每個客戶端連接到服務器,發送大量的請求(在發送下一個請求之前等待響應),並最終在完成時斷開連接。

我目前的方法是讓每個工作線程在隊列中等待,獲取套接字,然後處理一個請求,並最終將(仍然打開的)套接字放回隊列中,然後再處理另一個請求, 。有更多的客戶端比工作線程多,所以很多連接排隊。

此方法的問題:即使客戶端不發送任何內容,線程也會被客戶端阻止。可能的僞解決方案,都不能令人滿意:在inputStream

  • 呼叫available(),並把連接回到隊列中,如果返回0的問題:這是不可能的檢測,如果客戶端仍連接。
  • 如上所述,但使用socket.isClosed()socket.isConnected()來確定客戶端是否仍連接。問題:這兩種方法都沒有檢測到客戶端掛起,正如EJP在Java socket API: How to tell if a connection has been closed?
  • 中很好地描述的一樣,通過讀取或寫入客戶端來探測客戶端是否仍在那裏。問題:讀取塊(即回到非活動客戶端阻塞隊列的原始情況),並且寫入實際上會將某些內容發送給客戶端,從而使測試失敗。

有沒有辦法解決這個問題?即是否有可能區分一個斷開的客戶端與被動客戶端沒有阻止或發送的東西?

回答

1

發現問題可以通過一些技巧來解決。有幾個人長時間的討論之後,我結合自己的想法,把工作中reasonnable時間完成:

  • 創建套接字後,將其配置使得阻塞read會禁止對特定時間,比如100毫秒:socket.setSoTimeout(100);
  • 此外,記錄每個連接上次成功讀取的時間戳,例如與System.currentTimeMillis()
  • 原則上(見下面的例外這個原則),在讀取前在連接上運行available()。如果這返回0,則將連接放回隊列中,因爲沒有可讀的內容。
  • 上述原則的例外情況,在這種情況下,不使用available():如果時間戳太舊(例如,超過1秒),請使用read()實際阻止連接。這將不會比您在上面爲套接字設置的SoTimeout更長。如果您遇到TimeoutException,請將連接重新放回隊列中。如果您讀取-1,則將該連接從遠端關閉後丟棄。

有了這個策略,大多數讀取嘗試立即終止,要麼返回一些數據,要麼什麼也不是,因爲沒有任何東西available(),所以它們被跳過。如果另一端關閉了連接,我們將在一秒內檢測到這一點,因爲上次成功讀取的時間戳太舊。在這種情況下,我們執行實際讀取操作,返回-1,並且相應地更新套接字的isClosed()。而在套接字仍然打開但隊列很長以至於我們有超過一秒的延遲的情況下,我們需要100ms才能發現連接仍然存在,但尚未準備就緒。

編輯:一個增強功能是將「上次成功讀取」更改爲「上次阻塞讀取」,並在得到TimeoutException時更新時間戳。

3

簡答:沒有。如需更長的回答,請參閱EJP的回答。

這就是爲什麼你可能不應該把套接字放回隊列,而是處理所有來自套接字的請求,然後關閉它。將連接傳遞給不同的工作線程以單獨處理請求不會帶來任何好處。

如果您的客戶端運行狀況不佳,您可以在套接字上使用讀取超時,因此只有發生超時時,讀取才會阻塞。然後,您可以關閉該套接字,因爲您的服務器沒有時間迎合那些表現不佳的客戶端。

2

有沒有辦法解決這個問題?即是否有可能區分一個斷開的客戶端與被動客戶端沒有阻止或發送的東西?

當使用阻塞IO時不是真的。

你可以看看非阻塞(NIO)包,它處理的事情有點不同。

實質上,你有一個套接字可以註冊一個「選擇器」。如果您將套接字註冊爲「可準備讀取數據」,則可以確定要讀取哪些套接字而無需單獨進行輪詢。

同樣的寫作內容。

這裏是一個tutorial on writing NIO servers

+0

如果我理解正確,這個問題與阻塞IO無關,可用()中的問題用於解決這個問題。相反,問題在於如何判斷客戶是否意外關閉,或只是安靜。我認爲使用NIO對此並無幫助。 –

+0

我試圖處理更大的查詢'這種方法的問題:即使客戶端不發送任何東西,線程也會被客戶端阻止。兩個模型都存在斷開連接的客戶端,但至少在NIO中,您並不等待它們。 – ptomli

+0

同樣可以使用available()no?還沒有等待。 –

0

號,辨別從客戶端沒有正確關閉其插座不活動的客戶端的唯一方法是發送ping或東西來檢查,如果他們還在那裏。

可能的解決方案,我可以看到的是,沒有一會送什麼

  • 踢的客戶。你將不得不跟蹤他們已經安靜了多久,一旦他們達到極限,你就認爲他們已經斷開了。
  • Ping客戶端,看他們是否還在那裏。我知道你要求一種不發送任何東西的方法,但如果這確實是一個問題,也就是說你不能使用上述解決方案,這可能是根據具體情況做到的最好方法(因爲它是一個練習你可能不得不想象具體細節)。
  • 兩者的混合,實際上這可能會更好。跟蹤他們平靜多久,稍微給他們發一個ping,看他們是否還活着。