整合RabbitMQ使用

本文介绍了消息中间件的重要性和应用场景,重点讲解了RabbitMQ的基本概念、与的区别,以及RabbitMQ的五种工作模式:简单模式、工作队列模式、广播模式、主题模式和订阅模式。此外,还探讨了RabbitMQ的高级特性,如过期时间、死信队列、消息确认机制和持久化策略。
摘要由CSDN通过智能技术生成

Demo地址

一、消息中间件

在项目中,可将一些无需即时返回且耗时的操作提取出来,进行异步处理,而这种异步处理的方式大大的节省了服务器的请求响应时间,从而提高了系统的吞吐量。

消息队列常用应用场景:

1、任务异步处理

将不需要同步处理的并且耗时长的操作由消息队列通知消息接收方进行异步处理。提高了应用程序的响应时间。

2、应用程序解耦合

MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦合。

3、削峰填谷

如订单系统,在下单的时候就会往数据库写数据。但是数据库只能支撑每秒1000左右的并发写入,并发量再高就容易宕机。低峰期的时候并发也就100多个,但是在高峰期时候,并发量会突然激增到5000以上,这个时候数据库肯定卡死了。

但是使用了MQ之后,限制消费消息的速度为1000,但是这样一来,高峰期产生的数据势必会被积压在MQ中,高峰就被“削”掉了。但是因为消息积压,在高峰期过后的一段时间内,消费消息的速度还是会维持在1000QPS,直到消费完积压的消息,这就叫做“填谷”。

二、AMQPJMS

MQ是消息通信的模型;实现MQ的大致有两种主流方式:AMQPJMS

2.1. AMQP

AMQP是一种协议,更准确的说是一种binary wire-level protocol(链接协议)。这是其和JMS的本质差别,AMQP不从API层进行限定,而是直接定义网络交换的数据格式。

2.2. JMS

JMSJava消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。

2.3. AMQPJMS 区别

  • JMS是定义了统一的接口,来对消息操作进行统一;AMQP是通过规定协议来统一数据交互的格式
  • JMS限定了必须使用Java语言;AMQP只是协议,不规定实现方式,因此是跨语言的。
  • JMS规定了两种消息模式;而AMQP的消息模式更加丰富

2.4. 消息队列产品

市场上常见的消息队列有如下:

  • ActiveMQ:基于JMS
  • ZeroMQ:基于C语言开发
  • RabbitMQ:基于AMQP协议,erlang语言开发,稳定性好
  • RocketMQ:基于JMS,阿里巴巴产品
  • Kafka:类似MQ的产品;分布式消息系统,高吞吐量

三、RabbitMQ概念

RabbitMQ官方地址:http://www.rabbitmq.com/

3.1.简介

RabbitMQ是由erlang语言开发,基于AMQP(Advanced Message Queue 高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。

RabbitMQ提供了6种模式:简单模式,work模式,Publish/Subscribe发布与订阅模式,Routing路由模式,Topics主题模式,RPC远程调用模式(远程调用,不太算MQ;暂不作介绍)。

3.2.核心概念

  • Message

消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。

  • Publisher

消息的生产者,也是一个向交换器发布消息的客户端应用程序。

  • Consumer

消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。

  • Exchange

交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。

Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别。

  • Binding

绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。

ExchangeQueue的绑定可以是多对多的关系。

  • Queue

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

  • Connection

网络连接,比如一个TCP连接。

  • Channel

信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。

  • Broker

表示消息队列服务器实体

  • Virtual Host

虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhostAMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 / 。

概念 说明
连接Connection 一个网络连接,比如TCP/IP套接字连接。
会话Session 端点之间的命名对话。在一个会话上下文中,保证“恰好传递一次”。
信道Channel 多路复用连接中的一条独立的双向数据流通道。为会话提供物理传输介质。
客户端Client AMQP连接或者会话的发起者。AMQP是非对称的,客户端生产和消费消息,服务器存储和路由这些消息。
服务节点Broker 消息中间件的服务节点;一般情况下可以将一个RabbitMQ Broker看作一台RabbitMQ 服务器。
端点 AMQP对话的任意一方。一个AMQP连接包括两个端点(一个是客户端,一个是服务器)。
消费者Consumer 一个从消息队列里请求消息的客户端程序。
生产者Producer 一个向交换机发布消息的客户端应用程序。

在这里插入图片描述
在这里插入图片描述

四、Spring Boot整合RabbitMQ

为了方便下面示例的编写,我们先直接整合Spring Boot ,不用RabbitMQ自带依赖写示例了。

4.1.导入依赖

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

4.2.在application.yml中添加RabbitMQ地址

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    virtual-host: /test

4.3.常用注解说明

@EnableRabbit:启动注解的消息监听,需要在配置类上加上@EnableRabbit注解。

@RabbitListener:可以标注在方法或类上,声明这个方法是一个消费者方法,可以定义监听的队列、队列绑定关系,常用的属性:

  • queues:指定关联容器可以侦听多个队列。

  • bindings:指定绑定关系,可以有多个。值是@QueueBinding的数组。@QueueBinding包含下面属性:

    • value:这个消费者关联的队列。值是@Queue,代表一个队列
    • exchange:队列所绑定的交换机,值是@Exchange类型
    • key:队列和交换机绑定的RoutingKey

@RabbitHandler:用在需要监听多个方法时,不同的方法接收不同的消息实体,必须能够明确区分不同的实体,否则消息不能监听成功。

@Header@Headers@Payload:用在方法签名上,用来获取消息头信息或者明确表明消息的负载实体(也可以自动推导)。

@SendTo:用于设定消息回复,标注的方法需要返回非空的回复实体对象

五、RabbitMQ工作模式 五种消息模型

官网对应模式介绍:https://www.rabbitmq.com/getstarted.html

5.1.简单模式 HelloWorld

5.1.1、介绍

RabbitMQ是一个消息代理:它接受和转发消息。 你可以把它想象成一个邮局:当你把邮件放在邮箱里时,你可以确定邮差先生最终会把邮件发送给你的收件人。 在这个比喻中,RabbitMQ是邮政信箱,邮局和邮递员。

图例:

在这里插入图片描述

P(producer/ publisher:生产者,一个发送消息的用户应用程序。

C(consumer:消费者,消费和接收有类似的意思,消费者是一个主要用来等待接收消息的用户应用程序

队列(红色区域)RabbitMQ内部类似于邮箱的一个概念。虽然消息流经RabbitMQ和你的应用程序,但是它们只能存储在队列中。队列只受主机的内存和磁盘限制,实质上是一个大的消息缓冲区。许多生产者可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。

总之:

生产者将消息发送到队列,消费者从队列中获取消息,队列是存储消息的缓冲区。

5.1.2、示例

编写两个程序:发送单个消息的生产者,以及接收消息并将其打印出来的消费者。

1).生产者发送消息
/******** 简单模式-声明队列 ***************/
@Bean("helloWorld")
public Queue helloWorld(){
   
    return QueueBuilder.durable("HelloWorld").build();
}

/******** 简单模式-发送消息 ***************/
@Test
void helloWord() {
   
    /**
     * 发送消息
     * 参数一:路由key
     * 参数二:发送的消息
     */
    rabbitTemplate.convertAndSend("HelloWorld","HelloWorld消息...");
}
2).消费者获取消息
/******** 简单模式-消费者获取消息 ***************/
@RabbitListener(queues = "HelloWorld")
public void helloWorld(String msg){
   
    System.out.println("简单模式接收的消息:" + msg);
}

5.2.工作队列模式 Work Queue

5.2.1、介绍

工作队列或者竞争消费者模式。

工作队列,又称任务队列。主要思想就是避免执行资源密集型任务时,必须等待它执行完成。相反我们稍后完成任务,我们将任务封装为消息并将其发送到队列。 在后台运行的工作进程将获取任务并最终执行作业。当你运行许多工人时,任务将在他们之间共享,但是一个消息只能被一个消费者获取。

这个概念在Web应用程序中特别有用,因为在短的HTTP请求窗口中无法处理复杂的任务。

图例:

在这里插入图片描述

5.2.2、示例

接下来我们来模拟这个流程:

P:生产者:任务的发布者

C1:消费者,领取任务并且完成任务,假设完成速度较快

C2:消费者2:领取任务并完成任务,假设完成速度慢

//  TODO 测试

5.3.订阅模式-Publish/Subscribe

Exchange类型有以下几种:

  • Fanout:广播,将消息交给所有绑定到交换机的队列。

  • Direct:定向,把消息交给符合指定routing key 的队列。

  • Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列。

5.3.1、介绍

Publish/Subscribe模式即Exchange广播(Fanout)类型,将消息交给所有绑定到交换机的队列。每个发到 fanout 类型交换器的消息都会分到所有绑定的队列上去。fanout 交换器不处理路由键,只是简单的将队列绑定到交换器上,每个发送到交换器的消息都会被转发到与该交换器绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。fanout 类型转发消息是最快的。

图例:

在这里插入图片描述

在广播模式下,消息发送流程是这样的:

  • 1) 可以有多个消费者
  • 2) 每个消费者有自己的Queue(队列)
  • 3) 每个队列都要绑定到Exchange(交换机)
  • 4) 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
  • 5) 交换机把消息发送给绑定过的所有队列
  • 6) 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
5.3.2、示例
1).生产者发送消息

声明队列交换机:

/******** Publish/Subscribe模式 ***************/
/** 容器中的Queue、Exchange、Binding 会自动创建(在RabbitMQ)不存在的情况下
 *  如果RabbitMQ有,@Bean声明属性发生变化也不会覆盖
 */
/**
 * 声明交换机
 * @return
 */
@Bean("fanoutExchange")
public Exchange fanoutExchange(){
   
    return ExchangeBuilder.fanoutExchange("fanoutExchange").durable(true).build();
}

/**
 * 声明队列
 * @return
 */
@Bean("fanoutQueue1")
public Queue fanoutQueue1(){
   
    return QueueBuilder.durable("fanoutQueue1").build();
}
@Bean("fanoutQueue2")
public Queue fanoutQueue2(){
   
    return QueueBuilder.durable("fanoutQueue2").build();
}
/**
 * 绑定队列和交换机
 * @param queue
 * @param exchange
 * @return
 */
@Bean
public Binding fanoutQueue1Exchange(@Qualifier("fanoutQueue1") Queue queue,
                                    @Qualifier("fanoutExchange") Exchange exchange){
   
    return BindingBuilder.bind(queue).to(exchange).with("fanoutQueue1").noargs();
}
@Bean
public Binding fanoutQueue2Exchange(@Qualifier("fanoutQueue2") Queue queue,
                                    @Qualifier("fanoutExchange") Exchange exchange){
   
    return BindingBuilder.bind(queue).to(exchange).with("fanoutQueue2").noargs();
}

生产者发送消息:

@Test
void fanoutQueue() {
   
    for (int i = 0; i < 50; i++) {
   
        /**
         * 发送消息
         * 参数一:交换机名称
         * 参数二:路由key:不管指定什么路由key都会转发到绑定交换机的所有队列
         * 参数三:发送的消息
         */
        rabbitTemplate.convertAndSend("fanoutExchange", "WorkQueue","fanoutQueue消息 ---> " + i)	;
    }
}
2).消费者获取消息
/******** Publish/Subscribe-消费者获取消息 ***************/
@RabbitListener(queues = "fanoutQueue1")
public void fanoutQueue1(String msg) {
   
    log.info("Publish/Subscribe 1模式接收的消息:" + msg);
}
@RabbitListener(queues = "fanoutQueue2")
public void fanoutQueue2(String msg) {
   
    log.info("Publish/Subscribe 2模式接收的消息:" + msg);
}

消费者控制台打印:

2020-12-21 16:21:07.073  INFO 9460 --- [ntContainer#1-1] t.d.c.c.FanoutQueueRabbitListener        : Publish/Subscribe 1模式接收的消息:"workQueue消息 ---> 37"
2020-12-21 16:21:07.073  INFO 9460 --- [ntContainer#0-1] t.d.c.c.FanoutQueueRabbitListener        : Publish/Subscribe 2模式接收的消息:"workQueue消息 ---> 44"
2020-12-21 16:21:07.073  INFO 9460 --- [ntContainer#1-1] t.d.c.c.Fano
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的 Spring Boot 整合 RabbitMQ使用 RabbitTemplate 实现 ACK 消息确认的示例: 首先,在 pom.xml 文件中添加 RabbitMQ 和 Spring Boot 的依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 然后,在 application.yml 文件中配置 RabbitMQ 的连接信息: ```yaml spring: rabbitmq: host: localhost port: 5672 username: guest password: guest ``` 接下来,在代码中使用 RabbitTemplate 发送消息,并实现 ACK 消息确认: ```java import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.support.CorrelationData; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MessageSender implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback { @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String exchange, String routingKey, Object message) { rabbitTemplate.setConfirmCallback(this); rabbitTemplate.setReturnCallback(this); CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString()); rabbitTemplate.convertAndSend(exchange, routingKey, message, correlationData); } @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { if (ack) { System.out.println("消息发送成功:" + correlationData); } else { System.out.println("消息发送失败:" + cause); } } @Override public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) { System.out.println("消息发送失败:" + message); } } ``` 以上代码中,MessageSender 类是一个消息发送者,通过 sendMessage 方法发送消息,使用 CorrelationData 存储消息的唯一标识,用于 ACK 消息确认。在发送消息前,需要设置 ConfirmCallback 和 ReturnCallback,以便在消息成功或失败时得到通知。在实现 ConfirmCallback 和 ReturnCallback 接口的方法中,打印出消息的成功或失败信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值