任务

  • nacos 的选择器数据同步逻辑
  • apache dubbo 的底层原理

nacos 数据同步原理

原理剖析

上次聊到了使用 nacos 进行数据同步时,有可能出现 selectorData 中出现旧的 id(垃圾数据),从而导致网关请求时,无法找到对应 id 的 ruleData,从而报 can not match rule data 的异常。

现在来看看 soul-admin 的数据同步逻辑。从代码中可以得出结论:

  • SyncDataService 的 syncPluginData 是通过 pluginId 通知数据发生改变。
  • NacosSyncDataService 的 start 会监听数据的变化。重点关注 NacosCacheHandler 的 updateSelectorMap , 这里会根据监听到的 configInfo 来对 selectorData 进行删除和新增。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// dubbo 插件的 pluginId 是 6
public boolean syncPluginData(final String pluginId) {
// 找到 dubbo 插件
PluginVO pluginVO = pluginService.findById(pluginId);
// 通知 PLUGIN 数据发生改变
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.UPDATE,
Collections.singletonList(PluginTransfer.INSTANCE.mapDataTOVO(pluginVO))));
// 找到 selectorDataList
List<SelectorData> selectorDataList = selectorService.findByPluginId(pluginId);
if (CollectionUtils.isNotEmpty(selectorDataList)) {
// 通知 SELECTOR 数据发生改变
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.REFRESH, selectorDataList));
// 找到所有的 ruleDataList
List<RuleData> allRuleDataList = new ArrayList<>();
for (SelectorData selectData : selectorDataList) {
List<RuleData> ruleDataList = ruleService.findBySelectorId(selectData.getId());
allRuleDataList.addAll(ruleDataList);
}
// 通知 RULE 数据发生改变
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, DataEventTypeEnum.REFRESH, allRuleDataList));
}
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public void start() {
//PLUGIN
watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
//SELECTOR
watcherData(SELECTOR_DATA_ID, this::updateSelectorMap);
//RULE
watcherData(RULE_DATA_ID, this::updateRuleMap);
//META
watcherData(META_DATA_ID, this::updateMetaDataMap);
//AUTH
watcherData(AUTH_DATA_ID, this::updateAuthMap);
}

protected void updateSelectorMap(final String configInfo) {
try {
//将 json 的数组转化为列表
List<SelectorData> selectorDataList = GsonUtils.getInstance().toObjectMapList(configInfo, SelectorData.class).values().stream().flatMap(Collection::stream).collect(Collectors.toList());
selectorDataList.forEach(selectorData -> Optional.ofNullable(pluginDataSubscriber).ifPresent(subscriber -> {
//这里是删除 selector 的数据
subscriber.unSelectorSubscribe(selectorData);
//这里是新增 selector 的数据
subscriber.onSelectorSubscribe(selectorData);
}));
} catch (JsonParseException e) {
log.error("sync selector data have error:", e);
}
}

小结

从这里可以看出,数据同步的时候,如果删除数据的动作不是通过 admin 去操作的话,就无法通知到 nacos,则可能存在两条重复的 dubbo plugin 的数据,有一条垃圾数据无法被删除,从而导致请求失败。

思考:这里是不是可以将 selectorData 根据 pluginId 和 name 确定唯一性,避免请求时找到垃圾数据,从而导致请求失败。