使用ab进行测试时,套接字服务器挂起,等待BufferedReader.readline()

时间:2011-04-08 15:33:51

标签: java sockets readline serversocket apachebench

我正在制作一个基于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();
                }
            }
        }
    }
}
BTW,在编写测试代码时,我发现了其他一些奇怪的东西

如果

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

替换为

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

ab将失败并显示以下消息:“apr_socket_recv:连接被拒绝(111)由对等方(104)重置连接”

但打开localhost:1080与Firefox 4会看到“不读取套接字”乱七八糟的显示。

1 个答案:

答案 0 :(得分:1)

我想知道这是否是ApacheBench测试的一个有意识的部分:了解服务器在打开连接但未发送数据时的行为方式。据推测,ApacheBench是开源的,所以你可以看看它是否会在16400次尝试之后调用某些特殊行为(我打赌是打开套接字然后不发送请求)。

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

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