RabbitMQ的从入门到入坟

168 阅读14分钟

一. RabbitMq官网的下载:

www.rabbitmq.com/docs/downlo…


  1. 由于rabbitmq是由erlang语言编写的,所以需要erlang的支持,erlang下载路径:

    www.erlang.org/downloads


  1. erlang和rabbitmq的版本对应关系:

    www.rabbitmq.com/docs/which-…


  1. erlang下载安装后,在本机的环境变量里,添加新的系统变量
变量名:ERLANG_HOME

变量值:rabbitmq的安装路径

  1. 下载安装完成之后在下载的rabbitmq_server路径下进入cmd窗口:

    列如:D:\SoftWare\RabbitMq\rabbitmq_server-3.12.10\sbin


  1. 在cmd窗口输入命令启动rabbitmq服务:
start rabbitmq-service.bat
  1. 在浏览器上访问rabbitmq地址:http://127.0.0.1:15672/

  1. 登入rabbitmq的初始账号密码为:
账号: guest

密码: guest 

二.RabbitMq在Spring中的使用:

  1. 创建一个父工程:
<?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>

  1. 创建一个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>

  1. 创建一个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>

  1. 在Producer生产者和Consumer消费者工程中加入properties配置文件:
rabbitmq.host = localhost
rabbitmq.port = 5672
rabbitmq.username = admin
rabbitmq.password = 123456
rabbitmq.virtual-host = /

  1. 在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. 简单队列的实现:

image-20240321161922541.png

  • 简单队列:一个消费者,对应一个生产者
  1. 在Procuder工程下的xml配置文件中声明一个队列:
 <!--==================================简单模式========================================-->
    <!--声明一个队列-->
    <rabbit:queue id="spring-queue" name="spring-queue" auto-declare="true" durable="true" auto-delete="false"/>

  1. 在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("消息已发送");
    }

}

  1. 消息发送后会在队列中,等待被消费:

image-->


  1. 在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()));      
    }
    
}


  1. 在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>

  1. 开启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. 包工头队列的实现:

image-20240321162006137.png

  • 包工头队列:消费者同时争抢一个队列的消息
  1. 在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"/>

  1. 在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("消息已发送");

}

  1. 在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. 广播模式队列的实现:

image-20240321162021570.png

  • 广播模式队列:发送一条消息会同时给多个消费者
  1. 在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>

  1. 在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("消息已发送");

}

  1. 在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. 路由模式队列的实现:

image-20240321162035866.png

  • 路由队列模式:交换机根据设置好的路由键,向对应的队列发送消息
  1. 在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>

  1. 在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("消息发送成功");
}

  1. 在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. 通配符模式队列的实现:

image-20240321162054065.png

  • 通配符模式队列:交换机通过一个,指定通配符,向队列发送消息
  1. 在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>

  1. 在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("消息发送成功");
}

  1. 在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项目:

  1. 搭建一个空的父工程项目:
<?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>

  1. 搭建一个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>

  1. 搭建一个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>

  1. 创建一个Producer的Yml配置文件:
spring:
  rabbitmq:
    virtual-host: /
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456 

  1. 创建一个Consumer的Yml配置文件:
spring:
  rabbitmq:
    virtual-host: /
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456

  1. 在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生产者:

  1. 在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);
    }

}

  1. 在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消费者:

  1. 在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);
    }

}

  1. 在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()));
    }

}

  1. 在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. 广播模式队列的实现:


  1. 在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);
}

  1. 在Producer的RabbitmqConfig下声明这个交换机:
//声明一个交换机
@Bean(FANOUT_EXCHANGE)
public FanoutExchange fanout_exchange(){
    return ExchangeBuilder.fanoutExchange(FANOUT_EXCHANGE).durable(true).build();
}

  1. 在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();
}

  1. 在Producer的test目录下发送测试消息:
@Test
public void fanout(){
    String msg = "广播队列的消息发送";
    rabbitTemplate.convertAndSend(RabbitmqConfig.FANOUT_EXCHANGE,"",msg);

    System.out.println("消息发送成功");
}

  1. 同样,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();
    }
  
}

  1. 在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. 路由模式队列的实现:


  1. 在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);
}

  1. 在Producer的Config下声明一个交换机:
/*声明一个路由交换机*/
@Bean(DIRECT_EXCHANGE)
public DirectExchange routing_exchange(){
    return ExchangeBuilder.directExchange(DIRECT_EXCHANGE).durable(true).build();
}

  1. 在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;
      }
}

  1. 在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();
}

  1. 同时在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();
    }

}

  1. 在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. 通配符模式队列的实现:


  1. 在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();
}

  1. 将这两个队列,绑定在这个交换机:
/*队列绑定到主题交换机*/
@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();
}

  1. 在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("消息发送成功");
}

  1. 在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):


  1. 在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"/>

  1. 在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>

  1. 在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);

}

  1. 在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):


  1. 在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"/>

  1. 在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);

}

  1. 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);

}

  1. 出错后的回调函数的响应结果:
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):


  1. 在Producer的yml配置文件中,添加配置:
spring:
  rabbitmq:
    virtual-host: /
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    publisher-confirm-type: correlated

  1. 在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();
    }
    
}

  1. 在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("消息发送成功");
    }
  
}

  1. 在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();
    }

}

  1. 在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):


  1. 在Producer的yml配置文件中添加配置:
spring:
  rabbitmq:
    virtual-host: /
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456
    publisher-confirm-type: correlated
    publisher-returns: true

  1. 在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);

}

  1. 当向交换机发送消息,失败时会调用回退函数:
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实现:


  1. 在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>

  1. 在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实现:


  1. 在Consumer的yml配置文件中的配置:
spring:
  rabbitmq:
    virtual-host: /
    host: 127.0.0.1
    port: 5672
    username: admin
    password: 123456

  1. 在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的消费端限流:


  1. 在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>

  1. 在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的消费端限流的实现:


  1. 在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
    

  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实现方式:


  1. 在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>

  1. 在Producer的Test目录下进行消息发送测试:
@Test
public void ttl(){
    String msg = "这是一个TTL消息";
    rabbitTemplate.convertAndSend("ttlExchange","ttl.pop",msg);

    System.out.println("消息发送成功");
}

  1. 在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秒之内没被消费,就会过期。

  1. 在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实现方式:


  1. 在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;
    }
  
}

  1. 在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();
}

  1. 在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实现方式:


​ 消息成为死信的三种条件:


  1. 队列消息长度到达限制;
  2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
  3. 原队列存在消息过期设置,消息到达超时时间未被消费;

  1. 在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>

  1. 继续在声明一个正常队列,和正常交换机,将正常队列绑定到死信交换机上:
<!--==================================声明正常队列和交换机==============================-->
    <!--声明一个正常队列,并绑定死信交换机-->
    <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>

  1. 在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实现方式:


  1. 在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;
    }
  
}

  1. 在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();
    }

}

  1. 在Producer的Test目录下发送消息进行测试:
@Test
public void dlx() {
    String msg = "这是一条DLX死信消息";

    rabbitTemplate.convertAndSend(RabbitmqConfig.OK_EXCHANGE, "ok.hello", msg);

}

  1. 在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();
    }

}

  1. 在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实现

  1. 在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>

  1. 在Producer的Test目录下进行消息发送测试:
@Test
public void delay() {
    String msg = "这是一条延迟队列";
    for (int i = 0; i < 10; i++) {
        rabbitTemplate.convertAndSend("delayExchange", "delay.wait", msg);
    }

    System.out.println("消息发送成功");
}

  1. 在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);
        }

    }

}

  1. 在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的延迟队列实现方式:


  1. 在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;
    }

}

  1. 在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();
    }
    
}

  1. 在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