rabbitMQ进阶
一、消息可靠性投递
消息传递过程中可能出现问题的节点:
1、生产者到交换机
2、交换机到队列
3、在队列中丢失
4、队列到消费者
解决方案:
1、 确认模式(confirm)
该模式用于保证生产者到交换机这个阶段消息的安全性。之前案例中,生产者发送消息到交换机,也没有确认是否发送成功,发了就是发了,没有安全性。使用确认模
式,可以将在消息发送完,接收消息的发送状态,根据发送成功与否,再去进行下边的业务逻辑。
Ⅰ、在application.yml文件中开启手动确认模式
server:
port: 8080
spring:
rabbitmq:
host: 192.168.253.128
# 开启rabbitMQ的生产方确认模式 生产者到交换机
publisher-confirm-type: correlated
所有生产者代码都在测试类中写
Ⅱ、生产者代码:
@SpringBootTest(classes = ProducerApp.class)中的ProducerApp是springboot的启动类的类名
@SpringBootTest(classes = ProducerApp.class)
public class DemoTest1 {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void confirmDemo(){
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
//ack的值为true和false,成功为true
System.out.println(ack);
}
});
for(int i=0;i<10;i++) {
rabbitTemplate.convertAndSend("fy_fanout", "", "hello super");
}
}
}
2、 退回模式(return)
退回模式解决了交换机和队列之间传递消息的安全性问题。交换机分发消息给队列,如果队列没有接收成功,则回退给交换机。根据返回的信息,进行下一步操作
Ⅰ、在application.yml文件中开启退回模式
server:
port: 8080
spring:
rabbitmq:
host: 192.168.253.128
# 开启rabbitMQ的生产方确认模式 生产者到交换机
publisher-confirm-type: correlated
# 开启发布者退回模式 交换机到队列
publisher-returns: true
2、示例代码
@SpringBootTest(classes = ProducerApp.class)
public class DemoTest1 {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void returnDemo(){
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returned) {
System.out.println("消息从交换机到队列发生错误"+returned.getReplyText());
}
});
// 可以让交换机和队列间的routing key 不匹配,来模拟分发消息失败
rabbitTemplate.convertAndSend("fy_topic","pppppppppp","hello return");
}
}
3、持久化
将队列数据持久化,防止队列数据丢失
4、客户端确认
解决了队列到客户端之间数据的安全性问题。一般情况下,客户端在拿到消息后,不管业务是否执行成功,队列中的该条消息都会被删除,那么如果业务逻辑没有执行成
功,再次拿该条消息进行业务处理时,就发现消息不存在,这是有问题的。解决方法:当客户端的业务逻辑执行成功后,确认后,再让队列将消息删除。
Ⅰ、在消费端模块中的application.yml中开启手动确认模式
server:
port: 8081
spring:
rabbitmq:
host: 192.168.253.128
listener:
simple:
# 消费端需要手动确认,有三个值 none 自动,manual 手动,auto 根据情况
acknowledge-mode: manual
Ⅱ、消费端的实现
自己加一个出错信息,模拟业务逻辑出现问题,观察控制台打印结果以及队列中的数据变化
@Component
public class Listener01 {
@RabbitListener(queues = "lyd_fanout01")
public void listener(Message message, Channel channel) throws IOException {
// 消息的标识标签
long deliveryTag = message.getMessageProperties().getDeliveryTag();
// 队列中的信息
byte[] body = message.getBody();
String msg=new String(body);
System.out.println(msg);
try{
// int a= 10 / 0;
System.out.println("业务逻辑处理");
/**
* @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
* @param multiple true to reject all messages up to and including
*/
// 消费端手动确认消息
channel.basicAck(deliveryTag,true); //确定接收后消息从队列中删除
}catch (Exception e){
/**
* @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}
* @param multiple true to reject all messages up to and including
* the supplied delivery tag; false to reject just the supplied
* delivery tag.
* @param requeue true if the rejected message(s) should be requeued rather
*/
channel.basicNack(deliveryTag,true,true);//如果业务出错,则消息不删除,并再次发从该消息
}
}
}
二、消费端限流
为什么要消费端限流:
如果有大量的消息堆积在队列中,而消费端的业务逻辑耗时很长,就不能一下让太多的访问消息到达消费端,严重了就服务器挂掉。
Ⅰ、消费端模块application.yml中开启限流
server:
port: 8081
spring:
rabbitmq:
host: 192.168.253.128
listener:
simple:
# 消费端需要手动确认
acknowledge-mode: manual
# 每次拿三个消息
prefetch: 3
Ⅱ、观看效果
使用上边客户端确认的代码,将channel.ack()方法注释了,不要有异常,就可以观看到效果
三、TTL(设置队列的存活时间)
1、设置的时间是给队列中的消息设置的。
2、如果消息没有在队头,那么即使存活时间到了,也不会被队列删除,但是这条消息是读取不到的
Ⅰ、给队列中的单个元素设置过期时间
@SpringBootTest(classes = ProducerApp.class)
public class DemoTest1 {
@Autowired
private RabbitTemplate rabbitTemplate;
// 给单个消息设置存活时间
@Test
public void infoTTL(){
for(int i=0;i<10;i++){
// i=3时,设置消息存活时间20秒
if(i==3){
MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
message.getMessageProperties().setExpiration("20000");
return message;
}
};
rabbitTemplate.convertAndSend("fy_fanout","","hello return"+i,messagePostProcessor);
}else {
rabbitTemplate.convertAndSend("fy_fanout","","hello return"+i);
}
}
}
}
Ⅱ、使用浏览器端给整个队列设置时间,作用到的也是队列中的消息
在创建队列时设置存活时间
四、springboot中使用代码创建交换机、队列
@Configuration
public class RabbitConfig {
@Bean
public Exchange exchange(){
// 创建交换机,并命名,设置持久性
Exchange exchange = ExchangeBuilder.fanoutExchange("fy_fanout01").durable(true).build();
return exchange;
}
@Bean
public Queue queue(){
// 创建队列,并命名,设置队列中消息的存活时间
Queue queue = QueueBuilder.durable("lyd_fanout04").withArgument("x-message-ttl",20000).build();
return queue;
}
@Bean
public Binding binding(Queue queue,Exchange exchange){
// 绑定交换机和队列
return BindingBuilder.bind(queue).to(exchange).with("").noargs();
}
}