Spring Boot实现异步请求(Servlet 3.0)
在spring3.2及以后版本中增加了对请求的异步处理,旨在提高请求的处理速度降低服务性能消耗。
在我们的请求中做了耗时处理,当并发请求的情况下,为了避免webserver的连接池被长期占用而引起性能问题,调用后生成一个非web的服务线程来处理,增加web服务器的吞吐量。
为此Servlet3.0新增了请求的异步处理,Spring也在此基础上做了封装处理。
本文还是以代码例子的方式说明如何在SpringBoot中应用异步请求。
首先说一下几个要点:
1、@WebFilter和@WebServlet注解中的asyncSupported=true属性
异步处理的servlet若存在过滤器,则过滤器的注解@WebFilter应设置asyncSupported=true,
否则会报错Afilterorservletofthecurrentchaindoesnotsupportasynchronousoperations.
2、@EnableAsync注解
SpringBoot默认添加了一些拦截/*的过滤器,因为/*会拦截所有请求,按理说我们也要设置asyncSupported=true属性。因为这些过滤器都是SpringBoot初始化的,所以它提供了@EnableAsync注解来统一配置,该注解只针对“非@WebFilter和@WebServlet注解的有效”,所以我们自己定义的Filter还是需要自己配置asyncSupported=true的。
3、AsyncContext对象
获取一个异步请求的上下文对象。
4、asyncContext.setTimeout(20*1000L);
我们不能让异步请求无限的等待下去,通过setTimeout来设定最大超时时间。
下面通过两种方式来测试异步任务:
先在SpringBootSampleApplication上添加@EnableAsync注解。
再检查所有自定义的Filter,如存在如下两种情况需要配置asyncSupported=true
1)自定义Filter拦截了/*
2)某Filter拦截了/shanhy/*,我们需要执行的异步请求的Servlet为/shanhy/testcomet
方法一:原生Servlet方式
packageorg.springboot.sample.servlet; importjava.io.IOException; importjava.util.Queue; importjava.util.concurrent.LinkedBlockingQueue; importjava.util.concurrent.TimeUnit; importjavax.servlet.AsyncContext; importjavax.servlet.ServletException; importjavax.servlet.annotation.WebServlet; importjavax.servlet.http.HttpServlet; importjavax.servlet.http.HttpServletRequest; importjavax.servlet.http.HttpServletResponse; /** *HTTP长连接实现 * *@author单红宇(365384722) *@mybloghttp://blog.csdn.net/catoop/ *@create2016年3月29日 */ @WebServlet(urlPatterns="/xs/cometservlet",asyncSupported=true) //异步处理的servlet若存在过滤器,则过滤器的注解@WebFilter应设置asyncSupported=true, //否则会报错Afilterorservletofthecurrentchaindoesnotsupportasynchronousoperations. publicclassCometServletextendsHttpServlet{ privatestaticfinallongserialVersionUID=-8685285401859800066L; privatefinalQueueasyncContexts=newLinkedBlockingQueue<>(); privatefinalThreadgenerator=newThread("AsyncEventgenerator"){ @Override publicvoidrun(){ while(!generator.isInterrupted()){//线程有效 try{ while(!asyncContexts.isEmpty()){//不为空 TimeUnit.SECONDS.sleep(10);//秒,模拟耗时操作 AsyncContextasyncContext=asyncContexts.poll(); HttpServletResponseres=(HttpServletResponse)asyncContext.getResponse(); res.getWriter().write("{\"result\":\"OK-"+System.currentTimeMillis()+"\"}"); res.setStatus(HttpServletResponse.SC_OK); res.setContentType("application/json"); asyncContext.complete();//完成 } }catch(InterruptedExceptione){ Thread.currentThread().interrupt(); e.printStackTrace(); }catch(IOExceptione){ e.printStackTrace(); } } } }; @Override publicvoidinit()throwsServletException{ super.init(); generator.start(); } @Override protectedvoiddoGet(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{ System.out.println(">>>>>>>>>>CometServletRequest<<<<<<<<<<<"); doPost(req,resp); } @Override protectedvoiddoPost(HttpServletRequestreq,HttpServletResponseresp)throwsServletException,IOException{ AsyncContextasyncContext=req.startAsync(); asyncContext.setTimeout(20*1000L); asyncContexts.offer(asyncContext); } @Override publicvoiddestroy(){ super.destroy(); generator.interrupt(); } }
方法二:Controller方式
@Controller publicclassPageController{ @RequestMapping("/async/test") @ResponseBody publicCallablecallable(){ //这么做的好处避免webserver的连接池被长期占用而引起性能问题, //调用后生成一个非web的服务线程来处理,增加web服务器的吞吐量。 returnnewCallable (){ @Override publicStringcall()throwsException{ Thread.sleep(3*1000L); return"小单-"+System.currentTimeMillis(); } }; } }
最后写一个comet.jsp页面测试:
<%@pagepageEncoding="UTF-8"%>长连接测试 $(function(){ functionlongPolling(){ $.getJSON('${pageContext.request.contextPath}/xs/cometservlet',function(data){ console.log(data.result); $('#n1').html(data.result); longPolling(); }); } longPolling(); functionlongPolling2(){ $.get('${pageContext.request.contextPath}/async/test',function(data){ console.log(data); $('#n2').html(data); longPolling2(); }); } longPolling2(); }); 长连接测试
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。