关于 Redis 的 keyspace notification(密钥空间通知)这个东西和环形队列

我决定收尾呼应先来说一下废墟乐队的新专辑《正大光明》。哈哈哈哈

首先是需求,其实我们想做的就是一个定时发系统消息的任务。这个时间节点是后台进行设置的。然而这个定时任务是需要精确到秒的。所以如果用 Linux 的 crontab 就不是很合适。所以就想别的方法。

然后就看到了网上说的 keyspace notification(密钥空间通知)这个东西。但我告诉你,这是其实是不行的。不哄你,

这里 有相关介绍,并且该功能是自** 2.8.0 *版本之后可用的功能。如果使用该功能需要到 redis 的配置中 配置 notify-keyspace-events 的参数为 “Ex” 或者 “Kx”。x 代表了过期事件 *大概就是一种你的 redis 的 key 发生变化的时候就会触发这个功能,并且通知你。比如你的 key 过期了,删除了之类的。

他会发送两种类型:

  • K键空间通知,所有通知以 __keyspace@ __ 为前缀的

  • E键事件通知,所有通知以 __keyevent@ __ 为前缀的

我们本来的思路是后台每添加一个定时任务的时候,就直接在 redis 里存一个具有时效性的key (SETEX、EXPIRE),当该 key 失效的时候就会触发 K 或者 E 的通知,收到通知后我们就做发消息处理。为了避免和其他的时效 key 有混淆,redis 的密钥空间通知是提供正则匹配的。所以美滋滋的实现了代码:

/**
 * Redis处理类
 */
class RedisInstance {

    private $redis;

    public function __construct($host = '127.0.0.1', $port = 6379) {
        $this->redis = new Redis();
        $this->redis->connect($host, $port);
    }

    public function expire($key = null, $time = 0) {
        return $this->redis->expire($key, $time);
    }

    public function psubscribe($patterns = array(), $callback) {
        $this->redis->psubscribe($patterns, $callback);
    }

    public function setOption() {
        $this->redis->setOption(\Redis::OPT_READ_TIMEOUT,-1);
    }
}

这里没有写具体的消息处理,只是收到通知并且把 key 打印出来!

<?php
/**
 * 处理BI后台消息定时发送
 * User: 郭贰小姐
 * Date: 2017/4/13
 * Time: 下午9:47
 */
require_once '/data/momoma.com/redis/RedisInstance.php';
$redis = new \RedisInstance();
// 解决Redis客户端订阅时候超时情况
$redis->setOption();
$redis->psubscribe(array('__keyspace@0*gm_cp*'), 'psCallback');
// 回调函数,这里写处理逻辑
function psCallback($redis, $pattern, $chan, $msg)
{
    echo "Pattern: $pattern\n";
    echo "Channel: $chan\n";
    echo "Payload: $msg\n\n";
}

结果,无论是用 Ex 还是用 Kx 都不是很好使,有时候可以收到通知,有时候很久都收不到通知。

那么 why? 为什么?我觉得大概的原因是:

首先 官方 最后一段关于这个 “过期活动的时间” 的说法,我理解的就是,当某个 key 过期了的时候,实际上他并没有马上删除,所以并不会马上去通知,什么情况会下触发过期并且通知:

  1. 当密钥被命令访问并被发现已过期时。
  2. Redis会随机扫带有过期时间的 key,当扫到并发现已过期时间。

所以官方讲:如果没有命令不断地定位密钥,并且有许多与TTL相关联的密钥,则在关键生存时间到零之间的时间和生成过期事件的时间之间(就是你设置了某个 key 失效时间【比如60秒】和该 key 实际失效并且触发通知事件的时间 【可能70秒】)可能存在显着的延迟。

其次 还有什么原因我就不得而知了...哈哈哈

既然不行

那只能再想别的办法,后来参考了 1分钟实现“延迟消息”功能 做一个环形队列,大概原理就是:

环形队列

做一个3600(其实我做了60)的环形队列,当你有一个任务是59秒后执行,你就可以将这个任务存到第59个格里,然后圈数存为0。当你有一个任务需要61秒后执行,那你可以将这个任务存到第一个格里,但是圈数要存为1。

然后有一个定时器,每秒钟都会沿着这个圈中的小格无线循环下去。当然这个定时器小人跳到某一格,他会把该格子中所有圈数为0的数据都处理掉,圈数不为0的,可能需要下一圈或者下下很多圈才需要处理。

这就是原理,如果你没太理解可以去看下原文,如果你看懂了是不是觉得,嗨,确实是这么回事。这TM有点意思哈...然而并没有什么卵用,代码我还是不会写。

这个环形队列要怎么实现呢?我想了下可以用 redis 的哈希来实现:

//添加任务 hash
$value = [0=>['a任务','b任务'],1=>['c任务'],2=>['d任务']];
$value = json_encode($value);
$redis->hSet('cycle','second_1',$value);

$value = [0=>['e任务','f任务'],1=>['g任务'],2=>['h任务']];
$value = json_encode($value);
$redis->hSet('cycle','second_2',$value);

然后是小人跳格子的环形队列:

/**
* 小人跳格子的环形队列啊
* User: 郭贰小姐
* Date: 2017/4/16
* Time: 下午10:38
*/
require_once '/data/momoma.com/redis/RedisInstance.php';
$redis = new \RedisInstance();

// 执行任务的函数
function task($task){
   //if(!empty($task)){
   echo "----开始一次执行任务----\n";
   foreach ($task as $val){
       echo "正在执行 $val\n";
   }
}

while (true){
   for ($i=1;$i<=60;$i++){
       // 存储当前指针的位置
       $redis->set('current',$i);
       $val = $redis->hGet('cycle','second_'.$i);
       $val = json_decode($val);

       if($val){
           // 执行当前要执行的任务
           task($val[0]);
           array_shift($val);
           // 把剩余的任务重新写到redis
           $val = json_encode($val);
           $redis->hSet('cycle','second_'.$i,$val);
       } else {
           echo "$i 当前时间没有任务可执行\n";
       }
       sleep(1);
   }
}

额好吧,我这里用的 sleep(1),你可以用 swoole 来做定时器。

但是其实这个方案我们最终并没有用,就算用 swoole 来做定时器,配合 supervisor 来实现进程管理。我觉得还差点什么呐...

最后,听一首,不,是一张专辑。来自废墟乐队的2017新专辑,从大二开始听周云山的废墟乐队。说实话,这张新专辑也是等了好久的,终于出了,满心欢喜。美滋滋。

美滋滋

《正大光明》 - 来自废墟乐队

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,376评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,126评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,966评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,432评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,519评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,792评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,933评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,701评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,143评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,488评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,626评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,292评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,896评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,324评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,494评论 2 348

推荐阅读更多精彩内容

  • 1.1 资料 ,最好的入门小册子,可以先于一切文档之前看,免费。 作者Antirez的博客,Antirez维护的R...
    JefferyLcm阅读 17,036评论 1 51
  • 分布式缓存技术PK:选择Redis还是Memcached? 经平台同意授权转载 作者:田京昆(腾讯后台研发工程师)...
    meng_philip123阅读 68,920评论 7 60
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,629评论 18 139
  • 相恋的小情侣几乎都一样,大多数通常都是要经历新鲜期、热恋期、降温期和冷淡期这四个恋爱阶段。前两个阶段可谓是充满了甜...
    Ketno花间阅读 1,502评论 5 2
  • 无奈的我们想的多做的少害怕却又不珍惜
    化沙阅读 75评论 0 1