Java多线程Web服务器 - 未接收多个GET请求

时间:2011-02-05 22:11:44

标签: java multithreading http sockets

我有一个非常基本的多线程Web服务器的启动,它可以接收所有GET请求,只要它们一次来一个。

然而,当多个GET请求同时进入时,有时它们都会被收到,有时则会丢失一些。

我通过创建一个html页面来测试这一点,该页面包含指向我的网络服务器并在firefox中打开页面的多个图像标记。我总是使用shift + refresh。

这是我的代码,我必须做一些根本错误的事情。

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)
        {

        }
}

2 个答案:

答案 0 :(得分:3)

首先,@ skaffman的评论是现货。您不应该像代码当前那样捕获并忽略异常。总的来说,这是一种可怕的做法。在这种情况下,你很可能会丢掉可以告诉你真正问题是什么的证据。

其次,我认为您可能会误解服务器的功能。无论您如何实现它,服务器每秒只能处理一定数量的请求。如果你向它发出的请求多于此,则必须删除一些请求。


我怀疑发生的事情是您在短时间内发送了太多请求,并且压倒了操作系统的请求缓冲区。

当您的代码绑定到服务器套接字时,操作系统会设置一个请求队列来保存绑定的IP地址/端口上的传入请求。此队列具有有限大小,如果在新请求到来时队列已满,则操作系统将丢弃请求。这意味着,如果您的应用程序无法足够快地accept请求,则会删除一些请求。

你能做些什么?

  • ServerSocket.bind(...)的重载,允许您指定要在OS级别队列中保留的backlog个请求。您可以使用此...或使用更大的待办事项。
  • 您可以更改主循环以更快地从队列中提取请求。您当前代码的一个问题是您正在为每个请求创建一个新线程。线程创建很昂贵,您可以通过使用线程池来回收用于先前请求的线程来降低成本。

<强> CAVEATS

你需要小心一点。很可能您可以修改您的应用程序以在短期内接受(而不是删除)更多请求。但从长远来看,您应该只接受请求,就像您实际处理它们一样快。如果它比你处理它们更快地接受它们,那么可能会发生许多不好的事情:

  • 您将在尝试处理请求的所有线程中使用大量内存。这会以各种方式增加CPU开销。
  • 您可能会增加对内部Java数据结构,数据库等的争用,从而导致吞吐量降低。
  • 您将增加处理和回复个人GET请求所需的时间。如果延迟太长,客户端可能会超时请求...并再次发送。如果发生这种情况,服务器完成的工作将被浪费。

为了防止这种情况,实际上最好不要急切地接受尽可能多的请求。相反,使用有界线程池,并调整池大小(等)以优化吞吐率,同时保持在合理限制内处理单个请求的时间。

答案 1 :(得分:2)

我实际上发现问题是这样的:

  static Socket clientSocket = null;

一旦我移除静电,它现在可以很好地工作。