2012-07-03 24 views
6

Estoy implementando un servidor de archivos simple usando Java NIO con un subproceso de selección y varios subprocesos de trabajo (para realizar lectura/escritura real).Java NIO SocketChannel.read() con multiproceso

La parte principal del código es el siguiente:

while (true) { 
    int num = selector.select(); 
    if (num > 0) { 
     Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); 
     final SelectionKey key = keys.next(); 
     keys.remove(); 

     if (key.isValid()) { 
      if (key.isAcceptable()) { 
       accept(key); 
      } else if (key.isReadable()) { 
       performReadInWorkerThread (key); 
      } else if (key.isWritable()) { 
       performWriteInWorkerThread (key); 
      } 
     } 
    } 
} 

Como se puede ver en el fragmento de código, cuando se selecciona un canal de lectura/escritura, que sacar datos de lectura/escritura de la rosca selección a un hilo de trabajador.

Ahora el problema es que cuando un canal que se puede leer se transfiere a la cadena de trabajo, y antes de que termine/comienza a leerse desde el canal, el hilo de selección vuelve a funcionar y selector.select() selecciona el canal legible seleccionado previamente (porque todavía hay buffer de entrada en el canal que aún no se ha consumido por la cadena de trabajo previamente asignada), por lo que el canal se transfiere a otra cadena de trabajo, lo que da como resultado múltiples hilos de trabajo que leen el mismo canal.

Creo que este es un problema de diseño. Mi pregunta es cómo puedo garantizar que solo un hilo lea un canal al mismo tiempo.

+0

consulte la sección de lectura de la respuesta ma que puede ayudarlo – Amith

+0

Si desea un servidor de archivos simple, utilizaría el bloqueo de NIO o el bloqueo de IO. En mi humilde opinión, esto es mucho más simple que jugar con selectores. –

Respuesta

6

¿Por qué? La lectura no se bloqueará. Hazlo en el hilo actual. Estás en problemas interminables de esta manera. Deberá cancelar el registro de OP_READ antes de entregar el hilo de lectura, lo cual es bastante fácil, pero la parte difícil es que cuando el hilo de lectura complete la lectura tendrá que volver a registrar OP_READ, lo que requiere ya sea (i) un selector wakeup(), que hace que el hilo de selección se ejecute cuando posiblemente no hay nada que hacer, lo cual es un desperdicio, o bien (ii) utiliza una cola de reinscripciones pendientes, lo que retrasa la siguiente lectura en ese canal hasta después de la próxima vez que el selector se despierta, lo que también es un desperdicio, o si no, debe activar el selector inmediatamente al agregarlo a la cola, lo que también es un desperdicio si no hay nada listo. Nunca he visto una arquitectura NIO convincente que utilizara diferentes hilos de selección y lectura.

No haga esto. Si debe tener multihilo, organice sus canales en grupos, cada uno con su propio selector y su propio hilo, y haga que todos esos hilos hagan su propia lectura.

De forma similar, no es necesario escribir en una secuencia separada. Solo escribe cuando tengas algo para escribir.

+0

Gracias por la explicación. ¿Entonces quieres decir que solo necesito un hilo?¿Qué sucede si tengo que leer y escribir archivos de gran tamaño al mismo tiempo, decir que hay decenas de miles de clientes que solicitan archivos grandes, cómo mi servidor atiende esas solicitudes al mismo tiempo con un solo hilo? – neevek

+0

Neevek, creo que EJP reveló solo el procedimiento de lectura, y su respuesta parece muy razonable. Lees en un solo hilo, pero luego cambias a hilo de trabajo para analizar datos de solicitud. Manejo de archivos de gran tamaño: mejor cambiar a nginx si estos archivos son estáticos, en lugar de hacer que java lo maneje, sin embargo, podría buscar material de cero: http://www.ibm.com/developerworks/library/j-zerocopy/ – user486075

+0

@Neevek EJP es rit. Cuando su conexión es aceptada, cada uno de sus clientes debe actuar como un hilo separado. – Amith

-1

Compruebe el método SelectionKey.cancel().

+0

Necesito la conexión 'keep-alive', haré uso del socket abierto para atender múltiples solicitudes, por lo que no debería cancelar la SelectionKey, todavía tengo que' leer del canal en el futuro'. – neevek

+0

Solo movería el problema a otra parte: ver mi respuesta. – EJP

1

Para NIO, solo tenga en cuenta un principio: Lea/escriba en el hilo de selección principal. Este principio es el reflejo de la naturaleza del hardware. No se preocupe, leer en el hilo de selección principal no es rápido. En un servidor moderno, la CPU siempre es más rápida que la tarjeta de red. Entonces, la lectura sin bloqueo en un hilo es también más rápida que las operaciones de la tarjeta de red. Un hilo ya es suficiente para leer el paquete. No necesitamos más hilos.