我是在線遊戲的主要開發人員。 玩家使用通過TCP/IP(TCP,而不是UDP)連接到遊戲服務器的特定客戶端軟件從經典的多線程到java.nio異步/非阻塞服務器
目前,服務器的體系結構是一個經典的多線程服務器,每個連接只有一個線程。 但是在繁忙時間,當連接的人數通常爲300或400人時,服務器越來越滯後。
我想知道,如果通過切換到java.nio。*異步I/O模型來處理多個連接的線程,如果性能會更好。 在Web上查找涵蓋此類服務器體系結構基礎知識的示例代碼非常簡單。然而,經過幾個小時的谷歌搜索,我沒有找到一些更高級的問題的答案:
1 - 該協議是基於文本,而不是基於二進制。客戶端和服務器交換以UTF-8編碼的文本行。單行文本代表單個命令,每行都以\ n或\ r \ n正確終止。 對於經典的多線程服務器,我有這樣的代碼:
public Connection (Socket sock) {
this.in = new BufferedReader(new InputStreamReader(sock.getInputStream(), "UTF-8"));
this.out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream(), "UTF-8"));
new Thread(this) .start();
}
,並通過與線的readLine然後運行,數據讀取線。
在文檔中,我找到了一個可以從SocketChannel創建Reader的實用類Channels。但據說,如果頻道處於非阻塞模式,生成的閱讀器將無法工作,這與非阻塞模式必須使用我願意使用的高性能頻道選擇API的必要性相矛盾。所以,我懷疑這不是我想要做的正確解決方案。 因此,第一個問題是:如果我不能使用它,如何有效地並正確地處理在nio API中使用緩衝區和通道轉換原生Java字符串和從UTF-8編碼的數據轉換爲UTF-8編碼數據? 我是否必須手動使用get/put或包裝的字節數組?如何從ByteBuffer轉換爲以UTF-8編碼的字符串?我承認不太清楚如何在charset包中使用類,以及它如何工作。
2 - 在異步/非阻塞I/O世界中,如何處理連續讀取/寫入本質上依次執行的順序? 例如,登錄過程通常是基於質詢 - 響應的:服務器發送一個問題(特定的計算),客戶端發送響應,然後服務器檢查客戶端給出的響應。 答案是,我認爲,在整個登錄過程中肯定不會發送一個任務發送給工作線程,因爲這個過程非常長,並且存在凍結工作線程太多時間的風險(想象一下:10個池線程,10個玩家嘗試同時連接;與已經在線的玩家有關的任務被延遲,直到一個線程再次準備好爲止)。
3 - 如果兩個不同的線程同時在同一個通道上調用Channel.write(ByteBuffer)會發生什麼? 客戶可能收到混合線嗎?例如,如果一個線程發送「aaaaa」,另一個發送「bbbbb」,客戶端是否可以收到「aaabbbbbaa」,或者我確保每個郵件都是按順序發送的?呼叫返回後,我允許修改使用的緩衝區嗎? 或者換個問題,我需要額外的同步來避免這種情況嗎? 如果我需要額外的同步,在寫入完成後如何知道發佈何時鎖定等等? 恐怕答案並不像在選擇器中註冊OP_WRITE那麼簡單。通過這樣做,我注意到我始終都會收到準備就緒的事件,並始終爲所有客戶端退出Selector。大部分時間選擇爲無,因爲每個客戶端只有3或4條消息發送人,而選擇循環每秒執行數百次。因此,潛在地,主動地等待觀點,那是非常糟糕的。
4 - 多個線程可以在同一個選擇器上同時調用Selector.select,而沒有任何併發問題,比如缺少事件,調度兩次等等。
5 - 事實上,nio和它所說的一樣好?保持經典的多線程模型是否有趣,但是不建立每個連接的線程,使用更少的線程並在連接上循環以查找使用InputStream.isAvailable的數據可用性?這個想法是愚蠢的還是低效的?
對於某些示例代碼,請查看Netty的源代碼:https://github.com/netty/netty它是一個非常好的庫。 – 2013-03-11 20:58:08