说明
- 本文将探讨shenyu-admin 与 shenyu网关数据通过websocket同步的主要流程
shenyu数据同步概述
shenyu网关为什么需要数据同步呢?这就要从shenyu API网关的设计谈起了。shenyu网关配置是热更新的,也就是说在shenyu-admin前端服务上配置网关相关参数后,为了让网关不用重启而更新配置,是需要将最新的配置同步到shenyu-web网关服务上的。shenyu是支持pull 或 push方式对数据进行通过。shenyu数据同步支持三种方式,分别为 zookeeper、http长轮询、websocket。
数据同步架构图
下图来源于shenyu 官网。
websocket同步方式
shenyu-web同步数据相关源码分析
源码结构分析
我们启动shenyul网关时,只需要启动shenyu-admin和shenyu-bootstrap两个spring boot 项目就可以了,admin是管理网关的,那网关的相关代码是哪些呢?shenyu-bootstrap里只有一个创建nettyserver的工厂类和一个健康检查的filter类,显然数据同步、高可用、插件支持等都是通过引入相关的shenyu-spring-boot-start完的。
以下截取shenyu-bootstrap项目中引用的部分start
<!--shenyu gateway start-->
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-spring-boot-starter-gateway</artifactId>
<version>${project.version}</version>
</dependency>
<!--if you use http proxy start this-->
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-spring-boot-starter-plugin-divide</artifactId>
<version>${project.version}</version>
</dependency>
<!--if you use http proxy end this-->
<!--shenyu data sync start use websocket-->
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-spring-boot-starter-sync-data-websocket</artifactId>
<version>${project.version}</version>
</dependency>
shenyul网关结构
shenyu网关的start是 shenyu-spring-boot-starter-gateway,主要处理的就是我们网关请求连接的。我们从改start的pom.xml配置得知,它的源码对应的就是 shenyu-web项目。
<parent>
<artifactId>shenyu-spring-boot-starter</artifactId>
<groupId>org.apache.shenyu</groupId>
<version>2.2.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<artifactId>shenyu-spring-boot-starter-gateway</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.shenyu</groupId>
<artifactId>shenyu-web</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
shenyu 数据同步方式之websocket项目结构
在上面shenyu-bootstrap给出的依赖中,shenyu-spring-boot-starter-sync-data-websocket 对应的就是websocket同步的项目。接下来我们看下它的核心结构。
spring.factories说明
springboot 会根据项目中spring.factories的配置,注入对象。
我们从shenyu-spring-boot-starter-sync-data-websocket项目中的spring.factories中得到以下配置:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.apache.shenyu.shenyu.spring.boot.starter.sync.data.websocket.WebsocketSyncDataConfiguration
引入shenyul-spring-boot-starter-sync-data-websocket 的项目会自动注入配置的这个 WebsocketSyncDataConfiguration类。
源码分析
我们分析下 WebsocketSyncDataConfiguration 类的源码都做了什么。由于配置类不多,我就全部贴在下面了。
@Configuration
@ConditionalOnClass(WebsocketSyncDataService.class)
@ConditionalOnProperty(prefix = "shenyu.sync.websocket", name = "urls")
@Slf4j
public class WebsocketSyncDataConfiguration {
@Bean
public SyncDataService websocketSyncDataService(final ObjectProvider<WebsocketConfig> websocketConfig, final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers, final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
log.info("you use websocket sync shenyu data.......");
return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList), authSubscribers.getIfAvailable(Collections::emptyList));
}
/**
* Config websocket config.
*
* @return the websocket config
*/
@Bean
@ConfigurationProperties(prefix = "shenyu.sync.websocket")
public WebsocketConfig websocketConfig() {
return new WebsocketConfig();
}
}
我们看到改类上有条件以下条件注解:
- @ConditionalOnClass(WebsocketSyncDataService.class) : 引用了WebsocketSyncDataService类才会装配改类
- @ConditionalOnProperty(prefix = "shenyu.sync.websocket", name = "urls") : 配置中有前缀为 "shenyu.sync.websocket",name中为"urls"的才会装配。
也就是说,我们要让websocket同步方式生效,在 shenyu-bootstrap中要有以下配置才生效
shenyu:
sync:
websocket:
urls: ws://localhost:9095/websocket # shenyu-admin地址
WebsocketSyncDataConfiguration 装配了一个WebsocketSyncDataService和WebsocketConfig配置类,WebsocketSyncDataService 就是shenyu网关和shenyu-admin异步同步数据建立socket连接的地方。接下来我们就分析下 WebsocketSyncDataService的源码
public class WebsocketSyncDataService implements SyncDataService, AutoCloseable {
……
public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
……
// 与shenyu-admin建立websocket连接
boolean success = client.connectBlocking(3000, TimeUnit.MILLISECONDS);
……
// 断线重连
executor.scheduleAtFixedRate(() -> {
try {
if (client.isClosed()) {
boolean reconnectSuccess = client.reconnectBlocking();
if (reconnectSuccess) {
log.info("websocket reconnect is successful.....");
} else {
log.error("websocket reconnection is error.....");
}
}
} catch (InterruptedException e) {
log.error("websocket connect is error :{}", e.getMessage());
}
}, 10, 30, TimeUnit.SECONDS);
}
@Override
public void close() {
……
}
- pluginDataSubscriber: 插件数据同步订阅
- metaDataSubscribers: 元数据同步订阅
- authDataSubscribers: 认证数据同步订阅
shenyu-admin同步数据相关源码分析
shenyu-admin管理服务,它的数据同步相关类是DataSyncConfiguration
@Configuration
@ConditionalOnProperty(name = "shenyu.sync.websocket.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(WebsocketSyncProperties.class)
static class WebsocketListener {
/**
* Config event listener data changed listener.
*
* @return the data changed listener
*/
@Bean
@ConditionalOnMissingBean(WebsocketDataChangedListener.class)
public DataChangedListener websocketDataChangedListener() {
return new WebsocketDataChangedListener();
}
……
}
- @ConditionalOnProperty: 在shenyu-admin的properties或者yml文件中配置 shenyu.sync.websocket.enabled = true 时才装配改类
- WebsocketDataChangedListener: 监听websocket数据变化的类。
在WebsocketDataChangedListener类中以下方法解释:
- onPluginChanged(): 当插件变化时触发同步插件信息
- onSelectorChanged(): 当选择器变化时触发同步选择器信息
- onRuleChanged(): 当规则变化时触发同步规则信息
- onAppAuthChanged():当应用认证规则变化时触发同步应用认证规则信息
- onMetaDataChanged(): 当原数据变化时出发同步原数据信息
…… @Override public void onSelectorChanged(final List<SelectorData> selectorDataList, final DataEventTypeEnum eventType) { WebsocketData<SelectorData> websocketData = new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList); WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType); } ……
当web-admin改动相关信息后就通过上述方式建立起的websocket通道发送信息,而shenyu-spring-boot-starter-sync-data-websocket里则订阅了相关信息,接收到信息后,就同步到本地内存中。