序
在上一篇文章里我们知道了 soul-bootstrap 启动之后会跟 soul-admin 建立 http 连接,并且定时去拉取数据。
但是这样的机制会导致数据有延迟,比如在 soul-admin 可视化界面修改了数据之后,soul-bootstrap 可能还得等一会儿才会通过 http 去同步数据。
那么当 soul-admin 更新数据之后,会使用什么方式去保证 soul-bootstrap 能及时的获取到数据呢?
http 长连接
答案就是 http 的长连接。
我们都知道 http 通讯需要建立连接,请求和响应都需要通过这个连接才能完成。
那么,如果建立了连接就不断开呢?这样是否就可以根据原来建立的连接发送返回值给客户端呢?
soul-admin 和 soul-bootstrap 间的 http 数据同步就是这样做的。
首先由 soul-bootstrap 建立 http 长连接,然后每当 soul-admin 有数据修改的时候就会通过建立的 http 连接异步返回更新的数据给 soul-bootstrap。
源码分析
回想前面的一篇文章,我们单独提出了一个未解决的问题 —— 使用 soul-admin 的可视化界面更新了数据之后,soul-admin 先是会更新数据库的数据,然后发布事件更新内存数据,最后还会执行我们的老朋友 HttpLongPollingDataChangedListener 类里面的 afterXxxChanged 方法,这里的 xxx 就是诸如「插件」「选择器」或者「规则」等数据。
我们还是以「选择器」数据为例:
public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener {
@Override
protected void afterSelectorChanged(final List<SelectorData> changed, final DataEventTypeEnum eventType) {
scheduler.execute(new DataChangeTask(ConfigGroupEnum.SELECTOR));
}
}
可以看到这里就是开启了一个新的线程,我们继续看这个线程类的内容。
class DataChangeTask implements Runnable {
@Override
public void run() {
for (Iterator<LongPollingClient> iter = clients.iterator(); iter.hasNext();) {
LongPollingClient client = iter.next();
iter.remove();
client.sendResponse(Collections.singletonList(groupKey));
log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime);
}
}
}
可以看到这里就是遍历 LongPollingClient 集合,然后针对每个对象去返回更新的数据,如果更新的是「选择器」数据,那么就会返回「选择器」数据。
上一篇文章我们就是记录了这里的问题,一开始的时候 LongPollingClient 集合是空的。
但是如果有 soul-bootstrap 建立 http 连接时就会添加一个客户端对象到 LongPollingClient 集合。
class LongPollingClient implements Runnable {
@Override
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);
}
}
也就是说,每当 soul-bootstrtap 跟 soul-admin 建立了一个 http 连接,不管是请求「选择器」数据还是其他类型的数据,都会添加到 LongPollingClient 集合中,如果这个时候更新了 soul-admin 里面对应类型的数据,就会触发 afterXxxChanged 方法,然后通过这个方法从 LongPollingClient 集合里取出对象,最后把更新的数据返回给 soul-bootstrap。
总结
到此为止呢,我们就算是已经把 soul-admin 跟 soul-bootstrap 间的 http 数据同步的过程梳理完了。
我们先从 soul-admin 自身需要保证数据库的数据和内存数据的同步,然后是 soul-bootstrap 启动时也需要先全部拉取一遍数据,这样才能保证网关启动之后就可以直接使用。
最后才是 soul-bootstrap 和 soul-admin 模块间的数据同步——主要就是 soul-admin 模块修改了数据之后如何及时的让 soul-bootstrap 知道并且更新数据。
由于 http 不同于 web socket,不能像后者一样建立连接之后可以双向通讯,http 必须由客户端发起请求,然后让服务端返回数据,所以在数据同步的时候,http 方式会比 web socket 麻烦一些。