Phil Clay
2018-10-18 20:39:53 UTC
Hi,
On Tomcat 9.0.12, what is the recommended way of having HTTP requests sent to a certain URL pattern be processed on a separate threadpool from all other requests?
I have found that the HTTP Connectors can only be configured with one executor, therefore all requests always get processed on that single executor.
I tried to implement a Filter that takes advantage of Servlet 3.0 async processing to delegate processing the remaining filterChain to a separate executor, but have found that the filterChain is released after the main thread finishes, and therefore is not usable on the downstream thread pool.
For example:
public class SeparateThreadPoolFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(SeparateThreadPoolFilter.class);
private final Executor executor;
public SeparateThreadPoolFilter(Executor executor) {
this.executor = executor;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
AsyncContext asyncContext = request.startAsync();
executor.execute(() -> {
try {
// This chain is no longer usable, because it is released in the main thread after doFilter returns
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
logger.error("Exception occurred while executing HTTP request on separate threadpool" + e);
} finally {
asyncContext.complete();
}
});
}
}
I have configured this as the first filter in the chain for the the desired url patterns. I create and register this filter programmatically, and pass the desired Executor into the constructor.
What I have observed is that after doFilter returns, Tomcat will release the filterChain here...
https://github.com/apache/tomcat/blob/acaeccf3df95b24904c5f0ca9dd0f553d8f43289/java/org/apache/catalina/core/StandardWrapperValve.java#L254
Releasing the filter chain makes it unusable, since it removes all the filters, and the servlet...
https://github.com/apache/tomcat/blob/acaeccf3df95b24904c5f0ca9dd0f553d8f43289/java/org/apache/catalina/core/ApplicationFilterChain.java#L295-L306
Therefore, a NullPointerException occurs within ApplicationFilterChain when it tries to invoke the servlet (which is now null)...
Exception in thread "worker-11" java.lang.NullPointerException
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.example.SeparateThreadPoolFilter.lambda$0(SeparateThreadPoolFilter.java:34)
So my questions are:
1) Is there another way of delegating processing of certain url patterns to a separate threadpool?
2) Is this a bug? i.e. should filterChains still be usable after doFilter returns if request.startAsync() has been called?
Thank you,
Phil
---------------------------------------------------------------------
To unsubscribe, e-mail: users-***@tomcat.apache.org
For additional commands, e-mail: users-***@tomcat.apache.org
On Tomcat 9.0.12, what is the recommended way of having HTTP requests sent to a certain URL pattern be processed on a separate threadpool from all other requests?
I have found that the HTTP Connectors can only be configured with one executor, therefore all requests always get processed on that single executor.
I tried to implement a Filter that takes advantage of Servlet 3.0 async processing to delegate processing the remaining filterChain to a separate executor, but have found that the filterChain is released after the main thread finishes, and therefore is not usable on the downstream thread pool.
For example:
public class SeparateThreadPoolFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(SeparateThreadPoolFilter.class);
private final Executor executor;
public SeparateThreadPoolFilter(Executor executor) {
this.executor = executor;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
AsyncContext asyncContext = request.startAsync();
executor.execute(() -> {
try {
// This chain is no longer usable, because it is released in the main thread after doFilter returns
chain.doFilter(request, response);
} catch (IOException | ServletException e) {
logger.error("Exception occurred while executing HTTP request on separate threadpool" + e);
} finally {
asyncContext.complete();
}
});
}
}
I have configured this as the first filter in the chain for the the desired url patterns. I create and register this filter programmatically, and pass the desired Executor into the constructor.
What I have observed is that after doFilter returns, Tomcat will release the filterChain here...
https://github.com/apache/tomcat/blob/acaeccf3df95b24904c5f0ca9dd0f553d8f43289/java/org/apache/catalina/core/StandardWrapperValve.java#L254
Releasing the filter chain makes it unusable, since it removes all the filters, and the servlet...
https://github.com/apache/tomcat/blob/acaeccf3df95b24904c5f0ca9dd0f553d8f43289/java/org/apache/catalina/core/ApplicationFilterChain.java#L295-L306
Therefore, a NullPointerException occurs within ApplicationFilterChain when it tries to invoke the servlet (which is now null)...
Exception in thread "worker-11" java.lang.NullPointerException
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at com.example.SeparateThreadPoolFilter.lambda$0(SeparateThreadPoolFilter.java:34)
So my questions are:
1) Is there another way of delegating processing of certain url patterns to a separate threadpool?
2) Is this a bug? i.e. should filterChains still be usable after doFilter returns if request.startAsync() has been called?
Thank you,
Phil
---------------------------------------------------------------------
To unsubscribe, e-mail: users-***@tomcat.apache.org
For additional commands, e-mail: users-***@tomcat.apache.org