2011-04-11 10 views
5

que tienen la siguiente estructura para la comunicación cliente-servidor TCP:Java: ¿una buena manera de detener el servidor TCP con subprocesos?

  • El servidor de inicio del servidor comienza hilo aceptor, que acepte conexiones cliente y pasa ServerSocket a ella.
  • Cuando llega una conexión de cliente, llamadas hilo aceptador de aceptar() en ServerSocket y sostiene cliente trabajo de procesamiento de subproceso de trabajo (en la piscina ejecutor/hilo) y proporciona socket de cliente a la misma.
  • Trabajador en bucle lee datos de secuencia de socket del cliente, lo procesa y envía respuestas.

La pregunta es cómo detener con gracia todo el sistema? Puedo detener el hilo del aceptador simplemente cerrando ServerSocket. Hará que accept() bloquee la llamada para lanzar SocketException. Pero, ¿cómo detener a los trabajadores? Leen de la transmisión y esta llamada es de bloqueo. De acuerdo con this, las transmisiones no lanzan InterruptedException y, por lo tanto, el trabajador no puede ser interrumpido() 'ed.

Parece que necesito cerrar socket de trabajador de otro hilo, ¿verdad? Para esto, el zócalo debe convertirse en un campo público o se debe proporcionar un método en el trabajador para cerrarlo. ¿Será esto bueno? ¿O puede ser que todo mi diseño sea defectuoso?

Respuesta

3

Su modelo funciona correctamente. La mejor forma de interrumpir construcciones no interrumpibles como IO es cerrar el socket. Por supuesto, puede manejarlo antes de entrar en un estado de bloqueo, pero si las funciones IO no reaccionan a la interrupción realmente no tiene muchas opciones buenas

2

Sugeriría usar un indicador booleano que los trabajadores verifiquen periódicamente. Llame a la bandera shouldStop y si se establece en verdadero, el trabajador limpia y luego muere. Seguir este método le permitiría implementar algún código de limpieza para que no deje recursos colgando, etc.

+0

No puedo marcar esta bandera mientras el trabajador está bloqueado en I/O –

+0

La mejor práctica es usar Thread.interrupted(). Esta es la solución estándar que funcionará correctamente en ThreadPool. –

+0

@Andrey Vityuk Thread.interrupted en realidad no es una buena práctica porque restablece el indicador de interrupción. Puede haber significado Thread.interrupt o Thread.isinterrupted. Dicho esto, como dije en mi respuesta, muchas construcciones de IO no tienen en cuenta la interrupción de la secuencia, por lo que (aunque estoy parcialmente de acuerdo con su comentario) no se cumple en esta situación. –

2

No debe simplemente detener el servidor. El proceso de apagado puede demorar un tiempo mientras se realiza la limpieza, ya que debe garantizar la coherencia.

Imagine un servidor de base de datos, si simplemente lo cierra mientras está realizando transacciones, puede dejar sus datos inconsistentes. Es por eso que normalmente lleva un tiempo apagar el servidor.

  • Primero debe dejar de aceptar nuevas conexiones en el servidor.
  • Luego puede esperar a que subprocesos de trabajo actuales finalicen su trabajo y luego cierren el servidor y cierren oficialmente.
  • O se fuerza a los subprocesos de trabajo cerrar sus conexiones con el cliente
    (probablemente utilizando algún tipo
    de marcar como sugerido). Esto podría implicar algo de limpieza para dejar los datos consistentes, por ejemplo revertir transacciones o cualquier tipo de cambios que haya hecho en archivos o en la memoria.

Cerrar las conexiones con los clientes en el lado del servidor debería hacer que los clientes obtengan un EOF de su lado, por lo que yo entiendo.

[INS-1]

he profundizado un poco más en el tema, simplemente porque no había utilizado tomas en cuando y porque encontré la cuestión interesante. Creo que como han señalado bien otros, la única opción es cerrar el socket, que de acuerdo con Javadocs cerrará automáticamente las secuencias de entrada y salida/

Si hay posibilidades de que el hilo no esté bloqueado por IO , pero en estado de espera o durmiendo, creo que todavía se recomienda emitir un Thread.interrupt() para la hebra de trabajo correspondiente de un socket dado; porque no puede haber certeza del bloqueo del estado de cada hilo.

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 
      } 
     } 
    } 
+0

Entiendo esto muy bien. "Entonces puede esperar a que los hilos de trabajo actuales terminen su trabajo" - si vuelve a leer mi pregunta, verá que esto es un problema. Los trabajadores pueden ser bloqueados leyendo de la corriente de socket. Y la pregunta es cómo interrumpirlos con gracia. La bandera no funcionará aquí porque el trabajador no puede verificarlo mientras está en estado de E/S bloqueado –

+0

"No debe simplemente detener el servidor." En realidad, si tiene el diseño correcto de su servidor (transacciones ACID y todo) simplemente dejarlo funcionará . Se considera que ha ocurrido un cambio que da como resultado una confirmación de la base de datos, y un cambio que no ocurre nunca ocurrió. Hacer que esto funcione bien es una de las buenas razones para separar la base de datos de la lógica de negocios del medio y la interfaz web. –

Cuestiones relacionadas