目录
一、背景介绍
二、侦听客户端长轮询
三、服务端推送变更配置
一、背景介绍
在之前上一篇文章中我们一起看了nacos配置中心动态刷新client端的长轮询逻辑监听、事件发布变更配置和client端怎么和SpringBoot整合。
所以,本篇我们主要来看nacos服务端是怎么处理客户端长轮询监听配置变更和主动推送变更配置的。
二、侦听客户端长轮询
从上一篇中我们知道nacos客户端在长轮询逻辑中通过checkUpdateDataIds()方法发出http请求到服务端探测是否有配置发生变更。
// check server config
List<String> changedGroupKeys = checkUpdateDataIds(cacheDatas, inInitializingCacheList);
if (!CollectionUtils.isEmpty(changedGroupKeys)) {
LOGGER.info("get changedGroupKeys:" + changedGroupKeys);
}
在checkUpdateDataIds()底层我们能够发现调用服务端的地址链接是/v1/cs/configs/listener。
HttpRestResult<String> result = agent
.httpPost(Constants.CONFIG_CONTROLLER_PATH + "/listener", headers, params, agent.getEncode(),
readTimeoutMs);
所以,我们直接看nacos服务端/v1/cs/configs/listener对应的方法。
@PostMapping("/listener")
@Secured(action = ActionTypes.READ, parser = ConfigResourceParser.class)
public void listener(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
String probeModify = request.getParameter("Listening-Configs");
if (StringUtils.isBlank(probeModify)) {
throw new IllegalArgumentException("invalid probeModify");
}
probeModify = URLDecoder.decode(probeModify, Constants.ENCODE);
Map<String, String> clientMd5Map;
try {
//计算 MD5 值
clientMd5Map = MD5Util.getClientMd5Map(probeModify);
} catch (Throwable e) {
throw new IllegalArgumentException("invalid probeModify");
}
// 进行长轮询逻辑
inner.doPollingConfig(request, response, clientMd5Map, probeModify.length());
}
该方法前面都是一些校验以及生成clientMd5Map,这个Map里面包含了监听配置变更的key以及value,下面会用到,核心主要看inner.doPollingConfig()这个逻辑。
public void doPollingConfig(HttpServletRequest request, HttpServletResponse response,
Map<String, String> clientMd5Map, int probeRequestSize) throws IOException {
//长轮询
if (LongPollingService.isSupportLongPolling(request)) {
longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize);
return;
}
// 兼容短轮询逻辑.
List<String> changedGroups = MD5Util.compareMd5(request, response, clientMd5Map);
// 兼容短轮询结果.
String oldResult = MD5Util.compareMd5OldResult(changedGroups);
String newResult = MD5Util.compareMd5ResultString(changedGroups);
String version = request.getHeader(Constants.CLIENT_VERSION_HEADER);
if (version == null) {
version = "2.0.0";
}
int versionNum = Protocol.getVersionNumber(version);
// Befor 2.0.4 version, return value is put into header.
if (versionNum < START_LONG_POLLING_VERSION_NUM) {
response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult);
response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult);
} else {
request.setAttribute("content", newResult);
}
Loggers.AUTH.info("new content:" + newResult);
// Disable cache.
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Cache-Control", "no-cache,no-store");
response.setStatus(HttpServletResponse.SC_OK);
}
该方法首先判断客户端发送过来的请求是长轮询还是短轮询,如果是长轮询直接执行长轮询逻辑然后return,下面的逻辑是兼容短轮询的逻辑实现。
在兼容的短轮询逻辑中拿到刚才的clientMd5Map进行解析、比对是否和服务端发生了变更,如果发生了变更就将老数据和新数据都放到响应的header里面,直接返回给nacos客户端了。
是否是长轮询其实就是判断请求头中是否设置了Long-Pulling-Timeout,刚才在ClientWorker的请求头中设置了这个参数。
确定是长轮询逻辑,我们直接看addLongPollingClient()添加客户长轮询这个逻辑。
public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map<String, String> clientMd5Map, int probeRequestSize) {
......
int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500);
//为 LoadBalance 添加延迟时间,并提前 500ms 返回响应,避免客户端超时(即超时时间减 500ms 后赋值给 timeout 变量)
long timeout = Math.max(10000, Long.parseLong(str) - delayTime);
//判断是否为固定轮询,是则 30s 后执行;否则 29.5s 后执行
if (isFixedPolling()) {
timeout = Math.max(10000, getFixedPollingInterval());
//除了设置修复轮询超时之外什么都不做
} else {
long start