【Soul网关探秘】http数据同步-Admin通知前处理
引言
本篇开始研究 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 同步策略的变更通知机制,期待惊喜。
个人知识库
【Soul网关探秘】http数据同步-Admin通知前处理的更多相关文章
- 【Soul网关探秘】http数据同步-Web端处理变更通知
个人知识库 引言 上一篇,梳理http 数据同步策略的变更通知机制,本篇开始探究配置变更通知到达后, soul-web 端的处理响应. 不同数据变更的通知机制应当是一致的,故本篇以 selector ...
- Soul 网关 Nacos 数据同步源码解析
学习目标: 学习Soul 网关 Nacos 数据同步源码解析 学习内容: 环境配置 Soul 网关 Nacos 数据同步基本概念 源码分析 学习时间:2020年1月28号 早7点 学习产出: 环境配置 ...
- 搞懂 ZooKeeper 集群的数据同步
本文作者:HelloGitHub-老荀 Hi,这里是 HelloGitHub 推出的 HelloZooKeeper 系列,免费开源.有趣.入门级的 ZooKeeper 教程,面向有编程基础的新手. 项 ...
- rsync+inotify实现服务器数据同步
一.什么是rsync rsync,remote synchronize是一款实现远程同步功能的软件,它在同步文件的同时,可以保持原来文件的权限.时间.软硬链接等附加信息.rsync是用 “rsync算 ...
- phpwind将服务器数据同步到本地之后网站不显示或者排版错误
在将phpwind的数据同步到本地服务器之后 如果访问本地服务器的首页不能显示的话 首先要查看global.php文件中的D_P变量,官方默认 的此变量应该指向和R_P变量是同一个文件夹即网站的根目录 ...
- 使用文件监控对象FileSystemWatcher实现数据同步
最近在项目中有这么个需求,就是得去实时获取某个在无规律改变的文本文件中的内容.首先想到的是用程序定期去访问这个文件,因为对实时性要求很高,间隔不能超过1S,而且每次获取到文本内容都要去分发给WEB服务 ...
- oracle数据同步方案
数据同步方案:--用DBLINK 创建与所需同步表的链接------------------------------------------------------------------------ ...
- rsync与inotify 数据同步
发布:thebaby 来源:脚本学堂 [大 中 小] 本文介绍下,在linux系统中,使用rsync与inotify实现数据同步的一个实例,有研究文件同步的朋友可以作个参考.本文转自:ht ...
- C#使用文件监控对象FileSystemWatcher 实现数据同步
在C#使用文件监控对象FileSystemWatcher 实现数据同步 2013-12-12 18:24 by 幕三少, 352 阅读, 3 评论, 收藏, 编辑 最近在项目中有这么个需求,就是得去实 ...
随机推荐
- 【剑指offer】04 重建二叉树
题目地址:重建二叉树 题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不 ...
- EF Core扩展工具记录 批量操作 记录修改删除历史 动态linq
Microsoft.EntityFrameworkCore.UnitOfWork Microsoft.EntityFrameworkCore的插件,用于支持存储库,工作单元模式以及支持分布式事务 ...
- (一)NumPy基础:数组和矢量计算
一.创建ndarray 1.各种创建函数的使用 import numpy as np #创建ndarray #1.array方法 data1 = [[6, 7.5, 8, 0, 1], [2, 8, ...
- matplotlib学习日记(五)-各种饼状图的绘制
(一)分裂式饼状图 import matplotlib as mpl import matplotlib.pyplot as plt import numpy as np mpl.rcParams[& ...
- AspectJ之@DeclareParents注解为对象添加新方法
众所周知,AspectJ可以通过@Before,@After,@Around等注解对连接点进行增强,今天我们来玩一个新注解@DeclareParents.对目标对象增强一个新方法. 场景引入: 现在我 ...
- JDBC UPDATE误区
1 package com.lykion; 2 3 import java.sql.Connection; 4 import java.sql.PreparedStatement; 5 import ...
- JavaDailyReports10_16
今天学习安装配置了JavaWeb的资源环境, 明天开始学习HTML!
- 使用BigDecimal舍小数取整数
项目需求说明: 解决WMS系统收货容差问题,例如:SKU的采购数量95件,容差是5,95+95*5/100=99.75,传WMS的数量是99,且容差传零. 参数说明: 其中ROUND_UP:向上取整, ...
- vue的路由以后的页面整合
前面呢也提到一点点,今天就吧这个页面整合给分享一下.有不对的地方还望包容. 在vue中,一般在主显示的界面的路径呢一般是'/'也就是单括号中有一斜杠的这个呢是默认的显示路径.只要路由配置了这个路径用& ...
- Maven项目编译之后xml文件不存在
如题: 问题请看图,target目录是编译之后的,发现并没有对应的mapper.xml文件 原因: maven项目默认不加载此类文件 解决办法有两个: 其一是将mybatis的xml映射文件放在mav ...