订单超时自动关闭的实现方案总结

本文探讨了业务场景中实现定时任务的不同方法,包括定时轮询、被动取消和延时消息等,并重点介绍了延时消息的设计与实现过程。通过环形队列与任务集合的数据结构,实现了高效且精确到秒的延时消息机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

统一来说,业务有“在一段时间之后,完成一个工作任务”的需求。
实现这种定时任务有哪些方法呢,来总结一下想到的方法。
一、定时轮询
这是一个比较直接的思路,启动一个计划任务,每隔一定时间处理一次,这种处理方式只是适用比较小而简单的项目。
假设订单表的结构为:t_order(oid, finish_time, stars, status, …),更具体的,定时任务每隔一个小时会这么做一次:
select oid from t_order where finish_time > 30minite and status=0;
update t_order set stars=5 and status=1 where oid in[…];
如果数据量很大,需要分页查询,分页update,这将会是一个for循环。

定时轮询的不足:
1、时效性差,会有一定的延迟,这个延迟时间最大就是每隔一定时间的大小,如果你设置每分钟定时轮询一次,那么理论上订单取消时间的最大误差就有一分钟,当然也可能更大,比如一分钟之内有大量数据,但是一分钟没处理完,那么下一分钟的就会顺延。
2、效率低。

二、被动取消
被动取消的方式很简单:只有当用户查询订单信息时,我们再判断该订单是否超时,如果超时再进行超时逻辑的处理。
但是这种方式依赖于用户的查询操作触发,这也就是说如果用户不进行查询订单的操作,该订单就永远不会被取消。
不足:
1、会产生额外影响
比如统计,订单数量等产生影响
2、影响用户体验
用户打开订单列表可能要处理大量数据,影响显示的实时性。
三、延时消息
延时消息设计与实现
高效延时消息,包含两个重要的数据结构:
(1)环形队列,例如可以创建一个包含3600个slot的环形队列(本质是个数组)
(2)任务集合,环上每一个slot是一个Set
 
  
 
同时,启动一个timer,这个timer每隔1s,在上述环形队列中移动一格,有一个Current Index指针来标识正在检测的slot。
 
Task结构中有两个很重要的属性:
(1)Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务
(2)Task-Function:需要执行的任务指针
 

 
  

假设当前Current Index指向第一格,当有延时消息到达之后,例如希望3610秒之后,触发一个延时消息任务,只需:
(1)计算这个Task应该放在哪一个slot,现在指向1,3610秒之后,应该是第11格,所以这个Task应该放在第11个slot的Set
  
   中
(2)计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1
 
Current Index不停的移动,每秒移动到一个新slot,这个slot中对应的Set
   
    ,每个Task看Cycle-Num是不是0:
(1)如果不是0,说明还需要多移动几圈,将Cycle-Num减1
(2)如果是0,说明马上要执行这个Task了,取出Task-Funciton执行(可以用单独的线程来执行Task),并把这个Task从Set
    
     中删除
 
使用了“延时消息”方案之后,“订单48小时后关闭评价”的需求,只需将在订单关闭时,触发一个48小时之后的延时消息即可:
(1)无需再轮询全部订单,效率高
(2)一个订单,任务只执行一次
(3)时效性好,精确到秒(控制timer移动频率可以控制精度)
 
四、总结
环形队列是一个实现“延时消息”的好方法,开源的MQ好像都不支持延迟消息,不妨自己实现一个简易的“延时消息队列”,能解决很多业务问题,并减少很多低效扫库的cron任务。

另外,关于MQ的可达性、幂等性未来撰文另述。
 






     

查看原文: http://www.architecy.com/archives/373
<think>好的,我现在要处理用户关于Yansongda\Pay\Pay设置订单超时的问题。首先,我需要回忆一下这个库的相关知识。Yansongda Pay是一个PHP的支付SDK,支持支付宝、微信等支付方式。用户的问题是关于如何设置订单超时时间,这可能涉及到不同支付平台的配置差异。 首先,我应该确定订单超时在支付宝和微信中的不同参数名称和格式。支付宝使用的是timeout_express,而微信支付是time_expire。这两个参数的时间单位可能不同,支付宝是分钟,微信是具体的日期时间格式。我需要确认这些细节是否正确,避免给出错误的信息。 接下来,用户可能需要代码示例。我需要确保示例中的参数名称正确,并且格式符合各自平台的要求。比如,支付宝设置的是分钟数,而微信需要传递一个符合ISO 8601标准的字符串,例如+30分钟这样的格式。同时,要提醒用户注意时区问题,微信支付的时间是基于服务器时区还是其他时区,这点可能需要进一步确认,但根据经验,通常建议使用UTC时间。 另外,用户可能想知道如果不设置超时时间会怎么样。支付宝和微信都有默认的超时时间,但具体时长可能因平台而异,需要指出这一点,并建议用户根据业务需求明确设置以避免意外情况。 还要注意提醒用户在处理超时订单时的逻辑,比如使用定时任务检查未支付的订单,并在超时关闭订单。这部分虽然不是直接设置的问题,但作为最佳实践,对用户来说可能有帮助。 我需要检查是否有常见的错误点,比如时间格式错误、单位混淆等,并在回答中特别指出,帮助用户避免这些问题。例如,微信的time_expire必须精确到分钟,且不能超过一定的时间范围,比如30分钟到两年,这些限制可能需要提及。 最后,确保回答结构清晰,分点说明支付宝和微信的不同配置方式,并提供代码示例和注意事项,这样用户能够一步步跟着操作。同时使用中文,避免专业术语错误,保持语言简洁明了。</think>关于 Yansongda\Pay\Pay 设置订单超时时间的配置,以下是分步说明: ### 一、核心配置方式 在支付宝/微信支付场景中,超时时间通过 `timeout_express` 或 `time_expire` 参数设置: 1. **支付宝** ```php $order = [ 'out_trade_no' => uniqid(), 'total_amount' => '0.01', 'subject' => '测试订单', 'timeout_express' => '30m' // 支持分钟单位(m) ]; ``` 2. **微信支付** ```php $order = [ 'out_trade_no' => uniqid(), 'total_fee' => '1', 'body' => '测试订单', 'time_expire' => date("YmdHis", time() + 1800) // 必须使用时间戳格式(精确到秒) ]; ``` ### 二、底层原理 - 支付宝:`timeout_express` 参数通过 `$unitConverter->durationToMin()` 转换为分钟 - 微信:`time_expire` 要求符合 `yyyyMMddHHmmss` 格式,SDK 会自动处理时间转换 ### 三、注意事项 1. **格式规范** - 支付宝:支持 `数字+m` 格式(如 `30m`/`120m`) - 微信:必须使用绝对时间(建议用 `strtotime('+30 minutes')` 生成) 2. **超时边界值** - 支付宝:最小5分钟,最大15天(21600分钟) - 微信:最小5分钟,最大2小时(实际业务建议不超过2小时) 3. **异常处理** ```php try { $response = Pay::wechat()->scan($order); } catch (\Yansongda\Pay\Exception\InvalidParamsException $e) { // 捕获参数错误(包含时间格式错误) } ``` ### 四、最佳实践 1. **统一时间处理器** ```php function getExpireTime(int $minutes) { return date("YmdHis", time() + $minutes * 60); } // 微信使用 'time_expire' => getExpireTime(30) ``` 2. **超时订单补偿方案** ```php // 定时任务示例 $expiredOrders = Order::where('status', 'pending') ->where('created_at', '<', now()->subMinutes(30)) ->get(); foreach ($expiredOrders as $order) { Pay::close($order->trade_no); $order->update(['status' => 'closed']); } ``` ### 五、调试技巧 1. 使用 `Pay::getLogger()` 查看实际发送的请求参数 2. 支付宝沙箱环境下超时时间会加速生效(实际约5分钟) 建议根据实际业务场景设置合适的超时时间,电商类建议30分钟,虚拟商品建议15分钟,注意与前端倒计时保持同步。
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值