【问题标题】:Socket server hang when testing with ab, waiting BufferedReader.readline()使用 ab 测试时套接字服务器挂起,等待 BufferedReader.readline()
【发布时间】:2023-04-01 01:12:01
【问题描述】:

我正在制作一个基于 Java 的 Web 服务器。但是当我使用 ApacheBench 对其进行测试时,它有时会停止响应。

在 Macbook Air 上:

ab -n 20000 -c 40 -d  http://localhost:1080/

保证在完成 16400 个或更多请求后超时。

在 Ubuntu 桌面上

ab -n 20000 -c 1000 -d  http://localhost:1080/

大部分时间都可以成功,但有时会在运行几次后停止响应。

我已经确定(使用 Eclipse)当服务器停止响应时,它正在等待 BufferedReader.readline(),我用它来读取 HTTP 请求标头。但我不知道它为什么要等待。

测试代码在这里:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestServer {
    public static void main(String[] args){
        ServerSocket socket = null;

        try{
            socket = new ServerSocket(1080);
            ExecutorService pool = Executors.newFixedThreadPool(10);
            while(true){
                Socket s = socket.accept();
                pool.execute(new RequestHandler(s) );
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }
        finally{
            if(null!=socket){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

class RequestHandler implements Runnable{
    final Socket s;
    public RequestHandler(Socket s) {
        this.s = s;
    }

    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
            String line = br.readLine();

            PrintWriter pw = new PrintWriter(s.getOutputStream());
            pw.print("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n");
            pw.print(line);
            pw.flush();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        finally{
            if(s!=null){
                try {
                    s.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }
}

顺便说一句,在写测试代码的时候,我发现了一些奇怪的地方

如果

BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = br.readLine();

被替换为

String line = "don't read the socket";

ab 将失败并显示以下消息:“apr_socket_recv: Connection denied (111)Connection reset by peer (104)”

但是使用 Firefox 4 打开 localhost:1080 会看到“不读取套接字”的混乱出现。

【问题讨论】:

  • 如果发生异常,您不会关闭套接字。您应该进行更改,以便 s.close 在 finally 块中。 (不确定这是否是问题,但它会帮助解决问题)
  • 在这种情况下没有帮助,但我认为你是对的。代码已更新。

标签: java sockets readline serversocket apachebench


【解决方案1】:

我想知道这是否是 ApacheBench 测试的故意部分:查看您的服务器在打开连接但没有发送数据时的行为。大概 ApacheBench 是开源的,所以你可以看看它是否在 16400 次尝试后调用了一些特殊行为(我的赌注是它打开套接字然后不发送请求)。

无论如何,您可能希望确保在套接字上设置显式超时,以防您的 Java 版本默认为 0 (=infinite)。不要假设每个客户都会表现得完美无缺,并且总是会准确地向您发送您期望的数据。

因此,作为一般规则,您需要确保您的网络服务器不会在“发生异常情况”时发生故障——网络就是这样,有时数据包/连接会随机丢弃,您需要处理用它。操作系统很可能会限制例如连接可以打开多长时间,因此您的服务器可能会突然看到操作系统“从脚下拉扯的地毯”。我想 ApacheBench 测试可能会模拟一些像这样的 gremlins(这甚至可能是您在 Ubuntu 中看到的,尽管 readLine() 挂起可能是模拟在我提到的开放连接上不发送请求)。

【讨论】:

    猜你喜欢
    • 2014-04-12
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-10
    相关资源
    最近更新 更多