• [Redis]处理定时任务的2种思路


    http://blog.csdn.net/orangleliu/article/details/52038092

    Redis完成类似 at 命令的功能,例如订单24小时后没有支付自动关闭,定时发邮件,主要说下任务生成之后怎么触发消费。

    使用 有序集合

    思路: 使用sorted Sets的自动排序, key 为任务id,score 为任务计划执行的时间戳,这样任务在加入sets的时候已经按时间排序,这样每隔1s(或者其他间隔)去取出sets顶部的数据,小于当前时间的可以通过pop取出来然后去执行。

    redis模拟
    127.0.0.1:6379> zadd cron 10001 task1
    (integer) 1
    127.0.0.1:6379> zadd cron 9001 task2
    (integer) 1
    127.0.0.1:6379> zadd cron 29001 task3
    (integer) 1
    127.0.0.1:6379> ZRANGE cron 0 -1 withscores
    1) "task2"
    2) "9001"
    3) "task1"
    4) "10001"
    5) "task3"
    6) "29001"
    
    假设当前的时间戳是 15000
    
    127.0.0.1:6379> ZRANGEBYSCORE cron -inf 15000
    1) "task2"
    2) "task1"
    127.0.0.1:6379> ZREM cron task2
    (integer) 1
    127.0.0.1:6379> ZREM cron task1
    (integer) 1
    127.0.0.1:6379> ZRANGE cron 0 -1 withscores
    1) "task3"
    2) "29001"
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    上面的测试直接把小于当前时间戳的所有任务都做了一遍,会有些bug,例如找个定时监测程序挂了2天, 对于某些任务可能有效期只有那么10分钟,重新启动定时监测程序,就会把过期任务也做了一遍, 那么我们选取任务的时候范围要更精确一些。

    如果当前时间戳是 29100 可以取到 task3
    127.0.0.1:6379> ZRANGEBYSCORE cron 28500 29100
    1) "task3"
    
    如果当前时间戳是 30600 就无法取到 task3, 注意对过期任务的清理
    127.0.0.1:6379> ZRANGEBYSCORE cron 30000 30600
    (empty list or set)
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    利用键过期通知

    思路: reids 2.8 有一种 键空间通知的机制 Keyspace Notifications (强烈推荐看一遍), 允许客户端去订阅一些key的事件,其中就有 key过期的事件,我们可以把 key名称设置为 task的id等标识(这种方式value的值无法取到,所以只用key来识别任务),expire设置为计划要执行的时间,然后开启一个客户端来订阅消息过期事件,然后处理task。

    需要更改redis配置,注意版本要在2.8.0以上, 如果没有这个key 请添加上,如果有请更改为下面这样

    notify-keyspace-events Ex
    • 1
    • 1

    重启redis,第一个窗口, 开启订阅

    liuzhizhi@lzz-rmbp|redis_test # redis-cli --csv psubscribe '__keyevent@0__:expired'
    Reading messages... (press Ctrl-C to quit)
    "psubscribe","__keyevent@0__:expired",1
    "pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task1"
    "pmessage","__keyevent@0__:expired","__keyevent@0__:expired","task2"
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    第二个窗口 设置key

    127.0.0.1:6379> set task1 xx
    OK
    127.0.0.1:6379> EXPIRE task1 5
    (integer) 1
    127.0.0.1:6379> set task2 xx
    OK
    127.0.0.1:6379> EXPIREAT task2 1469525560
    (integer) 1
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    当key过期的时候就看到第一个窗口的通知了,订阅的key __keyevent@<db>__:expired 这个格式是固定的,db代表的是数据库的编号,由于订阅开启之后这个库的所有key过期时间都会被推送过来,所以最好单独使用一个数据库来进行隔离。

    小结

    以上就是使用redis来处理定时任务的两种思路,常用的编程语言应该都比较容易实现。

  • 相关阅读:
    ① ts基础
    ⑦ 原型和原型链 作用域链
    ④ 小程序使用分包
    功能⑦ 小程序整合高德地图定位
    effective OC2.0 52阅读笔记(三 接口与API设计)
    effective OC2.0 52阅读笔记(二 对象、消息、运行期)
    effective OC2.0 52阅读笔记(一 熟悉Objective-C)
    perl的Getopt::Long和pod::usage ?
    安装你自己的perl modules
    Perl 之 use(), require(), do(), %INC and @INC
  • 原文地址:https://www.cnblogs.com/sanwenyu/p/6690048.html
Copyright © 2020-2023  润新知