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(); }); 长连接测试
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。