Servlet异步处理


1、异步处理

        为啥要异步处理呢?因为servlte是并行被访问,但是有时候是需要一些耗时的操作,如jdbc连接或者远程web服务的响应等。这时候如果等待响应对于体验来说是不好的。又或者,当并发太多的时候,通过异步处理中使用线程池,可以自己进行线程池管理。如果不使用线程池,那么就是由容器创建和销毁线程,这样子在大并发的时候会有很多的线程被创建和销毁,这本身很消耗服务器资源。而线程池的概念就是复用线程。

2、api说明

    ServletRequest类

     要servlte使用到这个功能,@WebServlet @WebFilter注解有一个属性——asyncSupportedboolean类型默认值为false。当asyncSupported设置为true,应用通过执行startAsync(见下文)可以启动一个单独的线程中进行异步处理,并把请求和响应的引用传递给这个线程,然后退出原始线程所在的容器。
    还有一种设置方式 req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

      isAsyncSupported()
       如果请求支持异常处理则返回 true,否则返回false。一旦请求传给了过滤器或servlet不支持异步处理(通过指定的注解或声明),异步支持将被禁用。

       isAsyncStarted()

          1、异步请求已经开始就返回true,否则就返回false  就是调用startAsync之前是false;调用之后是true

           2、如果调用了AsyncContext.dispatch 或者AsynContext.complete之后都是返回false,在startAsync前面两个之间都是返回true。

      HttpServletRequest。startAsync()

      HttpServletRequest。startAsync(ServletRequest,ServletResponse)

                       将请求转为异步模式,第二个方法是传入此次请求的必须是servletservicefilter doFilter方法相同的对象。或者是ServletRequestWrapperServletResponseWrapper子类的对象,当应用退出service方法的时候,调用该方法要确保应用没有被提交(应该是容器内部进行提交的)。当调用返回的AsyncContextAsyncContext.completeAsyncContext超时并且没有监听器处理超时时(没有addListener事件监听),它将被提交(内部AsyncContext.complete)。

       getDispatcherType()

                       返回请求的分派器(dispatcher)类型。容器使用请求的分派器类型来选择需要应用到请求的过滤器。只有匹配分派器类型和url模式(url pattern)的过滤器
才会被应用。允许一个过滤器配置多个分派器类型,过滤器可以根据请求的不同分派器类型处理请求。请 求 的 初 始 分 派 器 类 型 定 义 为
DispatcherType.REQUEST。 使 用RequestDispatcher.forward(ServletRequest, ServletResponse)RequestDispatcher.include(ServletRequest,ServletResponse)分 派 时 , 它 们 的 请 求 的 分 派 器 类 型 分 别 是DispatcherType.FORWARDDispatcherType.INCLUDE,当一个异步请求使用任意一个AsyncContext.dispatch方法分派时该请求的分派器类型是 DispatcherType.ASYNC。最后,由容器的错误处理机制分派到错误页面的分派器类型是DispatcherType.ERROR

      AsyncContext类

       该类表示在 ServletRequest启动的异步操作执行上下文,AsyncContext由之前描述的ServletRequest.startAsync创建并初始化。

        getRequest() –返回调用startAsync用于初始化 AsyncContext的请求对象。当在异步周期之前调用了complete或任意一个dispatch 方法,调用 getRequest将抛出 IllegalStateException

       getResponse() –返回调用startAsync用于初始化 AsyncContext的响应对象。当在 异 步 周 期 之 前 调 用 了complete或 任 意 一 个dispatch方 法 , 调 用getResponse将 抛 出IllegalStateException

        setTimeout(long timeoutMilliseconds) – 设置异步处理的超时时间,以毫秒为单位,默认是1000毫秒。该方法调用将覆盖容器设置的超时时间。如果没有调用 setTimeout 设置超时时间,将使用容器默认的超时时间。一个小于等于0的数表示异步操作将永不超时。当调用任意一个ServletRequest.startAsync方法时,一旦容器启动的分派返回到容器,超时时间将应用到AsyncContext。当在异步周期开始时容器启动的分派已经返回到容器后,再设置超时时间是非法的,这将抛出一个IllegalStateException异常

        getTimeout() – 获取AsyncContext关联的超时时间的毫秒值。该方法返回容器默认的超时时间,或最近一次调用setTimeout设置超时时间。

         addListener(AsyncListener listener, ServletRequest req, ServletResponse res)

             onTimeout     没有调用AsyncContext.complete,超时被时候调用
             onError            异步期间发生错误的时候调用

             onComplete    AsyncContext.complete 调用时候调用

             onStartAsync      通常是调用了   HttpServletRequest。startAsync()时候调用,也就是二次调用startAsync()方法

       dispatch(String path)

       dispatch()

         dispatch(ServletContext context, String path )


              分派方法,类似FORWARD INCLUDE

               1  如果当前得servlte是异步请求了,就不能使用FORWARDINCLUDE 进行分派,只能使用dispatch();

               2  当前servlte异步请求后,调用dispatch();是回调给自己,因为默认的ServletRequest 和 ServletResponse 是它本身的。

               3  当前的servlte(a)FORWARDINCLUDE 进行分派,在分派的那个servlte 进行AsyncContext ac = request.startAsync();ac.dispatch(); 会分派到a.

               4  当前的servlte(a)FORWARDINCLUDE 进行分派在分派的那个servlte(b) 进行AsyncContext ac = request.startAsync(request, response );ac.dispatch(); 会分派到b

               由此可以知道,dispatch分派是根据AsyncContextServletRequest 和 ServletResponse是谁的。

               AsyncContext.hasOriginalRequestAndResponse()  是表示是不是原始请求的,就是最初的那个请求,还是在分派的时候通过req.startAsync(req,resp)设置过了分派

之后的HttpServletRequest 和 HttpServletResponse 。

    以上是api说明

         
   

@WebServlet("/async")
public class Async extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

		AsyncContext ys = req.startAsync();
//		ys = req.startAsync(req,resp);
		ys.addListener(new AsyncListener() {
			
			@Override
			public void onTimeout(AsyncEvent event) throws IOException {
				// TODO Auto-generated method stub
				System.out.println("onTimeout----async");
			}
			
			@Override
			public void onStartAsync(AsyncEvent event) throws IOException {
				// TODO Auto-generated method stub
				System.out.println("onStartAsync----async");
			}
			
			@Override
			public void onError(AsyncEvent event) throws IOException {
				// TODO Auto-generated method stub
				System.out.println("onError----async");
			}
			
			@Override
			public void onComplete(AsyncEvent event) throws IOException {
				// TODO Auto-generated method stub
				System.out.println("onComplete----async");
			}
		});
		ys.dispatch("/async2");

//		req.getRequestDispatcher("/async2").forward(req, resp);
	}
}

@WebServlet(urlPatterns="/async2",asyncSupported=true)
public class Async2 extends HttpServlet {
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		// TODO Auto-generated method stub
		req.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
		//如果调用async2中的req,resp传入startAsync,则ys.dispatch()就是分派到async2
		//如果req.startAsync()方法,也就是无参数的。则ys.dispatch()就是分派到async
		AsyncContext ys = req.startAsync(req,resp);
		ys.dispatch();
	}
}


 


     还有异步请求的使用线程池的例子,别的网站有

    http://www.cnblogs.com/jiaoyiping/p/5979242.html

              http://jinnianshilongnian.iteye.com/blog/2245925

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值