根据上一篇文章,我们知道soul-admin启动后不会自动向nacos同步数据,需要手动操作。
本篇文章分析一下soul-admin,nacos,soul-bootstrap同步数据的过程。
1、插件信息更新后会发布一个DataChangedEvent事件
/**
* create or update plugin
* @param pluginDTO {@linkplain PluginDTO}
* @return rows
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String createOrUpdate(final PluginDTO pluginDTO) {
......
// publish change event.
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, eventType,
Collections.singletonList(PluginTransfer.INSTANCE.mapToData(pluginDO))));
return StringUtils.EMPTY;
}
2、由监听事件处理类 DataChangedEventDispatcher 负责调用具体的监听实现类对 DataChangedEvent事件进行处理,这里的具体实现类是NacosDataChangedListener。
org.dromara.soul.admin.listener.DataChangedEventDispatcher
DataChangedEventDispatcher初始化完成后会执行 afterPropertiesSet(),在容器中获取所有类型是DataChangedListener.class的bean
@Override
public void afterPropertiesSet() {
Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
}
DataChangedEventDispatcher 监听到变更事件后,会执行 onApplicationEvent,遍历所有的监听类对监听事件进行处理,这里是NacosDataChangedListener,如下图的debug。
@Override
@SuppressWarnings("unchecked")
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
......
case RULE:
listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
break;
......
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
3、NacosDataChangedListener 会执行 onRuleChanged,updateRuleMap 先将网关数据同步至内存,在通过publishConfig同步至nacos。
org.dromara.soul.admin.listener.nacos.NacosDataChangedListener
// 执行监听事件
@Override
public void onRuleChanged(final List<RuleData> changed, final DataEventTypeEnum eventType) {
updateRuleMap(getConfig(RULE_DATA_ID));
switch (eventType) {
......
default:
changed.forEach(rule -> {
List<RuleData> ls = RULE_MAP
.getOrDefault(rule.getSelectorId(), new ArrayList<>())
.stream()
.filter(s -> !s.getId().equals(rule.getSelectorId()))
.sorted(RULE_DATA_COMPARATOR)
.collect(Collectors.toList());
ls.add(rule);
RULE_MAP.put(rule.getSelectorId(), ls);
});
break;
}
publishConfig(RULE_DATA_ID, RULE_MAP);
}
// 同步至内存
private void updateRuleMap(final String configInfo) {
JsonObject jo = GsonUtils.getInstance().fromJson(configInfo, JsonObject.class);
Set<String> set = new HashSet<>(RULE_MAP.keySet());
......
RULE_MAP.keySet().removeAll(set);
}
// 同步至nacos
@SneakyThrows
private void publishConfig(final String dataId, final Object data) {
configService.publishConfig(dataId, GROUP, GsonUtils.getInstance().toJson(data));
}
4、DataChangedEventDispatcher 、NacosDataChangedListener 类继承关系
5、总结
1、例如 soul-admin 更新网关数据,发布一个DataChangedEvent事件,eventPublisher.publishEvent(new DataChangedEvent())
2、DataChangedEventDispatcher --> onApplicationEvent()方法监听事件到事件,判断监听类是 NacosDataChangedListener
3、NacosDataChangedListener --> onRuleChanged()处理事件
4、同步至内存 updateRuleMap(getConfig(RULE_DATA_ID))
5、同步至nacos publishConfig(RULE_DATA_ID, RULE_MAP)
1、soul-bootstrap 添加了nacos依赖 soul-spring-boot-starter-sync-data-nacos,服务启动后会自动注入NacosSyncDataConfiguration
org.dromara.soul.springboot.starter.sync.data.nacos.NacosSyncDataConfiguration
NacosSyncDataService 负责读取和同步nacos网关数据
@Configuration
@ConditionalOnClass(NacosSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url")
@Slf4j
public class NacosSyncDataConfiguration {
// 注入nacos数据同步服务
@Bean
public SyncDataService nacosSyncDataService(final ObjectProvider<ConfigService> configService, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
log.info("you use nacos sync soul data.......");
return new NacosSyncDataService(configService.getIfAvailable(), pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
}
// 注入nacos客户端配置服务
@Bean
public ConfigService nacosConfigService(final NacosConfig nacosConfig) throws Exception {
Properties properties = new Properties();
......
return NacosFactory.createConfigService(properties);
}
// 注入nacos配置服务
@Bean
@ConfigurationProperties(prefix = "soul.sync.nacos")
public NacosConfig nacosConfig() {
return new NacosConfig();
}
}
2、org.dromara.soul.sync.data.nacos.NacosSyncDataService
初始化会执行 start
watcherData 负责监听nacos网关数据
updatePluginMap 同步网关数据到内存
public void start() {
......
watcherData(RULE_DATA_ID, this::updateRuleMap);
......
}
@SneakyThrows
private String getConfigAndSignListener(final String dataId, final Listener listener) {
return configService.getConfigAndSignListener(dataId, GROUP, 6000, listener);
}
protected void watcherData(final String dataId, final OnChange oc) {
Listener listener = new Listener() {
@Override
public void receiveConfigInfo(final String configInfo) {
oc.change(configInfo);
}
......
};
oc.change(getConfigAndSignListener(dataId, listener));
LISTENERS.getOrDefault(dataId, new ArrayList<>()).add(listener);
}
3、NacosSyncDataService 类关系图
4、总结
1、soul-bootstrap 启动向容器自动注入 NacosSyncDataConfiguration
2、NacosSyncDataConfiguration 类中会向容器注入 NacosSyncDataService
3、NacosSyncDataService --> start() --> watcherData() 监听nacos,同步网关数据到内存
4、watcherData() --> updatePluginMap()