目录
2.5 上图5:主题模式 Topic Exchange (通配符模式)
1.基础知识:
1.0 前言:
RabbitMQ是一个由erlang语言开发的基于AMQP(Advanved Message Queue)队列协议的开源实现。
支持的操作系统 linux、windows、macox等
支持的开发语言 java、python、ruby、.net、php、c/c++、node.js等
1.1 rabbitmq的用户角色
1、 超级管理员(administrator)
可登陆管理控制台,可查看所有的信息,并且可以对用户,策略(policy)进行操作。
2、 监控者(monitoring)
可登陆管理控制台,同时可以查看rabbitmq节点的相关信息(进程数,内存使用情况,磁盘使用情况等)
3、 策略制定者(policymaker)
可登陆管理控制台, 同时可以对policy进行管理。但无法查看节点的相关信息(上图红框标识的部分)。
4、 普通管理者(management)
仅可登陆管理控制台,无法看到节点信息,也无法对策略进行管理。
5、 其他
无法登陆管理控制台,通常就是普通的生产者和消费者。
1.2 rabbitmq的端口号
5672: AMQP协议的端口号(与Java交互)
15672: 管理工具的端口
25672: 集群
这些端口需要防火墙放行,详细的操作细节后面的linux、windows搭建会有提及
1.3 消息队列的运行原理
从上图可以看出、生产者、消息队列、消费者是最重要的三个概念:
生产者(Producer):创建消息到消息队列中(消息的创建者,负责创建和推送数据到消息服务器)。
消息队列(Queue):存储消息。
消费者(Consumer):监听指定消息队列,当消息队列接收到消息后,获取消息并处理。
RabbitMQ:创建消息队列,并提供API给生产者和消费者进行存取消息,用于扮演“快递”的角色,本身不生产消息,只是扮演“快递”的角色。
生产者产生消息并调用RabbitMQ的API将消息加入到对应的消息队列中,消费者通过RabbitMQ的API从消息队列中获取消息进行消费。
这里面有一个虚拟机的概念,你可以把它理解为域名,在应用中需要通过配置或者代码指定你要用哪个虚拟机(Vhost)。如果你不指定,那么默认的会选用 "/".前提是你的当前用户有对这个虚拟机 "/" 的使用权限。虚拟机可以在rabbitmq的可视化页面里面配置。
1.4你必须知道的Rabbit
想要真正的了解Rabbit有些名词是你必须知道的。
包括:ConnectionFactory(连接管理器)、Channel(信道)、Exchange(交换器)、Queue(队列)、 RoutingKey(路由键)、BindingKey(绑定键)。
ConnectionFactory(连接管理器):应用程序与Rabbit之间建立连接的管理器,程序代码中使用;
Channel(信道):消息推送使用的通道;
Exchange(交换器):用于接受、分配消息;
Queue(队列):用于存储生产者的消息;
RoutingKey(路由键):用于把生成者的数据分配到交换器上;
BindingKey(绑定键):用于把交换器的消息绑定到队列上;
2.六种队列
rabbitmq一共有六种队列,其中RPC已被淘汰:
2.1 上图1: 简单队列
生产者与消费者一一对应 生产者将消息发送到队列,消费者从队列中获取消息。
在这种模式下一个生产者对应了多个消费者,但是一个消息只能被一个消费者获取。可以配置消息分发到消费者的路由规则
“P”是我们的生产者,“C”是我们的消费者。中间的框是一个队列。
生产者将消息发送到队列,消费者从队列中消费。
2.2 上图2:Work模式
一个生产者多个消费者,每个消费者获取到的消息唯一
2.3上图3: 订阅模式 Fanout Exchange
解读:
1、1个生产者,多个消费者
2、每一个消费者都有自己的一个队列
3、生产者没有将消息直接发送到队列,而是发送到了交换机
4、每个队列都要绑定到交换机
5、生产者发送的消息,经过交换机,到达队列,实现,一个消息被多个消费者获取的目的
注意:一个消费者队列可以有多个消费者实例,只有其中一个消费者实例会消费
fanout(不处理路由键):每个和交换机绑定的队列都会收到消息
2.4上图4: 路由模式 Direct Exchange
发送消息到交换机并且要指定路由key ,消费者将队列绑定到交换机时需要指定路由key。它会把消息路由到那些binding key与routing key完全匹配的Queue中。
2.5 上图5:主题模式 Topic Exchange (通配符模式)
这个可以理解为队列的模糊匹配。消息经过交换机推送到符合通配符规则的队列中。
将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词。当消息发送至交换机时,只有routingKey能够满足通配规则的队列才能够接收到消息。
例如:routingKey 为 testqueue 的队列 能够接收到 test# 的消息
3.应用场景
3.1.延迟队列
用户注册后长时间不活跃定时推送提醒
订单下单后长时间未支付转至推送提醒
延迟重试,比如服务原因导致业务执行终端通过延迟重试保障业务完整性等
3.2.消息错峰
秒杀、促销等大流量信息短时间注入是的错峰处理以平衡上下游吞吐量
3.3.消息分发
统一消息源发送的消息应对不同的消费逻辑。如业务处理和日志采集
3.4.应用解耦
通过发布订阅方式代替接口调用解耦项目
3.5.消息确认ACK
保障业务完整性 提供了confirm 和 return 两种消息确认回调方法。
当消息由生产端成功发送至交换机 confirm回调方法会收到布尔类型的ack确认。true为推送至交换机成功。
当消息由交换机根据routingKey推送至queue队列中时,return回调方法会收到字符类型的replyText确认。
4.rabbitMq与Erlang版本对应关系
在搭建RabbitMQ环境过程中,由于版本问题导致环境一直搭建不起来,以下是RabbitMQ与Erlang的版本对应关系
RabbitMQ版本 |
Erlang最低要求 |
Erlang最高要求 |
3.7.7 - 3.7.12 |
20.3.x |
21.x |
3.7.0 - 3.7.6 |
19.3 |
20.3.x |
5.windows搭建
5.1安装Erlang
5.1.1下载erlang
下载:http://www.erlang.org/download/otp_win64_17.3.exe
百度云盘下载:https://pan.baidu.com/s/1-5yJGzOl23BuQl1D2_n3SA
5.1.2安装erlang
安装完成。
5.2安装rabbitmq
5.2.1下载rabbitmq
官方下载地址:http://www.rabbitmq.com/download.html
百度云盘下载: https://pan.baidu.com/s/18Dxi01D0sbjPZq-RWymnww
5.2.2安装Rabbitmq
安装完成。
5.2.3 启用管理工具
cmd进入安装目录sbin文件夹下 输入命令:
启用管理页面命令 rabbitmq-plugins enable rabbitmq_management
在浏览器中输入地址查看:http://127.0.0.1:15672/
使用默认子账号登录:guest 密码 guest
操作界面不再赘述。 至此 windows 环境搭建已全部完成。
6.linux搭建
6.1 说明:
搭建过程中遇到了mq包与linux系统版本冲突问题,故在此提供了 基于centos6 和centos7两个版本系统的搭建步骤。
6.2CentOs6安装:
6.2.1下载安装包:
rabbitmq安装包:https://pan.baidu.com/s/1pAkK81dFFojnO6fTIxhHkw
erlang安装包:https://pan.baidu.com/s/1jWl-YUSSv1GBUJntZNgXDw
socat安装包:https://pan.baidu.com/s/1DfbX4a4dxtBFA3ZQ7NYDBQ
6.2.2centos6安装:
1.安装erlang的rpm库。
rpm -ivh erlang-20.3.8.7-1.el6.x86_64.rpm
判断erlang是否安装成功:命令行输入 erl
有输出即表示安装成功
2.安装socat依赖:
安装socat的rpm库。
yum install tcp_wrappers
rpm -ivh socat-1.7.3.0-1.el6.x86_64.rpm(不同版本号要修改)
3.安装RabbitMQ
rpm -ivh rabbitmq-server-3.7.7-1.el6.noarch.rpm (不同版本号要修改)
4.启动rabbitmq:service rabbitmq-server start/stop/restart
5.查看rabbitmq的启动状态 service rabbitmq-server status
6.RabbitMQ配置 这里主要是创建用户和赋权,因为guest这个用户 默认的只能本机访问,而我们部署时一般不会放在同一台机器上。创建了admin用户后就可以去浏览器设置其他用户了。
启动RabbitMQ后执行:
1、rabbitmqctl add_user admin 123456(创建用户)
2、rabbitmqctl set_user_tags admin administrator(将创建好的用户加入管理员)
3、rabbitmqctl set_permissions -p "/" admin "." "." ".*"(授权)
4、rabbitmq-plugins enable rabbitmq_management 启动RabbitMQ管理页面(执行完这个才能用浏览器可视化网页)
5、重启MQ服务 service rabbitmq-server restart
6、开放5672/15672/25672端口。
vi /etc/sysconfig/iptables
在红框处的下一行依次添加开放端口语句后保存:
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 5672-j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 15672-j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 25672-j ACCEPT
重启防火墙:
service iptables restart
7、在浏览器输入ip:15672后出现RabbitMQ管理台。
6.3 centos7安装:
方式一: rpm包安装
1.1. 安装依赖环境Erlang
使用Erlang Solutions源进行安装
# 下载rpm包
wget https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
# 从erlang-solutions中更新该包,并将erlang_solutions.repo添加到/etc/yum.repos.d
rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
# 安装
yum install erlang
该包还需依赖到epel源,请确保已有该源,若没有则可通过以下方式安装:
wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
rpm -ivh epel-release-latest-7.noarch.rpm
yum repolist # 查看安装是否成功
由于Erlang Solutions会进行不断地更新,且RabbitMQ对Erlang的版本有一定的要求(官方版本要求对应表)。所以官方建议我们禁止Erlang版本的自动更新。方法如下:参考如何禁止某个软件包的自动升级
# 安装yum-versionlock
yum install yum-plugin-versionlock
# 禁止Erlang自动更新
yum versionlock erlang
1.2. 安装RabbitMQ Server
从官网下载rpm包并上传到服务器上。官方下载链接
# 导入签名
rpm --import https://dl.bintray.com/rabbitmq/Keys/rabbitmq-release-signing-key.asc
# 或
rpm --import https://www.rabbitmq.com/rabbitmq-release-signing-key.asc
# 安装
yum install rabbitmq-server-3.7.7-1.el7.noarch.rpm
方式二: 使用脚本安装
2.1安装erlang
#创建erlang.repo库
curl -s https://packagecloud.io/install/repositories/rabbitmq/erlang/script.rpm.sh | sudo bash
#安装
yum install erlang
2.2安装rabbitmq
#创建rabbitmq-server.repo库
curl -s https://packagecloud.io/install/repositories/rabbitmq/rabbitmq-server/script.rpm.sh | sudo bash
#安装
yum install rabbitmq-server
这种方式虽然会简单点,但我尝试过,发现只有翻墙才能安装成功,所以不太推荐大家使用这种方法。
2.3. 启动RabbitMQ Server
# 设置开启启动
chkconfig rabbitmq-server on
# 启动服务
service rabbitmq-server start
#停止服务
service rabbitmq-server stop
#监测状态
service rabbitmq-server status
#用户赋权
rabbitmqctl add_user admin 123456(创建用户)
rabbitmqctl set_user_tags admin administrator(将创建好的用户加入管理员)
rabbitmqctl set_permissions -p "/" admin "." "." ".*"(授权)
2.4. 配置RabbitMQ
2.4.1 找到配置文件
/usr/share/doc/rabbitmq-server-3.7.7/ 目录下复制一份模板到 /etc/rabbitmq 目录下进行修改
cd /usr/share/doc/rabbitmq-server-3.7.7/
cp rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
2.4.2 开启管理后台 centos7 防火墙 叫firewall了 不是iptables了啊 ,这个注意下。
rabbitmq-plugins enable rabbitmq_management
# 开放端口
firewall-cmd --add-port=5672/tcp --permanen
firewall-cmd --add-port=15672/tcp --permanen
firewall-cmd --add-port=25672/tcp --permanent
firewall-cmd --reload
6.4 浏览器访问
http://ip:15672 ,进入如下页面就证明插件启动成功了
6.5 移除queue
清除的命令是: rabbitmqctl reset
但是在使用此命令前,要先关闭应用,否则不能清除。
关闭应用的命令为: rabbitmqctl stop_app
执行了这两条命令后再次启动此应用。
命令为: rabbitmqctl start_app
再次执行命令: rabbitmqctl list_queues
这次可以看到 listing 及 queues都是空的
7.与spingboot集成
7.1.配置rabbitmq连接信息:
application.properties中配置rabbitMQ的连接信息:
#对于rabbitMQ的支持
spring.rabbitmq.host=192.168.189.136
spring.rabbitmq.port=5672
spring.rabbitmq.username=yolly
spring.rabbitmq.password=yolly
spring.rabbitmq.virtualHost=yolly
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.publisher-returns=true
spring.rabbitmq.template.mandatory=true
#并发消费者的初始化值,并发消费者的最大值,每个消费者每次监听时可拉取处理的消息数量。
spring.rabbitmq.listener.concurrency=10
spring.rabbitmq.listener.max-concurrency=20
spring.rabbitmq.listener.prefetch=5
host、用户名、密码还有虚拟机名称这三个需要改成你自己的rabbitmq主机ip和账号密码虚拟机哈。
这个虚拟机spring.rabbitmq.virtualHost可以不写,如果不写或者写“” 他会选择使用 “/” 虚拟机,前提是你的当前账号有 “/” 虚拟机的使用权限。
7.2 添加AMQP支持
因为rabbitmq是基于amqp协议开发的 所以需要在pom.xml中添加amqp dependencies:
同时用到了web测试效果,所以需要引用web starter
<!--amqp-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>RELEASE</version>
</dependency>
7.3定制化AMQP模板
上面我们提到了rabbitmq的消息ack机制,在boot工程中这个机制生效的前提是声明confirm和return为true ,且实现他们的回调方法:
/**
* 定制化amqp模版 可根据需要定制多个
* <p>
* <p>
* 此处为模版类定义 Jackson消息转换器
* ConfirmCallback接口用于实现消息发送到RabbitMQ交换器后接收ack回调 即消息发送到exchange ack
* ReturnCallback接口用于实现消息发送到RabbitMQ 交换器,但无相应队列与交换器绑定时的回调 即消息发送不到任何一个队列中 ack
*
* @return the amqp template
*/
// @Primary
@Bean
public AmqpTemplate amqpTemplate() {
Logger log = LoggerFactory.getLogger(RabbitTemplate.class);
// 使用jackson 消息转换器
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.setEncoding("UTF-8");
// 开启returncallback yml 需要 配置 publisher-returns: true
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {
String correlationId = message.getMessageProperties().getCorrelationId();
log.debug("消息:{} 发送失败, 应答码:{} 原因:{} 交换机: {} 路由键: {}", correlationId, replyCode, replyText, exchange, routingKey);
});
// 消息确认 yml 需要配置 publisher-returns: true
rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
if (ack) {
log.debug("消息发送到exchange成功,id: {}", correlationData.getId());
} else {
log.debug("消息发送到exchange失败,原因: {}", cause);
}
});
return rabbitTemplate;
}
至此集成工作已经全部实现完毕了
7.4 direct类型
重定向模式 发送至exchange下满足规则的queue
7.4.1定义direct类型队列、交换机并绑定:
/* ----------------------------------------------------------------------------Direct exchange test--------------------------------------------------------------------------- */
/**
* 声明直连交换机 支持持久化.
*
* @return the exchange
*/
@Bean("directExchange")
public Exchange directExchange() {
return ExchangeBuilder.directExchange("DIRECT_EXCHANGE").durable(true).build();
}
/**
* 声明一个队列 支持持久化.
*
* @return the queue
*/
@Bean("directQueueA")
public Queue directQueueA() {
return QueueBuilder.durable("DIRECT_QUEUE_A").build();
}
/**
* 声明一个队列 支持持久化.
*
* @return the queue
*/
@Bean("directQueueB")
public Queue directQueueB() {
return QueueBuilder.durable("DIRECT_QUEUE_B").build();
}
/**
* 通过绑定键 将指定队列绑定到一个指定的交换机 .
*
* @param queue the queue
* @param exchange the exchange
* @return the binding
*/
@Bean
public Binding directBindingA(@Qualifier("directQueueA") Queue queue, @Qualifier("directExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("DIRECT_ROUTING_KEY_A").noargs();
}
/**
* 通过绑定键 将指定队列绑定到一个指定的交换机 .
*
* @param queue the queue
* @param exchange the exchange
* @return the binding
*/
@Bean
public Binding directBindingB(@Qualifier("directQueueB") Queue queue, @Qualifier("directExchange") Exchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with("DIRECT_ROUTING_KEY_B").noargs();
}
7.4.2 controller中发送消息
/**
* 测试Direct模式.
*direct类型的Exchange路由规则也很简单,它会把消息路由到那些binding key与routing key完全匹配的Queue中
* @param p the p
* @return the response entity
*/
@RequestMapping("/direct")
public ResponseEntity direct(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
//routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
//binding key与routing key一样也是句点号“. ”分隔的字符串
//binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
// 如果写成 "DIRECT_ROUTING_KEY_.*" 则为topic模式 即模糊匹配
rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY_A", p, correlationData);
return ResponseEntity.ok();
}
7.4.3 receiver端接收消息
/**
* DIRECT模式.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"DIRECT_QUEUE_A"})
public void messageA(Message message, Channel channel) throws IOException {
log.debug("DIRECTA "+new String (message.getBody()));
}
/**
* DIRECT模式.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"DIRECT_QUEUE_B"})
public void messageB(Message message, Channel channel) throws IOException {
log.debug("DIRECTB "+new String (message.getBody()));
}
只有队列A能够存储消息、消费者A能够收到消息
7.5 fanout类型
广播模式 发送到exchange下所有的queue
7.5.1定义fanout类型队列、交换机并绑定:
/* ----------------------------------------------------------------------------Fanout exchange test--------------------------------------------------------------------------- */
/**
* 声明 fanout 交换机.
*
* @return the exchange
*/
@Bean("fanoutExchange")
public FanoutExchange fanoutExchange() {
return (FanoutExchange) ExchangeBuilder.fanoutExchange("FANOUT_EXCHANGE").durable(true).build();
}
/**
* Fanout queue A.
*
* @return the queue
*/
@Bean("fanoutQueueA")
public Queue fanoutQueueA() {
return QueueBuilder.durable("FANOUT_QUEUE_A").build();
}
/**
* Fanout queue B .
*
* @return the queue
*/
@Bean("fanoutQueueB")
public Queue fanoutQueueB() {
return QueueBuilder.durable("FANOUT_QUEUE_B").build();
}
/**
* 绑定队列A 到Fanout 交换机.
*
* @param queue the queue
* @param fanoutExchange the fanout exchange
* @return the binding
*/
@Bean
public Binding bindingA(@Qualifier("fanoutQueueA") Queue queue, @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
/**
* 绑定队列B 到Fanout 交换机.
*
* @param queue the queue
* @param fanoutExchange the fanout exchange
* @return the binding
*/
@Bean
public Binding bindingB(@Qualifier("fanoutQueueB") Queue queue, @Qualifier("fanoutExchange") FanoutExchange fanoutExchange) {
return BindingBuilder.bind(queue).to(fanoutExchange);
}
7.5.2 controller中发送消息
/**
* 测试广播模式.
* fanout类型的Exchange路由规则非常简单,它会把所有发送到该Exchange的消息路由到所有与它绑定的Queue中。
* @param p the p
* @return the response entity
*/
@RequestMapping("/fanout")
public ResponseEntity send(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.convertAndSend("FANOUT_EXCHANGE", "", p, correlationData);
return ResponseEntity.ok();
}
7.5.3 receiver端接收消息
/**
* FANOUT广播队列监听一.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"FANOUT_QUEUE_A"})
public void on(Message message, Channel channel) throws IOException {
log.debug("FANOUT_QUEUE_A "+new String(message.getBody()));
}
/**
* FANOUT广播队列监听二.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"FANOUT_QUEUE_B"})
public void t(Message message, Channel channel) throws IOException {
log.debug("FANOUT_QUEUE_B "+new String(message.getBody()));
}
AB 都能接收到消息
7.6 diedletter类型(延迟消费类型)
A队列接收 过期后转发至B队列 B队列被消费 实现消息的延迟消费
原理图:
7.6.1定义diedletter类型队列、交换机并绑定:
/*----------------------------------------------------------------------------deadletter queue------------------------------------------------------------------------------*/
/**
* 死信队列跟交换机类型没有关系 不一定为directExchange 不影响该类型交换机的特性.
*
* @return the exchange
*/
@Bean("deadLetterExchange")
public Exchange deadLetterExchange() {
return ExchangeBuilder.directExchange("DL_EXCHANGE").durable(true).build();
}
/**
* 声明一个死信队列.
* x-dead-letter-exchange 对应 死信交换机
* x-dead-letter-routing-key 对应 死信队列
*
* @return the queue
*/
@Bean("deadLetterQueue")
public Queue deadLetterQueue() {
Map<String, Object> args = new HashMap<>(2);
// x-dead-letter-exchange 声明 死信交换机
args.put(DEAD_LETTER_QUEUE_KEY, "DL_EXCHANGE");
// x-dead-letter-routing-key 声明 死信路由键
args.put(DEAD_LETTER_ROUTING_KEY, "KEY_R");
return QueueBuilder.durable("DL_QUEUE").withArguments(args).build();
}
/**
* 定义死信队列转发队列.
*
* @return the queue
*/
@Bean("redirectQueue")
public Queue redirectQueue() {
return QueueBuilder.durable("REDIRECT_QUEUE").build();
}
/**
* 死信路由通过 DL_KEY 绑定键绑定到死信队列上.
*
* @return the binding
*/
@Bean
public Binding deadLetterBinding() {
return new Binding("DL_QUEUE", Binding.DestinationType.QUEUE, "DL_EXCHANGE", "DL_KEY", null);
}
/**
* 死信路由通过 KEY_R 绑定键绑定到死信队列上.
*
* @return the binding
*/
@Bean
public Binding redirectBinding() {
return new Binding("REDIRECT_QUEUE", Binding.DestinationType.QUEUE, "DL_EXCHANGE", "KEY_R", null);
}
7.6.2 controller中发送消息
/**
* 测试死信队列.
*
* @param p the p
* @return the response entity
*/
@RequestMapping("/dead")
public ResponseEntity deadLetter(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 声明消息处理器 这个对消息进行处理 可以设置一些参数 对消息进行一些定制化处理 我们这里 来设置消息的编码 以及消息的过期时间 因为在.net 以及其他版本过期时间不一致 这里的时间毫秒值 为字符串
MessagePostProcessor messagePostProcessor = message -> {
MessageProperties messageProperties = message.getMessageProperties();
// 设置编码
messageProperties.setContentEncoding("utf-8");
// 设置过期时间10*1000毫秒
messageProperties.setExpiration("2000");
return message;
};
// 向DL_QUEUE 发送消息 10*1000毫秒后过期 形成死信
rabbitTemplate.convertAndSend("DL_EXCHANGE", "DL_KEY", p, messagePostProcessor, correlationData);
return ResponseEntity.ok();
}
7.6.3 receiver端接收消息
/**
* 监听替补队列 来验证死信.
*
* @param message the message
* @param channel the channel
* @throws IOException the io exception 这里异常需要处理
*/
@RabbitListener(queues = {"REDIRECT_QUEUE"})
public void redirect(Message message, Channel channel) throws IOException {
log.debug("dead message 10s 后 消费消息 {}",new String (message.getBody()));
}
过一会替补队列接收到了消息
7.7 topic类型
模糊匹配模式 这个routingkey与bindingkey采用正则通配符形式模糊匹配
7.7.1定义topic类型队列、交换机并绑定:
同7.4.1
7.7.2 controller中发送消息
/**
* 测试Direct模式.
*direct类型的Exchange路由规则也很简单,它会把消息路由到那些binding key与routing key完全匹配的Queue中
* @param p the p
* @return the response entity
*/
@RequestMapping("/direct")
public ResponseEntity direct(String p) {
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
//routing key为一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词),如“stock.usd.nyse”、“nyse.vmw”、“quick.orange.rabbit”
//binding key与routing key一样也是句点号“. ”分隔的字符串
//binding key中可以存在两种特殊字符“*”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)
// 如果写成 "DIRECT_ROUTING_KEY_.*" 则为topic模式 即模糊匹配
rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY_.*", p, correlationData);
return ResponseEntity.ok();
}
7.7.3 receiver端接收消息
同7.4.3
这个时候AB队列都能够收到消息,且他们的监听这也能监听到同样的消息
8.上代码
这个源码亲测可用的哈,下载后直接浏览器调用controller对应的demo就能看验证效果了
下载地址:https://pan.baidu.com/s/1J3JQoyNv0_gfiy_p3hrZZg