servlet异步请求的实现
作者:liushangzaibeijing
1、什么是servlet异步请求
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:
(1)、Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
(2)、调用业务接口的某些方法,以完成业务处理;
(3)、根据处理的结果提交响应,Servlet 线程结束。
其中第二步处理业务逻辑时候很可以碰到比较耗时的任务,此时servlet主线程会阻塞等待完成业务处理,对于并发比较大的请求可能会产生性能瓶颈,则servlet3.0之后再此处做了调整,引入了异步的概念。
(1)、Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
(2)、调用业务接口的某些方法过程中request.startAsync()请求,获取一个AsyncContext
(3)、紧接着servlet线程退出(回收到线程池),但是响应response对象仍旧保持打开状态,新增线程会使用AsyncContext处理并响应结果。
(4)、AsyncContext处理完成触发某些监听通知结果
2、Servlet异步请求示例
2.1、示例准备
本示例采用web.xml配置的形式,模拟场景为:笔者所在的it公司每周的工作内容,首先研发总监分配给产品、研发、测试相关的任务,布置完任务就出差(模拟请求响应),余下的各个小组进行自己任务操作(模拟的耗时操作),最终出周报完成任务(异步任务处理完成的通知)
git地址:https://github.com/liushangzaibeijing/spsm.git 分支:dev_async
2.2、实现自定义的Servlet
/** * @ClassName AsyncServlet * @Desc 自定义异步Servlet处理器 * @Author xieqx * @Date 2020/12/9 15:38 **/ //通过注解的形式开始异步 @WebServlet(urlPatterns = "*.async",asyncSupported = true) public class AsyncServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //开启异步支持 //异步管理上下文 resp.setCharacterEncoding("GBK"); PrintWriter writer = resp.getWriter(); writer.println("周工作任务布置开始"); AsyncContext asyncContext = req.startAsync(); asyncContext.start(new WeekTask(asyncContext)); //添加监听器 处理完成监听 asyncContext.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"处理完成"); } @Override public void onTimeout(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"处理超时"); } @Override public void onError(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"处理出错"); } @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException { System.out.println("工作在"+new Date()+"处理开始"); } }); writer.println("周工作任务布置完成"); writer.flush(); } }
开启异步支持(默认异步支持不开启)有两种方式:
使用注解
web.xml配置
<servlet> <servlet-name>asyncServlet</servlet-name> <servlet-class>com.xiu.async.servlet.AsyncServlet</servlet-class> <!-- 开启servlet的异步请求操作 --> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>asyncServlet</servlet-name> <url-pattern>*.async</url-pattern> </servlet-mapping>
上述代码中通过request.startAsync()启动异步处理 返回一个异步上下文对象AsyncContext最终是使用该上下文对象来进行异步业务逻辑处理,其中有两个核心方法
asyncContext.start(new WeekTask(asyncContext)); 添加一个异步任务该任务是一个Runnable线程接口,这里就清晰了其实是servlet线程将处理任务交给另一个子线程,servlet直接返回从而达到提高系统吞吐量的作用。
对于异步请求可以我们需要获取其中的结果,所有这里提供了监听器模式添加事件监听AsyncListener
onComplete | 异步请求处理完成触发 前提示需要调用 asyncContext.complete()方法(因为程序也不知道什么时候任务算是调用完毕了) |
onTimeout | 异步请求处理超时触发,一般来说采用异步请求的任务都是比较耗时的任务,所以需要修改servlet默认的超时时间(修改的长一点) |
onError | 异步处理错误的时候触发 |
onStartAsync | 异步处理开始的时候触发即为request.startAsync(),因为添加监听器在startAsync()方法后,所以第一个启动是无法触发该监听的 |
这里异步处理只是简单的打印了相关日志,不过真实的业务场景中可以写复杂的业务处理逻辑。
2.3、异步任务
这里提供相关的异步操作是实现runnable的线程实现类,同时这里提供了相关Job,PmJob(产品任务),RDJob(研发任务),TestJob(测试任务),每个任务模拟了10秒的耗时任务。
/** * @ClassName WeekTask * @Desc 每周任务 * @Author xieqx * @Date 2020/12/10 9:36 **/ public class WeekTask implements Runnable { private List<Job> jobs = null; private AsyncContext asyncContext = null; //这里初始化产品任务PmJob、研发任务RDJob 测试任务TestJob public WeekTask(AsyncContext asyncContext) { this.asyncContext = asyncContext; jobs = new ArrayList<>(); PmJob pmJob = new PmJob(); RDJob rdJob = new RDJob(); TestJob testJob = new TestJob(); jobs.add(pmJob); jobs.add(rdJob); jobs.add(testJob); } @Override public void run() { for(Job job:jobs){ job.execute(); } System.out.println("周任务工作完成"); //job执行完成后通知 asyncContext.complete(); } }
PmJob
/** * @ClassName PmTask * @Desc 产品经理任务 * @Author xieqx * @Date 2020/12/9 16:03 **/ public class PmJob implements Job { @Override public void execute() { System.out.println("产品经理开评审会议"); try { Thread.sleep(10); System.out.println("模拟需求评审会议..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
RDJob
/** * @ClassName PmTask * @Desc 研发任务 * @Author xieqx * @Date 2020/12/9 16:03 **/ public class RDJob implements Job { @Override public void execute() { System.out.println("程序猿开始开发"); try { Thread.sleep(10); System.out.println("程序猿哼哧哼哧干活中..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
TestJob
/** * @ClassName TestJob * @Desc 测试任务 * @Author xieqx * @Date 2020/12/9 16:03 **/ public class TestJob implements Job { @Override public void execute() { System.out.println("测试开始测试"); try { Thread.sleep(10); System.out.println("测试用例测试..."); } catch (InterruptedException e) { e.printStackTrace(); } } }
2.4、测试场景
请求立马响应,但是异步任务在后面处理
到此这篇关于servlet异步请求的实现的文章就介绍到这了,更多相关servlet异步请求内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!