• 请不要在回答技术问题时复制粘贴 AI 生成的内容
zhangwugui
V2EX  ›  程序员

有一个有关订单自动取消的问题,请大家指教一下

  •  
  •   zhangwugui · Nov 20, 2017 · 10946 views
    This topic created in 3276 days ago, the information mentioned may be changed or developed.

    前提:是这样的,一笔订单下单之后,如果不支付,会处于待支付状态。而一般这个订单会有类似 30 分钟不支付自动取消订单的功能。

    实现:这种功能的实现,可以通过定时任务,每隔 30 秒或一分钟扫描一次。但如果集群环境下,可能定时任务会执行重复。当然我们可以通过类似操作系统 PV 操作限制同一时段只能一个定时任务执行。

    目标:我想知道,这种情况,除了定时任务,还有没有其他好点的实现?

    想法:目前在领导的提醒下,想到了用消息来处理,下完单后延时 30 分钟发送消息来处理,不知还有没有其他的处理方式,多谢大家,请多多指教。

    misaka19000
    misaka19000
    Nov 20, 2017
    1
    为什么在分布式环境中就会重复执行呢?
    Doodlister
    Doodlister
    Nov 20, 2017 via Android❤️1
    2
    记录下单时间 。在用户访问订单的时候用超时时间和下单时间比较如果超时就取消订单。
    th00000
    th00000
    Nov 20, 2017
    3
    让用户自己触发会好一点吧
    xman99
    xman99
    Nov 20, 2017
    4
    @Doodlister #2 超过时间还是可以支付的, 除非你在 支付宝或者微信平台关闭这个订单
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    5
    @Doodlister 如果用户不访问,那他的订单列表里有将一直有这个待支付的订单喽。
    gone
    gone
    Nov 20, 2017
    6
    cron 脚本就可以,即便是集群环境下 数据库肯定是唯一把 ,检查当前时间之前的正常状态订单,改掉状态.
    YORYOR
    YORYOR
    Nov 20, 2017
    7
    做好幂等处理,这个跟怎么实现没关系吧
    Felldeadbird
    Felldeadbird
    Nov 20, 2017
    8
    cron 脚本去检查 + 用户访问触发 。
    xinyewdz
    xinyewdz
    Nov 20, 2017
    9
    消息分桶处理。每分钟只消费对应桶的消息即可,不必全表扫描。
    Jakesoft
    Jakesoft
    Nov 20, 2017
    10
    每次生成一个但支付订单时,
    同时向 redis setnx 设定该未支付订单,
    每次查询一个待支付订单时须从 redis 中也查一遍,
    如果 redis 不存在该订单,
    则将该订单的(数据库)状态改为已取消。
    freelee
    freelee
    Nov 20, 2017
    11
    如果是 java,可以用 elastic-job,一个分布式定时任务框架
    gcli
    gcli
    Nov 20, 2017
    12
    推荐延时队列。
    可以 rabbitmq 实现的。AB 两个队列,A 队列设置消息过期时间,A 队列没有消费者,A 队列过期后自动转发到 B 队列,B 队列消费者进行取消队列。
    gcli
    gcli
    Nov 20, 2017
    13
    推荐延时队列。
    可以 rabbitmq 实现的。AB 两个队列,A 队列设置消息过期时间,A 队列没有消费者,A 队列过期后自动转发到 B 队列,B 队列消费者进行取消订单操作。
    gcli
    gcli
    Nov 20, 2017
    14
    yuankui
    yuankui
    Nov 20, 2017
    15
    你当前的方案问题是,机器一挂,消息就得丢,然后你就要扫描数据,修数据.

    标准做法就是楼上说的延时队列, 可以谷歌下.
    如果要自己实现,切记一定要是分布式的.
    paragon
    paragon
    Nov 20, 2017
    16
    @Doodlister 主要应该是退库存操作吧
    openbsd
    openbsd
    Nov 20, 2017
    17
    大并发情况下,定时任务会不会产生过大的系统开销 ?
    用户触发开销会稍微小点吧
    用户 再次? 请求订单列表时 做个核对,时间超过 30 分钟直接自动取消,简单粗暴
    openbsd
    openbsd
    Nov 20, 2017
    18
    @paragon 没付款就减库存 ?不符合某宝的 抢单风格啊......
    shawn7
    shawn7
    Nov 20, 2017 via Android
    19
    DelayQueue?
    picone
    picone
    Nov 20, 2017
    20
    延时队列+1
    就算是分布式 消费者很多个也只有一个拿到
    skyFuture
    skyFuture
    Nov 20, 2017
    21
    我觉得你考虑有点多,任务多次执行?执行一次任务和多次任务的效果目标都是会导致订单的取消掉。有何不可呢?而且有木有算过比例呢?值得让整个流程变的更加复杂呢?
    just1
    just1
    Nov 20, 2017 via Android
    22
    用户触发+定时( 1 小时一次),应该是个不错的方案
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    23
    @skyFuture 订单取消我们会涉及到退积分相关的操作,所以只有执行一次。而退积分又是 RPC 操作,我目前控制不了这个。但这个不是主要问题,我是说对于一段时间内自动取消订单这类问题,有没有更好的实现方案。
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    24
    @openbsd 哈哈哈,反正我们是下单减库存,不是扣款减库存。
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    25
    @YORYOR 是和幂等有点关系,不过我这的主要问题是 一段时间内订单的自动取消问题。
    openbsd
    openbsd
    Nov 20, 2017
    26
    @zhangwugui 内部系统么 ?下单前会核实库存数量不 ?
    有人恶意下个 9999999....的单,会怎样?
    好邪恶的想法
    chocotan
    chocotan
    Nov 20, 2017
    27
    我们用的 tbschedule
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    28
    @yuankui 嗯,延时队列可以考虑使用。下次试一试。
    SuperCowPower
    SuperCowPower
    Nov 20, 2017
    29
    维护一个优先级队列,以超时时间作为优先权,每隔 n 秒扫描一下队头( n 值取决于你们对延迟的敏感程度),只要队头节点大于等于当前时间就把队头节点丢到 worker 里去处理取消订单逻辑,队头节点超时时间小于当前时间,就完成此次扫描。
    如果业务量特别大,队列长度可以做限制,生产节点时如果超长,就把节点持久化从队列删除,消费节点时如果达到阈值,从磁盘读取节点,补充队列。
    Jealee
    Jealee
    Nov 20, 2017
    30
    我写过这种,利用的是 redis 发布订阅(pub/sub)
    weer0026
    weer0026
    Nov 20, 2017
    31
    刚做完这个功能,延时队列+10086,分布式情况下维护一个公共队列。
    owenliang
    owenliang
    Nov 20, 2017
    32
    脚本最靠谱,状态机,乐观锁什么的就不提了。
    MiguelValentine
    MiguelValentine
    Nov 20, 2017
    33
    redis expire - -什么都不用操心
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    34
    @MiguelValentine 放到 redis 缓存里?每看太懂呢。

    @owenliang 哈哈哈,脚本也是一种方式。

    @weer0026 嗯,延时队列我也准备试试。

    @Jealee 这是用 redis 做消息队列么
    owenliang
    owenliang
    Nov 20, 2017
    35
    有兴趣加我 Q 聊:120848369
    Jealee
    Jealee
    Nov 20, 2017
    36
    @zhangwugui reids 2.8 有一种 键空间通知的机制 Keyspace Notifications, 允许客户端去订阅一些 key 的事件,其中就有 key 过期的事件,我们可以把 key 名称设置为 task 的 id 等标识(这种方式 value 的值无法取到,所以只用 key 来识别任务),expire 设置为计划要执行的时间,然后开启一个客户端来订阅消息过期事件,然后处理 task。(网上复制的,但大致思路就是这样)
    stargazer242
    stargazer242
    Nov 20, 2017
    37
    扫描
    SlipStupig
    SlipStupig
    Nov 20, 2017
    38
    @Jealee 这种方法有个不好的地方,就是需要自定义一些 callback 否则涉及一些库存更新,无法直接完成
    SlipStupig
    SlipStupig
    Nov 20, 2017
    39
    @Jealee 还有一个坏处就一旦 hang out 后,可能导致大量消息丢失,跟踪都不好跟踪
    Jealee
    Jealee
    Nov 20, 2017
    40
    @SlipStupig 所以可以再做相对应的一些优化,比如服务异常处理之类的。
    BBCCBB
    BBCCBB
    Nov 20, 2017
    41
    消息队列里的延时队列, 可靠+时延低
    BBCCBB
    BBCCBB
    Nov 20, 2017
    42
    用户自己触发的这种并不能即使恢复库存.
    BBCCBB
    BBCCBB
    Nov 20, 2017
    43
    所以还是推荐延时队列, 只要你的代码没问题, 就能保证被消费掉.
    waczx
    waczx
    Nov 20, 2017
    44
    rabbitmq 有消息延迟处理的机制。
    把订单信息写进一个 queue,并指定消息的过期时间,到期后,rabbitmq 会把这个消息交给另一个 queue 来处理。
    很简单,在 admin 后台配置就可以。
    airyland
    airyland
    Nov 20, 2017
    45
    redis 定时任务 + 补偿任务:扫描过去 n 分钟内订单进行处理,更新时注意检查状态并且加 lock。
    willvvvvv
    willvvvvv
    Nov 20, 2017
    46
    定时任务去扫,操作前加 REDIS 锁
    SlipStupig
    SlipStupig
    Nov 20, 2017
    47
    @MiguelValentine 你这样订单到期了就没有了,但是相关的库存,还有用户历史订单也没有了,你这样还不如直接删除这个用户,然后呢,他到时候账号登陆上去,他会自己重新注册一个的新的账户,以后他们再也不敢随便下单了 23333

    @Doodlister 你这个复杂度有点高了,一个个对比,如果订单量比较大的话,性能下降会很厉害的

    @Jealee 本身 redis 就没有 ACK 机制,你也无法保证你的 producer 不会崩溃,一旦崩溃你如何保证消息不丢失呢,如果有 rdb 和 aof 开启的情况下,我曾经加载过 100g RBD 文件,8 core 32g 内存,差不多要 40 秒才能全部载入完成这个对一些电商场景来说是致命了,如果是 cluster 模式还好一点,但是也会出现短时间的一个数据不一致情况,特殊情况下可能并不能接受这种情况
    zhangwugui
    zhangwugui
    OP
    Nov 20, 2017
    48
    @SlipStupig 感谢建议。想了想,准备用延时队列试试看看。
    guyskk0x0
    guyskk0x0
    Nov 20, 2017 via Android
    49
    起个独立进程(1 个生产者),每秒查一次超时订单
    select * from order where now() > deadline
    ,然后扔(分布式)队列里,由多个消费者进程去关闭订单。
    dawncold
    dawncold
    Nov 20, 2017
    50
    你领导说的那个方法就挺好
    ioth
    ioth
    Nov 21, 2017
    51
    没区别,支付宝的订单状态你去参考一下,有时候要学习马家军
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Privacy   ·   Solana   ·   2184 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 136ms · UTC 14:28 · PVG 22:28 · LAX 06:28 · JFK 09:28
    ♥ Do have faith in what you're doing.