私人笔记:异步-Servlet-组件特性透析及最佳实践攻略

一、异步-Servlet-简介

在Java EE的系列规范中,-Servlet-规范(含-Servlet-、Filter以及Listener等)处在一个核心的地位,包括当下流行的成熟框架(如Strut、Spring MVC、JSF等)中,其都发挥着极其核心的作用,包括基于对Servlet组件的扩展和包装而形成的框架新功能组件等。

当然,此规范也是逐步完善并结合实际应用需求而发展壮大起来。目前Java Web应用中,通行要求支持的规范一般都是3.0版或3.1版(分别对应JavaEE 6和JavaEE 7)。由于JavaEE8的发布一再延期,导致Servlet4.0到目前也没发布。据说下半年一定会随着JavaEE8的发布而发布4.0版,若想先睹为快,可自行到Oracle官网去搜索看看。

简要描述其发展史如下:

SERVLET-版本

关于AsynServlet:我们这里主要讲Servlet-3.x规范里的很少用到的新增特性,即Servlet-异步处理特性(AsynServlet)。所谓异步Servlet-处理,可以这样理解:在服务器的并发请求数量比较大的时候,会产生很多的servlet线程(这些servlet线程由-servlet-容器在线程池中维护),如果每个请求需要耗费的时间比较长(比如,执行了一些IO的处理等),在之前的非异步的servlet中,这些-servlet-线程将会阻塞,严重耗费服务器的资源.而在servlet-3.0中首次出现的异步-servlet-,通过一个单独的新的线程来执行这些比较耗时的任务(也可以把这些任务放到一个自己维护的线程池里),servlet线程立即返回到servlet容器的-servlet-线程池,以便响应其他请求。这样,在降低了系统的资源消耗的同时,也会提升系统的吞吐量。

注意,所谓的异步-Servlet-,简单理解就是把耗时的处理另外安排线程来进行处理,而不至于使当前的Servlet阻塞等待。而实现这种异步处理机制的核心,就是由容器提供的异步上下文来统筹调度实现的。

二、异步-Servlet-关联构成

异步Servlet的所涉及的接口类和API主要包括如下几个标准类:

接口类 作用
ServletRequest 在常规的Servlet中,通过此类(或子类)对象获得AsyncContext异步上下文对象
ServletResponse 根据需要实现对异步处理结果的响应输出。
AsyncContext 异步Servlet处理的上下文环境对象。在这里可以把耗时的处理交给其它线程处理,而把Servlet返回给容器线程池来响应其它Servlet请求。
AsyncListener 异步Servlet监听器,监视AsyncContext的状态变化
AsyncEvent 异步事件,作为异步监听器方法接口参数而存在,其实例对象有容器提供。

异步-Servlet实现的常规类图关系结构:

异步Servlet结构图

为了总体了解所谓异步-Servlet和传统-Servlet的关系和区别,这里绘制了一幅Servlet组件的总体关系图,以便更好的理解和掌握这个核心组件的应用模式。如下:

Servlet总体关系图

三、异步-Servlet 实战

我们按照上述的关系图以及异步-Servlet-应用场景(比较耗时的业务处理)要求,接下来,就一步步来编程实现异步-Servlet-的应用案例。为了尽可能了解所谓异步处理,本案例把可能相关的类都分开实现了(自诩为最佳实践^_^),也可自行进行简化处理。演示本案例包括这样几个主要的类:

  • ²自定义线程池初始化类

  • ²接收异步处理的工作线程类;

  • ²异步监听器AsyncListener实现类

  • ²支持异步处理的自定义Servlet类;

1、创建线程池类:AppContextListener

import java.util.concurrent.ArrayBlockingQueue;

import java.util.concurrent.ThreadPoolExecutor;

import java.util.concurrent.TimeUnit;

import javax.servlet.ServletContextEvent;

import javax.servlet.ServletContextListener;

import javax.servlet.annotation.WebListener;

/**

* Application Lifecycle Listener implementation class AppContextListener

* 启动应用时,创建自己的业务处理线程池,并存放在应用的领域范围内(WebApp级别)

*/

@WebListener

public class AppContextListener implements ServletContextListener {

/**

* Default constructor.

*/

public AppContextListener() {

}

/**

* @see ServletContextListener#contextDestroyed(ServletContextEvent)

*/

public void contextDestroyed(ServletContextEvent sce) {

ThreadPoolExecutor executor = (ThreadPoolExecutor) sce

.getServletContext().getAttribute("executor");

executor.shutdown();

}

/**

* @see ServletContextListener#contextInitialized(ServletContextEvent)

*/

public void contextInitialized(ServletContextEvent sce) {

// 初始化时,创建自己的业务处理线程池

ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L,

TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));

sce.getServletContext().setAttribute("executor", executor);

}

}

2、创建接收异步Servlet业务处理的线程类:AsyncRequestHandler

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.AsyncContext;

public class AsyncRequestHandler implements Runnable {

//接管异步Servlet上下文的对象

private AsyncContext asyncContext;

private int secs; //处理时间

public AsyncRequestHandler() {

}

public AsyncRequestHandler(AsyncContext asyncContext, int secs) {

super();

this.asyncContext = asyncContext;

this.secs = secs;

}

@Override

public void run() {

//注意:在请求和响应时使用AsyncContext对象,

//然后在完成时调用 asyncContext.complete() 方法。

System.out.println("支持异步Servlet? "

+ asyncContext.getRequest().isAsyncSupported());

longTimeWorking(secs); //耗时业务处理

try {

asyncContext.getResponse().setCharacterEncoding("utf-8");

asyncContext.getResponse().setContentType("text/html;charset=UTF-8");

PrintWriter out = asyncContext.getResponse().getWriter();

System.out.println("处理完成,耗时: " + secs + " 毫秒!!");//控制台输出

out.write("处理完成,耗时: " + secs + " 毫秒!!"); //前段输出

} catch (IOException e) {

e.printStackTrace();

}

//完成处理

asyncContext.complete();

System.out.println("异步业务处理完成:asyncContext.complete()");

}

private void longTimeWorking(int secs) {

// 完成前等待的时间

try {

Thread.sleep(secs);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

3、创建业务异步上下文监听类:AppAsyncListener

@WebListener

public class AppAsyncListener implements AsyncListener {

/**

* Default constructor.

*/

public AppAsyncListener() {

}

/**

* @see AsyncListener#onComplete(AsyncEvent)

*/

public void onComplete(AsyncEvent ae) throws java.io.IOException {

System.out.println("异步Servlet业务处理完成,触发了此时间:"+System.currentTimeMillis());

System.out.println("AppAsyncListener onComplete");

//可以执行其它资源清理工作

}

/**

* @see AsyncListener#onError(AsyncEvent)

*/

public void onError(AsyncEvent ae) throws java.io.IOException {

System.out.println("AppAsyncListener onError");

//ae.getAsyncContext().getResponse().getWriter().println("业务处理错误");

//像客户端返回错误

}

/**

* @see AsyncListener#onStartAsync(AsyncEvent)

*/

public void onStartAsync(AsyncEvent ae) throws java.io.IOException {

System.out.println("触发异步侦听:AppAsyncListener onStartAsync"+System.currentTimeMillis());

//记录异步处理触发日志信息:根据需要进行

}

/**

* @see AsyncListener#onTimeout(AsyncEvent)

*/

public void onTimeout(AsyncEvent asyncEvent) throws java.io.IOException {

System.out.println("AppAsyncListener onTimeout");

System.out.println("允许的处理时间为:"+asyncEvent.getAsyncContext().getTimeout()+"毫秒。");

//we can send appropriate response to client

ServletResponse response = asyncEvent.getAsyncContext().getResponse();

PrintWriter out = response.getWriter();

out.write("处理超时:==TimeOut Error in Processing=="+System.currentTimeMillis());

}

}

4、创建具有异步处理能力的Servlet类:MyAsyncLongWorkServlet

import java.io.IOException;

import java.util.concurrent.ThreadPoolExecutor;

import javax.servlet.AsyncContext;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

/**

* Servlet implementation class MyAsyncServlet

*/

@WebServlet(asyncSupported = true, urlPatterns = { "/malServlet" })

public class MyAsyncLongWorkServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

/**

* @see HttpServlet#HttpServlet()

*/

public MyAsyncLongWorkServlet() {

super();

}

/**

* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)

*/

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

long startTime = System.currentTimeMillis();

System.out.println("MyAsyncLongWorkServlet Start::Name="

+ Thread.currentThread().getName() + "::ID="

+ Thread.currentThread().getId());

request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);

String time = request.getParameter("time");

int secs = Integer.valueOf(time);

// max 10 seconds

if (secs > 10000)

secs = 10000;

AsyncContext asyncCtx = request.startAsync();

asyncCtx.addListener(new AppAsyncListener());

asyncCtx.setTimeout(9000); //设置超时处理时间,若超时此值,后台将报错。

ThreadPoolExecutor executor = (ThreadPoolExecutor) request

.getServletContext().getAttribute("executor");

executor.execute(new AsyncRequestHandler(asyncCtx, secs));

long endTime = System.currentTimeMillis();

System.out.println("MyAsyncLongWorkServlet End::Name="

+ Thread.currentThread().getName() + "::ID="

+ Thread.currentThread().getId() + "::Time Taken="

+ (endTime - startTime) + " ms.");

}

/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

doGet(request, response);

}

}

接下来启动服务器(本人用的Tomcat8.0),保证启动正常,在浏览器中输入如下地址回车,观看控制台和前端响应:

MyWebApp/malServlet?time=8999

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

热门产品

php编程基础教程.pptx|php编程培训,php,编程,基础,教程,pptx
php编程基础教程.pptx

历史上的今天:04月29日

热门专题

中源管业|中源管业,中源管业公司,中源管业有限公司,中源管业电话,中源管业地址,中源管业电力管,中源管业mpp电力管,中源管业cpvc电力管,中源管业pe穿线管
中源管业
一年制中专|中专学历,中专是什么学历,中专是什么,中专有什么专业,中专升大专,一年制中专
一年制中专
天麻的功效与作用吃法|天麻的功效与作用,天麻的功效与作用吃法,天麻炖什么治头痛最好,天麻的功效与作用禁忌,天麻多少钱一斤,天麻的功效与作用吃法及禁忌,天麻怎么吃效果最好,天麻粉的功效与作用,天麻怎么吃
天麻的功效与作用吃法
综合高中|云南综合高中,昆明综合高中,综合高中能考本一吗,综合高中和普通高中的区别,综合高中是什么意思,综合高中能参加全国统一高考吗,综合高中可以考哪些大学,综合高中的学籍是什么
综合高中
云南高职单招|云南单招,云南单招网,云南高职单招网,云南高职单招,云南单招学校,云南单招培训
云南高职单招
易捷尔单招|易捷尔单招,易捷尔单招培训,易捷尔单招报名,易捷尔单招考试,易捷尔单招培训学校,易捷尔单招分数
易捷尔单招
云南开放大学|云南开放大学报名,云南开放大学报考,云南开放大学,什么是云南开放大学,云南开放大学学历,云南开放大学学费,云南开放大学报名条件,云南开放大学报名时间,云南开放大学学历,云南开放大学专业
云南开放大学
开放大学|开放大学报名,开放大学报考,开放大学,什么是开放大学,开放大学学历,开放大学学费,开放大学报名条件,开放大学报名时间,开放大学学历,开放大学专业
开放大学

微信小程序

微信扫一扫体验

立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部