【问题标题】:Tomcat filter does not support asynchronyTomcat 过滤器不支持异步
【发布时间】:2021-06-07 22:46:39
【问题描述】:

如标题

@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

【问题讨论】:

    标签: java asynchronous tomcat filter


    【解决方案1】:

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

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

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

    (参见Servlet Specification

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

    1. new 请求通过过滤器时,您调用 ServletRequest.startAsync() 并启动新线程或使用任何其他执行器进行异步过滤(例如 AsyncContext.start(Runnable)),
    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 :
          }
       }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-03
      • 2014-09-14
      • 2015-01-09
      • 2018-10-01
      • 1970-01-01
      • 2015-08-02
      • 1970-01-01
      • 2019-11-22
      相关资源
      最近更新 更多