Apollo(2) 长轮询

作者: Oliver_Li | 来源:发表于2022-12-08 22:11 被阅读0次
    • 在三个主要service之前先写一个Apollo关键的知识点DeferredResult,关联Apollo(3) Config Service部分
    1. 长链接、长轮询概念:https://blog.csdn.net/joeries/article/details/91389939
    • 最大区别是http长轮询不会立即返回,服务端挂起,触发某些事件才会返回。如果有兴趣还可以看看websocket实现了全双工通信也可以解决服务端推送问题
    1. DeferredResult:springMvc DeferredResult的long polling应用 | KL博客
    • spring在服务端实现长轮询请求挂起的机制,挂起后节省资源占用,setResult()后真正触发响应
    1. 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);
        }
    }
    

    相关文章

      网友评论

        本文标题:Apollo(2) 长轮询

        本文链接:https://www.haomeiwen.com/subject/avcnfdtx.html