Spring Boot整合 Rabbitmq

概述

通过一些demo来演示RabbitMQ被整合后是如何使用的

配置

1、添加依赖 或 编辑起步依赖添加Spring for RabbitMQ
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.amqp</groupId>
	<artifactId>spring-rabbit-test</artifactId>
	<scope>test</scope>
</dependency>
2、yml配置
spring:
  rabbitmq:
    host: 192.168.126.128
    username: admin
    password: admin

简单模式

在主程序中,创建Queue的实例

  • 注意Queue是Spring提供的
    Spring提供的Queue类,是队列的封装对象,他封装了队列的参数信息。
    RabbitMQ的自动配置类RabbitAutoConfiguration会发现这些Queue实例,并在RabbitMQ服务器中定义这些队列。
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import cn.tedu.RabbitmqSpringbootApplication;
@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	
	@Bean
	public Queue helloworldQueue() {
		return new Queue("helloworld", false);
	}
}

生产者

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sender {
	
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		t.convertAndSend("helloworld", "helloworld"+System.currentTimeMillis());
		System.out.println("消息已发送");
	}
}

消费者

  • 通过@RabbitListener注解从指定的队列接收消息
  • 使用@RebbitHandler注解的方法来处理消息
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = "helloworld") // 通过注解自动加载自动配置,成为消费者,一直接收数据
public class Receiver {

	// 如果把 @RabbitListener注解写道类上需要加@RabbitHandler,把接收到的数据交给这个方法处理
	@RabbitHandler
	public void receiver(String message) {
		System.out.println("收到:" + message);
	}
}

测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Test1 {

	@Autowired
	Sender sender;
	
	@Test
	public void test1() {
		sender.send();
	}
}

工作模式

1、主程序

  • 创建名为task_queue持久队列
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {

	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	@Bean
	public Queue task_queue() {
		// 这个构造方法创建的队列参数为: 持久,非排他,非自动删除
		return new Queue("task_queue");
	}
}

生产者

import java.util.Scanner;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sender {

	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.println("请输入消息:");
			String s = new Scanner(System.in).nextLine();
			t.convertAndSend("taskQueue", s); // 默认是持久消息
		}
	}
}

Spring Boot封装的 RabbitMQ API中,发送的消息默认是持久化消息。如果需要设置非持久化消息,则做一下设置

  • 使用 MessagePostProcessor 前置处理器参数
  • 从消息中获取消息的属性对象
  • 在属性中把 DeliveryMode 设置为非持久化
t.convertAndSend("taskQueue", (Object) s, new MessagePostProcessor() {
	@Override
	public Message postProcessMessage(Message message) throws AmqpException {
		MessageProperties props = message.getMessageProperties();
		props.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT);
		return message;
	}
});

消费者

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

@Component
public class Receiver {

	@RabbitListener(queues = "taskQueue")
	public void receiver1(String message) throws InterruptedException {
		System.out.println("消费者1 - 收到:" + message);
		for (int i = 0; i < message.length(); i++) {
			if ('.' == message.charAt(i)) {
				Thread.sleep(1000);
			}
		}
		System.out.println("消费者1 - 消息处理完毕");
	}
	@RabbitListener(queues = "taskQueue")
	public void receiver2(String message) throws InterruptedException {
		System.out.println("消费者2 - 收到:" + message);
		for (int i = 0; i < message.length(); i++) {
			if ('.' == message.charAt(i)) {
				Thread.sleep(1000);
			}
		}
		System.out.println("消费者2 - 消息处理完毕");
	}
	
}

测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Test1 {

	@Autowired
	Sender sender;
	
	@Test
	public void test1() {
		sender.send();
	}
}

ack模式

在 Spring Boot 中提供了三种确认模式:

  • NONE:使用 RabbitMQ 的自动确认
  • AUTO:使用 RabbitMQ 的手动确认,Spring Boot 回自动发送确认回执 (默认)
  • MANUAL:使用 RabbitMQ 的手动确认,且必须手动执行确认操作
    默认的 AUTO 模式中,处理消息的方法抛出异常,则表示消息没有被正确处理,该消息会被重新发送

设置 ack 模式

spring:
  rabbitmq:
    listener:
      simple:
        # acknowledgeMode: NONE # rabbitmq的自动确认
        acknowledgeMode: AUTO # rabbitmq的手动确认, springboot会自动发送确认回执 (默认)
        # acknowledgeMode: MANUAL # rabbitmq的手动确认, springboot不发送回执, 必须自己编码发送回执

手动执行确认操作

如果设置为MANUAL模式,必须手动执行确认操作

@RabbitListener(queues="taskQueue")
	public void receive1(String s, Channel c, @Header(name=AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {
		System.out.println("receiver1 - 收到: "+s);
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '.') {
				Thread.sleep(1000);
			}
		}
		// 手动发送确认回执
		c.basicAck(tag, false);
	}

设置抓取数量

工作模式中,为了合理的分发数据,需要将 qos 设置成1,每次只接收一条消息处理完成后才接受下一条消息。
Spring Boot 中是通过```prefetch``属性进行设置,该属性默认值为 250.

spring:
  rabbitmq:
    listener:
      simple:
        prefetch: 1 

发布和订阅模式

主程序

创建FanoutExcnahge实例,封装fanout类型交换机定义信息。
自动配置类会自动发现交换机实例,并在 RabbitMQ 服务器中定义该交换机。

import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	@Bean
	public FanoutExchange logsExchange() {
		// 非持久,不自动删除
		return new FanoutExchange("logs", false, false); 
	}
}

生产者

生产者向指定的交换机logs发送数据
不需要指定队列名或路由键,即使指定也无效,因为fanout交换机会向所有与之绑定的队列发送数据,并不是有选择的发送

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

@Component
public class Sender {
	
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.println("请输入消息:");
			String s = new Scanner(System.in).nextLine();
			t.convertAndSend("logs","", s); // 默认是持久消息  第二个参数是路由键
		}
	}
}

消费者

消费者需要完成一下操作:

  • 1、定义水机队列(随即命名,非持久,排他,自动删除)
  • 2、定义交换机(可以省略)
  • 3、将队列绑定到交换机

Spring Boot 通过注解完成以上操作:

@RabbitListener(bindings = @QueueBinding( //这里进行绑定设置
	value = @Queue, //这里定义随机队列,默认属性: 随机命名,非持久,排他,自动删除
	exchange = @Exchange(name = "logs", declare = "false") //指定 logs 交换机,因为主程序中已经定义,这里不进行定义
))
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.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "logs", declare = "false")  // declare false  表时使用已有的交换机,而不是新创建
			))
	public void receiver1(String message) throws InterruptedException {
		System.out.println("消费者1 - 收到:" + message);
		
	}
	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "logs", declare = "false")  // declare false  表时使用已有的交换机,而不是新创建
			))
	public void receiver2(String message) throws InterruptedException {
		System.out.println("消费者2 - 收到:" + message);

	}
}

测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Test1 {

	@Autowired
	Sender sender;
	
	@Test
	public void test1() {
		sender.send();
	}
}

路由模式

与发布订阅者模式类似,只做以下几点调整:

  • 1、使用直连交换机 (direct)
  • 2、队列与交换机绑定时,设置绑定键
  • 3、发送消息时,设置路由键

主程序

主程序中使用DirectExchange对象封装交换机信息,Spring Boot 自动配置类会自动发现这个对象,并在 RabbitMQ 服务器上定义这个交换机。

import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	@Bean
	public DirectExchange logsExchange() {
		// 非持久,不自动删除
		return new DirectExchange("direct_logs", false, false); 
	}
}

生产者

生产者向指定的交换机发送消息,并指定路由键。

import java.util.Scanner;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sender {
	// RabbitAutoConfiguration
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.println("请输入消息:");
			String s = new Scanner(System.in).nextLine();
			System.out.println("请输入路由键:");
			String key = new Scanner(System.in).nextLine();
			t.convertAndSend("direct_logs",key, s); // 默认是持久消息  第二个参数是路由键
		}
	}
}

消费者

消费者通过注解来定义随机队列,绑定到交换机,并指定绑定键。

@RabbitListener(bindings = @QueueBinding( // 这里做绑定设置
	value = @Queue, // 定义队列, 随机命名,非持久,排他,自动删除
	exchange = @Exchange(name = "direct_logs", declare = "false"), // 指定绑定的交换机,主程序中已经定义过队列,这里不进行定义
	key = {"error","info","warning"} // 设置绑定键
))
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.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "direct_logs", declare = "false"),  // declare false  表时使用已有的交换机,而不是新创建
			key = {"erro"}
	))
	public void receiver1(String message) throws InterruptedException {
		
		System.out.println("消费者1 - 收到:" + message);
		
	}
	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "direct_logs", declare = "false"),  
			key = {"info", "erro", "warning"}
	))
	public void receiver2(String message) throws InterruptedException {
		System.out.println("消费者2 - 收到:" + message);

	}
}

测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Test1 {

	@Autowired
	Sender sender;
	
	@Test
	public void test1() {
		sender.send();
	}
}

主题模式

主题模式是一种具有特殊规则的路由模式,代码与路由模式基本相同,只做一下两点调整:

  • 1、使用 topic交换机
  • 2、使用特殊的绑定键和路由键

主程序

import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	@Bean
	public TopicExchange logsExchange() {
		// 非持久,不自动删除
		return new TopicExchange("topic_logs", false, false);  // 默认持久
	}
}

生产者

import java.util.Scanner;

import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class Sender {
	// RabbitAutoConfiguration
	@Autowired
	AmqpTemplate t;
	
	public void send() {
		while (true) {
			System.out.println("请输入消息:");
			String s = new Scanner(System.in).nextLine();
			System.out.println("请输入路由键:");
			String key = new Scanner(System.in).nextLine();
			t.convertAndSend("topic_logs",key, s); // 默认是持久消息
		}
	}
}

消费者

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.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
public class Receiver {

	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "topic_logs", declare = "false"),  // declare false  表时使用已有的交换机,而不是新创建
			key = {"*.orange.*"}
			))
	public void receiver1(String message) throws InterruptedException {
		
		System.out.println("消费者1 - 收到:" + message);
		
	}
	@RabbitListener(bindings = @QueueBinding(
			value = @Queue,    // 如果@Queue注解不指定任何参数,则为服务器随即起名
			exchange = @Exchange(name = "topic_logs", declare = "false"),  // declare false  表时使用已有的交换机,而不是新创建
			key = {"*.*.rabbit", "lazy.#"}
			))
	public void receiver2(String message) throws InterruptedException {
		System.out.println("消费者2 - 收到:" + message);

	}
}

测试类

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Test1 {

	@Autowired
	Sender sender;
	
	@Test
	public void test1() {
		sender.send();
	}
}

RPC异步调用

主程序

主程序中定义两个队列

  • 1、发送调用信息的队列
  • 2、返回结果的队列
import java.util.UUID;

import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class Main {
	public static void main(String[] args) {
		SpringApplication.run(Main.class, args);
	}
	/*
	 * 定义队列:
	 *    1、发送队列  rpc_queue
	 *    2、返回队列  自己起随机名  非持久,排他,自动删除
	 */
	@Bean
	public Queue rpcQueue() {
		return new Queue("rpc_queue", false);
	}
	
	@Bean
	public Queue rndQueue() {
		return new Queue(UUID.randomUUID().toString(), false, true, true);
	}
}

服务端

rpc_queue接收调用数据,执行运算,并返回计算结果
@RabbitListener注解对于具有返回值的方法:

  • 回自动获取返回队列名称(replyTo属性)
  • 会自动获取关联id(correlationId属性)
  • replyTo属性指定的队列发送计算结果,并携带 correlationId属性
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class RpcServer {
	/*
	 * 1、接收调用数据
	 * 2、执行服务器端计算
	 * 3、发送计算结果
	 * 
	 * spring boot 自动处理:
	 * 		1、自动从rpc_queue接收消息,传到receive方法
	 * 		2、自动获取返回队列的队列名
	 * 		3、自动获取关联id
	 * 		4、返回值,会自动发回到rpc客户端
	 * 
	 */
	@RabbitListener(queues = "rpc_queue")
	public long receive(int n) {
		return f(n);
	}

	private long f(int n) {
		if (n == 1 || n == 2) {
			return 1;
		}
		return f(n-1) + f(n-2);
	}
}

客户端

1、使用 SPEL 表达式 获取随机队列名:#{rndQueue.name}
2、发送调用数据时,携带随机队列名和关联id
3、从随机队列接收调用结果,并获取关联id

import java.util.UUID;

import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

@Component
public class RpcClient {
	@Autowired
	AmqpTemplate t;
	
	// SPEL表达式语言
	@Value("#{rndQueue.name}")
	String replyTo;
	public void send(int n) {
		// 携带返回队列名, 关联id
		t.convertAndSend("rpc_queue", n, new MessagePostProcessor() {
			
			@Override
			public Message postProcessMessage(Message message) throws AmqpException {
				MessageProperties props = message.getMessageProperties();
				props.setReplyTo(replyTo);
				props.setCorrelationId(UUID.randomUUID().toString());
				return message;
			}
		});
		System.out.println("调用已发送");
	}
	
	@RabbitListener(queues = "#{rndQueue.name}")
	public void recive(long r, @Header(name = AmqpHeaders.CORRELATION_ID) String cid) {
		System.out.println(cid + " - 斐波那契数:" + r);
	}
}

测试类

import java.util.Scanner;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class TopicTests {

	@Autowired
	RpcClient client;

	@Test
	void test1() throws Exception {
		while (true) {
			System.out.print("请输入要计算的值: ");
			int n = new Scanner(System.in).nextInt();
			client.send(n);
		}
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值