Tomcat过滤器不支持异步

时间:2021-06-07 22:46:39

标签: java asynchronous tomcat filter

比如标题

@WebFilter("/*")
@Component
@Slf4j
public class FilterDemo1 implements Filter {

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException, IOException {
        new Thread(() -> {
            try {
                chain.doFilter(req, resp);
            } catch (Exception e) {
                log.error("a", e);
            }
        }).start();
     
    }

}

因此,如果Tomcat过滤器中有耗时任务(如RPC或HTTP),必须等待,不能异步

java.lang.NullPointerException: null
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]```


[error][1]

  [1]: https://i.stack.imgur.com/tFYTH.png

1 个答案:

答案 0 :(得分:1)

ServletRequestServletResponseFilterChain 对象仅在调用 doFilter 的线程上有效。如果要异步使用它们,则需要启用异步处理(参见 Jakarta EE Tutorial)。

ServletRequest.startAsync()ServletRequestServletResponse 置于异步模式,但 FilterChain 只能用于原始线程:

<块引用>

service 方法需要与应用于 servlet 的所有过滤器在同一线程中运行。

(参见Servlet Specification

因此您需要进行如下操作:

  1. 请求通过过滤器时,您调用ServletRequest.startAsync()并启动您的新线程或使用任何其他执行器进行异步过滤(例如AsyncContext.start(Runnable)),< /li>
  2. 异步任务完成后,将结果写入请求属性并调用AsyncContext.dispatch():这将从头开始重新启动过滤器链,
  3. 当再次调用 doFilter 时,您使用请求的属性来执行过滤逻辑并调用 FilterChain.doFilter

例如,您可以使用以下内容:

@WebFilter(asyncSupported = true, urlPatterns = {"/*"}, dispatcherTypes = {DispatcherType.ASYNC, DispatcherType.REQUEST})
public class Filter1 implements Filter {

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
      switch (request.getDispatcherType()) {
         case REQUEST :
            // First pass: start asynchronous processing
            final AsyncContext asyncContext = request.startAsync();
            new Thread(() -> {
               try {
                  Thread.currentThread().sleep(5000);
               } catch (Exception e) {
                  request.setAttribute("filter1Error", e);
               }
               asyncContext.dispatch();
            }).start();
            break;
         case ASYNC :
            // Second pass: throw or forward
            Exception e = (Exception) request.getAttribute("filter1Error");
            if (e instanceof IOException) {
               throw (IOException) e;
            } else if (e instanceof ServletException) {
               throw (ServletException) e;
            } else if (e != null) {
               throw new ServletException(e);
            }
            chain.doFilter(request, response);
            break;
         default :
      }
   }