soul源码解读(八)-- 数据同步之http长轮询原理分析

17 篇文章 0 订阅
本文解析了admin与bootstrap通过http长轮询实现数据同步的详细步骤,包括bootstrap定时拉取、admin判断更新并同步、响应与请求交互过程。关键节点涉及MD5校验、数据一致性检查和锁机制应用。
摘要由CSDN通过智能技术生成

数据同步之http长轮询-bootstrap端

我们梳理下昨天调试的 http 长轮询同步数据的流程
首先, admin 启动之后,会每隔5分钟查询一次数据库,把数据刷新到缓存
bootstrap 启动之后,会先从 admin 拉取数据

// HttpSyncDataService.java
// fetch all group configs.
this.fetchGroupConfig(ConfigGroupEnum.values());

然后 bootstrap 会每隔 60s 调用一次 /configs/listener ,把当前网关里缓存的数据和最后更新的时间戳发给 admin

//当前网关里缓存的数据和最后更新的时间戳
for (ConfigGroupEnum group : ConfigGroupEnum.values()) {
	ConfigData<?> cacheConfig = factory.cacheConfigData(group);
   	String value = String.join(",", cacheConfig.getMd5(), String.valueOf(cacheConfig.getLastModifyTime()));
    params.put(group.name(), Lists.newArrayList(value));
}
...
String listenerUrl = server + "/configs/listener";
...
//向 admin 发送请求
String json = this.httpClient.postForEntity(listenerUrl, httpEntity, String.class).getBody();

admin 收到请求后,会去判断数据的MD5,数据有没有更新,还有最后更新的时间戳

// HttpLongPollingDataChangedListener.java
private List<ConfigGroupEnum> compareChangedGroup(final HttpServletRequest request) {
        	...
            // 检查有没有数据更新
            if (this.checkCacheDelayAndUpdate(serverCache, clientMd5, clientModifyTime)) {
                changedGroup.add(group);
            }
        }
        return changedGroup;
    }

如果有数据更新,会先获取锁,然后判断两次从本地缓存获取的数据是否一致,一致的话,直接再从数据库拉取一次数据,然后返回数据有更新,不一致的话,再判断两个对象的MD5值是否相同,不相同,说明有更新。

// MD5相等,说明数据没有更新
if (StringUtils.equals(clientMd5, serverCache.getMd5())) {
	return false;
}
long lastModifyTime = serverCache.getLastModifyTime();
if (lastModifyTime >= clientModifyTime) {
    // MD5不相等,admin 缓存里的最后更新时间戳比 bootstrap 端的最后更新时间戳大,说明数据过期了
    return true;
}
// 考虑到并发问题,admin需要加锁,否则,可能会导致soul-web的请求同时更新缓存,从而导致数据库压力过大
boolean locked = false;
try {
	// 5s内尝试去获取锁,如果没获取到就抛异常
    locked = LOCK.tryLock(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    // 没获取到锁,说明有其他线程在修改数据,直接返回有数据更新,中断当前线程
    return true;
}
if (locked) {
    try {
        ConfigDataCache latest = CACHE.get(serverCache.getGroup());
        if (latest != serverCache) {
        //在并发的时候,数据有变动,两次从本地缓存获取的数据不一致,看下两个对象的MD5值是否相同,不相同,说明有更新。
            return !StringUtils.equals(clientMd5, latest.getMd5());
        }
        // 为了保险起见,再从数据库拉取一次数据
        this.refreshLocalCache();
        latest = CACHE.get(serverCache.getGroup());
        return !StringUtils.equals(clientMd5, latest.getMd5());
    } finally {
        LOCK.unlock();
    }
}

有数据更新的话,会返回更新数据的 group 给 bootstrap

if (CollectionUtils.isNotEmpty(changedGroup)) {
  	this.generateResponse(response, changedGroup);
    log.info("send response with the changed group, ip={}, group={}", clientIp, changedGroup);
    return;
}

bootstrap 收到响应后,会判断有没有数据更新,如果有数据更新,就调用 admin 的 /configs/fetch 获取数据

//获取 /configs/listener 接口的返回数据
groupJson = GSON.fromJson(json, JsonObject.class).getAsJsonArray("data");
// 判断有没有数据更新
if (groupJson != null) {
   // fetch group configuration async.
    ConfigGroupEnum[] changedGroups = GSON.fromJson(groupJson, ConfigGroupEnum[].class);
    if (ArrayUtils.isNotEmpty(changedGroups)) {
    	//有数据更新,就调用  admin 的 /configs/fetch 
        log.info("Group config changed: {}", Arrays.toString(changedGroups));
        this.doFetchGroupConfig(server, changedGroups);
    }
}

再回到 admin ,/configs/listener 判断如果没有数据更新,会把请求放到阻塞队列里,每隔60s去队列里消费消息,里面又会去判断有没有数据更新

 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 和 bootstrap 两端是如何通过 http 长轮询保持数据同步的。简单的说,就是bootstrap 会调用一个 listener 接口,admin 会返回有没有数据更新,有的话,bootstrap 就再调用 fetch 接口获取最新数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值