Soul源码解析(6)-webSocket数据同步原理

一、目的

使用websocket同步数据到网关并了解其运作原理

二、内容

2.1 背景

Soul 网关又是如何支持动态配置的呢?Soul目前支持四种数据同步模式:HTTP长轮询、Zookeeper、WebSocket、Nacos。今天我们主要是针对WebSocket同步策略进行分析。同步策略是在下图里的位置配置的

在这里插入图片描述
同时还要修改soul-admin的同步策略
在这里插入图片描述
修改之后记得重启两个服务;

下图展示了 Soul 数据同步的流程,Soul 网关在启动时,会从从配置服务同步配置数据,并且支持推拉模式获取配置变更信息,并且更新本地缓存。而管理员在管理后台,变更用户、规则、插件、流量配置,通过推拉模式将变更信息同步给 Soul 网关,具体是 push 模式,还是 pull 模式取决于配置。

在这里插入图片描述

如下图所示,soul-admin 在用户发生配置变更之后,会通过 EventPublisher 发出配置变更通知,由 EventDispatcher 处理该变更通知,然后根据配置的同步策略(http、weboscket、zookeeper),将配置发送给对应的事件处理器

在这里插入图片描述

如果是 websocket 同步策略,则将变更后的数据主动推送给 soul-web,并且在网关层,会有对应的 WebsocketCacheHandler 处理器处理来处 admin 的数据推送;

我们来看一下DataChangedEventDispatcher类,它实现了springframework里面的ApplicationListener接口,重写了接口里面的onApplicationEvent()方法

调用时序图如下所示,其实是通过一个Switch进行匹配,匹配到DataChangedListenter里面相应的方法进行处理

在这里插入图片描述

public void onApplicationEvent(final DataChangedEvent event) {
        for (DataChangedListener listener : listeners) {
            switch (event.getGroupKey()) {
                case APP_AUTH:
                    listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
                    break;
                ...
                    
                case META_DATA:
                    listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
                    break;
                default:
                    throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
            }
        }
    }

DataChangedListener是一个接口,接口里面定义了接收到不同类型数据改变的处理接口

public interface DataChangedListener {

  //处理AppAuth数据修改
    default void onAppAuthChanged(List<AppAuthData> changed, DataEventTypeEnum eventType) {
    }

    //处理Plugin数据修改
    default void onPluginChanged(List<PluginData> changed, DataEventTypeEnum eventType) {
    }

   ...

}

我们这里以onPluginChanged为例,继续进行分析,这里调用的是WebsocketDataChangedListener里面的onPluginChanged()方法:

//接收到15个插件的数据列表,和相应的eventType(DELETE、CREATE、UPDATE、REFRESH、MYSELF)进行处理
public void onPluginChanged(final List<PluginData> pluginDataList, final DataEventTypeEnum eventType) {
        //组装取得的数据转成json格式并进行发送
    WebsocketData<PluginData> websocketData =
                new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList);
        WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
    }

下一步走到WebsocketCollector.send()方法

public static void send(final String message, final DataEventTypeEnum type) {
        if (StringUtils.isNotBlank(message)) {
            //进行事件类型匹配,根据sessionKey从Session里面取得数据并进行发送
            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);
                } catch (IOException e) {
                    log.error("websocket send result is exception: ", e);
                }
            }
        }
    }

通过ApplicationEventPublisher里面的publishEvent()方法发出配置变更通知,通知所有匹配的监听者

public void syncData() {
        List<MetaDataDO> all = metaDataMapper.findAll();
        if (CollectionUtils.isNotEmpty(all)) {
            //publishEvent()方法发出配置变更通知,通知所有匹配的监听者
            eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.META_DATA, DataEventTypeEnum.REFRESH, MetaDataTransfer.INSTANCE.mapToDataAll(all)));
        }
    }

接下来数据就从Soul-admin就来到了soul-sync-data-websocket里面进行处理,AbstractDataHandler方法如下,它继承了DataHandler接口,重写了handle方法,匹配对应的事件并调相应的方法进行处理:

public abstract class AbstractDataHandler<T> implements DataHandler {

   //对list进行转换
    protected abstract List<T> convert(String json);

    //刷新数据
    protected abstract void doRefresh(List<T> dataList);

   //更新数据
    protected abstract void doUpdate(List<T> dataList);

    //删除数据
    protected abstract void doDelete(List<T> dataList);

    @Override
    public void handle(final String json, final String eventType) {
        List<T> dataList = convert(json);
        if (CollectionUtils.isNotEmpty(dataList)) {
            DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
            switch (eventTypeEnum) {
                case REFRESH:
                case MYSELF:
                    doRefresh(dataList);
                    break;
                ...
                    
                default:
                    break;
            }
        }
    }

接下来到了WebsocketDataHandler的executor()方法,根据事件类型匹配并调相应方法进行处理

public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
        ENUM_MAP.get(type).handle(json, eventType);
    }

接下来来到了SoulWebsocketClient的handleResult()方法

private void handleResult(final String result) {
        WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
        ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
        String eventType = websocketData.getEventType();
        String json = GsonUtils.getInstance().toJson(websocketData.getData());
        websocketDataHandler.executor(groupEnum, json, eventType);
    }

然后由websocket客户端把数据写入缓存,通知用户等处理;

三、总结

今天我们总体看了一下websocket数据同步的主要流程,里面涉及了很多很好的设计模式和思想,很值得我们花时间去学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值