Redis模块开发示例
实现一个Redis module,支持两个扩展命令:
1) 可同时对hash的多个field进行incr操作;
2) incrby同时设置一个key的过期时间
在没有module之前,需要借助eval+lua实现相同的功能。有了module,不但可以实现逻辑复杂,且性能高的扩展,同时享受Redis的持久化和容灾能力。
// Redis命令扩展module
#include "redismodule.h"
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
// 带超时的INCRBY命令
// 格式:INCRBYEX KEY SECONDS INCREMENT
//
// 示例:
// ex.incrbyex k1 10 1
// ex.incrbyex k1 10 2
int incrbyex_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
// When automatic memory management is enabled:
// 1) don't need to close open keys
// 2) don't need to free replies
// 3) don't need to free RedisModuleString objects
RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
if (argc != 4) return RedisModule_WrongArity(ctx);
RedisModuleKey *key = (RedisModuleKey*)RedisModule_OpenKey(ctx, argv[1],
REDISMODULE_READ|REDISMODULE_WRITE);
int type = RedisModule_KeyType(key);
if (type != REDISMODULE_KEYTYPE_STRING &&
type != REDISMODULE_KEYTYPE_EMPTY)
{
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}
RedisModuleString* seconds = argv[2];
RedisModuleString* increment = argv[3];
long long ll_seconds; // 过期时长
long long ll_newval; // 新的值
if (RedisModule_StringToLongLong(seconds, &ll_seconds) != REDISMODULE_OK) {
return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range");
}
if (RedisModule_StringToLongLong(increment, &ll_newval) != REDISMODULE_OK) {
return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range");
}
size_t len;
char *s = RedisModule_StringDMA(key, &len, REDISMODULE_READ|REDISMODULE_WRITE);
if (0 == len || NULL == s || s == '\0') {
// set必须在Expire之前,否则会冲掉Expire的作用,
// 这也是else分支未用RedisModule_StringSet的原因
RedisModule_StringSet(key, increment);
RedisModule_SetExpire(key, ll_seconds*1000); // 以秒为单位,需要乘以1000
}
else {
char* endptr;
long long ll_oldval = strtoll(s, &endptr, 10); // s不一定是有效的数字,所以需要做检查
ll_newval = ll_newval + ll_oldval;
if ((errno == ERANGE && (ll_oldval == LLONG_MAX || ll_oldval == LLONG_MIN))
|| (errno != 0 && ll_oldval == 0)) {
return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range");
}
if (endptr == s || *endptr != '\0') {
return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range");
}
size_t newval_len;
RedisModuleString* newval = RedisModule_CreateStringFromLongLong(ctx, ll_newval);
const char* newval_s = RedisModule_StringPtrLen(newval, &newval_len);
if (newval_len > len)
RedisModule_StringTruncate(key, newval_len);
strncpy(s, newval_s, newval_len);
}
RedisModule_ReplicateVerbatim(ctx); // 写AOF和复制到slaves
RedisModule_ReplyWithLongLong(ctx, ll_newval);
return REDISMODULE_OK;
}
// 实现命令HMINCRBY,同时对HASH的多个field值进行增减操作
// 格式:HMINCRBY KEY FIELD1 VALUE1 FIELD2 VALUE2 FIELD3 VALUE3 ......
//
// 示例:
// hmincrby k1 f1 1 f2 2
// hmincrby k1 f5 1 f6 2 f7 3
int hmincrby_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
// When automatic memory management is enabled:
// 1) don't need to close open keys
// 2) don't need to free replies
// 3) don't need to free RedisModuleString objects
RedisModule_AutoMemory(ctx); /* Use automatic memory management. */
if (argc < 4 || argc % 2 != 0) return RedisModule_WrongArity(ctx);
RedisModuleKey *key = (RedisModuleKey*)RedisModule_OpenKey(ctx, argv[1],
REDISMODULE_READ|REDISMODULE_WRITE);
int type = RedisModule_KeyType(key);
if (type != REDISMODULE_KEYTYPE_HASH &&
type != REDISMODULE_KEYTYPE_EMPTY)
{
return RedisModule_ReplyWithError(ctx, REDISMODULE_ERRORMSG_WRONGTYPE);
}
const int count = argc/2 - 1; // 键值对个数
std::vector<long long> newval_array(count); // 用来存储新值的数组
for (int i=2; i<argc; i+=2) {
RedisModuleString* field = argv[i];
RedisModuleString* incrvalue = argv[i+1];
long long ll_newvalue;
if (RedisModule_StringToLongLong(incrvalue, &ll_newvalue) != REDISMODULE_OK) {
return RedisModule_ReplyWithError(ctx, "ERR value is not an integer or out of range");
}
RedisModuleString *oldval;
RedisModule_HashGet(key, REDISMODULE_HASH_NONE, field, &oldval, NULL);
if (NULL == oldval) { // field不存在时
RedisModule_HashSet(key,REDISMODULE_HASH_NONE, field, incrvalue, NULL);
}
else { // field已存在时
long long ll_oldval;
if (RedisModule_StringToLongLong(oldval, &ll_oldval) != REDISMODULE_OK) {
return RedisModule_ReplyWithError(ctx, "ERR hash value is not an integer");
}
ll_newvalue = ll_newvalue + ll_oldval; // 累加得到新值
RedisModuleString* newval = RedisModule_CreateStringFromLongLong(ctx, ll_newvalue);
RedisModule_HashSet(key,REDISMODULE_HASH_NONE, field, newval, NULL);
}
newval_array[i] = ll_newvalue;
}
RedisModule_ReplicateVerbatim(ctx); // 写AOF和复制到slaves
RedisModule_ReplyWithArray(ctx, count); // 返回数组类型的Reply
for (std::vector<long long>::size_type i=0; i<newval_array.size(); ++i)
RedisModule_ReplyWithLongLong(ctx, newval_array[i]);
return REDISMODULE_OK;
}
extern "C"
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (RedisModule_Init(ctx, "command_extension", 1, REDISMODULE_APIVER_1)
== REDISMODULE_ERR) return REDISMODULE_ERR;
/* Log the list of parameters passing loading the module. */
for (int j=0; j<argc; ++j) {
const char *s = RedisModule_StringPtrLen(argv[j], NULL);
printf("Module loaded with ARGV[%d] = %s\n", j, s);
}
if (RedisModule_CreateCommand(ctx, "ex.incrbyex",
incrbyex_RedisCommand,"write", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "ex.hmincrby",
hmincrby_RedisCommand,"write", 1, 1, 1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
Redis模块开发示例的更多相关文章
- Xposed模块开发基本方法记录
由于某些课程实验的要求,需要通过xposed框架对某应用进行hook操作,笔者选用了开源且免费的xposed框架进行实现.虽然网上存在一些利用xposed实现特定功能的文章资源,但大多均将xposed ...
- Openresty最佳案例 | 第7篇: 模块开发、OpenResty连接Redis
转载请标明出处: http://blog.csdn.net/forezp/article/details/78616714 本文出自方志朋的博客 Lua模块开发 在实际的开发过程中,不可能把所有的lu ...
- openresty开发系列26--openresty中使用redis模块
openresty开发系列26--openresty中使用redis模块 在一些高并发的场景中,我们常常会用到缓存技术,现在我们常用的分布式缓存redis是最知名的, 操作redis,我们需要引入re ...
- nginx模块开发篇 (阿里著作)
背景介绍 nginx历史 使用简介 nginx特点介绍 nginx平台初探(100%) 初探nginx架构(100%) nginx基础概念(100%) connection request 基本数据结 ...
- 【转】Nginx模块开发入门
转自: http://kb.cnblogs.com/page/98352/ 结论:对Nginx模块开发入门做了一个helloworld的示例,简单易懂.也有一定的深度.值得一看. Nginx模块开发入 ...
- VB 核心编程及通用模块开发 笔记1
前言:学习任何编程语言.编程工具的目的不外乎解决生活问题,能够快速.高效解决问题的方式就是不错的方式,至于选择什么“工具”,众位看官看着办,本人虽然有过3年vb开发经验,但是一直没有深入学习,现已购买 ...
- 为Lua5.3编写C模块简单示例
为Lua5.3编写C模块简单示例 一.编译安装Lua5.3 MSVC 命令行安装脚本: @echo off md bin md lib md include cd src cl /c /nologo ...
- springboot多模块开发以及整合dubbo\zookeeper进行服务管理
之前研究了springboot单工程的使用,参考git地址:https://github.com/qiao-zhi/springboot-ssm 下面研究springboot多模块开发的过程. 1.模 ...
- Apache模块开发
一.简介 Apache HTTP服务器是一个模块化的软件,使管理者可以选择核心中包含的模块以裁剪功能.可以在编译时选择被静态包含进httpd二进制映象的模块,也可以编译成独立于主httpd二进制映象的 ...
随机推荐
- WorkerMan源码分析(resetStd方法,PHP中STDIN, STDOUT, STDERR的重定向)
WorkerMan中work.php中 resetStd 方法中代码如下 public static function resetStd() { if (!static::$daemonize || ...
- 在pom.xml中引入jar包坐标的依赖范围
A依赖B,需要在A的pom.xml文件中添加B的坐标,添加坐标时需要指定依赖范围,依赖范围包括: compile:编译范围,指A在编译时依赖B,此范围为默认依赖范围.编译范围的依赖会用在编译.测试.运 ...
- localstorage和vue结合使用
父组件 <template> <div class="hello"> <p>Original message:"{{message}} ...
- 迭代器、生成器 day13
一 迭代器 迭代器的由来pythone2.2引进的,是一种序列(也是一种数据类型),也是为类对象提供一个序列的入口. for 循环str list tuple dict set 文件句柄可迭代: s ...
- u-boot之make all执行过程分析
在执行make 100ask24x0_config之后就配置完成了针对JZ2440开发板的UBOOT,接下来需要执行make all进行编译链接最终生成u-boot.map.u-boot.srec.u ...
- Svn启动窗口报错 Could not load file or assembly 'SharpSvn.dll' or one of its
win10 64位系统生成没问题,测试都没问题,结果换到win7 64位系统上,点开就出现,网上搜了下,通过以下方式解决, 必须把bin 文件夹全部删除,重新生成.要不还是会报错. Solve it. ...
- Java的OOP三大特征之一——多态
OOP(面对对象)三大特征之一——多态 What:多态性是指允许不同类的对象对同一消息作出响应,多态性包括参数化多态性和包含多态性,多态性语言具有灵活.抽象.行为共享.代码共享的优势,很好的解决了应用 ...
- NC 6系初始化EJB
6系开发时,调用远程接口去操作数据时,需先调用EJB. InvocationInfoProxy.getInstance().setUserDataSource(design); InvocationI ...
- 使用hMailServer搭建邮件服务器
本文没有什么高深的技术内容,只是使用hMailServer,介绍搭建邮件服务器的全过程,供参考. 一.安装邮件服务器组件 打开软件,点下一步 选择存储数据的数据库,这里有两种选择, 一种是使用嵌入型数 ...
- base64编码是什么1
首先明确一点base64 是一种编码格式.就想UNICODE一样,能在电脑上表示所有字符,或者换句话说通过编码能让电脑理解你想要表示的字符(因为电脑只知道0和1 ) 就像ascII 中 0100 00 ...