MQ消息丢失和消息重复的解决方案和实战

场景

假设支付宝转账1000元到余额宝, 通过RabbitMQ对转账过程进行解耦,

支付宝将转账的消息投递到RabbitMQ, 余额宝通过监听RibbitMQ的消息队列获得消息, 然后通过应答队列告诉支付宝消息已经消费

遇到的问题

1. 当余额宝获取到消息之后, 可能转账失败, 消息队列不会关心余额宝是否操作成功, 这就是消息丢失的问题

2. 如果余额宝成功转账, 但响应队列迟迟没有将消费成功的消息告诉支付宝, 导致支付宝重复发送消息, 这就是消息重复发送的问题.

解决方案

1. 第一种是引入ZK, 顺序消费

2. 本地消息表

在支付宝端和余额宝端同时建立消息表

1. 当在支付宝端扣款成功的同时, 在消息表中建立一条记录, 状态标识为unconfirm, 将消息投递到消息队列

2. 余额宝从消息队列中获取消息后, 在余额宝中扣款成功后, 同时在消息表中建立一条消息, 状态标识为confirmed.

3. 余额宝将响应消息投递到响应队列, 支付宝获得响应后, 查询余额宝的消息表, 如果其中没有消费记录, 则插入新的消息。如果查询有消费的消息, 就停止插入, 并返回已经消费的消息。这样可以避免消息重复消费的问题。

4. 支付宝端会有一个定时任务, 相隔一段时间就从消息表中将unconfirm的消息拉取并重新发送, 这样可以避免消息丢失的问题

代码

数据库脚本

DROP DATABASE IF EXISTS `rabbit_taobao_consumer`;
CREATE DATABASE IF NOT EXISTS `rabbit_taobao_consumer` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `rabbit_taobao_consumer`;



DROP TABLE IF EXISTS `tb_account`;
CREATE TABLE IF NOT EXISTS `tb_account` (
  `user_id` varchar(10) NOT NULL,
  `amount` int(11) NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


INSERT INTO `tb_account` (`user_id`, `amount`, `update_time`) VALUES
	('SAM0000001', 0, '2019-09-22 14:30:49'),
	('SAM0000002', 0, '2019-09-22 14:02:59'),
	('SAM0000003', 0, '2019-09-22 14:03:09');

DROP TABLE IF EXISTS `tb_message`;
CREATE TABLE IF NOT EXISTS `tb_message` (
  `message_id` varchar(100) NOT NULL,
  `user_id` varchar(10) NOT NULL,
  `amount` int(11) NOT NULL,
  `state` varchar(10) NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


DROP DATABASE IF EXISTS `rabbit_taobao_provider`;
CREATE DATABASE IF NOT EXISTS `rabbit_taobao_provider` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
USE `rabbit_taobao_provider`;


DROP TABLE IF EXISTS `tb_account`;
CREATE TABLE IF NOT EXISTS `tb_account` (
  `user_id` varchar(10) NOT NULL,
  `amount` int(11) NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


INSERT INTO `tb_account` (`user_id`, `amount`, `update_time`) VALUES
	('SAM0000001', 64000, '2019-09-22 14:30:48'),
	('SAM0000002', 80000, '2019-09-21 18:10:44'),
	('SAM0000003', 70000, '2019-09-21 18:10:57');


DROP TABLE IF EXISTS `tb_message`;
CREATE TABLE IF NOT EXISTS `tb_message` (
  `message_id` varchar(100) NOT NULL,
  `user_id` varchar(10) NOT NULL,
  `amount` int(11) NOT NULL,
  `state` varchar(10) NOT NULL,
  `update_time` datetime NOT NULL,
  PRIMARY KEY (`message_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

建立alipay-server工程, 下面是pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.1.8.RELEASE</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
   <groupId>com.teddy</groupId>
   <artifactId>alipay-server</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <name>alipay-server</name>
   <description>Demo project for Spring Boot</description>

   <properties>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <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>

      <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>druid-spring-boot-starter</artifactId>
         <version>1.1.10</version>
      </dependency>
      <dependency>
         <groupId>tk.mybatis</groupId>
         <artifactId>mapper-spring-boot-starter</artifactId>
         <version>2.0.2</version>
      </dependency>
      <dependency>
         <groupId>mysql</groupId>
         <artifactId>mysql-connector-java</artifactId>
         <scope>runtime</scope>
      </dependency>

      <dependency>
         <groupId>org.springframework.boot</grou
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值