2.6 Spring boot 整合 RabbitMQ



1.项目准备

我们在rabbitmq目录下创建rabbitmq-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 https://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.3.2.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.study</groupId>
	<artifactId>rabbitmq-springboot</artifactId>
	<version>1.0-SNAPSHOT</version>
	<name>rabbitmq-springboot</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

并设置配置文件添加rabbitmq的连接配置:

spring:
  rabbitmq:
    host: 192.168.64.140
    port: 5672
    username: admin
    password: admin

我们这里只做springboot整合rabbitmq的API进行测试,所以不需要其他额外的配置;

我们将在后续演示多种工作模式,每种工作模式都将有自己的生产者类与消费者类,如果我们使用统一的启动类进行启动运行,将会扫描所有工作模式的所有包,并自动创建所有的对象实例;

这样的话会导致实例混乱,无法有效的控制每种工作模式并区分,所以我们这里选择单独创建自己工作模式的启动类,也就是说,每种工作模式都有自己的单独的启动类;

这样启动项目时,只会扫描当前包,不会扫描其他包;

2.工作模式

springboot整合rabbitmq后,rabbitmq的相关配置将变得十分简单:

2.1 简单模式

生产者向helloworld队列发送消息,消费者从helloworld队列接收消息;

2.1.1 启动类

我们在m1包下创建单独运行的启动类,在启动类中添加队列:

package cn.study.rabbitmqspringboot.m1;

import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import javax.annotation.PostConstruct;

@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
    }

    //配置队列使用的参数
    // RabbitAutoConfiguration 自动配置类中,
    // 会从 spring 容器自动发现这个Queue实例,使用其中的参数在服务器上创建队列
    @Bean
    public Queue hellowordQueue(){
        return new Queue("helloworld",false,false,false);
    }
 }

2.1.2 生产者

对于生产者来说,spingboot并不能为我们自动创建,所以还是需要我们自己去进行配置:

package cn.study.rabbitmqspringboot.m1;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Scanner;

@Component
public class Producer {
    @Autowired
    private AmqpTemplate t; //用来发送消息的工具对象

    //调用send方法来发送消息
    public void send(){
        while (true){
            System.out.print("请输入要发送的消息:");
            String s = new Scanner(System.in).nextLine();
            //使用convertAndSend方法转换并发送hellowrld队列中的消息
            //这里会自动将消息转换byte[],不需要手动调用方法再进行转换
            t.convertAndSend("hellowrld",s);
        }
    }
}

2.1.3 消费者

但是对于消费者来说,通过注解配置后,spingboot会自动为我们创建实例并进行消息接收:

package cn.study.rabbitmqspringboot.m1;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener( queues = "hellowrld")
public class consumer {
    @RabbitHandler
    public void receive(String msg){
        System.out.println("收到的消息为:"+msg);
    }

/**
 * 也可以直接将 @RabbitListener( queues = "hellowrld")放到方法上进行使用
 * 但是因为@RabbitListener( queues = "hellowrld")一个类中只能存在一个
 * 所以一个类中只能创建一个消费者,没有那么灵活,不能写多个消费者的代码
    @RabbitListener( queues = "hellowrld")
    public void receive(String msg){

    }*/
}

2.1.4 测试

代码完成后,我们在启动类中创建测试方法进行消息的发送以及接收的测试:因为我们需要调用send()方法进行测试,此方法是一个while的死循环方法,直接调用会导致一直在此循环中无法结束跳出;

所以我门这里使用启动一个新的线程来执行此方法,springboot的主线程并不参与,保证主线程可以执行后续的方法;

    @Autowired
    private Producer p;

    /**
     *springboot主线程执行流程:
     *自动扫描创建实例 ---> 完成自动注入 ---> @PostConstruct ---> 后续步骤
     *@PostConstruct 一般用来执行初始化操作
     */
    @PostConstruct
    public void test() {
        new Thread(() -> p.send()).start();
    }

启动启动类后,会加载producer和consumer俩个类,自动加载扫描创建生产者和消费者,在同一个应用中创建生产者和消费者对象,这里是为了模拟测试这样设计,在实际生产环境中,生产者和消费者不应该在同一个模块中,甚至不在同一台服务器中;

在这里插入图片描述

输入消息的提示可能会和其他日志混到一起,直接输入即可;

2.2 工作队列模式

2.2.1 项目改造

对于后续的其他工作模式均与简单模式一样,进行简单的配置即可,所以后续的工作模式案例我们均在简单模式的基础上进行改造,直接复制m1包到当前目录下:
在这里插入图片描述

在启动类中,我们需要重新配置队列的属性,将其设置为持久,非独占,不自动删除的队列,并重新为其命名队列名;

并将生产者与消费者中的队列名更换为我们重新创建的队列名即可;

并且在consumer中创建第二个消费者,让其已负载均衡的方式接收处理消息:

package cn.study.rabbitmqspringboot.m2;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class consumer {
    @RabbitListener( queues = "task_queue")
    public void receive1(String msg){
        System.out.println("receive1收到的消息为:"+msg);
    }

    @RabbitListener( queues = "task_queue")
    public void receive2(String msg){
        System.out.println("receive2收到的消息为:"+msg);
    }

}

然后启动项目进行测试即可:
在这里插入图片描述

2.2.2 合理分发

目前默认的分发策略是负载均衡策略,为了实现合理分发,将消息发送到空闲的消费者,需要实现手动ack和配置qos参数为1的配置;

springboot整合RabbitMQ中,默认就是ack模式,会自动发送消息回执,也就是说,我们只需要将qos参数设置为1即可实现合理分发;

qos=1是yml配置文件中的配置内容,表示其每次接收一条消息,处理完成前不接收下一条消息:

spring:
  rabbitmq:
    host: 192.168.64.140
    port: 5672
    username: admin
    password: admin
    listener:
      simple:
        prefetch: 1 #每次接收一条,处理完成前不接收下一条消息,默认为250

2.2.3 持久化

设置持久化首先要将队列设置为持久队列,然后将发送的消息添加为持久消息,此前创建新的队列时,我们已经将其设置为了持久化队列,并且当前springboot整合RabbitMQ中,默认发送的消息就是持久消息,所以当前消息就是持久化队列;

2.3 发布订阅模式

我们将m2包中的类复制到m3包中,并在启动类中去除创建队列的代码,创建交换机:

    //创建交换机:非持久,不自动删除(默认为true,false)
    @Bean
    public FanoutExchange logs(){
        return new FanoutExchange("logs",false,false);
    }

修改生产中发送消息的相关代码,添加交换机,并删除指定的队列,在使用交换机的情况下,指定队列并没有什么效果,其是随机群发消息到所有队列的,所以指定队列的参数为空即可:

    public void send(){
        while (true){
            System.out.print("请输入要发送的消息:");
            String s = new Scanner(System.in).nextLine();
            //使用convertAndSend方法转换并发送hellowrld队列中的消息
            //这里会自动将消息转换byte[],不需要手动调用方法再进行转换
            t.convertAndSend("logs","",s);
        }
    }

消费者启动后,要创建随机队列并绑定到交换机,这里我们使用了多层嵌套注解用来完成相关绑定及配置设置:

package cn.study.rabbitmqspringboot.m3;

import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class consumer {
    @RabbitListener(
            bindings = @QueueBinding(value = @Queue,//队列,默认参数为:随机命名,非持久,独占,自动删除
                    exchange = @Exchange(name = "logs",declare = "false")//交换机,指定交换机名,并规定不创建交换机
                    ))
    public void receive1(String msg){
        System.out.println("receive1收到的消息为:"+msg);
    }

    @RabbitListener(
            bindings = @QueueBinding(value = @Queue,//队列,默认参数为:随机命名,非持久,独占,自动删除
                    exchange = @Exchange(name = "logs",declare = "false")//交换机,指定交换机名,并规定不创建交换机
            ))
    public void receive2(String msg){
        System.out.println("receive2收到的消息为:"+msg);
    }

}

修改完成后,启动项目进行测试我们观察到,此时消息已经是分发状态:
在这里插入图片描述

2.4 路由模式

路由模式还是和此前一致,生产者发送消息时,需要携带路由键,消费者队列和交换机绑定,需要设置绑定键,双方通过关键字进行匹配:
路由模式

我们将m3包中的代码复制到m4包中:
在这里插入图片描述

首先我们更改启动类中交换机的类型为DirectExchange,并为其命名为direct_logs;因为生产者发送消息要携带路由键,所以我们额外增加一行接收控制台输入信息的代码,并将其传递给convertAndSend,并更改交换机名称,这里要注意路由键与消息作为参数传入的顺序:

    public void send(){
        while (true){
            System.out.println("请输入要发送的消息:");
            String s = new Scanner(System.in).nextLine();
            System.out.println("请输入路由键:");
            String k = new Scanner(System.in).nextLine();
            //使用convertAndSend方法转换并发送hellowrld队列中的消息
            //这里会自动将消息转换byte[],不需要手动调用方法再进行转换
            t.convertAndSend("direct_logs",k,s);
        }
    }

消费者中我们除了需要更改交换机名称外,还需要额外添加一个关键词参数:
在这里插入图片描述

完成后即可启动项目进行测试了,

在这里插入图片描述

2.5 主题模式

同样的,我们将m4中的代码复制到m5包中,并修改启动类中的交换机为TopicExchange,修改交换机名为topic_logs,同时修改生产者与消费者的交换机为topic_logs,并重新设置消费者的绑定键:
在这里插入图片描述

完成后即可启动测试了:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值