2011-09-02 20 views
12

Mi chiedo come Tomcat 7 implementa l'elaborazione asincrona. Comprendo che il thread di richiesta ritorna immediatamente, consentendo al thread di richiesta di ascoltare immediatamente una nuova richiesta e rispondervi.Elaborazione asincrona Tomcat 7

Come viene gestita la richiesta "asincrona"? Esiste un pool di thread separato che gestisce richieste asincrone? Presumo che il blocco dell'IO venga gestito utilizzando qualcosa come java.nio.Selector per le prestazioni. Che dire dei thread che bloccano i calcoli della CPU?

+0

Si consiglia di dare un'occhiata a questo: http://stackoverflow.com/questions/ 7749350/illegalstateexception-not-supported-on-asynccontext-startasyncreq-res se si desidera implementare l'elaborazione asincrona su Tomcat 7. C'è una svolta. – JVerstry

risposta

37

Stai mescolando concetti diversi. È necessario distinguere tra:

  1. asincrono richiesta consegna come da Servlet 3.0; un'API che consente di disaccoppiare una richiesta di servlet in arrivo dal pool di thread del contenitore Web. Non crea alcun thread al volo. È sull'utente dell'interfaccia implementare una soluzione multi-thread corretta. Non si riferisce a IO non bloccanti.
  2. Pool di thread; fornisce un meccanismo per ottenere e gestire i thread in un contenitore web. Per quanto riguarda la richiesta asincrona di richieste hai 2 opzioni. È possibile definire il proprio ExecutorService e utilizzarlo per elaborare ulteriormente la richiesta oppure è possibile creare un nuovo Runnable e inviarlo allo AsyncContext ottenuto chiamando AsyncContext.start(). In caso di Tomcat, quest'ultimo approccio utilizza il pool di thread di Tomcat definito in server.xml.
  3. IO non bloccante (NIO); Sebbene sia asincrono, è una storia diversa. Si riferisce a operazioni di I/O non bloccanti come il disco o l'I/O di rete. Se si desidera abilitare NIO per l'elaborazione delle richieste HTTP, dare un'occhiata a documentation di Tomcat.

Il sotto esempio delinea come può lavoro. Usa solo un thread per i lavori dei lavoratori. Se lo si esegue da 2 browser differenti in parallelo all'uscita si presenta così (io uso un registratore su ordinazione):

DATE       THREAD_ID LEVEL  MESSAGE 
2011-09-03 11:51:22.198 +0200  26  I:  >doGet: chrome 
2011-09-03 11:51:22.204 +0200  26  I:  <doGet: chrome 
2011-09-03 11:51:22.204 +0200  28  I:  >run: chrome 
2011-09-03 11:51:27.908 +0200  29  I:  >doGet: firefox 
2011-09-03 11:51:27.908 +0200  29  I:  <doGet: firefox 
2011-09-03 11:51:32.227 +0200  28  I:  <run: chrome 
2011-09-03 11:51:32.228 +0200  28  I:  >run: firefox 
2011-09-03 11:51:42.244 +0200  28  I:  <run: firefox 

Si vede che i metodi doGet immediatamente finire, mentre il lavoratore corre ancora. Le 2 richieste di test: http://localhost:8080/pc/TestServlet?name=chrome e http://localhost:8080/pc/TestServlet?name=firefox.

esempio semplice Servlet

@WebServlet(asyncSupported = true, value = "/TestServlet", loadOnStartup = 1) 
public class TestServlet extends HttpServlet { 
    private static final Logger LOG = Logger.getLogger(TestServlet.class.getName()); 
    private static final long serialVersionUID = 1L; 
    private static final int NUM_WORKER_THREADS = 1; 

    private ExecutorService executor = null; 

    @Override 
    public void init() throws ServletException { 
     this.executor = Executors.newFixedThreadPool(NUM_WORKER_THREADS); 
    } 

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 

     final String name = request.getParameter("name"); 
     LOG.info(">doGet: " + name); 

     AsyncContext ac = request.startAsync(); // obtain async context 
     ac.setTimeout(0); // test only, no timeout 

     /* Create a worker */ 
     Runnable worker = new TestWorker(name, ac); 

     /* use your own executor service to execute a worker thread (TestWorker) */ 
     this.executorService.execute(worker); 

     /* OR delegate to the container */ 
     // ac.start(worker); 

     LOG.info("<doGet: " + name); 
    } 
} 

... e il TestWorker

public class TestWorker implements Runnable { 
    private static final Logger LOG = Logger.getLogger(TestWorker.class.getName()); 
    private final String name; 
    private final AsyncContext context; 
    private final Date queued; 

    public TestWorker(String name, AsyncContext context) { 
     this.name = name; 
     this.context = context; 
     this.queued = new Date(System.currentTimeMillis()); 
    } 

    @Override 
    public void run() { 

     LOG.info(">run: " + name); 

     /* do some work for 10 sec */ 
     for (int i = 0; i < 100; i++) { 
      try { 
       Thread.sleep(100); 
      } catch (InterruptedException e) { 
       throw new RuntimeException(e); 
      } 
     } 

     ServletResponse response = this.context.getResponse(); 
     response.setContentType("text/plain"); 

     try { 
      PrintWriter out = response.getWriter(); 
      out.println("Name:\t\t" + this.name); 
      out.println("Queued:\t\t" + this.queued); 
      out.println("End:\t\t" + new Date(System.currentTimeMillis())); 
      out.println("Thread:\t\t" + Thread.currentThread().getId()); 
      out.flush(); 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 

     this.context.complete(); 

     LOG.info("<run: " + name); 
    } 
} 
+0

Grazie per il chiarimento. Per quanto riguarda l'API CometProcessor, c'è un thread che sta interrogando tutte le connessioni attuali per vedere quale ha "dati disponibili per la lettura"? Per essere precisi, quale sarebbe il numero previsto di thread quando ci sono 10 richieste vs 100 richieste? Supponendo che si tratti di richieste che hanno ricevuto l'evento BEGIN, ma non hanno ricevuto l'evento READ. http://tomcat.apache.org/tomcat-7.0-doc/aio.html –

+0

@John Se sei soddisfatto della risposta di casa, dovresti approvarla.La domanda aggiuntiva dovrebbe essere formulata come una nuova domanda (eventualmente, puoi fare riferimento a questa domanda). – JVerstry

+0

HI @JVerstry, a dire il vero non mi sento completamente soddisfatto della risposta. Non ho ancora la sensazione di avere una buona conoscenza dell'architettura di Tomcat per "elaborazione asincrona". –