Связь через сокет: поток зависает - PullRequest
0 голосов
/ 25 января 2012

У меня есть простой сервер сокетов (он для связи HL7). Когда он работает дольше в производственном процессе, потоки сокетов зависают и занимают много процессорного времени.

Это соответствующий код (сокращенный) для потока слушателя:

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 с, что приводит к повторному вызову метода. Почему это съедает все процессорное время?

У вас есть какой-нибудь совет?

Заранее спасибо, Matthias

1 Ответ

2 голосов
/ 25 января 2012

Одна вещь, которую я сразу вижу, это:

         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);

- приведение inputReader.read () к char. BufferedReader.read () возвращает int, значение со знаком. Вы приводите его к символу, который является значением без знака, отбрасывая отрицательный знак, если он есть, сужающее преобразование. Тогда преобразование в короткое не возвращает отрицательный знак, если он был. Попробуйте переписать как:

         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; Ваш код компилируется?

...