一、消息队列----应用场景

场景名称

场景描述

传统做法

消息队列做法

异步处理

用户注册后,需要发注册邮件和注册短信

1.串行的方式:

  信息写入数据库50ms + 发送注册短信50ms + 发送注册邮件50ms =》 150ms

2.并行方式:

  信息写入数据库50ms +【发送注册邮件的同时,发送注册短信】50ms=》100ms

信息写入数据库50ms + 【注册邮件,发送短信写入消息队列】0.0001ms =>50ms

注:因此写入消息队列的速度很快,基本可以忽略;

中心思想:引入消息队列,将不是必须的业务逻辑,异步处理

应用解耦

用户下单后,订单系统需要通知库存系统

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

流量削峰

秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉;

 

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,

则直接抛弃用户请求或跳转到错误页面

日志处理

将消息队列用在日志处理中;

(我一般关注把日志记录下来,不怎么关注日志后续的处理,所以这个用的不多)

 

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

消息通讯

消息通讯是指,【消息队列一般都内置了高效的通信机制】,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等

 

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

 

二、消息队列----优缺点

优点

缺点

解耦

引入复杂度(引入消息队列本身就有创建维护成本)

提速

暂时的不一致性(你把消息给队列后,默认它一定会成功执行的,但实际上不一定)

广播(一次生成,可多人订阅)

 

削峰

 

 

三、消息队列----重试补偿,事务补偿

问题场景

解决思想

解决办法

消费者已经收到消息或消费消息了,但因为网络中断没给mq发送ack,导致消息重发重复消费;

消费消息时先判断该消息是否已消费过(这个状态位如何存储读取?

发送消息时】给消息分配一个全局id,只要消费过该消息,将 < id,message>K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可

客户下单,若订单创建成功,库存扣减失败,如何回滚订单?在一台服务器上用事务能解决,但分布式如何处理?

既然不能把【订单创建】和【库存减扣】放到一个事务里,那就把【订单创建】和【库存减扣分身--消息事件表】放到一个事务里;

 

实现:

1.【订单创建】和【库存减扣分身--消息事件表】放到一个事务里;

2. 或者 消息事件表消费失败更新事件表状态,根据状态扔异常进行事务回滚;

3. 或者 库存服务定时扫描消息事件表,将未投递失败/消费 失败的消息进行消费,即补偿事务一致性

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

 

 

 

 

四、消息队列----幂等性(如何保证 重复消费的结果 与 消费一次的结果是相同的)

解决办法

举例说明

利用数据库唯一约束

将订单表中的订单编号设置为唯一索引,创建订单时,根据订单编号就可以保证幂等

去重表

首先在去重表上建唯一索引,其次操作时把业务表和去重表放在同个本地事务中,如果出现重复消费,数据库会抛唯一约束异常,操作就会回滚

利用redis的原子性

每次操作都直接set到redis里面,然后将redis数据定时同步到数据库中

多版本(乐观锁)控制

此方案多用于【更新】的场景下。大体思路是:给业务数据增加一个版本号属性,每次更新数据前,比较当前数据的版本号是否和消息中的版本一致,如果不一致则拒绝更新数据,更新数据的同时将版本号+1

状态机机制

此方案多用于更新且业务场景存在多种状态流转的场景

token机制

生产者发送每条数据的时候,增加一个全局唯一的id,这个id通常是业务的唯一标识,比如订单编号。在消费端消费时,则验证该id是否被消费过,如果还没消费过,则进行业务处理。处理结束后,在把该id存入redis,同时设置状态为已消费。如果已经消费过了,则不进行处理。

 

 

 

五、消息队列----消息堆积(要么是发送端变快,要么是消费端变慢造成)

产生原因:

  1. Producer 端单位时间发送的消息增多,Consumer 端短时间内来不及消费;
  2. Producer 端单位时间发送的消息正常,Consumer 端因消费线程低效不能及时消费;

解决思想:

设计MQ系统的时候,一定要保证 Consumer 端的消费性能要高于 Producer 端的发送性能

发送端性能优化:

发送端性能低:检查是否因为业务逻辑耗时太久导致的 + 设置合适的 并发 和 批量 大小;

消费端性能优化:

消费端性能低:优化业务逻辑耗时 + 水平扩容 (扩充consumer端的 实例数 和 topic中的 partition 数)

 

六、消息队列----有序性(产生原因:多个消费者/多线程)

RabbitMQ 无序原因:

一个queue,多个consumer

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

RabbitMQ无序解决办法:

拆分多个queue,每个queue一个consumer,就是多一些queue而已,确实是麻烦点;

或者就一个queue,但是对应一个consumer,然后这个consumer内部用内存队列做排队,然后分发给底层不同的worker来处理

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

 

kafka 无序原因:

一个topic,一个partition,一个consumer,但是内部多线程

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

Kafka 无序解决办法:

一个topic,一个partition,一个consumer,内部单线程消费,写N个内存queue,然后N个线程分别消费一个内存queue即可

消息队列----使用场景,重试补偿,事务补偿,幂等性,消息堆积,有序性

相关文章: