2011-02-05 62 views
1

我有一個非常基本的多線程Web服務器的啓動,它可以接收所有的GET請求,只要它們一次只有一個。Java多線程Web服務器 - 沒有收到多個GET請求

但是,當多個GET請求同時進入時,有時它們都會被接收,而其他時間會有一些丟失。

我通過創建一個帶有多個圖像標籤指向我的web服務器並在Firefox中打開頁面的html頁面來測試此項。我總是使用shift +刷新。

這是我的代碼,我必須做一些根本性的錯誤。

public final class WebServer 
{ 
    public static void main(String argv[]) throws Exception 
    { 
     int port = 6789; 

     ServerSocket serverSocket = null; 
     try 
     { 
      serverSocket = new ServerSocket(port); 
     } 
     catch(IOException e) 
     { 
      System.err.println("Could not listen on port: " + port); 
      System.exit(1); 
     } 

     while(true) 
     { 
      try 
      { 
       Socket clientSocket = serverSocket.accept(); 
       new Thread(new ServerThread(clientSocket)).start(); 
      } 
      catch(IOException e) 
      { 

      } 
     } 
    } 
} 

public class ServerThread implements Runnable 
{ 
    static Socket clientSocket = null; 

    public ServerThread(Socket clientSocket) 
    { 
     this.clientSocket = clientSocket; 
    } 

    public void run() 
    { 
     String headerline = null; 
     DataOutputStream out = null; 
     BufferedReader in = null; 

     int i; 

     try 
     { 
      out = new DataOutputStream(clientSocket.getOutputStream()); 
      in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); 

      while((headerline = in.readLine()).length() != 0) 
      { 
       System.out.println(headerline); 
      } 
     } 
     catch(Exception e) 
     { 

     } 
} 
+8

你可以從注意你的例外開始,而不是忽略它們。他們在那裏是有原因的;聽他們,你的答案可能在那裏。 – skaffman 2011-02-05 22:35:39

回答

2

其實我發現這個問題是這樣的:

static Socket clientSocket = null; 

一旦我刪除了靜態的,現在完美的作品。

3

首先,@ skaffman的評論是現貨。你不應該忽視和忽略你的代碼正在做的異常。一般來說,這是一種可怕的做法。在這種情況下,你很可能會扔掉證據,告訴你真正的問題是什麼。

其次,我想你可能會因爲誤解服務器的能力而感到苦惱。無論你如何實現它,服務器每秒只能處理一定數量的請求。如果你對此提出更多要求,那麼有些人必須放棄。


我懷疑正在發生的事情是,你在短時間內發送過多的請求,和鋪天蓋地的操作系統的請求緩衝區。

當您的代碼綁定到服務器套接字時,操作系統將設置一個請求隊列以在綁定的IP地址/端口上保存傳入請求。此隊列的大小是有限的,並且如果在發出新請求時隊列已滿,操作系統將丟棄請求。這意味着如果你的應用程序不能夠快速地請求accept,那麼一些將被丟棄。

你能做些什麼呢?

  • 還有就是ServerSocket.bind(...)過載,使您可以指定要求的backlog在操作系統級隊列舉行。你可以使用這個...或使用更大的積壓。
  • 您可以更改主循環以更快地從隊列中提取請求。您當前的代碼存在的一個問題是您要爲每個請求創建一個新的線程。創建線程非常昂貴,您可以通過使用線程池回收用於先前請求的線程來降低成本。

CAVEATS

你需要小心一點。很可能您可以修改您的應用程序以在短期內接受(而不是刪除)更多請求。但從長遠來看,您應該儘可能快地接受請求,因爲您可以實際處理它們。如果接受它們的速度超過您的處理速度,可能會發生一些不好的事情:

  • 您將在所有嘗試處理請求的線程中使用大量內存。這會以各種方式增加CPU開銷。
  • 您可能會增加對內部Java數據結構,數據庫等的爭用,從而降低吞吐量。
  • 您將增加處理和回覆單個GET請求所花費的時間。如果延遲太長,客戶端可能會超時請求並重新發送。如果發生這種情況,服務器完成的工作將被浪費。

爲了防止這種情況發生,最好不要急於接受盡可能多的請求。相反,使用有界的線程池,並調整池大小(等)以優化吞吐率,同時將處理各個請求的時間保持在合理限制內。

+0

我認爲這是正確的。你將如何調整游泳池的大小? – Cratylus 2011-02-06 10:25:02