商品下单未支付,如果取消订单?

商品下单未支付,如果取消订单?,第1张

商品下单未支付,如果取消订单? 一、业务场景

当下完订单,一般超过15分钟或者30分钟就需要对未支付的订单进行关闭。

二、解决方案 1.定时任务

通过定时任务隔一段时间扫描一次的数据,超过时间的订单,修改为取消状态

public class OrderCloseTask {

    @Scheduled(cron = "0 0/15 * * * ?")
    public void closeOrder() {

        // .. 关闭订单
    }
}

问题:

    时效性差
    定时任务每15分钟扫一次,如果14:59创建了的单子,那关闭订单的时候就滞后了性能差
    每隔一段时间就要扫描一次表数据,扫描表 *** 作数据的时候,可能会加锁,性能比较低
2.redis pub/sub 发布订阅

下单,在redis创建一个带失效时间的key。通过key失效,监听key失效的事件,去取消订单

package com.xiaokk.house.web.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import java.nio.charset.StandardCharsets;


@Slf4j
package com.xiaokk.house.web.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;

import java.nio.charset.StandardCharsets;


@Slf4j
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
    
    public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

    @Override
    public void onMessage(Message message, byte[] pattern) {
        // 失效的key
        String key = new String(message.getBody(), StandardCharsets.UTF_8);
        log.info("key = {}", key);
        // 消息来自于那个渠道,redis默认有16个库,就有16个渠道
        String channel = new String(message.getChannel(), StandardCharsets.UTF_8);
        log.info("channel = {}", channel);
        // 监听的渠道 "__keyevent@*__:expired 默认兼容所有的数据库
        String patt = new String(pattern, StandardCharsets.UTF_8);
        log.info("patt = {}",patt);

        // 取消订单
    }
}

创建消息监听的一个容器

 @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        return redisMessageListenerContainer;
    }

    @Bean
    public KeyExpiredListener keyExpiredListener(RedisMessageListenerContainer redisMessageListenerContainer) {
        return new KeyExpiredListener(redisMessageListenerContainer);
    }

问题

    发布到所有的订阅者
    一般订单服务会有多个,假如订单服务有3个,那3个都会去取消订单。如果想到加入分布式锁,如果抢到锁的服务挂了。取消订单就是失败了。就没有了可靠性性能问题
    keyevent@*:expired 默认是监听的所有渠道,代表不是订单取消相关的key也会进入到这个监听方法。时效性
    redis 的key 是维护在一个字典表当中,redis是每1秒去检查几次这些key,每次检查中是抽取一定的比例的key,看这些key是否已经失效。如果key比较多,可能就有个别的key总是没有被抽取到,导致key没有及时失效。在抽取的key当中,如果超过一定量的比例都失效,redis会减少去检查的时间,导致检查得更加频繁。而woker本身是单线程,所有有性能损耗。
3.延时消息

创建订单的时候发送一个延时消息。消费的时候判断订单是否还是待支付状态,如果是,则关闭订单

优点

    正常情况下,只有一个订单服务会去消费消息ask机制可以保证,至少消费一次。保证了可靠性

缺点
发送消息可能是有网络延时的,或者mq队列中消息很多,时间到了还没有被消费到。还是会有实时性问题。
解决方案:
通过业务方式去处理,在支付的时候去判断这个订单是否是已经过期,如果已经过期,在取消订单

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5717207.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存