茂展的分享博客

深入学习消息中间件 —— RabbitMQ

深入学习消息中间件 —— RabbitMQ

消息丢失了怎么办?

由于我比较了解RabbitMQ,所以我们以RabbitMQ为例子介绍
消息丢失分为三个不同的丢失

  1. 生产者丢失问题
  2. 消息中间件丢失问题
  3. 消费者丢失问题

生产者端丢失(RabbitMQ)

生产者生产的消息要发送到RabbitMQ,但是很容易数据在半路上弄丢,或者网络原因。所以如何保证消息可以安全送达到RabbitMQ呢
解决方案有两个

1、开启RabbitMQ事务机制(同步)(RabbitMQ)

我们可以使用channel.txSelect来开启事务,然后如果消息没有预期到达RabbitMQ,那么会执行事务回退channel.Rollback执行成功的话提交事务channel.txCommit
但是由于事务机制是同步的,会导致吞吐量减少,太耗费性能。

2、开启confirm模式(异步)(RabbitMQ)

生产者生产的每一个消息都会有一个唯一的id,自身会在内存中维护这个id的状态信息,然后将消息写入RabbitMQ,如果RabbitMQ接受到消息,会回传一个ack消息,告诉生产者这个消息我已经接受到了,但是如果消息没有被RabbitMQ处理,那么会回传一个nack,如果你收到这个nack回传消息或者很久没有收到任何响应就利用内存维护的对应Id状态信息再次发送。

事务机制和confirm两个不同点(RabbitMQ)

由于事务机制是同步的,即你提交一个事务之后会阻塞到那儿,但是confirm机制是异步的,消息发送出去之后,然后继续发送,然后RabbitMQ收到消息之后会异步回调给生产者一个接口来通知你消息收到了

RabbitMQ端丢失(RabbitMQ)

我们解决的方案可以开启RabbitMQ持久化,就是将消息写入之后持久化到磁盘,就算某个时候RabbitMQ宕机了,恢复之后也会读取之前存储的数据。但是仍然也会有一种情况,消息存入到RabbitMQ,还没有来得及持久化,RabbitMQ就发生宕机了,从而导致数据丢失,那么如何解决呢?我们可以结合confirm机制,当生产者发送消息给RabbitMQ的时候,我们将数据存入并且持久化到磁盘时再回传ack,这样就可以解决上面的RabbitMQ还没有来得及持久化就宕机了。

消费者端丢失(RabbitMQ)

情形:让消费者从RabbitMQ拿到数据之后,还没有来得及消费,自身就宕机了,可是RabbitMQ以为你已经消费了
解决办法:使用由于RabbitMQ提供的ack机制。简单来说,就是必须关闭RabbitMQ的自动ack,可以通过一个api来调用就行,每次在代码中确认处理完成之后,自己添加ack,这样如果你没有处理完,就无法返回ack,那么RabbitMQ会认为你没没有处理,会把消息让别的消费者处理。

消息中间件的优缺点(RabbitMQ)

优点(RabbitMQ)

解耦合(RabbitMQ)

加入B系统、C系统、D系统都使用A系统执行的结果,那么如何解决C宕机或者需要添加E系统调用,太麻烦了,耦合度太高,每次修改都需要更改原先的代码,所以我们可以使用消息中间件。把A返回的消息存储在消息中间件中,然后任何系统需要用,就去其中拿,不需要就不拿。

异步(RabbitMQ)

加入用户写操作需要往A,B,C三个系统中写数据,但是A需要2ms,B需要200ms,C需要300ms,那么用户写一次数据需要502ms,那么用户可能会崩溃,一次操作需要等待那么久,所以我们可以让A执行操作后,把B、C操作放入到消息中间件中,然后再逐个从其中取出执行,那么用户就会感受到执行速度很快

削锋(RabbitMQ)

一般系统的流量访问都是不均衡的,可能夜里访问人数就几十个,但是例如外卖互联网公司,饭点的时候系统的对数据的操作可能就比较繁重,用户请求大到几K,但是MySQL数据库的每秒的访问到达2k已经差不多了,所以超过这个数量之后,MySQL就会扛不住甚至崩溃,那么我们可以使用消息中间件,把数据库最大操作数量交由数据库处理,剩余的放在消息中间件,等待数据库处理完,我们可以继续从消息中间件取数据。从而解决了高并发下对数据库的压力

缺点(RabbitMQ)

系统可用性降低(RabbitMQ)

系统引入外面依赖越多,越容易挂掉。系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用

系统复杂度提高(RabbitMQ)

硬生生加个 MQ 进来,你怎么[保证消息没有重复消费]?怎么[处理消息丢失的情况]?怎么保证消息传递的顺序性?这些问题都要去解决

一致性问题(RabbitMQ)

A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了

如何保证消息的一致性(RabbitMQ)

RabbitMQ
假如我们把对于数据库的操作放进RabbitMQ,那么插入和删除的顺序必须和送入RabbitMQ的顺序一致,否则执行结果就不一样了,所以我们必要要保证一致性
我们能不能这样解决,由于队列有序(先进先出),就是当消费者消费消息,RabbitMQ等待消费者消费结束之后返回给RabbitMQ一个内容,然后再去执行下一个消息,显然这样不符合高并发下的生产条件,效率太低了。
RabbitMQ消息顺序性解决
即 在MQ中创建多个queue,按照某一规则,有顺序的放进MQ的queue里面,然后消费者只取一个queue里面的数据消费。
或者还是只有一个 queue 但是对应一个消费者,然后这个消费者内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

消息中间件的高可用(RabbitMQ)

RabbitMQ可以有三种模式

  • 单机模式
  • 普通集群模式
  • 镜像集群模式(高可用)

镜像集群模式

你创建的每个queue里面的消息或者元数据都会同步到其他的queue,对于每一个RabbitMQ节点,都有这个queue的一个完整镜像,包含queue的全部数据的意思。
缺点:

  1. 性能开销太大,因为数据需要同步到各个RabbitMQ节点
  2. 不是分布式,没有可扩展性,如果queue负载过重,即使加机器,也是把所有的节点都复制到新的机器上,并没有办法扩展。

知识补记

分布式和集群理解

理解
分布式 一个业务拆分为多个子业务,部署到多个服务器上
集群 同一个业务部署在多个服务器上

主从和集群的更新

更新
主从 服务器之间是异步的,从服务器可能和主服务器不一致
集群 更新是同步的,数据节点都是一致的
------本文结束感谢阅读------
🐶 您的支持将鼓励我继续创作 🐶