怎么让一个任务在发起后的第 6 个小时,自动执行?

2021-07-06 20:17:34 +08:00
 uiosun

详细提问:

  1. 场景是游戏,日活跃用户 1.5 万。
  2. 每个用户会发起多个“X 小时后执行 Y 任务”,X 范围 1~72 ; Y 包含且不限于“种地”、“建房”、“赶路”等动作;
  3. 服务器性能有限。

目前已启用的解决办法:

Redis 开启 Key 失效 Event,服务以长连接监听事件推送。

考虑到意外,DB 也构建了一张“任务表”,用于存储任务结束时间戳,玩家每次登录时,检测存在的时间戳数据,如果有,则自动执行并清理时间戳数据( Redis 回调时,也会清理时间戳数据)。

2321 次点击
所在节点    编程
16 条回复
Jooooooooo
2021-07-06 20:26:46 +08:00
延迟任务就是有个系统在帮忙轮询

一个简单的做法是把每一秒当成一个 key, 任务挂在这个 key 上, 然后弄个系统用比如 100ms 的速度去轮询这个 key, 发现上面有任务就用消息触发
qq316107934
2021-07-06 20:38:34 +08:00
把任务按照时间顺序插入一个有序列表中,每隔 N 段时间扫描列表头,发现到了时间就出列读下一个。如果任务很多的话可以把任务按照时间段分块插入

上述内容都可以用 redis 实现

你用发布订阅的话,可能留坑
uiosun
2021-07-06 21:02:29 +08:00
@Jooooooooo 大佬,所有新增任务都挂在对应的 key 的已有 value 后面,用符号分割,每分钟查询对应 60 秒的任务,批量执行应该就可以?!我去试试

@qq316107934 大佬,如果 Redis 宕机了,又没有开持久化,这部分数据是不是就没了?当前情况下,只能靠那个 DB 的方式去救火😂
uiosun
2021-07-06 21:05:35 +08:00
@Jooooooooo @qq316107934
我都试试,哪种更适合就用哪种。实在是感谢两位大佬了。

( 10 铜币感谢,小小心意)
Jooooooooo
2021-07-06 21:07:37 +08:00
@uiosun 比如 redis 的 list 结构, 你把任务都 append 进去

至于稳定性相关的问题, 如果是一个完整的延迟任务系统, 确实是需要用 db 去搞的, 缓存只能是小打小闹的方案
EscYezi
2021-07-06 21:16:03 +08:00
尝试一下延时队列?比如 rabbitmq (用的不多但是有这个功能)
qq316107934
2021-07-06 21:16:31 +08:00
@uiosun #3 可以写日志,出了问题回扫日志;如果不想离线扫,需要加上 DB/Redis 双写,检测到 Redis 宕机数据源就切换到 DB,然后定时 where 开始执行时间戳 < 现在时间 + 扫描时间间隔,效果一样的,但要注意 Redis 和 DB 间的任务状态同步。
CEBBCAT
2021-07-06 21:44:59 +08:00
也没有考虑过延时队列?比如楼上说的 rabbitmq 或者 beanstalk ?

听起来这个 Y 任务也有一点意思,因为赶路是需要占据时间的吧?
crystom
2021-07-06 21:48:05 +08:00
redis 专门搞个消费者取 zset,生产者则用 zset 分数存时间和任务
akira
2021-07-06 22:18:45 +08:00
dau 1.5w ,每个用户假设每天是 10 个任务,每日总任务数量大约是 15w

做个数据表,待记录执行时间,任务编号,执行标记 以及其他必要信息,
然后定时器一直扫就好了。

再做个定期清理 足够了
woolong800
2021-07-06 22:20:07 +08:00
延迟队列吧,可以用 rabbitmq,或者自己基于 redis 的 sorted set 写一个
triptipstop
2021-07-06 23:30:22 +08:00
时间轮
Rocketer
2021-07-07 02:36:12 +08:00
标准做法是死信队列。

具体来说就是,把队列的超时处理方式设为转移至另一个队列(这就是所谓的死信队列),然后任务入队时设置超时(就是需要的延时)。

这样任务需要执行时就会因为超时而自动进入死信队列,你的任务执行程序从死信队列里取就行了。这样做的好处是纯异步,即使需要执行的任务突然来了很多也不怕,有死信队列帮你削峰呢。
aguesuka
2021-07-07 09:15:25 +08:00
HashedWheelTimer 的算法可以在允许少量误差的情况下实现 O(1) 级别的定时任务的插入, 删除, 和弹出.
Alexf4
2021-07-07 10:06:55 +08:00
用 celery 任务的 eta 参数,可以了解下
cctrv
2021-08-20 13:20:07 +08:00
Crontab 以每 1 小時定義執行一個指定腳本(你的程序)

然後,這個程序可以讀取到之前的安排的任務,判斷是否滿足執行條件,並開始處理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/787949

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX