变更通知在开源SpringBoot/SpringCloud微服务中的最佳实践

news/2025/1/19 18:46:12/

目录导读

  • 变更通知在开源SpringBoot/SpringCloud微服务中的最佳实践
    • 1. 什么是变更通知
    • 2. 变更通知的场景分析
    • 3. 变更通知的技术方案
      • 3.1 变更通知的技术实现方案
    • 4. 变更通知的最佳实践总结
    • 5. 参考资料

变更通知在开源SpringBoot/SpringCloud微服务中的最佳实践

1. 什么是变更通知

  • 变更通知是指数据发生变化后,相对实时地通知到关联端的技术实现方案;
  • 变更通知是在服务拆分的发展过程中,逐渐衍生出的,解耦关联服务的一种技术解决方案;
  • 微服务架构中常说的配置中心就是变更通知的技术集成方案;

2. 变更通知的场景分析

  • 变更通知首先是数据发生了变化,数据变化后的通知实现,数据变化的交互场景列举分析如下:
通知类型模块1模块1数据变更模块2模块2实时变更技术实现方式本文讨论范围
消息通知前端1前端2前端总线等
消息保存前端1后端2Post Ajax请求等
消息通知后端1前端2WebSocket等推送到前端(推数据),也叫消息推送
消息通知后端1前端2前端Http主动轮询(拉数据)
变更通知后端1后端2Redis/ZooKeeper/Etcd/Nacos/MQ等

总结:

  • 变更通知首先是数据(也可叫消息,下同。)发生了变化,然后引起了关联方同步变化;
  • 数据变化可以发生在前端后端之间的任意组合之间,包括前端+前端前端+后端后端+后端,一般只有后端+后端的连锁反应叫变更通知,这也是本文关注的重点;
  • 上表串联了所有常规的交互过程,大家也可以从中理解消息通知变更通知之间的关联关系:只是不同交互方式下的数据变化的叫法罢了;
  • 前端感知后端的数据变化,主要有前端轮询后端推送两种,二者技术实现差异较大,前者技术实现简单,但是后端资源消耗较大,无法承担高并发;后者技术实现较复杂,但是点对点通信效率高(长连接)。这不是本文讲述的重点,但希望能够类比分析下各种交互方式下的技术方案,做到一通百通;
  • 变更通知主要关注的是后端数据变化,但是不是所有的后端数据变化都需要变更通知。一般来说,跨了服务实例,才比较适用变更通知,单个服务实例内,完全可以通过接口引用等方式自行获取到变化的数据;
  • 本文重点关注的是变更通知而不是配置中心,像NacosSpringCloud-ConfigApollo配置中心并不会过多介绍;
  • 本文主要关注后端与后端之间的数据变化机制及技术实现,其发展阶段如下:
变更类型应用发展阶段技术方案技术实现说明
主动获取单实例共享数据库查询数据库数据库查询量不大
主动获取单实例共享内存缓存查询时先查缓存数据库查询量非常大
主动获取多实例分布式缓存使用读写性能极高的分布式缓存组件如:Memcached/Redis
变更通知多实例消息中间件Redis/ZooKeeper/Etcd/MQ/binlog+canal等订阅通知机制

总结:

  • 变更通知是随着服务的高并发、分布式发展而发展的,在单体架构时,因为都在一个服务内,仅通过数据库或者共享缓存即可达到数据共享的目的,不一定需要变更通知
  • 在微服务架构中,也可以采用共享缓存方案,而不是必须使用变更通知变更通知适用于并发高、实时性要求高,且服务解耦的场景;
  • 实时是一个相对概念,在变更通知语境里,一般是指异步监听的方式获取变化的数据(专业术语叫订阅通知);

3. 变更通知的技术方案

  • 变更通知中间件种类较多,基于本人的理解,对比列举如下:
中间件类型实现特点适用场景不足补充说明
Redis基于key的订阅/通知并发高、消息量大NoSQL可读性差,持久化不是必选项,存在数据丢失和审计风险Redis是极度常用的高效内存组件,建议优选;
ZooKeeper基于分布式临时Node创建的订阅/通知可靠性高、实时性高使用的是内存存储,不适合高并发和大量数据的消息变更场景一般是项目中有ZooKeeper,正好可以用作变更通知组件,而不是因为变更通知诉求而引入ZooKeeper
Etcd基于分布式的Key/Value创建的订阅/通知可靠性高、实时性高使用的是内存存储,不适合高并发和大量数据的消息变更场景Etcd的实现参考了ZooKeeper,一个是Go语言编写、一个是Java语言
MQ异步消息协议可靠性高、并发高、消息量大组件较重包括:Kafka/RocketMQ/RabbitMQ/ActiveMQ等,非常适合电商优惠卷等场景使用
binlog+canal针对MySQL的数据变更监听方案直接监听数据库表字段变化只适用于MySQL,而MySQL使用量正逐年下降只是监听了MySQL的数据表变化,一般还需要配合其它的变更通知组件来配合使用,如:ZooKeeper
配置中心服务端推送把配置中心组件当成业务配置中心组件重、可靠性低、实时性低配置中心严格来说是个变更通知的解决方案,而上面列举的中间件是纯技术组件,二者的维度不太一样;
配置中心一般和注册中心配合使用,因为它本身也需要注册至服务中心,如:ApolloSpringCloud-Config;有些干脆就被注册中心兼任,如Nacos

总结:

  • 变更通知一般来说是微服务中的增强功能,不建议因为有变更通知需求就新增一个组件。如果系统中已经有了Redis,就建议优先选择Redis,因为其性能高、消息存储量大。但Redis是NoSQL存储结构,可读性较差;Redis也有可能没有开启持久化导致数据丢失;Redis也缺失了类似关系数据库自带的操作审计,一旦数据出现了异常,将很难知道是谁做了什么;
  • ZooKeeper/Etcd则比较适合系统中已经引入该组件了,且变更通知消息数量较小的场景;
  • MQ比较适用于可靠性高、消息量巨大的场景,值得单独引入。如:大型电商的活动卡券配置等场景;
  • binlog+canal这个特定组合一般不建议单独使用,一般是canal把变更数据发送给其它变更通知Server,然后在业务模块订阅变更通知Server的这个变化数据,并做相应的业务处理。其它Server可以是ZooKeeper/Etcd/Redis等;
  • 一般来说,配置中心包含了单独的配置规则界面和变更通知的能力,拆箱即用,效果当然较好。但并不是所有的变更通知都需要重量级的配置中心,不是非要在配置中心去配置变更数据的,就都没有必要用它;业务需要使用到配置中心组件时,建议在选定注册中心后,再来决策选择其配置中心
  • 变更通知有非常多的实现方式,讲讲本人实际经历的业务场景:
场景类型业务诉求微服务架构微服务技术栈方案说明
场景1DB数据变更立即触发定时任务微服务架构spring+mysql+binlog+canal+ZooKeeper基于spring自研微服务框架
场景2-1业务阈值在1天内生效微服务云原生架构SpringBoot+Redis+PostgreSQL1.依赖k8s提供服务发现等;
2.依赖redis限流;
场景2-2业务阈值在1天内生效微服务私部署架构SpringBoot1.部署至客户机房,不依赖Redis/DB;
2.使用Nginx做负载均衡,也不需要服务注册和发现;
场景3业务阈值在5分钟内生效微服务云原生架构SpringBoot+Redis1.依赖k8s提供服务发现等;
2.依赖redis限流;
场景4业务阈值在立即生效SpringCloud微服务架构SpringBoot+Redis+Nacos1.依赖Nacos提供服务发现等;
2.依赖redis限流;
  • 补充说下上述业务场景的技术选型限定条件:
    • 场景1:DB数据变更立即触发定时任务:当时刚刚时兴微服务,我所在公司1的部门基于spring自研了一个低代码平台,我们需要实时监听数据的变化,以触发不同的定时任务,正好平台中也引入了canal开源组件,用于监听mysql的binlog
      变化,于是就选定了canal方案,这样就不用定时轮询数据库了,也不用和增删改数据的服务耦合了;
    • 场景2:业务阈值在1天内生效场景2-1场景2-2其实对应同一个目标:我所在公司2的部门希望设计一套架构、一套代码,既能满足云上微服务架构,也能够支持私部署微服务架构。当时云上选型为云原生微服务架构,完全基于k8s+非侵入式的链路追踪中间件,我们基本上只使用最简单的SpringBoot+Redis即可;而私部署则继续沿用了这套架构和代码,只不过移除了k8s、redis、服务注册和服务发现,仅使用Nginx做服务负载均衡;
    • 场景3:业务阈值在5分钟内生效:则是我所在公司2的业务团队认为一天生效对业务影响较大,需要调整为5分钟内生效;
    • 场景4:业务阈值在立即生效:则是我所在公司2的另一个新项目,在场景3代码架构的基础上,使用SpringCloud套件替换了k8s,同时业务也更复杂,业务上必须保证实时生效;
  • 单独来说上面的每个业务场景,都可以多种技术实现。本人仅站在过来人的角度,逐一展开分析。

3.1 变更通知的技术实现方案

  • 场景1的实现方案:单独部署了一个Canal服务,用于监听binlog变化,在Canal服务中又集成了ZooKeeper客户端,Canal收到变化的数据后,通过ZooKeeper推送至订阅的业务微服务;

  • 场景2的实现方案:经过分析,私部署不需要变更通知,因为私部署不带数据库,业务阈值是配在yaml中,修改后隔离重启服务即可;云原生微服务则因为数据刷新后1天内生效即可,但为了考虑私部署和云原生架构的统一,所以采用了Guava+持久层的本地缓存方案,Guava缓存的有效期设置为24小时,过期后就会重新从PostgreSQL/yaml中获取。但由于云原生的运维不接收直接修改数据库阈值数据,于是又配套开发了一个小的命令行工具(也可以做成Web运维平台),用于专门更改数据库表的字段值。

    从中可以看出:

    • 因为要兼顾云原生和私部署场景,所以需要选择两种场景下都能使用的本地缓存方案:Guava;
    • 因为缓存刷新的时效要求低,不使用变更通知也是完全可行的。
  • 场景3的实现方案:场景3其实是场景2的延续,只是现在数据生效的时间从1天变成了5分钟,考虑技术方案的延续性,在云原生方案中,新增了一个定时任务+Redis,用于刷新缓存,交互逻辑如下图所示:
    缓存刷新

    说明:

    • 第1次请求到业务服务时,Guava缓存中也没有数据,则需要业务服务查询一次Database并把数据缓存至Guava,流程为蓝色①→②箭头所示;
    • 第2次请求到业务服务时,Guava缓存中已经有了数据,则只需要直接返回数据即可,流程为绿色①所示;
    • 当用运维工具更新数据时,同时也会清理掉Redis的缓存标记,一旦ScheduleJob获取的Redis Key(采用redis的setNX语法)不一致时,则会让Guava缓存失效,流程为红色的①→②→③→④箭头所示;
    • 后面再有请求过来时,会重新执行上面的第1步和第2步;

    总结:

    • 场景3仅在场景2的基础上迭代增加了虚线框框中的2个小功能点代码就可以了,代码延续性较好;
    • ScheduleJob设置为1分钟就可以满足5分钟内生效的业务诉求了;
    • 回过头来看看场景2:其实并没有做变更通知,只是采用了Guava自带的缓存过期机制而已;场景3其实也可以采用Guava自带的缓存过期机制,但是会导致微服务需要频繁的穿透缓存去查询数据库,得不偿失;场景3的实际做法则兼顾了准实时性和便利性:只有命令行工具变更了数据时,缓存才会刷新。另外场景3也没有做到变更通知,只是变相的达到了准实时变更通知的效果。
  • 场景4的实现方案:场景4是在复用场景3的代码框架的基础上,要求做到数据变更实时通知。此时项目中虽然已引入了SpringCloud的多个组件,但是业务参数配置都有单独的配置界面,无须使用配置中心。考虑到Redis其实也有变更通知的能力,此处正好可以在场景3的基础上继续迭代,去掉ScheduleJob,增加Redis订阅通知的代码即可。

4. 变更通知的最佳实践总结

  • 需要搞清楚什么是变更通知,什么是配置中心,不要因为有变更通知的需求就上配置中心,这样有可能把系统搞得非常复杂;
  • 好的设计都要顺势而为,首先需要了解系统的需求到底是什么,是不是一定要做变更通知,如本人列举的项目实践中就多次未使用变更通知组件,但是达到了变更通知的效果;
  • 如果只需要做变更通知,不需要独立的配置中心,建议优选Redis,因为它可以兼顾业务限流、高速缓存、不规则数据的处理(NoSQL)等,很有可能Redis就已经存在于项目中了;
  • 如果数据量非常庞大,还要支持复杂的规则,比如消息确认和重传等,则建议采用MQ(Kafka/RocketMQ/RabbitMQ/ActiveMQ);
  • 有些场景下的变更通知非常适合使用配置中心,如:SpringCloud-Gateway的路由规则yaml配置,就非常适合放在配置中心(如:Nacos等);当然如果使用的是k8s,则建议直接使用其ConfigMap;

5. 参考资料

  • [1]深入浅出阿里数据同步神器:Canal原理+配置+实战全网最全解析!
  • [2]图文解析 Nacos 配置中心的实现
  • [3]apollo 基本原理
  • [4]Spring Cloud Config 原理简介和实现

http://www.ppmy.cn/news/1043214.html

相关文章

excel功能区(ribbonx)编程笔记--1 初识功能区

再office2003版本以前,excel是具有菜单栏和工具栏的,再office2007及以后的版本中,界面中没有菜单栏和工具栏,使用功能区替换了菜单和工具栏。 您可能意识到自定义用户界面也变得更加困难,其实设置功能区并不会像您想像的那样困难,因为Microsoft也意识到必须有一种方式供开…

SpringBoot-lombok

为什么要使用lombok? Lombok是一个通过注解以达到减少代码的Java库,如通过注解的方式减少getter,setter方法,构造方法等。通过注解的形式自动生成构造器、getter/setter、equals、hashcode、toString等方法,并可以自动化生成日志变量,简化java开发、提高…

JDK动态代理与CGLIB动态代理

一、代理模式概述 1.1.生活中代理案例 房屋中介代理 客户手里面没有房源信息,找一个中介 商品代购 这些代购者可以去拿到比较低成本的商品,拥有自己的渠道 1.2.为什么要使用代理 对于消费者而言,可以减少成本,只需要关心自己…

Adobe Acrobat 无法使用 PS 编辑图片 的解决方法

问题描述 使用较新版本的Adobe Acrobat时,有时会遇到问题。 比如对pdf中的图片使用 PS 进行编辑,会弹出以下窗口,导致打不开 PS ,无法对图片进行编辑。 Adobe 无法启动您指定的图像编辑应用程序。请在"首选项"的"…

python进阶之图像编程 pillow扩展库

一、概述 1.1pillow简介 Python Imaging Library (PIL)是python 下的图像处理模块,支持多种格式,并提供强大的图像处理功能,可以通过pip进行安装后使用。 1.2pillow具体应用 Pillow 库是 Python3 最常用的图像处理库,它支持多种图像格式&a…

【java安全】Log4j反序列化漏洞

文章目录 【java安全】Log4j反序列化漏洞关于Apache Log4j漏洞成因CVE-2017-5645漏洞版本复现环境漏洞复现漏洞分析 CVE-2019-17571漏洞版本漏洞复现漏洞分析 参考 【java安全】Log4j反序列化漏洞 关于Apache Log4j Log4j是Apache的开源项目,可以实现对System.out…

模型评估的常用指标

模型评估的指标 模型是在大量的数据集上训练而来的,无论一个模型是从零训练的还是基于某一个模型,通过微调方法得到的,靠人工评价模型的效果都是异常困难的。那么要想客观的、自动化的评价一个LLM模型,就需要能够选择正确评估模型效果的指标或者基准测试,来客观和自动化的…

javap获取Kotlin方法JNI方法签名

获取Kotlin方法签名和JAVA不一样的地方就是需要使用Kotlin 命令行编译器生成.class文件: 编写一个Kotlin类,添加JNI方法: class TestLib {external fun init(callBack: CallBack)interface CallBack{fun onData(count:Int,data:String)} }在…