Servlet3.0中支持的异步处理

72 篇文章 18 订阅
56 篇文章 15 订阅

【1】HelloAsyncServlet

在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求,即每一次Http请求都由某一个线程从头到尾负责处理。

这里写图片描述

如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。

为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。


实例代码

asyncSupported=true开启异常支持,默认为false

@WebServlet(value="/async",asyncSupported=true)
public class HelloAsyncServlet extends HttpServlet {	
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		//1、支持异步处理asyncSupported=true
		//2、开启异步模式
		System.out.println("主线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
		AsyncContext startAsync = req.startAsync();
		//3、业务逻辑进行异步处理;开始异步处理
		startAsync.start(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
					sayHello();
					//获取到异步上下文
					AsyncContext asyncContext = req.getAsyncContext();
					//4、获取响应
					ServletResponse response = asyncContext.getResponse();
					response.getWriter().write("hello async...");
					System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
					startAsync.complete();
				} catch (Exception e) {
				}
			}
		});		
		System.out.println("主线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
	}

	public void sayHello() throws Exception{
		System.out.println(Thread.currentThread()+" processing...");
		Thread.sleep(3000);
	}
}

测试结果如下:

主线程开始。。。Thread[http-apr-8080-exec-7,5,main]==>1524209187508
主线程结束。。。Thread[http-apr-8080-exec-7,5,main]==>1524209187560
副线程开始。。。Thread[http-apr-8080-exec-9,5,main]==>1524209187560
Thread[http-apr-8080-exec-9,5,main] processing...
asyncContext..org.apache.catalina.core.AsyncContextImpl@e9f2202
副线程结束。。。Thread[http-apr-8080-exec-9,5,main]==>152420919900

【2】ThreadPoolAsyncHelloServlet

自己手动创建新线程一般是不被鼓励的,并且此时线程不能重用。因此,一种更好的办法是我们自己维护一个线程池。这个线程池不同于Servlet容器的主线程池,如下图:

这里写图片描述


实例代码如下:

@WebServlet(value = "/threadPoolAsync", asyncSupported = true)
public class ThreadPoolAsyncHelloServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	private static ThreadPoolExecutor executor = 
	new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

    protected void doGet(HttpServletRequest request, HttpServletResponse response) {
        AsyncContext startAsync = request.startAsync();
        executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
					sayHello();
					//获取到异步上下文
					AsyncContext asyncContext = request.getAsyncContext();
					//4、获取响应
					ServletResponse response = asyncContext.getResponse();
					response.getWriter().write("hello async...");
					System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
					startAsync.complete();
				} catch (Exception e) {
				}
			}
		});
    }
    
    public void sayHello() throws Exception{
		System.out.println(Thread.currentThread()+" processing...");
		Thread.sleep(3000);
	}
}

测试结果如下:

副线程开始。。。Thread[pool-1-thread-1,5,main]==>1524210139124
Thread[pool-1-thread-1,5,main] processing...
副线程结束。。。Thread[pool-1-thread-1,5,main]==>1524210142125

【3】NonBlockingAsyncHelloServlet

Servlet 3.0对请求的处理虽然是异步的,但是对InputStreamOutputStream的IO操作却依然是阻塞的,对于数据量大的请求体或者返回体,阻塞IO也将导致不必要的等待。

因此在Servlet 3.1中引入了非阻塞IO(参考下图红框内容),通过在HttpServletRequestHttpServletResponse中分别添加ReadListener和WriterListener方式,只有在IO数据满足一定条件时(比如数据准备好时),才进行后续的操作。

这里写图片描述


实例代码如下:

import javax.servlet.AsyncContext;
import javax.servlet.ReadListener;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@WebServlet(value = "/nonBlockingThreadPoolAsync", asyncSupported = true)
public class NonBlockingAsyncHelloServlet extends HttpServlet {

    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(100, 200, 50000L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(100));

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

        AsyncContext startAsync = request.startAsync();

        ServletInputStream inputStream = request.getInputStream();

        inputStream.setReadListener(new ReadListener() {
            @Override
            public void onDataAvailable() throws IOException {

            }
            @Override
            public void onAllDataRead() throws IOException {
                executor.execute(new Runnable() {
        			@Override
        			public void run() {
        				try {
        					System.out.println("副线程开始。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
        					sayHello();
        					//获取到异步上下文
        					AsyncContext asyncContext = request.getAsyncContext();
        					//4、获取响应
        					ServletResponse response = asyncContext.getResponse();
        					response.getWriter().write("hello async...");
        					System.out.println("副线程结束。。。"+Thread.currentThread()+"==>"+System.currentTimeMillis());
        					startAsync.complete();
        				} catch (Exception e) {
        				}
        			}
        		});
            }

            @Override
            public void onError(Throwable t) {
            	startAsync.complete();
            }
        });
    }
    
    public void sayHello() throws Exception{
		System.out.println(Thread.currentThread()+" processing...");
		Thread.sleep(3000);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值