redis modules扩展模块的开发使用

上下文:

      Redis 4.0 的RC终于出来了, 看了redis官方及antirez的博客都有提到4.0的各种新功能,对我来说是相当有吸引力的 。 

估计有人好奇现在不是才redis 3 .x么, 怎么直接干到redis 4.0 大版本了,据antirez说, redis 3.x 主要是集群功能, redis 4.0 是个全新的大跃进 ( 当然我自己加工了下 ,哈哈)  。 

      另外蛋疼的找了台大内存的服务器,使用以前的golang redis压力测试脚本,  综合测了下redis各数据结构的读写性能,  结果跟现在的redis 3.4 没啥差异的,看来4.0 真的就是功能上的功能。

该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.   

http://xiaorui.cc/?p=4047


我们这里可以简单说下 redis 4.0 的新功能:   

1.   自定义扩展redis的模块,  当然相比 redis lua脚本来说,还是有些学习成本的…

2.   psync2 他的好处在于 redis 主从切换后,不需要重新进行 重新fullsync同步,只需要部分同步,有点类似binlog那种.  具体实现还没看,阿里自己是通过timestamp时间戳实现的.

3.   memory 指令可以估算内容大小

4.   非数据命令的异步执行,我们知道redis处理请求是单线程的,执行数据偏大的flush及del会阻塞的,redis 4.0 加了异步操作的功能…. 很好奇他的实现.

等等 …


    我自己最关注Redis 4.0 新版加入了自定义模块开发,其实以前在unstable的版本时 redis 就支持社区的自定义模块了, 只是一直游离在外,很多人不关注不知道而已。  Redis 开放自定义模块是大势所趋呀,  我们只需要按照扩展的样例代码撸就可以了,不用细心琢磨redis那内部的密集代码了。 

在redis 2.x的时候,作者加入了lua的解释器,但因为种种的安全限制,只能用那固定几个库包,但如果是应对频繁网络IO的场景, redis lua 绝逼是够用了…   有兴趣可以看看. 

http://xiaorui.cc/2016/03/27/%E8%AF%A6%E8%A7%A3python%E8%B0%83%E7%94%A8redis-lua%E5%86%85%E5%B5%8C%E8%84%9A%E6%9C%AC%E7%9A%84%E9%AB%98%E7%BA%A7%E7%94%A8%E6%B3%95/


首先我们要安装redis 4.0 rc 新版.    antirez说过,在3.x版本中不会加入自定义模块加载的.

#xiaorui.cc

wget  https://github.com/antirez/redis/archive/4.0-rc2.tar.gz
tar  zxvf  4.0*
cd  4.0
make
make  test

下载第三方的Redis modules模块,make编译

git clone https://github.com/RedisLabsModules/redex
cd redex
make


上面编译完了后,在src下是有一堆动态链接库so 文件的, 然后我们修改redis.conf的配置, 加入这些so文件。

--loadmodule /path/src/module.so

Redis模块源码在这里.   https://github.com/RedisLabsModules/redex  ,   这名字有点意思哈….     redex  vs redis  ? 

redisEx moudules的扩展基本是 在redis本身数据结构基础上做的调整 .

Included modules:

rxkeys - extended keys commands (Module Hub page)
rxstrings - extended Strings commands (Module Hub page)
rxhashes - extended Hashes commands (Module Hub page)
rxlists - extended Lists commands (Module Hub page)
rxsets - extended Sets commands (Module Hub page)
rxzsets - extended Sorted Sets commands (Module Hub page)
rxgeo - extended Geo Sets commands (Module Hub page)


我们看下RedisEx对 zset的相关调整,比如里面的zpop 和  zrevpop  , 一看这名字我觉得大家就能感觉出来他是干嘛的吧?  

redis 本身的zset 是没有pop命令的,如果你想获取并删除,那么就只能 zrange and zrem .   zpop 是帮你pop出来一个最小score的数据,zrevpop 反之.

# xiaorui.cc

int ZPopGenericCommand(RedisModuleCtx *ctx, RedisModuleString **argv,
                       int argc) {
  if (argc < 2 || argc > 3) {
    return RedisModule_WrongArity(ctx);
  }
  RedisModule_AutoMemory(ctx);

  /* Get the target command. */
  size_t cmdlen;
  const char *cmd = RedisModule_StringPtrLen(argv[0], &cmdlen);
  int rev = !strncasecmp("zrevpop", cmd, cmdlen);
  int withscore = RMUtil_ArgExists("WITHSCORE", argv, argc, 2);
  if ((argc == 3) && !withscore) return RedisModule_WrongArity(ctx);

  // open the key and make sure it's indeed a ZSET and not empty
  RedisModuleKey *key =
      RedisModule_OpenKey(ctx, argv[1], REDISMODULE_READ | REDISMODULE_WRITE);

  if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_ZSET) {
    // and empty key - return null
    if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) {
      RedisModule_ReplyWithNull(ctx);
      return REDISMODULE_OK;
    }

    // 类型错误,抛出异常
    RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
    return REDISMODULE_ERR;
  }

  // 获取最小score分值的元素 
  double score;
  (rev ? RedisModule_ZsetLastInScoreRange : RedisModule_ZsetFirstInScoreRange)(
      key, REDISMODULE_NEGATIVE_INFINITE, REDISMODULE_POSITIVE_INFINITE, 0, 0);
  RedisModuleString *ele = RedisModule_ZsetRangeCurrentElement(key, &score);
  RedisModule_ZsetRangeStop(key);

  // 删除这个元素
  RedisModule_ZsetRem(key, ele, NULL);

  // 返回元素 或者 含有分值 (取决于是否传入withscore参数)
  RedisModule_ReplyWithArray(ctx, (withscore ? 2 : 1));
  RedisModule_ReplyWithString(ctx, ele);
  if (withscore) RedisModule_ReplyWithDouble(ctx, score);

  return REDISMODULE_OK;
}


Redis set里多了一个 msismember的命令扩展,是个sismember的多扩展 .  用来判断多个集合里是否含有某个元素, 符合一个条件加 +1  , 如果返回 2 ,那么就是两个集合都含有这个元素.

redis> SADD admins Alice Bob xiaorui.cc
(integer) 3
redis> SADD oncall Zoe Bob Xavier
(integer) 3
redis> MSISMEMBER admins oncall Alice
(integer) 1
redis> MSISMEMBER admins oncall Zoe
(integer) 1
redis> MSISMEMBER admins oncall Bob
(integer) 2

Reids Msismember 命令源代码如下 :

#xiaorui.cc

int MSIsMemberCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
  if (argc < 3) {
    if (RedisModule_IsKeysPositionRequest(ctx))
      return REDISMODULE_OK;
    else
      return RedisModule_WrongArity(ctx);
  }

  if (RedisModule_IsKeysPositionRequest(ctx)) {
    size_t i;
    for (i = 1; i < argc - 1; i++) RedisModule_KeyAtPos(ctx, i);
    return REDISMODULE_OK;
  }

  RedisModule_AutoMemory(ctx);

  int iele = argc - 1;
  size_t count = 0;
  int i;
  for (i = 1; i < iele; i++) {
    RedisModuleKey *key =
        RedisModule_OpenKey(ctx, argv[i], REDISMODULE_READ | REDISMODULE_WRITE);

    if (RedisModule_KeyType(key) == REDISMODULE_KEYTYPE_EMPTY) continue;

    if (RedisModule_KeyType(key) != REDISMODULE_KEYTYPE_SET) {   // 判断数据类型
      RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
      return REDISMODULE_ERR;
    }

    RedisModuleCallReply *rep =
        RedisModule_Call(ctx, "SISMEMBER", "ss", argv[i], argv[iele]);   // 循环遍历调用 sismember 查看set集合中是否含有该元素
    RMUTIL_ASSERT_NOERROR(rep)

    count += RedisModule_CallReplyInteger(rep);  // 如何一个就加一个数
  }

  RedisModule_ReplyWithLongLong(ctx, count);  //返回
  return REDISMODULE_OK;
}


通过上面几个redis扩展源码我们发现,基本都是多个命令合并成一个函数,对于客户端来说节省了网络io的开销,又保证了原子性。


分享个Dvir Volk 的redis 模块开发视频分享,我看了好几遍,受益匪浅,就差动手去开发了…

https://www.youtube.com/watch?v=bQ9MyImCYqc



打个小样, 这两天在发力写个可自增 zset ,敬请关注呀.

END.


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注