引言

本篇开始研究 Soul 网关 http 数据同步,将分为三篇进行分析:

  • 《Admin通知前处理》
  • 《变更通知机制》
  • 《Bootstrap处理变更通知》

希望三篇完结后能对 Soul 的 http 数据同步策略有所收获。

本篇旨在探究 soul-admin 端在发起变更通知前所做的处理。

不同数据变更的处理模式应当是一致的,故本篇以 selector 配置变更为切入点进行深入。

一、配置变更入口

找到 SelectorController,这是 selector 配置变更的入口

其持有一个 SelectorService 引用,通过 SelectorService 实现 selector 配置变更。

二、配置变更服务

再来看看 SelectorService,实现了配置变更的具体处理。

其内部持有5个 mapper、1个 eventPublisher和1个 upstreamCheckService,对外提供一系列对 selector 的crud方法

注意 createOrUpdate 方法

public int createOrUpdate(final SelectorDTO selectorDTO) {
int selectorCount;
SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO);
List<SelectorConditionDTO> selectorConditionDTOs = selectorDTO.getSelectorConditions();
// 数据落库
if (StringUtils.isEmpty(selectorDTO.getId())) {
selectorCount = selectorMapper.insertSelective(selectorDO);
selectorConditionDTOs.forEach(selectorConditionDTO -> {
selectorConditionDTO.setSelectorId(selectorDO.getId());
selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO));
});
} else {
selectorCount = selectorMapper.updateSelective(selectorDO);
//delete rule condition then add
selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId()));
selectorConditionDTOs.forEach(selectorConditionDTO -> {
selectorConditionDTO.setSelectorId(selectorDO.getId());
SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO);
selectorConditionMapper.insertSelective(selectorConditionDO);
});
}
// 发布 spring 事件
publishEvent(selectorDO, selectorConditionDTOs);
// 更新 divide 上游服务
updateDivideUpstream(selectorDO);
return selectorCount;
}

处理策略是先落库,再发布 spring 事件,最后更新 divide 上游服务

三、spring 事件通知机制

此处涉及 spring 的事件通知机制,在此简要说明:

ApplicationContext通过ApplicationEvent类和ApplicationListener接口提供事件处理。

如果一个bean实现ApplicationListener接口在容器中,每次一个ApplicationEvent被发布到ApplicationContext中,这类bean就会收到这些通知。

实现Spring事件机制主要有4个类:

  • ApplicationEvent:事件,每个实现类表示一类事件,可携带数据。
  • ApplicationListener:事件监听器,用于接收事件处理时间。
  • ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的广播。
  • ApplicationEventPublisher:事件发布者,委托ApplicationEventMulticaster完成事件发布。

四、soul 实现事件通知

下面我们看看 Soul 是如何使用 spring 的时间通知机制。

事件定义

DataChangedEvent 继承 ApplicationEvent,提供了 DataChangedEvent(groupKey, type, source) 事件构造方法

事件监听器

DataChangedEventDispatcher 实现了 ApplicationListener接口,借助 onApplicationEvent 方法监听事件

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 PLUGIN:
listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
break;
case RULE:
listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
break;
case SELECTOR:
listener.onSelectorChanged((List<SelectorData>) 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());
}
}
}

该方法内按事件类型分别处理,DataChangedEventDispatcher 同时实现了 InitializingBean 接口,在初始化后完成 listeners 的注入。

五、响应数据变更事件

上面的事件监听处理用到 soul 的 DataChangedListener 接口

DataChangedListener 实现了不同类型事件的事件响应方法用于响应 DataChangedEvent 事件。

1)AbstractDataChangedListener 的 onSelectorChanged 实现:

public void onSelectorChanged(final List<SelectorData> changed, final DataEventTypeEnum eventType) {
if (CollectionUtils.isEmpty(changed)) {
return;
}
// 更新 selector 缓存
this.updateSelectorCache();
// selector 变更后处理,实现具体的变更通知
this.afterSelectorChanged(changed, eventType);
}

可以看到 selector 变更处理是先更缓存后发通知。

2)AbstractDataChangedListener 的 updateSelectorCache 实现:

protected void updateSelectorCache() {
this.updateCache(ConfigGroupEnum.SELECTOR, selectorService.listAll());
}

3)AbstractDataChangedListener 的 updateCache 实现:

protected <T> void updateCache(final ConfigGroupEnum group, final List<T> data) {
String json = GsonUtils.getInstance().toJson(data);
ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis());
ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal);
log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal);
}

可以看到最终是创建对应的 ConfigDataCache 存入 CACHE。

总结

本篇梳理了 soul-admin 在真正发出数据变更通知前的处理脉络,其策略是:先写库后更缓存,最后发出数据变更通知。

先写库保证数据不丢,另外在集群部署时,其他 soul-admin 节点也可通过浏览页面时查库保证数据一致。

意外学到 spring 的事件通知机制,soul 中的设计果真小巧精妙。

下篇,将探究 http 同步策略的变更通知机制,期待惊喜。

个人知识库

高性能微服务API网关-Soul

【Soul网关探秘】http数据同步-Admin通知前处理的更多相关文章

  1. 【Soul网关探秘】http数据同步-Web端处理变更通知

    个人知识库 引言 上一篇,梳理http 数据同步策略的变更通知机制,本篇开始探究配置变更通知到达后, soul-web 端的处理响应. 不同数据变更的通知机制应当是一致的,故本篇以 selector ...

  2. Soul 网关 Nacos 数据同步源码解析

    学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: 环境配置 Soul 网关 Nacos 数据同步基本概念 源码分析 学习时间:2020年1月28号 早7点 学习产出: 环境配置 ...

  3. 搞懂 ZooKeeper 集群的数据同步

    本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...

  4. rsync+inotify实现服务器数据同步

    一.什么是rsync rsync,remote synchronize是一款实现远程同步功能的软件,它在同步文件的同时,可以保持原来文件的权限.时间.软硬链接等附加信息.rsync是用 “rsync算 ...

  5. phpwind将服务器数据同步到本地之后网站不显示或者排版错误

    在将phpwind的数据同步到本地服务器之后 如果访问本地服务器的首页不能显示的话 首先要查看global.php文件中的D_P变量,官方默认 的此变量应该指向和R_P变量是同一个文件夹即网站的根目录 ...

  6. 使用文件监控对象FileSystemWatcher实现数据同步

    最近在项目中有这么个需求,就是得去实时获取某个在无规律改变的文本文件中的内容.首先想到的是用程序定期去访问这个文件,因为对实时性要求很高,间隔不能超过1S,而且每次获取到文本内容都要去分发给WEB服务 ...

  7. oracle数据同步方案

    数据同步方案:--用DBLINK 创建与所需同步表的链接------------------------------------------------------------------------ ...

  8. rsync与inotify 数据同步

    发布:thebaby   来源:脚本学堂     [大 中 小] 本文介绍下,在linux系统中,使用rsync与inotify实现数据同步的一个实例,有研究文件同步的朋友可以作个参考.本文转自:ht ...

  9. C#使用文件监控对象FileSystemWatcher 实现数据同步

    在C#使用文件监控对象FileSystemWatcher 实现数据同步 2013-12-12 18:24 by 幕三少, 352 阅读, 3 评论, 收藏, 编辑 最近在项目中有这么个需求,就是得去实 ...

随机推荐

  1. Python之格式化unix时间戳

    就瞎倒腾,格式化时间: 1 import time 2 3 unixTime = time.time() #定义unixTime以存储系统当前的unix时间戳 4 print(unixTime); # ...

  2. Hive动态分区详解

    目录 动态分区调整 注意 动态分区插入 动静分区结合 例子 动态分区调整 动态分区属性:设置为true表示开启动态分区功能(默认为false)hive.exec.dynamic.partition=t ...

  3. Windows 上安装 PostgreSQL

    PostgreSQL官网–>Download–>Windows 64位,如图所示: (1)官网: https://www.postgresql.org/ (2)Download: http ...

  4. PHP7.4.3的BUG导致微信公众号CURl上传文件的412错误

    https://segmentfault.com/q/1010000021407039 升级PHP就好了 https://segmentfault.com/q/1010000021407039

  5. Java源码研究001:关于List的并发修改异常

    这个就是实现一个简单的 ArrayList 的遍历,如果存在一个为"aaa"的值,就添加一个"ccc" package Array; import java.u ...

  6. 基于ESP32的智能家居管理系统的设计与实现

    基于ESP32的智能家居管理系统的设计与实现 ESP32的智能家居管理系统访问链接: https://www.cnblogs.com/easyidea/p/13101165.html 一.需求分析 1 ...

  7. docker基础属性简介包含镜像 容器 registry服务等概念及关系

    Docker 镜像 我们都知道,操作系统分为内核和用户空间.对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持.而 Docker 镜像(Image),就相当于是一个 r ...

  8. 别再费劲去找后台的前端框架了,2021 年就用 Fantastic-admin 吧

    前言 你知道光是基于 Vue 的后台框架在 Github 上有多少个仓库么? 如果你搜索 vue admin 会得到 13120 个仓库,如果用 vue 后台 会得到 7596 个仓库,如果把两者结合 ...

  9. Playwright VS Selenium VS Puppeteer VS Cypress

    参考:https://www.testim.io/blog/puppeteer-selenium-playwright-cypress-how-to-choose/ 这四款自动化测试框架在我们的公众号 ...

  10. 使用Modbus4J进行RTU模式串口通信

    Modus协议是由MODICON(现为施耐德电气公司的一个品牌)在1979年开发的,是全球第一个真正用于工业现场的总线协议,应用非常广泛,可谓大名鼎鼎. 理论性的东西就不多介绍了,推荐一本书<M ...