Сервер сокетов зависает при тестировании с помощью ab, ожидая BufferedReader.readline()

Я делаю веб-сервер на основе Java. Но когда я тестирую его с помощью 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();
                }
            }
        }
    }
}

Кстати, при написании тестового кода я обнаружил еще кое-что странное

If

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, чтобы увидеть беспорядок «не читать сокет».


person CQD    schedule 08.04.2011    source источник
comment
Вы не закрываете свои сокеты в случае исключения. Вы должны изменить так, чтобы s.close находился в блоке finally. (не уверен, что это проблема, но это поможет прояснить ситуацию)   -  person Jeff Foster    schedule 08.04.2011
comment
Не помогает в этом случае, но я думаю, что вы правы. Код обновлен.   -  person CQD    schedule 08.04.2011


Ответы (1)


Интересно, является ли это преднамеренной частью теста ApacheBench: посмотреть, как ведет себя ваш сервер, когда к нему открыто соединение, но данные не отправляются. Предположительно ApacheBench имеет открытый исходный код, поэтому вы можете посмотреть, вызывает ли он какое-то особое поведение (я ставлю на то, что он открывает сокет, а затем не отправляет запрос) после 16400 попыток.

В любом случае вы, вероятно, захотите убедиться, что вы установили явный тайм-аут для сокета на случай, если ваша версия Java по умолчанию равна 0 (= бесконечность). Не думайте, что каждый клиент будет вести себя идеально и всегда отправлять вам именно те данные, которые вы ожидаете.

Таким образом, как правило, вам нужно убедиться, что ваш веб-сервер не упадет, если «произойдет что-то необычное» - сети такие, и иногда пакеты / соединения будут случайным образом сбрасываться, и вам нужно иметь дело с этим. Операционные системы вполне могут налагать ограничения, например, на как долго может быть открыто соединение, чтобы ваш сервер мог внезапно увидеть «ковер, вытащенный из-под ног» операционной системой. Я предполагаю, что тест ApacheBench может имитировать несколько таких гремлинов (что может быть даже тем, что вы видите в Ubuntu, хотя зависание readLine(), вероятно, является симуляцией отсутствия отправки запроса по открытому соединению, как я уже упоминал).

person Neil Coffey    schedule 08.04.2011