17.12 异步处理

应用服务器的web容器一般会为每一个请求分配一个服务线程。在重负载的场景下,容器须要大量的线程去服务全部客户端请求。服务可扩展性的限制包括内存不足或者耗尽容器线程。建立可扩展web程序,你必须确保没有关联请求的线程是空闲的,因此容器可使用他们处理新请求。
这里有两个关联请求的线程空闲的两个场景:html

  • 线程须要在构建响应以前等待一个资源可用或者处理数据。例如,一个应用程序须要在构建响应前查询数据库或者经过远程web服务访问数据。
  • 线程须要在构建响应以前等待一个事件。例如,线程在构建响应以前须要等待一个JMS消息,另外一个客户端的新信息,或者队列里面的新数据可用。

这些场景表明限制web程序可扩展性的阻塞操做。异步处理是指给这些阻塞操做分配一个新线程,并把关联请求处理的线程返回给web容器。java

servlet 异步处理

java ee支持servlet和filter的异步处理。若是一个servlet或一个filter处理请求时可能到达一个阻塞操做,它能够把操做分配给一个异步处理上下文而且在不生成响应的状况下将关联请求处理的线程回送给web容器。阻塞操做在不一样线程的异步上下文中执行完成,它能够生成响应或者转发请求到另外一个servlet。
在一个servlet上启用异步处理,设置@WebServlet注解的asyncSupported参数为true,以下:web

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet { ... }

javax.servlet.AsyncContext类提供在Service方法中执行异步处理所需的功能。得到一个AsyncContext实例,在service方法中调用request对象的startAsync()方法;例如:数据库

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
   ...
   AsyncContext acontext = req.startAsync();
   ...
}

这个调用将请求进入异步模式而且确保响应在退出service方法时每个月被提交。你必须在异步上下文完成阻塞操做时生成响应或者转发到其余servlet中。
AsyncContext类提供的基础功能描述:
方法签名:void start(Runnable run)
描述:容器提供的能提供阻塞操做处理的不一样线程
方法签名:ServletRequest getRequest()
描述:返回用来初始化异步上下文的请求。在上面的例子中,request与service方法中的相同。你能够经过这个方法在异步上下文中从请求中获取参数。
方法签名:ServletResponse getResponse()
描述:返回初始化异步上下文的响应。在上面的例子中,response与service方法中的相同。你能够在异步上下文中,使用这个方法写入阻塞操做的结果到响应中。
方法签名:void complete()
描述:完成异步操做,并关闭与此异步上下文关联的响应。你能够在异步上下文完成写入响应后调用这个操做。
方法签名:void dispatch(String path)
描述:转发请求和响应到给定的路径。在阻塞操做完成后,使用这个方法调用另外一个servlet写出响应。服务器

等待一个资源

这个章节示范了怎么使用AsyncContext上下文提供的功能,有以下用例:并发

  1. servlet从一个GET请求中获取参数
  2. servlet使用一个资源,好比一个数据库或者一个web service,基于这个参数获取信息。这个资源可能比较缓慢,因此这多是一个阻塞操做。
  3. servlet使用资源的结果生成响应。

下面的例子是一个不使用异步处理的常见servlet:异步

@WebServlet(urlPatterns={"/syncservlet"})
public class SyncServlet extends HttpServlet {
   private MyRemoteResource resource;
   @Override
   public void init(ServletConfig config) {
      resource = MyRemoteResource.create("config1=x,config2=y");
   }

   @Override
   public void doGet(HttpServletRequest request, 
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      String param = request.getParameter("param");
      String result = resource.process(param);
      /* ... print to the response ... */
   }
}

下面的示例是同一个servlet,但使用了异步处理:async

@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
   /* ... Same variables and init method as in SyncServlet ... */

   @Override
   public void doGet(HttpServletRequest request, 
                     HttpServletResponse response) {
      response.setContentType("text/html;charset=UTF-8");
      final AsyncContext acontext = request.startAsync();
      acontext.start(new Runnable() {
         public void run() {
            String param = acontext.getRequest().getParameter("param");
            String result = resource.process(param);
            HttpServletResponse response = acontext.getResponse();
            /* ... print to the response ... */
            acontext.complete();
   }
}

AsyncServlet在@WebServlet注解属性中添加asyncSupported=true。其他的差别在service方法中:ide

  • request.startAsync() 引起request被异步执行;response不会在service方法结束时发送到客户端;
  • acontext.start(new Runnable() {...})从容器中获得一个新的线程。
  • 内部类中的run() 中的代码在一个新线程中执行。内部类须要从异步上下文中读取请求参数和写入响应。调用异步上下文的complete()方法来提交并发送响应到客户端。

AsyncServlet的service方法当即返回,同时请求在异步上下文中处理。url

相关文章
相关标签/搜索