V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
uiosun
V2EX  ›  编程

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

  •  
  •   uiosun · 2021-07-06 20:17:34 +08:00 · 2547 次点击
    这是一个创建于 1234 天前的主题,其中的信息可能已经有所发展或是发生改变。

    详细提问:

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

    目前已启用的解决办法:

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

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

    16 条回复    2021-08-20 13:20:07 +08:00
    Jooooooooo
        1
    Jooooooooo  
       2021-07-06 20:26:46 +08:00   ❤️ 1
    延迟任务就是有个系统在帮忙轮询

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

    上述内容都可以用 redis 实现

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

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

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

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

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

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

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

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

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

    然後,這個程序可以讀取到之前的安排的任務,判斷是否滿足執行條件,並開始處理。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4148 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 36ms · UTC 01:01 · PVG 09:01 · LAX 17:01 · JFK 20:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.