消息队列初见:一起聊聊引入系统mq 之后的问题
消息队列前文目录
消息队列初见:一起聊聊引入系统mq 之后的问题 https://www.cnblogs.com/yizhiamumu/p/16573472.html
分布式事务实战方案汇总 https://www.cnblogs.com/yizhiamumu/p/16625677.html
分布式事务原理及解决方案案例 https://www.cnblogs.com/yizhiamumu/p/16662412.html
消息队列为什么选用redis?聊聊如何做技术方案选型 https://www.cnblogs.com/yizhiamumu/p/16690033.html
消息队列的对比测试与RocketMQ使用扩展 https://www.cnblogs.com/yizhiamumu/p/16677881.html
消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化 https://www.cnblogs.com/yizhiamumu/p/16694126.html
一:为什么使用MQ
中小公司系统一开始体量较小,后来随着用户量逐渐扩大,需要把很多业务进行拆分,因此引入消息队列来优化问题。
其通用的使用场景可以简单地描述为:
当不需要立即获得结果,但是并发量又需要进行控制的时候,就是需要消息队列的时候。
二:消息队列应用的几大场景:
解耦
多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败。
具体场景:用户注册,系统发送邮件并验证短信。
一般情况下,串行方式,并行方式均可。

或使用消息队列,注册信息写入到消息队列。
或电商交易系统,交易服务发送消息,要生成订单,要扣减库存,要物流发货。

异步
多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间。
具体场景:人脸识别系统。用户上传图片,调用人脸识别,调用完成后再返回信息。该方法的缺点是:1 人脸识别失败导致图片上传失败;2 延迟高,需要系统处理完成后再返回客户端,即使用户不需要立即知道结果;3 图片上传系统和人脸识别系统互相调用,耦合度高。

客户端上传图片后,图片上传系统将图片信息如uid, 批次写入mq, 直接返回成功。人脸识别系统定时从mq 中取数据,完成对新增图片的识别。
此时,图片上传系统不需要关心人脸识别系统是否对这些图片信息的处理,以及何时对这些图片的处理。事实上,由于用户并不需要立即知道人脸识别的结果,人脸识别系统可以选择不同的调度策略,按忙时、闲时、正常时间对队列中的图片信息进行处理。
消峰
广泛应用于秒杀或抢购活动,避免流量过大导致系统挂掉。
具体场景:秒杀活动一般瞬时访问量过大,服务器接收过大,会导致流量暴增,相关系统无法处理请求甚至崩溃。而加入mq 后,系统可以从mq 中取数据,相当于mq 做了一次缓冲。
优点:
- 1 请求先入mq, 而不是由业务系统直接处理,做了一次缓冲,极大减少业务处理系统的压力。
- 2 队列长度可以做限制,事实上,秒杀时,后入队列的用户无法秒杀到商品,这些请求可以直接被抛弃,返回活动已结束或商品已售完。
消息驱动的系统
系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,多个消费者负责对消息进行处理。
具体场景:用户新上传一批照片,人脸识别系统需要对这个用户的所有照片进行聚类,聚类完成后由对帐系统重新生成用户的人脸索引加快查询。这三个子系统间由mq 连接起来,前一个阶段处理结果放入队列中,后一个阶段从队列中获取消息继续处理。

优点:
- 避免了直接调用下一个系统导致当前系统失效
- 每个子系统对于消息的处理方式可以更灵活,可以选择接收到消息 时就处理,可以选择定时处理,也可以划分时间段按不同处理速度处理。
三:MQ 存在问题及场景优化
3.1 重复消费问题
问题出现的场景:
- 消息生产者产生了重复的消息
- kafka 和 RocketMQ 的 offset 被回调了
- 消息消费者确认失败
- 消息消费者确认超时
- 业务系统主动发起重试
解决方案
不管是生产者产生的重复消息,还是消费者导致的重复消息,我们都可以在消费者中解决这个问题。这就需要我们做幂等设计。
增加一张消费消息表,使用messageId 做唯一索引。在处理逻辑前,先根据messageId 查询一下该消息有没有处理过,如果已经处理过则直接返回,如果没有处理过,则继续做业务处理
3.2 数据一致性问题(异步分布式事务问题)
问题出现的场景:
当服务是同步调用的时候,我们可以使用本地事务来控制数据一致性。但异步调用会存在数据不一致的问题。如商品订单下单成功,但是扣减库存失败,就是造成超卖问题。
解决方案
数据一致性分为:强一致性,弱一致性,最终一致性。
mq 为了性能使用的是最终一致性,那么必定会出现数据不一致的问题。这类问题大概率是因为消费者读取消息后,业务逻辑处理失败导致的。这个时候可以增加重试机制。
重试可以分为同步重试和异步重试。消息量比较小的业务场景,可以采用同步重试,如果消息处理失败立即重试3-5次,如果还失败则写入记录表中。如果消息量比较大,则采用异步方式。在处理失败后立即写入重试表,有个job 专门重定时重试。

还有一种做法,如果消费失败,自己给同一个topic 发一条消息,在后面的某个时间点自己又会消费到那一条消息,起到了重试的效果。如果对消息顺序要求不高的场景。这种做法适合对消费顺序要求不高的场景。
需要事务强一致的,不用消息异步,如下单、减库存要放在一个事务时在,加积分等业务放在mq 中异步处理。
3.3 消息丢失问题
问题出现的场景:
- 生产者产生消息时,由于网络原因发送到mq 失败
- mq 服务器持久化,存储磁盘时出现异常
- kafka 和 rocketMQ 的offset 被回调时,略过了很多消息
- 消费者刚读取消息,已经ack 确认,但是业务还没处理完,服务被重启了。
生产者、消费者、服务器都可能产生问题,最终的结果会导致消费者无法正确的处理消息而导致数据不一致的情况。
解决方案
增加一张消息发送表。
- 当生产者发完消息后,会往该表中写入一条数据,状态status 标记为待确认
- 如果消费者读取消息后,调用生产者api 更新该消息status 为已确认
- 有个job 每隔一段时间检查一次消息发送表,如果执行后还有状态是待确认的消息,则认为该消息丢失,重发该消息。

3.4 消息顺序问题
问题出现的场景:
如商品订单-支付-完成-退货等状态,如果订单数据作为 消息体,就会涉及顺序问题。如果先支付后下单,是不可以的。
这样的问题场景有很多,比如:
- kafka 同一个partition 中能保证顺序,但是不同的partition 无法保证顺序
- rabbitMQ 的同一个queue 能够保证顺序,但是如果多个消费者同一个queue 也会有顺序问题
- 如果消费者使用多线程消费消息,无法保证顺序
- 如果生产者发送到mq 中的路由规则跟消费者不一样,也无法保证顺序
解决方案
首先我们需要确认,消费者是否真的需要知道中间状态,只知道最终状态行不行。

其实很多时候我们真的需要知道的最终状态,这样我们可以把流程优化一下:

这种流程可以解决大部分的消息顺序问题。但是如果真的有需要保证顺序的需求,可以将订单号路由到不同的partition,同一个订单号的消息,每次发送到同一个partition
3.5 消息堆积问题
问题出现的场景:
很多时候,由于某些批处理或其他原因,导致消费速度小于生产速度,这样会直接导致消息堆积问题,从而影响业务功能。
比如开通会员,如果消息出现堆积会导致用户下单后,很久才变会员。这种情况肯定会引起大量用户投诉。
解决方案
看消息是否需要保证顺序。如果不需要保证顺序,可以读取消息之后用多线程处理业务逻辑。
这样就能增加业务逻辑处理速度,解决消息堆积问题。但是线程池的核心线程数和最大线程数需要合理配置,不然可能会浪费系统资源。

如果需要保证顺序,可以读取消息后将消息按照一定的规则分发到多个队列中,然后在队列中用单线程处理。

rabbitmq ,activemq , rocketmq, kafka 对比

引入mq 之后的问题
本来我们需要保证系统服务可用就可以了,现在引入新的服务,我们要保证mq 的可用,系统的可用性和复杂性都会有新的要求。
文::一只阿木木
消息队列初见:一起聊聊引入系统mq 之后的问题的更多相关文章
- 为什么要用消息队列 及 自己如何设计一个mq架构
1. 解耦:如左图, 系统a因为业务需求需要调用系统b,后续因为业务需求可能需要改代码调用系统c,甚至还要考虑被调用的系统挂了访问超时的问题.耦合性太高! 如右图, 系统a产生一条数据发送到消息队列里 ...
- 消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化
前文目录链接参考: 消息队列的一些场景及源码分析,RocketMQ使用相关问题及性能优化 https://www.cnblogs.com/yizhiamumu/p/16694126.html 消息队列 ...
- 一个用消息队列 的人,不知道为啥用 MQ,这就有点尴尬
消息队列 为什么写这篇文章? 博主有两位朋友分别是小A和小B: 小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑.再不然就是和运营聊聊天,写几个SQL, ...
- 转载:消息队列MQ
本文大概围绕如下几点进行阐述: 为什么使用消息队列? 使用消息队列有什么缺点? 消息队列如何选型? 如何保证消息队列是高可用的? 如何保证消息不被重复消费? 如何保证消费的可靠性传输? 如何保证消息的 ...
- IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列
1.引言 消息是互联网信息的一种表现形式,是人利用计算机进行信息传递的有效载体,比如即时通讯网坛友最熟悉的即时通讯消息就是其具体的表现形式之一. 消息从发送者到接收者的典型传递方式有两种: 1)一种我 ...
- 天天都用消息队列,却不知道为啥要用MQ,这就有点尴尬了
1.为什么要使用消息队列? 分析:一个用消息队列的人,不知道为啥用,有点尴尬.没有复习这点,很容易被问蒙,然后就开始胡扯了. 回答:这个问题,咱只答三个最主要的应用场景(不可否认还有其他的,但是只答三 ...
- rabbit MQ 消息队列
为什么会需要消息队列(MQ)? 一.消息队列概述消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有 ...
- 深入消息队列MQ,看这篇就够了!
大厂面试爱问消息队列 MQ. 因为消息队列MQ,既是大型分布式系统不可缺少的中间件,也是高并发系统的基石中间件. 如果你想要快速掌握消息队列 MQ 最内核的知识,以及消息队列MQ的主流应用场景.主流产 ...
- 消费端如何保证消息队列MQ的有序消费
消息无序产生的原因 消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer) ...
- (转)Linux环境进程间通信----系统 V 消息队列列
转:http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 消息队列(也叫做报文队列)能够克服早期unix通信机制的一些缺点.作为早期unix通 ...
随机推荐
- Swift开发基础03-函数
定义 形参默认是let,也只能是let func sum(v1: Int, v2: Int) -> Int { return v1 + v2 } sum(v1: 10, v2: 20) // 无 ...
- mysql 删除数据表报错 表删除时 Cannot delete or update a parent row: a foreign key constraint fails 异常处理
mysql 删除数据表报错 表删除时 Cannot delete or update a parent row: a foreign key constraint fails 异常处理 MySQL报错 ...
- [oeasy]python0116_文字的起源_苏美尔文明_楔形文字_两河流域
文字起源 回忆上次内容 上次回顾了西里尔字符的编码过程 KOI-7 KOI-8 ISO-8859 系列进行总结 字符扩展 ascii 共 16 种 由iso组织制定 从 iso-8859-1 到 ...
- iOS开发基础133-崩溃预防
现代移动应用的用户体验依赖于其稳定性和可靠性.然而,在开发过程中,我们时常会遇到各种崩溃问题.崩溃不仅会影响用户的使用体验,还可能损害应用的声誉.因此,本文将详细介绍一个名为CrashPreventi ...
- Python 结合opencv实现图片截取和拼接
实践环境 python 3.6.2 scikit-build-0.16.7 win10 opencv_python-4.5.4.60-cp36-cp36m-win_amd64.whl 下载地址: ht ...
- python selenium 判断元素是否存在,实现:找到元素,执行对应的代码;找不到元素,继续执行其他代码
selenium因为找不到元素会抛出异常,导致执行结束 可以考虑使用driver.find_elements(),找不到元素时就会返回空列表,使用if-else语句,判断列表是否为空,非空,则正常找到 ...
- java+mysql+tomcat环境变量配置(windows版)
jdk8(本人用的jdk8) 系统变量->新建:{JAVA_HOME=[JDK安装目录]} 系统变量->PATH:头部追加%JAVA_HOME%\bin;%JAVA_HOME%\jre\b ...
- hive导入mysql
hive测试--HIVE数据分析02 题目: 4.处理结果入库:(在虚拟机安装mysql) 将上述统计分析的结果数据保存到mySQL数据库中. #text3_1入库 #1.添加驱动,在hive的 ...
- Android studio报错:Failed to allocate a 3213123 byte allocation with 31231 free bytes and 189MB ontil 0OM
这个问题是运行内存超了 在AndroidManifest中加入 android:hardwareAccelerated="false"android:largeHeap= &quo ...
- 【Linux】Re01
一.三种网络模式 https://www.bilibili.com/video/BV1Sv411r7vd?p=7 1.桥接模式 该虚拟机和宿主机同一网段,和外部网络相通,但是占用网段地址资源,IP分配 ...