可重入锁思想,设计MQ迁移方案

如果你的MQ消息要从Kafka切换到RocketMQ且不停机,怎么做?在让这个MQ消息调用第三方发奖接口,但无幂等字段又怎么处理?今天小傅哥就给大家分享一个关于MQ消息在这样的场景中的处理手段。
这是一种比较特例的场景,需要保证切换的MQ消息不被两端同时消费,并且还需要在一段消费失败后的MQ还可以继续重试。并且这一端消费的MQ消息,也要保证自身的幂等。
我们知道一般通用场景下,MQ消息都会有一个业务唯一ID值,用于接收方做仿重处理。但除此之外还应该有一个MQ消息本身的ID,这个ID也要全局唯一,每一条消息都要有一个ID,这是因为MQ是可能重复发送的(发送MQ成功,但获取MQ发送结果响应超时或更新库表消息状态失败,则重复发送),如果没有消息的唯一ID也就没法确保是哪一条消息了。
这个ID可以用于;唯一标识、去重、链路追踪、幂等性、事务以及安装性等,但可能有些伙伴在做MQ消息发送的时候,是容易忽略而没有在MQ中添加这个ID,或者随意用时间戳来当ID用,这样都是不合理的。会影响一些场景的代码健壮性设计。
需求背景描述好了,接下来,我们看看这样的场景怎么设计。
1. 场景问题
将原本使用 Kafka 的MQ方式,迁移到 RocketMQ,同时部分场景的 MQ 消息调用三方接口是没有幂等字段的,需要做好程序兼容处理。
2. 场景思考
首先我们要知道在分布式架构下,我们每做的技术方案都要考虑顺序性和临界状态。像是MQ的生产和消费都是多套应用实例部署的,那么生产端发送出来的MQ消息到不同的队列中也是有延迟和存放顺序以及拉取消费不同的情况。如;生产端发送MQ为A、B、C、D,但到Kafka/RocketMQ以及不同的消费端拉取时,不一定是A、B、C、D的顺序,那么直接做切量开关,是可能导致一个A消息在Kafka队列中消费完,点击切换开关(一种切量哈希计算手段,如消息{A}哈希值最后两位当做百分比用),正好RocketMQ也会把A消费掉。这样同一个消息就被重复消费了。
3. 方案设计
在整个方案设计中,我们要考虑几个非常重要的点。如图:

一个是切换的两端MQ消费是抢占式加锁,避免重复消费。这是因为切量开关,切换过程中,两个消息队列中的MQ并不是顺序可靠的,可能存在重复消费,所以要加分布式锁。
一段MQ消费失败要进行重试,但这个时候不能在消费失败后删分布式锁,因为MQ消费都是很快的,可能导致删锁后另外一端MQ进行了相同的消费。那可能有些伙伴会说,那也没关系呀,反正失败的这段没有消费成功。当往往失败并不一定是直接的结果失败,可能是网络失败,可能是超时失败等。也就是实际成功了,但超时反馈了。所以不能被其他端重复消费,并且要保证自己这一端消费失败后可重试。所以这块要设计可重入锁,也就是 setnx 加锁的值,为自身一段的 mq 类型,这样自己在接收mq消息以后,检查锁为自身加锁值可重试。这样也就保证了一端消费重试,不会让另外一端把MQ也跟着消费掉,因为setnx存在,并且有加锁值判断,所以不能进入。
另外MQ消息还可能存在同一个MQ发送多次的场景,这个是非常正常的。比如,你再发送MQ的时候,超时网络抖动失败(1万次会有1次),那么就会补偿重发。但这个MQ已经发送过了,所以会接收2条MQ消息。那么在消费的时候,不能让2个MQ消息都进入消费中,因为多台实例消费,可能都去调用发奖了。那么这里还需要给MQ的ID进行幂等加锁。确保一个MQ消息,失败后,顺序轮训重试。也就保证了,发奖的过程中不会出现超发奖品。大部分三方接口还是有幂等字段的,有的话会更好。
另外还有2个开关,一个是
消费开关,一个是切量开关。消费开关要在整个新的MQ改造工程工程全部上线后开启,但还要被切量开关限定消费。开启后,切量开关才会生效。切量是一种哈希值的百分比比对,比如一个哈希值最后两位是10,那么切量配置小于等于10%则这个MQ则可以被切量后消费,另外一段则不消费这个MQ。另外,为了方便测试线上功能,还会加入白名单。不过大部分时候这类东西会用通用组件能力解决。
这样的场景方案设计,是非常值得积累的,同类的思想也可以帮我们解决很多共性问题。
可重入锁思想,设计MQ迁移方案的更多相关文章
- Java不可重入锁和可重入锁的简单理解
基础知识 Java多线程的wait()方法和notify()方法 这两个方法是成对出现和使用的,要执行这两个方法,有一个前提就是,当前线程必须获其对象的monitor(俗称“锁”),否则会抛出Ille ...
- JAVA锁机制-可重入锁,可中断锁,公平锁,读写锁,自旋锁,
如果需要查看具体的synchronized和lock的实现原理,请参考:解决多线程安全问题-无非两个方法synchronized和lock 具体原理(百度) 在并发编程中,经常遇到多个线程访问同一个 ...
- Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等
Java 中15种锁的介绍 Java 中15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁,乐观锁,分段锁,自旋锁等等,在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类 ...
- Java 种15种锁的介绍:公平锁,可重入锁,独享锁,互斥锁等等…
Java 中15种锁的介绍 1,在读很多并发文章中,会提及各种各样的锁,如公平锁,乐观锁,下面是对各种锁的总结归纳: 公平锁/非公平锁 可重入锁/不可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲 ...
- AQS学习(二) AQS互斥模式与ReenterLock可重入锁原理解析
1. MyAQS介绍 在这个系列博客中,我们会参考着jdk的AbstractQueuedLongSynchronizer,从零开始自己动手实现一个AQS(MyAQS).通过模仿,自己造轮子来学习 ...
- Java中可重入锁ReentrantLock原理剖析
本文由码农网 – 吴极心原创,转载请看清文末的转载要求,欢迎参与我们的付费投稿计划! 一. 概述 本文首先介绍Lock接口.ReentrantLock的类层次结构以及锁功能模板类AbstractQue ...
- ReenTrantLock可重入锁(和synchronized的区别)总结
ReenTrantLock可重入锁(和synchronized的区别)总结 可重入性: 从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也 ...
- synchronized 是可重入锁吗?为什么?
什么是可重入锁? 关于什么是可重入锁,我们先来看一段维基百科的定义. 若一个程序或子程序可以“在任意时刻被中断然后操作系统调度执行另外一段代码,这段代码又调用了该子程序不会出错”,则称其为可重入(re ...
- 可重入锁 & 不可重入锁
可重入锁指同一个线程可以再次获得之前已经获得的锁,避免产生死锁. Java中的可重入锁:synchronized 和 java.util.concurrent.locks.ReentrantLock. ...
- Java并发编程原理与实战十七:AQS实现重入锁
一.什么是重入锁 可重入锁就是当前持有锁的线程能够多次获取该锁,无需等待 二.什么是AQS AQS是JDK1.5提供的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架,这个基础框架的重要性可 ...
随机推荐
- SpringBoot 利用Timer 在指定时间2小时后执行任务
/** * @Description * @Author songwp * @Date 2022/8/5 12:51 * @Version 1.0.0 **/ @Component public cl ...
- Ubuntu更新源文件报错:E: 仓库 “http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release” 没有 Release 文件。
E: 仓库 "http://ppa.launchpad.net/chris-lea/node.js/ubuntu bionic Release" 没有 Release 文件. 一条 ...
- python交教程4:文件操作
文件操作流程 人类操作一个word流程: 1.找到文件.双击打开 2. 读或修改 3. 保存&关闭 ⽤python操作⽂件也差不多: 只读模式 创建模式 追加模式 遍历文件 图片视频- ...
- C数据结构:哈夫曼树算法实现与应用
学习哈夫曼树(编码) 带权二叉树 认识WPL 最优二叉树 构造哈夫曼树的过程 哈夫曼树的应用 建立哈夫曼树 代码如下: 结构体代码部分 建立操作代码 找到最小结点(※难点) 附上建立哈夫曼树源代码 带 ...
- EDP .Net开发框架--自动化日志
平台下载地址:https://gitee.com/alwaysinsist/edp 自动化日志不需要额外调用日志相关功能即可无感实现程序集方法调用的日志记录. 创建业务逻辑处理类 public cla ...
- Istio(十):istio多集群部署模式
目录 一.模块概览 二.多集群部署 2.1 多集群部署 2.2 网络部署模式 2.3 控制平面部署模型 2.4 网格部署模型 2.5 租户模式 2.6 最佳多集群部署 一.模块概览 在本模块中,我们将 ...
- pod(二):创建包含多个容器的pod(sidecar)
目录 一.系统环境 二.前言 三.创建包含多个容器的pod 3.1 环境介绍 3.2 在一个pod里创建多个容器 一.系统环境 服务器版本 docker软件版本 CPU架构 CentOS Linux ...
- 【C#】初次使用webapi的体会
webapi controller 要继承 ApiController 重写权限验证 ,记得使用 System.Web.Http下的AuthorizeAttribute. webapi默认路由 ...
- Pandas学习之路【1】
安装pandas: pip install pandas pandas读取数据: 数据类型 读取方式 csv, tsv, txt pd.read_csv Excel pd.read_excel mys ...
- golang 接口动态派发性能问题
测试代码 package main type Duck interface { Quack() } type Cat struct { Name string } //go:noinline func ...