[MQ] SpringBoot使用直连交换机Direct Exchange

✨个人主页:沫洺的主页

📚📚系列专栏: 📖 JavaWeb专栏📖 JavaSE专栏 📖 Java基础专栏📖vue3专栏 

                           📖MyBatis专栏📖Spring专栏📖SpringMVC专栏📖SpringBoot专栏

                           📖Docker专栏📖Reids专栏📖MQ专栏📖SpringCloud专栏     

💖💖如果文章对你有所帮助请留下三连✨✨

1️⃣环境搭建

创建SpringBoot项目,引入相关依赖

 application.properties配置rabbitmq配置信息

spring.rabbitmq.host=192.168.0.109
spring.rabbitmq.port=5670
#如果使用的是/,可以不用配置,因为默认就是/
spring.rabbitmq.virtual-host=/
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest

2️⃣消费者接收消息

@RabbitHandler注解: 当spring扫到该注解,就当成消费者

@RabbitListener注解: 绑定队列Queue与交换机Exchange
durable持久化
autoDelete自动删除
type=ExchangeTypes.DIRECT交换机类型为直连交换机,不写默认直连
key: 就是Routing key

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者1-1收到:"+msg+"");
        //这里写业务逻辑代码
    }
}

3️⃣生产者发送消息

注入RabbitTemplate,调用convertAndSend方法发送消息

参数一:交换机 参数二: Routing key 参数三: 消息

@Component
public class DirectProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(){

        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01","生产者发送的第一条消息");
        //System.out.println("生产者第一条消息发送成功");
    }
}

启动类调用sendMessage发送

@SpringBootApplication
public class App {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
        //调用生产者发送消息
        DirectProducer producer = context.getBean(DirectProducer.class);
        producer.sendMessage();
    }
}

💦多个消费者消费同一个队列

当生产者发送多条消息时,同一个队列的多个消费者去接收消息

生产者发送多条消息

@Component
public class DirectProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(){
        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01","生产者发送的第一条消息");
        System.out.println("生产者第一条消息发送成功");
        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01","生产者发送的第二条消息");
        System.out.println("生产者第二条消息发送成功");
    }
}

同一个队列的多个消费者去接收消息

都是value = "211-DirectQueue-01"的队列

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者1-1收到:"+msg+"");
        //这里写业务逻辑代码
    }
    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者1-2收到:"+msg+"");
        //这里写业务逻辑代码
    }
}

可以理解为快递员送快递,只关注哪一家(队列)签收的快递,而不关注该家哪个成员(消费者)签收的快递,(只是在签收时的消费者是轮循的)

像这种同一队列多个消费者的好处就是保障了高可用性,只要有一个消费者就能保障接收到消息

💦多个消费者消费不同的队列

当生产者发送多条消息时,不同队列的多个消费者去接收消息

生产者发送多条消息

@Component
public class DirectProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(){
        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01","生产者发送的第一条消息");
        System.out.println("生产者第一条消息发送成功");
        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01","生产者发送的第二条消息");
        System.out.println("生产者第二条消息发送成功");
    }
}

不同队列的多个消费者去接收消息

不同队列value = "211-DirectQueue-01"和value = "211-DirectQueue-02"

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者1收到:"+msg+"");
        //这里写业务逻辑代码
    }
    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-02", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者2收到:"+msg+"");
        //这里写业务逻辑代码
    }
}

同样的不是同一家(队列)的成员(消费者),在签收快递时,要签收自己家所有快递(消息)

⛅消费者重试机制/自动应答

application.properties配置

  1. 配置消费者应答模式
  2. 配置rabbitmq重试配置信息
#开启消费者应答模式为 auto自动应答 manual手动应答
spring.rabbitmq.listener.direct.acknowledge-mode = auto
#spring.rabbitmq.listener.simple.acknowledge-mode = auto

#开启消费者自动重试机制,也就是消费者函数只要抛出异常,就会触发重试 false关闭
spring.rabbitmq.listener.simple.retry.enabled=true
#设置重试最大次数
spring.rabbitmq.listener.simple.retry.max-attempts=5
#设置重试时间最大间隔
spring.rabbitmq.listener.simple.retry.max-interval= 8000ms
#设置重试时间间隔
spring.rabbitmq.listener.simple.retry.initial-interval=1000ms
#设置重试时间间隔的倍数
spring.rabbitmq.listener.simple.retry.multiplier=2

当不配置重试机制时,消费者应答出现异常不处理时,就会出现死循环

如下代码段

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process2(Message message){
        String msg = new String(message.getBody());
        System.out.println("消费者1-1收到:"+msg+"");
        //这里写业务逻辑代码
        int i = 1/0//会出现死循环
    }
}

配置后

⛅手动应答模式

配置手动应答,取消重试机制

#开启消费者应答模式为 auto自动应答 manual手动应答
spring.rabbitmq.listener.direct.acknowledge-mode = manual
#开启消费者自动重试机制,也就是消费者函数只要抛出异常,就会触发重试
spring.rabbitmq.listener.simple.retry.enabled=false

手动应答模式

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    //手动应答
    public void process1(Message message, Channel channel) throws IOException {
        String msg = new String(message.getBody());
        System.out.println("消费者1-1收到:"+msg+ DateUtil.format(DateUtil.date(),"HH:ss"));
        //获取应答标签
        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        try {
            int i = 1/0;//会出现死循环
            //手动应答 参数一:消息标识 参数二:true批量应答,false单个应答
            channel.basicAck(deliveryTag,false);
        } catch (Exception ex) {
            //把异常消息插入数据库
            System.out.println("消费者1-1收到:"+msg+"出现异常信息插入数据库");
            System.out.println("异常信息: "+ex.getMessage());
            channel.basicAck(deliveryTag,false);
        }
    }
}

 出现异常,将异常消息插入数据库后,这样保障应答后在队列中不堵,然后再让生产者发送消息

channel.basicAck正常应答

channel.basicCancel取消应答

channel.basicReject拒绝应答

没有重试机制,需要自己去写,所以一般不会使用手动应答

🧭投递业务对象

一般生产的都是领域对象dto

例如

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserRegisterOk {

    private String name;
    private String phone;
}

生产者生产对象

@Component
public class DirectProducer {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(){
        UserRegisterOk userRegisterOk = UserRegisterOk.builder().name("张三").phone("123456").build();
        //要将对象序列化,转成字符串,使用消息转换器MessageConverter
        rabbitTemplate.convertAndSend("211-DirectExchage-01","211-Direct-RoutingKey-01",userRegisterOk);
        System.out.println("生产者生产对象发送成功");
    }
}

消费者接收对象

@Component
public class DirectConsumer {

    @RabbitHandler
    @RabbitListener(bindings =
    @QueueBinding(value  = @Queue(value = "211-DirectQueue-01", durable = "true", autoDelete = "false"),
            exchange = @Exchange(value = "211-DirectExchage-01", type = ExchangeTypes.DIRECT),
            key = "211-Direct-RoutingKey-01"))
    public void process4(UserRegisterOk userRegisterOk){
        System.out.println("消费者收到:"+userRegisterOk.getName()+","+userRegisterOk.getPhone());
    }
}

运行后报错

 配置序列化

@Configuration
public class RabbitConfig {

    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}

💖补充写法

@Component
public class DirectConsumer {
    private static  final  String ENAME = "211-DirectExchage-01";
    private static  final  String QNAME = "211-DirectQueue-01";
    private static  final  String KEY = "211-Direct-RoutingKey-01";
    //定义一个交换机
    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(ENAME, true, false);
    }
    //定义一个队列
    @Bean
    public Queue directQueue(){
        return QueueBuilder.durable(QNAME).build();
    }
    //创建队列和交换机的绑定关系
    @Bean
    public Binding binding(){
        return BindingBuilder.bind(directQueue()).to(directExchange()).with(KEY);
    }

    @RabbitHandler
    @RabbitListener(queues =QNAME)
    public void process4(UserRegisterOk userRegisterOk){
        System.out.println("消费者收到:"+userRegisterOk.getName()+","+userRegisterOk.getPhone());
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot集成MQ(消息队列)可以通过以下步骤进行配置和调用。 1. 导入依赖:在`pom.xml`文件中添加如下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> <version>2.1.4.RELEASE</version> </dependency> ``` 2. 修改配置文件:在`application.properties`或`application.yml`中添加MQ的连接信息,例如: ``` spring.rabbitmq.host=192.168.3.10 spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ ``` 3. 创建配置类:创建一个配置类,例如`RabbitConfig.java`,用于配置消息转换器等相关信息。 ```java @Configuration public class RabbitConfig { @Bean public MessageConverter messageConverter(){ return new Jackson2JsonMessageConverter(); } } ``` 4. 测试:可以进行一些简单的测试来验证配置是否正确,例如创建交换机和队列,并发送/接收消息。 4.1 管理类测试:可以通过`AmqpAdmin`进行交换机和队列的声明。 ```java @Autowired AmqpAdmin amqpAdmin; @Test public void createExchange() { DirectExchange directExchange = new DirectExchange("java.exchange", true, false); amqpAdmin.declareExchange(directExchange); } @Test public void createQueue() { Queue queue = new Queue("java.queue", true, false, false); amqpAdmin.declareQueue(queue); } ``` 4.2 消息发送与接收:可以使用`@RabbitListener`注解来监听指定队列的消息,并编写对应的处理方法。 ```java @RabbitListener(queues = {"java.queue"}) public void getMessage(Message message, OrderItemEntity orderItemEntity){ byte[] body = message.getBody(); System.out.println("接收到消息:" + message); } ``` 5. 拓展:可以通过修改配置文件和配置类来实现消息的可靠投递。 5.1 修改配置文件:可以开启发送端确认和消息抵达队列的确认,并配置手动ack消息。 ```properties # 开启发送端确认 spring.rabbitmq.publisher-confirms=true # 开启发送端消息抵达队列的确认 spring.rabbitmq.publisher-returns=true # 只要抵达队列,以异步发送优先回调我们这个returnConfirm spring.rabbitmq.template.mandatory=true # 手动ack消息 spring.rabbitmq.listener.simple.acknowledge-mode=manual ``` 5.2 修改配置类:可以添加发送消息的回调方法来处理发送消息的确认和返回结果。 这样,就完成了Spring Boot集成MQ的配置和使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [springboot-集成MQ](https://download.csdn.net/download/tao724624173/10657288)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [springboot整合MQ](https://blog.csdn.net/weixin_45865428/article/details/123199733)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沫洺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值