RabbitMQ
1.工作模型
上图说明:
producer:生产者
channel:信道(多路复用,虚连接Connection)
Broker:主机
VHost:虚拟主机
Exchange:交换机,主要用于消息的路由。常见类型:direct、fanout、headers、topic
Queue:队列
Consumer:消费者
2.交换器
2.1 Direct Exchange 直连
2.2 Fanout Exchange 广播
2.3 Topic Exchange 主题
3.SpringBoot整合RabbitMQ
3.1 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!-- springboot引入rabbitmq -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.1.0.RELEASE</version>
</dependency>
<!-- RabbitMQ原生的客户端 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.0.0</version>
</dependency>
3.2 properties配置文件
spring.application.name=springboot-rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
3.3 配置类:
服务器配置、创建交换机、创建队列、创建绑定关系
package com.zwt.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitConfig {
@Value("${spring.rabbitmq.host}")
private String address;
@Value("${spring.rabbitmq.port}")
private String port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
// 连接工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(address + ":" + port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
return connectionFactory;
}
// rabbitAdmin封装了对rabbitMQ的管理操作
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
return new RabbitTemplate(connectionFactory);
}
// 声明交换器
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange");
}
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanoutExchange");
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
// 声明队列
@Bean
public Queue queue1(){
return new Queue("queue1");
}
@Bean
public Queue queue2(){
return new Queue("queue2");
}
// 绑定关系
@Bean
public Binding bindQueue1AndDirectExchange(Queue queue1, DirectExchange directExchange){
return BindingBuilder.bind(queue1).to(directExchange).with("red");
}
@Bean
public Binding bindQueue2AndDirectExchange(Queue queue2, DirectExchange directExchange){
return BindingBuilder.bind(queue2).to(directExchange).with("white");
}
@Bean
public Binding bindQueue1AndFanoutExchange(Queue queue1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue1).to(fanoutExchange);
}
@Bean
public Binding bindQueue2AndFanoutExchange(Queue queue2, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue2).to(fanoutExchange);
}
@Bean
public Binding bindQueue1AndTopicExchange(Queue queue1, TopicExchange topicExchange){
return BindingBuilder.bind(queue1).to(topicExchange).with("red.*");
}
@Bean
public Binding bindQueue2AndTopicExchange(Queue queue2, TopicExchange topicExchange){
return BindingBuilder.bind(queue2).to(topicExchange).with("white.82");
}
}
3.4 生产者:
使用路由键发送消息(使用template)
package com.zwt.rabbitmq.producer;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
@RequestMapping("/rabbitmq")
public class RabbitProducer {
@Resource
private RabbitTemplate rabbitTemplate;
@GetMapping("/direct")
public String direct(@RequestParam String key){
String msg = "key(" + key + "),exchange(direct)-" + System.currentTimeMillis();
System.out.println("DirectSender: " + msg);
rabbitTemplate.convertAndSend("directExchange", key, msg);
return "发送direct消息成功";
}
@GetMapping("/topic")
public String topic(@RequestParam String key){
String msg = "key(" + key + "),exchange(topic)-" + System.currentTimeMillis();
System.out.println("TopicSender: " + msg);
rabbitTemplate.convertAndSend("topicExchange", key, msg);
return "发送topic消息成功";
}
@GetMapping("/fanout")
public String fanout(@RequestParam String key){
String msg = "key(" + key + "),exchange(fanout)-" + System.currentTimeMillis();
System.out.println("FanoutSender: " + msg);
rabbitTemplate.convertAndSend("fanoutExchange", key, msg);
return "发送fanout消息成功";
}
}
3.5 消费者
监听类(监听队列 )
package com.zwt.rabbitmq.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queue1")
public class RabbitConsumer1 {
@RabbitHandler
public void process(String msg){
System.out.println("Consumer1: " + msg);
}
}
package com.zwt.rabbitmq.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "queue2")
public class RabbitConsumer2 {
@RabbitHandler
public void process(String msg){
System.out.println("Consumer2: " + msg);
}
}
4.消息可靠性分析
1.确保消息到MQ
2.确保消息路由到正确的队列,即路由保证
3.确保消息在队列正确的存储,即消息持久化
4.确保消息从队列中正确的投递到消费者,即消费者确认
4.1 确保消息到MQ
采用确认模式,来确保。这里需要1.修改配置类中的连接工厂ConnectionFactory 2.修改配置类中的rabbitTemplate
// 连接工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(address + ":" + port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
// 修改1:确保消息到MQ-开启确认模式
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 修改2:确保消息到MQ-开启一个异步回调
rabbitTemplate.setConfirmCallback(confirmCallback());
return rabbitTemplate;
}
// 生产者发送确认
private RabbitTemplate.ConfirmCallback confirmCallback(){
return new RabbitTemplate.ConfirmCallback() {
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
System.out.println("确认消息发送到mq成功");
}else {
System.out.println("确认消息发送到mq失败,考虑重发:" + cause);
}
}
};
}
4.2 路由保证
1.失败通知(mandatory+ReturnListener)
这里需要1.修改配置类中的rabbitTemplate
2.备用交换机
// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 设置监听
rabbitTemplate.setConfirmCallback(confirmCallback());
// 修改1:另外一个监听,失败通知
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(returnCallback());
return rabbitTemplate;
}
// 路由失败通知
private RabbitTemplate.ReturnCallback returnCallback(){
return new RabbitTemplate.ReturnCallback() {
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("无法路由的消息,需要考虑另行处理");
System.out.println("returned message: " + new String(message.getBody()));
System.out.println("returned replyCode: " + replyCode);
System.out.println("returned replyText: " + replyText);
System.out.println("returned exchange: " + exchange);
System.out.println("returned routingKey: " + routingKey);
}
};
}
4.3 消息持久化
在3.3环节中的配置类:
有关Queue的初始化、Exchange的初始化都是持久化的
4.4 消费者确认
在3.3环节中的配置类:
1.新增配置SimpleMessageListenerContainer
@Resource
private Receiver receiver;
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory, Queue queue1, Queue queue2){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 绑定队列
container.setQueues(queue1, queue2);
// 手动提交
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
// 消费确认方法
container.setMessageListener(receiver);
return container;
}
package com.zwt.rabbitmq.consumer;
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 Receiver implements ChannelAwareMessageListener {
public void onMessage(Message message, Channel channel) {
try {
String msg = new String(message.getBody());
System.out.println("Receiver------>接收到的信息: " + msg);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("Receiver------>消息已经消费");
} catch (Exception e) {
System.out.println(e.getMessage());
try {
// true的意思就是重发
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} catch (IOException e1) {
System.out.println("Receiver------>拒绝消息,要求MQ重新发送");
}
}
}
public void onMessage(Message message) {
}
}
5.项目结构与代码
环节3、4整合后的项目结构与代码
5.1 完整的RabbitConfig代码
package com.zwt.rabbitmq.config;
import com.zwt.rabbitmq.consumer.Receiver;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
@Configuration
public class RabbitConfig {
@Value("${spring.rabbitmq.host}")
private String address;
@Value("${spring.rabbitmq.port}")
private String port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
@Resource
private Receiver receiver;
// 连接工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(address + ":" + port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
// 开启确认模式
connectionFactory.setPublisherConfirms(true);
return connectionFactory;
}
// rabbitAdmin封装了对rabbitMQ的管理操作
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
// 使用template
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
// 设置监听
rabbitTemplate.setConfirmCallback(confirmCallback());
// 另外一个监听,路由失败通知
rabbitTemplate.setMandatory(true);
rabbitTemplate.setReturnCallback(returnCallback());
return rabbitTemplate;
}
// 生产者发送确认
private RabbitTemplate.ConfirmCallback confirmCallback(){
return new RabbitTemplate.ConfirmCallback() {
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(ack){
System.out.println("确认消息发送到mq成功");
}else {
System.out.println("确认消息发送到mq失败,考虑重发:" + cause);
}
}
};
}
// 路由失败通知
private RabbitTemplate.ReturnCallback returnCallback(){
return new RabbitTemplate.ReturnCallback() {
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("无法路由的消息,需要考虑另行处理");
System.out.println("returned message: " + new String(message.getBody()));
System.out.println("returned replyCode: " + replyCode);
System.out.println("returned replyText: " + replyText);
System.out.println("returned exchange: " + exchange);
System.out.println("returned routingKey: " + routingKey);
}
};
}
// 声明交换器
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange");
}
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanoutExchange");
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
// 声明队列
@Bean
public Queue queue1(){
return new Queue("queue1");
}
@Bean
public Queue queue2(){
return new Queue("queue2");
}
// 绑定关系
@Bean
public Binding bindQueue1AndDirectExchange(Queue queue1, DirectExchange directExchange){
return BindingBuilder.bind(queue1).to(directExchange).with("red");
}
@Bean
public Binding bindQueue2AndDirectExchange(Queue queue2, DirectExchange directExchange){
return BindingBuilder.bind(queue2).to(directExchange).with("white");
}
@Bean
public Binding bindQueue1AndFanoutExchange(Queue queue1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue1).to(fanoutExchange);
}
@Bean
public Binding bindQueue2AndFanoutExchange(Queue queue2, FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue2).to(fanoutExchange);
}
@Bean
public Binding bindQueue1AndTopicExchange(Queue queue1, TopicExchange topicExchange){
return BindingBuilder.bind(queue1).to(topicExchange).with("red.*");
}
@Bean
public Binding bindQueue2AndTopicExchange(Queue queue2, TopicExchange topicExchange){
return BindingBuilder.bind(queue2).to(topicExchange).with("white.82");
}
@Bean
public SimpleMessageListenerContainer messageContainer(ConnectionFactory connectionFactory, Queue queue1, Queue queue2){
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
// 绑定队列
container.setQueues(queue1, queue2);
// 手动提交
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
// 消费确认方法
container.setMessageListener(receiver);
return container;
}
}
5.2 完整的Receiver代码
package com.zwt.rabbitmq.consumer;
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 Receiver implements ChannelAwareMessageListener {
public void onMessage(Message message, Channel channel) {
try {
String msg = new String(message.getBody());
System.out.println("Receiver------>接收到的信息: " + msg);
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("Receiver------>消息已经消费");
} catch (Exception e) {
System.out.println(e.getMessage());
try {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
} catch (IOException e1) {
System.out.println("Receiver------>拒绝消息,要求MQ重新发送");
}
}
}
public void onMessage(Message message) {
}
}