消息队列专题(基础篇):消息队列入门
Posted 盛夏温暖流年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消息队列专题(基础篇):消息队列入门相关的知识,希望对你有一定的参考价值。
一. 什么是消息队列
消息队列 就是我们常说的 MQ,英文为 Message Queue,是一个需要独立部署的中间件产品。
首先从字面来看,它包含了两个关键词:消息和队列。
消息:是指需要传输的数据,可以是普通的字符串类型,也可以是其他的复杂数据类型;
队列:用于存放消息的中转站,是一种先进先出的数据结构,元素从队尾入队,从队首出队,如下图所示:
二. 消息队列的两种模式
消息队列主要分为两种模式:点对点模式和发布订阅模式。
点对点模式
这种模式类似于单播,生产者发布消息放入队列,如果有多个消费者,则只有一个消费者能够接收到对应消息,读完即被删除,此时的不同消费者之间是竞争关系。
发布-订阅模式
发布-订阅模式类似于广播,消息生产者将消息发布到主题中,订阅了对应主题的多个消费者都可以消费该消息。
和点对点模式不同,发布到主题中的消息会被所有消费者消费,消费者之间不再是竞争关系,也就是说,每个消费者都可以收到同一个主题的全量消息。
三. 为什么要使用消息队列
消息队列中间件是分布式系统中重要的组件,主要解决异步消息、应用耦合、流量削锋等问题,是大型分布式系统不可缺少的中间件。
异步处理减少响应时间
使用消息队列可以对业务进行异步处理达到减少响应时间的目的。
举个例子,比如下单流程主要分为以下几步:用户下单支付 -> 扣减优惠券 -> 短信提醒 -> 积分累加,采用同步方式的话,就需要等待所有操作全部完成后才返回结果,如下图所示。
这样的方式简单但是响应时间长,用户需要等待较长时间才能得到结果,体验不好。
那如果我们使用消息队列,就可以把 "扣减优惠券" , "短信提醒" ,“积分累加” 这种非关键步骤放在消息队列中异步完成,如下图所示。
用户下单支付后,在消息队列中完成其余步骤,能够有效提高响应速度,优化用户体验。
降低系统耦合性
以消息-发布订阅模式为例,消息生产者发布消息,一个或多个消费者订阅消息,双方之间没有直接的关系,达到了解耦的目的。
而如果新增了其他业务,也无需改动原有的生产者业务逻辑,降低耦合性的同时也提高了可扩展性。
流量削锋
流量削峰的典型应用场景就是秒杀业务的处理。
由于队列能转储消息,对于超出系统承载能力的场景,可以用消息队列作为临时中转站存储暂时无法处理的用户请求,从而达到对系统的限流保护作用,这就是所谓的流量削峰。
四. 使用消息队列有什么问题
技术是把双刃剑,所以我们在使用前也需要了解其弊端,从而确定它是否适用于当前业务场景,以及我们是否能够接受解决对应弊端所花费的时间成本。
使用消息队列会出现的问题主要包括:系统可用性降低,重复消费,数据不一致,消息丢失,消息顺序性问题,消息堆积。
系统可用性降低
使用消息队列虽然降低了系统间的耦合,但是,我们对消息队列的依赖也变重了,一旦消息队列发生故障,系统就不能正常使用了,会导致系统的可用性降低。
同时,引入消息队列还有一定的学习成本,需要额外部署消息队列服务器,而且有些消息队列的用法较为复杂,使用不好会出现很多问题,从而导致系统的复杂度提升,可用性降低。
重复消费
重复消费的原因有很多,比如网络故障导致消息队列无法收到消费成功的返回消息,会发送重复消息,此时消费者就需要对重复消息进行处理。
就好比累加积分的消息发送了两遍,消费者在收到第二个消息时就不应该进行二次累加,否则积分数据结果就不对了。
防止消息的重复消费的关键就在于保证消息的幂等性,也就是要保证消费者对相同资源的一次请求或者多次请求得到的结果是一致的。
数据不一致
我们都知道数据一致性主要分为三种:
- 强一致性
- 弱一致性
- 最终一致性
而消息队列为了性能考虑使用的是最终一致性,那么必定会出现数据不一致的问题。
这类问题大概率是因为消费者读取消息后,业务逻辑处理失败导致的,这时候可以增加重试机制。
重试机制又分为同步重试和异步重试。
同步重试适用于消息量较少的场景,而异步重试适用于消息量较多的场景,那如果异步重试失败了该怎么办呢?
我们可以考虑添加一个重试表,将重试失败的消息存储在里面,开启定时任务去轮询该表,处理失败消息。
如果对消息顺序要求不高的场景,也可以采用重发消息的方式,生产者重新将重试失败的消息放到消息队列中,让消费者再次获取进行消费。
消息丢失
消息从生产到消费经历了三个阶段:生产阶段、存储阶段和消费阶段。
- 生产阶段:生产者生产消息,经过网络传输发送到消息队列中。
- 存储阶段: 消息在消息队列中存储。
- 消费阶段:消息队列中的消息,经过网络传输发送到消费者端进行消费。
在这三个阶段都存在消息可能丢失的情况,所以我们需要分情况进行处理:
生产阶段的消息丢失,需要捕获消息发送错误,并重发消息;
存储阶段的消息丢失,可以通过配置刷盘和复制参数,让消息写入多个副本的磁盘上;
消费阶段消息丢失,需要在处理完全部消费业务逻辑后,再发送确认消息。
消息顺序性问题
如果有顺序依赖的消息,我们可以给每一个消息都设置一个唯一的 key,保证同一个 key 的消息发送到相同的队列。
A 用户产生的消息(包括创建消息和删除消息)都按 A 的 key 分发到同一个队列,且一个队列只对应一个消费者。
消息堆积
如果消息消费者读取消息的速度,能够跟上消息生产者的节奏,那么整套消息队列机制就能发挥最大作用。
但是很多时候,由于某些批处理或者其他原因,导致消息消费的速度小于生产的速度。这样会直接导致消息堆积问题,从而影响业务功能。
本篇博客简单介绍了消息队列的入门知识,下一篇将主要针对 RocketMQ 进行介绍,为了督促自己尽快写,先立下 flag ~
以上是关于消息队列专题(基础篇):消息队列入门的主要内容,如果未能解决你的问题,请参考以下文章
RabbitMQ消息队列入门篇(环境配置+Java实例+基础概念)
消息队列专题(高级特性篇):RabbitMQ 如何保证消息的可靠性投递传输和消费
消息队列专题(高级特性篇):RabbitMQ 如何保证消息的可靠性投递传输和消费