Soul-源码阅读19-Http长轮询3
继上一节ConfigController
# fetchConfigs
从admin 获取响应数据后,bootstrap 更新本地缓存。
boostrap刷新数据
-
HttpSyncDataService
# doFetchGroupConfigprivate void doFetchGroupConfig(final String server, final ConfigGroupEnum... groups) { // ...... String json = this.httpClient.getForObject(url, String.class); // update local cache boolean updated = this.updateCacheWithJson(json); // ...... }
-
DataRefreshFactory
#executor
刷新数据缓存:dataRefresh.refreshpublic boolean executor(final JsonObject data) { final boolean[] success = {false}; ENUM_MAP.values().parallelStream().forEach(dataRefresh -> success[0] = dataRefresh.refresh(data)); return success[0]; }
-
刷新数据缓存之前,再比对MD5值和 最后一次更新缓存的时间判断是否需要更新。
protected boolean updateCacheIfNeed(final ConfigData<T> newVal, final ConfigGroupEnum groupEnum) { // first init cache if (GROUP_CACHE.putIfAbsent(groupEnum, newVal) == null) { return true; } ResultHolder holder = new ResultHolder(false); GROUP_CACHE.merge(groupEnum, newVal, (oldVal, value) -> { // MD5值不相等 且 旧数据的修改时间小于新数据的修改时间 if (!StringUtils.equals(oldVal.getMd5(), newVal.getMd5()) && oldVal.getLastModifyTime() < newVal.getLastModifyTime()) { log.info("update {} config: {}", groupEnum, newVal); holder.result = true; return newVal; } log.info("Get the same config, the [{}] config cache will not be updated, md5:{}", groupEnum, oldVal.getMd5()); return oldVal; }); return holder.result; }
最近几节总结
-
http长轮询60秒的秘密:bootstrap调用admin的“/configs/listener” 接口,有数据更新的时候会马上返回,bootstrap 就再调用 fetch 接口获取最新数据,没有数据更新的时候会60s再返回数据。
HttpLongPollingDataChangedListener
#doLongPolling
public void doLongPolling(final HttpServletRequest request, final HttpServletResponse response) { // compare group md5 List<ConfigGroupEnum> changedGroup = compareChangedGroup(request); String clientIp = getRemoteIp(request); // response immediately. if (CollectionUtils.isNotEmpty(changedGroup)) { this.generateResponse(response, changedGroup); log.info("send response with the changed group, ip={}, group={}", clientIp, changedGroup); return; } // listen for configuration changed. final AsyncContext asyncContext = request.startAsync(); // AsyncContext.settimeout() does not timeout properly, so you have to control it yourself asyncContext.setTimeout(0L); // block client's thread. scheduler.execute(new LongPollingClient(asyncContext, clientIp, HttpConstants.SERVER_MAX_HOLD_TIMEOUT)); }
public void run() { this.asyncTimeoutFuture = scheduler.schedule(() -> { clients.remove(LongPollingClient.this); List<ConfigGroupEnum> changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest()); sendResponse(changedGroups); }, timeoutTime, TimeUnit.MILLISECONDS); clients.add(this); }
-
admin 启动后每隔300s 从数据库同步一次数据到本地缓存。
-
在 admin 管理后台修改数据,会马上返回响应给 bootstrap。