使用DeferredResult

时间:2015-09-21 16:53:34

标签: java spring servlets asynchronous deferred

我有一个旧的应用程序,客户端通过非常慢的连接(GPRS)上传大文件。目前我们使用Spring MVC和旧的servlet 2.0标准并直接获取请求inputStream,这显然会导致长时间运行的被阻塞线程。我的任务是将应用程序升级到servlet 3.1以利用新的异步读取监听器(这被认为是直接的!)但是我已经走到了尽头。

将应用程序直接升级到servlet 3.1很顺利,忽略了我立即返回响应的事实,因为我更关心的是测试文件本身是否正确上传并且onDataAvailable()正在进行的事实多次正确调用。当我使用DeferredResult对象(看起来它或多或少是为此目的而发明)将视图名称返回给控制器时,问题就出现了,因为我需要显然让客户知道上传是否成功,在这个异步环境中我无法看到其他方法。

当我返回DeferredResult对象时,它被错误地直接返回给客户端,并且在第一次onDataAvailable()调用时,无法读取inputStream,因为我得到了一个java.io.IOException:Stream关闭异常。在这里调用getDispatcherType()告诉我类型现在已经切换到DispatcherType.ERROR,而当我运行不带DeferredResult对象的代码(并且无论如何直接响应)时,类型仍然是DispatcherType.REQUEST。我曾经想过,在堆栈的某个地方,inputStream很早就被读取了(不是由我)并且导致了这一点,但它似乎并没有,而奇怪的是inputStream.isReady()返回true而inputStream.isFinished()返回false并且我可以在第一个onDataAvailable()的缓冲区中看到文件中的文本,但是我无法直接从inputStream读取而没有获得java.io.IOException:Stream关闭异常。

使用微小文件或非常大的文件进行的任何测试都不会改变这种行为。有没有人成功使用Spring MVC中的DeferredResult和servlet 3.1?我已经被困了几天了,调试了更多的代码而不是我再做的事情了!

Heres来自我的代码(如果你觉得它看起来有点贫血的话,会剪掉一些商业资料)

@Controller
public class ContentController {
    @RequestMapping(value = “/upload/“, method = POST)
    public DeferredResult<String> upload(ServletRequest request) throws IOException, InterruptedException {
       DeferredResult<String> deferredResult = new DeferredResult<String>();
       final AsyncContext asyncContext = httpServletRequest.startAsync();
       ServletInputStream servletInputStream =  httpServletRequest.getInputStream();
       NioReadListener readListener =  new NioReadListener(servletInputStream, asyncContext, deferredResult, size);
       servletInputStream.setReadListener(readListener);
       return deferredResult
}

public class NioReadListener implements ReadListener {
    private final ServletInputStream _input;
    private final AsyncContext _context;
    private final DeferredResult<String> deferredResult;

    public NioReadListener(ServletInputStream servletInputStream, AsyncContext asyncContext, DeferredResult<String> deferredResult, Long size) throws IOException {
        this._input = servletInputStream;
        this._context = asyncContext;
        this.deferredResult = deferredResult;
    }

    @Override
    public void onDataAvailable() throws IOException {
        try {
            int bytesRead;
            byte b[] = new byte[_size.intValue()];
            while (_input.isReady() && (bytesRead = _input.read(b)) != -1) {
                _totalBytesRead += bytesRead;
            }
        } catch (IOException e) {
            _log.error(e);
        }

    }

    @Override
    public void onAllDataRead() throws IOException {
        this._context.complete();
        deferredResult.setResult("VIEW_NAME");
    }
}

1 个答案:

答案 0 :(得分:0)

(这是我原来问题的答案,但不是整个应用程序问题的答案)。经过几个小时的调试和头部刮擦,我找到了为什么我的deferredResult未正确设置的原因。我们为一些REST utils包含了第三方库,这使得应用程序使用Spring中不推荐使用的AnnotationMethodHandlerAdapter而不是它应该使用的RequestMappingHandlerAdapter。一旦我覆盖了这个外部bean,就会正确处理deferredResult。

唯一的问题是这个调用流程的异步性质仍然不起作用,这可以追溯到我最初的一个子问题,即是否有人将Spring MVC / DeferredResults和Servlet 3.1一起工作。我已经看到一些stackoverflow问题问同样的事情,但遗憾的是没有答案。