2011-04-08 6 views
0

Я делаю веб-сервер на основе Java. Но когда я тестирую его с ApacheBench, он иногда перестает отвечать.Socket server зависает при тестировании с ab, ожидая BufferedReader.readline()

На 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-запроса. Но я понятия не имею, почему он ждет.

код Test здесь:

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: Соединение отклонено (111) соединения сброса одноранговой (104)»

Но открыт локальный: 1080 с Firefox 4 будет видеть «не читать гнездо» беспорядок показать ,

+1

Вы не закрываете свои сокеты в случае исключения. Вы должны изменить так, чтобы 's.close' был в конечном блоке. (не уверен, что это проблема, но это поможет мне разобраться) –

+0

Не помогая в этом случае, но я думаю, что вы правы. Код обновлен. – CQD

ответ

1

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

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

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