一、基础
1、依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、基本配置
spring:
rabbitmq:
port: 5672
host: localhost
username: guest
password: guest
virtual-host: / #虚拟主机 默认/
二、生产者配置
1、声明队列和交换机
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 简单模式、生产模式可以就声明 队列,不声明交换机,不绑定 发送消息时路由直接填队列名
* 发布/订阅模式 在绑定时,不需要设置路由; 发送消息时,路由为null
*
*/
@Configuration
public class DirectRabbitConfig {
//队列 起名:TestDirectQueue
@Bean
public Queue TestDirectQueue() {
// durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
// exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
// autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
// return new Queue("TestDirectQueue",true,true,false);
//一般设置一下队列的持久化就好,其余两个就是默认false
return new Queue("TestDirectQueue",true);
}
//Direct交换机 起名:TestDirectExchange
@Bean
DirectExchange TestDirectExchange() {
// return new DirectExchange("TestDirectExchange",true,true);
return new DirectExchange("TestDirectExchange",true,false);
}
//绑定 将队列和交换机绑定, 并设置用于匹配键:TestDirectRouting
@Bean
Binding bindingDirect() {
return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");
}
}
二、发送消息
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
*
* map 可以不传
*
**/
@RestController
public class SendMessageController {
@Autowired
RabbitTemplate rabbitTemplate; //使用RabbitTemplate,这提供了接收/发送等等方法
@GetMapping("/sendDirectMessage")
public String sendDirectMessage() {
String messageId = String.valueOf(UUID.randomUUID());
String messageData = "test message, hello!";
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String,Object> map=new HashMap<>();
map.put("messageId",messageId);
map.put("messageData",messageData);
map.put("createTime",createTime);
//将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchange
rabbitTemplate.convertAndSend("TestDirectExchange", "TestDirectRouting", map);
return "ok";
}
}
参考:
三、消费者设置
接受消息
/**
* @RabbitListener 可以直接放在方法上,放在类上需要配合@RabbitHandler使用
*/
@Component
@RabbitListener(queues = "TestDirectQueue")//监听的队列名称 TestDirectQueue
public class DirectReceiver {
@RabbitHandler
public void process(Map testMessage) {
System.out.println("DirectReceiver消费者收到消息 : " + testMessage.toString());
}
}
4、发布确认
生产者中配置,用来确认消息是否正确发送
server:
port: 8021
spring:
#给项目来个名字
application:
name: rabbitmq-provider
#配置rabbitMq 服务器
rabbitmq:
host: 127.0.0.1
port: 5672
username: root
password: root
#虚拟host 可以不设置,使用server默认host
virtual-host: JCcccHost
#确认消息已发送到交换机(Exchange)
publisher-confirm-type: correlated
#确认消息已发送到队列(Queue)
publisher-returns: true
配置发布确认
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author : JCccc
* @CreateTime : 2019/9/3
* @Description :
**/
@Configuration
public class RabbitConfig {
@Bean
public RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate();
rabbitTemplate.setConnectionFactory(connectionFactory);
// 配置发布到交换机的确认
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
*correlationData :客户端在发送原始消息时提供的对象。
*ack:exchange交换机是否成功收到了消息。true成功,false代表失败。
*cause:失败原因。
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("ConfirmCallback: "+"相关数据:"+correlationData);
System.out.println("ConfirmCallback: "+"确认情况:"+ack);
System.out.println("ConfirmCallback: "+"原因:"+cause);
}
});
// 配置交换机没有找到对应的消息队列时,消息退回时的处理
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("ReturnCallback: "+"消息:"+message);
System.out.println("ReturnCallback: "+"回应码:"+replyCode);
System.out.println("ReturnCallback: "+"回应信息:"+replyText);
System.out.println("ReturnCallback: "+"交换机:"+exchange);
System.out.println("ReturnCallback: "+"路由键:"+routingKey);
}
});
return rabbitTemplate;
}
}
详情
五、消息确认
消费者要手动确认消息时,
配置消息监听器
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*
*
**/
@Configuration
public class MessageListenerConfig {
@Autowired
private CachingConnectionFactory connectionFactory;
@Autowired
private MyAckReceiver myAckReceiver;//消息接收处理类
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
container.setConcurrentConsumers(1); // 指定要创建的并发消费者的数量。默认值为1。
container.setMaxConcurrentConsumers(1); //设置消费者数量的上限;默认为concurrentConsumers。消费者将按需添加。不能小于concurrentConsumers。
container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息
//设置一个队列
container.setQueueNames("TestDirectQueue");
//如果同时设置多个如下: 前提是队列都是必须已经创建存在的
// container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3");
//另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues
//container.setQueues(new Queue("TestDirectQueue",true));
//container.addQueues(new Queue("TestDirectQueue2",true));
//container.addQueues(new Queue("TestDirectQueue3",true));
container.setMessageListener(myAckReceiver);
return container;
}
}
消息接收处理类
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.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.Map;
@Component
public class MyAckReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
byte[] body = message.getBody();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(body));
Map<String,String> msgMap = (Map<String,String>) ois.readObject();
String messageId = msgMap.get("messageId");
String messageData = msgMap.get("messageData");
String createTime = msgMap.get("createTime");
ois.close();
// 同时处理多个队列的不同逻辑
if ("TestDirectQueue".equals(message.getMessageProperties().getConsumerQueue())){
System.out.println("消费的消息来自的队列名为:"+message.getMessageProperties().getConsumerQueue());
System.out.println("消息成功消费到 messageId:"+messageId+" messageData:"+messageData+" createTime:"+createTime);
System.out.println("执行TestDirectQueue中的消息的业务处理流程......");
}
if ("fanout.A".equals(message.getMessageProperties().getConsumerQueue())){
System.out.println("消费的消息来自的队列名为:"+message.getMessageProperties().getConsumerQueue());
System.out.println("消息成功消费到 messageId:"+messageId+" messageData:"+messageData+" createTime:"+createTime);
System.out.println("执行fanout.A中的消息的业务处理流程......");
}
channel.basicAck(deliveryTag, true);
// channel.basicReject(deliveryTag, true);//为true会重新放回队列
} catch (Exception e) {
channel.basicReject(deliveryTag, false);
e.printStackTrace();
}
}
}
注:
1、basicAck 用来确认消息,其中第二个参数multiple为 ture 的时候确认当前和之前的消息;false只确认当前这条
2、basicNack(long deliveryTag, boolean multiple, boolean requeue) 用来拒绝消费消息,multiple
传入true表示将此条数据和id小于他的消息等拒绝;requeue 为true改消息重新入列,false丢弃
3、basicReject 和basicNack基本相同一次只能拒绝一条
4、也可以直接在配置文件中开启手动确认,然后使用@RabbitListener 来接受
spring:
rabbitmq:
port: 5672
host: localhost
username: guest
password: guest
virtual-host: / #虚拟主机 默认/
listener:
simple:
# 表示消费者消费成功消息以后需要手工的进行签收(ack确认),默认为 auto
acknowledge-mode: manual
详细参考消费端手动ACK确认机制
六、死信队列
以下条件只要满足一条,即可以成为死信队列。
- 队列长度满了:排在前面的消息会被拒收或者进入死信交换机
- 消息的ttl时间到了:消息超时未被消费
- 消息被拒收了:手动拒绝消息
1、队列消息超过指定长度
package com.student.rabbitmq.config;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
*
*/
@Configuration
public class RabbitmqConfig {
public static final String QUEUE = "test_queue";
public static final String DIRECT_ROUTING = "test";
public static final String DIRECT_EXCHANGE = "test_Exchange";
public static final String DEAD_EXCHANGE = "DEAD_EXCHANGE";
public static final String DEAD_ROUNTE_KEY = "DEAD_ROUNTE_KEY";
public static final String DEAD_QUEUE = "dead_queue";
@Bean
public Queue informDirectQueue(){
Map<String, Object> map = new HashMap<>();
//设置队列最大长度
map.put("x-max-length",5);
//设置死信队列交换机
map.put("x-dead-letter-exchange",DEAD_EXCHANGE);
//设置死信队列路由key
map.put("x-dead-letter-routing-key",DEAD_ROUNTE_KEY);
return new Queue(QUEUE,true,false,false,map);
}
@Bean
public DirectExchange informDirectExchange(){
return new DirectExchange(RabbitmqConfig.DIRECT_EXCHANGE,true,false);
}
@Bean
public Binding bindingInformDirect(){
return BindingBuilder.bind(informDirectQueue()).to(informDirectExchange()).with(RabbitmqConfig.DIRECT_ROUTING);
}
@Bean
public Queue deadQueue(){
return new Queue(DEAD_QUEUE,true);
}
@Bean
public DirectExchange deadExchange(){
return new DirectExchange(RabbitmqConfig.DEAD_EXCHANGE,true,false);
}
@Bean
public Binding bindingDeadDirect(){
return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(RabbitmqConfig.DEAD_ROUNTE_KEY);
}
}
2、消息超时
继续使用上面的配置文件修改部门配置
@Bean
public Queue informDirectQueue(){
Map<String, Object> map = new HashMap<>();
//设置队列超时时间
map.put("x-message-ttl",5000);
//设置死信队列交换机
map.put("x-dead-letter-exchange",DEAD_EXCHANGE);
//设置死信队列路由key
map.put("x-dead-letter-routing-key",DEAD_ROUNTE_KEY);
return new Queue(QUEUE,true,false,false,map);
}
3、消息拒收
继续使用上面的配置文件,去掉配置超时时间的,消息拒收且不放回队列就会进入死信队列
参考
rabbitmq死信队列详解(亲手实践)_rabbitmq死信队列详解与使用-CSDN博客