引言
本篇开始钻研 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