RabbitMQ学习(一)之 “消息队列基本认识”

本文详细阐述了RabbitMQ的基本概念,如MQ模型、异步通信、解耦与流量削峰的应用,以及RabbitMQ的核心特性如高可靠、交换机路由和权限管理。讨论了为何使用MQ以及其带来的问题和挑战,最后聚焦于RabbitMQ实例,包括Exchange的路由方式和VHOST的权限隔离。

消息中间件是基于队列与消息传递技术,在网络环境中为应用系统提供同步或异步、可靠的消息传输的支撑性软件系统

什么是MQ?

MQ的定义

用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息队列模型,可以在分布式环境下扩展进程的通信。

MQ的主要特性

  1. 是一个独立运行的服务。生产者发送消息,消费者接收消息并消费,需要先跟服务器建立连接。
  2. 采用队列作为数据结构,有先进先出的特点。
  3. 具有发布订阅(publis/subscribe)的模型,消费者可以获取自己需要的消息。

MQ的作用

  1. 异步

    • 同步通信:发出一个调用请求之后,在没有得到结果之前,就不返回。由调用者主动等待这个调用的结果
    • 异步通信:异步与同步是相反的,调用在发出之后,这个调用就直接返回了,所以没有返回结果。也就是说,当一个异步过程调用发出后,调用者不会马上得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用

    例子:银行转账

  2. 解耦

    系统耦合示意图:
    在这里插入图片描述

// 伪代码
public void returnGoods(){
	stockService.updateInventory();
	payService.refund();
	noticeService.notice();
}

可以看到,如果没有用到MQ的时候,如果做一个取消订单的操作。代码是依次执行的,如果有任何一行报错,那么下面的代码将无法执行,其实,更新库存、退回金额、通知消息等操作是没有先后顺序的。当然,也可以用多线程来实现同时执行多个操作。但是线程池的管理本身也是一个比较麻烦的事情。这时候就体现了 MQ的好处

引入 MQ 以后的示意图
在这里插入图片描述
引入MQ之后,订单系统只需要配置一个MQ的ip地址即可,不需要关心其他子系统。将消息发送给MQ之后,接下来就是由各个下游的业务系统自己创建队列,然后监听队列消费消息。如果以后系统需要加一个记录日志的系统,那么订单系统也不需要改代码。日志系统直接实现消息队列的接口就可以了。这样,我们就实现了系统之间依赖关系的解耦

  1. 削峰(流量削峰)
    第三个主要功能,是实现流量削峰。
    在很多的电商系统里面,有一个瞬间流量达到峰值的情况,比如京东的618,淘宝的双11,还有小米抢购。普通的硬件服务器肯定支撑不了这种百万或者千万级别的并发量,就像2012 年的小米一样,动不动服务器就崩溃。如果通过堆硬件的方式去解决,那么在流量峰值过去以后就会出现巨大的资源浪费。

    那要怎么办呢?如果说要保护我们的应用服务器和数据库,限流也是可以的,但是这样又会导致订单的丢失,没有达到我们的目的。

    为了解决这个问题,我们就可以引入MQ,MQ 既然是队列,一定有队列的特性,我们知道队列的特性是什么?(先进先出FIFO)
    这样,我们就可以先把所有的流量承接下来,转换成MQ 消息发送到消息队列服务器上,业务层就可以根据自己的消费速率去处理这些消息,处理之后再返回结果。就像我们在火车站排队一样,大家只能一个一个买票,不会因为人多就导致售票员忙不过来。如果要处理快一点,大不了多开几个窗口(增加几个消费者)

为什么要用MQ?

  1. 对于数据量大或者处理耗时长的操作,我们可以引入MQ 实现异步通信,减少客户端的等待,提升响应速度
  2. 对于改动影响大的系统之间,可以引入MQ 实现解耦,减少系统之间的直接依赖。
  3. 对于会出现瞬间的流量峰值的系统,我们可以引入MQ 实现流量削峰,达到保护应用和数据库的目的。

使用MQ(消息队列)带来的一些问题

  1. 系统可用性降低:原来是两个节点的通信,现在还需要独立运行一个服务,如果MQ服务器或者通信网络出现问题,就会导致请求失败。
  2. 系统复杂性提高: 为什么说复杂?第一个就是你必须要理解相关的模型和概念,才能正确地配置和使用MQ。第二个,使用MQ 发送消息必须要考虑消息丢失和消息重复消费的问题。一旦消息没有被正确地消费,就会带来数据一致性的问题。

所以,我们在做系统架构的时候一定要根据实际情况来分析,不要因为我们说了这
么多的MQ 能解决的问题,就盲目地引入MQ

RabbitMQ简介

基本特性

  1. 高可靠:RabbitMQ 提供了多种多样的特性让你在可靠性和性能之间做出权衡,包括持久化、发送应答、发布确认以及高可用性。
  2. 灵活的路由:通过交换机(Exchange)实现消息的灵活路由。
  3. 支持多客户端:对主流开发语言(Python、Java、Ruby、PHP、C#、JavaScript、Go、Elixir、Objective-C、Swift 等)都有客户端实现。
  4. 集群与扩展性:多个节点组成一个逻辑的服务器,支持负载。
  5. 高可用队列:通过镜像队列实现队列中数据的复制
  6. 权限管理:通过用户与虚拟机实现权限管理
  7. 插件系统:支持各种丰富的插件扩展,同时也支持自定义插件。
  8. 与Spring 集成:Spring 对AMQP 进行了封装。

AMQP(Advanced Message Queuing Protocol)协议

AMQP:高级消息队列协议,是一个工作于应用层的协议,最新的版本是1.0 版本。
在这里插入图片描述
除了RabbitMQ 之外,AMQP 的实现还有OpenAMQ、Apache Qpid、Redhat
Enterprise MRG、AMQP Infrastructure、ØMQ、Zyre

除了AMQP 之外, RabbitMQ 支持多种协议, STOMP 、MQTT 、HTTP and
WebSockets

可以使用WireShark 等工具对RabbitMQ 通信的AMQP 协议进行抓包。

工作模型

由于RabbitMQ 实现了AMQP 协议,所以RabbitMQ 的工作模型也是基于AMQP的。理解这张图片至关重要
在这里插入图片描述

  1. Broker
    我们要使用RabbitMQ 来收发消息,必须要安装一个RabbitMQ 的服务,可以安装在Windows 上面也可以安装在Linux 上面,默认是5672 的端口。这台RabbitMQ 的服务器我们把它叫做Broker,中文翻译是代理/中介,因为MQ 服务器帮助我们做的事情就是存储、转发消息。

  2. Connection
    无论是生产者发送消息,还是消费者接收消息,都必须要跟Broker 之间建立一个连接,这个连接是一个TCP 的长连接

  3. Channel
    如果所有的生产者发送消息和消费者接收消息,都直接创建和释放TCP 长连接的话,对于Broker 来说肯定会造成很大的性能损耗,因为TCP 连接是非常宝贵的资源,创建和释放也要消耗时间
    所以在AMQP 里面引入了Channel 的概念,它是一个虚拟的连接。我们把它翻译成通道,或者消息信道。这样我们就可以在保持的TCP 长连接里面去创建和释放Channel,大大了减少了资源消耗。另外一个需要注意的是,Channel 是RabbitMQ 原生API 里面的最重要的编程接口,也就是说我们定义交换机、队列、绑定关系,发送消息消费消息,调用的都是Channel 接口上的方法

  4. Queue
    现在我们已经连到Broker 了,可以收发消息了。在其他一些MQ 里面,比如ActiveMQ 和Kafka,我们的消息都是发送到队列上的。

    队列是真正用来存储消息的,是一个独立运行的进程,有自己的数据库(Mnesia)。

    消费者获取消息有两种模式,一种是Push 模式,只要生产者发到服务器,就马上推送给消费者。另一种是Pull 模式,消息存放在服务端,只有消费者主动获取才能拿到消息。消费者需要写一个while 循环不断地从队列获取消息吗?不需要,我们可以基于事件机制,实现消费者对队列的监听。
    由于队列有FIFO 的特性,只有确定前一条消息被消费者接收之后,才会把这条消息
    从数据库删除,继续投递下一条消息

  5. Exchange
    在RabbitMQ 里面永远不会出现消息直接发送到队列的情况。因为在AMQP 里面引入了交换机(Exchange)的概念,用来实现消息的灵活路由。
    交换机是一个绑定列表,用来查找匹配的绑定关系。
    队列使用绑定键(Binding Key)跟交换机建立绑定关系。
    生产者发送的消息需要携带路由键(Routing Key),交换机收到消息时会根据它保
    存的绑定列表,决定将消息路由到哪些与它绑定的队列上。
    注意:交换机与队列、队列与消费者都是多对多的关系

  6. Vhost
    我们每个需要实现基于RabbitMQ 的异步通信的系统,都需要在服务器上创建自己
    要用到的交换机、队列和它们的绑定关系。如果某个业务系统不想跟别人混用一个系统,怎么办?再采购一台硬件服务器单独安装一个RabbitMQ 服务?这种方式成本太高了。
    在同一个硬件服务器上安装多个RabbitMQ 的服务呢?比如再运行一个5673 的端口?
    没有必要,因为RabbitMQ 提供了虚拟主机VHOST。
    VHOST 除了可以提高硬件资源的利用率之外,还可以实现资源的隔离和权限的控
    制。它的作用类似于编程语言中的namespace 和package,不同的VHOST 中可以有
    同名的Exchange 和Queue,它们是完全透明的。
    这个时候,我们可以为不同的业务系统创建不同的用户(User),然后给这些用户
    分配VHOST 的权限。比如给风控系统的用户分配风控系统的VHOST 的权限,这个用户可以访问里面的交换机和队列。给超级管理员分配所有VHOST 的权限。

我们说到RabbitMQ 引入Exchange 是为了实现消息的灵活路由,到底有哪些路由
方式?

消息队列的路由方式
1. 直连 Direct

队列与直连类型的交换机绑定,需指定一个精确的绑定键。

生产者发送消息时会携带一个路由键。只有当路由键与其中的某个绑定键完全匹配时,这条消息才会从交换机路由到满足路由关系的此队列上。多个队列也可以使用相同的绑定键。

在这里插入图片描述

例如 : channel.basicPublish(“MY_DIRECT_EXCHANGE”,”spring”,”msg 1”); 只有第一个队列能收到消息。

2.主题 Topic

队列与主题类型的交换机绑定时,可以在绑定键中使用通配符。两个通配符:

  • (#号代表 0 个或者多个单词)
  • (* 不多不少一个单词)

单词(word)指的是用英文的点“.”隔开的字符。例如 abc.def 是两个单词。

在这里插入图片描述
第一个队列支持路由键以 abc 开头的消息路由,后面可以有单词,也可以没有。
第二个队列支持路由键以 java 开头,并且后面是一个单词的消息路由。
第三个队列支持路由键以 jvm 结尾,并且前面是一个单词的消息路由。

例如:
channel.basicPublish(“MY_TOPIC_EXCHANGE”,“abc.fjd.klj”,“msg 2”); 只有第一个队列能收到消息。
channel.basicPublish(“MY_TOPIC_EXCHANGE”,“abc.jvm”, “msg 3”); 第一个队列和第三个队列能收到消息。

3.广播 Fanout

广播类型的交换机与队列绑定时,不需要指定绑定键。因此生产者发送消息到广播类型的交换机上,也不需要携带路由键。消息达到交换机时,所有与之绑定了的队列,都会收到相同的消息的副本。
在这里插入图片描述
例如:
channel.basicPublish(“MY_FANOUT_EXCHANGE”, “”, “msg 4”); 三个队列都会收到 msg 4。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值