【问题标题】:Spring Servlet 3.0 Async Controllers - what thread handles response?Spring Servlet 3.0 异步控制器 - 什么线程处理响应?
【发布时间】:2023-03-27 05:29:01
【问题描述】:

我对 Java(我正在使用 Java SE 7)和 JVM 还很陌生,并尝试使用以下方法编写异步控制器:

  • 雄猫 7
  • Spring MVC 4.1.1
  • Spring Servlet 3.0

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

我正在考虑返回一个 DeferredResponse——用 ListenableFuture 来桥接这个似乎很容易——但我似乎找不到任何资源来解释一旦 DeferredResponse 解决后如何将响应传递回客户端。

也许我没有完全理解异步控制器应该如何工作,但是有人可以解释一下 DeferredResponse 解决后响应如何返回给客户端吗?必须有一些线程负责发送响应的工作,对吧?

【问题讨论】:

    标签: java multithreading spring tomcat


    【解决方案1】:

    我最近使用 Spring 的 DeferredResponse 在我最近编写的长轮询情况下取得了出色的效果。我认为,关注返回给用户的响应的“方式”并不是思考对象的正确方式。根据使用它的位置,它以与常规同步调用完全相同的方式向用户返回消息,只是以延迟的异步方式。同样,该对象没有定义也没有提出交付机制。只是一种将异步响应“插入”到现有渠道的方法。

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

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

    在实践中,该对象完美无缺,让我能够实现有效的长轮询机制。

    这是我示例中代码的 sn-p:

        @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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-16
      • 1970-01-01
      • 2021-10-12
      • 2017-12-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多