Soul 网关 Nacos 数据同步源码解析
学习目标:
学习Soul 网关 Nacos 数据同步源码解析
学习内容:
- 环境配置
- Soul 网关 Nacos 数据同步基本概念
- 源码分析
学习时间:2020年1月28号 早7点
学习产出:
环境配置:
- 引入依赖
在 soul-bootstrap项目的 pom.xml 文件中引入了 soul-spring-boot-starter-sync-data-nacos 这个 starter 。
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-nacos</artifactId>
<version>${last.version}</version>
</dependency>
- 环境配置
在 soul-bootstrap 项目的 application-local.yml 文件中,配置
soul :
sync:
nacos:
url: localhost:8848
namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
acm:
enabled: false
endpoint: acm.aliyun.com
namespace:
accessKey:
secretKey:
#url: 配置成你的nacos地址,集群环境请使用(,)分隔。
在 soul-admin 项目中的 application.yml 文件中配置 nacos 同步的相关配置如下:
soul:
sync:
nacos:
url: localhost:8848
namespace: 7aba82c9-426b-4d72-89c9-060d9af63e14
# 此处为前文中提到的命名空间ID或命名空间名称 soul
Nacos 的关键特性
服务发现和服务健康监测
动态配置服务
动态 DNS 服务
服务及其元数据管理
Nacos 中的几个概念
命名空间(Namespace): 不同环境的配置隔离
配置分组(Group): 不同的服务可以归类到同一分组。一般将一个项目的配置分到一组
配置集(Data ID) : 一个配置文件通常就是一个配置集
- nacos同步策略-源码追踪
从 soul-bootstrap 开始追踪
NacosSyncDataConfiguration 这个类加载了 soul.sync.nacos 这段配置。该类创建了 nacosSyncDataService 。
@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));
}
public NacosSyncDataService(final ConfigService configService, final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {
super(configService, pluginDataSubscriber, metaDataSubscribers, authDataSubscribers);
start();
}
public void start() {
watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
watcherData(SELECTOR_DATA_ID, this::updateSelectorMap);
watcherData(RULE_DATA_ID, this::updateRuleMap);
watcherData(META_DATA_ID, this::updateMetaDataMap);
watcherData(AUTH_DATA_ID, this::updateAuthMap);
}
进入 watcherData 方法,此时我们会进入一个重要的类 NacosCacheHandler 。
// 即便对 lambda 不熟悉,我们也可以大致推测,这里的功能应该是通过调用updateXxxMap方法,对元数据、插件、选择器等进行监听
protected void watcherData(final String dataId, final OnChange oc) {
Listener listener = new Listener() {
@Override
public void receiveConfigInfo(final String configInfo) {
oc.change(configInfo);
}
@Override
public Executor getExecutor() {
return null;
}
};
oc.change(getConfigAndSignListener(dataId, listener));
LISTENERS.computeIfAbsent(dataId, key -> new ArrayList<>()).add(listener);
}
protected interface OnChange {
void change(String changeData);
}
updatePluginMap 方法:
protected void updatePluginMap(final String configInfo) {
try {
// Fix bug #656(https://github.com/dromara/soul/issues/656) 此处解决了一个 nacos data sync error 的 bug
List<PluginData> pluginDataList = new ArrayList<>(GsonUtils.getInstance().toObjectMap(configInfo, PluginData.class).values());
pluginDataList.forEach(pluginData -> Optional.ofNullable(pluginDataSubscriber).ifPresent(subscriber -> {
subscriber.unSubscribe(pluginData);
subscriber.onSubscribe(pluginData);
}));
} catch (JsonParseException e) {
log.error("sync plugin data have error:", e);
}
}
追踪一下 unSubscribe(pluginData) 和 onSubscribe(pluginData) ,进入 CommonPluginDataSubscriber 中:
@Override
public void onSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.UPDATE);
}
@Override
public void unSubscribe(final PluginData pluginData) {
subscribeDataHandler(pluginData, DataEventTypeEnum.DELETE);
}
// 根据传入的数据类型ID,和数据处理类型,调用相应的处理函数,看具体是执行update更新还是delete删除
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
if (data instanceof PluginData) {
PluginData pluginData = (PluginData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cachePluginData(pluginData);
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removePluginData(pluginData);
Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData));
}
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
}
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
}
}
});
}
admin追踪:
是 DataSyncConfiguration 这个类加载了 soul.sync.nacos 这段配置。该类创建了 NacosDataChangedListener 。
/**
* The type Nacos listener.
*/
@Configuration
@ConditionalOnProperty(prefix = "soul.sync.nacos", name = "url")
@Import(NacosConfiguration.class)
static class NacosListener {
/**
* Data changed listener data changed listener.
*
* @param configService the config service
* @return the data changed listener
*/
@Bean
@ConditionalOnMissingBean(NacosDataChangedListener.class)
public DataChangedListener nacosDataChangedListener(final ConfigService configService) {
return new NacosDataChangedListener(configService);
}
/**
* Nacos data init zookeeper data init.
*
* @param configService the config service
* @param syncDataService the sync data service
* @return the nacos data init
*/
@Bean
@ConditionalOnMissingBean(NacosDataInit.class)
public NacosDataInit nacosDataInit(final ConfigService configService, final SyncDataService syncDataService) {
return new NacosDataInit(configService, syncDataService);
}
}
@Override
public void run(final String... args) {
String pluginDataId = NacosPathConstants.PLUGIN_DATA_ID;
String authDataId = NacosPathConstants.AUTH_DATA_ID;
String metaDataId = NacosPathConstants.META_DATA_ID;
if (dataIdNotExist(pluginDataId) && dataIdNotExist(authDataId) && dataIdNotExist(metaDataId)) {
syncDataService.syncAll(DataEventTypeEnum.REFRESH);
}
}
// type 是 REFRESH
@Override
public boolean syncAll(final DataEventTypeEnum type) {
appAuthService.syncData();
List<PluginData> pluginDataList = pluginService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));
List<SelectorData> selectorDataList = selectorService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList));
List<RuleData> ruleDataList = ruleService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList));
metaDataService.syncData();
return true;
}
public static void send(final String message, final DataEventTypeEnum type) {
if (StringUtils.isNotBlank(message)) {
if (DataEventTypeEnum.MYSELF == type) {
Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
if (session != null) {
sendMessageBySession(session, message);
}
} else {
SESSION_SET.forEach(session -> sendMessageBySession(session, message));
}
}
}
Soul 网关 Nacos 数据同步源码解析的更多相关文章
- Eureka应用注册与集群数据同步源码解析
在之前的EurekaClient自动装配及启动流程解析一文中我们提到过,在构造DiscoveryClient类时,会把自身注册到服务端,本文就来分析一下这个注册流程 客户端发起注册 boolean r ...
- nacos服务注册源码解析
1.客户端使用 compile 'com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery:2.2.3.RELEASE' compi ...
- Nacos服务发现源码解析
1.Spring服务发现的统一规范 Spring将这套规范定义在Spring Cloud Common中 discovery包下面定义了服务发现的规范 核心接口:DiscoveryClient 用于服 ...
- Eureka源码解析系列文章汇总
先看一张图 0 这个图是Eureka官方提供的架构图,整张图基本上把整个Eureka的核心功能给列出来了,当你要阅读Eureka的源码时可以参考着这个图和下方这些文章 EurekaServer Eur ...
- 死磕 java同步系列之StampedLock源码解析
问题 (1)StampedLock是什么? (2)StampedLock具有什么特性? (3)StampedLock是否支持可重入? (4)StampedLock与ReentrantReadWrite ...
- 死磕 java同步系列之Semaphore源码解析
问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...
- [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler
[源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampler 目录 [源码解析] PyTorch 分布式(1) --- 数据加载之DistributedSampl ...
- [源码解析] PyTorch分布式优化器(2)----数据并行优化器
[源码解析] PyTorch分布式优化器(2)----数据并行优化器 目录 [源码解析] PyTorch分布式优化器(2)----数据并行优化器 0x00 摘要 0x01 前文回顾 0x02 DP 之 ...
- HDFS源码解析:教你用HDFS客户端写数据
摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...
随机推荐
- 基于SpringBoot+Mybatis+MySQL5.7的轻语音乐网
一个基于SpringBoot+Mybatis+MySQL5.7的轻语音乐网站项目 1.主要用到的技术: 使用maven进行项目构建 使用Springboot+Mybatis搭建整个系统 使用ajax连 ...
- 5、Mycat安全
1.权限配置 [1].user标签 目前 Mycat 对于中间件的连接控制并没有做太复杂的控制,目前只做了中间件逻辑库级别的读写权限控制.是通过 server.xml 的 user 标签进行配置. # ...
- java中对list集合中的数据按照某一个属性进行分组
import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Ite ...
- maven中pom的结构介绍
1.自己的坐标 groupId-->artifactId-->version> ` 2.打包方式 jar war pom packaging-->jar/war/pom 3. ...
- msf ms17-010利用笔记
nmap -sV -Pn xxxxx 扫描端口and系统信息 use auxiliary/scanner/smb/smb_ms17_010 扫描模块 set Rhosts 扫描目标 use ex ...
- centos7中redis安装配置
1.官网下载对应版本,本例以5.0.5为例 2.tar -zxvf xxxxx 并mv到安装目录 3.进入redis-5.0.5目录下,执行编译命令 make 4.编译完成后,经redis安装到指定目 ...
- 入门oj 6451: The XOR Largest Pair之二
Description 今天小W用了1s不到的时候完成了这样一个题:在给定的N个整数 A_1,A_2,-,A_N中选出两个进行异或运算,得到的结果最大是多少?正当他志得意满时,L老师亮出了另一个题:给 ...
- TurtleBot3使用课程-第三节a(北京智能佳)
目录 1.[第5类]操纵 2 1.1 软件的安装 2 1.2 硬件设置 2 1.3 打开CR设置 4 1.4 TurtleBot3 提出 5 1.4.1运行 5 1.4.2 Turtle Bot3模型 ...
- Java异常体系概述
Java的异常体系结构 Java异常体系的根类是 Throwable, 所以当写在java代码中写throw抛出异常时,后面跟的对象必然是Throwable或其子类的对象. 其中Exception异常 ...
- 辅助调用函数【call,apply,bind】
函数也是对象,每个函数都有自己的方法. e.g. var jane = { name:'Jane', sayHelloTo:function(name) { 'use strict'; console ...