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

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

  •  
  •   uiosun · 148 天前 · 1232 次点击
    这是一个创建于 148 天前的主题,其中的信息可能已经有所发展或是发生改变。

    详细提问:

    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  
       148 天前   ❤️ 1
    延迟任务就是有个系统在帮忙轮询

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

    上述内容都可以用 redis 实现

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

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

    ( 10 铜币感谢,小小心意)
    Jooooooooo
        5
    Jooooooooo  
       148 天前   ❤️ 1
    @uiosun 比如 redis 的 list 结构, 你把任务都 append 进去

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

    听起来这个 Y 任务也有一点意思,因为赶路是需要占据时间的吧?
    crystom
        9
    crystom  
       148 天前
    redis 专门搞个消费者取 zset,生产者则用 zset 分数存时间和任务
    akira
        10
    akira  
       148 天前
    dau 1.5w ,每个用户假设每天是 10 个任务,每日总任务数量大约是 15w

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

    再做个定期清理 足够了
    woolong800
        11
    woolong800  
       148 天前
    延迟队列吧,可以用 rabbitmq,或者自己基于 redis 的 sorted set 写一个
    triptipstop
        12
    triptipstop  
       148 天前
    时间轮
    Rocketer
        13
    Rocketer  
       148 天前
    标准做法是死信队列。

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

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

    然後,這個程序可以讀取到之前的安排的任務,判斷是否滿足執行條件,並開始處理。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2915 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 10:58 · PVG 18:58 · LAX 02:58 · JFK 05:58
    ♥ Do have faith in what you're doing.