RabbitMQ介绍
- 一、前言
-
- [1. 回顾生产者消费者模型](#1. 回顾生产者消费者模型)
- 2.忙闲不均与负载均衡
- 3.改造线程池使其支持负载均衡
- 4.MQ的引入
- 二、MQ的介绍
- 三、AMQP协议
- 四、RabbitMQ介绍
- 五、项目结构
- 六、RabbitMQ如何诞生--总结/铺垫
-
-
- [1. 解决传统同步调用的局限性](#1. 解决传统同步调用的局限性)
- [2. 引入生产者消费者模型](#2. 引入生产者消费者模型)
- [3. 多对多通信与特性设计](#3. 多对多通信与特性设计)
- [4. 虚拟主机的设计](#4. 虚拟主机的设计)
- [5. 信道(Channels)与多线程环境](#5. 信道(Channels)与多线程环境)
- [6. 借鉴数据库与表的设计](#6. 借鉴数据库与表的设计)
- [7. 迭代更新与持续演进](#7. 迭代更新与持续演进)
-
一、前言
这是我项目的基础版本,目的是先实现一个基础功能的RabbitMQ消息队列,后续再对其进行扩展,丰富更多功能
1. 回顾生产者消费者模型
在学习了生产者消费者模型之后,我们回顾一下生产者消费者模型的优点:
我们之前写生产者消费者模型的阻塞队列和环形队列版本时,介绍了解耦与并发。
但是没有提到过忙闲不均这一问题,也没有提到过负载均衡,下面我们谈一下二者
2.忙闲不均与负载均衡
忙闲不均就是指在多线程等并行处理环境当中,不同组件之间的工作负载分配不均,导致某些组件过忙,而另一些组件过闲,从而影响整体性能和资源利用率。
负载均衡是解决忙闲不均的一种有效处理方法。通过负载均衡器将工作负载均匀地分配到多个组件上,从而实现组件之间的负载均衡
因此我们的生产消费模型就变成了下面这个样子:
3.改造线程池使其支持负载均衡
问题来了?
负载均衡器怎么分配任务呢?
我们之前的代码都是从线程在(互斥锁,条件变量)/(互斥锁、信号量)的约束下实现的生产消费模型
都是从线程主动取任务,有没有什么方法能让我们主动给从线程分配任务?
我们遇到的这种问题肯定都不大,首先就想:能不能加一层?
我现在要统计负载,那我给每个从线程单独维护一个任务队列不就可以实现负载均衡了吗?
这样的话甚至连互斥锁和条件变量这种维护线程互斥和同步的手段都不需要了,
也就是:把原先的一个任务队列直接拆分为多个任务队列,通过负载均衡器来将任务放到负载少的队列当中
怎么办呢?
再加一层,对于我们遇到的问题,大部分情况都能解决
引入一个监控线程和它的热备份加上心跳机制,就可以解决这一问题
其实在很多分布式系统的组件/主从服务器当中,心跳机制和热备份是非常常见的,比如:
1. HDFS 2.0的活跃名称节点和待命名称节点与Zookeeper
2. HBase当中的Master服务器与Region服务器
3. MapReduce当中的JobTracker与TaskTracker
4. Storm当中的Nimbus和Supervisor
5. Spark当中的Dirver Program和Worker Node,(它的容错更强:采用基于血缘和管道化形成的RDD)
不过加了监控线程之后的确不好写,写出来是完全没有问题的,只不过总觉得不够优雅,我们一个普普通通的线程池怎么都用上分布式系统的设计方式了呢?
是不是有点太大材小用了呢?
光看一眼上面那5个Hadoop当中的强大框架,
咱就知道凭咱目前的实力不好整啊,我觉得整是能整出来的,
只不过用的设计方式来重了吧...或者说不太适合~,
写出来之后代码也需要不断重构,而且代码不易扩展
4.MQ的引入
那怎么办呢?
我们就是想提高任务执行的容错性,
那我们把任务队列和消费者的对应关系从1对1改成1对多,然后再任务队列内部加上负载均衡器不就行了吗?
那我们这个项目就是要实现这个?
当然不是,有一个更强👍的东西早已出现,它叫做MQ(消息中间件)
不过理解它对于我们理解RabbitMQ有非常好的启发性和关联性
二、MQ的介绍
MQ(Message Queue消息队列)是一个基于生产者消费者模型的消息传递机制,通过队列来保存和转发消息,实现了应用程序之间的解耦、异步处理和流量削峰,通常用于应用层和应用层之间的通信
可能看起来很懵,但是它是基于生产者消费者模型的啊,也就一定具有生产者消费者模型的优点:
- 多执行流之间的解耦
- 通过负载均衡,解决忙闲不均
- 实现并发,从而提高效率
而这些优点放在MQ下面就被放大了,尤其是在目前的Web2.0/3.0时代,这些优点格外明显也突出
而且还有了其他很强大的功能:发布订阅,支持分布式事务等等
1.应用/模块解耦,且提高容错性
MQ降低了不同模块或系统之间的耦合度,使得它们可以独立地进行开发和维护。
当一个业务需要多个模块共同实现时,只需要主业务完成后发送一条MQ消息,
其余模块消费MQ消息即可实现业务逻辑,无需直接调用对方接口,从而简化了系统间调用的问题
因此尽管一个模块可能短时间出现了问题无法提供服务,但是依然没有大碍
否则中间数据就需要额外保存,增加代码的复杂程度
2.异步处理
有些服务之间的调用是异步的
比如直播服务当中:
3.流量削峰填谷
4.分布式事务
MQ通过消息的可靠传递和事务性操作,将复杂的事务拆分成多个简单的子事务,由各个节点或服务分别处理。
这些子事务之间通过消息进行协调和同步,从而实现分布式事务的一致性和完整性。
1.两阶段提交协议(2PC协议)
2.事务消息(RocketMQ)
Rocket(火箭),象征着高速和强大的推动力,是阿里开源的消息中间件,天生为金融互联网领域而生
广泛应用在订单、交易、充值、流计算、消息推送、日志流式处理等场景当中
它提供了事务消息的功能
5.发布订阅
MQ最重要的功能:发布订阅功能
以RabbitMQ为例:
三、AMQP协议
1.简单介绍RabbitMQ
Rabbit:兔子的意思,象征着系统的快速,可靠且易于扩展(因为兔子敏捷嘛)
它是在AMQP(高级消息队列协议)的基础上完成的,是当前最主流的消息队列协议之一
2.AMQP协议
AMQP(Advanced Message Queuing Protocol 高级消息队列协议)规定:
- Broker服务器负责消息的存储与转发
- 虚拟机(virtual host):类似于MySQL的"database",是一个逻辑上的集合,在Broker服务器上可以存在多个
- 交换机(Exchange):接收生产者发送的消息,根据路由匹配将消息转发到不同的队列上
- 队列(Queue):真正用来存储消息的部分,每个消费者决定自己要订阅哪个队列的消息
- 绑定(Binding):Exchange和Queue之间的绑定关系
- 消息(Message):传递的内容
3.需要对外提供的接口
我们先暂且不提供声明/删除虚拟机的操作,后面扩展的时候再实现
RabbitMQ当中的声明是一种强断言的思想:有就ok,没有就创建
四、RabbitMQ介绍
1.交换机类型
因为交换机是负责转发消息的,而消息的发布订阅功能的需求通常比较灵活,因此RabbitMQ当中的交换机类型分为四类:
- 直接交换:Direct(生产者发送消息时直接指定被该交换机绑定的队列名)
只有当routing_key跟binding_key完全相等时才能够匹配成功 - 广播交换:Fanout(生产者发送的消息会被推送到该交换机绑定的所有队列当中)
匹配时全都是true - 主题交换:Topic(根据消息的routing_key和队列的binding_key来进行模式匹配)
2.网络通信
1.生活小例子理解计科当中的一种资源隔离机制
其实,这个机制的实质也是"在中间加一层"
2.RabbitMQ为何要有信道
Broker是服务器,针对每个客户端提供的服务都是以连接为单元进行提供的
也就是说每个客户端都要凭借一个TCP连接来进行通信
而RabbitMQ将连接细化为了一个个信道,信道和信道之间的数据是独立的,不会相互干扰
这样做主要是为了更好的复用TCP连接,达到长连接的效果,避免频繁的创建和关闭TCP连接
其实信道不就是在"中间加一层"嘛
下面我们仔细考虑一下这个临界资源的划分这一问题
为何不能让多线程/多进程共用同一个TCP连接直接进行服务呢,而非要划分信道呢?
3.持久化
RabbitMQ当中,虚拟机、交换机、队列、绑定信息和消息都允许设置持久化标志进行持久化,以便即使服务重启,也能保证这些信息都能被重新恢复
而消费者却无需持久化
为何呢?
怎么冒出一个消费者来呢?
我们后面都会介绍的,而且也会写代码的。
消费者会不会存在很多呢? -> 当然会啦,那么要不要对消费者进行管理呢?
如果不管理,任务队列关联了A和C,我去哪里找A和C呢?
对不对,所以必须要对消费者进行管理,因此我们的项目结构当中必须要有消费者管理模块
五、项目结构
1.服务器模块化架构
1.数据管理模块
我们的核心数据结构和数据都在这个数据管理模块当中
关于Sqlite3数据库我们后面会介绍,这个模块是我们首先要实现的模块
2.业务处理模块
虚拟机模块/数据管理模块:交换机+队列+绑定信息
路由匹配模块:负责根据交换机类型、绑定信息的binding_key和消息的routing_key来进行路由匹配的模块
消费者管理模块:管理所有消费者,如何设计与实现,我们后面细谈
异步工作线程模块:其实就是一个异步工作的线程池,为了提高我们程序效率而设计
我们后面会详细实现它的
3.网络模块
其实就是服务器,连接与信道,对外提供网络服务的模块
2.客户端模块化架构
客户端其实就很简单了
只需要一个网络模块和业务模块
网络模块负责跟服务器进行通信
业务模块负责构建并发送网络请求,接收并处理网络响应,接收消息进行消费或者向服务器发送消息
3.总图
六、RabbitMQ如何诞生--总结/铺垫
1. 解决传统同步调用的局限性
随着大数据时代的到来,数据处理的实时性和效率成为关键挑战。传统的模块同步调用方式在面对高并发、大规模数据处理时显得力不从心,因为它会导致服务间的直接依赖,增加系统的耦合度,降低响应速度和可扩展性。RabbitMQ作为消息中间件,通过引入异步通信机制,有效解决了这些问题。
2. 引入生产者消费者模型
RabbitMQ基于生产者消费者模型设计,允许生产者(消息的发送者)和消费者(消息的处理者)之间解耦。生产者将消息发送到RabbitMQ服务器上的队列中,而消费者则从队列中拉取消息进行处理。这种设计不仅提高了系统的灵活性和可扩展性,还增强了系统的容错能力。
3. 多对多通信与特性设计
RabbitMQ支持多对多的通信模式,即多个生产者可以向同一个队列发送消息,多个消费者也可以从同一个队列接收消息。此外,RabbitMQ还提供了丰富的特性,如交换机(Exchanges)和绑定(Bindings),以支持更复杂的消息路由和分发策略。交换机用于接收生产者发送的消息,并根据路由规则将消息分发到一个或多个队列中;绑定则定义了交换机和队列之间的关系。
4. 虚拟主机的设计
为了支持分布式和云计算环境,RabbitMQ引入了虚拟主机(Virtual Hosts)的概念。每个虚拟主机本质上是一个独立的RabbitMQ服务器实例,拥有自己的队列、交换机、绑定和权限机制。这使得RabbitMQ能够在同一物理或虚拟机上隔离不同的应用或租户,提高了系统的安全性和可管理性。
5. 信道(Channels)与多线程环境
为了适应多线程环境,RabbitMQ将连接(Connections)细分为信道(Channels)。每个连接可以包含多个信道,每个信道都是轻量级的,并且可以独立地进行消息的发送和接收。这种设计减少了线程间的资源争用,提高了系统的并发处理能力。
6. 借鉴数据库与表的设计
RabbitMQ的虚拟机和模块设计在一定程度上借鉴了数据库与表的设计思想。虚拟机提供了一种命名空间或隔离层,用于组织和管理不同的消息队列和交换机;而交换机和队列则类似于数据库中的表和索引,用于存储和检索消息。这种设计使得RabbitMQ的架构既灵活又强大。
7. 迭代更新与持续演进
随着技术的不断进步和新的应用场景的出现,RabbitMQ一直在进行迭代更新和持续演进。新版本的RabbitMQ引入了更多的特性和优化,以支持更复杂的消息传递需求、提高系统的性能和可靠性、简化部署和管理等。
综上所述,RabbitMQ的设计是一个集大成者,它融合了生产者消费者模型、多对多通信、虚拟主机、信道、数据库设计等思想,构建了一个高效、灵活、可扩展的消息中间件系统。
关于RabbitMQ的其他机制等到我们把基础项目全部实现,介绍,分析完毕,完全吃透之后,我们再来介绍,并实现
以上就是项目第一弹:RabbitMQ介绍的全部内容