Apollo的长连接实现是
- Spring的DeferredResult来实现的,先看怎么用
import ...
@RestController
@RequestMapping("deferredResult")
public class DeferredResultController {
private Map<String, Consumer<DeferredResultResponse>> taskMap = new HashMap<>();
private String requestId = "demo";
@GetMapping("get")
public DeferredResult<DeferredResultResponse> deferredResult(){
DeferredResult<DeferredResultResponse> ret = new DeferredResult<>();
ret.onTimeout(() -> {
DeferredResultResponse deferredResultResponse = new DeferredResultResponse();
deferredResultResponse.setCode(HttpStatus.REQUEST_TIMEOUT.value());
deferredResultResponse.setMsg(DeferredResultResponse.Msg.TIMEOUT.getDesc());
ret.setResult(deferredResultResponse);
});
taskMap.put(requestId, ret::setResult);
return ret;
}
@GetMapping("set")
public void setResult(){
DeferredResultResponse deferredResultResponse = new DeferredResultResponse();
deferredResultResponse.setCode(HttpStatus.OK.value());
deferredResultResponse.setMsg(DeferredResultResponse.Msg.SUCCESS.getDesc());
taskMap.get(requestId).accept(deferredResultResponse);
taskMap.remove(requestId);
}
public static class DeferredResultResponse{
private Integer code;
private String msg;
...
public enum Msg {
TIMEOUT("超时"),
FAILED("失败"),
SUCCESS("成功");
private String desc;
...
Msg(String desc) {
this.desc = desc;
}
}
}
}
-
这时候请求get接口,客户端是卡住的
-
当有结果设置的时候才会响应,调set接口来设置结果
-
如果一直没有结果设置,就会等到超时的时候才会响应
-
对于Apollo的客户端来说请求流程也是这样的
调用notification/v3接口等待获取变更的数据(namespace),如果一直没有变更,就会等到60秒超时的时候才响应。
原理
在了解原理之前,问几个问题?
- 这个客户端阻塞是怎么实现的?
- 超时是怎么唤醒的?
- 设置值的时候如果找到原来的请求
回答
- 阻塞是通过socket来实现的,只要socket请求之后不往回写response,就会一直等待
spring判断controller是DeferredResult异步请求,就会把这个请求挂起,请求的信息先保存起来,不返回response - 超时唤醒是利用tomcat的能力,tomcat会启动线程去扫描队列里面挂起的请求,如果有超时时间到的,会重新dispatch
- setResult会AsyncManager保存的request,把结果设置之后,重新dispatch