rabbitMQ的高级特性02

1.死信队列

	死信队列:DLX,dead-letter-exchange
	利用DLX,当消息在一个队列中变成死信 (dead message) 之后,它能被重新publish到另一个Exchange,
这个Exchange就是DLX

在这里插入图片描述

消息成为死信的三种情况:
1. 队列消息长度到达限制;
2. 消费者拒接消费消息,basicNack/basicReject,并且不把消息重新放入原目标队列,requeue=false;
3. 原队列存在消息过期设置,消息到达超时时间未被消费;

如何实现死信队列呢?
(1)给队列绑定死信交换机

给队列设置参数:x-dead-letter-exchange 和 x-dead-letter-routing-key

代码实现如下:

先创建死信交换机,死信队列,普通交换机,普通队列,然后将普通队列与普通交换机绑定,死信队列与死信交换机绑定,为普通队列设置参数,为了消息过期后,将消息发送到死信交换机上

package com.wyj;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 王逸君
 * @version 1.0
 * @date 2021/4/22 19:08
 */
@Configuration
public class Config {
    private final String EXCHANGE="exchange";
    private final String QUEUE="queue";
    private final String DEAD_EXCHANGE="dead_exchange";
    private final String DEAD_QUEUE="dead_queue";

    //队列
    @Bean
    public Queue queue(){
        return QueueBuilder
                .durable(QUEUE)
                .withArgument("x-dead-letter-exchange",DEAD_EXCHANGE)//设置死信交换机
                .withArgument("x-dead-letter-routing-key","error")//设置队列与死信交换机的rootingkey
                .build();
    }

    //死信队列
    @Bean
    public Queue deadQueue(){
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    //创建交换机
    @Bean
    public Exchange exchange(){
        return ExchangeBuilder.directExchange(EXCHANGE).build();
    }

    //创建死信交换机
    @Bean
    public Exchange deadExchange(){
        return  ExchangeBuilder.directExchange(DEAD_EXCHANGE).build();
    }

    //绑定交换机与队列
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(exchange()).with("error").noargs();
    }

    //绑定死信队列与死信交换机
    @Bean
    public Binding deadBinding(){
        return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("error").noargs();
    }

}

接着创建消费者,进行测试:

package com.wyj.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author 王逸君
 * @version 1.0
 * @date 2021/4/22 20:36
 */
@Component
public class Mylistener {
    @Autowired
    private RedisTemplate redisTemplate;

    @RabbitListener(queues = "queue")
    public void listener(Message message, Channel channel) throws Exception{
        //判断消息是否被消费了
        Object o = redisTemplate.opsForValue().get(message.getMessageProperties().getDeliveryTag());
        if (o==null){
            //执行业务代码
            System.out.println("完成业务功能");
            //设置消息的唯一标识
            redisTemplate.opsForValue().set(message.getMessageProperties().getDeliveryTag(),"wyj");
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
            } catch (IOException e) {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,true);
            }
        }else {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
        }

    }
}

2.延迟队列

rabbitMQ没有直接实现延迟队列,我们可以通过设置死信队列,以及对消息设置过期时间来实现延迟队列,即使用DLX和TTL实现延迟队列

代码实现如下:
生产者:

package com.wyj;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author 王逸君
 * @version 1.0
 * @date 2021/4/22 19:08
 */
@Configuration
public class Config {
    private final String EXCHANGE="exchange";
    private final String QUEUE="queue";
    private final String DEAD_EXCHANGE="dead_exchange";
    private final String DEAD_QUEUE="dead_queue";

    //队列
    @Bean
    public Queue queue(){
        return QueueBuilder
                .durable(QUEUE)
                .withArgument("x-message-ttl",10000)//设置过期时间
                .withArgument("x-max-length",10)//设置消息的最大存储条数
                .withArgument("x-dead-letter-exchange",DEAD_EXCHANGE)//设置死信交换机
                .withArgument("x-dead-letter-routing-key","error")//设置队列与死信交换机的rootingkey
                .build();
    }

    //死信队列
    @Bean
    public Queue deadQueue(){
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    //创建交换机
    @Bean
    public Exchange exchange(){
        return ExchangeBuilder.directExchange(EXCHANGE).build();
    }

    //创建死信交换机
    @Bean
    public Exchange deadExchange(){
        return  ExchangeBuilder.directExchange(DEAD_EXCHANGE).build();
    }

    //绑定交换机与队列
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(exchange()).with("error").noargs();
    }

    //绑定死信队列与死信交换机
    @Bean
    public Binding deadBinding(){
        return BindingBuilder.bind(deadQueue()).to(deadExchange()).with("error").noargs();
    }

}

3.消息的幂等性保障

	幂等性:一次和多次请求某一个资源对于资源本身应该具有同样的效果,也就是说,其任意多次执行对资源本
身所产生的的影响均与一次执行的影响相同,保证消息不会被重复消费。

如何实现消息的幂等性?

(1)消费者获取到消息后先根据id查询redis/db中是否存在该消息
(2)如果存在,则正常消费,消费完毕后写入redis/db
(3)如果不存在,则证明消息被消费过,直接丢弃

代码实现如下:

消费者:

package com.wyj.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @author 王逸君
 * @version 1.0
 * @date 2021/4/22 20:36
 */
@Component
public class Mylistener {
    @Autowired
    private RedisTemplate redisTemplate;

    @RabbitListener(queues = "queue")
    public void listener(Message message, Channel channel) throws Exception{
        //判断消息是否被消费了
        Object o = redisTemplate.opsForValue().get(message.getMessageProperties().getDeliveryTag());
        if (o==null){
            //执行业务代码
            System.out.println("完成业务功能");
            //设置消息的唯一标识
            redisTemplate.opsForValue().set(message.getMessageProperties().getDeliveryTag(),"wyj");
            try {
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
            } catch (IOException e) {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(),true,true);
            }
        }else {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(),true);
        }

    }
}

4.rabbitMQ伪集群的搭建

(1)停止rabbitMQ服务

	service rabbitmq-server stop

(2)开启第一个节点

	RABBITMQ_NODE_PORT=5673 RABBITMQ_NODENAME=rabbit1 rabbitmq-server start

(3)开启第二个节点

	RABBITMQ_NODE_PORT=5674 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15674}]" RABBITMQ_NODENAME=rabbit2 rabbitmq-server start

(4)设置主从关系。将rabbit1设置为主节点,rabbit2设置为从节点

	先操作rabbit1
	[root@super ~]# rabbitmqctl -n rabbit1 stop_app  
	Stopping node rabbit1@super ...
	[root@super ~]# rabbitmqctl -n rabbit1 reset	 
	Resetting node rabbit1@super ...
	[root@super ~]# rabbitmqctl -n rabbit1 start_app
	Starting node rabbit1@super ...

	再操作rabbit2
	[root@super ~]# rabbitmqctl -n rabbit2 stop_app
	Stopping node rabbit2@super ...
	[root@super ~]# rabbitmqctl -n rabbit2 reset
	Resetting node rabbit2@super ...
	[root@super ~]# rabbitmqctl -n rabbit2 join_cluster rabbit1@'localhost' ###''内是主机名换成自己的
	Clustering node rabbit2@super with rabbit1@super ...
	[root@super ~]# rabbitmqctl -n rabbit2 start_app
	Starting node rabbit2@super ...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值