该篇文章翻译自:http://developerlife.com/tutorials/?p=1437

  一、简介

  Servlet API 3.0 之前,需要使用类似Comet的方式来实现创建异步的Servlet。然而,Tomcat7 与 Servlet API 3.0 支持同步与异步方式。在同步Servlet中,一个处理客户端HTTP请求的线程将在整个请求的过程中被占用。对于运时较长的任务,服务器主要在等待一个应答,这导致了线程的饥渴,并且负载加重。这是由于即使服务器只是等待,服务端的线程还是被请求占光。

  异步Servlet,使用在其他线程中执行耗时(等待)的操作,而允许Tomcat的线程返回线程池的方式来解决问题。当任务完成并且结果就绪,Servlet容器需要得到通知,然后另外一个线程将被分配用于处理将结果返回给客户端。客户端完全感受不到服务器的差别,并且不需要任何改变。为了允许异步魔法,新的Servlet需要使用一种回调机制(有App Server提供),告知app server结果就绪。另外,需要告知Servlet容器(app server)何时可以释放当前正在处理的请求(随后该任务将在后台的线程中得到真正的处理)。

  二、简要的伪代码

  1、客户端请求通过HTTP请求,然后被分配到某个Servlet。

  2、Servlet.service方法在Servlet容器的某个线程中执行。

  3、Servlet.service方法创建一个AsyncContext对象(使用sartAsync())。

  4、Servlet.service方法后将创建AsyncContext对象传递给另外的线程执行。
  5、Servlet.service方法随后返回并结束执行。

  然后,客户端照样请求服务器,并且之前的那个连接被挂起等待,直到某时触发了如下事件:

  1、后台的线程处理完AsyncContext任务,并且结果已经就绪,将通知AsyncContext处理已经完成。它将向HttpResponse中写入返回的数据,并且调用AsyncContext的Complete方法。这将通知Servlet容器将结果返回给客户端。

  三、异常情况

  假如某些异常在后台线程处理期间发生,客户端应用将得到某些网络异常。然而,若后台线程处理任务时有错误,有一种方式处理这种情况。当创建AsyncContext时,可以指定两件事情:

  1、设置后台线程处理得到结果的最大时间,超过时产生一个超时异常。

  2、可以在超时事件上设置监听器来处理这种情况。

  因此,假如在后台线程出现了某些错误,经过一个你指定的合适时间后,监听器将被触发并且告知超时条件被触发。超时处理函数将被执行,可以向客户端发送一些错误信息。若后台线程在这之后试图向HttpResponse写入数据,将会触发一个异常。之后就需要将之前执行过程中产生的结果丢弃。

  四、简单实现

  将提供两组异步Servlet,一个简单实现一个一个复杂的。简单实现只是介绍异步Servlet的理念以及部分web.xml。另一个将展示耗时操作超时与触发异常,以便看到如何处理他们。

@javax.servlet.annotation.WebServlet(
    // servlet name
    name = "simple",
    // servlet url pattern
    value = {"/simple"},
    // async support needed
    asyncSupported = true
)
public class SimpleAsyncServlet extends HttpServlet {

/**
 * Simply spawn a new thread (from the app server's pool) for every new async request.
 * Will consume a lot more threads for many concurrent requests.
 */
public void service(ServletRequest req, final ServletResponse res) 
    throws ServletException, IOException {

  // create the async context, otherwise getAsyncContext() will be null
  final AsyncContext ctx = req.startAsync();

  // set the timeout
  ctx.setTimeout(30000);

  // attach listener to respond to lifecycle events of this AsyncContext
  ctx.addListener(new AsyncListener() {
    public void onComplete(AsyncEvent event) throws IOException {
      log("onComplete called");
    }
    public void onTimeout(AsyncEvent event) throws IOException {
      log("onTimeout called");
    }
    public void onError(AsyncEvent event) throws IOException {
      log("onError called");
    }
    public void onStartAsync(AsyncEvent event) throws IOException {
      log("onStartAsync called");
    }
  });

  // spawn some task in a background thread
  ctx.start(new Runnable() {
    public void run() {

      try {
        ctx.getResponse().getWriter().write(
            MessageFormat.format("<h1>Processing task in bgt_id:[{0}]</h1>",
                                 Thread.currentThread().getId()));
      }
      catch (IOException e) {
        log("Problem processing task", e);
      }

      ctx.complete();
    }
  });

}

}
服务端简单示例

相关文章:

  • 2022-12-23
  • 2021-07-24
  • 2021-09-27
  • 2021-06-09
  • 2021-09-15
  • 2022-12-23
  • 2021-10-10
猜你喜欢
  • 2021-07-23
  • 2021-08-08
  • 2022-12-23
  • 2021-11-16
  • 2022-12-23
  • 2021-07-15
  • 2021-06-11
相关资源
相似解决方案