使用RabbitMQ实现异步支付状态通知

在支付系统中,如何确保支付状态的准确传递和处理显得尤为重要。今天,我们将以一个支付流程为例,探讨在引入RabbitMQ前后的实现和优化。

改造前

在引入RabbitMQ之前,我们通常会直接在支付方法中完成所有的操作。这包括查询支付单、判断状态、扣减余额、修改支付单状态以及通知订单服务等。以下是一个典型的实现:

@Override
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
    // 1.查询支付单
    PayOrder po = getById(payOrderFormDTO.getId());
    // 2.判断状态
    if (!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())) {
        // 订单不是未支付,状态异常
        throw new BizIllegalException("交易已支付或关闭!");
    }
    // 3.尝试扣减余额
    userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
    // 4.修改支付单状态
    boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
    if (!success) {
        throw new BizIllegalException("交易已支付或关闭!");
    }
    // 5.修改订单状态
    tradeClient.markOrderPaySuccess(po.getBizOrderNo());
}
代码解读
  1. 查询支付单:通过支付单ID查询对应的支付单信息。
  2. 判断支付状态:检查支付单状态是否为“等待买家付款”,如果状态异常则抛出业务异常。
  3. 扣减用户余额:调用用户服务,尝试扣减用户余额。
  4. 修改支付单状态:如果扣减余额成功,则更新支付单状态为“支付成功”。
  5. 通知订单服务:直接调用订单服务,更新订单状态。
存在的问题
  1. 强耦合:支付服务和订单服务强耦合,修改其中一个模块可能会影响到另一个模块。
  2. 失败处理:如果在某一步骤失败,整个流程需要回滚,复杂度增加。
  3. 可扩展性差:难以扩展其他需要在支付成功后进行处理的业务逻辑。

改造后

为了提高系统的可靠性和可维护性,我们引入RabbitMQ来实现支付状态的异步通知。改造后的代码如下:

@Override
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
    // 1.查询支付单
    PayOrder po = getById(payOrderFormDTO.getId());
    // 2.判断状态
    if (!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())) {
        // 订单不是未支付,状态异常
        throw new BizIllegalException("交易已支付或关闭!");
    }
    // 3.尝试扣减余额
    userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
    // 4.修改支付单状态
    boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
    if (!success) {
        throw new BizIllegalException("交易已支付或关闭!");
    }
    // 5.发送支付成功消息
    try {
        rabbitTemplate.convertAndSend("pay.direct", "pay.success", po.getBizOrderNo());
    } catch (AmqpException e) {
        log.error("发生支付状态通知失败,订单id:{}", po.getBizOrderNo(), e);
    }
}

public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
    return lambdaUpdate()
            .set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
            .set(PayOrder::getPaySuccessTime, successTime)
            .eq(PayOrder::getId, id)
            // 支付状态的乐观锁判断
            .in(PayOrder::getStatus, PayStatus.NOT_COMMIT.getValue(), PayStatus.WAIT_BUYER_PAY.getValue())
            .update();
}
代码解读
  1. 查询支付单:通过支付单ID查询对应的支付单信息。
  2. 判断支付状态:检查支付单状态是否为“等待买家付款”,如果状态异常则抛出业务异常。
  3. 扣减用户余额:调用用户服务,尝试扣减用户余额。
  4. 修改支付单状态:如果扣减余额成功,则更新支付单状态为“支付成功”。
  5. 发送支付成功消息:通过RabbitMQ发送支付成功的消息,通知其他系统或服务支付已完成。
异步通知的实现

改造后的系统中,我们通过RabbitMQ实现了异步消息通知。以下是支付状态监听器的实现:

@Component
@RequiredArgsConstructor
public class PayStatusListener {
    private final IOrderService orderService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "trade.pay.success.queue", durable = "true"),
            exchange = @Exchange(name = "pay.direct"),
            key = "pay.success"
    ))
    public void listenPaySuccess(Long orderId) {
        orderService.markOrderPaySuccess(orderId);
    }
}
监听器解读
  1. 监听队列:绑定支付成功的队列和交换机,并指定路由键。
  2. 处理消息:监听到支付成功的消息后,调用订单服务更新订单状态。

总结

通过引入RabbitMQ,我们实现了支付状态的异步通知,解决了系统强耦合、失败处理复杂、可扩展性差的问题。RabbitMQ不仅提高了系统的可靠性,还使得系统更加易于维护和扩展。

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用RabbitMQ实现异步更新数据库的一般步骤如下: 1.创建RabbitMQ连接和通道。 2.创建一个队列,用于接收需要异步处理的消息。 3.在生产者端生成需要进行异步处理的消息并将其发送到队列中。 4.在消费者端监听队列中的消息,并处理消息。 5.在处理完成后更新数据库。 下面是一个简单的示例代码: ```python import pika import json import time import threading import pymysql # 连接 RabbitMQ connection = pika.BlockingConnection(pika.ConnectionParameters('localhost')) channel = connection.channel() # 创建消息队列 channel.queue_declare(queue='async_database') # 连接 MySQL 数据库 db = pymysql.connect(host='localhost', user='root', password='123456', database='test') cursor = db.cursor() # 定义处理消息的函数 def callback(ch, method, properties, body): data = json.loads(body) print(f"Received message: {data}") # 模拟异步处理过程 time.sleep(5) # 更新数据库 sql = f"UPDATE users SET name='{data['name']}' WHERE id='{data['id']}'" cursor.execute(sql) db.commit() print("Database updated successfully") # 监听消息队列中的消息 def consume(): channel.basic_consume(queue='async_database', on_message_callback=callback, auto_ack=True) channel.start_consuming() # 启动消费者线程 t = threading.Thread(target=consume) t.start() # 发送消息到队列 data = {'id': 1, 'name': 'Alice'} channel.basic_publish(exchange='', routing_key='async_database', body=json.dumps(data)) # 关闭数据库连接和 RabbitMQ 连接 cursor.close() db.close() connection.close() ``` 在上面的示例代码中,我们创建了一个名为 "async_database" 的消息队列,并在消费者端监听该队列。在生产者端,我们生成一条包含需要更新到数据库的数据的消息,并将其发送到队列中。在消费者端接收到消息后,我们模拟了一个异步处理过程,并更新了数据库。 需要注意的是,在使用 RabbitMQ 进行异步处理时,可能会有一些消息未能被及时处理。因此,我们需要在消费者端对这些消息进行重试或者将其记录下来以备后续处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值