Apache ShenYu 网关数据同步之 websocket

说明

  • 本文将探讨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里则订阅了相关信息,接收到信息后,就同步到本地内存中。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注