我最近使用 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;
}