【RabbitMQ】之 MQ 与 RabbitMQ 简介

一、MQ 简介


1、为什么使用MQ?MQ的优点

简答:

  • 异步处理 - 相比于传统的串行、并行方式,提高了系统吞吐量。
  • 应用解耦 - 系统间通过消息通信,不用关心其他系统的处理。
  • 流量削锋 - 可以通过消息队列长度控制请求量;可以缓解短时间内的高并发请求。
  • 日志处理 - 解决大量日志传输。
  • 消息通讯 - 消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。

详答:

主要是:解耦、异步、削峰。

解耦:A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃…A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。

就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦。

异步:A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求。如果使用 MQ,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms。

削峰:减少高峰时期对服务器压力。

2、MQ 的缺点

系统可用性降低

本来系统运行好好的,现在你非要加入个消息队列进去,那消息队列挂了,你的系统不是呵呵了。因此,系统可用性会降低;

系统复杂度提高

加入了消息队列,要多考虑很多方面的问题,比如:一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东西更多,复杂性增大。

一致性问题

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

所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。

3、MQ 的常见问题

MQ 的常见问题有:

  1. 消息的顺序问题
  2. 消息的重复问题

1)、消息的顺序问题

消息有序指的是可以按照消息的发送顺序来消费。

假如生产者产生了 2 条消息:M1、M2,假定 M1 发送到 S1,M2 发送到 S2,如果要保证 M1 先于 M2 被消费,怎么做?

img

解决方案:

(1)保证生产者 - MQServer - 消费者是一对一对一的关系

img

缺陷:

  • 并行度就会成为消息系统的瓶颈(吞吐量不够)
  • 更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花费更多的精力来解决阻塞的问题。

(2)通过合理的设计或者将问题分解来规避

  • 不关注乱序的应用实际大量存在
  • 队列无序并不意味着消息无序,所以从业务层面来保证消息的顺序而不仅仅是依赖于消息系统,是一种更合理的方式。

2)、消息的重复问题

造成消息重复的根本原因是:网络不可达。所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消息,应该怎样处理?

解决的办法就是:消费端处理消息的业务逻辑应该保持幂等性。只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样。

关于接口幂等性问题可以参考我的另一篇博客:【编程开发】之接口的幂等性

4、MQ 相关概念

1)、消息服务中两个重要概念

消息代理(message broker)和目的地(destination);

当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。

2)、消息队列主要的两种形式

  • 队列(queue):点对点式
    • –消息发送者发送消息,消息代理将其放入一个队列中,消息接收者从队列中获取消息内容,消息读取后被移出队列
    • –消息只有唯一的发送者和接受者,但并不是说只能有一个接收者
  • 主题(topic):发布订阅式
    • –发送者(发布者)发送消息到主题,多个接收者(订阅者)监听(订阅)这个主题,那么就会在消息到达时同时收到消息

3)、JMS(Java Message Service)JAVA消息服务

基于JVM消息代理的规范。ActiveMQ、HornetMQ 是 JMS 实现。

JMS 是由 Java Api 定义,不跨语言也不跨平台,提供两种消息模型:点对点(Peer-2-Peer)和发布订阅(Pub/sub)。

支持多种消息类型:TextMessage、MapMessage、BytesMessage、StreamMessage、ObjectMessage、Message (只有消息头和属性)。

总结:JMS 定义了 JAVA API 层面的标准;在 java 体系中,多个 client 均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差。

4)、AMQP(Advanced Message Queuing Protocol)

高级消息队列协议,也是一个消息代理的规范,兼容 JMS,RabbitMQ 是 AMQP 的实现。

AMQP 由网络线级协议定义,即跨语言又跨平台,提供了五种消息模型:

  • (1)、direct exchange
  • (2)、fanout exchange
  • (3)、topic change
  • (4)、headers exchange
  • (5)、system exchange

本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分。

但消息类型只有一种:byte[],当实际应用时,有复杂的消息,可以将消息序列化后发送。

总结:AMQP 定义了 wire-level 层的协议标准;天然具有跨平台、跨语言特性。


二、Rabbit MQ 简介


1、什么是 RabbitMQ

RabbitMQ 是一个由 erlang 开发的 AMQP(Advanved Message Queue Protocol) 的开源实现。

2、RabbitMQ 核心概念

  • Message:消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
  • Publisher:消息的生产者,也是一个向交换器发布消息的客户端应用程序。
  • Exchange:交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。Exchange有4种类型:direct(默认),fanout,topic,和headers,不同类型的Exchange转发消息的策略有所区别
  • Queue:消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
  • Binding:绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。Exchange 和 Queue 的绑定可以是多对多的关系。
  • Routing Key: 路由关键字,exchange 根据这个关键字进行消息投递
  • Connection:网络连接,比如一个TCP连接。
  • Channel:信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
  • Consumer:消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
  • Virtual Host:虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。
  • Broker:表示消息队列服务器实体

在这里插入图片描述

3、RabbitMQ 运行机制

AMQP 中的消息路由
  • AMQP 中消息的路由过程和 Java 开发者熟悉的 JMS 存在一些差别,AMQP 中增加了 ExchangeBinding 的角色。生产者把消息发布到 Exchange 上,消息最终到达队列并被消费者接收,而 Binding 决定交换器的消息应该发送到那个队列。

在这里插入图片描述

Exchange 类型

Exchange 分发消息时根据类型的不同分发策略有区别,目前共四种类型:direct、fanout、topic、headers 。headers 匹配 AMQP 消息的 header 而不是路由键, headers 交换器和 direct 交换器完全一致,但性能差很多,目前几乎用不到了,所以直接看另外三种类型:

1)、Direct Exchange

在这里插入图片描述

消息中的路由键(routing key)如果和 Binding 中的 binding key 一致, 交换器就将消息发到对应的队列中。路由键与队列名完全匹配,如果一个队列绑定到交换机要求路由键为“dog”,则只转发 routing key 标记为“dog”的消息,不会转发“dog.puppy”,也不会转发“dog.guard”等等。它是完全匹配、单播的模式。

2)、Fanout Exchange

在这里插入图片描述

每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

3)、Topic Exchange

在这里插入图片描述

topic 交换器通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号 # 和符号 *# 匹配 0 个或多个单词,* 匹配一个单词。


4、RabbitMQ 的工作模式

1)、Simple 模式(最简单的的收发模式)

  • 消息产生消息,将消息放入队列
  • 消息的消费者监听消息队列,如果队列中有消息,就消费掉,消息被拿走后,自动从队列中删除

隐患:消息可能没有被消费者正确处理,已经从队列中消失了,造成消息的丢失,这里可以设置成手动的 ack,但如果设置成手动 ack,处理完后要及时发送 ack 消息给队列,否则会造成内存溢出。

2)、Work 模式(资源的竞争)

消息产生者将消息放入队列,多个消费者同时监听同一个队列,共同争抢当前的消息队列内容,谁先拿到谁负责消费消息。

隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用。

3)、Publish/Subscribe 发布订阅模式(共享资源)

  • 每个消费者监听自己的队列;
  • 生产者将消息发给 broker,由交换机将消息转发到绑定此交换机的每个队列,每个绑定交换机的队列都将接收到消息。

4)、Routing 路由模式

  • 消息生产者将消息发送给交换机,交换机按照路由键匹配把消息发送给对应的消息队列;
  • 可以根据业务场景定义路由字符串。

5)、Topic 主题模式(路由模式的一种)

主题模式中的路由功能添加了模糊匹配:

  • * 号匹配一个单词
  • # 号匹配 0 个或多个单词

工作流程如下:

  • 消息产生者产生消息,把消息交给交换机
  • 交换机根据 key 的规则模糊匹配到对应的队列,由队列的监听消费者接收消息消费

5、解决 RabbitMQ 的消息问题

1)、消息的顺序性

RabbitMQ 保证消息的顺序性的方法为:

  • 1、拆分多个 queue,每个 queue 一个 consumer,这种方法会产生比较多的 queue,比较麻烦点;
  • 2、一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

2)、解决消息的丢失问题

消息丢失分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息;

  • 生产者丢失消息:生产者开启确认模式,配置确认回调和回退回调
  • 消息列表丢失消息:开启消息持久化,创建队列的时候设置 durable 为 true,发送消息的时候设置 deliveryMode 为 2
  • 消费者丢失消息:设置为手动 Ack 应答模式

3)、消息的重复消费问题

保证消费消息的幂等性!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值