Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle Jetty exception - a long running HTTP request times out, but the process it calls never terminates and Jetty is unhappy

I have a Jetty server handling long running HTTP requests- the responses are generated by an a different process X and end up in a collector hash which Jetty requests periodically check.

There are 3 cases:

  1. Process X finishes before the timeout period of the HTTP request - no problem
  2. Process X finishes after the timeout period of the request - no problem
  3. Process X never finishes - below exception occurs

How do I detect this situation (3) and prevent the exception while allowing the other two cases to properly work?

Exception:

2012-06-18 00:13:31.055:WARN:oejut.QueuedThreadPool:
java.lang.IllegalStateException: IDLE,initial
    at org.eclipse.jetty.server.AsyncContinuation.complete(AsyncContinuation.java:569)
    at server.AsyncHTTPRequestProcessor.run(AsyncHTTPRequestProcessor.java:72)
    at org.eclipse.jetty.server.handler.ContextHandler.handle(ContextHandler.java:1119)
    at org.eclipse.jetty.server.AsyncContinuation$1.run(AsyncContinuation.java:875)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:599)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:534)
    at java.lang.Thread.run(Thread.java:679)


Jetty continuation of an HTTP request:

public class AsyncHTTPRequestProcessor implements Runnable {

    private ConcurrentHashMap<String, String> collector;
    private Logger logger;
    private AsyncContext ctx;
    //Defined this here because of strange behaviour when running junit
    //tests and the response json string being empty...
    private String responseStr = null;

    public AsyncHTTPRequestProcessor(AsyncContext _ctx, 
            ConcurrentHashMap<String, String> _collector, Logger _logger) {
        ctx = _ctx;
        collector = _collector;
        logger = _logger;
    }

    @Override
    public void run() {

        logger.info("AsyncContinuation start");

        //if(!((AsyncContinuation)ctx).isInitial()){
        String rid = (String) ctx.getRequest().getAttribute("rid");
        int elapsed = 0;
        if(rid !=null)
        {

            logger.info("AsyncContinuation rid="+rid);

            while(elapsed<ctx.getTimeout())
            {
                if(collector.containsKey(rid)){
                    responseStr = collector.get(rid);
                    collector.remove(rid);

                    logger.info("--->API http request in collector:"+responseStr);
                    ctx.getRequest().setAttribute("status",200);
                    ctx.getRequest().setAttribute("response", responseStr);
                    ctx.getRequest().setAttribute("endTime",System.currentTimeMillis());
                    //ctx.complete();
                    break;
                }
                try {
                    Thread.sleep(10);
                    elapsed+=10;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            //}
            logger.info("Collector in async stuff:");
            for(String key:collector.keySet()){
                logger.info(key+"->"+collector.get(key));
            }

            for(Entry<String, String> x:collector.entrySet()){
                logger.info(x.getKey()+"->"+x.getValue());
            }
            ctx.complete(); <---- this line 72
        }
    }

}
like image 950
nflacco Avatar asked Jun 18 '12 00:06

nflacco


People also ask

How do you stop a jetty service?

If jetty as maven plugin, you stop the server by pressing Ctrl + C and press Y to confirm terminate.

How does jetty handle multiple requests?

There is this server port that jetty listens on and some number of acceptor threads whose job it is to get connection objects made between the client and server side.

What is http jetty?

Jetty provides a web server and servlet container, additionally providing support for HTTP/2, WebSocket, OSGi, JMX, JNDI, JAAS and many other integrations. These components are open source and are freely available for commercial use and distribution.

Does jetty use Apache?

In terms of licensing, Tomcat enjoys the Apache 2.0 open source license, while Jetty is dual licensed through both the Apache 2.0 License and the Eclipse Public License 1.0.


1 Answers

The problem here is not your call of AsyncContext#complete() but the overal design of the code.

Continuations (same thing for Servlet async) is designed to be asynchronous. The while loop that makes use of the internal continuations timeout must not be here. You are transforming an asynchronous design to a synchronous one by doing this. The right thing to do is to register a listener using Continuation#addContinuationListener() and implements onTimeout() method to process the timeout case appropriately.

Once your timeout logic is out, I would recommend to move the process X logic to the class AsyncHTTPRequestProcessor and move out from the need of using a collector. During the processing, you should assume that the current thread will never be timed out. By doing this your call to complete() makes sens and you will be immune to concurrency trouble on the collector.

like image 113
Minh-Triet LÊ Avatar answered Oct 11 '22 10:10

Minh-Triet LÊ