Java: хороший способ остановить потоковый TCP-сервер?


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

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

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

3 5

3 ответа:

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

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

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

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

Закрытие соединений с клиентами на стороне сервера должно привести к тому, что клиенты получат Эф на их боках, насколько я понимаю.

[править-1]

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

Если есть вероятность, что поток не заблокирован IO, но находится в состоянии ожидания или спит, я думаю, что это по-прежнему рекомендуется, чтобы выдать поток.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
            }
        }
    }