RubbitMQ的使用以及死信队列的处理Demo(JAVA)

RubbitMQ的安装开启请自行百度,接下来直入正题。

1.1 后台配置

RubbitMQ后台 创建admin账户   添加 virtual-host为admin_host

1.2 yml文件配置  以及jar包文件

rabbitmq:
  host: 127.0.0.1
  port: 5672
  username: admin
  password: admin
  virtual-host: admin_host
  publisher-confirm-type: correlated
  publisher-returns: true
  listener:
    simple:
      acknowledge-mode: manual  #开启手动确认,none代表不确认,manual才是手动确认,auto自动确认
      retry:
        enabled: true #开启重试
        max-attempts: 3 #最大重试次数,默认3次,达到次数后,会进行消息移除。若绑定了死信队列,则会放入死信队列中
        initial-interval: 2000ms  #重试间隔时间

 

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
    <version>2.7.0</version>
</dependency>

1.3 用配置类自启动创建普通交换机和死信交换机  并将其绑定(例子是两个普通队列绑定两个死信队列)

package com.example.logindemo.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;


/**
 * Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输,
 * Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。
 * Queue:消息的载体,每个消息都会被投到一个或多个队列。
 * Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来.
 * Routing Key:路由关键字,exchange根据这个关键字进行消息投递。
 * vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。
 * Producer:消息生产者,就是投递消息的程序.
 * Consumer:消息消费者,就是接受消息的程序.
 * Channel:消息通道,在客户端的每个连接里,可建立多个channel.
 */
@Slf4j
@Component
public class RabbitConfig {
    //业务交换机
    public static final String EXCHANGE_PHCP = "phcp";
    //业务队列1
    public static final String QUEUE_COMPANY = "company";
    //业务队列1的key
    public static final String ROUTINGKEY_COMPANY = "companyKey";
    //业务队列2
    public static final String QUEUE_PROJECT = "project";
    //业务队列2的key
    public static final String ROUTINGKEY_PROJECT = "projectKey";

    //死信交换机
    public static final String EXCHANGE_PHCP_DEAD = "phcp_dead";
    //死信队列1
    public static final String QUEUE_COMPANY_DEAD = "company_dead";
    //死信队列2
    public static final String QUEUE_PROJECT_DEAD = "project_dead";
    //死信队列1的key
    public static final String ROUTINGKEY_COMPANY_DEAD = "companyKey_dead";
    //死信队列2的key
    public static final String ROUTINGKEY_PROJECT_DEAD = "projectKey_dead";


//    /**
//     * 解决重复确认报错问题,如果没有报错的话,就不用启用这个
//     *
//     * @param connectionFactory
//     * @return
//     */
    @Bean
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return factory;
    }

    /**
     * 声明业务交换机
     * 1. 设置交换机类型
     * 2. 将队列绑定到交换机
     * FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
     * HeadersExchange :通过添加属性key-value匹配
     * DirectExchange:按照routingkey分发到指定队列
     * TopicExchange:多关键字匹配
     */
    @Bean("exchangePhcp")
    public DirectExchange exchangePhcp() {
        return new DirectExchange(EXCHANGE_PHCP);
    }

    //     * 声明死信交换机
    @Bean("exchangePhcpDead")
    public DirectExchange exchangePhcpDead() {
        return new DirectExchange(EXCHANGE_PHCP_DEAD);
    }

    //
//     * 声明业务队列1
//                *
//     * @return
    @Bean("queueCompany")
    public Queue queueCompany() {
        Map<String, Object> arguments = new HashMap<>(2);
        arguments.put("x-dead-letter-exchange", EXCHANGE_PHCP_DEAD);
        //绑定该队列到死信交换机的队列1
        arguments.put("x-dead-letter-routing-key", ROUTINGKEY_COMPANY_DEAD);
        return QueueBuilder.durable(QUEUE_COMPANY).withArguments(arguments).build();
    }

    //     * 声明业务队列2
    @Bean("queueProject")
    public Queue queueProject() {
        Map<String, Object> arguments = new HashMap<>(2);
        arguments.put("x-dead-letter-exchange", EXCHANGE_PHCP_DEAD);
        //绑定该队列到死信交换机的队列2
        arguments.put("x-dead-letter-routing-key", ROUTINGKEY_PROJECT_DEAD);
        return QueueBuilder.durable(QUEUE_PROJECT).withArguments(arguments).build();
    }

    //     * 声明死信队列1
    @Bean("queueCompanyDead")
    public Queue queueCompanyDead() {
        return new Queue(QUEUE_COMPANY_DEAD);
    }

    //     * 声明死信队列2
    @Bean("queueProjectDead")
    public Queue queueProjectDead() {
        return new Queue(QUEUE_PROJECT_DEAD);
    }

    /**
     * 绑定业务队列1和业务交换机
     *
     * @param queue
     * @param directExchange
     */
    @Bean
    public Binding bindingQueueCompany(@Qualifier("queueCompany") Queue queue, @Qualifier("exchangePhcp") DirectExchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(RabbitConfig.ROUTINGKEY_COMPANY);
    }

    //     * 绑定业务队列2和业务交换机
    @Bean
    public Binding bindingQueueProject(@Qualifier("queueProject") Queue queue, @Qualifier("exchangePhcp") DirectExchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(RabbitConfig.ROUTINGKEY_PROJECT);
    }

    //     * 绑定死信队列1和死信交换机
    @Bean
    public Binding bindingQueueCompanyDead(@Qualifier("queueCompanyDead") Queue queue, @Qualifier("exchangePhcpDead") DirectExchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(RabbitConfig.ROUTINGKEY_COMPANY_DEAD);
    }

    //     * 绑定死信队列2和死信交换机
    @Bean
    public Binding bindingQueueProjectDead(@Qualifier("queueProjectDead") Queue queue, @Qualifier("exchangePhcpDead") DirectExchange directExchange) {
        return BindingBuilder.bind(queue).to(directExchange).with(RabbitConfig.ROUTINGKEY_PROJECT_DEAD);
    }
}

1.4 生产者发送消息

package com.example.logindemo.config;


import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Component
@Slf4j
public class RabbltProducer implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnsCallback{
    @Resource
    private RabbitTemplate rabbitTemplate;
    /**
     * 初始化消息确认函数
     */
    @PostConstruct
    public void init() {
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
        rabbitTemplate.setMandatory(true);
    }
    /**
     * 发送消息服务器确认函数
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if (ack) {
            System.out.println("消息发送成功" + correlationData);
        } else {
            System.out.println("消息发送失败:" + cause);
        }
    }
    /**
     * 消息发送失败,消息回调函数
     * @param returnedMessage
     */
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
        String str = new String(returnedMessage.getMessage().getBody());
        System.out.println("消息发送失败:" + str);
    }
    /**
     * 处理消息发送到队列1
     * @param str
     */
    public void sendCompany(String str){
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType("application/json");
        //设置信息过期时间,过期未手动处理则放入死信队列   自行根据业务设置时长
        messageProperties.setExpiration("20000");
        Message message = new Message(str.getBytes(StandardCharsets.UTF_8),messageProperties);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        this.rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_PHCP,RabbitConfig.ROUTINGKEY_COMPANY,message,correlationData);
        //也可以用下面的方式
        //CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        //this.rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_PHCP,RabbitConfig.ROUTINGKEY_COMPANY,str,correlationData);
    }
    /**
     * 处理消息发送到队列2
     * @param str
     */
    public void sendProject(String str){
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setContentType("application/json");
        Message message = new Message(str.getBytes(StandardCharsets.UTF_8),messageProperties);
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        this.rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_PHCP,RabbitConfig.ROUTINGKEY_PROJECT,message,correlationData);
        //也可以用下面的方式
        //CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        //this.rabbitTemplate.convertAndSend(RabbitConfig.EXCHANGE_PHCP,RabbitConfig.ROUTINGKEY_PROJECT,str,correlationData);
    }
}

1.5 消费者监听业务交换机中的队列 消费消息   

demo中测试注释部分是关闭了监听业务队列,获取消息但是不及时消费,如需消费可以打开,自行测试

package com.example.logindemo.config;

import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
 * 监听业务交换机
 * @author JeWang
 */
@Component
@Slf4j
public class RabbitConsumer {
    /**
     * 监听业务队列1
     * @param message
     * @param channel
     * @throws IOException
     */
//    @RabbitListener(queues = "company")
//    @RabbitHandler
//    public void company(Message message, Channel channel) throws IOException {
//        try{
//            System.out.println("次数" + message.getMessageProperties().getDeliveryTag());
//            channel.basicQos(1);
//            Thread.sleep(2000);
//            String s = new String(message.getBody());
//            log.info("处理消息"+s);
//            //下面两行是尝试手动抛出异常,用来测试重试次数和发送到死信交换机
            String str = null;
            str.split("1");
//            //处理成功,确认应答
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//        }catch (Exception e){
//            log.error("处理消息时发生异常:"+e.getMessage());
//            Boolean redelivered = message.getMessageProperties().getRedelivered();
//            if(redelivered){
//                log.error("异常重试次数已到达设置次数,将发送到死信交换机");
//                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
//            }else {
//                log.error("消息即将返回队列处理重试");
//                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
//            }
//        }
//    }
    /**
     * 监听业务队列2
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(queues = "project")
//    @RabbitHandler
    public void project(Message message, Channel channel) throws IOException {
        try{
            System.out.println("次数" + message.getMessageProperties().getDeliveryTag());
            channel.basicQos(1);
            Thread.sleep(2000);
            String s = new String(message.getBody());
            log.info("处理消息"+s);
            //下面两行是尝试手动抛出异常,用来测试重试次数和发送到死信交换机
            //String str = null;
            //str.split("1");
            //处理成功,确认应答
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }catch (Exception e){
            log.error("处理消息时发生异常:"+e.getMessage());
            Boolean redelivered = message.getMessageProperties().getRedelivered();
            if(redelivered){
                log.error("异常重试次数已到达设置次数,将发送到死信交换机");
                channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
            }else {
                log.error("消息即将返回队列处理重试");
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
            }
        }
    }
}

1.6 消费者监听死信交换机中的死信队列 消费消息   

package com.example.logindemo.config;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
 * 监听死信交换机
 * @author JeWang
 */
@Component
@Slf4j
public class RabbitConsumerDead {
    /**
     * 处理死信队列1
     * @param message
     * @param channel
     * @throws IOException
     */
//    @RabbitListener(queues = "company_dead")
//    @RabbitHandler
//    public void company_dead(Message message, Channel channel) throws IOException {
//        try{
//            channel.basicQos(1);
//            String s = new String(message.getBody());
//            log.info("处理死信"+s);
//            //在此处记录到数据库、报警之类的操作
//            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
//        }catch (Exception e){
//            log.error("接收异常:"+e.getMessage());
//        }
//    }
    /**
     * 处理死信队列2
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(queues = "project_dead")
//    @RabbitHandler
    public void project_dead(Message message, Channel channel) throws IOException {
        try{
            channel.basicQos(1);
            String s = new String(message.getBody());
            log.info("处理死信"+s);
            //在此处记录到数据库、报警之类的操作
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }catch (Exception e){
            log.error("接收异常:"+e.getMessage());
        }
    }
}

demo中测试注释部分是关闭了监听业务队列,获取消息但是不及时消费,如需消费可以打开,自行测试

1.7 mq的相关配置处理完之后就可以发消息了(关于发送消息的格式可以自行选择配置,这就不详解了)

package com.example.logindemo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RequestMapping("/def")
@RestController
@Slf4j
public class MsgController {
    @Resource
    private RabbltProducer rabbltProducer;

    @RequestMapping("/handleCompany")
    public void handleCompany(@RequestBody String jsonStr){
        rabbltProducer.sendCompany(jsonStr);
    }
}

 Ok,本次教程到此完啦,看完记得点个赞哦!谢谢!

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要设置一个死信队列,在 RabbitMQ 中,需要进行以下步骤: 1. 首先创建两个队列,分别是正常队列死信队列,以及一个交换机。 2. 正常队列中需要设置死信队列地址。 3. 将交换机和正常队列通过一个 routing key 绑定起来。 4. 当正常队列中的消息过期或者未能被消费时,就会被转发到死信队列中。 以下是一个使用 Java 实现的示例代码: ``` // 创建连接工厂对象 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setUsername("guest"); factory.setPassword("guest"); // 创建连接对象 Connection connection = factory.newConnection(); // 创建信道对象 Channel channel = connection.createChannel(); // 定义交换机名称和类型 String exchangeName = "demo.exchange"; String exchangeType = "direct"; // 定义正常队列名称、死信队列名称和 routing key String queueName = "demo.queue"; String deadLetterQueueName = "demo.dead.letter.queue"; String routingKey = "demo.routing.key"; // 定义正常队列属性 Map<String, Object> queueArgs = new HashMap<>(); queueArgs.put("x-message-ttl", 10000); queueArgs.put("x-dead-letter-exchange", exchangeName); queueArgs.put("x-dead-letter-routing-key", deadLetterRoutingKey); // 声明交换机 channel.exchangeDeclare(exchangeName, exchangeType, true, false, null); // 声明正常队列 channel.queueDeclare(queueName, true, false, false, queueArgs); // 声明死信队列 channel.queueDeclare(deadLetterQueueName, true, false, false, null); // 绑定正常队列和 routing key 到交换机 channel.queueBind(queueName, exchangeName, routingKey); // 发送消息到正常队列 channel.basicPublish(exchangeName, routingKey, null, "Hello World!".getBytes()); // 关闭连接 channel.close(); connection.close(); ``` 在以上代码中,我们创建了一个连接工厂对象,使用默认的主机、用户名和密码创建连接对象。然后我们创建了一个信道对象,定义了交换机名称和类型,以及正常队列名称、死信队列名称和 routing key。接着,我们定义了正常队列的属性,其中包括了消息过期时间和死信队列的地址。然后我们声明了交换机、正常队列死信队列,并将正常队列和 routing key 绑定到交换机上。最后,我们发送了一个消息到正常队列,此时如果消息未被消费或者过期了,就会被转发到死信队列中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值