2011-11-15 14 views
5

Tengo un servidor implementado en netty que maneja una solicitud de usuario, habla con un middleware y envía una respuesta. Se espera que la E/S sea despreciable en comparación con el viaje de ida y vuelta al middleware, por lo que para minimizar el bloqueo tengo un ExecutionHandler en la tubería sobre un OrderedMemoryAwareThreadPoolExecutor. Sin problemas hasta ahora.Priorizar tareas de Netty en ThreadPoolExecutors

Estoy investigando cómo se comporta el servidor con mucha carga. De la experiencia pasada con nuestro protocolo, tendemos a quedar abrumados por los ataques accidentales de DOS, la mayoría de las veces, el guión de un usuario está atrapado en un ciclo infinito o algo similar. Idealmente, podríamos des-priorizar sus canales una vez que pasen cierto umbral de uso, para que el servicio de otros usuarios no se vea afectado.

Implementé un ThreadPoolExecutor simple que usa un PriorityBlockingQueue y establece la prioridad en función de los datos extraídos de nuestra propia clase Session (adjunta al contexto en ChannelHandler). De nuevo, no hay problemas hasta el momento.

La dificultad surge cuando se trata de aprovechar el orden y la conciencia de la memoria de los ThreadPoolExecutors incorporados de netty. Lo ideal es que MyThreadPoolExecutor extienda OrderedMemoryAwareThreadPoolExecutor y establezca las tareas de cola de prioridad. Por desgracia, esto no es posible por dos razones: privada y final. En más detalle:

a) ThreadPoolExecutor.workQueue se puede ajustar en su constructor, pero MemoryAwareThreadPoolExecutor duros códigos de esto como un LinkedTransferQueue, y no exponga este a su hijo OrderedMemoryAwareThreadPoolExecutor (es decir MyThreadPoolExecutor no tiene acceso al conjunto eso). Si es necesario, esto se puede superar con un poco de ajuste de campo privado basado en la reflexión.

b) Me gustaría poder anular MyThreadPoolExecutor.doUnorderedExecute(), de modo que pueda insertar el manejo de prioridad y construir los objetos necesarios, pero se declara final. El código que lo llama no necesita ser cambiado.

El resultado es que para mantener todas las características de netty, pero use una cola de prioridad, tendría que copiar tanto OrderedMemoryAwareThreadPoolExecutor como MemoryAwareThreadPoolExecutor, ajustar un par de líneas de cada una, y luego extender desde allí. ¡Esto no me parece una buena práctica de codificación! Incluso considerando que hace sonar las alarmas.

ahora a algunas preguntas:

1) ¿Estoy resolviendo el problema equivocado? ¿Estoy ladrando completamente del árbol equivocado por lo que quiero lograr?

2) De lo contrario, ¿existe una forma mejor de hacer esto que las mencionadas anteriormente?

3) El enfoque anterior conlleva un riesgo de inanición para las tareas sin prioridad donde la carga total del servidor está constantemente en su capacidad. Estoy dispuesto a tolerar esto para los usuarios 'traviesos', pero tan pronto como vuelvan al estado normal, sus tareas existentes seguirán muriendo de hambre, y para preservar el orden, cualquier tarea nueva y de mayor prioridad debe agregarse detrás de ellas. ¿Tiene alguna recomendación sobre la mejor manera de lidiar con esto? (Prohibir a los usuarios no está permitido por la empresa.)

4) Esta es la mitad de la pregunta, la mitad de la retroalimentación. La documentación de Netty para OrderedMemoryAwareThreadPoolExecutor tiene un práctico diagrama para el subproceso X & Y - ¿presumiblemente estos son los subprocesos agrupados en ThreadPoolExecutor y no en subprocesos de trabajo de E/S? Puede valer la pena aclarar esto. Además, cuando no se utiliza un ExecutionHandler, cada canal está vinculado a un solo hilo de trabajo de E/S. ¿Sigue siendo así cuando está detrás de ExecutionHandler? es decir, ¿el orden en que se agregan las tareas a ExecutionHandler garantiza que será el mismo que el orden en que llegan al Canal?Si este es el caso, entonces no puedo ver cómo Thread X en los documentos para MemoryAwareThreadPoolExecutor podría procesar el evento 2 antes del evento 1 - Acepto que aquí diferentes hilos pueden terminar de trabajar en cualquier orden, pero no puedo ver cómo puede ser el trabajo. asignado al mismo hilo fuera de servicio (sale de workQueue). Los documentos en ExecutionHandler insinúan esto, pero se beneficiarían de un poco más de detalle.

Muchas gracias por leer, y cualquier ayuda es muy apreciada.

Respuesta

0

Su solución de "alarma de alarma" parece familiar: simplemente comience con un tamaño de grupo pequeño.

Parece que dice que necesita una solución para un servidor lento. Es posible que desee ver por qué su servidor se ralentiza bajo carga. - problemas de contención de hilos (raza). - tamaños de grupo inicial - configuración de GC

+2

Este servidor todavía está en desarrollo, pero el (C++) que está reemplazando está fuertemente vinculado a la base de datos. Solucionamos la posibilidad de que una gran carga borrara la base de datos (v. Malas noticias) con un grupo limitado de hilos de trabajo, pero luego admitió la posibilidad de que un usuario hambriento muerda a todos los trabajadores y empeore el servicio de todos los demás (tenemos SLAs muy ajustados para mantener). Nuestra solución a este problema en el servidor anterior se basaba en suposiciones que no se sostienen en netty, por lo que tenemos que encontrar algo mejor, de ahí la pregunta. ¡No nos preocupamos por el tamaño de la piscina todavía! – Liche

3

1) No, su idea está bien. Es solo OrderedMemoryAwareThreadPoolExecutor que carece de dicha función. ¿Podrías file an issue?

2) Me gustaría simplemente teclear OrderedMemoryAwareThreadPoolExecutor, simplificarlo y agregar la cola de prioridad. De esta forma, tiene mucho más control sobre cómo se procesan los elementos de la cola (eventos).

3) En lugar de utilizar una cola de prioridad, puede tener dos colas: una para elementos de alta prioridad y la otra para elementos de menor prioridad. El hilo podría procesar primero la cola de alta prioridad, pero puede controlar el ciclo para que no permanezca demasiado tiempo allí.

4) Sí, son los hilos de ThreadPoolExecutor. Si no está claro, debemos actualizar nuestra documentación. Por favor, siéntase libre de presentar un problema o contribuir directamente al bifurcarlo.

+0

Gracias, me aprovecharé de estos cambios, y si obtengo el permiso de mi empleador, lo enviaré al proyecto a su debido tiempo. – Liche

+0

Sugiero una solución más elegante que dos colas o PriorityQueue. OMATPE se implementa de manera tal que la cola de tareas de un canal se entrega, a través de la workQueue principal, a un Worker, que ejecuta todas las tareas de ese canal (y las que se agreguen mientras tanto) antes de regresar y obtener el trabajo de otro canal. Por lo tanto, un canal ocupado puede monopolizar a un trabajador. Por lo tanto, para un canal de baja prioridad, procese hasta un número fijo de tareas, luego empújelo nuevamente en la cola de la workQueue. Por lo tanto, se procesa sin inanición, pero con un tope a la cantidad que puede acumular recursos. (Sobrecarga adicional, sin embargo) – Liche