java - Spring的DeferredResult setResult与超时的交互

标签 java spring tomcat comet spring-3

我正在 Tomcat 上试验 Spring 的 DeferredResult,我得到了疯狂的结果。是我做错了什么,还是 Spring 或 Tomcat 中存在一些错误?我的代码很简单。

@Controller
public class Test {
    private DeferredResult<String> deferred;

    static class DoSomethingUseful implements Runnable {
        public void run() {
            try { Thread.sleep(2000); } catch (InterruptedException e) { }
        }
    }

    @RequestMapping(value="/test/start")
    @ResponseBody
    public synchronized DeferredResult<String> start() {
        deferred = new DeferredResult<>(4000L, "timeout\n");
        deferred.onTimeout(new DoSomethingUseful());
        return deferred;
    }

    @RequestMapping(value="/test/stop")
    @ResponseBody
    public synchronized String stop() {
        deferred.setResult("stopped\n");
        return "ok\n";
    }
}

所以。 start 请求创建一个超时为 4 秒的 DeferredResultstop 请求将在 DeferredResult 上设置一个结果。如果您在延迟结果超时之前或之后发送 stop,一切正常。

但是,如果您在 start 超时的同时发送 stop,事情就会变得疯狂。我已经添加了一个 onTimeout 操作以使其易于重现,但这并不是问题发生所必需的。使用 APR 连接器,它只会死锁。使用 NIO 连接器时,它有时可以工作,但有时它会错误地将“超时”消息发送到 stop 客户端并且永远不会响应 start 客户端。

测试一下:

curl http://localhost/test/start & sleep 5; curl http://localhost/test/stop

我不认为我做错了什么。 Spring 文档似乎说可以随时调用 setResult ,即使在请求已经过期之后,也可以从任何线程(“the 应用程序可以从其选择的线程中生成结果”)。

使用的版本:Linux 上的 Tomcat 7.0.39,Spring 3.2.2。

最佳答案

这是一个很好的错误发现!
只需添加有关错误的更多信息 (that got fixed) 以便更好地理解。

setResult() 中有一个同步块(synchronized block),它扩展到提交分派(dispatch)的部分。如果同时发生超时,这可能会导致死锁,因为 Tomcat 超时线程有自己的锁定,只允许一个线程进行超时或分派(dispatch)处理。

详细解释:

当您在请求“超时”的同时调用“停止”时,两个线程正在尝试锁定 DeferredResult 对象“已延迟”。

  1. 执行“onTimeout”处理程序的线程 这是 Spring 文档的摘录:

    This onTimeout method is called from a container thread when an async request times out before the DeferredResult has been set. It may invoke setResult or setErrorResult to resume processing.

  2. 执行“停止”服务的另一个线程。

如果在 stop() 服务期间调用的分派(dispatch)处理获得“延迟”锁,它将等待 tomcat 锁(比如 TomcatLock)完成分派(dispatch)。
如果另一个执行超时处理的线程已经获取了 TomcatLock,则该线程将等待获取 'deferred' 上的锁以完成 setResult()!

所以,我们最终陷入了典型的死锁局面!

关于java - Spring的DeferredResult setResult与超时的交互,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16104980/

相关文章:

Java 数组中的通用最大值

tomcat - 无法在服务器上运行应用程序

java.lang.IllegalArgumentException : Not a managed type - Multiple Databases in Spring Boot

java - 使用运算符 "length"无法在类 "java.lang.String"的对象中找到 "."的值

spring - 如何使用 Spring Boot 禁用 Redis 缓存?

java - 使用 JAX-RS 创建 RESTful Web 服务并将其部署到 tomcat

tomcat - 如何从 Intellij IDEA 部署 tomcat/webapps 文件夹

java - 如何动态构建列表并将其作为方法参数传递?

Java MySQL JDBC 内存泄漏

java - for bean 使用 spEL 的 Spring AOP 代理创建错误