Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聊聊在集群环境中本地缓存如何进行同步

聊聊在集群环境中本地缓存如何进行同步

原创
作者头像
lyb-geek
发布于 2023-08-15 02:33:48
发布于 2023-08-15 02:33:48
5720
举报
文章被收录于专栏:Linyb极客之路Linyb极客之路

前言

之前有发过一篇文章聊聊如何利用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 生成其后缀。这样,就能保证每个项目启动的消费者分组不同,从而达到广播消费的目的

示例

代码语言:java
AI代码解释
复制
  @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

代码语言:java
AI代码解释
复制
public class ServerAddrEnvironmentPostProcessor implements EnvironmentPostProcessor{



    private String SERVER_ADDRESS = "server.addr";

    @Override
    @SneakyThrows
    public 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,配置内容如下

代码语言:java
AI代码解释
复制
org.springframework.boot.env.EnvironmentPostProcessor=\
com.github.lybgeek.comsumer.ip.ServerAddrEnvironmentPostProcessor

c、 @KafkaListener配置如下内容

代码语言:java
AI代码解释
复制
 @KafkaListener(topics = "${userCache.topic}",groupId =  "${userCache.topic}_group_" + "${server.addr}" + "_${server.port}")
小结

该方式的实现优点是比较简单,但如果需要对服务进行运维监控统计,那就不怎么友好了,虽然指定IP会比随机UUID好点,但如果是容器化部署,每次部署其IP也是会变化,这样跟随机指定UUID,差别也不大了。其次如果是使用云产品,比如阿里云对comsume group是有数量上限,且消费者组需要提前创建,这种情况使用该方案就不是很合适了

assign模式

通过assign模式手动消费对应的分区

示例

代码语言:java
AI代码解释
复制
   @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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Mq消息队列核心问题剖析与解决
这篇文章就谈谈对mq各种问题的思考,以及不同的mq业务方案的解决,注意这篇文章为了解决在学习三大mq的一些问题,和不同mq差异导致的出现的不同的消息解决方案,这往往是很多人所忽视的,我教你!
Joseph_青椒
2023/10/06
1.3K0
Mq消息队列核心问题剖析与解决
Kafka详解
  Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、Storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源项目。
忧愁的chafry
2022/10/30
1.4K0
Kafka详解
kafka 基础知识梳理及集群环境部署记录
一、kafka基础介绍 Kafka是最初由Linkedin公司开发,是一个分布式、支持分区的(partition)、多副本的(replica),基于zookeeper协调的分布式消息系统,它的最大的特性就是可以实时的处理大量数据以满足各种需求场景:比如基于hadoop的批处理系统、低延迟的实时系统、storm/Spark流式处理引擎,web/nginx日志、访问日志,消息服务等等,用scala语言编写,Linkedin于2010年贡献给了Apache基金会并成为顶级开源 项目。 kafka是一种高吞吐量的分
洗尽了浮华
2018/06/14
8100
一文搞懂 Kafka consumer 与 broker 交互机制与原理
AutoMQ 致力于构建下一代云原生 Kafka 系统,解决过去 Kafka 的诸多痛点问题,引领 Kafka 走向云原生时代。作为国内 Kafka 生态的忠实拥护者,我们将持续为 Kafka 技术爱好者带来优质的 Kafka 技术内容分享,欢迎关注我们。今天给大家带来的是 Kafka Consumer 与 Kafka Broker 之间的交互机制解析,并简要介绍其背后的主要工作机制,参考的 Kafka 源码版本为 3.4。
用户10807116
2024/05/27
1.3K0
Kafka入门与实战
今天我们来聊一聊现在MQ中最火爆的Kafka吧。关于Kafka的内容还是比较多的。本篇大概15000左右字,大家根据自己的需求来看吧。本文的大纲如下图所示: 一、消息队列的作用是什么?
爪哇缪斯
2023/05/10
7740
Kafka入门与实战
一文读懂springboot整合kafka
Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。
QGS
2024/05/03
13.3K0
springboot中使用kafka
kafka 的事务是从0.11 版本开始支持的,kafka 的事务是基于 Exactly Once 语义的,它能保证生产或消费消息在跨分区和会话的情况下要么全部成功要么全部失败
六个核弹
2021/07/26
3.2K0
第五章 RocketMQ工作原理
Producer可以将消息写入到某Broker中的某Queue中,其经历了如下过程:
RookieCyliner
2025/06/04
1290
消息队列的消费幂等性如何保证
当出现消费者对某条消息重复消费的情况时,重复消费的结果与消费一次的结果是相同的,并且多次消费并未对业务系统产生任何负面影响
lyb-geek
2020/08/09
2.7K0
消息队列的消费幂等性如何保证
分布式消息队列
作者:vincentchma,腾讯 IEG 后台开发工程师 一、消息队列的演进 分布式消息队列中间件是是大型分布式系统中常见的中间件。消息队列主要解决应用耦合、异步消息、流量削锋等问题,具有高性能、高可用、可伸缩和最终一致性等特点。消息队列已经逐渐成为企业应用系统内部通信的核心手段,使用较多的消息队列有 RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、Pulsar 等,此外,利用数据库(如 Redis、MySQL 等)也可实现消息队列的部分基本功能。 1.基于 OS 的
腾讯技术工程官方号
2021/09/28
2.2K0
一篇文章把RabbitMQ、RocketMQ、Kafka三元归一
点击上方“芋道源码”,选择“设为星标” 管她前浪,还是后浪? 能浪的浪,才是好浪! 每天 10:33 更新文章,每天掉亿点点头发... 源码精品专栏 原创 | Java 2021 超神之路,很肝~ 中文详细注释的开源项目 RPC 框架 Dubbo 源码解析 网络应用框架 Netty 源码解析 消息中间件 RocketMQ 源码解析 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析 作业调度中间件 Elastic-Job 源码解析 分布式事务中间件 TCC-Transaction
芋道源码
2022/07/12
7810
一篇文章把RabbitMQ、RocketMQ、Kafka三元归一
全网把Kafka概念讲的最透彻的文章,别无二家
消息队列老大哥Kafka在官网的介绍是这么说的,真是霸气:全球财富前100强公司有超过80%信任并使用Kafka。Kafka目前在GitHub目前也已经有star数27.6k、fork数13.6k。
JavaSouth南哥
2024/07/03
4320
全网把Kafka概念讲的最透彻的文章,别无二家
『互联网架构』kafka集群原理(117)
PS:原理至关重要,面试的时候不可能问你命令的,都是问原理,懂了原理线上如果使用kafka出了问题才可能快速定位,而不是一脸蒙圈。必须要明白原理,如果不说原理直接实战,就真成搬砖了。
IT架构圈
2019/07/30
7610
『互联网架构』kafka集群原理(117)
【Kafka】Kafka 基础知识总结
Kafka其实是一款基于发布与订阅模式的消息系统,如果按常理来设计,大家是不是把消息发送者的消息直接发送给消息消费者?但Kafka并不是这么设计的,Kafka消息的生产者会对消息进行分类,再发送给中间的消息服务系统,而消息消费者通过订阅某分类的消息去接受特定类型的消息。
JavaSouth南哥
2024/12/04
2060
【Kafka】Kafka 基础知识总结
快速学习-Kafka架构深入
Kafka 中消息是以 topic 进行分类的,生产者生产消息,消费者消费消息,都是面向 topic的。
cwl_java
2020/02/24
4120
kafka学习之消息的消费原理与存储(二)
在 kafka 中,topic 是一个存储消息的逻辑概念,可以认为是一个消息集合。每条消息发送到 kafka 集群的消息都有一个类别。物理上来说,不同的 topic 的消息是分开存储的,每个 topic 可以有多个生产者向它发送消息,也可以有多个消费者去消费其中的消息。
周杰伦本人
2022/10/25
5650
kafka学习之消息的消费原理与存储(二)
Kafka
重新分配分区后,新增分区要等几分钟后才可被触发使用 多分区场景下,kafka服务端lag 有负数情况,目前官方修复为最多-1,此bug并不影响数据的准确性,客户端消费依然是正常
leon公众号精选
2022/04/27
5730
Kafka
第一天:Kafka理论学习
Kafka是一个分布式的基于发布/订阅模式的消息队列(Message Queue),主要应用于大数据实时处理领域。在流式计算中,Kafka一般用来缓存数据,Storm通过消费Kafka的数据进行计算。
sowhat1412
2020/11/05
5280
第一天:Kafka理论学习
Kafka全面认知
最早设计的目的是作为LinkedIn的活动流和运营数据的处理管道。这些数据主要是用来对用户做用户画像分析以及服务器性能数据的一些监控。
花落花相惜
2021/11/22
5150
Apache Kafka-通过设置Consumer Group实现广播模式
传统的消息传递模式有2种:队列( queue) 和(publish-subscribe)
小小工匠
2021/08/17
9.3K1
相关推荐
Mq消息队列核心问题剖析与解决
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档