大厂生产解决方案:泳道隔离机制

在这里插入图片描述

更多大厂面试内容可见 -> http://11come.cn

大厂生产解决方案:泳道隔离机制

背景

在公司中,由于项目多、开发人员多,一般会有多套测试环境(可以理解为多个服务器),同一套服务会在多套测试环境中都部署方便不同开发人员对项目同时进行改动,服务中会涉及到很多消息发送、接收的地方,就拿 Kafka 消息来说,多套测试环境共用一套 kafka 集群,那么就 需要一套机制来隔离不同测试环境中的消息 ,避免不同测试环境之间的消息错乱消费

如下图,对于不同测试环境,生产服务 A 去发送同一个 topic1 下的消息,所有消费服务 B 都去订阅 topic1 的消息,到底哪个消费服务 B 来进行消费呢?

image-20240719205157706

我们希望的 目标 是希望 test1 环境下的消费服务 B 去消费当前环境 test1 中的消息,而不会消费到其他环境中的消息

最终希望的效果如下图,每个测试环境就是一个泳道,各个测试环境之间的消息可以被隔离开来,并且有一套基线环境作为兜底消费,在测试环境中不需要去部署一套完整的上下游服务

image-20240720160007728

初步方案

既然是因为多个测试环境中的消费服务 B 都去订阅了同一个 topic 下的消息,那么让不同环境下的消费服务 B 去订阅不同的 topic 消息不就可以了?

如下图:

  • 在测试环境 test1 下的服务 A 和 B,自己实现一个组件,判断当前处于什么样的测试环境,让他去订阅对应环境 topic:test1 这个消息
  • 在测试环境 test2 下的服务 A 和 B,让他去订阅 topic:test2 这个消息

image-20240719210044068

这样不就实现了不同测试环境之间的消息隔离了吗?

确实实现了,但是还存在一些问题

如果在测试时,只有生产服务 A 的代码发生了变动,但是想要测试的话,需要在测试环境 test1 中同时部署 A 和 B,如果不部署 B 服务的话,会导致没有对应的消费者,生产服务 A 的消息就没有人来消费了

这样会同时带来两个缺点:

  • 部署大量冗余服务,导致大量占用服务器内存
  • 给测试带来极大麻烦,有时候服务上下游依赖很多,我们也不知道具体有哪些,如何去一个一个进行部署?

优化方案

那么显然初版方案虽然实现了消息隔离,但还存在一些问题,如何去解决上边的问题呢?

即我们希望部分没有代码变动的服务不需要在各个测试环境中都去部署一套,那么可以准备一套 基线环境 ,基线环境包含了一套完整的服务,而其他测试环境可以只部署自己发生变动的服务即可,没有代码变动的服务我们希望可以使用基线环境上存在的服务

如下图,有 3 套环境,分别为 test1test2基线环境 default

  • test1、test2 是不同的测试环境
  • 基线环境上部署完整的上下游服务

image-20240719212219809

优化后的方案,需要遵循两个规则:

  • 同一个消息的所有测试环境使用相同的 topic
  • 不同测试环境的消费服务使用不同的消费者组

简单解释一下,第一个说的是同一种消息,比如“发起退款”的消息,所有测试环境都使用相同的 topic,这一点和初步方案不同

第二个是不同环境的消费服务的消费者组标识 groupId 不同,命名方式 {服务名:环境标识}

先说一下,优化版本中如何实现消息隔离

当测试环境 test1 中的生产服务 A 去发送消息,消息 topic 为【发起退款】,在消息中我们会带上泳道标识,消息会给每个消费者组都进行发送,消息隔离通过消费者来实现:

  • test1 环境的消费服务 B 收到消息之后,判断消息中的泳道标识和当前环境是否相同,显然相同,进行消费
  • test2 环境的消费服务 B 收到消息之后,发现消息的泳道标识为 test1,但是当前环境的泳道标识为 test2,不同,则不消费
  • 基线环境 的消费服务 B 收到消息之后,由于基线环境是作为兜底消费,所以会先判断当前消息对应环境是否存在消费者组,如果不存在的话,就作为兜底进行消费;如果当前消息存在对应的消费者组,则不消费,避免重复消费

这样既实现了消息隔离,又可以避免服务的冗余部署

image-20240719215723357

当测试环境 test1 中的消费服务 B 没有部署的话,那么他的生产服务 A 去发送消息,这个消息就会被 基线环境 的消费服务 B 进行兜底消费

这里基线环境的消费服务 B 去判断发现 test1 环境中对应的消费者组并没有上线,那么我基线环境就对这个消息进行消费,避免这个消息没有对应的服务进行消费

通过增加基线环境的概念,就可以避免初步方案中的问题,避免大量冗余服务的部署,对于那些没有代码变动的服务,我们就不部署,通过基线环境中的服务进行消费

收益提升

经生产验证,机器内存为 300G,使用率由 78% 降低至 54%,收益明显

最终方案

在优化方案中,实现了消息隔离,但是当消息流转到基线环境的服务之后,如何再转回原来的泳道环境?

image-20240720144612910

如下图,test1 环境的服务 A 发送消息,经过基线环境的服 B、服务 C 消费之后,也去发送 kafka 消息,此时 test1 环境部署了服务 D,需要让基线环境的消息可以再回到 test1 环境

实现思路:

在每个服务去处理消息时,拿到消息中的泳道标识,存储到线程上下文中,这样在当前线程的处理流程中都可以取到这个标识,之后在每个服务去发送消息的时候,从这个上下文中拿到泳道标识放入消息中即可

实现细节

通过接入组件的方式来实现泳道隔离,组件需要包含功能:

  • 拦截 kafka 消息发送:在消息中添加泳道标识
  • 拦截 kafka 消息接收:判断消息泳道标识与当前环境标识是否相同

基于 spring-kafka 实现泳道隔离组件

<dependency><groupId>org.springframework.kafka</groupId><artifactId>spring-kafka</artifactId>
</dependency>

泳道标识获取

当前测试环境的标识可以放在 SpringBoot 应用的 System.property 中,这样可以通过 System.getProperty("ROLE") 即可获取当前环境标识

消息接收拦截

kafka 提供了有发送和接收消息的拦截器

在接收消息时,取出消息中的泳道标识,和当前环境的泳道标识做对比,如果对比通过,则放行;如果不通过,则拦截不进行消费

基线环境的判断会更多,因为基线环境作为兜底消费,需要了解到收到的消息是否存在对应的消费者组进行消费,这点可以通过 AdminClient 来和 kafka 交互进行查询

消息发送拦截

在发送消息时,拦截到消息,将泳道标识放入消息中

kafka 提供了消息头(kafka header)来传递元数据,泳道的标识可以放入 kafka 消息头的位置

消费者组的设置

对于泳道隔离,我们要让上层应用无感知,也就是应用接入了泳道隔离组件,总不能让对应的开发人员自己去设置消费者组的 groupId 为 {服务名:泳道标识}

应用只需要设置 groupId 为服务名,在组件中我们对 groupId 进行处理,在后边拼接上泳道标识即可,这部分涉及到 ConcurrentKafkaListenerContainerFactory 的 Bean 构建以及拦截器的设置,需了解 kafka 源码

涉及 spring-kafka 相关类:

DefaultKafkaConsumerFactory # 
ConcurrentKafkaListenerContainerFactory # 并发消费容器创建工厂类
KafkaProperties # Kafka 属性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/48466.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何解决微服务下引起的 分布式事务问题

一、什么是分布式事务&#xff1f; 虽然叫分布式事务&#xff0c;但不是一定是分布式部署的服务之间才会产生分布式事务。不是在同一个服务或同一个数据库架构下&#xff0c;产生的事务&#xff0c;也就是分布式事务。 跨数据源的分布式事务 跨服务的分布式事务 二、解决方…

配置服务器

参考博客 1. https://blog.csdn.net/qq_31278903/article/details/83146031 2. https://blog.csdn.net/u014374826/article/details/134093409 3. https://blog.csdn.net/weixin_42728126/article/details/88887350 4. https://blog.csdn.net/Dreamhai/article/details/109…

javac详解 idea maven内部编译原理 自制编译器

起因 不知道大家在开发中&#xff0c;有没有过下面这些疑问。有的话&#xff0c;今天就一次解答清楚。 如何使用javac命令编译一个项目&#xff1f;java或者javac的一些参数到底有什么用&#xff1f;idea或者maven是如何编译java项目的&#xff1f;&#xff08;你可能猜测底层…

【一刷《剑指Offer》】面试题 47:不用加减乘除做加法

力扣对应题目链接&#xff1a;LCR 190. 加密运算 - 力扣&#xff08;LeetCode&#xff09; 牛客对应题目链接&#xff1a;不用加减乘除做加法_牛客题霸_牛客网 (nowcoder.com) 一、《剑指Offer》对应内容 二、分析题目 sumdataA⊕dataB 非进位和&#xff1a;异或运…

Unity UGUI 之 Graphic Raycaster

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 首先手册连接如下&#xff1a; Unity - Manual: Graphic Raycaster 笔记来源于&#xff…

无人车技术浪潮真的挡不住了~

正文 无人驾驶汽车其实也不算是新鲜玩意了&#xff0c;早在十年前大家都开始纷纷投入研发&#xff0c;在那时就已经蠢蠢欲动&#xff0c;像目前大部分智驾系统和辅助驾驶系统都是无人驾驶系统的一个中间过度版本&#xff0c;就像手机进入智能机时代的中间版本。 然而前段时间突…

SpringBoot 介绍和使用(详细)

使用SpringBoot之前,我们需要了解Maven,并配置国内源(为什么要配置这些,下面会详细介绍),下面我们将创建一个SpringBoot项目"输出Hello World"介绍. 1.环境准备 ⾃检Idea版本: 社区版: 2021.1 -2022.1.4 专业版: ⽆要求 如果个⼈电脑安装的idea不在这个范围, 需要…

LeetCode 热题 HOT 100 (001/100)【宇宙最简单版】

【链表】 No. 0160 相交链表 【简单】&#x1f449;力扣对应题目指路 希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【力扣详解】谢谢你的支持&#x…

搜维尔科技:【产品推荐】Euleria Health Riablo 运动功能训练与评估系统

Euleria Health Riablo 运动功能训练与评估系统 Riablo提供一种创新的康复解决方案&#xff0c;将康复和训练变得可激励、可衡量和可控制。Riablo通过激活本体感觉&#xff0c;并通过视听反馈促进神经肌肉的训练。 得益于其技术先进和易用性&#xff0c;Riablo是骨科、运动医…

jmeter部署

一、windows环境下部署 1、安装jdk并配置jdk的环境变量 (1) 安装jdk jdk下载完成后双击安装包&#xff1a;无限点击"下一步"直到完成&#xff0c;默认路径即可。 (2) jdk安装完成后配置jdk的环境变量 找到环境变量中的系统变量&#xff1a;此电脑 --> 右键属性 …

C语言:温度转换

1.题目&#xff1a;实现摄氏度&#xff08;Celsius&#xff09;和华氏度&#xff08;Fahrenheit&#xff09;之间的转换。 输入一个华氏温度&#xff0c;输出摄氏温度&#xff0c;结果保留两位小数。 2.思路&#xff1a;&#xff08;这是固定公式&#xff0c;其中 F 是华氏度&a…

【C语言】详解结构体(下)(位段)

文章目录 前言1. 位段的含义2. 位段的声明3. 位段的内存分配&#xff08;重点&#xff09;3.1 存储方向的问题3.2 剩余空间利用的问题 4. 位段的跨平台问题5. 位段的应用6. 总结 前言 相信大部分的读者在学校或者在自学时结构体的知识时&#xff0c;可能很少会听到甚至就根本没…

STM32实战篇:按键(外部输入信号)触发中断

功能要求 将两个按键分别与引脚PA0、PA1相连接&#xff0c;通过按键按下&#xff0c;能够触发中断响应程序&#xff08;不需明确功能&#xff09;。 代码流程如下&#xff1a; 实现代码 #include "stm32f10x.h" // Device headerint main() {//开…

JUC并发编程01-基础概念

概念 进程 进程可以视为程序的一个实例&#xff0c;进程就是用来加载指令、管理内存、管理I0 线程 一个进程内可以有多个线程&#xff0c;一个线程就是一个指令流。 在Java中&#xff0c;线程作为最小调度单位&#xff0c;进程作为资源分配的最小单位&#xff0c;可以说进程…

Mysql数据库第二次作业

(1)显示所有职工的基本信息。 mysql> select * from t_worker; (2)查询所有职工所属部门的部门号&#xff0c;不显示重复的部门号。 mysql> select distinct department_id from t_worker; (3)求出所有职工的人数。 mysql> select count(1) from t_worker; (4)列…

Figma 中文版指南:获取和安装汉化插件

Figma是一种主流的在线团队合作设计工具&#xff0c;也是一种基于 Web 端的设计工具。在当今的设计时代&#xff0c;Figma 的使用满足了每个人的设计需求&#xff0c;不仅可以实现在线编辑&#xff0c;还可以方便日常管理&#xff0c;有效提高工作效率。然而&#xff0c;相信很…

分页查询与分页条件查询

--------------- 无PageHelper插件分页查询 1.创建PageBean实体类 Data NoArgsConstructor AllArgsConstructor public class PageBean<T> {private Long total;//总条数private List<T> items;//当前页数据集合 }类型安全性 泛型&#xff1a;提供了编译时的类型…

【Apache Doris】周FAQ集锦:第 15 期

【Apache Doris】周FAQ集锦&#xff1a;第 15 期 SQL问题数据操作问题运维常见问题其它问题关于社区 欢迎查阅本周的 Apache Doris 社区 FAQ 栏目&#xff01; 在这个栏目中&#xff0c;每周将筛选社区反馈的热门问题和话题&#xff0c;重点回答并进行深入探讨。旨在为广大用户…

JMeter:BeanShell到JSR223迁移中的注意事项

前言 在之前的文章JMeter&#xff1a;BeanShell向JSR223迁移过程遭遇的java标准库不可用问题-如何切换JDK版本中引用了一段使用BeanShell对入参进行加密的脚本&#xff0c;迁移到JSR223&#xff0c;虽然更换JDK后编译通过&#xff0c;看似也可以执行了&#xff0c;但是其实那段…

windows USB 设备驱动开发-开发Type C接口的驱动程序(二)

编写 USB Type C 连接器驱动程序 在以下情况下&#xff0c;需要编写 USB Type-C 连接器驱动程序&#xff1a; 如果 USB Type-C 硬件能够处理电源输送 (PD) 状态机。 否则&#xff0c;请考虑编写 USB Type C 端口控制器驱动程序&#xff1b; 如果硬件没有嵌入式控制器。 否则&…