Apollo(2) 长轮询

  • 在三个主要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);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容