Java: хороший способ остановить многопоточный TCP-сервер? - PullRequest
5 голосов
/ 11 апреля 2011

У меня есть следующая структура для связи клиент-сервер TCP:

  • При запуске сервера запускается сервер поток акцептора, который принимает клиента соединения и проходит ServerSocket к этому.
  • Когда приходит клиентское соединение, вызовы потока акцептора accept () на ServerSocket и отправляет клиент обработка задания в рабочий поток (по исполнителю / пулу потоков) и предоставляет клиентский сокет к нему.
  • Рабочий в цикле читает данные из поток клиентских сокетов, обрабатывает его и отправляет ответы.

Вопрос в том, как изящно остановить всю систему? Я могу остановить поток акцептора, просто закрыв ServerSocket. Это вызовет блокирующий вызов accept () для выдачи SocketException. Но как остановить рабочих? Они читают из потока, и этот вызов блокируется. Согласно этот поток не генерирует исключение InterruptedException, и, следовательно, рабочий не может быть прерван ()

Похоже, мне нужно закрыть рабочий сокет из другого потока, верно? Для этого сокет должен быть открытым полем или в работнике должен быть предусмотрен метод для его закрытия. Это будет хорошо? Или, может быть, весь мой дизайн имеет недостатки?

Ответы [ 3 ]

3 голосов
/ 11 апреля 2011

Ваша модель работает надлежащим образом. Лучший способ прервать непрерывные конструкции, такие как IO, - закрыть сокет. Конечно, вы можете справиться с этим до того, как перейдете в состояние блокировки, но если функции ввода-вывода не реагируют на прерывание, у вас действительно нет много хороших вариантов

2 голосов
/ 11 апреля 2011

Вы не должны просто останавливать сервер. Процесс выключения может занять некоторое время, пока происходит очистка, поскольку необходимо обеспечить согласованность.

Представьте себе сервер базы данных, если вы просто выключите его во время выполнения транзакций, вы можете оставить его данные несогласованными. Вот почему обычно требуется некоторое время для выключения сервера.

  • Вы должны сначала прекратить принимать новые соединения на сервере.
  • Тогда вы можете подождать текущие рабочие потоки, чтобы закончить их работа, а затем закройте сервер и завершение работы официально.
  • Или вы заставляете рабочие потоки закрыть свои связи с
    клиент (возможно, использующий что-то вроде
    флага как предложено). Это может подразумевает некоторую очистку, чтобы оставить данные последовательный, например, возврат трансакции или любые изменения Вы сделали в файлах или в памяти.

Насколько я понимаю, закрытие соединений с клиентами на стороне сервера должно привести к тому, что клиенты получат EOF на своих сторонах.

[EDIT-1]

Я немного углубился в эту проблему, просто потому, что некоторое время не использовал сокеты и потому, что мне этот вопрос показался интересным. Я думаю, как уже было отмечено другими, единственный вариант - закрыть сокет, который, согласно полученным Javadocs, автоматически закроет входной и выходной потоки /

Если есть вероятность, что поток не заблокирован IO, но находится в состоянии ожидания или в спящем режиме, я думаю, что все же рекомендуется выполнить Thread.interrupt () для соответствующего рабочего потока данного сокета; потому что не может быть уверенности в блокировке состояния каждого потока.

public static class IOServerWorker implements Runnable{

        private Socket socket;

        public IOServerWorker(Socket socket){
            this.socket = socket;
        }

        @Override
        public void run() {
            String line = null;
            try{
                BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while( (line = reader.readLine())!=null){
                    System.out.println(line);
                }
                reader.close();
            }catch(IOException e){
                //TODO: do cleanup here
                //TODO: log | wrap | rethrow exception
            }
        }
    }
2 голосов
/ 11 апреля 2011

Я бы предложил использовать логический флаг, который рабочие периодически проверяют. Вызовите флаг shouldStop, и если он установлен в значение true, работник очищается, а затем умирает. Следование этому методу позволит вам реализовать некоторый код очистки, чтобы не оставлять ресурсы зависшими и т. Д.

...