关于soul网关的数据同步机制,已经从源码里面大致了解了websocket数据同步、http长轮询数据同步、zookeeper数据同步,今天来看一下soul网关是怎么使用nacos进行数据同步的。
先来了解一下nacos,根据nacos官方文档的介绍,可以知道:
Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。
Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。 Nacos 是构建以“服务”为中心的现代应用架构 (例如微服务范式、云原生范式) 的服务基础设施。
那么可以猜测soul网关使用nacos进行数据同步的话,其原理和使用zookeeper是差不多的。数据同步的步骤可以理解成以下几个:
- 业务方将API注册到soul-admin
- soul-admin将配置信息上传到nacos(或zookeeper)
- soul-bootstrap启动的时候,会拉取nacos(或zookeeper)上的信息,并且对数据的变动进行监听
- 管理员在soul-admin修改配置、或者有新的业务方注册API到soul-admin的时候,soul-admin将变动的数据上传到nacos(或zookeeper)
- soul-bootstrap监听到数据更新之后,更新自己保存在内存中的配置信息
nacos安装
我们先来安装一下nacos,以便后面进行调试。nacos可以在官方github上下载。
目前为止,nacos最新的是2.0.0-alpha.2。我们可以直接下载压缩文件。
解压之后,会看到目录里面有如下文件:
使用命令sh bin/startup.sh -m standalone
就可以以单机模式启动nacos,它默认运行在8848端口。
我们在浏览器里面输入http://127.0.0.1:8848/nacos
,看到登录入口,输入nacos/nacos
就可以进入管理界面
soul网关使用nacos数据同步
nacos安装之后,我们可以将soul-admin管理后台、示例项目、网关运行起来。
soul-admin管理后台运行之前,确保application.yml
配置文件里面,设置的数据同步方式是nacos,然后让soul-admin跑起来
soul:
sync:
nacos:
url: localhost:8848
namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
acm:
enabled: false
endpoint: acm.aliyun.com
namespace:
accessKey:
secretKey:
然后运行soul-examples-http
示例项目,就可以在soul-admin的divide插件里面看到示例项目的API已经映射到selector和rule里面了。
按照上面我们分析的数据同步方式,此时soul-admin已经把配置都上传到了nacos了,我们去nacos的管理界面看一下。
…………在配置管理->配置列表
里面啥也没看到,我还以为是我的nacos地址配错了,或者是什么原因导致soul-admin往nacos上传数据失败。后来在nacos管理后台的命名空间
里看了一下,发现只有一个public(保留空间)
,而我们用的空间名是1c10d748-af86-43b9-8265-75f487d20c6c
,在命名空间的界面上没有。
于是,加上这个命名空间,如下图所示
然后在配置管理->配置列表
里面,切换到刚刚加上的命名空间,就可以看到数据了。
我们看一下soul.plugin.json
这个配置的详情,如下图所示,和我们想的一样,是各个插件的PluginData
对象的json形式
再来看一下soul.selector.json
这个配置的详情,如下图所示
然后我们运行soul-boostrap试一下能不能正常通过网关进行api请求,在运行soul-bootstrap之前,确保pom.xml
里面有以下依赖:
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-nacos</artifactId>
<version>${project.version}</version>
</dependency>
确保soul-bootstrap的application.yml
配置文件里面配置的数据同步方式也是nacos
soul:
sync:
nacos:
url: localhost:8848
namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
acm:
enabled: false
endpoint: acm.aliyun.com
namespace:
accessKey:
secretKey:
在浏览器里面输入一个url测试一下,可以通过网关正常访问业务方的api
在我们分析soul网关关于nacos数据同步的原理之前,我们先去nacos官网看下它提供的一些API
- 获取配置的API
getConfig
- 发布配置的API
publishConfig
- 取消监听的API
removeListener
- 还有一个API
getConfigAndSignListener
在官方文档里没找到,不过在网上搜了一下,这个API的意思是获取配置的时候注册一个监听器
soul-admin是如何使用nacos来进行数据同步的
上面已经粗略的了解了一下nacos提供的几个重要的API,下面从源码里面来看下soul-admin是如何使用nacos来进行数据同步的。
有了之前学习websocket数据同步、http长轮询数据同步、zookeeper数据同步的经验,我们知道在DataSyncConfiguration
这个声明了@Configuration
注解的类里面,检测到如果配置文件里面有soul.sync.nacos
,就会像spring容器注入NacosDataChangedListener
和NacosDataInit
两个bean。
来看一下NacosDataInit
,它与之前分析过的ZookeeperDataInit
类似,主要的逻辑就是:
- 判断注册中心的配置不存在的时候,就将配置上传到注册中心去
我们也知道了DataChangedEventDispatcher
这个数据变动事件分发器,它实现了ApplicationListener
接口,当监听到DataChangedEvent
这个事件的时候,就会调用各种XXXDataChangedListener
的onxxChanged
方法。
我们来看下NacosDataChangedListener
里面的一些属性,可以看到是使用了ConcurrentMap
来在内存中存储PluginData、SelectorData、RuleData、AppAuthData、MetaData。
看到NacosDataChangedListener
里面一些方法,可以看到熟悉的onAppAuthChanged
、onPluginChanged
、onSelectorChanged
……
以onSelectorChanged
为例,看一下里面的逻辑,以UPDATE为例,具体的逻辑是:
- 从nacos拉取selector配置,并缓存到SELECTOR_MAP中
- 根据改动的selector,更新
SELECTOR_MAP
内存中缓存的selector配置信息 - 将最新的selector配置信息上传到nacos
soul-bootstrap是如何使用nacos来进行数据同步的
看完了soul-admin的nacos数据同步部分,下面来看下soul-bootstrap的nacos数据同步部分。
我们可以直接找到soul-sync-data-nacos
模块里面找到NacosSyncDataServices
,它的构造函数里面调用了start()
方法,在start()方法里面拉取了nacos配置并且注册了listener。
watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
这句话的意思就是从nacos拉取plugin相关配置,并且注册监听,当监听到变动的时候,就调用本地的updatePluginMap
方法。可能这种形式比较难理解,但是我们可以从最基础的来变形到这一种。
我们看下watcherData
方法接收两个参数,第二个参数是一个Interface
protected void watcherData(final String dataId, final OnChange oc)
我们一开始可以这样写:
watcherData(PLUGIN_DATA_ID, new OnChange() {
@Override
public void change(String changeData) {
updatePluginMap(changeData);
}
});
然后IDEA编辑器会提示你可以简化成lambda表达式的形式,然后就简化成这样:
watcherData(PLUGIN_DATA_ID, changeData -> updatePluginMap(changeData));
然后IDEA编辑器还提示可以进行简化,最后就简化成这样:
watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
其实这个watcherData
方法的主要作用就是:
- 从nacos拉取配置的时候顺便注册listener
- 将listener使用一个map存在内存中。(这些listener会在NacosSyncDataService结束的时候取消监听)
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);
}
我们来看下updatePluginMap
方法,它会在监听到nacos的plugin配置变动的时候被调用。从updatePluginMap
里面我们可以看到熟悉的subscriber.unSubscribe()
和subscriber.onSubscribe()
方法,它最终会去更新内存中缓存的数据。
protected void updatePluginMap(final String configInfo) {
try {
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);
}
}
至此,我们已经把soul网关如何使用nacos进行数据同步的大致流程梳理清楚了。