【发布时间】:2013-04-19 12:47:55
【问题描述】:
我在 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 秒超时的 DeferredResult。 stop 请求将在DeferredResult 上设置结果。如果您在延迟结果超时之前或之后发送stop,一切正常。
但是,如果您在 start 超时的同时发送 stop,事情就会变得疯狂。我添加了一个onTimeout 操作以使其易于重现,但这不是问题发生所必需的。使用 APR 连接器,它只会死锁。使用 NIO 连接器,它有时可以工作,但有时它会错误地将“超时”消息发送到 stop 客户端,并且永远不会回复 start 客户端。
对此进行测试:
curl http://localhost/test/start & sleep 5; curl http://localhost/test/stop
我不认为我做错了什么。 Spring 文档似乎说可以随时调用setResult,即使在请求已经过期之后,也可以从任何线程(“
应用程序可以从它选择的线程中产生结果”)。
使用的版本:Linux 上的 Tomcat 7.0.39,Spring 3.2.2。
【问题讨论】:
-
当 DeferredResult 被认为是根据文档提供异步响应的替代方法时,对方法上的同步有点警惕。 static.springsource.org/spring/docs/3.2.0.BUILD-SNAPSHOT/api/… 告诉一个方法它是同步的假定同步行为,如果不小心可能会导致死锁。
-
synchronizeds 是我原始代码的残余。可以肯定的是,我删除了它们并重新测试。它没有改变任何东西。 -
我在上游报告了这个问题,这个 bug 已经在 Spring 3.2.3 中修复了。
标签: java spring tomcat comet spring-3