场景
-
JavaWeb
的Servlet 3.0
规范已经支持异步ServletRequest
, 有这个异步请求,容器(Tomcat
)线程池只需要少量的线程即可处理大量的并发请求,因为处理请求的线程可以把异步AsyncContext
交给业务线程池处理之后立即返回。Node.js
的异步IO
应该也是用的这种技术,用单线程处理异步请求IO
。那么在使用Jfinal
框架时如何引入异步ServletRequest
?
说明
JavaWeb
的常规容器处理用户请求都是通过容器内定义的线程池进行处理。当所有线程处理的业务耗时比较长的时候,比如2
秒,线程池里没有空余的线程响应用户的请求,就会造成容器阻塞,丢失接下来的所有用户请求。如果用异步IO
就没这种情况,只要线程池的线程提交到业务线程足够快,那么就能一直处理用户请求数据,不会丢失用户请求。而业务线程使用完之后,新提交的任务会进入等待,直到内存耗尽才有可能丢失用户请求。- 要使用异步
Request
,那么需要先配置部署描述符web.xml
或者代码配置Filter
的异步支持。添加<async-supported>
元素值为true
。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee web-app_3_0.xsd"
version="3.0">
<filter>
<filter-name>jfinal</filter-name>
<filter-class>com.jfinal.core.JFinalFilter</filter-class>
<init-param>
<param-name>configClass</param-name>
<param-value>com.demo.common.DemoConfig</param-value>
</init-param>
<async-supported>true</async-supported>
</filter>
<filter-mapping>
<filter-name>jfinal</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
- 如果使用
undertow
,那么通过编码在UndertowServer
的子类重载configJFinalFilter
加入异步支持。
@Override
protected void configJFinalFilter() {
FilterInfo filterInfo = Servlets.filter("jfinal", getJFinalFilter());
filterInfo.setAsyncSupported(true);
filterInfo.addInitParam("configClass", config.getJFinalConfig());
filterInfo.addInitParam("staticResourcePostfix","jpg;webp;gif;png;js;css;map;ttf;");
deploymentInfo.addFilter(filterInfo).addFilterUrlMapping("jfinal", "/*", DispatcherType.REQUEST);
}
- 如果不加入这个异步支持的配置,会报以下错误:
UT010026: Async is not supported for this request, as not all filters or Servlets were marked as supporting async
- 在获取异步
IO
,首先就要调用Servlet.startAsync
得到一个AsyncContext
异步上下文,他表示把请求放入异步模式。在将此请求放入异步模式后,在出站方向调用的任何过滤器都可以使用(AsyncContext.hasOriginalRequestAndResponse(), 一直返回true
)这一点作为一个指示,即它们在入站调用期间添加的任何请求和响应包装器都不需要在异步操作期间存在,因此它们的任何关联资源都可以被释放。 - 通过
asyncContext.getRequest()
和asyncContext.getResponse()
来获取原始的ServletRequest
和ServletResponse
对象。比如获取参数request.getParameter()
或者获取输出流response.getOutputStream()
. - 在使用
jfinal
的时候,如果在Controller
开始异步模式,需要执行renderNull();
来执行一个空渲染,就是不调用response
操作,避免response
关闭。 - 在异步线程执行完之后任务之后,需要调用
AsyncContext.complete()
来关闭异步模式,这时候response
会被关闭。 注意,异步模式启用关闭有可能会抛出异常,最好把它放在异常处理代码里。可以封装一个Controller
子类来处理异步模式的启用和完成。
例子
- 这个例子是使用异步模式来删除博客文章。关于
jfinal
的博客例子项目教程参考学院课程.开发JavaWeb网站精讲-基于JFinal框架[5],这里有很多常用功能的例子. 当然也可以去jfinal
官网下载原始的例子项目[6]。
BaseController.java
package com.demo.common;
import com.jfinal.core.Controller;
import com.jfinal.kit.LogKit;
import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class BaseController extends Controller {
protected AsyncContext asyncContext = null;
protected AsyncContext startAsync(){
try {
asyncContext = getRequest().startAsync();
asyncContext.addListener(new MyAsyncListener());
setHttpServletRequest((HttpServletRequest) asyncContext.getRequest());
setHttpServletResponse((HttpServletResponse) asyncContext.getResponse());
renderNull();
}catch (Exception e){
if(asyncContext != null) {
asyncContext.complete();
asyncContext = null;
}
LogKit.error(e.getMessage());
}
return asyncContext;
}
protected void completeAsync(){
if(asyncContext == null)
return;
try{
asyncContext.complete();
}catch (Exception e){
LogKit.error(e.getMessage());
}
}
public void render(){
if(asyncContext == null)
return;
try {
getRender().setContext((HttpServletRequest) asyncContext.getRequest(),
(HttpServletResponse) asyncContext.getResponse()).render();
}catch (Exception e){
LogKit.error(e.getMessage());
}
}
}
BlogController.java
public class BlogController extends BaseController {
...
public void delete() {
startAsync();
ThreadPoolKit.execute(()->{
service.deleteById(getParaToInt());
redirect("/blog");
render();
completeAsync();
});
}
DemoConfig
public void configConstant(Constants me) {
int processors = Runtime.getRuntime().availableProcessors();
ThreadPoolKit.init(processors);
参考
- 关于servlet3.0中的异步servlet
- jfinal如何配置异步的context
- Async 拦截器
- ServletRequest
- 开发JavaWeb网站精讲-基于JFinal框架-1
- JFinal 极速开发