在上一篇文章里我们知道了 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 就是诸如「插件」「选择器」或者「规则」等数据。

我们还是以「选择器」数据为例:

  1. public class HttpLongPollingDataChangedListener extends AbstractDataChangedListener {
  2. @Override
  3. protected void afterSelectorChanged(final List<SelectorData> changed, final DataEventTypeEnum eventType) {
  4. scheduler.execute(new DataChangeTask(ConfigGroupEnum.SELECTOR));
  5. }
  6. }

可以看到这里就是开启了一个新的线程,我们继续看这个线程类的内容。

  1. class DataChangeTask implements Runnable {
  2. @Override
  3. public void run() {
  4. for (Iterator<LongPollingClient> iter = clients.iterator(); iter.hasNext();) {
  5. LongPollingClient client = iter.next();
  6. iter.remove();
  7. client.sendResponse(Collections.singletonList(groupKey));
  8. log.info("send response with the changed group,ip={}, group={}, changeTime={}", client.ip, groupKey, changeTime);
  9. }
  10. }
  11. }

可以看到这里就是遍历 LongPollingClient 集合,然后针对每个对象去返回更新的数据,如果更新的是「选择器」数据,那么就会返回「选择器」数据。

上一篇文章我们就是记录了这里的问题,一开始的时候 LongPollingClient 集合是空的。

但是如果有 soul-bootstrap 建立 http 连接时就会添加一个客户端对象到 LongPollingClient 集合。

  1. class LongPollingClient implements Runnable {
  2. @Override
  3. public void run() {
  4. this.asyncTimeoutFuture = scheduler.schedule(() -> {
  5. clients.remove(LongPollingClient.this);
  6. List<ConfigGroupEnum> changedGroups = compareChangedGroup((HttpServletRequest) asyncContext.getRequest());
  7. sendResponse(changedGroups);
  8. }, timeoutTime, TimeUnit.MILLISECONDS);
  9. clients.add(this);
  10. }
  11. }

也就是说,每当 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 麻烦一些。