Spring Servlet 3.0异步控制器 - 什么线程处理响应?

时间:2015-08-05 19:58:18

标签: java multithreading spring tomcat

我对Java(我使用Java SE 7)和JVM并尝试使用以下命令编写异步控制器相当新:

  • Tomcat 7
  • Spring MVC 4.1.1
  • Spring Servlet 3.0

我有一个组件,我的控制器委托一些具有异步部分的工作并返回一个ListenableFuture。理想情况下,我希望释放最初处理控制器响应的线程,因为我等待异步操作返回,因此需要异步控制器。

我正在考虑返回DeferredResponse - 将它与ListenableFuture联系起来似乎很容易 - 但我似乎无法找到任何资源来解释如何将响应传递回客户端DeferredResponse解析。

也许我并不完全了解异步控制器应该如何工作,但有人可以解释一旦DeferredResponse结算后响应如何返回给客户端?必须有一些线程能够完成发送响应的工作,对吗?

1 个答案:

答案 0 :(得分:0)

我最近在最近编码的长轮询情况下使用了Spring的DeferredResponse。我相信,专注于回复用户的响应的“方式”并不是思考对象的正确方法。根据它的使用位置,它以与常规同步调用完全相同的方式向用户返回消息,这种方式只能以延迟的异步方式进行。同样,该对象没有定义或提出传递机制。只是一种将异步响应“插入”现有通道的方法。

根据您的查询,是的,它通过创建一个具有用户规范超时的线程来实现。如果代码在超时之前完成,则使用'setResult',该对象返回代码的结果。否则,如果超时在结果之前触发,则返回由用户设置的默认值。无论哪种方式,对象都不会返回任何内容(除了对象本身),直到调用其中一个机制。此外,必须丢弃该对象,因为它不能被重用。

在我的情况下,我使用HTTP请求/响应函数将返回的响应包装在DeferredResponse对象中,该对象将提供默认响应 - 从客户端请求另一个数据包,以便浏览器不会超时 - 如果计算正在处理的代码在超时之前没有返回。每当计算完成时,它将通过'setResult'函数调用发送响应。在这种情况下,两种情况都只是使用HTTP响应将数据包发送回用户。但是,在任何情况下,响应都不会立即返回给用户。

在实践中,该对象完美无缺,并允许我实施有效的长轮询机制。

以下是我的示例中的代码片段:

    @RequestMapping(method = RequestMethod.POST, produces = "application/text")
@ResponseBody
// public DeferredResult<String> onMessage(@RequestBody String message, HttpSession session) {
public DeferredResult<String> onMessage(InputStream is, HttpSession session) {
    String message = convertStreamToString(is);
    // HttpSession session = null;
    messageInfo info = getMessageInfo(message);
    String state = info.getState();
    String id = info.getCallID();

    DeferredResult<String> futureMessage =
            new DeferredResult<>(refreshIntervalSecs * msInSec, getRefreshJsonMessage(id));
    if(state != null && id != null) {
        if(state.equals("REFRESH")) {
            // Cache response for future and "swallow" call as it is restocking call
            LOG.info("Refresh received for call " + id);
            synchronized (lock) {
                boolean isReplaceable = callsMap.containsKey(id) && callsMap.get(id).isSetOrExpired();
                if (isReplaceable)
                    callsMap.put(id, futureMessage);
                else {
                    LOG.warning("Refresh packet arrived on a non-existent call");
                    futureMessage.setResult(getExitJsonMessage(id));
                }
            }
        } else if (state.equals("NEW")){
            // Store response for future and pass the call onto the processing logic
            LOG.info("New long-poll call received with id " + id);
            ClientSupport cs = clientSupportMap.get(session.getId());
            if(cs == null) {
                cs = new ClientSupport(this, session.getId());
                clientSupportMap.put(session.getId(), cs);
            }
            callsMap.put(id, futureMessage);
            // *** IMPORTANT ****
            // This method sets up a separate thread to do work
            cs.newCall(message);
        }
    } else {
        LOG.warning("Invalid call information");
        // Return value immediately when return is called
        futureMessage.setResult("");
    }
    return futureMessage;
}