2015-09-02 1 views
0

Quelqu'un at-il essayé la technique non bloquante des servlets 3.1 sur tomcat?Utilisation de la fonction d'E/S non bloquante des servlets 3.1 sur tomcat 8

La demande du navigateur semble attendre éternellement, mais lorsque je cours le serveur en mode débogage, l'appel revient mais je ne vois toujours pas "Data read.." et "Data written.." dans les journaux.

Servlet:

@WebServlet(urlPatterns = "/asyncn", asyncSupported = true) 
public class AsyncN extends HttpServlet { 

    private static final long serialVersionUID = 1L; 

    @Override 
    protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { 

     println("Before starting job"); 

     final AsyncContext actx = request.startAsync(); 
     actx.setTimeout(Long.MAX_VALUE); 
     actx.start(new HeavyTask(actx)); 

     println("After starting job"); 

    } 

    class HeavyTask implements Runnable { 
     AsyncContext actx; 

     HeavyTask(AsyncContext actx) { 
      this.actx = actx; 
     } 

     @Override 
     public void run() { 
      try { 
       Thread.currentThread().setName("Job-Thread-" + actx.getRequest().getParameter("job")); 
       // set up ReadListener to read data for processing 
       ServletInputStream input = actx.getRequest().getInputStream(); 
       ReadListener readListener = new ReadListenerImpl(input, actx); 
       input.setReadListener(readListener); 
      } catch (IllegalStateException e) { 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static void println(String output) { 
     System.out.println("[" + Thread.currentThread().getName() + "]" + output); 
    } 
} 

Auditeurs:

public class ReadListenerImpl implements ReadListener { 

    private ServletInputStream input = null; 
    private AsyncContext actx = null; 

    // store the processed data to be sent back to client later 
    private Queue<String> queue = new LinkedBlockingQueue<>(); 

    ReadListenerImpl(ServletInputStream input, AsyncContext actx) { 
     this.input = input; 
     this.actx = actx; 
    } 

    @Override 
    public void onDataAvailable() throws IOException { 
     println("Data is now available, starting to read"); 
     StringBuilder sb = new StringBuilder(); 
     int len = -1; 
     byte b[] = new byte[8]; 
     // We need to check input#isReady before reading data. 
     // The ReadListener will be invoked again when 
     // the input#isReady is changed from false to true 
     while (input.isReady() && (len = input.read(b)) != -1) { 
      String data = new String(b, 0, len); 
      sb.append(data); 
     } 
     println("Data read: "+sb.toString()); 
     queue.add(sb.toString()); 
    } 

    @Override 
    public void onAllDataRead() throws IOException { 
     println("All Data read, now invoking write listener"); 
     // now all data are read, set up a WriteListener to write 
     ServletOutputStream output = actx.getResponse().getOutputStream(); 
     WriteListener writeListener = new WriteListenerImpl(output, queue, actx); 
     output.setWriteListener(writeListener); 
    } 

    @Override 
    public void onError(Throwable throwable) { 
     println("onError"); 
     actx.complete(); 
     throwable.printStackTrace(); 
    } 

    public static void println(String output) { 
     System.out.println("[" + Thread.currentThread().getName() + "]" + output); 
    } 
} 

public class WriteListenerImpl implements WriteListener { 

    private ServletOutputStream output = null; 
    private Queue<String> queue = null; 
    private AsyncContext actx = null; 

    WriteListenerImpl(ServletOutputStream output, Queue<String> queue, AsyncContext actx) { 
     this.output = output; 
     this.queue = queue; 
     this.actx = actx; 
    } 

    @Override 
    public void onWritePossible() throws IOException { 
     println("Ready to write, writing data"); 
     // write while there is data and is ready to write 
     while (queue.peek() != null && output.isReady()) { 
      String data = queue.poll(); 
      //do some processing here with the data 
      try { 
       data = data.toUpperCase(); 
       Thread.sleep(3000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      println("Data written: "+data); 
      output.print(data); 
     } 
     // complete the async process when there is no more data to write 
     if (queue.peek() == null) { 
      actx.complete(); 
     } 
    } 

    @Override 
    public void onError(Throwable throwable) { 
     println("onError"); 
     actx.complete(); 
     throwable.printStackTrace(); 
    } 

    public static void println(String output) { 
     System.out.println("[" + Thread.currentThread().getName() + "]" + output); 
    } 
} 

logs sysout:

[http-nio-8080-exec-4]Before starting job 
[http-nio-8080-exec-4]After starting job 

journaux sysout (quand je lance le serveur en mode débogage):

[http-nio-8080-exec-6]Before starting job 
[http-nio-8080-exec-6]After starting job 
[http-nio-8080-exec-6]All Data read, now invoking write listener 
[http-nio-8080-exec-6]Ready to write, writing data 
+0

Je ne suis pas expert en NIO, mais la création de nouveaux threads ne me semble pas raison –

+0

Pour que votre travail Async s'exécute sur un thread différent, vous devez le faire, je crois. – John

Répondre

0

La création du nouveau thread est inutile, définissez readListener à partir de la méthode de service et tout fonctionnera de manière asynchrone.

Couple de commentaires sur votre code. Dans le readListener vous avez:

while (input.isReady() && (len = input.read(b)) != -1) 

suggérerait au lieu d'utiliser cela pour tenir pleinement avec l'api asynchrone:

while (input.isReady() && !input.isFinished()) 

également pour votre écouteur d'écriture vous avez:

while (queue.peek() != null && output.isReady()) 

vous devriez Inverser les conditions à:

while (output.isReady() && queue.peek() != null) 

cela protège contre l'appel ac.complete() tôt si la toute dernière écriture devient asynchrone.

+0

Vous voulez donc dire que les écouteurs sont placés dans la servlet et non dans un thread exécutable séparé? Mais serait-il en train de lire et d'écrire de façon asynchrone? – John

+0

Correct. Si vous définissez readListener à partir de la méthode de service du servlet et que vous revenez, vous effectuez maintenant des E/S asynchrones. L'écouteur de lecture sera appelé lorsque les données sont disponibles, il peut être sur le même thread que le servlet ou un nouveau thread en fonction de l'implémentation sous-jacente et lorsque les données sont disponibles. Si, pour une raison quelconque, les données entrantes sont bloquées, input.isReady() renverra false, readListener retournera et le thread sera abandonné. OnDataAvailable() sera ensuite appelé sur un nouveau thread lorsque plus de données seront disponibles. – mmulholl

+0

De même, dans votre application, l'inscription writeListener à partir de onAllDataRead(), lorsque toutes les données de sortie sont disponibles, est correcte. – mmulholl