一. RabbitMq官网的下载:
-
由于rabbitmq是由erlang语言编写的,所以需要erlang的支持,erlang下载路径:
-
erlang和rabbitmq的版本对应关系:
- erlang下载安装后,在本机的环境变量里,添加新的
系统变量
变量名:ERLANG_HOME
变量值:rabbitmq的安装路径
-
下载安装完成之后在下载的rabbitmq_server路径下进入cmd窗口:
列如:D:\SoftWare\RabbitMq\rabbitmq_server-3.12.10\sbin
- 在cmd窗口输入命令启动rabbitmq服务:
start rabbitmq-service.bat
- 在浏览器上访问rabbitmq地址:http://127.0.0.1:15672/
- 登入rabbitmq的初始账号密码为:
账号: guest
密码: guest
二.RabbitMq在Spring中的使用:
- 创建一个父工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.chenshi</groupId>
<artifactId>rabbitmq_practice</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!--子模块-->
<modules>
<module>spring-producer</module>
<module>spring-consumer</module>
</modules>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!--spring整合rabbitmq-->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>2.4.3</version>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 创建一个Producer生产者子工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.chenshi</groupId>
<artifactId>rabbitmq_practice</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-producer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 创建一个Consumer消费者子工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.chenshi</groupId>
<artifactId>rabbitmq_practice</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>spring-consumer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 在Producer生产者和Consumer消费者工程中加入properties配置文件:
rabbitmq.host = localhost
rabbitmq.port = 5672
rabbitmq.username = admin
rabbitmq.password = 123456
rabbitmq.virtual-host = /
- 在Producer生产者和Consumer消费者工程中加入xml配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:rabbitL="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/rabbit
http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="rabbitmq.properties"/>
<!--rabbitmq的连接工厂-->
<rabbit:connection-factory id="factory"
virtual-host="${rabbitmq.virtual-host}"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"/>
<!--rabbitmq的管理,包括插件的启用-->
<rabbit:admin connection-factory="factory"/>
<!--template: 第三方的组件-->
<rabbit:template id="rabbitTemplate" connection-factory="factory"/>
</beans>
1. 简单队列的实现:
- 简单队列:一个消费者,对应一个生产者
- 在Procuder工程下的xml配置文件中声明一个队列:
<!--==================================简单模式========================================-->
<!--声明一个队列-->
<rabbit:queue id="spring-queue" name="spring-queue" auto-declare="true" durable="true" auto-delete="false"/>
- 在Producer中的test目录下进行消息发送测试:
package rabbitmq_practice;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class RabbitmqTest {
@Resource private RabbitTemplate rabbitTemplate;
@Test
public void simple(){
String msg = "简单队列的消息发送";
for (int i = 0; i < 30; i++) {
rabbitTemplate.convertAndSend("","simple-queue",msg + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("消息已发送");
}
}
- 消息发送后会在队列中,等待被消费:
image-->
- 在Consumer工程中搭建一个监听类:
package com.chenshi.rabbitmq.listener;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import java.util.Arrays;
public class QueueListener implements MessageListener {
private static MessageProperties messageProperties;
@Override
public void onMessage(Message message) {
System.out.println(new String(message.getBody()));
}
}
- 在Consumer中xml文件里设置监听类,以及声明监听容器:
<!--声明一个监听类-->
<bean id="queueListener" class="com.chenshi.rabbitmq.listener.QueueListener"/>
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener" queue-names="simple-queue"/>
</rabbit:listener-container>
- 开启Consumer消费者的服务,就能持续监听到队列消息:
package rabbitmq_practice;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@ContextConfiguration(locations = "classpath:spring-rabbitmq.xml")
public class RabbitmqTest {
@Test
public void durable(){
while (true) {
}
}
}
2. 包工头队列的实现:
- 包工头队列:消费者同时争抢一个队列的消息
- 在Producer生产者的xml配置文件中声明两个交换机:
<!--==================================包工头模式=======================================-->
<!--声明两个队列-->
<rabbit:queue id="work-queue1" name="work-queue1" auto-declare="true" durable="true" auto-delete="false"/>
<rabbit:queue id="work-queue2" name="work-queue2" auto-declare="true" durable="true" auto-delete="false"/>
- 在Producer生产者的test目录下进行消息发送测试:
@Test
public void work(){
String msg = "包工头队列的消息发送";
for (int i = 0; i < 20; i++) {
rabbitTemplate.convertAndSend("","work-queue1",msg);
rabbitTemplate.convertAndSend("","work-queue2",msg);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("消息已发送");
}
- 在Consumer消费者的xml配置文件中加入这两个队列,启动Consumer工程:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,work-queue2"/>
</rabbit:listener-container>
3. 广播模式队列的实现:
- 广播模式队列:发送一条消息会同时给多个消费者
- 在Producer生产者的xml配置文件中,声明两个队列,一个交换机,将这两个队列,绑定到这个交换机上:
<!--==================================广播模式========================================-->
<!--声明两个队列-->
<rabbit:queue id="fanout-queue1" name="fanout-queue1" auto-declare="true" durable="true" auto-delete="false"/>
<rabbit:queue id="fanout-queue2" name="fanout-queue2" auto-declare="true" durable="true" auto-delete="false"/>
<!--声明一个交换机-->
<rabbit:fanout-exchange id="fanoutExchange" name="fanoutExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="fanout-queue1"/>
<rabbit:binding queue="fanout-queue2"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
- 在Producer生产者工程的test目录下发送消息进行测试:
@Test
public void fanout(){
String msg = "广播队列的消息发送";
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("fanoutExchange","",msg + i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("消息已发送");
}
- 在Consumer消费者的xml配置文件中加入这两个广播类型的队列,启动Consumer工程:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,work-queue2,fanout-queue1,fanout-queue2"/>
</rabbit:listener-container>
4. 路由模式队列的实现:
- 路由队列模式:交换机根据设置好的路由键,向对应的队列发送消息
- 在Producer生产者工程的xml配置文件,声明两个队列,一个交换机:
<!--==================================路由模式========================================-->
<!--声明两个队列-->
<rabbit:queue id="routing-queue1" name="routing-queue1" auto-declare="true" durable="true" auto-delete="false"/>
<rabbit:queue id="routing-queue2" name="routing-queue2" auto-declare="true" durable="true" auto-delete="false"/>
<!--声明一个交换机-->
<rabbit:direct-exchange id="directExchange" name="directExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="routing-queue1" key="info"/>
<rabbit:binding queue="routing-queue2" key="info"/>
<rabbit:binding queue="routing-queue2" key="error"/>
<rabbit:binding queue="routing-queue2" key="warning"/>
</rabbit:bindings>
</rabbit:direct-exchange>
- 在Producer生产者工程的test目录下发送消息进行测试:
@Test
public void routing(){
String info = "内蒙古呼伦贝冬季奥运会举办成功";
String error = "全球首例新冠患者出现";
String warning = "地球能源枯竭";
rabbitTemplate.convertAndSend("directExchange","info","info:" + info);
rabbitTemplate.convertAndSend("directExchange","warning","warning:" + warning);
System.out.println("消息发送成功");
}
- 在Consumer消费者的xml配置文件中加入这两个路由键类型的队列,启动Consumer工程:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2"/>
</rabbit:listener-container>
5. 通配符模式队列的实现:
- 通配符模式队列:交换机通过一个,指定通配符,向队列发送消息
- 在Producer生产者工程的xml配置文件下,声明两个队列,一个交换机:
<!--==================================通配符模式=======================================-->
<!--声明两个队列-->
<rabbit:queue id="topic-queue1" name="topic-queue1" auto-declare="true" durable="true" auto-delete="false"/>
<rabbit:queue id="topic-queue2" name="topic-queue2" auto-declare="true" durable="true" auto-delete="false"/>
<!--声明一个交换机-->
<rabbit:topic-exchange id="topicExchange" name="topicExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="topic-queue1" pattern="*.sichuan.*"></rabbit:binding>
<rabbit:binding queue="topic-queue2" pattern="*.*.guangzhou"></rabbit:binding>
<rabbit:binding queue="topic-queue2" pattern="china.#"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
- 在Producer生产者工程的test目录下发送消息进行测试:
@Test
public void topic(){
String sichuanInfo = "四川发生地震,绵阳地带震感强烈";
String guangzhouInfo = "广州发生洪涝,珠江水位上涨";
String chinaInfo = "中国GDP同比增长8.9%";
rabbitTemplate.convertAndSend("topicExchange","china.sichuan.mianyang",sichuanInfo);
rabbitTemplate.convertAndSend("topicExchange","china.guangdong.guangzhou",guangzhouInfo);
rabbitTemplate.convertAndSend("topicExchange","china.newsInfo",chinaInfo);
System.out.println("消息发送成功");
}
- 在Consumer消费者的xml配置文件中加入这两个通配符类型的队列,启动Consumer工程:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2,
topic-queue1,topic-queue2"/>
</rabbit:listener-container>
三.RabbitMq在SpringBoot中的使用:
1. 搭建SpringBoot项目:
- 搭建一个空的父工程项目:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
</parent>
<artifactId>springboot_rabbitmq</artifactId>
<packaging>pom</packaging>
<modules>
<module>rabbitmq_producer</module>
<module>rabbitmq_consumer</module>
</modules>
<dependencies>
<!-- RabbitMQ 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.6.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.7.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 搭建一个Producer子工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>springboot_rabbitmq</artifactId>
<version>2.7.17</version>
</parent>
<groupId>com.chenshi</groupId>
<artifactId>rabbitmq_producer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 搭建一个Consumer子工程:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>springboot_rabbitmq</artifactId>
<version>2.7.17</version>
</parent>
<groupId>com.chenshi</groupId>
<artifactId>rabbitmq_consumer</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
- 创建一个Producer的Yml配置文件:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
- 创建一个Consumer的Yml配置文件:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
- 在Producer和Consumer的java目录下,创建一个Producer启动类和Consumer启动类:
package com.chenshi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProducerApp {
public static void main(String[] args) {
SpringApplication.run(ProducerApp.class,args);
}
}
2. Producer生产者:
- 在Producer的java目录下创建一个RabbitmqConfig类,并声明一个队列:
package com.chenshi.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String SIMPLE_QUEUE = "simple-queue";
@Bean(SIMPLE_QUEUE)
public Queue simple_queue(){
return new Queue(SIMPLE_QUEUE,true,false,false,null);
}
}
- 在Producer的test目录下测试消息发送:
package com.chenshi.rabbitmq;
import com.chenshi.config.RabbitmqConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
@Resource private RabbitTemplate rabbitTemplate;
@Test
public void simple(){
String msg = "简单队列的消息发送";
rabbitTemplate.convertAndSend("", RabbitmqConfig.SIMPLE_QUEUE,msg);
System.out.println("消息发送成功");
}
}
3. Consumer消费者:
- 在Consumer的java目录下创建一个RabbitmqConfig类:
package com.chenshi.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String SIMPLE_QUEUE = "simple-queue";
@Bean(SIMPLE_QUEUE)
public Queue simple_queue(){
return new Queue(SIMPLE_QUEUE,true,false,false,null);
}
}
- 在Consumer的java目录下创建一个listener类,并声明监听的队列:
package com.chenshi.listener;
import com.chenshi.config.RabbitmqConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
@Component
public class QueueListener implements ChannelAwareMessageListener {
@Override
@RabbitListener(queues = RabbitmqConfig.SIMPLE_QUEUE)//监听哪个队列
public void onMessage(Message message, Channel channel) throws Exception {
System.out.println(new String(message.getBody()));
}
}
- 在Consumer的test目录下,启动Consumer进行消息接收测试:
package com.chenshi.rabbitmq;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ConsumerTest {
@Test
public void durable(){
while (true) {
}
}
}
4. 广播模式队列的实现:
- 在Producer的RabbitmqConfig下声明两个队列:
//声明两个广播队列
@Bean(FANOUT_QUEUE1)
public Queue fanout_queue1(){
return new Queue(FANOUT_QUEUE1,true,false,false,null);
}
@Bean(FANOUT_QUEUE2)
public Queue fanout_queue2(){
return new Queue(FANOUT_QUEUE2,true,false,false,null);
}
- 在Producer的RabbitmqConfig下声明这个交换机:
//声明一个交换机
@Bean(FANOUT_EXCHANGE)
public FanoutExchange fanout_exchange(){
return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE).durable(true).build();
}
- 在Producer的RabbitmqConfig将两个队列绑定到,该交换机:
//队列绑定交换机
@Bean
public Binding queue_binding_fanout1(@Qualifier(FANOUT_QUEUE1) Queue queue,
@Qualifier(FANOUT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
@Bean
public Binding queue_binding_fanout2(@Qualifier(FANOUT_QUEUE2) Queue queue,
@Qualifier(FANOUT_EXCHANGE)Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
- 在Producer的test目录下发送测试消息:
@Test
public void fanout(){
String msg = "广播队列的消息发送";
rabbitTemplate.convertAndSend(RabbitmqConfig.FANOUT_EXCHANGE,"",msg);
System.out.println("消息发送成功");
}
- 同样,Consumer下的Config类也需要声明,交换机和队列,并进行绑定:
package com.chenshi.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String SIMPLE_QUEUE = "simple-queue";
public static final String FANOUT_QUEUE1 = "fanout-queue1";
public static final String FANOUT_QUEUE2 = "fanout-queue2";
public static final String FANOUT_EXCHANGE = "fanout-exchange";
@Bean(SIMPLE_QUEUE)
public Queue simple_queue(){
return new Queue(SIMPLE_QUEUE,true,false,false,null);
}
//声明两个广播队列
@Bean(FANOUT_QUEUE1)
public Queue fanout_queue1(){
return new Queue(FANOUT_QUEUE1,true,false,false,null);
}
@Bean(FANOUT_QUEUE2)
public Queue fanout_queue2(){
return new Queue(FANOUT_QUEUE2,true,false,false,null);
}
//声明一个交换机
@Bean(FANOUT_EXCHANGE)
public FanoutExchange fanout_exchange(){
return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE).durable(true).build();
}
//队列绑定交换机
@Bean
public Binding queue_binding_fanout1(@Qualifier(FANOUT_QUEUE1) Queue queue,
@Qualifier(FANOUT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
@Bean
public Binding queue_binding_fanout2(@Qualifier(FANOUT_QUEUE2) Queue queue,
@Qualifier(FANOUT_EXCHANGE)Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
}
- 在Consumer下的listener类,声明一个广播队列的监听,然后启动Consumer:
/*广播模式队列*/
@RabbitListeners({
@RabbitListener(queues = RabbitmqConfig.FANOUT_QUEUE1),
@RabbitListener(queues = RabbitmqConfig.FANOUT_QUEUE2)})
public void fanoutMessage(Message message,Channel channel){
System.out.println(new String(message.getBody()));
}
5. 路由模式队列的实现:
- 在Producer的Config下声明两个队列:
/*声明两个路由队列*/
@Bean(DIRECT_QUEUE1)
public Queue routing_queue1(){
return new Queue(DIRECT_QUEUE1,true,false,false,null);
}
@Bean(DIRECT_QUEUE2)
public Queue routing_queue2(){
return new Queue(DIRECT_QUEUE2,true,false,false,null);
}
- 在Producer的Config下声明一个交换机:
/*声明一个路由交换机*/
@Bean(DIRECT_EXCHANGE)
public DirectExchange routing_exchange(){
return ExchangeBuilder.directExchange(DIRECT_EXCHANGE).durable(true).build();
}
- 在Producer和Consumer中,java目录下创建一个RoutingKey枚举类:
package com.chenshi.utils;
public enum RoutingKey {
INFO("info"),
ERROR("error"),
Warning("warning");
private final String routingKey;
RoutingKey(String routingKey) {
this.routingKey = routingKey;
}
public String getRoutingKey() {
return routingKey;
}
}
- 在Producer的Config下,将这两个队列绑定到,路由交换机上:
/*队列绑定交换机*/
//绑定Info
@Bean
public Binding queue_binding_direct1(@Qualifier(DIRECT_QUEUE1) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.INFO).noargs();
}
//绑定Info
@Bean
public Binding queue_binding_direct2Info(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.INFO).noargs();
}
//绑定Error
@Bean
public Binding queue_binding_direct2Error(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.ERROR).noargs();
}
//绑定Warning
@Bean
public Binding queue_binding_direct2Warning(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.Warning).noargs();
}
- 同时在Consumer的Config下也需要声明两个队列,一个交换机,绑定在一起:
package com.chenshi.config;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String DIRECT_QUEUE1 = "direct-queue1";
public static final String DIRECT_QUEUE2 = "direct-queue2";
public static final String DIRECT_EXCHANGE = "direct-exchange";
/*声明一个路由交换机*/
@Bean(DIRECT_EXCHANGE)
public DirectExchange routing_exchange(){
return ExchangeBuilder.directExchange(DIRECT_EXCHANGE).durable(true).build();
}
/*队列绑定交换机*/
@Bean
public Binding queue_binding_direct1(@Qualifier(DIRECT_QUEUE1) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.INFO).noargs();
}
@Bean
public Binding queue_binding_direct2Info(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.INFO).noargs();
}
@Bean
public Binding queue_binding_direct2Error(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.ERROR).noargs();
}
@Bean
public Binding queue_binding_direct2Warning(@Qualifier(DIRECT_QUEUE2) Queue queue,
@Qualifier(DIRECT_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.Warning).noargs();
}
}
- 在Consumer的listener类中监听这两个队列,启动Consumer服务:
/*路由模式队列*/
@RabbitListeners({
@RabbitListener(queues = RabbitmqConfig.DIRECT_QUEUE1),
@RabbitListener(queues = RabbitmqConfig.DIRECT_QUEUE2)})
public void directMsg(Message message,Channel channel){
System.out.println(new String(message.getBody()));
}
6. 通配符模式队列的实现:
- 在Producer中的Config下声明两个队列,一个交换机:
/*声明两个通配符队列*/
@Bean(TOPIC_QUEUE1)
public Queue topicQueue1(){
return new Queue(TOPIC_QUEUE1,true,false,false,null);
}
@Bean(TOPIC_QUEUE2)
public Queue topicQueue2(){
return new Queue(TOPIC_QUEUE2,true,false,false,null);
}
/*声明一个主题交换机*/
@Bean(TOPIC_EXCHANGE)
public TopicExchange topicExchange(){
return ExchangeBuilder.topicExchange(TOPIC_EXCHANGE).durable(true).build();
}
- 将这两个队列,绑定在这个交换机:
/*队列绑定到主题交换机*/
@Bean
public Binding queueBindingTopic1(@Qualifier(TOPIC_QUEUE1) Queue queue,
@Qualifier(TOPIC_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.BeijingNews).noargs();
}
@Bean
public Binding queueBindingTopic2HOHHOT(@Qualifier(TOPIC_QUEUE2) Queue queue,
@Qualifier(TOPIC_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.HohhotNews).noargs();
}
@Bean
public Binding queueBindingTopic2CN(@Qualifier(TOPIC_QUEUE2) Queue queue,
@Qualifier(TOPIC_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with(RoutingKey.ChinaNews).noargs();
}
- 在Producer的test目录下进行消息发送测试:
@Test
public void topic(){
String bjNews = "北京大兴机场正式启用";
String hohhotNews = "呼和浩特市进入大雪黄色预警";
String CnNews = "中国每月薪资约有4亿人不超过3000RMB";
rabbitTemplate.convertAndSend(RabbitmqConfig.TOPIC_EXCHANGE, String.valueOf(RoutingKey.BeijingNews),bjNews);
rabbitTemplate.convertAndSend(RabbitmqConfig.TOPIC_EXCHANGE, String.valueOf(RoutingKey.HohhotNews),hohhotNews);
rabbitTemplate.convertAndSend(RabbitmqConfig.TOPIC_EXCHANGE, String.valueOf(RoutingKey.ChinaNews),CnNews);
System.out.println("消息发送成功");
}
- 在Consumer的listener类中,监听这两个队列,启动Consumer服务:
/*通配符模式队列*/
@RabbitListeners({
@RabbitListener(queues = RabbitmqConfig.TOPIC_QUEUE1),
@RabbitListener(queues = RabbitmqConfig.TOPIC_QUEUE2)})
public void topicMsg(Message message,Channel channel){
System.out.println(new String(message.getBody()));
}
四. 消息可靠性投递:
1. spring-rabbitmq的消息可靠性投递-确认模式(Confirm):
- 在Producer的xml配置文件中,给rabbitmq连接工厂,添加参数:
<!--rabbitmq的连接工厂-->
<rabbit:connection-factory id="factory"
virtual-host="${rabbitmq.virtual-host}"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
confirm-type="CORRELATED"/>
- 在Producer的xml配置文件中,声明一个队列,一个交换机:
<!--==================================消息可靠性投递====================================-->
<!--声明一个队列-->
<rabbit:queue id="confirm-queue" name="confirm-queue" auto-declare="true" durable="true" auto-delete="false"/>
<!--声明一个交换机-->
<rabbit:direct-exchange id="confirmExchange" name="confirmExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding queue="confirm-queue" key="confirm"></rabbit:binding>
</rabbit:bindings>
</rabbit:direct-exchange>
- 在Producer的test目录下,定义confirmcallback的回调函数,并发送测试信息:
@Test
public void confirm(){
// 设置消息接受确认的回调函数
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
//判断该交换机是否接受到该消息
if (ack) {
System.out.println("消息接收成功!: " + cause);
} else {
System.out.println("消息接收失败!: " + cause);
}
}
});
String msg = "这是一条confirm消息";
rabbitTemplate.convertAndSend("confirmExchange","confirm",msg);
}
- 在Consumer下的xml配置文件中声明监听容器,添加confirm队列,并启动Consumer服务:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2,
topic-queue1,topic-queue2,confirm-queue"/>
</rabbit:listener-container>
2. spring-rabbitmq的消息可靠性投递-回退模式(return):
- 在Producer的xml配置文件中,给rabbitmq连接工厂,添加return模式的配置:
<!--rabbitmq的连接工厂-->
<rabbit:connection-factory id="factory"
virtual-host="${rabbitmq.virtual-host}"
host="${rabbitmq.host}"
port="${rabbitmq.port}"
username="${rabbitmq.username}"
password="${rabbitmq.password}"
confirm-type="CORRELATED"
publisher-returns="true"/>
- 在Producer的test目录下发送测试消息,并启动Consumer服务进行测试:
@Test
public void returnTest(){
//设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
//实现回调函数
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("ReplyCode: " + returned.getReplyCode());
System.out.println("ReplyText: " + returned.getReplyText());
System.out.println("Exchange: " + returned.getExchange());
System.out.println("RoutingKey: " + returned.getRoutingKey());
System.out.println("Message" + returned.getMessage());
}
});
String msg = "这是一个return模式";
rabbitTemplate.convertAndSend("confirmExchange","confirm",msg);
}
- Producer发送消息失败后,回退模式的回调响应:
@Test
public void returnTest(){
//设置交换机处理失败消息的模式
rabbitTemplate.setMandatory(true);
//实现回调函数
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("ReplyCode: " + returned.getReplyCode());
System.out.println("ReplyText: " + returned.getReplyText());
System.out.println("Exchange: " + returned.getExchange());
System.out.println("RoutingKey: " + returned.getRoutingKey());
System.out.println("Message" + returned.getMessage());
}
});
String msg = "这是一个return模式";
//rabbitTemplate.convertAndSend("confirmExchange","confirm",msg);
//测试出错后的回退回调
rabbitTemplate.convertAndSend("confirmExchange","nothing",msg);
}
- 出错后的回调函数的响应结果:
ReplyCode: 312 //错误码
ReplyText: NO_ROUTE //错误原因
Exchange: confirmExchange //交换机名
RoutingKey: nothing //路由键
Message(Body:'这是一个return模式' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0]) //消息体
3. springboot-rabbitmq的消息可靠性投递-确认模式(confirm):
- 在Producer的yml配置文件中,添加配置:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
publisher-confirm-type: correlated
- 在Producer的config配置类下,声明一个交换机,和一个队列:
package com.chenshi.config;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String CONFIRM_QUEUE = "confirm-queue";
public static final String CONFIRM_EXCHANGE = "confirm_exchange";
/*声明一个确认队列*/
@Bean(CONFIRM_QUEUE)
public Queue confirmQueue(){
return new Queue(CONFIRM_QUEUE,true,false,false,null);
}
/*声明一个确认交换机*/
@Bean(CONFIRM_EXCHANGE)
public DirectExchange confirmExchange(){
return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE).durable(true).build();
}
/*将确认队列绑定在,确认交换机*/
@Bean
public Binding queueBindingConfirm(@Qualifier(CONFIRM_QUEUE) Queue queue,
@Qualifier(CONFIRM_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("confirm").noargs();
}
}
- 在Producer的test目录下,发送消息进行测试:
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProducerTest {
@Resource private RabbitTemplate rabbitTemplate;
@Test
public void confirm(){
//设置消息可靠性的回调函数
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
System.out.println("消息成功消费");
} else {
System.out.println("消息消费失败:" + cause);
}
}
});
String msg = "这是一条confirm确认消息";
rabbitTemplate.convertAndSend(RabbitmqConfig.CONFIRM_EXCHANGE,"confirm",msg);
System.out.println("消息发送成功");
}
}
- 在Consumer的config下,也同样声明相同的交换机,和队列:
package com.chenshi.config;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String CONFIRM_QUEUE = "confirm-queue";
public static final String CONFIRM_EXCHANGE = "confirm_exchange";
/*声明一个确认队列*/
@Bean(CONFIRM_QUEUE)
public Queue confirmQueue(){
return new Queue(CONFIRM_QUEUE,true,false,false,null);
}
/*声明一个确认交换机*/
@Bean(CONFIRM_EXCHANGE)
public DirectExchange confirmExchange(){
return ExchangeBuilder.directExchange(CONFIRM_EXCHANGE).durable(true).build();
}
/*将确认队列绑定在,确认交换机*/
@Bean
public Binding queueBindingConfirm(@Qualifier(CONFIRM_QUEUE) Queue queue,
@Qualifier(CONFIRM_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("confirm").noargs();
}
}
- 在Consumer的listener类中,监听这个队列:
package com.chenshi.listener;
import com.chenshi.config.RabbitmqConfig;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.annotation.RabbitListeners;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
@Component
public class QueueListener implements ChannelAwareMessageListener {
/*监听确认模式队列*/
@RabbitListener(queues = RabbitmqConfig.CONFIRM_QUEUE)
public void confirmMsg(Message message,Channel channel){
System.out.println(new String(message.getBody()));
}
}
4. springboot-rabbitmq的消息可靠性投递-回退模式(return):
- 在Producer的yml配置文件中添加配置:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
publisher-confirm-type: correlated
publisher-returns: true
- 在Producer的Test目录下发送消息进行测试:
@Test
public void returnTest(){
//设置消息回退模式调用函数
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("ReplyCode: " + returned.getReplyCode());
System.out.println("ReplyText: " + returned.getReplyText());
System.out.println("Exchange: " + returned.getExchange());
System.out.println("RoutingKey: " + returned.getRoutingKey());
System.out.println("Message" + returned.getMessage());
}
});
String msg = "这是一个return回退消息";
rabbitTemplate.convertAndSend(RabbitmqConfig.CONFIRM_EXCHANGE,"confirm",msg);
}
- 当向交换机发送消息,失败时会调用回退函数:
ReplyCode: 312
ReplyText: NO_ROUTE
Exchange: confirm_exchange
RoutingKey: noKeyLa
Message(Body:'这是一个return回退消息' MessageProperties [headers={}, contentType=text/plain, contentEncoding=UTF-8, contentLength=0, receivedDeliveryMode=PERSISTENT, priority=0, deliveryTag=0])
五. Consumer Ack(手动应答):
1. spring-rabbitmq的Consumer Ack实现:
- 在Consumer的Xml配置文件中,给声明的监听类,配置为手动应答:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory" acknowledge="manual">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2,
topic-queue1,topic-queue2,confirm-queue"/>
</rabbit:listener-container>
- 在Consumer的listener类下,实现ChannelAwareMessageListener接口,模拟业务成功,和失败
package com.chenshi.rabbitmq.listener;
import com.rabbitmq.client.*;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import javax.xml.transform.OutputKeys;
import java.io.IOException;
@Component
public class QueueListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
//接受消息
System.out.println(new String(message.getBody()));
// int i = 1/0; //出错模拟
//手动签收
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
//拒绝签收,重返队列
channel.basicNack(deliveryTag,false, true);
throw new RuntimeException(e);
}
}
}
2. springboot-rabbitmq的Consumer Ack实现:
- 在Consumer的yml配置文件中的配置:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
- 在Consumer的listener类中,监听该队列:
/*监听确认模式队列*/
@RabbitListener(queues = RabbitmqConfig.CONFIRM_QUEUE)
public void confirmMsg(Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
int requeue = 5;
while (true) {
try {
//int a = 1 / 0;//模拟失败
System.out.println(new String(message.getBody()));
//消息手动应答
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
requeue -= 1;
//消息消费失败 进行重试
channel.basicNack(deliveryTag, true, true);
//重试次数超过5次 不在进行重试
if (requeue < 1) {
System.out.println("消息接受失败多次,不在重回队列");
channel.basicReject(deliveryTag, false);
break;
}
}
}
}
六. 消费端的限流:
1. spring-rabbitmq的消费端限流:
- 在Consumer下的Xml配置文件中,给listener容器新增,prefetch配置,并一定要是手动应答模式:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory" acknowledge="manual" prefetch="1">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2,
topic-queue1,topic-queue2,confirm-queue"/>
</rabbit:listener-container>
- 在Producer的Test目录下发送消息进行测试:
@Test
public void prefetch() throws InterruptedException {
String msg = "这是一条限流测试";
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("confirmExchange","confirm",msg + i);
Thread.sleep(1000);
}
System.out.println("消息发送成功");
}
2. springboot-rabbitmq的消费端限流的实现:
- 在Consumer的yml配置文件下,添加prefetch配置:
spring:
rabbitmq:
virtual-host: /
host: 127.0.0.1
port: 5672
username: admin
password: 123456
listener:
direct:
acknowledge-mode: manual
prefetch: 1
simple:
acknowledge-mode: manual
prefetch: 1
- 在Producer的Test目录下发送消息进行测试,并启动Consumer服务:
@Test
public void prefetch() throws InterruptedException {
String msg = "这是一条prefetch限流消息";
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitmqConfig.CONFIRM_EXCHANGE, "confirm", msg);
Thread.sleep(1000);
}
System.out.println("消息发送成功");
}
七. TTL-队列的存活时间:
1. spring-rabbitmq的TTL实现方式:
- 在Producer的xml配置文件下,声明一个队列和交换机,并配置ttl参数:
<!--==================================TTL===========================================-->
<!--声明一个队列-->
<rabbit:queue id="ttl-queue" name="ttl-queue" auto-declare="true" durable="true" auto-delete="false">
<!--配置ttl参数-->
<rabbit:queue-arguments>
<entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"></entry>
</rabbit:queue-arguments>
</rabbit:queue>
<!--声明一个交换机-->
<rabbit:topic-exchange id="ttlExchange" name="ttlExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding pattern="ttl.#" queue="ttl-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
- 在Producer的Test目录下进行消息发送测试:
@Test
public void ttl(){
String msg = "这是一个TTL消息";
rabbitTemplate.convertAndSend("ttlExchange","ttl.pop",msg);
System.out.println("消息发送成功");
}
- 在Consumer的Xml配置文件中,队列监听容器,新增这个ttl队列,启动Consumer服务:
<!--声明一个监听容器-->
<rabbit:listener-container connection-factory="factory" acknowledge="manual" prefetch="1">
<rabbit:listener ref="queueListener"
queue-names="simple-queue,work-queue1,
work-queue2,fanout-queue1,fanout-queue2,
routing-queue1,routing-queue2,
topic-queue1,topic-queue2,confirm-queue,ttl-queue"/>
</rabbit:listener-container>
- 功能小结:如果说交换机发送消息给,队列之后,10秒之内没被消费,就会过期。
- 在Producer的Test目录下,设置某个消息,单独的存活时间,并进行消息发送测试:
@Test
public void ttlByOrder(){
//消息后处理对象,配置消息的参数信息
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
//设置过期时间
message.getMessageProperties().setExpiration("5000");
return message;
}
};
String msg0 = "这是一个单独的TTL消息";
String msg1 = "这是一个正常的TTL消息";
for (int i = 0; i < 10; i++) {
//第5条消息设置了单独的过期时间
if (i == 5) {
rabbitTemplate.convertAndSend("ttlExchange","ttl.pop",msg0,messagePostProcessor);
}else {
//其余的正常发送
rabbitTemplate.convertAndSend("ttlExchange","ttl.pop",msg1);
}
}
}
2. springboot-rabbitmq的TTL实现方式:
- 在Producer的utils类中,添加一个Argument类,用于实现ttl参数:
package com.chenshi.utils;
import java.util.HashMap;
public class RabbitArgument {
public static HashMap ttl(){
HashMap<String, Integer> ttl = new HashMap<>();
ttl.put("x-message-ttl", 10000);//10秒
return ttl;
}
}
- 在Producer的config配置类中,申明一个交换机,和一个队列,并绑定起来:
public static final String TTL_QUEUE = "ttl-queue";
public static final String TTL_EXCHANGE = "ttl_exchange";
/*声明一个TTL队列*/
@Bean(TTL_QUEUE)
public Queue ttlQueue(){
return new Queue(TTL_QUEUE,true,false,false,RabbitArgument.ttl());
}
/*声明一个交换机*/
@Bean(TTL_EXCHANGE)
public DirectExchange ttlExchange(){
return ExchangeBuilder.directExchange(TTL_EXCHANGE).durable(true).build();
}
/*将队列绑定到交换机*/
@Bean
public Binding queueBidingTTL(@Qualifier(TTL_QUEUE) Queue queue,
@Qualifier(TTL_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("ttl.*").noargs();
}
- 在Producer的Test同目录下,发送消息进行测试:
@Test
public void ttl() throws InterruptedException {
String msg = "这是一条TTL延迟消息";
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitmqConfig.TTL_EXCHANGE,"ttl.*",msg);
Thread.sleep(1000);
}
System.out.println("消息发送成功");
}
八. DLX-死信交换机:
1. spring-rabbitmq的DLX实现方式:
消息成为死信的三种条件:
- 队列消息长度到达限制;
- 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
- 原队列存在消息过期设置,消息到达超时时间未被消费;
- 在Producer的Xml配置文件下,声明一个死信队列和,一个死信交换机:
<!--==================================声明死信队列和交换机==============================-->
<!--声明一个队列-->
<rabbit:queue id="dlx-queue" name="dlx-queue" auto-declare="true" auto-delete="false" durable="true"/>
<!--声明一个交换机-->
<rabbit:topic-exchange id="dlxExchange" name="dlxExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding pattern="dlx.#" queue="dlx-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
- 继续在声明一个正常队列,和正常交换机,将正常队列绑定到死信交换机上:
<!--==================================声明正常队列和交换机==============================-->
<!--声明一个正常队列,并绑定死信交换机-->
<rabbit:queue id="ok-queue" name="ok-queue" auto-declare="true" durable="true" auto-delete="false">
<rabbit:queue-arguments>
<!--将队列绑定到死信交换机-->
<entry key="x-dead-letter-exchange" value-type="dlxExchange"></entry>
<!--绑定死信路由键-->
<entry key="x-dead-letter-routing-key" value="dlx.dead"></entry>
<!--给队列设置长度,实现消息成为死信-->
<entry key="x-max-length" value="5" value-type="java.lang.Integer"></entry>
</rabbit:queue-arguments>
</rabbit:queue>
<!--声明一个交换机-->
<rabbit:topic-exchange id="okExchange" name="okExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding pattern="ok.#" queue="ok-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
- 在Producer的Test目录下进行消息发送测试:
@Test
public void dlx() throws InterruptedException {
String msg = "这是一条dlx测试消息";
//消息发送超过,队列最大值,进入死信交换机
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("okExchange","ok.fine",msg);
}
System.out.println("消息发送成功");
}
- 功能小结:当消息成为死信时,队列会往死信交换机发送,给死信队列。
2. springboot-rabbitmq的DLX实现方式:
- 在Producer的utils目录下,配置一个dlx参数:
package com.chenshi.utils;
import java.util.HashMap;
public class RabbitArgument {
public static HashMap dlx(){
HashMap<String, String> dlx = new HashMap<>();
dlx.put("x-dead-letter-exchange", "dlxExchange");
dlx.put("x-dead-letter-routing-key","dlx.dead");
return dlx;
}
}
- 在Producer下的Config配置类,声明一个正常的交换机和,一个正常的队列,以及一个死信交换机,和死信队列:
package com.chenshi.config;
import com.chenshi.utils.RabbitArgument;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String DLX_QUEUE = "dlx-queue";
public static final String DLX_EXCHANGE = "dlxExchange";
public static final String OK_QUEUE = "ok-queue";
public static final String OK_EXCHANGE = "OK_EXCHANGE";
/*正常的队列*/
@Bean(OK_QUEUE)
public Queue okQueue(){
return new Queue(OK_QUEUE,true,false,false,RabbitArgument.dlx());
}
/*正常的交换机*/
@Bean(OK_EXCHANGE)
public TopicExchange okExchange(){
return ExchangeBuilder.topicExchange(OK_EXCHANGE).durable(true).build();
}
/*队列绑定正常交换机*/
@Bean
public Binding queueBidingOK(@Qualifier(OK_QUEUE) Queue queue,
@Qualifier(OK_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("ok.#").noargs();
}
/*死信队列*/
@Bean(DLX_QUEUE)
public Queue dleQueue(){
return new Queue(DLX_QUEUE,true,false,false,null);
}
/*死信交换机*/
@Bean(DLX_EXCHANGE)
public TopicExchange dlxExchange(){
return ExchangeBuilder.topicExchange(DLX_EXCHANGE).durable(true).build();
}
/*队列绑定死信交换机*/
@Bean
public Binding queueBidingDlx(@Qualifier(DLX_QUEUE) Queue queue,
@Qualifier(DLX_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs();
}
}
- 在Producer的Test目录下发送消息进行测试:
@Test
public void dlx() {
String msg = "这是一条DLX死信消息";
rabbitTemplate.convertAndSend(RabbitmqConfig.OK_EXCHANGE, "ok.hello", msg);
}
- 在Consumer的Config配置类中,也同样申明,这几个交换机和队列,以及他们的绑定关系:
package com.chenshi.config;
import com.chenshi.utils.RabbitArgument;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String DLX_QUEUE = "dlx-queue";
public static final String DLX_EXCHANGE = "dlxExchange";
public static final String OK_QUEUE = "ok-queue";
public static final String OK_EXCHANGE = "ok_exchange";
/*正常的队列*/
@Bean(OK_QUEUE)
public Queue okQueue(){
return new Queue(OK_QUEUE,true,false,false, RabbitArgument.dlx());
}
/*正常的交换机*/
@Bean(OK_EXCHANGE)
public TopicExchange okExchange(){
return ExchangeBuilder.topicExchange(OK_EXCHANGE).durable(true).build();
}
/*队列绑定正常交换机*/
@Bean
public Binding queueBidingOK(@Qualifier(OK_QUEUE) Queue queue,
@Qualifier(OK_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("ok.#").noargs();
}
/*死信队列*/
@Bean(DLX_QUEUE)
public Queue dleQueue(){
return new Queue(DLX_QUEUE,true,false,false,null);
}
/*死信交换机*/
@Bean(DLX_EXCHANGE)
public TopicExchange dlxExchange(){
return ExchangeBuilder.topicExchange(DLX_EXCHANGE).durable(true).build();
}
/*队列绑定死信交换机*/
@Bean
public Binding queueBidingDlx(@Qualifier(DLX_QUEUE) Queue queue,
@Qualifier(DLX_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("dlx.#").noargs();
}
}
- 在Consumer的Java目录下新增一个监听类,启动Consumer服务进行测试:
/*死信交换机*/
@RabbitListener(queues = RabbitmqConfig.OK_QUEUE)
public void dlxMsg(Message message,Channel channel) throws IOException{
long deliveryTag = message.getMessageProperties().getDeliveryTag();
//通过拒收,测试死信交换机
try {
int a= 1/0;
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
//拒绝回到队列,就会被认定为死信
channel.basicReject(deliveryTag,false);
throw new RuntimeException(e);
}
}
九. Delayed-延迟队列:
1. spring-rabbitmq的延迟队列实现:
- 延迟队列:即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费,使用TTL+DLX实现
- 在Producer的Xml配置文件中,声明一个队列,一个交换机,并将队列绑定死信交换机,和设置TTL:
<!--==================================声明正常队列和交换机==============================-->
<!--声明一个队列-->
<rabbit:queue id="delay-queue" name="delay-queue" auto-delete="false" auto-declare="true" durable="true">
<rabbit:queue-arguments>
<!--绑定死信交换机-->
<entry key="x-dead-letter-exchange" value="dlxExchange"></entry>
<!--死信交换机路由键-->
<entry key="x-dead-letter-routing-key" value="dlx.dead"></entry>
<!--TTL-->
<entry key="x-message-ttl" value="10000" value-type="java.lang.Integer"></entry>
</rabbit:queue-arguments>
</rabbit:queue>
<!--声明一个交换机-->
<rabbit:topic-exchange id="delayExchange" name="delayExchange" auto-declare="true" durable="true" auto-delete="false">
<rabbit:bindings>
<rabbit:binding pattern="delay.#" queue="delay-queue"></rabbit:binding>
</rabbit:bindings>
</rabbit:topic-exchange>
- 在Producer的Test目录下进行消息发送测试:
@Test
public void delay() {
String msg = "这是一条延迟队列";
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("delayExchange", "delay.wait", msg);
}
System.out.println("消息发送成功");
}
- 在Consumerjaval目录下,声明一个新的监听类:
package com.chenshi.rabbitmq.listener;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class DelayListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
System.out.println("消息体: " + new String(message.getBody()));
channel.basicAck(deliveryTag, true);
System.out.println("消息应答成功");
} catch (Exception e) {
System.out.println("消息拒绝签收");
channel.basicNack(deliveryTag, true, false);
throw new RuntimeException(e);
}
}
}
- 在Consumer的Xml配置文件中,声明一个新的监听类,和监听容器,最后启动Consumer服务:
<!--监听类-->
<bean id="delayListener" class="com.chenshi.rabbitmq.listener.DelayListener"></bean>
<!--声明监听容器-->
<rabbit:listener-container connection-factory="factory" auto-declare="true" acknowledge="manual">
<rabbit:listener ref="delayListener" queue-names="delay-queue,dlx-queue"/>
</rabbit:listener-container>
2. springboot-rabbitmq的延迟队列实现方式:
- 在Producer的Java目录中utils下,配置延迟队列的参数信息:
package com.chenshi.utils;
import java.util.HashMap;
public class RabbitArgument {
public static HashMap delay(){
HashMap<String, Object> delay = new HashMap<>();
delay.put("x-dead-letter-exchange","dlxExchange");
delay.put("x-dead-letter-routing-key","dlx.dead");
delay.put("x-message-ttl",10000);//10秒
return delay;
}
}
- 在Producer的Java目录中Config配置类下,声明一个队列,和一个交换机:
package com.chenshi.config;
import com.chenshi.utils.RabbitArgument;
import com.chenshi.utils.RoutingKey;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfig {
public static final String DELAY_QUEUE = "delay-queue";
public static final String DELAY_EXCHANGE = "delay_exchange";
/*声明一个延迟队列*/
@Bean(DELAY_QUEUE)
public Queue delayQueue(){
return new Queue(DELAY_QUEUE,true,false,false,RabbitArgument.delay());
}
/*声明一个交换机*/
@Bean(DELAY_EXCHANGE)
public TopicExchange delayExchange(){
return ExchangeBuilder.topicExchange(DELAY_EXCHANGE).durable(true).build();
}
/*延迟队列绑定交换机*/
@Bean
public Binding queueBindingDelay(@Qualifier(DELAY_QUEUE) Queue queue,
@Qualifier(DELAY_EXCHANGE) Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("delay.#").noargs();
}
}
- 在Producer的Test目录下,发送消息进行测试:
@Test
public void delay() {
String msg = "这是一条Delay延迟消息";
rabbitTemplate.convertAndSend(RabbitmqConfig.DELAY_EXCHANGE, "delay.queue", msg);
}
十. 日志与监控:
1. RabbitMQ默认日志存放路径:
Linux下: /var/log/rabbitmq/rabbit@xxx.log
windows下: C:\Users\Administrator\AppData\Roaming\RabbitMQ\log