- 在三个主要service之前先写一个Apollo关键的知识点DeferredResult,关联Apollo(3) Config Service部分
- 长链接、长轮询概念:https://blog.csdn.net/joeries/article/details/91389939
- 最大区别是http长轮询不会立即返回,服务端挂起,触发某些事件才会返回。如果有兴趣还可以看看websocket实现了全双工通信也可以解决服务端推送问题
- DeferredResult:springMvc DeferredResult的long polling应用 | KL博客
- spring在服务端实现长轮询请求挂起的机制,挂起后节省资源占用,setResult()后真正触发响应
- DeferredResult原理见博客,下面的代码根据博客代码进行改造,更贴近Apollo长轮询的服务端源码逻辑,模拟了包括Config Service接收Client请求、Config Service接收到Admin Service监听时setResult()返回消息,长轮训是Apollo最重要的逻辑之一
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.Map;
/**
* 模拟两个Client长轮训请求:localhost:9998/async/longPolling?namespace=namespace1、localhost:9998/async/longPolling?namespace=namespace2
* 模拟触发监听响应Client:localhost:9998/async/returnLongPollingValue?namespace=namespace1
*/
@RestController
@RequestMapping("/async")
public class DeferredResultController {
final Map<String, DeferredResult<ResponseEntity<String>>> deferredResultMap = new ConcurrentReferenceHashMap<>(); // 存deferredResult的Map
private static final ResponseEntity<String> STATUS_304 = new ResponseEntity<>(HttpStatus.NOT_MODIFIED); // http 304
@GetMapping("/longPolling")
public DeferredResult<ResponseEntity<String>> DeferredResultLongPolling(String namespace) {
// 初始化DeferredResult,超时5s后返回304,如果被setResult()也会触发响应
DeferredResult<ResponseEntity<String>> deferredResult = new DeferredResult<>(5000L, STATUS_304);
// 加入到全局集合deferredResultMap,否则returnLongPollingValue接口获取不到DeferredResult对象
deferredResultMap.put(namespace, deferredResult);
System.out.println("deferredResult.hashCode()" + deferredResult.hashCode());
// 超时、deferredResult.setResult()都会回调onCompletion(),代表Map里的DeferredResult使用结束后必须被清掉
deferredResult.onCompletion(() -> {
deferredResultMap.remove(namespace);
System.err.println("还剩" + deferredResultMap.size() + "个deferredResult未响应");
});
// controller会正常执行,但response会被拦住不会返回客户端,超时或者DeferredResult被setResult()才会真正响应给客户端
return deferredResult;
}
@GetMapping("/returnLongPollingValue")
public void returnLongPollingValue(String namespace) {
// 模拟Apollo Config Service发布触发listener回调,返回200并带上namespace最新的release message表ID,后续Client会根据ID查具体配置
DeferredResult<ResponseEntity<String>> deferredResult = deferredResultMap.get(namespace);
ResponseEntity<String> response = new ResponseEntity<>(namespace + " new release message ID!", HttpStatus.OK);
deferredResult.setResult(response);
}
}
网友评论