RabbitMQ入门详解

server/2024/10/21 19:52:01/

前言

本篇文章将详细介绍rabbitmq的基本概念知识,以及rabbitmq各个工作模式在springboot中如何使用。

文章目录

介绍

简介

RabbitMQ 核心

生产者与消费者

Exchange

Queue

工作模式

简单模式

工作队列模式

发布订阅模式

路由模式

主题模式

SpringBoot中使用RabbitMQ

简单模式

基础配置

生产者

消费者

工作队列模式

发布/订阅模式(广播模式)

路由模式

主题模式


介绍

简介

RabbitMQ 是采用 Erlang 语言实现 AMQP(Advanced Message Queuing Protocol,高级消息队列协议)的消息中间件,它最初起源于金融系统,用于在分布式系统中存储转发消息。

RabbitMQ 发展到今天,被越来越多的人认可,这和它在易用性、扩展性、可靠性和高可用性等方面的卓著表现是分不开的。RabbitMQ 的具体特点可以概括为以下几点:

  • 可靠性: RabbitMQ使用一些机制来保证消息的可靠性,如持久化、传输确认及发布确认等。

  • 灵活的路由: 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能,RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个交换器绑定在一起,也可以通过插件机制来实现自己的交换器。这个后面会在我们将 RabbitMQ 核心概念的时候详细介绍到。

  • 扩展性: 多个RabbitMQ节点可以组成一个集群,也可以根据实际业务情况动态地扩展集群中节点。

  • 高可用性: 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队列仍然可用。

  • 支持多种协议: RabbitMQ 除了原生支持 AMQP 协议,还支持 STOMP、MQTT 等多种消息中间件协议。

  • 多语言客户端: RabbitMQ几乎支持所有常用语言,比如 Java、Python、Ruby、PHP、C#、JavaScript等。

  • 易用的管理界面: RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息、集群中的节点等。在安装 RabbitMQ 的时候会介绍到,安装好 RabbitMQ 就自带管理界面。

  • 插件机制: RabbitMQ 提供了许多插件,以实现从多方面进行扩展,当然也可以编写自己的插件。感觉这个有点类似 Dubbo 的 SPI机制。

RabbitMQ 核心

生产者与消费者

RabbitMQ 整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息。可以把消息传递的过程想象成:当你将一个包裹送到邮局,邮局会暂存并最终将邮件通过邮递员送到收件人的手上,RabbitMQ就好比由邮局、邮箱和邮递员组成的一个系统。

  • Producer(生产者) :生产消息的一方(邮件投递者)

  • Consumer(消费者) :消费消息的一方(邮件收件人)

消息一般由 2 部分组成:消息头(或者说是标签 Label)和 消息体。消息体也可以称为 payLoad ,消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括 routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。生产者把消息交由 RabbitMQ 后,RabbitMQ 会根据消息头把消息发送给感兴趣的 Consumer(消费者)。

Exchange

在 RabbitMQ 中,消息并不是直接被投递到Queue中的,中间还必须经过 Exchange(交换机) 这一层,Exchange 会把我们的消息分配到对应的Queue中。

Exchange用来接收生产者发送的消息并将这些消息路由给服务器中的队列中,如果路由不到,或许会返回给 Producer(生产者) ,或许会被直接丢弃掉 。这里可以将RabbitMQ中的交换器看作一个简单的实体。

RabbitMQ 的 Exchange(交换器) 有4种类型,不同的类型对应着不同的路由策略:direct(默认)fanout, topic, 和 headers,不同类型的Exchange转发消息的策略有所区别。

Queue

Queue(消息队列) 用来保存消息直到发送给消费者。RabbitMQ 中消息只能存储在队列中 ,它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。

多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理,这样避免的消息被重复消费。

工作模式

简单模式

RabbitMQ简单模式,也称为基本模式(Basic Model),是RabbitMQ的最简单的消息传递模式,仅涉及到一个生产者和一个消费者。

在这个模式中,当我们启动一个程序作为生产者并向RabbitMQ发出消息时,我们希望它直接进入队列中,然后消费者会从队列中获取这个消息并进行处理。

简单模式在RabbitMQ中是一个单队列单生产者单消费者的模式,主要适用于单纯的任务处理,消息的生产者和消费者的削峰填谷能力非常高。

工作队列模式

RabbitMQ工作队列模式,也称为Task Queues或Background Tasks,是一种常见的应用场景,它用于处理大量的任务,将任务进行排队,然后分发给多个消费者进行处理。这种模式适用于需要异步处理耗时的、密集型任务并且要求可靠性的情况。

RabbitMQ工作队列模式的基本原理是,将需要处理的任务投递到RabbitMQ中,生成任务队列(Task Queues),多个消费者通过拉取任务队列中的任务进行处理。

在RabbitMQ的工作队列模式中,队列中的每个消息都会分配给一个消费者进行处理。消费者可以是不同的进程、线程或服务,从而实现可扩展性和并行性。

在一个生产者-多个消费者的场景下,生产者只需要将消息发送到一个消息队列中,消费者会自动从队列中获取消息进行处理。如果存在多个消费者,队列中的消息将会被分摊给多个消费者进行处理,即实现了任务并行处理的功能。而且如果一个消费者挂掉,该消费者所占用的任务在一定的时间内不会被重新分配,即实现了任务可靠性处理的功能。

一般情况下,RabbitMQ的工作队列模式可以应用于以下场景:

  • 任务比较繁重,处理较慢
  • 任务多且耗时,无法同步处理
  • 需要执行一些必须的后台任务,如发送邮件、生成报表等

发布订阅模式

RabbitMQ发布/订阅模式,也叫做“广播(Broadcast)模式”,是RabbitMQ的一种高级消息传递模式,主要用于广播消息。

在发布/订阅模式中,消息发送到Exchange(交换机)上,并携带着一个Routing Key(路由键),Exchange将收到的消息转发到绑定在它上面的所有队列。每个绑定键(Binding Key)都与一个队列相关联,而队列和消息的接收者实现了完全解耦,接收者只需要订阅(subscribe)与该队列相关联的绑定键即可。

我们将它作为“广播”模式,因为可以将一条消息同时发送到多个消费者。例如,我们可以让多个消费者接收网站上发布的新闻消息。

发布/订阅模式在RabbitMQ中的架构非常简单,主要可以描述为以下四个步骤:

  • 生产者将消息发送到exchange中,并指定了Routing Key。
  • Exchange将消息分发到所有绑定它的队列上。
  • 消费者从队列中接收消息,并进行处理。
  • 消费者对队列进行确认操作,告诉RabbitMQ该消息已经被接收并处理。

路由模式

RabbitMQ路由模式是一种高级消息传递模式,它可以通过选择路由键(Routing Key)将消息推送到绑定键(Binding Key)与之匹配的队列中,以满足不同的消费者需要。

路由模式主要用于单一应用程序内的消息传递,生产者将消息发送到指定的Exchange(交换机)中,并且Exchange会根据Routing Key将消息放到绑定到Exchange上的队列中。而不同的消费者使用不同的Binding Key来决定与哪个队列建立联系并接收消息。

在RabbitMQ中,路由模式有以下几个步骤:

  • 生产者将消息发送到Exchange中,并指定了Routing Key。
  • Exchange将消息根据Routing Key发送到绑定到Exchange中的队列。
  • 消费者从队列中接收消息,并进行处理。
  • 消费者对队列进行确认操作,告诉RabbitMQ该消息已经被接收并处理。

主题模式

RabbitMQ主题模式(Topic Model)是一种高级消息传递模式,它使你可以订阅一个特定的主题(Topic)并接收所有与该主题相关的消息。主题模式是在发布/订阅模式基础上进一步增强了消息传递的粒度。

在主题模式中,Exchange不仅可以使用Routing Key来将消息传递到队列中,还可以使用一个模式字符串来匹配Routing Key,这个模式字符串被称为主题(Topic)。消费者可以通过订阅不同的主题来接收不同的消息。

一个主题可以包含一个或多个单词(Word),单词之间使用"."(点号)来分割。通配符符号“#”表示跟单词数不限,而“”则表示只匹配一个单词。

主题模式在RabbitMQ中的架构非常简单,主要可以描述为以下四个步骤:

  • 生产者将消息发送到Exchange中,并指定了Routing Key。
  • Exchange将消息根据匹配的主题字符串发送到绑定到Exchange中的队列。
  • 消费者从队列中接收消息,并进行处理。
  • 消费者对队列进行确认操作,告诉RabbitMQ该消息已经被接收并处理。

SpringBoot中使用RabbitMQ

在使用之前先要确保在电脑中已经安装了rabbitmq,并启动服务,启动成功后是可以在本地15672端口中访问到网页的。如图:

简单模式

基础配置

首先创建两个SpringBoot项目,一个作为消费者,一个作为生产者,并导入依赖项。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

 在两个项目的yaml文件中写入rabbitmq的配置信息

spring:rabbitmq:host: localhost # 主机名port: 5672 # 端口virtual-host: / # 虚拟主机username: guest # 用户名password: guest # 密码

rabbitmq中创建一个队列叫做queue1

生产者

在生产者中编写测试类,发送消息

java">import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class RabbitPublisherApplicationTests {@Autowiredprivate RabbitTemplate rabbitTemplate;@Testpublic void SimpleQueue(){//队列名字String name = "queue1";//具体的消息String message = "你好!!!rabbit";//发送消息rabbitTemplate.convertAndSend(name,message);}
}

执行测试类后在rabbitmq的相应队列中就会有消息了。

消费者

随后编写消费者来消费消息。

创建一个监听类,来监听具体的队列

java">@Component
public class SpringRabbitListener {/*** 从队列queue1中取消息* @param msg*/@RabbitListener(queues = "queue1")public void listenSimpleQueueMessage(String msg) {System.out.println("spring 消费者接收到消息:" + msg);}
}

随后启动消费者项目,就可以从队列中成功获取到消息了。

工作队列模式

编写方法发送20条消息

java">    @Testpublic void WorkQueue(){String name = "work.queue";for(int i=0;i<20;i++){String message = "第"+i+"消息";rabbitTemplate.convertAndSend(name,message);}}

用两个消费者来接收

java">    @RabbitListener(queues = "work.queue")public void listenWorkQueueMessage1(String msg) {System.out.println("第一位消费者接收到消息:" + msg);}@RabbitListener(queues = "work.queue")public void listenWorkQueueMessage2(String msg) {System.out.println("第二位消费者接收到消息:" + msg);}

先启动消费者再发送消息:

此时可以发现两个消费者将消息均匀分配,每位各接收10条消息,这里是因为mq有预分配,会先预取,每个消费者和队列中有一个通道,存放预取的消息,所以会平均分消息,然后各自独立消费。

但是假如我们的消费者消费能力不一样呢,rabbitmq也会将其平均分配,这样的机制显然是不合理的,因此面对这种情况时,可以通过控制预取数量来改变这种情况,我们可以在yml文件中编写配置。这样就实现了取一个消费一个,不会先预取很多个,如果消费能力强取得就快,消费能力弱,取得就慢。

java">spring:rabbitmq:host: localhost # 主机名port: 5672 # 端口virtual-host: / # 虚拟主机username: guest # 用户名password: guest # 密码listener:simple:prefetch: 1

发布/订阅模式(广播模式)

广播模式使用fanout交换机

首先编写一个配置类,配置交换机和队列

java">@Configuration
public class FanoutConfig {//声明交换机,设置名称@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange("exchange1");}//队列1@Beanpublic Queue fanoutQueue1() {return new Queue("fanout1",true);}//绑定队列1和交换机@Beanpublic Binding binding1() {return BindingBuilder.bind(fanoutQueue1()).to(fanoutExchange());}//队列2@Beanpublic Queue fanoutQueue2() {return new Queue("fanout2",true);}//绑定交换机和队列2@Beanpublic Binding bindingQueue2() {return BindingBuilder.bind(fanoutQueue2()).to(fanoutExchange());}
}

发送消息

java">    @Testpublic void fanout(){String name = "exchange1";String meg = "这里时fanout";rabbitTemplate.convertAndSend(name,"",meg);}

接收消息

java">    @RabbitListener(queues = "fanout1")public void listenWorkQueueMessage1(String msg) {System.out.println("第一位消费者接收到消息:" + msg);}@RabbitListener(queues = "fanout2")public void listenWorkQueueMessage2(String msg) {System.out.println("第二位消费者接收到消息:" + msg);}

结果我们可以看到两个消费者都接收到了消息。

路由模式

路由模式使用direct交换机,除了使用配置类的方式进行队列和交换机的绑定,还可以在消费者中使用注解的方式,例如

java">    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue1"),exchange = @Exchange(name="direct",type = ExchangeTypes.DIRECT),key = {"one","two"}))public void directListener1(String msg){System.out.println("第一位消费者接收到来自direct.queue1的消息:"+ msg);}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "direct.queue2"),exchange = @Exchange(name="direct",type = ExchangeTypes.DIRECT),key = {"three"}))public void directListener2(String msg){System.out.println("第二位消费者接收到来自direct.queue2的消息:"+ msg);}

发送消息

java">    @Testpublic void directTest(){// 交换机名称String exchangeName = "direct";// 消息String message = "第 one 消息";// 发送消息rabbitTemplate.convertAndSend(exchangeName, "one", message);// 消息String message2 = "第 two 消息";// 发送消息rabbitTemplate.convertAndSend(exchangeName, "two", message2);// 消息String message3 = "第 three 消息";// 发送消息rabbitTemplate.convertAndSend(exchangeName, "three", message3);}

最后可以看到,根据key路由接收到了消息。

主题模式

主题模式和路由模式类似,使用topic交换机,只是可以在key中使用通配符,

主题模式中RoutingKey一般由一个或多个单词组成,用“.”分割。

通配符规则

# 匹配一个或多个词

* 匹配一个词

修改消费者配置

java">    @RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue1"),exchange = @Exchange(name="topic",type = ExchangeTypes.TOPIC),key = {"a.*"}))public void topicListener1(String msg){System.out.println("第一位消费者接收到来自topic.queue1的消息:"+ msg);}@RabbitListener(bindings = @QueueBinding(value = @Queue(name = "topic.queue2"),exchange = @Exchange(name="topic",type = ExchangeTypes.TOPIC),key = {"a.#"}))public void topocListener2(String msg){System.out.println("第二位消费者接收到来自topic.queue2的消息:"+ msg);}

发送消息

java">    @Testpublic void topicTest(){// 交换机名称String exchangeName = "topic";String message = "消息:a.b";rabbitTemplate.convertAndSend(exchangeName, "a.b", message);String message2 = "消息:a.b.c";rabbitTemplate.convertAndSend(exchangeName, "a.b.c", message2);String message3 = "消息:a.b.c.d";rabbitTemplate.convertAndSend(exchangeName, "a.b.c.d", message3);}

最后结果可以看到,第二个消费者可以拿到所有消息,第一个消费者只能拿到a.b的消息


http://www.ppmy.cn/server/90162.html

相关文章

FFmpeg解复用器如何从封装格式中解析出不同的音视频数据

目录 1、ffmpeg介绍 2、FFMPEG的目录结构 3、FFmpeg的格式封装与分离 3.1、数据结构 3.2、封装和分离(muxer和demuxer) 3.2.1、Demuxer流程 3.2.2、Muxer流程 4、总结 4.1、播放器 4.2、转码器 C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续…

Linux定时同步系统时间到硬件时间

Linux定时同步系统时间到硬件时间 1. 系统时间、软件时间 系统时间 &#xff08;System Time&#xff09;&#xff1a; 一般说来就是我们执行 date命令看到的时间&#xff0c;linux系统下所有的时间调 用&#xff08;除了直接访问硬件时间的命令&#xff09;都是使用的这个时…

LeetCode //C - 257. Binary Tree Paths

257. Binary Tree Paths Given the root of a binary tree, return all root-to-leaf paths in any order. A leaf is a node with no children. Example 1: Input: root [1,2,3,null,5] Output: [“1->2->5”,“1->3”] Example 2: Input: root [1] Output: […

昇思MindSpore 应用学习-GAN图像生成-CSDN

模型简介 生成式对抗网络(Generative Adversarial Networks&#xff0c;GAN)是一种生成式机器学习模型&#xff0c;是近年来复杂分布上无监督学习最具前景的方法之一。 最初&#xff0c;GAN由Ian J. Goodfellow于2014年发明&#xff0c;并在论文Generative Adversarial Nets中…

docker常用命令总结

Docker 的常用命令可以大致分为几大类&#xff1a;启动类、镜像类、容器类、网络类以及其他一些辅助命令。以下是对这些常用命令的详细归纳&#xff1a; 一、启动类命令 启动 Docker&#xff1a;systemctl start docker关闭 Docker&#xff1a;systemctl stop docker重启 Doc…

Redisson分布式锁使用详解

引言 日常开发中&#xff0c;难免遇到一些并发的场景&#xff0c;为了保证接口执行的一致性&#xff0c;通常采用加锁的方式&#xff0c;因为服务是分布式部署模式&#xff0c;本地锁Reentrantlock和Synchnorized这些就先放到一边了&#xff0c;Redis的setnx锁存在无法抱保证原…

Spring、SpringMVC、SpringBoot之间有什么关系?

Spring、SpringMVC、SpringBoot之间有什么关系&#xff1f; Spring通常是指Spring框架&#xff08;SpringFramework&#xff09;是一款开源的轻量级的JavaEE开发框架&#xff0c;旨在简化Java项目的开发。 SpringFramework中包含很多模块&#xff0c;包括IOC控制反转、AOP面向…

MySQL4.索引及视图

1.建库 create database mydb15_indexstu; use mydb15_indexstu;2.建表 2.1 student表学&#xff08;sno&#xff09;号为主键&#xff0c;姓名&#xff08;sname&#xff09;不能重名&#xff0c;性别&#xff08;ssex&#xff09;仅能输入男或女&#xff0c;默认所在系别&a…