soul源码解读(五)-- 数据同步之websocket

17 篇文章 0 订阅

soul源码解读(五)

数据同步之websocket

今天我们来看下 soul admin 与 bootstrap 之间数据同步的源码。
官网链接如下:https://dromara.org/zh-cn/docs/soul/dataSync.html
先来张流程图
在这里插入图片描述

从第一天的启动日志我们发现它们是通过 websocket 来进行通信的。
在这里插入图片描述
看 admin 的配置发现,soul 数据同步有4种方式,分别是 websocket 、zookeeper 、http 、 nacos。
bootstrap 的配置和 admin 一样,只是多配置了一个 websocket 的地址

sync:
        websocket :
             urls: ws://localhost:9095/websocket

我们找到 admin 模块 org.dromara.soul.admin.listener.websocket 包下的 WebsocketCollector

@ServerEndpoint("/websocket")
public class WebsocketCollector {

可以看到类上注解了一个端点 websocket ,这就是上面 bootstrap 里的配置urls的地址来源。
接着看这个类里的代码,

@OnOpen
    public void onOpen(final Session session) {
        log.info("websocket on open successful....");
        SESSION_SET.add(session);
    }

我们看到 admin 在开启 websocket 连接之后,会把当前 session 存到一个 set 里。

有消息过来之后(比如 bootstrap 启动之后会发送连接消息过来,admin 就会把数据推送过去),会判断一下类型,把 session 存到当前线程的 ThreadLocal里,

 @OnMessage
    public void onMessage(final String message, final Session session) {
        if (message.equals(DataEventTypeEnum.MYSELF.name())) {
            try {
                ThreadLocalUtil.put(SESSION_KEY, session);
                SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF);
            } finally {
                ThreadLocalUtil.clear();
            }
        }
    }

然后通过 sping 获取 SyncDataService 的 bean ,调用 syncAll(),里面会同步索引的权限、插件、选择器、规则、元数据等。

 @Override
    public boolean syncAll(final DataEventTypeEnum type) {
        appAuthService.syncData();
        List<PluginData> pluginDataList = pluginService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));
        List<SelectorData> selectorDataList = selectorService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList));
        List<RuleData> ruleDataList = ruleService.listAll();
        eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList));
        metaDataService.syncData();
        return true;
    }

接下来,我们看看数据是怎么同步的。
以插件数据为例。

List<PluginData> pluginDataList = pluginService.listAll();

先获取所有的插件,通过 mapper 去数据库查询数据,然后经过 stream 转成 list 。

@Override
    public List<PluginData> listAll() {
        return pluginMapper.selectAll().stream()
                .map(PluginTransfer.INSTANCE::mapToData)
                .collect(Collectors.toList());
    }

中间我们看到有个属性转换,点进去之后发现 PluginTransfer 的实现类是自动生成的。
在这里插入图片描述
我去搜索了下 mapstruct ,发现它是一个属性映射工具,只需要定义一个 Mapper 接口,MapStruct 就会自动实现这个映射接口,避免了复杂繁琐的映射实现。MapStruct官网地址: http://mapstruct.org/

今天又学到了一新的知识点。

然后我们往回看,获取到所以插件列表之后,执行了下面这段代码

eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));

我们又发现了新东西, ApplicationEventPublisher ,查资料发现这是一个 spring 官方推出的事件发布器,用来给程序解耦。

流程图如下
在这里插入图片描述
在 admin 后台更新一个插件的状态,然后 debug 跟踪之后搞清楚了调用链。

先更新数据库,然后发布事件,DataChangedEventDispatcher 监听到事件后,调用具体的WebsocketDataChangedListener ,然后通过 session 推送消息。

admin 发送消息的调用栈如下:
在这里插入图片描述

发布消息流程

PluginServiceImpl
DataChangedEventDispatcher
DataChangedListener
WebsocketDataChangedListener
WebsocketCollector

最终推送消息是 WebsocketCollector 的 send()

public static void send(final String message, final DataEventTypeEnum type) {
      //其他代码省略
      session.getBasicRemote().sendText(message);
    }

bootstrap 端接收调用栈如下:
在这里插入图片描述

接收流程

SoulWebsocketClient
WebsocketDataHandler
AbstractDataHandler
PluginDataHandler
CommonPluginDataSubscriber
BaseDataCache

最终接收推送后,执行更新数据是 CommonPluginDataSubscriber 的 subscribeDataHandler()

private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
        Optional.ofNullable(classData).ifPresent(data -> {
            if (data instanceof PluginData) {
                PluginData pluginData = (PluginData) data;
                if (dataType == DataEventTypeEnum.UPDATE) {
                    BaseDataCache.getInstance().cachePluginData(pluginData);
                    Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData));
                } 
            } 
            //其他代码省略
        });
    }

总结

今天学到了两个新的知识点:
1.mapstruct ,用来做各种 POJO 之间的属性转换
2.事件发布监听机制,可以解耦程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值