当前位置: 首页 >服务端 > Servlet 异步处理

Servlet 异步处理

web容器会为每个请求分配一个线程,Servlet3.0新增了异步处理,解决多个线程不释放占据内存的问题。可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成后再对客户端进行响应。

一、AsyncContex简介

    为了支持异步处理,在ServletRequest上提供了startAsync()方法。可以通过AsyncContext的getRequest()和getResponse()方法取得请求、响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()或dispatch()方法为止。

    首先要告知此容器支持Servlet异步处理,如:

  1: @WebServlet(urlPattes="/some.do", asyncSupported = true)
  2: public class AsyncServlet extends HttpServlet{
  3: 
  4: }

    例1:异步处理的例子

AsyncServlet.java

  1: package ServletAPI;
  2: 
  3: import java.io.IOException;
  4: import java.util.concurrent.ExecutorService;
  5: import java.util.concurrent.Executors;
  6: import javax.servlet.AsyncContext;
  7: import javax.servlet.ServletException;
  8: import javax.servlet.annotation.WebServlet;
  9: import javax.servlet.http.HttpServlet;
 10: import javax.servlet.http.HttpServletRequest;
 11: import javax.servlet.http.HttpServletResponse;
 12: 
 13: /**
 14:  * Servlet implementation class AsyncServlet
 15:  */
 16: @WebServlet(name = "AsyncServlet", urlPattes = { "/async.do" },asyncSupported=true)
 17: public class AsyncServlet extends HttpServlet {
 18: private static final long serialVersionUID = 1L;
 19: private ExecutorService executorService=Executors.newFixedThreadPool(10);
 20: /**
 21:  * @see HttpServlet#HttpServlet()
 22:  */
 23: public AsyncServlet() {
 24: super();
 25: // TODO Auto-generated constructor stub
 26: }
 27: 
 28: /**
 29:  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 30:  */
 31: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 32:// TODO Auto-generated method stub
 33:response.setContentType("text/html;charset=UTF-8");
 34:AsyncContext ctx=request.startAsync();//开始异步处理,释放请求线程
 35:executorService.submit(new AsynvRequest(ctx)); //创建AsyncRequest,调度线程
 36: 
 37: }
 38: 
 39: /**
 40:  * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
 41:  */
 42: protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 43:// TODO Auto-generated method stub
 44: }
 45: public void destroy(){
 46:executorService.shutdown();//关闭线程池
 47: }
 48: 
 49: }
 50: 

    首先告诉容器,这个Servlet支持异步处理,对于每个请求,Servlet会取得其AsyncContext,并释放容器所分配的线程,响应被延迟后。对于这些被延迟后响应的请求,创建一个实现Runnable接口的AsyncRequest对象,并将其调度一个固定数量的线程池,让这些必须长时间处理的请求,在线程池中完成,不用每次分配线程。

    例2:AsyncRequest是个实现Runnable的类,其模拟了长时间处理。

AsyncRequest.java

  1: package ServletAPI;
  2: 
  3: import java.io.PrintWriter;
  4: import javax.servlet.AsyncContext;
  5: public class AsynvRequest implements Runnable{
  6:private AsyncContext ctx;
  7: 
  8:public AsynvRequest(AsyncContext ctx) {
  9: super();
 10: this.ctx = ctx;
 11:}
 12: 
 13:@Override
 14:public void run() {
 15: // TODO Auto-generated method stub
 16: try {
 17:Thread.sleep(10000);//模拟冗长请求
 18:PrintWriter out=ctx.getResponse().getWriter();
 19:out.println("久等了...XD");//输出结果
 20:ctx.complete();//对客户端完成响应
 21: } catch (Exception e) {
 22:// TODO Auto-generated catch block
 23:throw new RuntimeException(e);
 24: }
 25: 
 26:}
 27:
 28: }
 29: 

    以暂停线程的方式来模拟长时间处理,并输出简单的文字,最后调用complete()对客户端完成响应。

二、模拟服务器推播

    HTTP是基于请求、响应模型,如果客户端要获得服务器的最新状态,就必须以定期方式发送请求,查询服务器端的最新状态。

    Servlet 3.0提供的异步处理技术,可以解决每个请求占用线程的问题,再结合Ajax异步请求技术,就可以达到类似服务器主动通知浏览器的行为。这就是所谓的服务器端推播。

    例3:模拟应用程序不定期产生最新数据,这个部分由实现ServletContextListener的类负责,会在程序启动时进行。

WebInitListener.java

  1: package ServletAPI;
  2: 
  3: import java.util.ArrayList;
  4: import java.util.List;
  5: import javax.servlet.AsyncContext;
  6: import javax.servlet.ServletContextEvent;
  7: import javax.servlet.ServletContextListener;
  8: import javax.servlet.annotation.WebListener
  9: 
 10: /**
 11:  * Application Lifecycle Listener implementation class WebInitListener
 12:  *
 13:  */
 14: @WebListener
 15: public class WebInitListener implements ServletContextListener {
 16: private List<AsyncContext> asyncs=new ArrayList<>();//所有的异步请求AsyncContext将存储在这个List中。
 17: 
 18: public void contextDestroyed(ServletContextEvent arg0) {
 19: // TODO Auto-generated method stub
 20: }
 21: 
 22:/**
 23:  * @see ServletContextListener#contextInitialized(ServletContextEvent)
 24:  */
 25: public void contextInitialized(ServletContextEvent arg0) {
 26: // TODO Auto-generated method stub
 27:new Thread(new Runnable(){
 28: public void run(){
 29:while(true){
 30: try {//模拟产生随机数字
 31: Thread.sleep((int)(Math.random()*10000));
 32: double num=Math.random()*10;
 33: synchronized (asyncs) {
 34:for(AsyncContext ctx:asyncs){
 35: ctx.getResponse().getWriter().println(num);
 36: ctx.complete();
 37:}
 38: }
 39:} catch (Exception e) {
 40: // TODO Auto-generated catch block
 41: throw new RuntimeException();
 42:}
 43:}
 44: }
 45:}).start();
 46: }
 47:
 48: }
 49: 

    有个List会存储所有的异步请求的AsyncContext,并在不定时产生数字后,逐一对客户端响应,并调用AsyncContext的conmplete()来完成请求。

    负责接收请求的Servlet,一收到请求,就将之加入到List中。

AsyncNumServlet.java

  1: package ServletAPI;
  2: 
  3: import java.io.IOException;
  4: import java.util.List;
  5: import javax.servlet.AsyncContext;
  6: import javax.servlet.ServletException;
  7: import javax.servlet.annotation.WebServlet;
  8: import javax.servlet.http.HttpServlet;
  9: import javax.servlet.http.HttpServletRequest;
 10: import javax.servlet.http.HttpServletResponse;
 11: 
 12: @WebServlet(name = "AsyncNumServlet", urlPattes = { "/asyncNum.do" }, asyncSupported=true)
 13: public class AsyncNumServlet extends HttpServlet {
 14: private static final long serialVersionUID = 1L;
 15: private List<AsyncContext> asyncs; 
 16: 
 17: public void init() throws ServletException{
 18:asyncs=(List<AsyncContext>)getServletContext().getAttribute("asyncs");
 19:
 20: }
 21: /**
 22:  * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
 23:  */
 24: protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 25:// TODO Auto-generated method stub
 26:AsyncContext ctx=request.startAsync();//开始异步处理
 27:synchronized (asyncs) {
 28: asyncs.add(ctx);//加入维护AsyncContext的List中
 29:}
 30: }
 31: 
 32: }
 33: 

    由于List是储存为ServletContext属性,所以在Servlet中,必须从ServletContext中取出,每次请求到来时,调用HttpServletRequest的startAsync()进行异步处理,并取得AsyncContext加入维护AsyncContext的List中。

    可以使用一个简单的HTML,使用Ajax技术,发送异步请求值服务器端,这个请求会被延迟,直到服务器端完成响应后,更新网页上的资料,并再度发送异步请求:

async.html

  1: <!DOCTYPE html>
  2: <html>
  3: <head>
  4: <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
  5: <title>实时资料</title>
  6: <script>
  7:function asyncUpdate(){
  8: var xhr;
  9: if(window.XMLHttpRequest){
 10:xhr=new XMLHttpRequest();
 11: }else
 12: if(window.ActiveXObject){
 13:xhr=new ActiveXObject('Microsoft.XMLHTTP');
 14: }
 15: xhr.onreadystatechange=function(){
 16:if(xhr.readyState==4){
 17: if(xhr.status==200){
 18:document.getElementById("data").innerHTML=xhr.responseText;
 19:asyncUpdate();
 20: }
 21:}
 22: };
 23: xhr.open('GET','asyncNum.do?timestamp='+new Date().getTime());
 24: xhr.send(null);
 25:}
 26:window.onload=asyncUpdate;
 27: </script>
 28: </head>
 29: <body>
 30:实时资料:<span id="data">0</span>
 31: </body>
 32: </html>

    可以试着用多个浏览器请求这个页面,会看到每个浏览器的资料是同步的。

作者:liurio
来源链接:https://www.cnblogs.com/liuzhongfeng/p/6107594.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaclub.cn/server/113153.html

标签:Servlet
分享给朋友:

“Servlet 异步处理” 的相关文章

pycharm安装教程,超详细 2022年05月16日 21:28:02
划分子网和构造超网的学习 2022年05月17日 13:57:19
Logback日志框架 2022年05月19日 20:04:05
炒冷饭系列:设计模式 原型模式 2022年05月30日 22:50:53
为了应付优性测评而自欺欺人 2022年06月02日 22:00:25
Python安装与环境变量配置 2022年06月08日 00:03:17
python2和python3的pip安装 2022年06月08日 22:15:42
Win10+vscode+Python安装 2022年06月11日 08:31:02