spring整合rabbitMQ各种模式,TTL死信高级特性


源码及安装包已上传到csdn, 下载地址:https://download.csdn.net/download/weixin_45352783/85862706

基本配置

创建两个项目,方便后期测试在这里插入代码片
在这里插入图片描述
cus: 消费者
pus: 生产者

pom文件

    <dependencies>
        <!-- web 方便测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- rabbitMQ -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
    </dependencies>

application.properties
生产者和消费者只有端口不同就不贴两次了

server.port=9091
# 应用名
spring.application.name=springboot-rabbitmq
# rabbitmq配置信息
# ip
spring.rabbitmq.host=127.0.0.1
# 端口
spring.rabbitmq.port=5672
# 用户名
spring.rabbitmq.username=admin
# 密码
spring.rabbitmq.password=admin
# 配置虚拟机
spring.rabbitmq.virtual-host=/
# 消息开启手动确认
spring.rabbitmq.listener.direct.acknowledge-mode=manual

work模式

在这里插入图片描述
这是rabbitMQ官网给的work_queues的图例

生产者添加配置文件

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

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Configuration
public class RabbitmqConfig {

    /**
     * 配置一个工作模型队列
     * @return
     */
    @Bean
    public Queue queueWork1() {
        return new Queue("queue_work");
    }
}

添加controller方便测试

import com.xianyu.pro.service.RabbitmqService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@RestController
public class RabbitmqController {
    @Autowired
    private RabbitmqService rabbitmqService;

    @RequestMapping("/sendWork")
    public Object sendWork() {
        rabbitmqService.sendWork();
        return "发送成功...";
    }
}

service


import com.xianyu.pro.service.RabbitmqService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Service
public class RabbitmqServiceImpl implements RabbitmqService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void sendWork() {
        for (int i = 0; i < 10; i++) {
            //消息一次性发送
            rabbitTemplate.convertAndSend("queue_work","测试work模型: " + i);
        }
    }
}

启动,发送测试

在这里插入图片描述

在rabbitMQ的控制台已经可以看到消息
在这里插入图片描述

编写消费者

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Component
public class WorkReceiveListener {

    @RabbitListener(queues = "queue_work")
    public void receiveMessage(String msg) {
        // 只包含发送的消息
        System.out.println("1接收到消息:" + msg);

    }

    @RabbitListener(queues = "queue_work")
    public void receiveMessage2(String msg) {
        // 包含所有的信息
        System.out.println("2接收到消息:" + msg);
    }

}

启动消费者

在这里插入图片描述
已完成消费
在这里插入图片描述
控制台上可以看到队列也没有消息了

发布订阅模式

Publish/Subscribe模式
在这里插入图片描述
这里用到了交换机Exchange,就是紫色的X

work一个队列里面的消息,被竞争消费,发布订阅模式互补干扰
比如生产者发布了10条消息,有两个消费者,work模式可能就是一个消费者消费5条,发布订阅模式就是每个消费者都能消费10条

修改RabbitmqConfig


import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Configuration
public class RabbitmqConfig {

    /**
     * 配置一个工作模型队列
     * @return
     */
    @Bean
    public Queue queueWork1() {
        return new Queue("queue_work");
    }

    /**
     * 准备一个交换机
     * @return
     */
    @Bean
    public FanoutExchange exchangeFanout() {
        return new FanoutExchange("exchange_fanout");
    }

    /**
     * 声明两个队列
     * @return
     */
    @Bean
    public Queue queueFanout1() {
        return new Queue("queue_fanout1");
    }
    @Bean
    public Queue queueFanout2() {
        return new Queue("queue_fanout2");
    }

    /**
     * 将交换机和队列1进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange1() {
        return BindingBuilder.bind(queueFanout1()).to(exchangeFanout());
    }

    /**
     * 将交换机和队列2进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange2() {
        return BindingBuilder.bind(queueFanout2()).to(exchangeFanout());
    }

}

添加接口

controller

    @RequestMapping("/sendPublish")
    public Object sendPublish() {
        rabbitmqService.sendPublish();
        return "发送成功...";
    }

service

    /**
     * 发送订阅消息
     */
    void sendPublish();

serviceImpl

    @Override
    public void sendPublish() {
        for (int i = 0; i < 5; i++) {
            rabbitTemplate.convertAndSend("exchange_fanout", "", "测试发布订阅模型:" + i);
        }
    }

在这里插入图片描述
测试,发送成功,查看控制台
在这里插入图片描述
编写消费端

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Component
public class PublishReceiveListener {

    @RabbitListener(queues = "queue_fanout1")
    public void receiveMsg1(String msg) {
        System.out.println("队列1接收到消息:" + msg);
    }

    @RabbitListener(queues = "queue_fanout2")
    public void receiveMsg2(String msg) {
        System.out.println("队列2接收到消息:" + msg);
    }
}

在这里插入图片描述
接收到消息
在这里插入图片描述
控制台

routing和topics模式

routing模式

在这里插入图片描述
修改RabbitmqConfig配置文件

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

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Configuration
public class RabbitmqConfig {

    /**
     * 配置一个工作模型队列
     * @return
     */
    @Bean
    public Queue queueWork1() {
        return new Queue("queue_work");
    }

    /**
     * 准备一个交换机
     * @return
     */
    @Bean
    public FanoutExchange exchangeFanout() {
        return new FanoutExchange("exchange_fanout");
    }

    /**
     * 声明两个队列
     * @return
     */
    @Bean
    public Queue queueFanout1() {
        return new Queue("queue_fanout1");
    }
    @Bean
    public Queue queueFanout2() {
        return new Queue("queue_fanout2");
    }

    /**
     * 将交换机和队列1进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange1() {
        return BindingBuilder.bind(queueFanout1()).to(exchangeFanout());
    }

    /**
     * 将交换机和队列2进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange2() {
        return BindingBuilder.bind(queueFanout2()).to(exchangeFanout());
    }


    /**
     * routing交换机
     * @return
     */
    @Bean
    public DirectExchange exchangeRouting() {
        return new DirectExchange("exchange_routing");
    }

    /**
     * routing队列1
     * @return
     */
    @Bean
    public Queue queueRouting1() {
        return new Queue("queue_routing1");
    }

    /**
     * routing队列2
     * @return
     */
    @Bean
    public Queue queueRouting2() {
        return new Queue("queue_routing2");
    }

    @Bean
    public Binding bindingRouting1() {
        return BindingBuilder.bind(queueRouting1()).to(exchangeRouting()).with("error");
    }
    @Bean
    public Binding bindingRouting2() {
        return BindingBuilder.bind(queueRouting2()).to(exchangeRouting()).with("info");
    }


}

在这里插入图片描述

在交换机绑定队列是添加routingKey,这里是自定义字符串
添加接口

    @RequestMapping("/sendRouting")
    public Object sendRouting() {
        rabbitmqService.sendRouting();
        return "发送成功...";
    }

service

    /**
     * 发送routing消息
     */
    void sendRouting();

serviceImpl

    @Override
    public void sendRouting() {
        for (int i = 0; i < 5; i++) {
            rabbitTemplate.convertAndSend("exchange_routing", "error", "测试routing模型,error:" + i);
            rabbitTemplate.convertAndSend("exchange_routing", "info", "测试routing模型,info:" + i);
        }
    }

消费端添加监听

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Component
public class RoutingReceiveListener {

    @RabbitListener(queues = "queue_routing1")
    public void receiveMsg1(String msg) {
        System.out.println("消费者1接收到:" + msg);
    }

    @RabbitListener(queues = "queue_routing2")
    public void receiveMsg2(String msg) {
        System.out.println("消费者2接收到:" + msg);
    }

}

postman测试
在这里插入图片描述
消费端打印
在这里插入图片描述
这里消费者1只接收到了error,消费者2只接收到了info

topics模式

routing和topics类似,topics可以理解为使用 * # 通配符的routing
在这里插入图片描述

topic里面的通配符,*表示一个单词,#表示多个

修改RabbitmqConfig 配置文件


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

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Configuration
public class RabbitmqConfig {

    /**
     * 配置一个工作模型队列
     * @return
     */
    @Bean
    public Queue queueWork1() {
        return new Queue("queue_work");
    }

    /**
     * 准备一个交换机
     * @return
     */
    @Bean
    public FanoutExchange exchangeFanout() {
        return new FanoutExchange("exchange_fanout");
    }

    /**
     * 声明两个队列
     * @return
     */
    @Bean
    public Queue queueFanout1() {
        return new Queue("queue_fanout1");
    }
    @Bean
    public Queue queueFanout2() {
        return new Queue("queue_fanout2");
    }

    /**
     * 将交换机和队列1进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange1() {
        return BindingBuilder.bind(queueFanout1()).to(exchangeFanout());
    }

    /**
     * 将交换机和队列2进行绑定
     * @return
     */
    @Bean
    public Binding bindingExchange2() {
        return BindingBuilder.bind(queueFanout2()).to(exchangeFanout());
    }


    /**
     * routing交换机
     * @return
     */
    @Bean
    public DirectExchange exchangeRouting() {
        return new DirectExchange("exchange_routing");
    }

    /**
     * routing队列1
     * @return
     */
    @Bean
    public Queue queueRouting1() {
        return new Queue("queue_routing1");
    }

    /**
     * routing队列2
     * @return
     */
    @Bean
    public Queue queueRouting2() {
        return new Queue("queue_routing2");
    }

    @Bean
    public Binding bindingRouting1() {
        return BindingBuilder.bind(queueRouting1()).to(exchangeRouting()).with("error");
    }
    @Bean
    public Binding bindingRouting2() {
        return BindingBuilder.bind(queueRouting2()).to(exchangeRouting()).with("info");
    }

    /**
     * topic交换机
     * @return
     */
    @Bean
    public TopicExchange exchangeTopic() {
        return new TopicExchange("exchange_topic");
    }

    /**
     * topic队列1
     * @return
     */
    @Bean
    public Queue queueTopic1() {
        return new Queue("queue_topic1");
    }
    /**
     * topic队列2
     * @return
     */
    @Bean
    public Queue queueTopic2() {
        return new Queue("queue_topic2");
    }

    /**
     * 绑定topic队列和交换机
     * @return
     */
    @Bean
    public Binding bindingTopic1() {
        return BindingBuilder.bind(queueTopic1()).to(exchangeTopic()).with("topic.#");
    }
    @Bean
    public Binding bindingTopic2() {
        return BindingBuilder.bind(queueTopic2()).to(exchangeTopic()).with("topic.*");
    }

}

添加controller

    @RequestMapping("/sendTopic")
    public Object sendTopic() {
        rabbitmqService.sendTopic();
        return "发送成功...";
    }

service

    /**
     * 发送topic消息
     */
    void sendTopic();

serviceImpl

	@Override
    public void sendTopic() {
        for (int i = 0; i < 5; i++) {
            rabbitTemplate.convertAndSend("exchange_topic", "topic.hello", "测试topic模型,hello:" + i);
            rabbitTemplate.convertAndSend("exchange_topic", "topic.hello.word", "测试topic模型,hello_word:" + i);
        }
    }

启动,postman测试
在这里插入图片描述
在这里插入图片描述
编写消费者


import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Component
public class TopicReceiveListener {

    @RabbitListener(queues = "queue_topic1")
    public void receiveMsg1(String msg) {
        System.out.println("消费者1接收到:" + msg);
    }

    @RabbitListener(queues = "queue_topic2")
    public void receiveMsg2(String msg) {
        System.out.println("消费者2接收到:" + msg);
    }
}

启动测试
在这里插入图片描述
可以看到消费者1能接收到所有的消息,消费者2只能接收到hello,不能接收hello.word作为routingKey的消息

rabbitMQ的几种基础用法到这里就说完了

rabbitMQ 3种消息确认机制

# 消息开启手动确认 手动确认后队列才会删除消息
spring.rabbitmq.listener.direct.acknowledge-mode=manual
# 不用确认,消费端接收到消息后,队列中删除
spring.rabbitmq.listener.direct.acknowledge-mode=none
# 自动模式,根据报错信息来判断是否删除消息,较麻烦,实际使用也很少
spring.rabbitmq.listener.direct.acknowledge-mode=auto

消息的确认投递和确认消费

后续再补充

TTL队列

死信队列

一般来说,producer 将消息投递到 broker 或者直接到queue 里了,consumer 从 queue 取出消息
进行消费,但某些时候由于特定的原因导致 queue 中的某些消息无法被消费,这样的消息如果没有
后续的处理,就变成了死信,有死信自然就有了死信队列。
为了保证订单业务的消息数据不丢失,需要使用到 RabbitMQ 的死信队列机制,当消息
消费发生异常时,将消息投入死信队列中.

死信的来源

消息 TTL 过期
队列达到最大长度(队列满了,无法再添加数据到 mq 中)
消息被拒绝(basic.reject 或 basic.nack)并且 requeue=false.

代码架构图
在这里插入图片描述
RabbitmqConfig配置文件中添加业务交换机,业务队列,死信队列

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;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */
@Configuration
public class RabbitmqConfig {

    /**
     * 配置一个工作模型队列
     *
     * @return
     */
    @Bean
    public Queue queueWork1() {
        return new Queue("queue_work");
    }

    /**
     * 准备一个交换机
     *
     * @return
     */
    @Bean
    public FanoutExchange exchangeFanout() {
        return new FanoutExchange("exchange_fanout");
    }

    /**
     * 声明两个队列
     *
     * @return
     */
    @Bean
    public Queue queueFanout1() {
        return new Queue("queue_fanout1");
    }

    @Bean
    public Queue queueFanout2() {
        return new Queue("queue_fanout2");
    }

    /**
     * 将交换机和队列1进行绑定
     *
     * @return
     */
    @Bean
    public Binding bindingExchange1() {
        return BindingBuilder.bind(queueFanout1()).to(exchangeFanout());
    }

    /**
     * 将交换机和队列2进行绑定
     *
     * @return
     */
    @Bean
    public Binding bindingExchange2() {
        return BindingBuilder.bind(queueFanout2()).to(exchangeFanout());
    }


    /**
     * routing交换机
     *
     * @return
     */
    @Bean
    public DirectExchange exchangeRouting() {
        return new DirectExchange("exchange_routing");
    }

    /**
     * routing队列1
     *
     * @return
     */
    @Bean
    public Queue queueRouting1() {
        return new Queue("queue_routing1");
    }

    /**
     * routing队列2
     *
     * @return
     */
    @Bean
    public Queue queueRouting2() {
        return new Queue("queue_routing2");
    }

    @Bean
    public Binding bindingRouting1() {
        return BindingBuilder.bind(queueRouting1()).to(exchangeRouting()).with("error");
    }

    @Bean
    public Binding bindingRouting2() {
        return BindingBuilder.bind(queueRouting2()).to(exchangeRouting()).with("info");
    }

    /**
     * topic交换机
     *
     * @return
     */
    @Bean
    public TopicExchange exchangeTopic() {
        return new TopicExchange("exchange_topic");
    }

    /**
     * topic队列1
     *
     * @return
     */
    @Bean
    public Queue queueTopic1() {
        return new Queue("queue_topic1");
    }

    /**
     * topic队列2
     *
     * @return
     */
    @Bean
    public Queue queueTopic2() {
        return new Queue("queue_topic2");
    }

    /**
     * 绑定topic队列和交换机
     *
     * @return
     */
    @Bean
    public Binding bindingTopic1() {
        return BindingBuilder.bind(queueTopic1()).to(exchangeTopic()).with("topic.#");
    }

    @Bean
    public Binding bindingTopic2() {
        return BindingBuilder.bind(queueTopic2()).to(exchangeTopic()).with("topic.*");
    }


    // 声明业务Exchange
    @Bean
    public FanoutExchange businessExchange() {
        return new FanoutExchange("business_exchange");
    }

    // 声明死信Exchange
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("dead_letter_exchange");
    }

    // 声明业务队列A
    @Bean
    public Queue businessQueueA() {
        Map<String, Object> args = new HashMap<>(2);
        args.put("x-dead-letter-exchange", "dead_letter_exchange");
        args.put("x-dead-letter-routing-key", "dead_letter_queuea_routing_key");
        return QueueBuilder.durable("business_queueA").withArguments(args).build();
    }

    // 声明业务队列B
    @Bean
    public Queue businessQueueB() {
        Map<String, Object> args = new HashMap<>(2);
        args.put("x-dead-letter-exchange", "dead_letter_exchange");
        args.put("x-dead-letter-routing-key", "dead_letter_queueb_routing_key");
        return QueueBuilder.durable("business_queueB").withArguments(args).build();
    }

    // 声明死信队列A
    @Bean
    public Queue deadLetterQueueA() {
        return new Queue("dead_letter_queueA");
    }

    // 声明死信队列B
    @Bean
    public Queue deadLetterQueueB() {
        return new Queue("dead_letter_queueB");
    }

    // 声明业务队列A绑定关系
    @Bean
    public Binding businessBindingA() {
        return BindingBuilder.bind(businessQueueA()).to(businessExchange());
    }

    // 声明业务队列B绑定关系
    @Bean
    public Binding businessBindingB() {
        return BindingBuilder.bind(businessQueueB()).to(businessExchange());
    }

    // 声明死信队列A绑定关系
    @Bean
    public Binding deadLetterBindingA() {
        return BindingBuilder.bind(deadLetterQueueA()).to(deadLetterExchange()).with("dead_letter_queuea_routing_key");
    }

    // 声明死信队列B绑定关系
    @Bean
    public Binding deadLetterBindingB() {
        return BindingBuilder.bind(deadLetterQueueB()).to(deadLetterExchange()).with("dead_letter_queueb_routing_key");
    }


}

在这里插入图片描述
参数map的key是固定的,在RabbitMQ控制台中能够看到
在这里插入图片描述
添加controller方便测试

    @RequestMapping("/sendDead")
    public Object sendDead() {
        rabbitmqService.sendDead();
        return "发送成功...";
    }

service

    /**
     * 测试死信队列
     */
    void sendDead();

serviceImpl

    @Override
    public void sendDead() {
        rabbitTemplate.convertSendAndReceive("business_exchange", "", "测试死信队列");
    }

消费端代码实现


import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * @authoer:xianyu
 * @createDate:2022/7/1
 * @description:
 */

@Component
public class BusinessMessageReceiver {

    @RabbitListener(queues = "business_queueA")
    public void receiveA(Message message, Channel channel) throws IOException {
        String msg = new String(message.getBody());
        boolean ack = true;
        try {
            //手动制造异常
            int i=1/0;
        } catch (Exception e){
            ack = false;
        }
        if (!ack){
            //拒绝消息,并不放回队列
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
        } else {
            //确认消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }
    }

    @RabbitListener(queues = "business_queueB")
    public void receiveB(Message message, Channel channel) throws IOException {
        System.out.println("收到业务消息B:" + new String(message.getBody()));
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }
}

在这里插入图片描述
这里basicNack方法第三个参数:是否重回队列,false否,如果绑定了死信队列,就会进入死信队列

这里只展示者一种进入死信队列的方式,其他的后续补充

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值