文章目录
RabbitMQ
1 基本概念
消息 (Message) 是指在应用间传送的数据。消息可以非常简单,比如只包含文本字符串、JSON 等,也可以很复杂,比如内嵌对象。
消息队列中间件 (Message Queue Middleware,简称为 MQ) (消息队列或者消息中间件)是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。通过提供消息传递和消息排队模型,它可以在分布式环境下扩展进程间的通信。
两种传递模式:点对点(P2P, Point-to-Point) 模式和发布/订阅 (Pub/Sub) 模式
比较主流的有 RabbitMQ,Kafka,ActiveMQ, RocketMQ等。 面向消息的中间件(简称为 MOM , Message Oriented Middleware) 提供了以松散藕合的灵活方式集成应用程序的一种机制。它们提供了基于存储和转发的应用程序之间的异步数据发送,即应用程序彼此不直接通信,而是与作为中介的消息中间件通信 。 消息中间件提供了有保证的消息发送,应用程序开发人员无须了解远程过程调用 ( RPC) 和网络通信协议的细节。
作用:
解耦,冗余(存储),扩展性,削峰,可恢复性,顺序保证,缓冲,异步通信
消息队列(Message Queue,简称 MQ),从字面意思上看,本质是个队列,FIFO 先入先出,
只不过队列中存放的内容是 message 而已。
其主要用途:不同进程 Process/线程 Thread 之间通信
MQ简单模型:
●Producer: 消息生产者,负责产生和发送消息到Broker; .
●Broker: 消息处理中心。负责消息存储、确认、重试等,一般其中会包含多个queue;
●Consumer: 消息消费者,负责从Broker 中获取消息,并进行相应处理;
RabbitMQ 是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ 服务器是用 Erlang 语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
支持主流操作系统: linux,windows,macox…
多种开发语言的支持: Java,Python,Ruby,.NET,PHP,C/C++,Node.js…
Erlang
AMQP
RabbitMQ 的特点:
可靠性,灵活的路由,扩展性,高可用性,多种协议,多语言客户端,管理界面,插件机制
2 安装
搜索:docker search rabbitmq
拉取镜像:docker pull docker.io/rabbitmq:3.7.17-management(这个版本有web管理页面)
查看镜像:docker images
启动容器:
docker run -d --name rbmq -p 5672:5672 -p 15672:15672 -v /var/rabbitmq/data:/var/lib/rabbitmq --hostname myRabbit -e RABBITMQ_DEFAULT_VHOST=my_vhost -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=1111 docker.io/rabbitmq:3.7.17-management
docker run
-d # 后台运行
--name hsj-rabbitmq # 名字
-p 5672:5672 # RbbitMQ 端口
-p 15672:15672 # web 控制端口
-v /var/rabbitmq/data:/var/lib/rabbitmq #映射的目录
--hostname myRabbit #主机名(RabbitMQ 的一个重要注意事项是它根据所谓的 “节点名称” 存储数据,默认为主机名)
-e RABBITMQ_DEFAULT_VHOST=my_vhost #默认虚拟机名字
-e RABBITMQ_DEFAULT_USER=admin #默认用户名
-e RABBITMQ_DEFAULT_PASS=123123 #默认用户名密码
docker.io/rabbitmq:3.7.17-management #运行的镜像名字
浏览器访问 web 管理:http://外网ip:15672
输入用户名和密码
安装完成!!!
如果不用docker安装rabbitmq,那就需要先安装Erlang
3 核心概念
生产者–消费者模型,主要负责接收、存储、转发消息。
基础架构
生产者
消费者
Broker(Server):消息中间件 的服务节点
消息队列的运转过程
队列
Queue队列:RabbitMQ的内部对象,用于存储消息。
队列模型
官方文档:https://www.rabbitmq.com/getstarted.html
1.简单模式
2.Work Queues/Task Queues
工作队列,在消费者之间分配任务(竞争的消费者模式),一个消息只能被一个消费者获取
3.Publish/Subscribe
订阅模式,消息被路由投递给多个队列,同一个消息被多个消费者获取,以达到同时向多个
消费者发送消息的目的。ExchangeType 为 fanout。
4.Routing
路由模式,消费者可以有选择地接收消息。ExchangeType 为 direct。当消费者的 binding key 与 Exchange 的 Routing Key 匹配时则消费者可以接收到该消息。
5.Topic
通配符模式,一个消息被多个消费者获取。消息的目的 queue 可用 BindingKey 以通配符
(#:一个或多个词,*:一个词)的方式指定。ExchangeType 为 topic。
6.RPC:远程调用,不属于队列模型
交换器:Exchange
路由键:RoutingKey
生产者将消息发给交换器的时候,一般会指定一个 RoutingKey,用来指定这个消息的路由规则,而这个 RoutingKey 需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效。
在交换器类型和绑定键(BindingKey)固定的情况下,生产者可以在发送消息给交换器时,通过指定 RoutingKey 来决定消息流向哪里。
绑定:Binding
通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个绑定键(BindingKey),这样RabbitMQ 就知道如何正确地将消息路由到队列了。
总结:
交换器相当于投递包裹的邮箱,RoutingKey 相当于填写在包裹上的地址,BindingKey 相当于包裹的目的地,当填写在包裹上的地址和实际想要投递的地址相匹配时,那么这个包裹就会被正确投递到目的地,最后这个目的地的"主人"一一队列可以保留这个包裹。如果填写的地址出错,邮递员不能正确投递到目的地,包裹可能会回退给寄件人,也有可能被丢弃。在某些情形下,RoutingKey 与 BindingKey 可以看作同一个东西。
交换器类型
RabbitMQ 常用的交换器类型有 fanout 、 direct 、 topic 、 headers 这四种。
消息队列运转过程
生产消息:
(1)生产者连接到 RabbitMQ Broker,建立一个连接(Connection),开启一个信道(Channel)。
(2)生产者声明一个交换器,并设置相关属性,比如交换机类型、是否持久化等。
(3)生产者声明一个队列设置相关属性,比如是否排他、是否持久化、是否自动删除等。
(4)生产者通过路由键将交换器和队列绑定起来。
(5)生产者发送消息至 RabbitMQ Broker,其中包含路由键、交换器等信息。
(6)相应的交换器根据接收到的路由键查找相匹配的队列。
(7)如果找到,则将从生产者发送过来的消息存入相应的队列中。
(8)如果没有找到,则根据生产者配置的属性选择丢弃还是回退给生产者。
(9)关闭信道。
(10)关闭连接。
消费消息:
(1)消费者连接到 RabbitMQBroker,建立一个连接(Connection),开启一个信道(Channel)。
(2)消费者向 RabbitMQBroker 请求消费相应队列中的消息,可能会设置相应的回调函数,以及做一些准备工作。
(3)等待 RabbitMQBroker 回应并投递相应队列中的消息,消费者接收消息。
(4)消费者确认(ack)接收到的消息。
(5)RabbitMQ 从队列中删除相应己经被确认的消息。
(6)关闭信道。
(7)关闭连接。
4 入门demo
原生代码
-
引入依赖:spring-boot-starter-amqp
-
生产者代码
查看web管理器 -
消费者代码
结果:
Springboot写法 -
依赖:spring-boot-starter-amqp
-
配置
-
配置类
-
生产者
-
消费者
Springboot rabbitMQ传递对象
配置同上,生产者:
消费者:手动ACK(手动确认消息确认),实际开发必用
客户端开发相关说明
1 exchangeDeclare方法
exchangeDeclare 有多个重载方法,这些重载方法都是由下面这个方法中缺省的某些参
数构成的。
Exchange . DeclareOk exchangeDeclare(
String exchange ,
String type,
boolean durable,
boolean autoDelete ,
boolean internal ,
Map<String, Object> arguments) throws IOException ;
这个方法的返回值是 Exchange . DeclareOK , 用来标识成功声明了一个交换器。
各个参数详细说明如下: ⚫ exchange : 交换器的名称。
⚫ type : 交换器的类型,常见的如 fanout、 direct 、 topic
⚫ durable: 设置是否持久化 。 durable 设置为 true 表示持久化, 反之是非持久化 。
持久化可以将交换器存盘,在服务器重启的时候不会丢失相关信息。
⚫ autoDelete : 设置是否自动删除。 autoDelete 设置为 true 则表示自动删除。自动删除
的前提是至少有一个队列或者交换器与这个交换器绑定,之后所有与这个交换器绑定的
队列或者交换器都与此解绑。注意不能错误地把这个参数理解为 : "当与此交换器连接
的客户端都断开时,RabbitMQ 会自动删除本交换器 "。 ⚫ internal : 设置是否是内置的。如果设置为 true ,则表示是内置的交换器,客户端程
序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式。
⚫ argument : 其他一些结构化参数,比如 alternate-exchange
2 queueDeclare方法
Queue. DeclareOk queueDeclare (
String queue ,
boolean durable ,
boolean exclusive,
boolean autoDelete,
Map<String,Object> arguments) throws IOException;
方法的参数详细说明如下: ⚫ queue:队列的名称。
⚫ durable:设置是否持久化。为 true 则设置队列为持久化。持久化的队列会存盘,在服务
器重启的时候可以保证不丢失相关信息。
⚫ exclusive:设置是否排他。为 true 则设置队列为排他的。如果一个队列被声明为排他队
列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意三点:
排他队列是基于连接(Connection)可见的,同一个连接的不同信道(Channel)是可以同时
访问同一连接创建的排他队列;"首次"是指如果一个连接己经声明了一个排他队列,其他
连接是不允许建立同名的排他队列的,这个与普通队列不同:即使该队列是持久化的,
一旦连接关闭或者客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户
端同时发送和读取消息的应用场景。
⚫ autoDelete:设置是否自动删除。为 true 则设置队列为自动删除。自动删除的前提是:至
少有一个消费者连接到这个队列,之后所有与这个队列连接的消费者都断开时,才会自
动删除。不能把这个参数错误地理解为:“当连接到此队列的所有客户端断开时,这个队
列自动删除”,因为生产者客户端创建这个队列,或者没有消费者客户端与这个队列连
接时,都不会自动删除这个队列。
⚫ argurnents:设置队列的其他一些参数,如 x-rnessage-ttl、x-expires、x-rnax-length、xrnax-length-bytes、x-dead-letter-exchange、x-deadletter-routing-key,x-rnax-priority
等。
3 消费消息
⚫ queue : 队列的名称: ⚫ autoAck : 设置是否自动确认。建议设成 false ,即不自动确认
⚫ consumerTag: 消费者标签,用来区分多个消费者: ⚫ noLocal : 设置为 true 则表示不能将同一个 Connectio 口中生产者发送的消息传送给
这个 Connection 中的消费者
⚫ exclusive : 设置是否排他
⚫ arguments : 设置消费者的其他参数
⚫ callback : 设置消费者的回调函数。用来处理 RabbitMQ 推送过来的消息,比如
DefaultConsumer,使用时需要客户端重写 (override) 其中的方法。
4 消费的确认与拒绝
autoAck 等于 false时,RabbitMQ 会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)。消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为 RabbitMQ 会一直等待持有消息直到消费者显式调用 Basic.Ack 命令为止。
当 autoAck 等于 true 时,RabbitMQ 会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正地消费到了这些消息(不安全)。
5 消息的可靠性投递
配置:
RabbitTemplate配置
// 消息投递到exchange 无论成功与否都会回调这个函数
rabbitTemplate.setConfirmCallback(new RabbitTemplate,ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData,boolean ack,String msg) {
// 根据ack做判断
if(ack){
System.out.println("消息投递成功");
}else{
System.out.println("消息投递失败");
// 消息投递失败之后我们需要再次投递消息
// ...
// 可以从correlationData里面去获取消息的标记
// 如果消息存在数据库中,那么这个标记就是主键
// 如果消息存在redis中,这个标记就是key
}
}
}
// 再次通过标记去取出对应的消息数据
消息可靠性投递解决方案
Step1:业务数据入库
Step2:定时任务每次在队列中读取- t条业务数据(不弹出),并记录读取次数
Step3:将业务数据发送给生产者
Step4:生产者将数据发送给MQ
Step5:消息发送到Exchange成功
Step6:消息投递成功将业务数据从队列中弹出
Step7:消息路由到queue失败,将业务数据存放到异常队列
Step8:每条业务数据有3次重试机会(根据实际业务员来定),重试超过三次将数据转入异常队列
来源:CSDN
作者:莫小夕儿呀
链接:https://blog.csdn.net/weixin_45044097/article/details/103859324