异步请求api调用说明
在编写实际代码之前,我们来了解下一些关于异步请求的api的调用说明。
获取AsyncContext:根据HttpServletRequest对象获取
AsyncContext asyncContext = request.startAsync();
设置监听器:可设置其开始、完成、异常、超时等事件的回调处理
public interface AsyncListener extends EventListener {
void onComplete(AsyncEvent event) throws IOException;
void onTimeout(AsyncEvent event) throws IOException;
void onError(AsyncEvent event) throws IOException;
void onStartAsync(AsyncEvent event) throws IOException;
}
说明
- onStartAsync:异步线程开始时调用
- onError:异步线程出错时调用
- onTimeout:异步线程执行超时调用
- onComplete:异步执行完毕时调用
一般上,我们在超时或者异常时,会返回给前端相应的提示,比如说超时了,请再次请求等等,根据各业务进行自定义返回。同时,在异步调用完成时,一般需要执行一些清理工作或者其他相关操作。
需要注意的是只有在调用request.startAsync
前将监听器添加到AsyncContext
,监听器的onStartAsync
方法才会起作用,而调用startAsync
前AsyncContext
还不存在,所以第一次调用startAsync
是不会被监听器中的onStartAsync
方法捕获的,只有在超时后又重新开始的情况下onStartAsync
方法才会起作用。
设置超时:通过setTimeout
方法设置,单位:毫秒。
一定要设置超时时间,不能无限等待下去,不然和正常的请求就一样了。
Servlet方式实现异步请求
面已经提到,可通过HttpServletRequest
对象中获得一个AsyncContext
对象,该对象构成了异步处理的上下文。所以,我们来实际操作下。
编写一个简单控制层
package Net.XiangCaoWuYu.Controllers;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* ClassName: AsyncController <br/>
* Description: <br/>
* date: 2019/7/28 16:06<br/>
*
* @author lisen01<br />
* @since JDK 1.8
*/
@RestController
@Api(tags = "测试异步请求")
@ResponseBody
@RequestMapping(value = "/AsyncTest")
public class ServletAsyncController {
private final static Logger logger = LoggerFactory.getLogger(ServletAsyncController.class);
@PostMapping(value = "test")
@ApiOperation(value = "测试异步请求")
public void test(HttpServletRequest request, HttpServletResponse response) {
AsyncContext context = request.startAsync();
context.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
logger.info("执行完成");
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
logger.info("执行超时:");
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
logger.info("发生错误:" + asyncEvent.getThrowable());
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
logger.info("开始执行:");
}
});
//设置超时
context.setTimeout(2000);
//开启线程
context.start(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(100);
logger.info("内部线程:"+Thread.currentThread().getName());
context.getResponse().setCharacterEncoding("UTF-8");
context.getResponse().setContentType("text/html;charset=utf-8");
context.getResponse().getWriter().println("这是内部线程");
//异步请求完成通知
//此时整个请求才完成
//其实可以利用此特性 进行多条消息的推送 把连接挂起。。
context.complete();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
//此时之类 request的线程连接已经释放了
logger.info("线程:" + Thread.currentThread().getName());
}
}
注意:异步请求时,可以利用ThreadPoolExecutor
自定义个线程池
使用过滤器时,需要加入asyncSupported为true配置,开启异步请求支持
@WebServlet(urlPatterns = "/okong", asyncSupported = true )
public class AsyncServlet extends HttpServlet ...
太好了。