rabbitmq学习记录
一、RabbitMQ的概念
RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ 是一个快递站,一个快递员帮你传递快件。RabbitMQ 与快递站的主要区别在于:它不处理快件而是接收,存储和转发消息数据。
二、四大核心概念
生产者:产生数据发送消息的程序是生产者。
交换机:交换机是 RabbitMQ 非常重要的一个部件,一方面它接收来自生产者的消息,另一方面它将消息推送到队列中。交换机必须确切知道如何处理它接收到的消息,是将这些消息推送到特定队列还是推送到多个队列,亦或者是把消息丢弃,这个是由交换机类型决定的。
队列:队列是 RabbitMQ 内部使用的一种数据结构,尽管消息流经 RabbitMQ 和应用程序,但它们只能存储在队列中。队列仅受主机的内存和磁盘限制的约束,本质上是一个大的消息缓冲区。许多生产者可以将消息发送到一个队列,许多消费者可以尝试从一个队列接收数据。
消费者:消费与接收具有相似的含义。消费者大多时候是一个等待接收消息的程序。请注意生产者,消费者和消息中间件很多时候并不在同一机器上。同一个应用程序既可以是生产者又是可以是消费者。
三、可视化页面解读
1、用户操作
- 默认会提供一个默认用户guest,密码也是guest,线上环境需要创建一个新用户,并把guest用户删除。
- 首先切换到Admin标签页,可以查看或添加用户,添加用户时,可指定Tags,相当于角色,会拥有对应的权限:
部分权限解释:
none:不能访问 management plugin
management:
- 查看自己相关节点信息
- 列出自己可以通过AMQP登入的虚拟机
- 查看自己的虚拟机节点virtual hosts的queues,exchanges和bindings信息
- 查看和关闭自己的channels和connections
- 查看有关自己的虚拟机节点virtual hosts的统计信息。包括其他用户在这个节点virtual hosts中的活动信息
Policymaker
- 包含management所有权跟
- 查看和创建和删除自己的virtual hosts所属的policies和parameters信息
Monitoring
- 包含management所有权限
- 罗列出所有的virtual hosts,包括不能登录的virtual hosts
- 查看其他用户的connections和channels信息
- 查看节点级别的数据如clustering和memory使用情况
- 查看所有的virtual hosts的全局统计信息。
Administrator
- 最高权限
- 可以创建和删除 virtual hosts
- 可以查看,创建和删除users
- 查看创建permissions
- 关闭所有用户的connections
2、交换机操作
切换到“Exchanges”标签,可查看和管理交换器,单击交换器名称,可查看到更多详细信息,比如交换器绑定,还可以添加新的绑定
3、队列
切换到“Queues”标签,可以查看队列信息,点击队列名称,可查看队列所有状态的消息数量和大小等统计信息:
四、体验RabbitMQ
1、因为我用的是SpringBoot,所以在生产者这边加入对应的依赖即可:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>
2、包结构(个人):
3、在application.yml文件加上RabbitMQ的配置信息:
spring: rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest #这个配置是保证提供者确保消息推送到交换机中,不管成不成功,都会回调 publisher-confirm-type: correlated #保证交换机能把消息推送到队列中 publisher-returns: true virtual-host: /
4、创建一个Direct交换机以及队列的配置类:
import org.springframework.amqp.core.*; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @author wjx */ @Configuration public class RabbitMqConfig { //队列1 @Bean public Queue Queue1() { /** * 1、name: 队列名称 * 2、durable: 是否持久化 * 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。 * 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。 * */ return new Queue("Queue1", true,true,false); } //队列2 @Bean public Queue Queue2() { /** * 1、name: 队列名称 * 2、durable: 是否持久化 * 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。 * 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。 * */ return new Queue("Queue2", true,true,false); } //DirectExchange交换机 @Bean public DirectExchange DirectExchange() { /** * 1、name: 交换机名称 * 2、durable: 是否持久化 * 3、autoDelete: 是否自动删除。也就是临时交换机。当最后一个消费者断开连接后,会自动删除。 * */ return new DirectExchange("DirectExchange",true,false); } //FanoutExchange交换机 @Bean public FanoutExchange FanoutExchange() { /** * 1、name: 交换机名称 * 2、durable: 是否持久化 * 3、autoDelete: 是否自动删除。也就是临时交换机。当最后一个消费者断开连接后,会自动删除。 * */ return new FanoutExchange("FanoutExchange",true,false); } //TopicExchange交换机 @Bean public TopicExchange TopicExchange() { /** * 1、name: 交换机名称 * 2、durable: 是否持久化 * 3、autoDelete: 是否自动删除。也就是临时交换机。当最后一个消费者断开连接后,会自动删除。 * */ return new TopicExchange("TopicExchange",true,false); }
//---测试DirectExChange交换机 //绑定交换机、队列、路由key关系 @Bean public Binding Binding1() { return BindingBuilder .bind(Queue1()) .to(DirectExchange()) .with("routingKey1"); } //绑定交换机、队列、路由key关系 @Bean public Binding Binding2() { return BindingBuilder .bind(Queue2()) .to(DirectExchange()) .with("routingKey2"); } //---测试FanoutExChange交换机 //绑定交换机、队列、路由key关系 // @Bean // public Binding Binding1() { // return BindingBuilder // .bind(Queue1()) // .to(FanoutExChange()); // } // //绑定交换机、队列、路由key关系 // @Bean // public Binding Binding2() { // return BindingBuilder // .bind(Queue2()) // .to(FanoutExChange()); // } //----测试TopicExChange类型交换机 //绑定交换机、队列、路由key关系 // @Bean // public Binding Binding1() { // return BindingBuilder // .bind(Queue1()) // .to(TopicExchange()) // .with("*.orange.*"); // } //绑定交换机、队列、路由key关系 // @Bean // public Binding Binding2() { // return BindingBuilder // .bind(Queue2()) // .to(TopicExchange()) // .with("*.*.rabbit"); // } //绑定交换机、队列、路由key关系 // @Bean // public Binding Binding3() { // return BindingBuilder // .bind(Queue2()) // .to(TopicExchange()) // .with("lazy.#"); // } }
5、创建一个发送消息的VO类:
@Data public class MsgSendVO implements Serializable { private static final long serialVersionUID = 5905249092659173678L; private String priKey; private String businessType; private String phoneNum; private String msg; }
6、创建一个发送消息的生产者类:
/** * @author wjx */ @Slf4j @Service public class MsgProducer { @Resource private RabbitTemplate rabbitTemplate; //测试Direct public void sendDirectExchange(MsgSendVO msgSendVO) { log.info("生产消息【{}】"+msgSendVO); rabbitTemplate.convertAndSend("DirectExchange", "routingKey1", msgSendVO); rabbitTemplate.convertAndSend("DirectExchange", "routingKey2", msgSendVO); } //Fanout public void sendFanoutExchange(MsgSendVO msgSendVO) { log.info("生产消息【{}】"+msgSendVO); rabbitTemplate.convertAndSend("FanoutExchange", null,msgSendVO); } //Topic public void sendTopicExchange(MsgSendVO msgSendVO) { log.info("生产消息【{}】"+msgSendVO); rabbitTemplate.convertAndSend("TopicExchange", "quick.orange.rabbit", msgSendVO); } }
7、创建一个消费消息的消费者类:
/** * @author 71561 */ @Component @Slf4j public class MsgConsumer {
//监听消息队列Queue1 @RabbitListener(queues = {"Queue1"}) public void msgSend(MsgSendVO vo) { System.out.println("消费者1收到消息:"+vo); }
//监听消息队列Queue2 @RabbitListener(queues = {"Queue2"}) public void getMag(MsgSendVO vo) { System.out.println("消费者2收到消息:"+vo); } }
8、然后根据业务放在需要用的地方,比如定时任务,或者接口。我这里就简单一点使用Controller层进行发送:
/** * @author 71561 */ @RestController @RequestMapping("/mq") public class TestMqController { @Resource private MsgProducer notifyMsgProducer; @GetMapping("/produce") public String produce() { MsgSendVO vo = new MsgSendVO(); vo.setPriKey(UUID.randomUUID().toString()); vo.setPhoneNum("191xxxxxxxx"); vo.setBusinessType("msg_send"); vo.setMsg("大帅哥"); //测试Direct notifyMsgProducer.sendDirectExchange(vo); //Fanout // notifyMsgProducer.sendFanoutExchange(vo); //Topic // notifyMsgProducer.sendTopicExchange(vo); return "success"; } }
五、认识交换机
1 Exchanges概念
RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上,通常生产 者甚至都不知道这些消息传递传递到了哪些队列中。
相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来自生产者的消息,另一方面将它们推入队列。交换机必须确切知道如何处理收到的消息。是应该把这些消息放到特定队列还是说把他们到许多队列中还是说应该丢弃它们。这就的由交换机的类型来决定。
2 Exchanges的类型
Exchanges总共有以下类型
- 直接(direct)
- 主题(topic)
- 标题(header)
- 扇出(fanout)
3 、绑定(bindings)
什么是 bingding 呢,binding 其实是 exchange 和 queue 之间的桥梁,它告诉我们 exchange 和那个队 列进行了绑定关系。比如说下面这张图告诉我们的就是 X 与 Q1 和 Q2 进行了绑定
4、Direct类型队列介绍
bindings中绑定是交换机和队列之间的桥梁关系。也可以理解为: 队列只对它绑定的交换机的消息感兴趣。绑定用参数:routingKey 来表示也可称该参数为 binding key
5、Fanout类型队列介绍
Fanout 类型。它是将接收到的所有消息广播到它知道的 所有队列中。系统中默认有些exchange 类型
6、Topic类型队列介绍
发送到类型是 topic 交换机的消息的 routing_key 不能随意写,必须满足一定的要求,它必须是一个单词列表,以点号分隔开。这些单词可以是任意单词,比如说:“stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”.这种类型的。当然这个单词列表最多不能超过 255 个字节。
在这个规则列表中,其中有两个替换符是大家需要注意的
*(星号)可以代替一个单词
#(井号)可以替代零个或多个单词
如上图所示
Q1队列绑定的是* .orange. * ,表示中间带 orange 带 3 个单词的字符串( * .orange. )
Q2队列绑定的是*. *.rabbit和lazy.#
*最后一个单词是 rabbit 的 3 个单词(*. .rabbit)
六、小知识
Feign和MQ的区别
- feign采用同步调用方式,具有时效性强等优点,但是性能低、吞吐量下降、耦合度高容易导致级联失败等缺点
- MQ采用异步调用方式,具有性能高、吞吐量高、解耦合、流量削峰、故障隔离等优点;但是也存在架构复杂、业务没有明显流程线、跟踪管理困难、强烈依靠Broker的可靠性等缺点
在微服务项目中,假如有两个微服务项目 查询价格 和 修改价格, 修改了价格之后,需要在查询库中同步更新价格. feign是同步的,所以会增加等待时间. rabbitmq是异步通信,这就是它们的主要区别.
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
· 上周热点回顾(2.17-2.23)