内容简介:我今天在逛redis官网的时候(redis.io),无意中看到r回答redis modules是什么问题之前我们可以先看看redis modules能做什么,通过redis modules,用户可以扩展redis的功能,比如用户可以实现自定义命令, 当客户端往redis server发送这个命令的时候,redis server会执行用户实现的代码,运行起来就像redis内置命令一样。总结一下,redis modules是redis提供的一种插件化机制,他能够让用户的代码以插件(modules)的形式集成到r
一、引言
我今天在逛 redis 官网的时候(redis.io),无意中看到r edis m odules 这个功能,于是就去了解了一下同时记录为本篇内容。本文包括两个部分:第一部分是对redis modules进行入门级介绍,回答的问题包括r edis m odules 是什么?redis modules怎么用这两个问题?同时提出了三个问题。第二部分是针对第一部分提出的三个问题进行回答,回答主要是以个人观点为主。
二、redis modules
2.1 redis modules是什么
回答redis modules是什么问题之前我们可以先看看redis modules能做什么,通过redis modules,用户可以扩展redis的功能,比如用户可以实现自定义命令, 当客户端往redis server发送这个命令的时候,redis server会执行用户实现的代码,运行起来就像redis内置命令一样。总结一下,redis modules是redis提供的一种插件化机制,他能够让用户的代码以插件(modules)的形式集成到redis服务中,如下是redis modules示意图帮助理解。
图1 redis模块系统插件化机制示意图
2.2 redis模块系统怎么玩
这个功能看起来很有意思,那么我们怎么玩呢?如果只用一句话描述的话,可以为: 引入redismodule.h头文件实现指定的接口和自定义命令,并将你的代码打包成动态链接库 , 然后使用redis提供的命令将动态链接库集成到redis服务中就可以啦 。除了运行期动态加载链接库之外,我们还可以使用配置文件的方式(redis.conf),让redis在启动的时候加载动态链接库,这样会有一个预热的过程,下面是代表两种做法的代码,帮助理解:
MODULE LOAD /path/to/mymodule.so 或 loadmodule /path/to/mymodule.so
1)那么,有哪些接口可以或者需要实现呢?
其实没那么多,也就只有RedisModule_OnLoad这个接口需要实现,RedisModule_OnLoad相当于一个redis提供的一个回调方法,在模块刚被加载时候,redis服务会回调这个方法,因此我们可以在这个方法中添加一些模块初始化相关的逻辑,这部分逻辑可以在module.c的moduleLoad方法中找到,redis服务分别调用dlopen方法加载动态链接库库并获得句柄,然后传给dlsym方法来调用动态链接库中的用户代码,注意用户是必须要实现RedisModule_OnLoad方法的,否则模块不会生效。
handle = dlopen(path,RTLD_NOW|RTLD_LOCAL);
//省略
onload = (int (*)(void *, void **, int))(unsigned long) dlsym(handle,"RedisModule_OnLoad");
if (onload == NULL) {
//省略
return C_ERR;
}
2)然后,怎么实现自定义命令呢?
实现起来可以认为一共分两步,第一步是实现自定义命令处理逻辑,这里提供一个简单的例子:
int HelloSimple_RedisCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
RedisModule_ReplyWithLongLong(ctx, 1);
return REDISMODULE_OK;
}
这个例子实现的功能就只是往客户端返回固定的数字“1”,这段代码涉及对RedisModule_ReplyWithLongLong这个方法的调用,RedisModule_ReplyWithLongLong方法是Redis Modules提供给自定义模块开发者的标准接口,换句话说就是你在实现自己的模块逻辑的时候,可以调用这样一组标准接口,来实现自己想要的功能(类系统调用),这样的标准接口还有很多很多。 第二步是注册自定义命令名与自定义命令处理逻辑,具体是在RedisModule_OnLoad方法的实现中调用 RedisModule_CreateCommand 方法注册自定义命令,假设自定义的命令名为“hello.simple”,那么注册方式如下:
RedisModule_CreateCommand(ctx,"hello.simple", HelloSimple_RedisCommand)
RedisModule_CreateCommand方法也是属于redis模块系统提供给模块实现者的标准接口。
2.3 提出引申问题
上面是基于redis modules实现一个自定义命令的完整示例,打包加载部分这里就不带着大家一起看了,接下来我提出三个引申问题:
1、从redis的Copyright看,从2006年就开始了,从代码提交看可追溯到2009年,也就是说redis至少经历了10年的迭代,功能不断增增加,代码规模不断增大,redis是如何控制代码复杂度的?
2、插件化这个思路也很常见,官网显示MODULE LOAD是从redis 4.0.0版本开始使用的,从代码提交看,redis 模块系统代码至少提交于2016年5月,实际开始编写会更早,redis为什么会提供这样的功能?
3、插件化设计的技术难点是什么?(有什么需要注意的?)
三、引申问题思考
3.1 redis是如何应分解代码的?
redis是基于c编写的,redis src文件夹下的代码行数约10W行,对于 c语言 编写的代码来说规模上算是比较宏大的了,我们从redis src下面的.h和.c文件可以看出一些规律,不完全枚举,大家可以感受一下:
图二 redis源码结构
可以看出, redis主要是以功能模块的方式分解代码的 ,个人认为redis源码没有给人一种非常复杂的感觉,每个模块的命名很清晰,比如zmalloc.c一看就知道是内存分配相关功能的具体实现,同时每个功能都会分成.h和.c两个文件,如果我们只想了解这个功能大概包括哪些核心功能,只需要看.h就可以了,比如下面是zmalloc.h中声明的所有方法:
void *zmalloc(size_t size);
void *zcalloc(size_t size);
void *zrealloc(void *ptr, size_t size);
void zfree(void *ptr);
char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
size_t zmalloc_get_rss(void);
int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);
void set_jemalloc_bg_thread(int enable);
int jemalloc_purge();
size_t zmalloc_get_private_dirty(long pid);
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
size_t zmalloc_get_memory_size(void);
void zlibc_free(void *ptr);
3.2 redis为什么要引入插件化功能?
从时代背景看,redis的诞生是在PC互联网时代迁移到移动互联网时代中的十年(2000-2010),发展是在移动互联网爆发的十年(2010-2020),redis模块系统从2016年开始提交代码,我印象中2015年是互联网创业的泡沫时期,当时创业项目成千上万,是一个ppt拿融资的年代,随着移动互联网的快速发展,移动流量不断突破记录对后端高性能系统设计提出了挑战,而redis作为一个高性能的内存数据库产品恰好是这方面的优秀解决方案,互联网的发展带动了技术的发展,因此,我认为这是一部分原因。然而redis本身是一个慢项目,redis定位是基础设施层,是存储数据的地方,千万不能出现问题,核心是求稳,每个版本都要经过足够一段时间的beta才能发布,其不可能像互联网业务项目一样能够快速迭代?需求需要支撑,系统要求稳定,怎么办呢?redis想到了一个办法, 按开放式的思路设计,提供可插拔的能力,每个人都可以将自己的代码集成进来 ,以应对redis团队资源方面的问题以及各种各样业务场景中redis本身无法满足的业务需求 。 其实,横向看一下我们会发现无独有偶,从技术角度看,2017年阿里双十一交易系统TMP2.0也是基于同样的思路设计的,TMF是在中台化的背景下产生的,其能够支持业务代码以插件包的形式集成到交易平台上,交易平台整体框架,插件化支持业务定制。
图三 TMP 2.0
3.3 插件化系统设计的难点是什么?
“设计最难的部分在于设计什么”——《设计原本》
类似,我认为插件化系统的核心难点是插件什么?这里包括两个部分问题,一是在哪里设置插口?二是插口如何设计?从大的方向看,这部分核心依赖对业务流程的理解,对变与不变的识别,将变化的东西通过插口隔离出来。
除此之外,我认为还有一个很重要的问题是安全性问题,既然允许用户代码跑在核心系统里面,那么如何保证这部分代码执行是安全的?举个栗子,用户实现了一个死循环丢给核心系统调度会有什么后果?这个问题操作系统设计的时候也有同样的困惑,因此引入了用户态和内核态的概念。
这部分我主要是作为提问者吧,读者有答案欢迎分享。
四、总结
本文从redis模块系统出发,首先介绍了redis模块系统是什么,怎么玩,然后提出了三个引申的问题,后面针对这三个引申的问题试着解答,但答案仅仅是作者个人理解。插件化思路无论是对基础服务来说,还是对业务系统来说都是非常具有设计参考价值,希望本文能够对读者有帮助,谢谢。
五、参考文献
【1】https://www.sohu.com/a/212071978_612370
【2】https://redis.io/topics/modules-intro
以上所述就是小编给大家介绍的《Redis Modules及其引申问题》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- acme.sh 续期问题(路径问题)
- 缓存的一些问题和一些加密算法【缓存问题】
- 如何把设计问题转化为数学问题(方法论)
- 推荐系统中的冷启动问题和探索利用问题
- GraphQL 教程(六)—— N+1问题和缓存等问题
- Golang 并发问题(四)之单核上的并发问题
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。