快速入门RabbitMQ并且加入项目实战

【RabbitMQ】RabbitMQ快速入门 通俗易懂 初学者入门 【推荐】RabbitMQ快速入门 1小时上手学会RabbitMQ 阅读详情

📣 📣 📣 📢📢📢
☀️☀️你好啊!小伙伴,我是小冷。是一个兴趣驱动自学练习两年半的的Java工程师。
📒 一位十分喜欢将知识分享出来的Java博主⭐️⭐️⭐️,擅长使用Java技术开发web项目和工具
📒 文章内容丰富:覆盖大部分java必学技术栈,前端,计算机基础,容器等方面的文章
📒 如果你也对Java感兴趣,关注小冷吧,一起探索Java技术的生态与进步,一起讨论Java技术的使用与学习
✏️高质量技术专栏专栏链接: 微服务,数据结构,netty,单点登录,SSM ,SpringCloudAlibaba等
😝公众号😝 : 想全栈的小冷,分享一些技术上的文章,以及解决问题的经验
⏩当前专栏:RabbitMQ系列

文章目录

一、MQ概述

精短简介

Java中有队列数据结构,但是是基于内存的,只有本JVM可以使用

MQ中间件是一个多客户端节点可以操作的队列结构

简单案例

异步消息处理(注册)

1639403611783

发送邮件、发送注册短信使用异步消息的模式,使得注册操作快速响应

应用解耦(订单_库存)

1639403799134

扣减库存接口可能会根据库存系统的升级而升级,而不得不导致订单系统也需要升级(重新部署)

使用消息队列后,库存系统订阅队列即可,无需关心库存系统接口升级的问题

流量控制

1639403877743

高并发场景下(秒杀),将请求存入mq,后台系统按照自己的处理能力来消费任务,达到削峰的目的

概述

1639404125733

消息代理(message broker)

消息代理:指安装了消息中间件的服务器,用于接收消息和发送消息

目的地(destination)

通俗解释:消息代理接收到消息后会将消息继续发给目的地(生产者发送消息)
目的地主要有两种形式:队列、主题
队列(queue)【单播_点对点消息通信】
点对点消息通信(point-to-point)
1.消息发送者发送消息,消息代理将其放入一个队列中,消息接受者从队列中获取消息内容,消息读取后被移出队列
2.队列可以被多个消费者监听,但一条消息只会被一个消费者成功消费
主题(topic)【广播_发布/订阅】
发布(publish)/订阅(subscribe)消息通信
1.发送者发送消息到主题,多个订阅者订阅该主题,多个消费者会同时收到消息

两种规范

JMS(JAVA消息服务)

JMS:(Java Message Service)
JAVA消息服务,基于JVM信息代理的规范。ActiveMQ、HornetMQ是JMS实现

AMQP(高级消息队列协议)

AMQP:(Advanced Message Queuing Protocol)
高级消息队列协议,也是一个消息代理的规范,兼容JMS
RabbitMQ是AMQP的实现

基于以上两种规范的分析

支持消息类型:byte[]=》只要能支持byte[]就可以传输,例如将对象转换为json,然后转二进制流传输即可

五种消息模型:重要,direct exchange其实就是队列
            后四种就是主题的变种

1639405192579

Spring支持与SpringBoot自动装配

1639405624744

使用@EnableRabbit开启自动配置

二、RabbitMQ安装与测试

文档:https://www.rabbitmq.com/networking.html

docker安装

4369,25672(Erlang发现&集群端口)
5672,5671(AMQP端口)
15672(web管理后台端口)
61613,61614(STOMP协议端口)
1883,8883(MQTT协议端口)
https://www.rabbitmq.com/networking.html 

1.下载镜像并启动
docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management

2.修改为自动重启
docker update rabbitmq --restart=always

3.登录管理页面
http://192.168.56.10:15672/
guest
guest

RabbitMQ运行机制

1639486954176

消息路由名词解释

1.新增了Exchange和Binding角色
2.消息路由:消息到达Exchange,并根据Binding关系发布到Queue队列中的过程,被称为消息路由

Exchange类型

direct
fanout
topic
1.Exchange名词解释:是AMQP高级消息队列协议的划分

2.RabbitMQ有四种Exchange类型:
	2.1.点对点通信
		2.1.1.direct
			解析:直接交换机【单播模式,按照路由关系精确匹配】
			例:路由键是dog,direct Exchange绑定了两个队列,队列1:dog、队列2:dogTom
			使用direct Exchange只会把消息发送给队列1
		2.1.2.headers【性能比较低,不会用到,与direct几乎一致】

	2.2.发布/订阅:
		2.2.1.fanout
			解析:扇形【广播模式,不关心路由键】
			例:消息会发送给fanout Exchange绑定的所有队列
		2.2.2.topic
			解析:主题【部分广播,区分路由键】
			例:binding.key=usa.* :*处必须有一个单词
               binding.key=#.news:#处有0个或多个单词
			注:不能匹配字母,只能匹配单词

3.

direct案例:

1639487844649

fanout案例:

1639487913417

Topic案例:

1639487923953

测试案例

测试案例1

http://192.168.56.10:15672/#/exchanges

1.新建交换机
Exchanges=》Add a new Exchange=》Add Exchange

2.新建队列
Queues=》Add a new Queue=》Add Queue

3.在交换机表格中点击新建的交换机,绑定刚刚新建的队列
Bindings=》Add binding from this exchange=》To queue=》Bind

4.发送消息
=》publishe Message

5.获取消息
Nack message requeue true
Automatic ack
Reject requeue true
Reject requeue false

6.解除binding
=》unbind
1.新建交换机:

1639488692329

2.新建队列:

1639488927909

3.在交换机表格中点击新建的交换机:

1639488787531

1639489151587

4.发送消息

1639490481854

消息发送成功:

1639490629311

5.获取消息
Nack message requeue true
不回复消息,并且消息重新入队
Automatic ack
自动回复消息,消息不重新入队
Reject requeue true

Reject requeue false

6.解除binding

1639489487003

测试案例2

1639489298287

根据上表
1.建立交换机:
	exchange.direct【点对点】
	exchange.fanout【广播,消息会发送给binding的多个队列中】
	exchange.topic【广播,会先找到存在binding关系的队列,然后按照binding关系的路由规则与路由键进行模糊匹配】

2.建立队列:
	atguigu
	atguigu.news
	atguigu.emps
	gulixueyuan.news

3.创建binding关系
	exchange.direct绑定所有队列【绑定规则是根据路由键与路由精确绑定决定消息进入哪个队列】
	exchange.fanout绑定所有队列【消息会发送给每个队列】
	exchange.topic绑定所有队列【并设置不同的路由键atguigu.#、*.news】

4.发送消息

三、RabbitMQ概念

1639407551973

1.简介

1639405993748

1639406310372

1639406273053

1.1.Message消息

每条消息都需要在消息头中指定route-key

1.2.publisher生产者

1.3.Exchange交换器

Exchange:交换器,每一个交换器都连接一个队列(可以看作局域网中的交换机的端口,每一个端口都连接一台电脑)

1.4.Queue队列

Queue队列:用于存储生产者发送的消息

1.5.Binding绑定

交换器与Queue之间存在绑定关系,一个交换器可以绑定多个队列,存在多种绑定关系
根据消息头中的路由键指定的绑定关系发送到匹配的队列中

1.6.Connection连接

网络连接
	生产者与Broker、消费者与Broker通过连接传输消息
	一个客户端只会建立一条连接

1.7.Channel通道

一个客户端建立一条连接,一条连接内存储多个通道用于监听不同队列

1.8.Virtual Host虚拟主机

只需要安装一个rabbitmq,但是可以分离出多个微主机,互相之间配置隔离,使用不同的url访问

1.例如dev、test可以使用不同VHost
2.不同的系统使用不同的VHost

2.消息确认(可靠消息)

1639582496156

2.1.可靠抵达_发送端确认

简介:
	1.可靠抵达:消息可靠抵达MQ,消息可靠抵达消费者
	2.事务消息:性能下降250倍,所以采用确认机制代替

文档:https://www.rabbitmq.com/confirms.html#publisher-confirms
Reliable Delivery=》Acknowledgements and Confirms=>Publisher confirms
confirmCallback回调

1639583499327

简介:
	1.生产者发送消息到Queue会经过两个两个过程【确认机制看做一种协议】
		1)消息从publisher到达Broker(到达后会回调confirmCallback,消费者被告知消息是否抵达服务器)
			【集群情况下必须所有的broker接收到才会调用confirmCallback】
		2)消息从Exchange投递到Queue(失败后会回调returnCallback,消费者被告知消息是否抵达Queue)
# 开启发送端确认:
spring.rabbitmq.publisher-returns=true
# 消息在没有被队列接收时是否强行退回
spring.rabbitmq.template.mandatory=true
// 测试步骤,调用单元测试中发送消息的方法,触发回调

@Configuration
public class MyRabbitConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Bean
    public MessageConverter messageConverter() {
        // 使用json序列化器来序列化消息,发送消息时,消息对象会被序列化成json格式
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     * 1、服务收到消息就会回调
     * 1、spring.rabbitmq.publisher-confirms: true
     * 2、设置确认回调
     * 2、消息正确抵达队列就会进行回调
     * 1、spring.rabbitmq.publisher-returns: true
     * spring.rabbitmq.template.mandatory: true
     * 2、设置确认回调ReturnCallback
     * <p>
     * 3、消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)
     */
    @PostConstruct   // (MyRabbitConfig对象创建完成以后,执行这个方法)
    public void initRabbitTemplate() {

        /**
         * 发送消息触发confirmCallback回调
         * @param correlationData:当前消息的唯一关联数据(如果发送消息时未指定此值,则回调时返回null)
         * @param ack:消息是否成功收到(ack=true,消息抵达Broker)
         * @param cause:失败的原因
         */
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            System.out.println("发送消息触发confirmCallback回调" +
                    "\ncorrelationData ===> " + correlationData +
                    "\nack ===> " + ack + "" +
                    "\ncause ===> " + cause);
        });
    }
}
returnCallback回调
简介:
	1.生产者发送消息到Queue会经过两个两个过程【确认机制看做一种协议】
		1)消息从publisher到达Broker(到达后会回调confirmCallback,消费者被告知消息是否抵达服务器)
		2)消息从Exchange投递到Queue(失败后会回调returnCallback,消费者被告知消息是否抵达Queue)
# 开启发送端抵达队列确认
spring.rabbitmq.publisher-returns=true
@Configuration
public class MyRabbitConfig {

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Bean
    public MessageConverter messageConverter() {
        // 使用json序列化器来序列化消息,发送消息时,消息对象会被序列化成json格式
        return new Jackson2JsonMessageConverter();
    }

    /**
     * 定制RabbitTemplate
     * 1、服务收到消息就会回调
     * 1、spring.rabbitmq.publisher-confirms: true
     * 2、设置确认回调
     * 2、消息正确抵达队列就会进行回调
     * 1、spring.rabbitmq.publisher-returns: true
     * spring.rabbitmq.template.mandatory: true
     * 2、设置确认回调ReturnCallback
     * <p>
     * 3、消费端确认(保证每个消息都被正确消费,此时才可以broker删除这个消息)
     */
    @PostConstruct   // (MyRabbitConfig对象创建完成以后,执行这个方法)
    public void initRabbitTemplate() {

        /**
         * 消息未到达队列触发returnCallback回调
         * 只要消息没有投递给指定的队列,就触发这个失败回调
         * @param message:投递失败的消息详细信息
         * @param replyCode:回复的状态码
         * @param replyText:回复的文本内容
         * @param exchange:接收消息的交换机
         * @param routingKey:接收消息的路由键
         */
        rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
            // 需要修改数据库 消息的状态【后期定期重发消息】
            System.out.println("消息未到达队列触发returnCallback回调" +
                    "\nmessage ===> " + message +
                    "\nreplyCode ===> " + replyCode + 
                    "\nreplyText ===> " + replyText +
                    "\nexchange ===> " + exchange + 
                    "\nroutingKey ===> " + routingKey);
        });
    }
}

2.2.可靠接收_消费端确认

ack机制

1639904298481

简介:
	1.消费者接收消息也会经过ack消息确认机制,只有消费者成功接收消息,broker才允许删除消息
	2.默认情况下消息抵达客户端后自动确认,服务端消息自动删除
	3.手动确认模式下,消费者接收消息后但是不执行ack/nack进行确认,服务端队列中的消息会从unacked状态变为ready状态等待下一次消费(即使consumer宕机消息也不会丢失)
	4.注意:消息到达消费端,消息会进入unacked状态,如果手动确认模式下,消费者因宕机而未执行ack/nack,消息会一直处于unacked状态不会被删除(持久化了)

	5.签收,ack
# 消费者手动确认模式,关闭自动确认,否则会消息丢失
spring.rabbitmq.listener.simple.acknowledge-mode=manual
@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {

	@RabbitHandler
    public void revieveMessage(Message message, OrderEntity entity, Channel channel) {
        // 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
        byte[] body = message.getBody();
        // 请求头
        MessageProperties properties = message.getMessageProperties();
        // channel内按顺序自增的long类型消息标签
        long deliveryTag = properties.getDeliveryTag();
        // JSON反序列得到消息内容对象
        OrderEntity reason = JSONObject.parseObject(body, OrderEntity.class);
        System.out.println("接受到的消息对象" + message);
        System.out.println("接受到的消息内容" + reason);
        System.out.println("接受到的消息内容" + entity);
        try {
            if (deliveryTag == 2) {
                // 手动确认,消息会从unacked中删除,total数量减1
                // boolean multiple:是否批量签收
                channel.basicAck(deliveryTag, false);
            } else {
                // 手动拒签
                // boolean multiple:是否批量拒签
                // boolean requeue:当前拒签消息是否发回服务器重新入队
                channel.basicNack(deliveryTag, false, true);
            }
        } catch (IOException e) {
            // 网络中断
            e.printStackTrace();
        }
    }
}

2.3.本地事务表

3.延时队列(柔性事务)

3.1.使用场景

场景:
	未付款订单,超时后,系统自动取消订单并释放占有物品

常用解决方案:
	Spring的Schedule定时任务轮询数据库
缺点:
	消耗系统内存、增加数据库的压力、存在较大的时间误差

解决:
	在保证可靠消息的前提下,使用延时队列+死信队列,达到最终一致性(柔性事务)

3.2.TTL和死信Exchange

TTL

TTL(Time To Live)

  • 消息的TTL就是消息的存活时间
  • RabbitMQ中对队列、消息都可以设置TTL
    • 对队列设置TTL,就是队列没有消费者连着的保留时间;对消息设置TTL,超过了这个时间,消息就死了,称之为死信。
    • 如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是—样的效果。
死信Exchange
  • 可以进入死信路由的情况
    • 被consumer拒收的消息,并且reject方法的参数里requeue是false(不会重新入队)
    • TTL过期的消息
    • 队列消息满了,排在前面的消息会被丢弃或进入死信路由
死信Exchange其实就是普通的Exchange
队列设置好自己的Dead Letter Exchange,当此队列中的消息过期后会被转发到这个路由,被称为死信路由
延时队列
TTL消息 + 死信Exchange
使用一个队列接收死信Exchange中的TTL消息,这样的队列被称为延时队列

注意:存放TTL消息的队列不要让客户端监听(这个队列和延时队列不是同一个,延时队列是存储已经超时的TTL消息)

3.3.延时队列实现方案

  • 实现1:给队列设置TTL

    • 采用方案1,因为队列是先进先出的。如果存在一个消息已经超时但是队首的消息未超时,已超时的消息无法出队
arguments.put("x-dead-letter-exchange", "order-event-exchange");// 死信路由
arguments.put("x-dead-letter-routing-key", "order.release.order");// 死信路由键
arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
  • 实现2:给消息设置TTL
    QQ图片20220102211522
    QQ图片20220102213900

4.可靠消息(柔性事务)

场景:
	配合延时队列+死信队列实现最终一致性(柔性事务)

创建mq_message表

CREATE TABLE `mq_message` (
  `message_id` char(32) NOT NULL,
  `content` json,
  `to_exchane` varchar(255) DEFAULT NULL,
  `routing_key` varchar(255) DEFAULT NULL,
  `class_type` varchar(255) DEFAULT NULL,
  `message_status` int(1) DEFAULT '0' COMMENT '0-新建 1-已发送 2-错误抵达 3-已抵达',
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

4.1.消息丢失

QQ图片20220103220617.jpg

前提:
    使用定时器扫描mq_message定时重发

情况1:网络连接失败,消息未抵达Broker
    解决:发送消息时同时将消息持久化到MQ中并设定状态为已抵达
         当出现异常时在catch处修改消息状态为错误抵达

情况2:消息抵达Broker,但为抵达queue,消息会丢失(只有抵达了queue消息才会持久化)
	解决:开启生产者确认机制,将触发returnCallback的returnedMessage的消息状态修改为错误抵达

情况3:消费者未ack时宕机,导致消息丢失
	解决:开启消费者手动ack

4.2.消息重复

QQ图片20220103231446.png

情况1:业务逻辑已经执行,但是ack时宕机,消息由unack变为ready,消息重新入队
  解决:将接口设计成幂等性,例如库存解锁时判断工作单的状态,已解锁则无操作
  解决2:防重表

4.3.消息积压

情况1:生产者流量太大
  解决:减慢发送消息速率(验证码、防刷、重定向、削峰)
  
情况2:消费者能力不足或宕机
  解决:上线更多消费者
  解决2:上线专门的队列消费服务,批量取出消息入库,离线处理业务慢慢处理

优化方案

可以添加一个消息服务,各模块调用发送消息API即可
实现消息存库+异常修改状态

思考:如果feign调用失败没有问题,做好本地事务,feign调用失败回滚即可

5.队列削峰(高并发_秒杀)

场景:
	高并发秒杀模块,将秒杀成功创建订单的消息发送给MQ,订单模块按照自己的能力消费生成订单
	这个过程就是队列削峰(不走购物车逻辑,否则秒杀的高并发流量会带给订单模块)

四、Springboot整合RabbitMQ

1639491342358

1.引入spring-boot-starter-amqp依赖

1.订单服务中引入依赖,场景启动器,引入后RabbitAutoConfiguration自动生效
<!--rabbitmq-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2.RabbitAutoConfiguration生效后自动注入多个容器:
	CachingConnectionFactory:
	RabbitTemplate:
	AmqpAdmin:
	RabbitMessagingTemplate:

3.RabbitProperties:配置类

CachingConnectionFactory


RabbitTemplate


AmqpAdmin

用于创建Exchange、Queue、Binding

RabbitMessagingTemplate


2.开启Rabbit

启动类标注:(只有需要用到监听消息时才需要该注解,开启后可以使用@RabbitListener)
@EnableRabbit

3.配置属性

spring:
  rabbitmq:
    host: 192.168.56.10
    port: 5672
    # 虚拟主机
    virtual-host: /
    # 开启发送端抵达队列确认【发送端确认机制+本地事务表】
    publisher-returns: true
    # 开启发送确认【发送端确认机制+本地事务表】
    publisher-confirm-type: correlated
    # 只要抵达队列,优先回调return confirm
    template:
      mandatory: true
    # 使用手动确认模式,关闭自动确认【消息丢失】
    listener:
      simple:
        acknowledge-mode: manual

4.配置类

@Configuration
public class MyRabbitConfig {

    @Bean
    public MessageConverter messageConverter() {
        // 使用json序列化器来序列化消息,发送消息时,消息对象会被序列化成json格式
        return new Jackson2JsonMessageConverter();
    }

}

5.单元测试

创建Exchange

@Autowired
AmqpAdmin amqpAdmin;

@Test
void createExchange() {
    // 创建交换机
    // String name, boolean durable, boolean autoDelete
    DirectExchange exchange = new DirectExchange("hello-java-exchange", true, false);
    amqpAdmin.declareExchange(exchange);
    log.info("Exchange创建[{}]成功", "hello-java-exchange");
}

创建Queue

@Autowired
AmqpAdmin amqpAdmin;

@Test
void createQueue() {
    // 创建队列
    // String name, boolean durable, boolean exclusive, boolean autoDelete
    // exclusive:是否排他,true:只有一个连接可以使用此队列,其他连接无法连上此队列
    Queue queue = new Queue("hello-java-queue", true, false, false);
    amqpAdmin.declareQueue(queue);
    log.info("Queue创建[{}]成功", "hello-java-queue");
}

创建Binding_交换机绑定

@Autowired
AmqpAdmin amqpAdmin;

@Test
void createBinding() {
    // 创建绑定,交换机绑定目的地
    // String destination:目的地name
    // DestinationType destinationType:目的地类型【queue或exchange(路由)】
    // String exchange:待绑定交换机
    // String routingKey:路由键
    Binding bind = new Binding("hello-java-queue", Binding.DestinationType.QUEUE,"hello-java-exchange", "hello.java", null)
    amqpAdmin.declareBinding(bind);
    log.info("Binding创建[{}]成功", "hello-java-binding");
}

发送消息 1639498067759

@Autowired
RabbitTemplate rabbitTemplate;

@Test
void sendMsg() {
    // 如果发送的消息是个对象,会使用序列化机制,将对象写出去。对象类必须实现 serializable
    // 如果使用JSON序列化器,则不需要类实现Serializable
    // String exchange:交换机
    // String routingKey:路由键
    // final Object object:消息
    // CorrelationData correlationData:可指定消息ID
    
    // 消息对象,可以是任意类型,类必须实现serializable,消息会以序列化的方式写入流中
    OrderReturnReasonEntity message = new OrderReturnReasonEntity();// 退货原因
    message.setId(1L);
    message.setCreateTime(new Date());
    message.setName("哈哈");
    // 消息ID
    CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
    rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", message, correlationData);
}

接收消息

@RabbitListener
简介:
	1.用于标注在监听类或监听方法上,接收消息,需要指定监听的队列(数组)
	2.使用该注解之前,需要在启动类加上该注解:@EnableRabbit
	3.@RabbitListener即可以标注在方法上又可以标注在类上
		标注在类上:表示该类是监听类,使得@RabbitHandler注解生效
		标注在方法上:表示该方法时监听方法,会监听指定队列获得消息
	4.一般只标注在方法上,并配合@RabbitHandler使用,重载的方式接收不同消息对象
import com.rabbitmq.client.Channel;

@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {

    /**
     * queues:声明需要监听的队列
     * channel:当前传输数据的通道
     * 获取实际消息内容有两种方式:
     *  方式一:在方法参数列表中直接声明出来
     *  方式二:从请求体中取出消息的二进制形式,然后通过JSON反序列化即可
     */
    @RabbitListener(queues = {"hello-java-queue"})
    public void revieveMessage(Message message, OrderReturnReasonEntity entity, Channel channel) {
        // 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
        byte[] body = message.getBody();
        // 请求头
        MessageProperties messageProperties = message.getMessageProperties();
        // JSON反序列得到消息内容对象
        OrderReturnReasonEntity reason = JSONObject.parseObject(body, OrderReturnReasonEntity.class);
        System.out.println("接受到的消息对象" + message);
        System.out.println("接受到的消息内容" + reason);
        System.out.println("接受到的消息内容" + entity);
    }
}
测试集群多客户端监听接收消息

1639580605368

简介:
	复制一份订单配置9011
	-Xmx100m
	--server.port=9011
	同时运行多个订单模块,然后测试发送多条消息
	
结论:
	1.多个客户端可以共同监听同一队列
	2.一条消息同时只能被一个客户端接收
	3.同一个客户端接收消息是串行的,revieveMessage方法执行完后才会继续接收下一条消息

9010接收消息:

1639580888252

9011接收消息:

1639580848532

@RabbitHandler
作用:
	配合@RabbitListener,使用方法重载的方法接收不同的消息类型
简介:
	1.用于标注在监听方法上,接收消息,不需要指定监听的队列
	2.使用该注解之前,需要在启动类加上该注解:@EnableRabbit
	3.@RabbitListener只可以标注在方法,重载的方式接收不同消息对象
@Slf4j
@SpringBootTest
class GulimallOrderApplicationTests {

    @Autowired
	RabbitTemplate rabbitTemplate;

    @Test
    void sendMsg2() {
        // 该测试案例向同一队列发送了两个不同类型的消息对象
        // 消息一:
        OrderReturnReasonEntity message1 = new OrderReturnReasonEntity();// 退货原因
        message1.setId(1L);
        message1.setCreateTime(new Date());
        message1.setName("哈哈");
        CorrelationData correlationData1 = new CorrelationData(UUID.randomUUID().toString());// 消息ID
        rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", message1, correlationData1);

        // 消息二:
        OrderEntity message2 = new OrderEntity();// 退货原因
        message2.setId(1L);
        message2.setCreateTime(new Date());
        message2.setOrderSn("哈哈");
        CorrelationData correlationData2 = new CorrelationData(UUID.randomUUID().toString());// 消息ID
        rabbitTemplate.convertAndSend("hello-java-exchange", "hello.java", message2, correlationData2);
    }
}
@RabbitListener(queues = {"hello-java-queue"})
@Service("orderItemService")
public class OrderItemServiceImpl extends ServiceImpl<OrderItemDao, OrderItemEntity> implements OrderItemService {

    /**
     * 方法重载1
     */
    @RabbitHandler
    public void revieveMessage(Message message, OrderReturnReasonEntity entity, Channel channel) {
        // 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
        byte[] body = message.getBody();
        // 请求头
        MessageProperties messageProperties = message.getMessageProperties();
        // JSON反序列得到消息内容对象
        OrderReturnReasonEntity reason = JSONObject.parseObject(body, OrderReturnReasonEntity.class);
        System.out.println("接受到的消息对象" + message);
        System.out.println("接受到的消息内容" + reason);
        System.out.println("接受到的消息内容" + entity);
    }

    /**
     * 方法重载2
     */
    @RabbitHandler
    public void revieveMessage(Message message, OrderEntity entity, Channel channel) {
        // 请求体,序列化存储(本例中已使用Jackson2JsonMessageConverter序列化器作JSON序列化存储)
        byte[] body = message.getBody();
        // 请求头
        MessageProperties messageProperties = message.getMessageProperties();
        // JSON反序列得到消息内容对象
        OrderEntity reason = JSONObject.parseObject(body, OrderEntity.class);
        System.out.println("接受到的消息对象" + message);
        System.out.println("接受到的消息内容" + reason);
        System.out.println("接受到的消息内容" + entity);
    }
}

6.消息确认

spring:
  rabbitmq:
    host: 192.168.56.10
    port: 5672
    # 虚拟主机
    virtual-host: /
    # 开启发送端发送确认,无论是否到达broker都会触发回调【发送端确认机制+本地事务表】
    publisher-confirm-type: correlated
    # 开启发送端抵达队列确认,消息未被队列接收时触发回调【发送端确认机制+本地事务表】
    publisher-returns: true
    # 消息在没有被队列接收时是否强行退回
    template:
      mandatory: true
    # 消费者手动确认模式,关闭自动确认,否则会消息丢失
    listener:
      simple:
        acknowledge-mode: manual

7.延时队列、死信队列

QQ图片20220102220024.jpg

描述:
	可以共用一个exchange,指定不同的路由分别绑定延时队列和死信队列
/**
 * 创建队列,交换机,延时队列,绑定关系 的configuration
 * 1.Broker中的Queue、Exchange、Binding不存在的情况下,会自动创建(在RabbitMQ),不会重复创建覆盖
 * 2.懒加载,只有第一次使用的时候才会创建(例如监听队列)
 */
@Configuration
public class MyRabbitMQConfig {

    /**
     * 延时队列
     */
    @Bean
    public Queue orderDelayQueue() {
        /**
         * Queue(String name,  队列名字
         *       boolean durable,  是否持久化
         *       boolean exclusive,  是否排他
         *       boolean autoDelete, 是否自动删除
         *       Map<String, Object> arguments) 属性【TTL、死信路由、死信路由键】
         */
        HashMap<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", "order-event-exchange");// 死信路由
        arguments.put("x-dead-letter-routing-key", "order.release.order");// 死信路由键
        arguments.put("x-message-ttl", 60000); // 消息过期时间 1分钟
        return new Queue("order.delay.queue", true, false, false, arguments);
    }

    /**
     * 交换机(死信路由)
     */
    @Bean
    public Exchange orderEventExchange() {
        return new TopicExchange("order-event-exchange", true, false);
    }

    /**
     * 死信队列
     */
    @Bean
    public Queue orderReleaseQueue() {
        return new Queue("order.release.order.queue", true, false, false);
    }

    /**
     * 绑定:交换机与订单解锁延迟队列
     */
    @Bean
    public Binding orderCreateBinding() {
        /**
         * String destination, 目的地(队列名或者交换机名字)
         * DestinationType destinationType, 目的地类型(Queue、Exhcange)
         * String exchange,
         * String routingKey,
         * Map<String, Object> arguments
         **/
        return new Binding("order.delay.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.create.order",
                null);
    }

    /**
     * 绑定:交换机与订单解锁死信队列
     */
    @Bean
    public Binding orderReleaseBinding() {
        return new Binding("order.release.order.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.order",
                null);
    }

    /**
     * 绑定:交换机与库存解锁
     */
    @Bean
    public Binding orderReleaseOtherBinding() {
        return new Binding("stock.release.stock.queue",
                Binding.DestinationType.QUEUE,
                "order-event-exchange",
                "order.release.other.#",
              
RabbitMQ快速入门 RabbitMq MQ框架非常之多,比较流行的有RabbitMq、ActiveMq、ZeroMq、kafka,以及阿里开源的RocketMQ。 本文主要介绍RabbitMq RabbitMQ是以AMQP协议实现的一种消息中间件产品。 AMQP是Advanced Message Queuing Protocol的简称,它是一个面向消息中间件的开放式标准应用层协议。 阅读详情

相关推荐

RabbitMQ 从入门到精通

消息中间件基于队列模型实现异步/同步传输数据作用:可以实现支撑高并发、异步解耦、流量削峰、降低耦合度。

rc1596919047的博客 1868

RabbitMQ的快速入门

RabbitMQ是一套开源(MPL)的消息队列服务软件,是由 LShift 提供的一个 Advanced Message Queuing Protocol (AMQP) 的开源实现,由以高性能、健壮以及可伸缩性出名的Erlang写成。

博客 1156

PCB印刷电路缺陷检测实战:从数据集准备到模型训练全流程解析

本文详细解析了PCB缺陷检测从数据集准备到模型训练的全流程。重点介绍了PKU-Market-PCB数据集的特点与下载方法,并提供了数据格式转换、模型选择(如YOLOv8)与训练配置的实战代码。针对PCB小目标检测的难点,文章给出了数据分析和模型优化的具体策略,帮助读者构建高效的缺陷检测系统。

weixin_29065791的博客 547

超详细的RabbitMQ入门

转载:超详细的RabbitMQ入门,看这篇就够了!-阿里云开发者社区 思维导图 一、什么是消息队列 消息指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。 “消息队列(Message Queue)”是在消息的传输过程中保存消息的容器。在消息队列中,通常有生产者和消费者两个角色。生产者只负责发送数据到消息队列,谁从消息队列中取出数据处理,他不管。消费者只负责从消息队列中取出数据处理,他不管这是谁发送的数据。 二、为什么使用消息队列 主要有三个作用

Rok728的博客 2万+

51单片机基础知识

前言 周末闲着无聊,以后定时更新一期关于51单片机的学习案例,以供大家平时参考使用。至于单片机,你可以理解为人的大脑,而人各项身体器官的运作需要意识给到大脑,然后大脑再驱动我们的中枢神经进行相应的动作,这里的意识可以理解为我们要写的程序,而人的器官相当于各种输出设备,例如我们以后要学的LED、电机、各种传感器等等。学好单片机能让你找到一份相对不错的工作,当然越可观的东西也是要付出相应努力的。 一、51单片机是什么? 通常我们一说到 51 单片机,指的都是兼容IntelMCS-51体系架构的一系列单片机,而

qq_43537819的博客 6905

关系代数表达式优化步骤

关系代数表达式优化步骤 本篇主要讲解怎么画查询语法树并对其优化,因为我在学关系代数的语法树的时候,在网上找不到比较详细的教法或者技巧,最后通过答案反推原理,所以想写一篇技巧来描述一下这类题的解题方法。 先上书内讲解 1、构造查询树 第一步:把用高级语言定义的查询转换为关系代数表达式 ★ 以 SELECT子向对应投影操作,以FROM字向对应笛卡尔积以 WHERE子句对应选择操作,生成原始查询树 ★ SQL语句转化为原始查询树 第二步:把关系代数表达式转换为查询树。 注: 查询树是一种表示关系代数表达式的树形结

m0_51718449的博客 7016

RabbitMQ快速入门(详细)

在介绍RabbitMQ之前,我们先来看下面一个电商项目的场景: 商品的原始数据保存在数据库中,增删改查都在数据库中完成。 搜索服务数据来源是索引库(Elasticsearch),如果数据库商品发生变化,索引库数据不能及时更新。 商品详情做了页面静态化处理,静态页面数据也不会随着数据库商品更新而变化。 如果我们在后台修改了商品的价格,搜索页面和商品详情页显示的依然是旧的...

kavito的博客 24万+

【驭风计划 算法训练营】周测4:习题11-4、8-4、12-4

给出 nn 个非负整数 x1,x2,⋯,xnx1,x2,⋯,xn,有 qq 个询问,第 jj 个询问给出一个非负整数 yjyj,求其中,xor 表示按位异或运算。

Mege1的博客 1043

RabbitMQ3:Java客户端快速入门

AMQP:Advanced Message Queuing Protocol(高级消息队列协议)。解读:首先AMQP是一种协议,类似于http这种,规定了应用程序之间传递消息的标准。该协议与语言、平台无关,因此可实现跨语言、跨平台。Spring AMQP:是基于AMQP协议,定义的一套API规范。提供了模板来发送和接收消息。Spring AMQP官方网址:此时是生产者直接将消息放入队列,都没经过交换机这一环节。可见此入门案例,是helloworld级别的。t=O83A。

casually的博客 1501

verilog常用模块1——按键消抖模块详解

按键消抖模块key_filter 1. 原理介绍 如图,按键未按下时keys信号为高电平,按下则为低电平;通过检测keys信号电平,就可以判断按键状态。 但反作用弹簧会导致抖动现象,电平信号出现一段不确定波形 一般情况下,抖动的电平信号为1的持续时间不会超过20ms,只要通过对抖动波形的发生时长计时(计数),当为1的时长超过20ms,则可判定 进入了发生了按键按下/释放动作,并确定按键状态...

coin的博客 1万+

基于ESP32-WROOM的智能蓝牙电压电流监测与远程控制系统设计

本文介绍了基于ESP32-WROOM的智能蓝牙电压电流监测与远程控制系统设计。该系统集成了INA219传感器和OLED显示屏,通过蓝牙实现无线数据传输和远程控制,适用于智能家居能耗监控和工业设备管理,具有成本低、精度高、功耗优化等优点。

qsc901234的博客 203

RabbitMQ2:介绍、安装、快速入门、数据隔离

d \-e:配置了一些参数,令账号为itheima,密码为123321-v:将数据卷mq-plugins挂载到容器的/plugins目录上-name:令该docker容器名为mq-hostname:主机名-p:端口映射(宿主机、容器之间的端口映射)--network:指定该容器处于哪个网络(此处是处于我们刚才创建的自定义网络hmall中)-d:表示该容器后台运行(一般都加上这个)rabbitmq:3.8-management:这是镜像名称。hello.queue2队列同上。

casually的博客 1942

【问题解决】Python 运行报错 `ModuleNotFoundError` 全网最全解决方案,一文搞定所有导包问题

本文全面解析Python中常见的ModuleNotFoundError和ImportError问题,提供从基础到高级的解决方案。文章首先分析问题原因,包括模块未安装、环境不一致、路径问题等,然后给出7种解决方案:安装缺失模块、检查Python环境、使用虚拟环境、添加搜索路径、相对导入、setup.py安装本地包和使用__init__.py。针对Jupyter Notebook、Docker容器等常见场景提供专门解决方案,并推荐使用requirements.txt、pipenv等预防措施。最后分享调试技巧,帮

博客 915

RabbitMQ消息中间件快速入门

文章目录一、同步和异步通讯1. 同步通讯2. 异步通讯二、RabbitMQ1. MessageQueue对比2. RabbitMQ快速入门【1】安装与启动【2】基本结构3. 五种消息模型介绍【1】入门案例1)publisher实现2)consumer实现4. SpringAMQP实现【1】Basic Queue 基本消息队列1)消息发送2)消息接收3)测试【2】WorkQueue工作消息队列1)消息发送2)消息接收3)测试4)prefetch提前获取【3】发布订阅三种模式1)Fanout[1] 声明队列和交

欢迎来到编程小栈 2894

深度学习笔记——前向传播与反向传播、神经网络(前馈神经网络与反馈神经网络)、常见算法概要汇总

本文简要介绍深度学习的前向传播与反向传播,以及前馈神经网络与反馈神经网络。

haopinglianlian的博客 3043

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

【RabbitMQ快速入门】RabbitMQ概述与安装

RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件。首先要知道一些RabbitMQ的特点,官网可查: - 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。 - 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。 - 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。 - 多种协议

鱼干的博客 2981

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

快速入门RabbitMQ

RabbitMQ基于AMQP协议,erlang语言开发,是部署最广泛的开源消息中间件,是最受欢迎的开源消息中间件之一。

huangjhai的博客 6189

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

RabbitMQ入门实战

Channel频道:理解为操作消息队列的client,提供了和消息队列server建立通信的方法(为了复用连接,提高传输效率)。如果想要在远程服务器安装访问 rabbitmq 管理面板,你要自己创建一个管理员账号,不能用默认的 guest,否则会被拦截(官方出于安全考虑)。安装 erlang 25.3.2(因为 RabbitMQ 依赖 erlang,不安装这个安装RabbitMQ会报错),这个语言的性能非常高。queueName:消息队列名称(注意,同名称的消息队列,只能用同样的参数创建一次)

诨号无敌鸭的博客 974

【RabbitMQ】 RabbitMQ快速上手

RabbitMQ快速入门

2403_87459748的博客 4244

RabbitMQ快速入门及Spring Boot Maven项目快速应用

入门级教程 简介: 大多应用中,可通过消息服务中间件来提升系统异步通信、扩展解耦能力 消息服务中两个重要概念:消息代理(message broker)和目的地(destination) 当消息发送者发送消息以后,将由消息代理接管,消息代理保证消息传递到指定目的地。 消息队列主要有两种形式的目的地 队列(queue):点对点消息通信(point-to-point) 主题(topic):...

嫒de洫天使 1万+
上一篇: JUC系列(四) callable与 常用的工具类
下一篇: 六千字带你快速上手操作MySQL
冷环渊
博客等级 码龄7年 1130粉丝 176原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冷环渊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值