2013-04-06 66 views
5

我知道這是一個反覆出現的問題,我已經閱讀了如下文章http://www.mailinator.com/tymaPaulMultithreaded.pdf這樣的說法,它不一定是真的,尼奧比io好。java.nio對於Web服務器有什麼好處?

但我很努力地看到,如何在開發web服務器時比傳統的acceptor/worker線程體系結構更好地擴展java nio?讓我來解釋:

一般的Java Web服務器使用下面的模式來處理連接:

  • 限於核心數量的一些接收器線程塊上的ServerSocket的accept()方法:

    while (true) { 
        socket = serverSocket.accept(); 
        // handleRequest submits the socket to a queue 
        handleRequest(socket); 
        socket.close(); 
    } 
    
  • 當檢索到客戶端套接字時,會將其提交到非阻塞隊列,然後由工作線程池中的工作線程處理。工作線程的數量取決於正在執行的io操作的持續時間。

如何使用java.nio使這種架構更具可擴展性?

我的意思是我仍然需要工作線程來處理阻塞操作(訪問數據庫或文件系統,調用外部服務)的請求。如果後端操作不像node.js中那樣異步執行,我仍然需要工作線程來限制整個可伸縮性與1或2個事件分派器線程。

+0

當您有長時間空閒的連接時,NIO更有用。因此,例如,大量使用長輪詢的應用程序可以使用NIO處理更多的請求,而不是使用IO處理更多請求,而每個長輪詢連接阻塞並關聯線程。有了NIO,情況並非如此。 – 2013-04-06 11:12:11

+0

爲了減少線程數 - 更多的線程,更多的系統調用。 – coolcfan 2013-04-08 09:08:39

回答

14

我真的很喜歡Paul Tyma在這個問題上的文章,它非常深入。我看見兩個要點在他的文章:

  • 你可以得到更好的吞吐量與傳統的,阻塞IO(他量了)
  • 傳統,阻塞IO讓你的服務器邏輯的方式更簡單 - 在客戶端 - 服務器對話的狀態在線程流中隱式定義。

使用非阻塞式NIO的主要原因是當你有很多,許多同時,空閒的請求。原因是:使用NIO,您可以從同一個線程提供多個請求,並且這是更好

好吧,這是你可以隨處讀取的東西。現在... 爲什麼是這樣的更好

主要有兩個原因,這都涉及到兩種不同類型的開銷來與每個線程:

  • 當調度改變了處理器在執行線程,有一個「上下文切換」,這可能是一個昂貴的操作(例如,線程在處理器中有一些狀態 - 寄存器中的值,L1,L2,L3緩存等中加載的大量數據) - 線程停止時必須「保存」並在線程繼續執行時「重新加載」;同樣,當你失去L1,L2,L3緩存的內容時,你可能會得到大量的緩存未命中,這可能是不好的(或不是,取決於工作量)
  • 每個線程分配自己獨立堆棧(通常用於存儲局部變量和返回的函數調用地址)

所以,每個線程帶有一些「浪費」的內存,並可能「浪費了「處理器週期(執行」上下文切換「)。假設你有一個聊天服務器,客戶端通過HTTP連接請求新消息,並且服務器只有在有新消息發送給該客戶端時纔會回答它們(以便客戶端立即收到新消息)。假設你有10K個這樣的客戶端。在傳統的,阻塞的,每個連接線程模型中,你會有10k個線程。在Java中,線程堆棧大小(-Xss)的典型標準值是256kb。使用10k線程,您將自動使用大約2GB的內存!!!!!!!!更糟糕的是:即使聊天服務器上沒有任何活動,也沒有消息被髮送,客戶仍然會讓你浪費2GB。添加大量的上下文切換,並且您看到您遇到問題。在這種情況下,你最好使用非阻塞的NIO,其中較少的線程(最終只有1個!)足以處理所有的10k客戶端,所以你可以保存上下文切換(即, CPU時間)和線程堆棧(即內存),即使代價是更復雜的代碼,這通常是使用非阻塞NIO的副作用。

+0

爲有興趣閱讀的人添加指向[Paul Tyma的文章](http://paultyma.blogspot.sg/2008/03/writing-java-multithreaded-servers.html?m=1)的鏈接。 – Misterhex 2017-06-13 00:25:47

3

對於高併發性,NIO或非阻塞IO可以很好地擴展,每個連接都不需要專用線程,只需要一個主線程來接受連接並且有一些其他工作線程來處理IO,線程的數量是固定的,這是爲什麼它比傳統的接受者/工作者線程架構更具可擴展性的主要原因。

相關問題