2010-03-02 8 views
6

Ich habe eine Situation, die scheint die Async Servlet 3.0/Comet-Situation zu passen, aber alles, was ich tun muss, ist ein 200 Antwortcode (oder andere) nach dem Akzeptieren der eingehenden Parameter.Beenden einer HttpServletResponse, aber weiterverarbeitung

Gibt es eine Möglichkeit für ein HttpServlet, den HTTP-Request/Response-Handshake abzuschließen und trotzdem mit der Verarbeitung fortzufahren? wie

Etwas ...

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

EDIT: am javax.servlet Paket suchen - die richtige Phrasierung auf meine Frage ist

Wie lege ich eine Antwort?

wie in Servlet.isCommitted()

+0

„Committed“ bedeutet, hat einen Teil der Antwortstream bereits an den Client gesendet wurde. Es kann nicht zurückgenommen oder geändert werden.Es kann passieren, sobald Sie einen Header oder schreiben Sie auf die Antwort, in der Theorie, aber in der Regel die Container-Puffer, so dass es etwas später passiert. Das ist nicht relevant für Sie, denke ich. Schließen Sie die Antwortstreams nicht. Setzen Sie einfach den Status, starten Sie einen Thread wie andere Antworten, lassen Sie doPost() fertig. –

+0

Ich habe versucht, response.sendError (SC_OK) zu verwenden, aber die Antwort wurde dem Requestor immer noch nicht geleert. Es scheint, dass die einzige Möglichkeit, eine Servlet-Anfrage zu schließen, darin besteht, von einem doPost() zurückzukommen. – Stevko

Antwort

6

Hier ist, wie ich diese Situation behandelt haben:

  1. Wenn die App gestartet wird, erstellen Sie ein ExecutorService mit Executors.newFixedThreadPool(numThreads) (es gibt auch andere Arten von Vollstrecker, aber ich schlage vor, beginnend mit dieser)
  2. In doPost(), eine Instanz von Runnable, die die gewünschten Verarbeitung durchführen werden - Ihre Aufgabe - und legt ihn die ExecutorService etwa so: executor.execute(task)
  3. Schließlich sollten Sie den HTTP-Status 202 Accepted zurückkehren und, wenn möglich, ein Location Header, der angibt, wo ein Client den Status der Verarbeitung überprüfen kann.

I sehr empfehlen Sie Java Concurrency in Practice lesen, es ist ein fantastisches und sehr praktisches Buch.

+0

Während ich mit allen Antworten einverstanden bin, hat diese die geringste Komplexität. Danke an Pajton, Beny23 und Avi für Ihre Eingabe. – Stevko

+0

Wie mache ich das, wenn ich executor.submit() anstelle von .execute verwenden möchte? Ich möchte .submit verwenden, damit ich "Futures" ergattern kann und weiß, wann alle Aufgaben erledigt sind (anhand derer ich bestimmte Bearbeitungen vornehmen muss?). – Tintin

3

Sie Verarbeitung in einem separaten Thread fortgesetzt werden kann.

Die Antwort wird ausgeführt, sobald Sie von der Methode doPost() zurückkehren.

+0

Ich stoße bei vollkommen guten Anforderungen, die von einer Google App-Engine stammen, auf den URL-Abruf-Dienst, der 5 Sekunden lang war. Der App Engine-Anforderer muss nicht auf das Warten auf eine OK-Antwort von etwas warten, das sehr lange dauert, bis die Verarbeitung abgeschlossen ist. – Stevko

+0

Ok, also alles was du brauchst ist nur ein Hintergrund-Thread :). – pajton

3

Eine Möglichkeit, dass Ihr Servlet eine Anforderung zur Verarbeitung im Hintergrund akzeptiert, besteht darin, dass das Servlet die Verarbeitung an einen separaten Thread weitergibt, der dann im Hintergrund ausgeführt wird.

Mit Spring können Sie einen separaten Thread mit der a TaskExecutor aufrufen. Der Vorteil der Verwendung von Spring gegenüber dem Standard-JDK 5 java.util.concurrent.Executor besteht darin, dass Sie, wenn Sie sich auf Anwendungsservern befinden, die verwaltete Threads verwenden müssen (IBM websphere oder Oracle weblogic), die WorkManagerTaskExecutor verwenden können, um sich in die CommonJ-Arbeitsmanager einzuklinken.

Eine andere Alternative wäre, die lange Abfragelogik in eine Message Driven Bean oder Message Driven POJO zu verschieben (Spring JMS kann hier helfen) und lassen Sie das Servlet einfach eine Nachricht in einer JMS-Warteschlange. Das hätte den Vorteil, dass Sie die MDB problemlos auf ein anderes (dediziertes) System verschieben können, sollte die Auslastung Ihres Webcontainers aufgrund der langen Abfrage zu groß werden.

0

Dieses Beispiel

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

Könnten Sie das weiter ausführen? –

+0

Es ist besser, einen ExecutorService nicht jedes Mal zu instanziieren, wenn das Servlet eine Anfrage erhält, oder Sie beginnen mit der Erstellung vieler Threads, die niemals abstürzen, zumindest in der newSingleThreadExecutor() - Dokumentation. –

Verwandte Themen