2011-02-05 68 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