Бесконечный цикл где-то в моем коде - PullRequest
2 голосов
/ 31 августа 2011

У меня есть этот игровой сервер Java, который обрабатывает до 3000 TCP-соединений, каждый игрок или каждое TCP-соединение имеет свой собственный поток, каждый поток выглядит примерно так:

public void run()
{
    try
    {
        String packet = "";
        char charCur[] = new char[1];

        while(_in.read(charCur, 0, 1)!=-1 && MainServer.isRunning)
        {
            if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r')
            {
                packet += charCur[0];
            }else if(!packet.isEmpty())
            {
                parsePlayerPacket(packet);
                packet = "";
            }
        }

        kickPlayer();

    }catch(IOException e)
    {
        kickPlayer();
    }catch(Exception e)
    {
        kickPlayer();
    }
    finally
    {
        try{
            kickPlayer();
        }catch(Exception e){};

        MainServer.removeIP(ip);
    }
}

Код работает нормально,и я знаю, что каждая нить для каждого игрока - плохая идея, но я пока тоже буду придерживаться ее.Сервер работает нормально на быстрой машине (6cor x2, 64 бита, 24 ГБ ОЗУ, Windows Server 2003).

Но в какой-то момент, после 12 часов работы UpTime, сервер начинает зацикливаться где-то ...Знайте, потому что процесс Java занимает 99% ЦП бесконечно до следующей перезагрузки.И мне трудно профилировать приложение, потому что я не хочу беспокоить игроков.Профилировщик, который я использую (visualvm), всегда заканчивает кешью сервера, не сообщая мне, в чем проблема.

В любом случае, в этом фрагменте кода выше, я думаю, возможно, проблема в следующем:

while(_in.read(charCur, 0, 1)!=-1)

(_in - это BufferedReader клиентского сокета).

Возможно ли, что _in.read() может бесконечно возвращать что-то еще, что будет поддерживать мой код в рабочем состоянии и брать 99% ресурсов?Что-то не так с моим кодом?Я не все понимаю, я написал только половину.

Ответы [ 3 ]

2 голосов
/ 31 августа 2011

Чтение по одному символу за раз почти так же медленно, как построение строки с + =. Я не смогу сказать тебе, что хуже. Меня не удивило бы, если бы одно соединение связывало все ядро, используя этот подход.

Самое простое «исправление» - использование BufferedReader и StringBuilder.

Однако наиболее эффективный способ чтения данных - это чтение байтов в ByteBuffer и анализ "строк". Я предполагаю, что вы получаете текст ASCII. Вы можете написать синтаксический анализатор, чтобы иметь возможность обрабатывать содержимое и конец строки в один этап (т. Е. С одним проходом данных)

Используя последний подход, вот пример (включая код), где я анализирую сообщение XML из сокета и отвечаю в XML. Типичная задержка составляла 16 микросекунд, а пропускная способность составляла 264K в секунду.

http://vanillajava.blogspot.com/2011/07/send-xml-over-socket-fast.html


Вы можете сделать что-то вроде следующего, что может быть достаточно быстрым

BufferedReader br = new BufferedReader(_in);
for(String line; ((line = br.readline()) != null;) {
    if(line.indexOf('\0') >= 0)
       for(String part: line.split("\0"))
          parsePlayerPacket(part);
    else
       parsePlayerPacket(line);
}

Если вы находите это решение очень простым и вы знакомы с ByteBuffer, вы можете рассмотреть возможность их использования.

0 голосов
/ 31 августа 2011

Эта обработка исключений просто повредила мне глаза. Нет смысла вызывать kickPlayer () внутри блоков catch, так как вы вызываете его снова в finally. Наконец выполняется (почти) всегда.

А теперь о твоей проблеме, забудь мой предыдущий ответ, я немного заснул XD. Я не вижу ничего склонного к зацикливанию в опубликованном цикле while. InputStream.read () либо возвращает -1, когда данных больше нет, либо выдает исключение. Проблема должна быть в другом коде, или, возможно, проблема с многопоточностью.

Как они говорили вам в других ответах, попробуйте использовать буферизованные потоки, читая блок символов вместо только одного за раз, и замените конкатенацию строк для метода добавления StringBuilder. Это должно улучшить производительность, но не уверен, решит ли это проблему (возможно, оно появится через 24 часа вместо 12).

0 голосов
/ 31 августа 2011

У меня была такая же проблема в одном из моих приложений, которые я написал.Мое приложение заняло 50% процессорного времени (в двухъядерном процессоре).

То, что я сделал тогда, чтобы решить проблему, это разрешить потоку спать 1 timetick

Thread.sleep(1);

Надеюсь, это полезноВы

редактировать:

о, и для чего это?
}catch(IOException e)<br> {<br> kickPlayer();<br> }catch(Exception e)<br> {<br> kickPlayer();<br> }

Я думаю, вам не нужен IOException Catch (Exception catch, ловитвсе исключения)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...