简体   繁体   English

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

[英]Java Multithreaded Web Server - Not recieving multiple GET requests

I have the starts of a very basic multi-hreaded web server, it can recieve all GET requests as long as they come one at a time. 我有一个非常基础的多线程Web服务器的启动,它可以接收所有GET请求,只要它们一次出现即可。

However, when multiple GET requests come in at the same time, sometimes they all are recieved, and other times, some are missing. 但是,当同时收到多个GET请求时,有时会全部收到,而有时却丢失了一些。

I tested this by creating a html page with multiple image tags pointing to my webserver and opening the page in firefox. 我通过创建一个带有多个指向我的网络服务器的图像标签的html页面并在firefox中打开该页面来进行测试。 I always use shift+refresh. 我总是使用shift + refresh。

Here is my code, I must be doing something fundamentally wrong. 这是我的代码,我必须做的是根本错误的事情。

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

        }
}

First, @skaffman's comment is spot on. 首先,@ skaffman的评论很明确。 You should not catch-and-ignore exceptions like your code is currently doing. 您不应像代码当前那样捕获并忽略异常。 In general, it is a terrible practice. 通常,这是一种可怕的做法。 In this case, you could well be throwing away the evidence that would tell you what the real problem is. 在这种情况下,您很可能会丢掉可以告诉您真正问题是什么的证据。

Second, I think you might be suffering from a misapprehension of what a server is capable of. 其次,我认为您可能会误解服务器的功能。 No matter how you implement it, a server can only handle a certain number of requests per second. 无论您如何实现,服务器每秒只能处理一定数量的请求。 If you throw more requests at it than that, some have to be dropped. 如果您对它提出的请求更多,则必须丢弃一些请求。


What I suspect is happening is that you are sending too many requests in a short period of time, and overwhelming the operating system's request buffer. 我怀疑正在发生的事情是,您在短时间内发送了太多请求,并且淹没了操作系统的请求缓冲区。

When your code binds to a server socket, the operating system sets up a request queue to hold incoming requests on the bound IP address/port. 当您的代码绑定到服务器套接字时,操作系统将设置一个请求队列,以将传入的请求保存在绑定的IP地址/端口上。 This queue has a finite size, and if the queue is full when a new request comes, the operating system will drop requests. 该队列的大小是有限的,如果在收到新请求时队列已满,则操作系统将丢弃请求。 This means that if your application is not able to accept requests fast enough, some will be dropped. 这意味着,如果您的应用程序不能足够快地accept请求,则某些请求将被丢弃。

What can you do about it? 你能为这个做什么?

  • There is an overload of ServerSocket.bind(...) that allows you to specify the backlog of requests to be held in the OS-level queue. ServerSocket.bind(...)的重载允许您指定要保存在OS级别队列中的请求的backlog You could use this ... or use a larger backlog. 您可以使用此...或使用更大的积压。
  • You could change your main loop to pull requests from the queue faster. 您可以更改主循环以更快地从队列中拉出请求。 One issue with your current code is that you are creating a new Thread for each request. 当前代码的一个问题是,您正在为每个请求创建一个新的线程。 Thread creation is expensive, and you can reduce the cost by using a thread pool to recycle threads used for previous requests. 创建线程很昂贵,您可以通过使用线程池回收用于先前请求的线程来降低成本。

CAVEATS 洞穴

You need to be a bit careful. 您需要注意一点。 It is highly likely that you can modify your application to accept (not drop) more requests in the short term. 您很有可能可以在短期内修改应用程序以接受(而不是丢弃)更多请求。 But in the long term, you should only accept requests as fast as you can actually process them. 但是从长远来看,您应该只接受尽可能快地接受请求的速度。 If it accepts them faster than you can process them, a number of bad things can happen: 如果接受它们的速度超过了处理它们的速度,则可能会发生许多不良情况:

  • You will use a lot of memory with all of the threads trying to process requests. 您将在所有试图处理请求的线程上使用大量内存。 This will increase CPU overheads in various ways. 这将以各种方式增加CPU开销。
  • You may increase contention for internal Java data structures, databases and so on, tending to reduce throughput. 您可能会增加对内部Java数据结构,数据库等的争用,从而倾向于降低吞吐量。
  • You will increase the time taken to process and reply to individual GET requests. 您将增加处理和回复单个GET请求所需的时间。 If the delay is too long, the client may timeout the request ... and send it again. 如果延迟时间太长,客户端可能会使请求超时……然后再次发送。 If this happens, the work done by the server will be wasted. 如果发生这种情况,服务器的工作将被浪费。

To defend yourself against this, it is actually best to NOT eagerly accept as many requests as you can. 为了保护自己,实际上最好不要急于接受尽可能多的请求。 Instead, use a bounded thread pool, and tune the pool size (etc) to optimize the throughput rate while keeping the time to process individual requests within reasonable limits. 相反,请使用有线程池,并调整池大小(等)以优化吞吐率,同时将处理单个请求的时间保持在合理的范围内。

I actually discovered the problem was this: 我实际上发现问题是这样的:

  static Socket clientSocket = null;

Once I removed the static, it works perfectly now. 移除静电后,它现在可以正常使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM