聊聊在集群环境中本地缓存如何进行同步
前言
之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说,因为他项目的redis版本不是6.0+版本,因此他使用我文章介绍通过MQ来实现本地缓存同步,他的同步流程大概如下图

他原来的业务流程是每天凌晨开启定时器去爬取第三方的数据,并持久化到redis,后边因为redis发生过宕机事故,他碰巧看了我文章,就觉得可以用使用多级缓存的策略,用来做个兜底。他的业务流程就如上图,即每天凌晨开启定时器去爬取第三方数据,持久化到redis和其中一台服务的本地缓存,然后将爬取到的业务数据发送到kafka,其他业务服务通过订阅kafka,将业务数据保存到本地缓存。
他改造完,某天突然发现在集群环境中,只要其中一台服务消费了kafka数据,其他就消费不到。今天就借这个话题,来聊聊集群环境中本地缓存如何进行同步
前置知识
kafka消费topic-partitions模式分为subscribe模式和assign模式。subscribe模式需要指定group.id,该模式会为consumer自动分配partition,且同一个group.id下的不同consumer不会消费同样的分区。assign模式需要为consumer手动、显示的指定需要消费的topic-partitions,不受group.id限制,相当与指定的group.id无效。通俗一点讲就是assign模式下,所有消费者都可以订阅指定分区
我们要通过消息队列实现本地缓存同步,本质上就是需要利用消息队列提供广播能力,而kafka默认不具备。不过我们可以根据kafka提供的消费模式进行定制,从而是kafka也具备广播能力
集群本地缓存同步方案
方案一:利用MQ广播能力
因为读者项目是使用kafka,且项目是使用spring-kafka,我们也就以此为例
1、subscribe模式
通过前置知识,我们了解到在subscribe模式下,同一个group.id下的不同consumer不会消费同样的分区,这就意味我们可以通过指定不同group.id来消费同样分区达到广播的效果
那如何在同个集群服务实现不同的group.id?
此时Spring EL 表达式就派上用场了,我们通过 Spring EL 表达式,在每个消费者分组的名字上配合 UUID 生成其后缀。这样,就能保证每个项目启动的消费者分组不同,从而达到广播消费的目的
示例
@KafkaListener(topics = "${userCache.topic}",groupId = "${userCache.topic}_group_" + "#{T(java.util.UUID).randomUUID()})")public void receive(Acknowledgment ack, String data){System.out.println(String.format("serverPort:【%s】,接收到数据:【%s】",serverPort,data));ack.acknowledge();}
如果我们决定UUID不直观,我们也可以使用IP作为标识,只要能保证同个集群服务的group.id是唯一即可
不过如果要改成ip,我们得做一定的改造。改造步骤如下
a、 获取ip地址信息,并放入environment
public class ServerAddrEnvironmentPostProcessor implements EnvironmentPostProcessor{private String SERVER_ADDRESS = "server.addr";@Override@SneakyThrowspublic void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {MutablePropertySources propertySources = environment.getPropertySources();Map<String, Object> source = new HashMap<>();String serverAddr = InetAddress.getLocalHost().getHostAddress();source.put(SERVER_ADDRESS,serverAddr);MapPropertySource mapPropertySource = new MapPropertySource("serverAddrProperties",source);propertySources.addFirst(mapPropertySource);}}
b、 配置spi
在src/main/resource目录下配置META-INF/spring.factories,配置内容如下
org.springframework.boot.env.EnvironmentPostProcessor=\
com.github.lybgeek.comsumer.ip.ServerAddrEnvironmentPostProcessor
c、 @KafkaListener配置如下内容
@KafkaListener(topics = "${userCache.topic}",groupId = "${userCache.topic}_group_" + "${server.addr}" + "_${server.port}")
小结
该方式的实现优点是比较简单,但如果需要对服务进行运维监控统计,那就不怎么友好了,虽然指定IP会比随机UUID好点,但如果是容器化部署,每次部署其IP也是会变化,这样跟随机指定UUID,差别也不大了。其次如果是使用云产品,比如阿里云对comsume group是有数量上限,且消费者组需要提前创建,这种情况使用该方案就不是很合适了
assign模式
通过assign模式手动消费对应的分区
示例
@KafkaListener(topicPartitions ={@TopicPartition(topic = "${userCache.topic}", partitions = "0")})public void receive(Acknowledgment ack, ConsumerRecord record){System.out.println(String.format("serverPort:【%s】,接收到数据:【%s】",serverPort,record));ack.acknowledge();}
小结
该方式实现也是很简单,如果我们不需要动态创建新的分区,用该方案实现广播,会是一个不错的选择。不过该方式的缺点很明显,因为是手动指定分区,当该分区有问题,也挺麻烦的
方案二:通过定时器触发
该方案主要基于读者目前的同步进行改造,改造后如下图

核心就是根据读者业务的特性,因为他是定时每天晚上同步爬取,那就意味着他这个数据至少在当天基本不变,就可以让集群里的服务都定时执行,此时仅需将xxl-job的调度策略改成分片广播就行,这样就可以持久化到redis的同时,也持久化到本地缓存
小结
该方案改动量比较小,有个小缺点就是,因为集群内所有服务都执行调度,这样就会使redis重复持久化,不过问题也不大就是好。最后读者选择该方案
总结
本文主要阐述集群环境中本地缓存如何进行同步,之前还有读者问我说,使用了多级缓存,数据一致性要如何保证?以前我可能会从技术角度来回答,比如你可以延迟双删,或者如果你是mysql,你可以使用canal+mq,更甚者你可以使用分布式锁来保证。但现在我更多从业务角度来思考这件事情,你都考虑使用缓存,是不是意味着你在业务上是可以容忍一定不一致性,既然可以容忍,是不是最终可以通过一些补偿方案来解决这个不一致性
没有完美的方案,你此时感觉的完美方案,可能是当时在那个业务场景下,做了一个贴合业务的权衡
demo链接
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-kafka-broadcast
相关文章:
聊聊在集群环境中本地缓存如何进行同步
前言 之前有发过一篇文章聊聊如何利用redis实现多级缓存同步。有个读者就给我留言说,因为他项目的redis版本不是6.0版本,因此他使用我文章介绍通过MQ来实现本地缓存同步,他的同步流程大概如下图 他原来的业务流程是每天凌晨开启定时器去爬取…...
【C++深入浅出】初识C++上篇(关键字,命名空间,输入输出,缺省参数,函数重载)
目录 一. 前言 二. 什么是C 三. C关键字初探 四. 命名空间 4.1 为什么要引入命名空间 4.2 命名空间的定义 4.3 命名空间使用 五. C的输入输出 六. 缺省参数 6.1 缺省参数的概念 6.2 缺省参数的分类 七. 函数重载 7.1 函数重载的概念 7.2 函数重载的条件 7.3 C支…...
租房合同范本
房屋租赁合同 甲方(出租方): 身份证: 联系电话: 乙方(承租方): 身份证: 联系电话: …...
轻薄的ESL电子标签有哪些特性?
在智慧物联逐渐走进千万家的当下,技术变革更加日新月异。ESL电子标签作为科技物联的重要组成部分,是推动千行百业数字化转型的重要技术,促进物联网产业的蓬勃发展。在智慧零售、智慧办公、智慧仓储等领域,ESL电子标签在未来是不可…...
AI 实力:利用 Docker 简化机器学习应用程序的部署和可扩展性
利用 Docker 的强大功能:简化部署解决方案、确保可扩展性并简化机器学习模型的 CI/CD 流程。 近年来,机器学习 (ML) 出现了爆炸性增长,导致对健壮、可扩展且高效的部署方法的需求不断增加。由于训练和服务环境之间的差异或扩展的困难等因素&a…...
商用汽车转向系统常见故障解析
摘要: 车辆转向系统是用于改变或保持汽车行驶方向的专门机构。其作用是使汽车在行驶过程中能按照驾驶员的操纵意图而适时地改变其行驶方向,并在受到路面传来的偶然冲击及车辆意外地偏离行驶方向时,能与行驶系统配合共同保持车辆继续稳定行驶…...
Python中的MetaPathFinder
MetaPathFinder 是 Python 导入系统中的一个关键组件,它与 sys.meta_path 列表紧密相关。sys.meta_path 是一个包含 MetaPathFinder 实例的列表,这些实例用于自定义模块的查找和加载逻辑。当使用 import 语句尝试导入一个模块时,Python 会遍历…...
工控机防病毒
2月3日,作为全球最大的半导体制造设备和服务供应商,美国应用材料公司(Applied Materials)表示,有一家上游供应商遭到勒索软件攻击,由此产生的关联影响预计将给下季度造成2.5亿美元(约合人民币17…...
LangChain手记 Question Answer 问答系统
整理并翻译自DeepLearning.AILangChain的官方课程:Question Answer(源代码可见) 本节介绍使用LangChian构建文档上的问答系统,可以实现给定一个PDF文档,询问关于文档上出现过的某个信息点,LLM可以给出关于该…...
如何优化css中的一些昂贵属性
如何优化css中的一些昂贵属性 就性能而言,某些 CSS 属性比其他属性的成本更高。如果使用不当,它们可能会减慢我们的网页速度并降低对用户的响应速度。在本文中,我们将探讨一些成本最高的 CSS 属性以及如何优化它们。 box-shadow box-shado…...
基于安防监控EasyCVR视频汇聚融合技术的运输管理系统的分析
一、项目背景 近年来,随着物流行业迅速发展,物流运输费用高、运输过程不透明、货损货差率高、供应链协同能力差等问题不断涌现,严重影响了物流作业效率,市场对于运输管理数字化需求愈发迫切。当前运输行业存在的难题如下…...
在WordPress站点中展示阅读量等流量分析数据(超详细实现)
这篇文章也可以在我的博客中查看 关于本文 专业的流量统计系统能够相对真实地反应网站的访问情况。 这些数据可以在后台很好地进行分析统计,但有时我们希望在网站前端展示一些数据 最常见的情景就是:展示页面的浏览量 这简单的操作当然也可以通过简单…...
学习 Iterator 迭代器
今天看到一个面试题, 让下面解构赋值成立。 let [a,b] {a:1,b:2} 如果我们直接在浏览器输出这行代码,会直接报错,说是 {a:1,b:2} 不能迭代。 看了es6文档后,具有迭代器的就一下几种类型,没有Object类型,…...
JVM---垃圾回收算法介绍
目录 分代收集理论 三种垃圾回收算法 标记-清除算法(最基础的、基本不用) 标记-复制算法 标记-整理算法 正式因为jvm有了垃圾回收机制,作为java开发者不会去特备关注内存,不像C和C。 优点:开发门槛低、安全 缺点…...
Ubuntu一直卡死的问题(20.04)
Ubuntu一直卡死的问题(18.04)_ubuntu频繁死机_Mr.Yi的博客-CSDN博客 我自己的解决方法: 1、首先强制关机重启后,直接打开命令行查看磁盘的使用: df -h发现/dev/loop都沾满了,我们能需要做的就是把他们清理干净 sud…...
自动化测试用例设计实例
在编写用例之间,笔者再次强调几点编写自动化测试用例的原则: 1、一个脚本是一个完整的场景,从用户登陆操作到用户退出系统关闭浏览器。 2、一个脚本脚本只验证一个功能点,不要试图用户登陆系统后把所有的功能都进行验证再退出系统…...
CSS3基础
CSS3在CSS2的基础上增加了很多功能,如圆角、多背景、透明度、阴影等,以帮助开发人员解决一些实际问题。 1、初次使用CSS 与HTML5一样,CSS3也是一种标识语言,可以使用任意文本编辑器编写代码。下面简单介绍CSS3的基本用法。 1.1…...
【栈】 735. 行星碰撞
735. 行星碰撞 解题思路 如果数组元素大于0 说明向右移动 那么不管 左边元素是不是大于0 都不会碰撞 如果数组元素小于0 说明想左边移动 那么判断左边元素 如果左边元素大于0 碰撞 那么遍历数组 当前元素大于0 直接入栈 如果当前元素小于0 判断栈顶元素是不是大于0 如果大…...
水库大坝安全监测MCU,提升大坝管理效率的利器!
水库大坝作为防洪度汛的重要设施,承担着防洪抗旱,节流发电的重要作用。大坝的安全直接关系到水库的安全和人民群众的生命财产安全。但因为水库大坝的隐患不易被察觉,发现时往往为时已晚。因此,必须加强对大坝的安全管理。其安全监…...
【vue2类型助手】vue2-cli 实现为 vue2 项目中的组件添加全局类型提示
实现 vue2 全局组件提示 vue2 项目全局注册组件直接使用没有提示 由于vue2中使用volar存在很大的性能问题,所以只能继续使用vetur,但是这样全局组件会没有提示,这对于开发来说,体验十分不友好,所以开发此cli并借助ve…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化
iOS 应用的发布流程一直是开发链路中最“苹果味”的环节:强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说,这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发(例如 Flutter、React Na…...
企业大模型服务合规指南:深度解析备案与登记制度
伴随AI技术的爆炸式发展,尤其是大模型(LLM)在各行各业的深度应用和整合,企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者,还是积极拥抱AI转型的传统企业,在面向公众…...
RKNN开发环境搭建2-RKNN Model Zoo 环境搭建
目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程. 本…...
