2010-03-02 15 views
6

Tengo una situación que parece ajustarse a la situación Async Servlet 3.0/Comet pero todo lo que necesito hacer es devolver un código de respuesta 200 (u otro) después de aceptar los parámetros entrantes.Finalizando un HttpServletResponse pero continúo procesando

¿Hay alguna manera para que HttpServlet complete el protocolo de solicitud/respuesta http y continúe procesando?

Algo así como ...

doPost(req, response) { 
    // verify input params... 
    response.setStatus(SC_OK); 
    response.close(); 
    // execute long query 
}  

EDIT: Mirando el paquete javax.servlet - el fraseo adecuado para mi pregunta es

¿Cómo cometer una respuesta?

como en Servlet.isCommitted()

+0

"comprometidos" significa una parte de la secuencia de respuesta ya ha sido enviado al cliente. No puede ser retirado o modificado.Puede suceder tan pronto como establezca un encabezado o escriba en la respuesta, en teoría, pero generalmente el contenedor se almacena temporalmente, por lo que ocurre un poco más tarde. Esto no es relevante para usted, creo. No cierre las secuencias de respuesta. Simplemente configure el estado, inicie un hilo según otras respuestas, termine doPost(). –

+0

Traté de usar response.sendError (SC_OK) pero todavía no resolvió la respuesta al solicitante. Parece que la única forma de cerrar una solicitud de servlet es regresar desde un doPost(). – Stevko

Respuesta

6

Así es como he manejado esta situación:

  1. Cuando la aplicación se inicia, crea una ExecutorService con Executors.newFixedThreadPool(numThreads) (hay otros tipos de ejecutores, pero sugieren comenzar con éste)
  2. En doPost(), crear una instancia de Runnable que llevará a cabo el procesamiento deseado - su tarea - y enviarlo a la ExecutorService así: executor.execute(task)
  3. Por último, se debe devolver el estado HTTP 202 Accepted, y, si es posible, un Location encabezado que indica dónde un cliente podrá verificar el estado del procesamiento.

I altamente recomiendo que lea Java Concurrency in Practice, es un libro fantástico y muy práctico.

+0

Si bien estoy de acuerdo con todas las respuestas, esta tiene la menor complejidad involucrada. Gracias a pajton, beny23 y avi por su aporte. – Stevko

+0

¿cómo hago esto, si quiero usar executor.submit() en lugar de .execute? Quiero usar .submit para poder obtener "futuros" y saber cuándo se han completado todas las tareas (en función del cual tengo que hacer cierto procesamiento). – Tintin

3

Puede continuar con el procesamiento en un hilo separado.

La respuesta se confirma una vez que regrese del método doPost().

+0

Me encuentro con el servicio de obtención de URL 5 segundos de tiempo de espera en solicitudes perfectamente buenas originadas en el motor de una aplicación de Google. No es necesario que el Solicitante de App Engine bloquee la espera de una respuesta correcta de algo que puede tomar mucho tiempo para completar el procesamiento. – Stevko

+0

Ok, entonces todo lo que necesitas es solo un hilo de fondo :). – pajton

3

La posibilidad de que su servlet acepte una solicitud de procesamiento en segundo plano, es que el servlet transfiera el procesamiento a un subproceso separado que luego se ejecuta en segundo plano.

Usando Spring, puede invocar un Thread separado usando el a TaskExecutor. La ventaja de utilizar Spring sobre el estándar JDK 5 java.util.concurrent.Executor es que si se encuentra en servidores de aplicaciones que necesitan utilizar hilos gestionados (IBM websphere o Oracle weblogic), puede usar el WorkManagerTaskExecutor para enganchar en los gestores de trabajo CommonJ.

Otra alternativa sería mover la lógica de consulta larga en un Bean controlado por mensaje o POJO controlado por mensaje (Spring JMS puede ayudar aquí) y dejar que el servlet simplemente publique un mensaje en una cola JMS. Eso tendría la ventaja de que si la carga en su contenedor web se volviera demasiado grande debido a su larga consulta, podría mover fácilmente el MDB a un sistema diferente (dedicado).

0

Este ejemplo puede ayudar

void doPost(){ 
    // do something 
    final ExecutorService executor = Executors.newSingleThreadExecutor(); 
     executor.execute(new Runnable() { 
      @Override 
      public void run() { 
       // processing after response 
      } 
     });} 
+1

¿Podría ampliar esto? –

+0

Es mejor no instanciar un ExecutorService cada vez que el servlet recibe una solicitud o de lo contrario comenzará a crear una gran cantidad de subprocesos que nunca mueren, al menos según la nueva documentación de SingleThreadExecutor(). –

Cuestiones relacionadas