Servlet3.0新特性之异步请求实践

 

1、为什么要使用异步Servlet?

 

非常适用于以下情况的Web应用程序

 

—— 长处理时间或者伪长处理时间

—— 等待资源释放——如数据库连接

—— 等待事件发生——如聊天消息

—— 等待缓慢服务的响应——如Web服务

 

释放当前请求处理以便处理其他请求

—— 为Web容器提供了更好的可伸缩性

 

浏览器将仍然显示为等待中

—— 用户体验没有改变

 

不要与异步I/O混淆

——目前的Servlet3.0不支持

 

2、AsyncContext 基本知识

 

调用HttpServletRequest.startAsync()将servlet置于异步模式

 

—— 流将不在从Servlet.service()返回时提交

—— 返回AsyncContext对象

 

AsyncContext.start()启动请求

 

—— 将其指派给工作线程

 

AsyncContext.dispatch()

—— 通知工作已经完成

—— 将控件传递回Web容器

 

在以前的Servlet规范化,如果Servlet作为控制器调用了一个耗时的业务方法,那么必须等到业务方法完全返回之后才能生成响应,这将使用Servlet对业务方法的调用变成一种阻塞式的调用,因此效率比较低。

Servlet3.0规范引入了异步处理来解决这个问题,异步处理允许Servlet重新发起一条新线程去调用 耗时业务方法,这样就可以避免等待。

Servlet3.0的异步处理是通过AsyncContext类来处理的,Servlet可通过ServletRequest的如下两个方法开启异步调用,创建AsyncContext对象:

AsyncContext startAsync()

AsyncContext startAsync(ServletRequest,ServletResponse)

 

3、Servlet3.0异步请求实例

 

这个例子只有2文件,一个Servlet和一个Jsp,代码如下:

AsynServlet.java

 

package org.test;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 
 * @ClassName: AsynServlet  
 * @description: 使用批注的方式,指定这个Servlet的映射URL,后面那个配置是设置其是支持异步请求
 * 
 * 1、其它创建一个异步请求和我们平常使用Servlet没什么区别,当然Filter也可以设置为异步的。
 * 2、但是在使用异步请求,我们需要注意一些东西:
 * 1)异步请求完成后指定向的JSP页面,session必须设定为false
 * 2)AsyncListener监听中监听开始在实际无效
 * 3)如果存在过滤器过滤该Servlet,则必须把Filter也要设置为asyncSupported=true,否则会出现异常,提示不支持异步
 *  
 * @createDate: 2013-5-30 下午4:59:31
 * @author linyaoliang 
 * @version
 */
@WebServlet(urlPatterns="/async",asyncSupported=true)
public class AsynServlet extends HttpServlet
{

    private static final long serialVersionUID = 5419388337018572479L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
    {
        resp.setContentType("text/html;charset=GBK");
        PrintWriter out = resp.getWriter();
        out.println("<title>异步调用示例</title>");
        out.println("进入Servlet的时间:"+new java.util.Date()+".<br>");
        out.flush();
        if(req.getAsyncContext() != null)
        {
            System.out.println("异步上下文已经存在");
        }
        //创建AsyncContext,开始异步调用
        AsyncContext actx = req.startAsync();
        actx.addListener(new MyAsyncListener());
        //设置异步调用的超时时长
        actx.setTimeout(30*1000);
        //异步请求上下文启动异步处理线程
        actx.start(new Executor(actx));
        //启动后,输出结束时间
        out.println("结束Servlet的时间:"+new java.util.Date()+".<br>");
        out.flush();
    }

    /**
     * 
     * @ClassName: Executor  
     * @description: 这个是异步请求需要启动来处理长时间业务的线程  
     * @createDate: 2013-5-30 下午4:56:55
     * @author linyaoliang 
     * @version AsynServlet
     */
    class Executor implements Runnable{

        private AsyncContext actx = null;
        
        public Executor(AsyncContext actx)
        {
            this.actx = actx;
        }

        @Override
        public void run()
        {
            try
            {
                System.out.println("====================");
                Thread.sleep(5*1000);
                ServletRequest request = actx.getRequest();
                List<String> books = new ArrayList<String>();
                books.add("Java Programer");
                books.add("Strut Spring hibernate");
                books.add("Test Action");
                request.setAttribute("books", books);
                System.out.println("||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
                //当该线程完成后,则返回指定的到这个JSP页面,这个JSP页面必须指定session="false",在这个JSP页面里你可以看到
                actx.dispatch("/async.jsp");
            }
            catch (InterruptedException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            
        }
    }
    /**
     * 
     * @ClassName: MyAsyncListener  
     * @description: 异步请求提供的监听  
     * @createDate: 2013-5-30 下午4:54:58
     * @author linyaoliang 
     * @version AsynServlet
     */
    class MyAsyncListener implements AsyncListener
    {

        @Override
        public void onComplete(AsyncEvent arg0) throws IOException
        {
           System.out.println("异步请求已经完成:"+arg0.getAsyncContext().getTimeout());
        }

        @Override
        public void onError(AsyncEvent arg0) throws IOException
        {
            System.out.println("异步请求出错了:"+arg0.getAsyncContext().getTimeout());
        }

        @Override
        public void onStartAsync(AsyncEvent arg0) throws IOException
        {
            //虽然提供了这个监听方法,但是实际没用,因为都是在已经启动异步请求后,得到AsynContext,再去绑定这个监听,可以在上面的代码中看到
            System.out.println("异步请求已经开始:"+arg0.getAsyncContext().getTimeout());
        }

        @Override
        public void onTimeout(AsyncEvent arg0) throws IOException
        {
            System.out.println("异步请求已经超时:"+arg0.getAsyncContext().getTimeout());
        }

    }
}

 

 

async.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8" session="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>异步响应的消息</title>
</head>
<body>
<ul>
	<c:forEach items="${books }" var="book">
		<li>${book }</li>
	</c:forEach>
</ul>
<%out.println("业务调用结束时间:"+new java.util.Date()); %>
</body>
</html>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值