2012-01-25 14 views
0

シンプルなソケットサーバー(HL7通信用)があります。プロダクションの実行時間が長くなると、ソケットスレッドがハングアップし、CPU時間を大量に消費します。ソケット通信:スレッドがハングする

これは、リスナー・スレッドに関連するコード(短縮)である:

public void run() { 
    try { 
     serverSocket = new ServerSocket(port, backlog, bindAddress); 
     serverSocket.setSoTimeout(timeout); // 1000 ms 
     do { 
      Socket socket = null; 
      try { 
       socket = serverSocket.accept(); 
      } catch (SocketTimeoutException to) { 
       socket = null; 
      } catch (InterruptedIOException io) { 
       socket = null; 
      } catch (IOException e) { 
       logger.fatal("IO exception while socket accept", e); 
       socket = null; 
      } 

      try { 
       if (socket != null) 
        processConnection(socket); 
      } catch (RuntimeException e) { 
       logger.fatal("caught RuntimeException trying to terminate listener thread", e); 
      } 
     } while (running); 
    } catch (IOException e) { 
     logger.fatal("error binding server socket - listener thread stopped", e); 
    } 
} 

このコードは、着信接続を処理するための新しいスレッドを開始:

protected void processConnection(Socket socket) { 
    Hl7RequestHandler requestHandler = createRequestHandler(); 
    requestHandler.setSocket(socket); 
    requestHandler.start(); 
} 

これは要求するためのコードでありますハンドラスレッド(keepAliveはtrueに設定されています):

public void run() { 
    try { 
     setName("Hl7RequestHandler-" + socket.getPort()); 
     processRequest(); 
    } catch (IOException e) { 
     logger.fatal("IO exception during socket communication", e); 
    } 
} 

public void processRequest() 
throws IOException { 
    socket.setSoTimeout(socketTimeout); // 1000 ms 

    InputStream inputStream = socket.getInputStream(); 
    OutputStream outputStream = socket.getOutputStream(); 

    BufferedReader inputReader = new BufferedReader(new InputStreamReader(inputStream, encoding)); 
    Writer outputWriter = new OutputStreamWriter(outputStream, encoding); 

    int timeouts = 0; 
    boolean failure = false; 
    do { 
     StringBuilder message = new StringBuilder(); 
     try { 
      char c; 
      do { 
       c = (char)inputReader.read(); 
       if ((c == CARRIAGE_RETURN || c == START_OF_MESSAGE) && 
        message.length() == 0) 
       else if (c != END_OF_MESSAGE && ((short)c) != -1) 
        // ein Byte "Nutzlast" 
        message.append(c); 
      } while (c != END_OF_MESSAGE && ((short)c) != -1); 
     } catch (SocketTimeoutException te) { 
      timeouts++; 
      if(!keepAlive && timeouts >= 3) { 
       socket.close(); 
       return; 
      } 
     } 

     String messageStr = message.toString(); 
     if (messageStr.length() == 0) 
      continue; 

     failure = !processMessage(messageStr, outputWriter); 
     outputWriter.flush(); 
     outputStream.flush(); 

     // nächste Runde? 
     if (!keepAlive || failure) 
      socket.close(); 
    } while (keepAlive && !failure); 
} 

これをテストするとローカルではうまくいきます。

しかし、実際には、「ハングする」リクエストハンドラスレッドが複数あります。 "Keep Alive"は、より多くのメッセージを待って接続を開いたままにしておくことです。 (私は常に新しい接続を開くことを避けるために)私はinputReader.read()が-1のタイムアウトの後に-1を返すと仮定します。その結果、メソッドが再び呼び出されます。なぜこれですべてのCPU時間が消費されますか?

アドバイスをいただけましたか?事前に

おかげで、 マティアス私はストレートオフに見ることができます

答えて

2

一つはこれです:

  char c; 
     do { 
      c = (char)inputReader.read(); 
      if ((c == CARRIAGE_RETURN || c == START_OF_MESSAGE) && 
       message.length() == 0) 
      else if (c != END_OF_MESSAGE && ((short)c) != -1) 
       // ein Byte "Nutzlast" 
       message.append(c); 
     } while (c != END_OF_MESSAGE && ((short)c) != -1); 

はcharへのinputReader.read()のキャストです。 BufferedReader.read()は、符号付きのint値を返します。符号なしの値であるcharにキャストし、負の符号があればそれを破棄して、変換を狭めます。次に、短く変換すると、負の符号があれば元に戻すことはできません。私はあなたのループをもう一度見て撮影したと私は混乱している

  char c; 
     int val; 
     do { 
      val = inputReader.read(); 
      // do this if you want, you don't have to 
      c = (char) val; 
      if ((c == CARRIAGE_RETURN || c == START_OF_MESSAGE) && 
       message.length() == 0) 
      else if (c != END_OF_MESSAGE && ((short)c) != -1) 
       // ein Byte "Nutzlast" 
       message.append(c); 
     } while (c != END_OF_MESSAGE && val != -1); 

: として書き換えてください。

 char c; 
     do { 
      c = (char)inputReader.read(); 
      if ((c == CARRIAGE_RETURN || c == START_OF_MESSAGE) && 
       message.length() == 0) 
      else if (c != END_OF_MESSAGE && ((short)c) != -1) 
       // ein Byte "Nutzlast" 
       message.append(c); 
     } while (c != END_OF_MESSAGE && ((short)c) != -1); 

if文のロジックが混乱しています(少なくとも私に)。 最初のif節にはステートメントがありません。空のステートメントではありません。 {}またはaを指定する必要があります。あなたのコードはコンパイルされますか?

+0

お返事ありがとうございます。私は確かにそれを変更します。しかしそれは時間消費を説明するのだろうか? –

+0

あなたが提案したコードを変更しました。状況は同じです。 - inputReader.read()は-1を返し、直ちにもう一度呼び出されます。私は2つのリクエストハンドラスレッドで終わります。最初の人は喜んで(単一の)ピアと連携し、メッセージを受信して​​処理します。 2つ目は100%のCPUとループを無限に食べる(cpu load 1.0)。しばらくすると(おそらくピアが終了する)3番目のスレッドが表示され、さらにCPU負荷が増加します。 –

+0

多分socket.setKeepAlive(true)を試してみます –