引言

本篇开始钻研 Soul 网关 http 数据同步,将分为三篇进行剖析:

  • 《Admin告诉前解决》
  • 《变更告诉机制》
  • 《Bootstrap解决变更告诉》

心愿三篇完结后能对 Soul 的 http 数据同步策略有所播种。

本篇旨在探索 soul-admin 端在发动变更告诉前所做的解决。

不同数据变更的解决模式该当是统一的,故本篇以 selector 配置变更为切入点进行深刻。

一、配置变更入口

找到 SelectorController,这是 selector 配置变更的入口

其持有一个 SelectorService 援用,通过 SelectorService 实现 selector 配置变更。

二、配置变更服务

再来看看 SelectorService,实现了配置变更的具体解决。

其外部持有5个 mapper、1个 eventPublisher和1个 upstreamCheckService,对外提供一系列对 selector 的crud办法

留神 createOrUpdate 办法

public int createOrUpdate(final SelectorDTO selectorDTO) {    int selectorCount;    SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO);    List<SelectorConditionDTO> selectorConditionDTOs = selectorDTO.getSelectorConditions();    // 数据落库    if (StringUtils.isEmpty(selectorDTO.getId())) {        selectorCount = selectorMapper.insertSelective(selectorDO);        selectorConditionDTOs.forEach(selectorConditionDTO -> {            selectorConditionDTO.setSelectorId(selectorDO.getId());            selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO));        });    } else {        selectorCount = selectorMapper.updateSelective(selectorDO);        //delete rule condition then add        selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId()));        selectorConditionDTOs.forEach(selectorConditionDTO -> {            selectorConditionDTO.setSelectorId(selectorDO.getId());            SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO);            selectorConditionMapper.insertSelective(selectorConditionDO);        });    }    // 公布 spring 事件    publishEvent(selectorDO, selectorConditionDTOs);    // 更新 divide 上游服务    updateDivideUpstream(selectorDO);    return selectorCount;}

解决策略是先落库,再公布 spring 事件,最初更新 divide 上游服务

三、spring 事件告诉机制

此处波及 spring 的事件告诉机制,在此简要阐明:

ApplicationContext通过ApplicationEvent类和ApplicationListener接口提供事件处理。

如果一个bean实现ApplicationListener接口在容器中,每次一个ApplicationEvent被公布到ApplicationContext中,这类bean就会收到这些告诉。

实现Spring事件机制次要有4个类:

  • ApplicationEvent:事件,每个实现类示意一类事件,可携带数据。
  • ApplicationListener:事件监听器,用于接管事件处理工夫。
  • ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的播送。
  • ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster实现事件公布。

四、soul 实现事件告诉

上面咱们看看 Soul 是如何应用 spring 的工夫告诉机制。

事件定义

DataChangedEvent 继承 ApplicationEvent,提供了 DataChangedEvent(groupKey, type, source) 事件构造方法

事件监听器

DataChangedEventDispatcher 实现了 ApplicationListener接口,借助 onApplicationEvent 办法监听事件

public void onApplicationEvent(final DataChangedEvent event) {    for (DataChangedListener listener : listeners) {        switch (event.getGroupKey()) {            case APP_AUTH:                listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());                break;            case PLUGIN:                listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());                break;            case RULE:                listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());                break;            case SELECTOR:                listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());                break;            case META_DATA:                listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());                break;            default:                throw new IllegalStateException("Unexpected value: " + event.getGroupKey());        }    }}

该办法内按事件类型别离解决,DataChangedEventDispatcher 同时实现了 InitializingBean 接口,在初始化后实现 listeners 的注入。

五、响应数据变更事件

下面的事件监听解决用到 soul 的 DataChangedListener 接口

DataChangedListener 实现了不同类型事件的事件响应办法用于响应 DataChangedEvent 事件。

1)AbstractDataChangedListener 的 onSelectorChanged 实现:

public void onSelectorChanged(final List<SelectorData> changed, final DataEventTypeEnum eventType) {    if (CollectionUtils.isEmpty(changed)) {        return;    }    // 更新 selector 缓存    this.updateSelectorCache();    // selector 变更后处理,实现具体的变更告诉    this.afterSelectorChanged(changed, eventType);}

能够看到 selector 变更解决是先更缓存后发告诉。

2)AbstractDataChangedListener 的 updateSelectorCache 实现:

protected void updateSelectorCache() {    this.updateCache(ConfigGroupEnum.SELECTOR, selectorService.listAll());}

3)AbstractDataChangedListener 的 updateCache 实现:

protected <T> void updateCache(final ConfigGroupEnum group, final List<T> data) {    String json = GsonUtils.getInstance().toJson(data);    ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis());    ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal);    log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal);}

能够看到最终是创立对应的 ConfigDataCache 存入 CACHE。

总结

本篇梳理了 soul-admin 在真正收回数据变更告诉前的解决脉络,其策略是:先写库后更缓存,最初收回数据变更告诉。

先写库保证数据不丢,另外在集群部署时,其余 soul-admin 节点也可通过浏览页面时查库保证数据统一。

意外学到 spring 的事件告诉机制,soul 中的设计果然玲珑精妙。

下篇,将探索 http 同步策略的变更告诉机制,期待惊喜。

集体知识库

高性能微服务API网关-Soul