关于servlet异步请求处理介绍参考这个博客点我1,点我2,本文代码参考这篇博客点我
异步Servlet
1.创建一个Servlet类
注意:1.开启异步功能必须要设置asyncSupported = true
import com.example.demo.listener.MyAsyncListener;
import com.example.demo.thread.AsyncHandler;
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;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* @author zinsanity
* @date 2020-05-20 16:26
* @desc
*/
@WebServlet(name = "asyncServlet", urlPatterns = "/asyncServlet", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
ScheduledThreadPoolExecutor userExecutor = new ScheduledThreadPoolExecutor(5);
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
AsyncContext ctx = req.startAsync(req, resp);
ctx.addListener(new MyAsyncListener());
userExecutor.execute(new AsyncHandler(ctx));
resp.getWriter().write("正在执行异步回调");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("AsyncServlet#doPost");
String body = getRequestBody(req);
System.out.println("####请求报文="+body);
// 不加这一行会导致中文乱码
resp.setContentType("application/json;charset=UTF-8");
AsyncContext ctx = req.startAsync(req, resp);
userExecutor.execute(new AsyncHandler(ctx));
ctx.addListener(new MyAsyncListener());
resp.getWriter().write("正在执行异步回调");
}
private String getRequestBody(HttpServletRequest request) {
try {
InputStream inputStream = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int x = 0;
while ((x = inputStream.read()) != -1) {
baos.write(x);
}
baos.flush();
return new String(baos.toByteArray(), UTF_8);
} catch (Exception e) {
System.out.println(e);
}
return "";
}
}
2.创建一个真正执行业务逻辑的线程类
package com.example.demo.thread;
import javax.servlet.AsyncContext;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
/**
* @author zinsanity
* @date 2020-05-20 16:28
* @desc
*/
public class AsyncHandler implements Runnable {
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
// 模拟耗时操作
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
PrintWriter pw = ctx.getResponse().getWriter();
pw.println("done!!!!");
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
// 调用complete方法通知回调,异步处理结束了
ctx.complete();
}
}
3.创建一个监听器
import javax.servlet.*;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author zinsanity
* @date 2020-05-20 16:27
* @desc
*/
public class MyAsyncListener implements AsyncListener {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步servlet【onComplete完成】");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
System.out.println("异步servlet错误");
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
System.out.println("开始异步servlet");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
ServletRequest request = asyncEvent.getAsyncContext().getRequest();
request.setAttribute("timeout", "true");
System.out.println("异步servlet【onTimeout超时】");
}
}
4.执行HTTP请求
http://localhost:8080/asyncServlet
响应结果
正在执行异步回调done!!!!
异步servlet+异步Filter
1.非注解方式创建两个Filter
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zinsanity
* @date 2020-05-14 10:24
* @desc
*/
public class FilterTest implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----FilterTest过滤器初始化----");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 对request和response进行一些预处理,比如说对报文的加解密、字符集的变更
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
System.out.println("FilterTest执行前!!!");
// 让目标资源执行,放行
filterChain.doFilter(request, response);
System.out.println("FilterTest执行后!!!");
}
@Override
public void destroy() {
System.out.println("----FilterTest过滤器销毁----");
}
}
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author zinsanity
* @date 2020-05-14 10:24
* @desc
*/
public class AsyncFilterTest implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----AsyncFilterTest过滤器初始化----");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 对request和response进行一些预处理,比如说对报文的加解密、字符集的变更
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
System.out.println("AsyncFilterTest执行前!!!");
// 让目标资源执行,放行
filterChain.doFilter(request, response);
System.out.println("AsyncFilterTest执行后!!!");
}
@Override
public void destroy() {
System.out.println("----AsyncFilterTest过滤器销毁----");
}
}
2.创建配置类,配置两个过滤器属性和执行顺序
import com.example.demo.filter.AsyncFilterTest;
import com.example.demo.filter.FilterTest;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.servlet.DispatcherType;
/**
* @author zinsanity
* @date 2020-05-20 20:09
* @desc
*/
@Configuration
public class MyFilterConfig {
@Bean
public FilterRegistrationBean<FilterTest> filterTestRegistration() {
FilterRegistrationBean<FilterTest> registration = new FilterRegistrationBean<FilterTest>();
registration.setFilter(new FilterTest());
registration.addUrlPatterns("/*");
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setOrder(1);
return registration;
}
@Bean
public FilterRegistrationBean<AsyncFilterTest> asyncFilterTestRegistration() {
FilterRegistrationBean<AsyncFilterTest> registration = new FilterRegistrationBean<AsyncFilterTest>();
registration.setFilter(new AsyncFilterTest());
registration.addUrlPatterns("/*");
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.ASYNC);
registration.setOrder(2);
return registration;
}
}
3.filter的asyncSupported属性(setAsyncSupported)
如果异步的servlet和这两个Filter相关联,这两个Filter过滤器也要配置asyncSupported = true(和开启异步的servlet相关的过滤器,一整条链上的都要设置asyncSupported允许才行)。如果异步的servlet前面的某个过滤器没有配置该属性(asyncSupported默认为false),将会如下错误。
java.lang.IllegalStateException: A filter or servlet of the current chain does not support asynchronous operations.
4.filter的dispatcherTypes(setDispatcherTypes)
根据查询资料,dispatcherTypes可以是一个或者一组dispatcherType,具体取值包括REQUEST、FORWEARD、INCLUDE、ERROR、ASYNC(servelet3.0新增)。 下图来自博客点我
5.执行HTTP请求
http://localhost:8080/asyncServlet
响应结果如下:
正在执行异步回调done!!!!
日志打印如下:
FilterTest执行前!!!
####请求报文={
"name": "zinsanity",
"deviceInfo1": "deviceInfo1"
}
FilterTest执行后!!!
异步servlet【onComplete完成】
从日志中可以看到AsyncFilterTest过滤器没有执行。
6.思考:怎么才能模拟一个异步请求被AsyncFilterTest过滤器拦截呢
(如何模拟这个场景呢?突然想到,如果一个异步的servlet去调用另外一个servlet,这个过滤器是不是就可以用到了。验证如下: 1.再增加一个servlet类
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import static java.nio.charset.StandardCharsets.UTF_8;
/**
* @author zinsanity
* @date 2020-05-20 16:05
* @desc
*/
@WebServlet(name = "firstServlet", urlPatterns = "/firstServlet", asyncSupported = true)
public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("FirstServlet#doGet");
resp.getWriter().append("firstServlet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
System.out.println("FirstServlet#doPost");
String body = getRequestBody(req);
System.out.println("####请求报文="+body);
String result = "{\"age\":\"18\",\"customerId\":\"1111031\",\"name\":\"zhangzaixing\"}";
PrintWriter out = resp.getWriter();
out.println(result);
//释放资源
out.flush();
out.close();
out = null;
/* resp.getOutputStream().write(result.getBytes("UTF-8"));
resp.getOutputStream().flush();*/
}
private String getRequestBody(HttpServletRequest request) {
try {
InputStream v_inputstream = request.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int x = 0;
while ((x = v_inputstream.read()) != -1) {
baos.write(x);
}
baos.flush();
return new String(baos.toByteArray(), UTF_8);
} catch (Exception e) {
System.out.println(e);
}
return "";
}
}
2.AsyncServlet转发到FirstServlet上面
由于AsyncServlet业务操作交给了另外一个线程A处理,因此转发也应该是A线程去转发,因此修改一下AsyncHandler的run方法
import javax.servlet.AsyncContext;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.TimeUnit;
/**
* @author zinsanity
* @date 2020-05-20 16:28
* @desc
*/
public class AsyncHandler implements Runnable {
private AsyncContext ctx;
public AsyncHandler(AsyncContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
// 模拟耗时操作
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 注意一定要加/这个字符
ctx.dispatch("/firstServlet");
}
}
3.执行HTTP请求
http://localhost:8080/asyncServlet
响应结果如下:
正在执行异步回调{
"age": "18",
"customerId": "1111031",
"name": "zinsanity"
}
日志打印如下:
FilterTest执行前!!!
AsyncServlet#doPost
####请求报文={
"name": "zinsanity",
"deviceInfo1": "deviceInfo1"
}
FilterTest执行后!!!
AsyncFilterTest执行前!!!
FirstServlet#doPost
####请求报文=
AsyncFilterTest执行后!!!
异步servlet【onComplete完成】
从日志中可以明确看到,请求进入AsyncServlet时,没有经过AsyncFilterTest过滤器;AsyncServlet转发给FirstServlet时,请求经过了AsyncFilterTest过滤器。
4.注意:AsyncServlet中往流中写用的是resp.getWriter().write("正在执行异步回调");,那么FirstServlet中往流写也要用resp.getWriter().write()。不能用如下写法,会报错。
resp.getOutputStream().write(result.getBytes("UTF-8"));`
resp.getOutputStream().flush();