Spring 4 中的 DeferredResult 是否支持 Servlet 3.1 (Read|Write)Listener?
Is Servlet 3.1 (Read|Write)Listener supported by DeferredResult in Spring 4?
我正在阅读 JayWay article 关于使用 Spring 对 Servlet 的异步支持。
有趣的部分是:
If your service is expected to receive large request or response bodies, especially if the clients write or read slowly, you would benefit from using the non-blocking IO feature introduced in Servlet 3.1, as mentioned earlier. On the ServletInputStream there is the method setReadListener where you can set a ReadListener.
我看到您可以在异步启动 servlet 方面使用 DeferredResult
做一些事情,但我找不到任何与 ReadListener
和 WriteListener
相关的信息。
或者至少,我期待那方面的东西,因为它在我的应用程序的边界上很好,我只需要收到请求并发送结果。
是的,可以将 ReadListeners 与 Spring 的 DeferredResult 一起使用,我将在下面概述该过程。我想 WriteListeners 的过程是相似的。
开始异步处理
当 Spring 将 DeferredResult 视为 return 类型时,WebAsyncManager 将在您的方法执行后调用 request.startAsync()。这将以异步模式启动请求处理。重要的是不要在控制器方法中手动启动异步处理,Spring 无法处理已经启动的异步处理,并且会抛出异常。
正在附加您的 ReadListener
这将因容器而异。在异步处理开始之前,Jetty 将允许您在控制器方法中将 readListener 附加到 ServletInputStream,如下所示:
DeferredResult<String> deferredResult = new DeferredResult<String>();
ReadListener readListener = new NioReadListener(request, deferredResult, modelMap);
ServletInputStream stream = request.getInputStream();
stream.setReadListener(readListener);
Tomcat,也许其他容器也不允许这样做。他们对 Servlet Spec 的解释是,在允许附加 ReadListener 之前,必须启动异步处理。因此,如果您希望您的应用程序与容器无关,我建议采用以下技术。
创建一个 DeferredResultProcessingInterceptor
Spring 提供了一种使用 DeferredResultProcessingInterceptors 拦截异步请求处理的机制。按如下方式创建拦截器:
/**
* Responsible for attaching a {@link NioReadListener} to the servlet input stream before async processing begins.
*/
public class NioDeferredResultInterceptor extends DeferredResultProcessingInterceptorAdapter {
private final DeferredResult<String> _deferredResult;
private final VaultServletRequest _request;
private final ModelMap _model;
public NioDeferredResultInterceptor(ServletRequest request, DeferredResult<String> deferredResult, ModelMap model) {
_deferredResult = deferredResult;
_request = request;
_model = model;
}
@Override
public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
ReadListener readListener = new NioReadListener(_request, _deferredResult, _model);
ServletInputStream servletInputStream = _request.getInputStream();
servletInputStream.setReadListener(readListener);
}
preProcess 方法将在 Spring 开始异步处理后立即执行,但在其他任何事情发生之前,这正是我们想要的。
DeferredResultProcessingInterceptor 可以在您的控制器方法中附加到您的请求,如下所示:
DeferredResultProcessingInterceptor deferredResultInterceptor =
new NioDeferredResultInterceptor(request, deferredResult, modelMap);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerDeferredResultInterceptor("SomeKey", deferredResultInterceptor);
我正在阅读 JayWay article 关于使用 Spring 对 Servlet 的异步支持。
有趣的部分是:
If your service is expected to receive large request or response bodies, especially if the clients write or read slowly, you would benefit from using the non-blocking IO feature introduced in Servlet 3.1, as mentioned earlier. On the ServletInputStream there is the method setReadListener where you can set a ReadListener.
我看到您可以在异步启动 servlet 方面使用 DeferredResult
做一些事情,但我找不到任何与 ReadListener
和 WriteListener
相关的信息。
或者至少,我期待那方面的东西,因为它在我的应用程序的边界上很好,我只需要收到请求并发送结果。
是的,可以将 ReadListeners 与 Spring 的 DeferredResult 一起使用,我将在下面概述该过程。我想 WriteListeners 的过程是相似的。
开始异步处理
当 Spring 将 DeferredResult 视为 return 类型时,WebAsyncManager 将在您的方法执行后调用 request.startAsync()。这将以异步模式启动请求处理。重要的是不要在控制器方法中手动启动异步处理,Spring 无法处理已经启动的异步处理,并且会抛出异常。
正在附加您的 ReadListener
这将因容器而异。在异步处理开始之前,Jetty 将允许您在控制器方法中将 readListener 附加到 ServletInputStream,如下所示:
DeferredResult<String> deferredResult = new DeferredResult<String>();
ReadListener readListener = new NioReadListener(request, deferredResult, modelMap);
ServletInputStream stream = request.getInputStream();
stream.setReadListener(readListener);
Tomcat,也许其他容器也不允许这样做。他们对 Servlet Spec 的解释是,在允许附加 ReadListener 之前,必须启动异步处理。因此,如果您希望您的应用程序与容器无关,我建议采用以下技术。
创建一个 DeferredResultProcessingInterceptor
Spring 提供了一种使用 DeferredResultProcessingInterceptors 拦截异步请求处理的机制。按如下方式创建拦截器:
/**
* Responsible for attaching a {@link NioReadListener} to the servlet input stream before async processing begins.
*/
public class NioDeferredResultInterceptor extends DeferredResultProcessingInterceptorAdapter {
private final DeferredResult<String> _deferredResult;
private final VaultServletRequest _request;
private final ModelMap _model;
public NioDeferredResultInterceptor(ServletRequest request, DeferredResult<String> deferredResult, ModelMap model) {
_deferredResult = deferredResult;
_request = request;
_model = model;
}
@Override
public <T> void preProcess(NativeWebRequest request, DeferredResult<T> deferredResult) throws Exception {
ReadListener readListener = new NioReadListener(_request, _deferredResult, _model);
ServletInputStream servletInputStream = _request.getInputStream();
servletInputStream.setReadListener(readListener);
}
preProcess 方法将在 Spring 开始异步处理后立即执行,但在其他任何事情发生之前,这正是我们想要的。
DeferredResultProcessingInterceptor 可以在您的控制器方法中附加到您的请求,如下所示:
DeferredResultProcessingInterceptor deferredResultInterceptor =
new NioDeferredResultInterceptor(request, deferredResult, modelMap);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerDeferredResultInterceptor("SomeKey", deferredResultInterceptor);