RabbitMQ可靠生产和推送确认

1、实施可靠生产和推送确认的必要

假设有订单系统A和派单系统B,A和B系统都有自己的单独模块、数据库。
小明在A下单后,A逻辑处理完后,保存相关数据到数据库,并发送消息给B系统通知其派单,B系统也保存相关数据到数据库。
一般都会在A和B中设置事务,但是因为某种原因,B系统延迟,A系统迟迟得不到B系统处理完成的反馈就当失败处理进行事务回滚,所以此时A系统并没有存储相关订单信息到数据库。而B系统尽管延迟但最后还是执行成功了,此处B系统存储成功了。就造成了两边数据的不一致。
利用rabbitmq的一些功能可以避免大部分此类事件

2、具体实现

  • 生产者建立消息冗余表,每发送一条数据到MQ都存一条道冗余表
  • 设置推送确认,确认返回后将冗余表状态变更
  • 设置定时器,重新发送冗余表中发送失败消息,重发次数大于2,说明消息异常需要进行人工确认
    在这里插入图片描述
    在这里插入图片描述

3、实现目标

生产者发送消息到MQ,并存一份到冗余表。MQ确认接收返回,修改冗余表状态

创建生产者工程

在这里插入图片描述

依赖

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

application.yml配置

server:
  port: 8082

spring:
  rabbitmq:
    host: 110.2=14.218.10
    port: 5672
    username: admin
    password: admin
    virtual-host: /
    # 发布确认属性配置 correlated: 发布消息成功到交换器后会触发回调方法
    publisher-confirm-type: correlated

Config配置

package com.example.service;

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

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

@Configuration
public class Config {

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange("order_exchange",true,false);
    }

    @Bean
    public Queue orderQueue(){
        return new Queue("order.queue",true);
    }

    @Bean
    public Binding orderBinding(){
        return BindingBuilder.bind(orderQueue()).to(directExchange()).with("order");
    }
}

OrderService

package com.example.service;

import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class OrderService {
    @Autowired
    private RabbitTemplate rabbitTemplate;


    // PostConstruct 很多人以为是spring提供的,其实是java自己的注解
    // 被PostConstruct修饰的方法会在服务器加载servlet的时候运行,并且只会被服务器执行一次
    // PostConstruct在构造函数之后执行,init()方法之前执行
    // 简单来说就是在下面send方法调用rabbitTemplate之前,将rabbitTemplate的确认方法重载好
    @PostConstruct
    public void regCallback(){
        // 消息发送成功后,给予生产者的消息回执,来确保生产者的可靠性
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                System.out.println("cause:------------>"+cause);

                String orderId = correlationData.getId();
                // 如果ack为true代表消息已经收到
                if (!ack){
                    // 这里需要其他的方式进行失败后的处理逻辑
                    System.out.println("MQ队列应答失败,orderId是:"+orderId);
                    return;
                }

                // 消息收到,则更改冗余表中状态
                try {
                    String sql = "update xxx_table set status ='1' where order_id = 'xxxxxx'";
//                    int count = jdbcTemplet.update(sql,orderId);
//                    if (count == 1){
                    // 这边没连接数据库,就模拟已经执行update操作后
                        System.out.println("本地消息状态修改成功,消息成功投递到消息队列中...");
//                    }
                }catch (Exception e){
                    System.out.println("本地消息状态修改失败,出现异常:"+e.getMessage());
                }
            }
        });
    }

    public void sendMsg(String msg){
        System.out.println("开始发送订单信息到MQ");
        rabbitTemplate.convertAndSend("order_exchange","order",msg,new CorrelationData("2021922"));
    }


    public void saveOrder(){
        // ...这边执行订单的处理流程
        System.out.println("创建保存订单信息");
        // 保存订单信息到本地冗余表
        saveLocalOrder();
    }

    public void saveLocalOrder(){
        // ...这边执行订单信息的冗余保存
        System.out.println("保存订单信息冗余表");
    }
}

测试类

package com.example.rabbitmqdirectproducer;

import com.example.service.OrderService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class RabbitmqDirectProducerApplicationTests {

    @Autowired
    private OrderService orderService;

    @Test
    void contextLoads() {
        // 下单-保存订单
        orderService.saveOrder();

        // 发送订单信息到MQ,让派单系统接收订单信息实行派单
        orderService.sendMsg("订单111201210");
    }

}

结果

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值