2011-01-20 21 views
34

非阻塞Java NIO是否仍然比每個連接的異步套接字的標準線程慢?另外,如果你要爲每個連接使用線程,你會創建新的線程還是使用非常大的線程池?每個連接模型的Java線程vs NIO

我正在寫一個Java的MMORPG服務器,應該能夠輕鬆地擴展10000個客戶端給予足夠強大的硬件,儘管客戶端的最大數量是24000(我相信每個連接模型都不可能達到這個線程數, Java中15000個線程限制)。 從一篇三年前的文章中,我聽說使用每個連接模型的線程阻塞IO的速度仍然比NIO快25%(即此文檔爲http://www.mailinator.com/tymaPaulMultithreaded.pdf),但是在這一天仍然可以實現?從那時起,Java發生了很大的變化,我聽說在比較真實生活場景時結果是可疑的,因爲所用的虛擬機不是Sun Java。 另外,因爲它是一個MMORPG服務器,許多併發用戶之間正在進行交互,所以使用同步和線程安全實踐會降低性能,使單個線程化的NIO選擇器服務10000個客戶端的速度會更快? (所有的工作不一定需要在選擇器的線程上處理,它可以像MINA/Netty的工作方式一樣在工作線程上處理)。

謝謝!

+7

10k線程不是任何(*商品*)服務器的勝利:另外,單個盒子上的10k活動客戶端是不太可能的。 – 2011-01-20 20:51:52

+1

@pst:如果按商品來看,你的意思是;非量子,尚未被發現的一種技術,我完全同意。我認爲凱文的問題最少是線程數。我對此事沒有任何有用的意見表示抱歉。還記得QOTD:測試。 – 2011-01-20 21:04:31

+0

@pst哦甜JRE它的彈性!你讓我的一天值得。 – 2011-01-20 21:19:57

回答

19

NIO的好處應該採取一粒鹽。

在HTTP服務器中,大多數連接都是保持連接狀態,它們大多數時間處於空閒狀態。爲每個線程預先分配一個線程會浪費資源。

對於MMORPG來說事情是非常不同的。我猜連接總是忙於接收用戶的指令並向用戶發送最新的系統狀態。大部分時間都需要一個線程進行連接。

如果您使用NIO,則必須不斷重新分配連接的線程。對於簡單的固定每個連接線程解決方案來說,這可能是一個劣質的解決方案。

默認的線程堆棧大小非常大,(1/4 MB?)這是爲什麼只有有限的線程的主要原因。嘗試減少它,看看你的系統是否可以支持更多。

但是,如果您的遊戲確實非常「繁忙」,那麼您最需要擔心的是您的CPU。 NIO與否,在一臺機器上處理數千個超級玩家真的很難。

9

如果你願意花足夠的錢在足夠強大的硬件上,爲什麼只限於一臺服務器。谷歌不使用一臺服務器,他們甚至不使用一臺服務器的數據中心。

一個常見的誤解是,NIO允許非阻塞IO,因此它是唯一值得進行基準測試的模型。如果你測試阻塞NIO,你可以比舊IO快30%。即如果您使用相同的線程模型並僅比較IO模型。

對於一個複雜的遊戲,在打擊10K連接之前,您很有可能會耗盡CPU。再次,有一個水平放大的解決方案更簡單。那麼你不必擔心你可以得到多少連接。

有多少用戶可以合理地進行交互? 24?在這種情況下,您有1000個獨立的小組進行互動。在一臺服務器中你不會有這麼多的內核。

您打算花費在服務器上的每個用戶多少錢?您可以購買64 GB內存的12核心服務器,價格低於5000英鎊。如果您在該服務器上放置2500個用戶,則每個用戶花費了2英鎊。

編輯:我有一個參考http://vanillajava.blogspot.com/2010/07/java-nio-is-faster-than-java-io-for.html這是我的。 ;)我曾經由Java Networking的GURU的某人進行過審查,並且廣泛地贊同他發現的內容。

12

實際上有3個解決方案:

  1. 多個線程
  2. 一個線程和NIO
  3. 這兩種解決方案1和2在同一 時間

換做的最好的事情性能指的是擁有一小部分有限數量的線程,並將NIO的網絡事件複用到這些線程上,因爲新的消息通過網絡傳入。


使用NIO有一個線程是有幾個原因是一個壞主意:

  • 如果你有多個CPU或內核,你會被閒置的資源,因爲你可以一次只使用一個核心如果你只有一個線程。
  • 如果由於某種原因(可能會執行磁盤訪問)必須阻止,則當您在等待磁盤時可能正在處理另一個連接時,CPU處於空閒狀態。每個連接

一個線程是一個壞主意,因爲它不能擴展。假設有:

  • 10 000連接
  • 2個CPU與2核,每個核
  • 只有100個線程將在任何給定的時間

然後塊就可以工作了,你只需要104個線程。還有,你在浪費管理你不需要的額外線程的資源。管理10 000個線程需要很多簿記。這會讓你放慢腳步。


這就是爲什麼你要結合這兩種解決方案。另外,請確保您的VM正在使用最快的系統調用。每個操作系統都有自己獨特的高性能網絡IO系統調用。確保你的虛擬機使用最新最好的。我相信這是在Linux中的epoll()。

此外,如果你每次連接使用 線程,你只是 創建新的線程或者你可以使用一個 非常大的線程池?

這取決於您想花多少時間進行優化。最快的解決方案是在需要時創建線程和字符串等資源。然後讓垃圾收集在您完成之後聲明它們。通過擁有一個資源池可以提高性能。不要創建一個新的對象,而是要求池中的一個,並在完成後將其返回池中。這增加了併發控制的複雜性。這可以通過高級併發算法(如non-blocking algorithms)進一步優化。 Java API的新版本中有一些適合您。您可以花費餘下的時間在一個程序上進行這些優化。對於你的特定應用來說最好的解決方案是什麼,這可能是一個值得擁有自己職位的問題。

1

服務器勢必會在CPU使用率都達到了10,000個併發用戶之前被關起來,我想這是我最好使用一個線程阻塞(N)IO方法,考慮到這樣一個事實,即對於這個特定的MMORPG,每個播放器每秒獲得幾個數據包並不少見,並且如果要使用一個選擇器,可能會陷入癱瘓。

Peter提出了一個有趣的觀點,即阻止NIO比舊庫更快,而不可否認的是,對於繁忙的MMORPG服務器,由於每個玩家接收到多少指令,最好使用線程。我不會指望太多玩家在這個遊戲中閒置,所以對於我來說,有一堆非運行的線程應該不成問題。我意識到即使使用基於NIO的框架,同步仍然是必需的,因爲它們使用多個工作線程同時運行來處理從客戶端接收到的數據包。上下文切換可能會很昂貴,但我會試試這個解決方案。重構我的代碼是相對容易的,這樣如果我發現有一個瓶頸,我可以使用NIO框架。

我相信我的問題已經得到解答。爲了從更多的人那裏獲得更多的見解,我會稍微多等一下。謝謝你的答案!

編輯:我終於選擇了我的行動方針。其實,我猶豫不決,決定使用JBoss的Netty,並允許用戶或者OIO或NIO之間切換使用類

org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 
org.jboss.netty.channel.socket.oio.OioServerSocketChannelFactory; 

很高興,Netty的支持!

3

如果您有繁忙的連接,這意味着他們經常向您發送數據並將其發送回去,您可以使用non-Blocking IOAkka一起使用。

Akka是一個開源工具包和運行時,簡化了JVM上併發和分佈式應用程序的構建。 Akka支持多種併發編程模型,但它強調基於actor的併發性,靈感來自Erlang。 Java和Scala都有語言綁定。

Akka的邏輯非阻塞,因此非常適合異步編程。使用Akka Actors,您可以刪除Thread overhead

但如果你的socket流阻止更多的時候,我建議結合使用Blocking IOQuasar

類星體是簡單的,輕量級的JVM併發的開源庫,它實現了對真正的輕量級線程(又名纖維) JVM。類星體光纖的行爲與簡單的Java線程相似,只是它們幾乎沒有內存和任務切換開銷,所以您可以在單個JVM中輕鬆生成數十萬根光纖 - 甚至是數百萬根光纖。 Quasar還爲Go語言提供的光纖通信提供通道,並與通道選擇器一起提供。它還包含了一個完整的actor模型的實現,並且緊跟在Erlang之後。

類星體的邏輯阻塞,所以你可能會產卵,說24000光纖等待不同的連接。 Quasar的好處之一是,光纖可以很容易地與簡單的線程交互。此外,Quasar還與流行的庫集成,如Apache HTTP clientJDBCJersey等,因此您可以在項目的許多方面使用Fibers。
您可能會在這兩個框架here之間看到很好的比較。