soul网关源码学习06-websocket数据同步

本文深入探讨Soul网关源码,详细分析了WebSocket数据同步的流程,包括配置同步策略、数据流向、同步流程及关键源码解读,展示了如何实现实时配置更新,无需重启网关。
摘要由CSDN通过智能技术生成

soul网关源码学习06-websocket数据同步

目标:

  • 演示websocket方式数据同步
  • 梳理websocket数据同步流程

一、数据同步分析

下图展示了 Soul 数据同步的流程,Soul 网关在启动时,会从从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,并且更新本地缓存。而管理员在管理后台,变更用户、规则、插件、流量配置,通过推拉模式将变更信息同步给 Soul 网关,具体是 push 模式,还是 pull 模式取决于配置。关于配置同步模块,其实是一个简版的配置中心。
在这里插入图片描述
如下图所示,soul-admin 在用户发生配置变更之后,会通过 EventPublisher 发出配置变更通知,由 EventDispatcher 处理该变更通知,然后根据配置的同步策略(http、weboscket、zookeeper),将配置发送给对应的事件处理器。
如果是 websocket 同步策略,则将变更后的数据主动推送给 soul-web,并且在网关层,会有对应的 WebsocketCacheHandler 处理器处理来处 admin 的数据推送。
在这里插入图片描述
将网关与 admin 建立好 websocket 连接时,admin 会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过 websocket 主动推送给 soul-web

使用websocket同步的时候,特别要注意断线重连,也叫保持心跳。soul使用java-websocket 这个第三方库来进行websocket连接。

二、启动项目

了解了websocket原理之后,开始进入soul数据同步的学习。soul-admin使用websocket方式进行数据同步,这里打开websocket开关。

soul:
	sync:
    	websocket:
      		enabled: true

启动soul-adminsoul-bootstrapsoul-examples-http,打开系统:http://localhost:9095,admin/123456
在这里插入图片描述
规则/http/order/findById处于开启状态,发起一次http请求,接口正常返回。
在这里插入图片描述
修改规则/http/order/findById为关闭状态。
在这里插入图片描述
再发起一次http请求,接口返回Rule not found!
在这里插入图片描述
从上面的演示可以看出,管理后台的配置会自动同步到网关,这个同步是怎么样实现的呢?
从websocket原理可以看出,首先要找到客户端和服务端,两边建立连接,然后可以互相通信。

三、数据流向分析

  • admin启动,websocket服务端暴露端点等待连接。
  • bootstrap启动的时候会建立与服务端的连接并发送消息。
  • admin收到消息之后发送全量的数据到bootstrap客户端,客户端更新全量数据。
  • adminCRUD操作时,会发送增量数据到bootstrap客户端,客户端更新增量数据。
    在这里插入图片描述

四、数据同步流程

  • 启动admin,加载事件分发器DataChangedEventDispatcher
  • 实例化DataChangedListener的实现类WebsocketDataChangedListener,开始监听admin数据的变化。
  • 根据配置加载websocket服务端WebsocketCollector
  • 启动bootstrap,加载websocket数据同步类WebsocketSyncDataService
  • 初始化websocket客户端SoulWebsocketClient,然后会自动发送一条消息到服务端要全量同步一次数据。
  • 客户端接收到消息后把数据发回给客户端,客户端把数据更新到缓存中。
  • adminbootstrap都启动之后,修改admin的数据,会触发事件监听,服务端会把最新有变动的数据发送到客户端,客户端把数据更新到缓存中。

在这里插入图片描述

五、源码分析

首先,找一下服务端,在soul-admin找到数据同步配置类DataSyncConfiguration,然后找到WebsocketListener,打上断点。

        @Bean
        @ConditionalOnMissingBean(WebsocketDataChangedListener.class)
        public DataChangedListener websocketDataChangedListener() {
   
            return new WebsocketDataChangedListener();
        }

重启soul-admin,debug会进入到上面的方法中,然后查看调用栈,找到DataChangedEventDispatcherafterPropertiesSet方法。

	public void afterPropertiesSet() {
   
        Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
        this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
    }

这个applicationContext.getBeansOfType(DataChangedListener.class)类似于工厂模式和策略模式的结合,加载所有实现了DataChangedListener接口的子类,这样做的好处是易于扩展。
但是从debug过程可以看到,这里只加载了WebsocketDataChangedListener,因为这里只配置了websocket一种同步方式,注意看DataSyncConfiguration这个配置类里面其他方式的@ConditionalOnProperty注解。

    public void onApplicationEvent(final DataChangedEvent event) {
   
        for (DataChangedListener listener : listeners) {
   
        //省略
        }
    }    

接着看一下WebsocketDataChangedListener,里面就是各种onchange,监听数据的变化,这里看一下onPluginChanged,其他的类似。定义一个统一的WebsocketData,然后通过WebsocketCollectorsend方法发送数据。

    public void onPluginChanged(final List<PluginData> pluginDataList, final DataEventTypeEnum eventType) {
   
        WebsocketData<PluginData> websocketData =
                new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList);
        WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
    }
public static void send(final String message, final DataEventTypeEnum type) {
   
        if (StringUtils.isNotBlank(message)) {
   
            if (DataEventTypeEnum.MYSELF == type) {
   
                try {
   
                    Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
                    if (session != null) {
   
                        session.getBasicRemote().sendText(message);
                    }
                } catch (IOException e) {
   
                    log.error("websocket send result is exception: ", e);
                }
                return;
            }
            for (Session session : SESSION_SET) {
   
                try {
   
                    session.getBasicRemote().sendText(message
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值