1 Redis内存管理

Redis内存管理相关文件为zmalloc.c/zmalloc.h,其只是对C中内存管理函数做了简单的封装,屏蔽了底层平台的差异,并增加了内存使用情况统计的功能。
void *zmalloc(size_t size) {
// 多申请的一部分内存用于存储当前分配了多少自己的内存
void *ptr = malloc(size+PREFIX_SIZE); if (!ptr) zmalloc_oom_handler(size);
#ifdef HAVE_MALLOC_SIZE
update_zmalloc_stat_alloc(zmalloc_size(ptr));
return ptr;
#else
*((size_t*)ptr) = size;
// 内存分配统计
update_zmalloc_stat_alloc(size+PREFIX_SIZE);
return (char*)ptr+PREFIX_SIZE;
#endif
}

内存布局图示:

2 事件处理

Redis的事件类型分为时间事件和文件事件,文件事件也就是网络连接事件。时间事件的处理是在epoll_wait返回处理文件事件后处理的,每次epoll_wait的超时时间都是Redis最近的一个定时器时间。
 
Redis在进行事件处理前,首先会进行初始化,初始化的主要逻辑在main/initServer函数中。初始化流程主要做的工作如下:
  • 设置信号回调函数。
  • 创建事件循环机制,即调用epoll_create。
  • 创建服务监听端口,创建定时事件,并将这些事件添加到事件机制中。
void initServer(void) {
int j; // 设置信号对应的处理函数
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
setupSignalHandlers();
... createSharedObjects();
adjustOpenFilesLimit();
// 创建事件循环机制,及调用epoll_create创建epollfd用于事件监听
server.el = aeCreateEventLoop(server.maxclients+CONFIG_FDSET_INCR);
server.db = zmalloc(sizeof(redisDb)*server.dbnum); /* Open the TCP listening socket for the user commands. */
// 创建监听服务端口,socket/bind/listen
if (server.port != &&
listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
exit();
... /* Create the Redis databases, and initialize other internal state. */
for (j = ; j < server.dbnum; j++) {
server.db[j].dict = dictCreate(&dbDictType,NULL);
server.db[j].expires = dictCreate(&keyptrDictType,NULL);
server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
server.db[j].ready_keys = dictCreate(&setDictType,NULL);
server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
server.db[j].eviction_pool = evictionPoolAlloc();
server.db[j].id = j;
server.db[j].avg_ttl = ;
}
... /* Create the serverCron() time event, that's our main way to process
* background operations. 创建定时事件 */
if(aeCreateTimeEvent(server.el, , serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create the serverCron time event.");
exit();
} /* Create an event handler for accepting new connections in TCP and Unix
* domain sockets. */
for (j = ; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
serverPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
// 将事件加入到事件机制中,调用链为 aeCreateFileEvent/aeApiAddEvent/epoll_ctl
if (server.sofd > && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event."); /* Open the AOF file if needed. */
if (server.aof_state == AOF_ON) {
server.aof_fd = open(server.aof_filename,
O_WRONLY|O_APPEND|O_CREAT,);
if (server.aof_fd == -) {
serverLog(LL_WARNING, "Can't open the append-only file: %s",
strerror(errno));
exit();
}
} ...
}
事件处理流程
事件处理函数链:aeMain / aeProcessEvents / aeApiPoll / epoll_wait。
 
常见的事件机制处理流程是:调用epoll_wait等待事件来临,然后遍历每一个epoll_event,提取epoll_event中的events和data域,data域常用来存储fd或者指针,不过一般的做法是提取出events和data.fd,然后根据fd找到对应的回调函数,fd与对应回调函数之间的映射关系可以存储在特定的数据结构中,比如数组或者哈希表,然后调用事件回调函数来处理。
 
Redis中用了一个数组来保存fd与回调函数的映射关系,使用数组的优点就是简单高效,但是数组一般使用在建立的连接不太多情况,而Redis正好符合这个情况,一般Redis的文件事件大都是客户端建立的连接,而客户端的连接个数是一定的,该数量通过配置项maxclients来指定。
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
aeApiState *state = eventLoop->apidata;
int retval, numevents = ; retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
tvp ? (tvp->tv_sec* + tvp->tv_usec/) : -);
if (retval > ) {
int j; numevents = retval;
for (j = ; j < numevents; j++) {
int mask = ;
struct epoll_event *e = state->events+j; if (e->events & EPOLLIN) mask |= AE_READABLE;
if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
if (e->events & EPOLLERR) mask |= AE_WRITABLE;
if (e->events & EPOLLHUP) mask |= AE_WRITABLE;
eventLoop->fired[j].fd = e->data.fd;
eventLoop->fired[j].mask = mask;
}
}
return numevents;
} int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
numevents = aeApiPoll(eventLoop, tvp);
for (j = ; j < numevents; j++) {
// 从eventLoop->events数组中查找对应的回调函数
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
int mask = eventLoop->fired[j].mask;
int fd = eventLoop->fired[j].fd;
int rfired = ; /* note the fe->mask & mask & ... code: maybe an already processed
* event removed an element that fired and we still didn't
* processed, so we check if the event is still valid. */
if (fe->mask & mask & AE_READABLE) {
rfired = ;
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
}
if (fe->mask & mask & AE_WRITABLE) {
if (!rfired || fe->wfileProc != fe->rfileProc)
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
}
processed++;
}
...
}
文件事件的监听
Redis监听端口的事件回调函数链是:acceptTcpHandler / acceptCommonHandler / createClient / aeCreateFileEvent / aeApiAddEvent / epoll_ctl。 
在Reids监听事件处理流程中,会将客户端的连接fd添加到事件机制中,并设置其回调函数为readQueryFromClient,该函数负责处理客户端的命令请求。
 
命令处理流程
命令处理流程链是:readQueryFromClient / processInputBuffer / processCommand / call / 对应命令的回调函数(c->cmd->proc),比如get key命令的处理回调函数为getCommand。getCommand的执行流程是先到client对应的数据库字典中根据key来查找数据,然后根据响应消息格式将查询结果填充到响应消息中。
 

3 如何添加自定义命令

如何在Redis中添加自定的命令呢?其中只需要改动以下几个地方就行了,比如自定义命令random xxx,然后返回redis: xxx,因为hello xxx和get key类似,所以就依葫芦画瓢。random命令用来返回一个小于xxx的随机值。
 
首先在redisCommandTable数组中添加自定义的命令,redisCommandTable数组定义在server.c中。然后在getCommand定义处后面添加randomCommand的定义,getCommand定义在t_string.c中。最后在server.h中添加helloCommand的声明。整个修改patch文件如下,代码基于redis-2.8.9版本。
From 5304020683078273c1bc6cc9666dab95efa18607 Mon Sep  ::
From: luoxn28 <luoxn28@.com>
Date: Fri, Jun :: -
Subject: [PATCH] add own command: random num ---
src/server.c | ++-
src/server.h | +
src/t_string.c | ++++++++++++++++++++++++++++++++++++++++++++
files changed, insertions(+), deletion(-) diff --git a/src/server.c b/src/server.c
index 609f396..e040104
--- a/src/server.c
+++ b/src/server.c
@@ -, +, @@ struct redisCommand redisCommandTable[] = {
{"pfdebug",pfdebugCommand,-,"w",,NULL,,,,,},
{"post",securityWarningCommand,-,"lt",,NULL,,,,,},
{"host:",securityWarningCommand,-,"lt",,NULL,,,,,},
- {"latency",latencyCommand,-,"aslt",,NULL,,,,,}
+ {"latency",latencyCommand,-,"aslt",,NULL,,,,,},
+ {"random",randomCommand,,"rF",,NULL,,,,,}
}; struct evictionPoolEntry *evictionPoolAlloc(void);
diff --git a/src/server.h b/src/server.h
index 3fa7c3a..427ac92
--- a/src/server.h
+++ b/src/server.h
@@ -, +, @@ void setnxCommand(client *c);
void setexCommand(client *c);
void psetexCommand(client *c);
void getCommand(client *c);
+void randomCommand(client *c);
void delCommand(client *c);
void existsCommand(client *c);
void setbitCommand(client *c);
diff --git a/src/t_string.c b/src/t_string.c
index 8c737c4..df4022d
--- a/src/t_string.c
+++ b/src/t_string.c
@@ -, +, @@ void getCommand(client *c) {
getGenericCommand(c);
} +static bool checkRandomNum(char *num)
+{
+ char *c = num;
+
+ while (*c != '\0') {
+ if (!(('' <= *c) && (*c <= ''))) {
+ return false;
+ }
+ c++;
+ }
+
+ return true;
+}
+
+/**
+ * command: random n
+ * return a random num < n, if n <= 0, return 0
+ * @author: luoxiangnan
+ */
+void randomCommand(client *c)
+{
+ char buff[] = {};
+ int num = ;
+ robj *o = NULL;
+
+ if (!checkRandomNum(c->argv[]->ptr)) {
+ o = createObject(OBJ_STRING, sdsnewlen("sorry, it's not a num :(",
+ strlen("sorry, it's not a num :(")));
+ addReplyBulk(c, o);
+ return;
+ }
+
+ sscanf(c->argv[]->ptr, "%d", &num);
+ if (num > ) {
+ num = random() % num;
+ } else {
+ num = ;
+ }
+
+ sprintf(buff, "%s %d", "redis: ", num);
+ o = createObject(OBJ_STRING, sdsnewlen(buff, strlen(buff)));
+ addReplyBulk(c, o);
+}
+
void getsetCommand(client *c) {
if (getGenericCommand(c) == C_ERR) return;
c->argv[] = tryObjectEncoding(c->argv[]);
--
1.8.3.1
结果如下所示:

Redis 内存管理与事件处理的更多相关文章

  1. Redis 内存管理 源码分析

    要想了解redis底层的内存管理是如何进行的,直接看源码绝对是一个很好的选择 下面是我添加了详细注释的源码,需要注意的是,为了便于源码分析,我把redis为了弥补平台差异的那部分代码删了,只需要知道有 ...

  2. Redis内存管理(二)

    上一遍详细的写明了Redis为内存管理所做的初始化工作,这篇文章写具体的函数实现. 1.zmalloc_size,返回内存池大小函数,因为库不同,所以这个函数在内部有很多的宏定义,通过具体使用的库来确 ...

  3. Redis内存管理(一)

    Redis数据库的内存管理函数有关的文件为:zmalloc.h和zmalloc.c. Redis作者在编写内存管理模块时考虑到了查看系统内是否安装了TCMalloc或者Jemalloc模块,这两个是已 ...

  4. TCMalloc优化MySQL、Nginx、Redis内存管理

    TCMalloc(Thread-Caching Malloc)与标准glibc库的malloc实现一样的功能,但是TCMalloc在效率和速度效率都比标准malloc高很多.TCMalloc是 goo ...

  5. redis内存管理

    Redis主要通过控制内存上线和回收策略来实现内存管理. 1. 设置内存上限 redis使用maxmemory参数限制最大可用内存.限制的目的主要有: 用户缓存场景,当超出内存上限maxmemory时 ...

  6. Redis内存管理的基石zmallc.c源代码解读(一)

    当我第一次阅读了这个文件的源代码的时候.我笑了,忽然想起前几周阿里电话二面的时候,问到了自己定义内存管理函数并处理8字节对齐问题. 当时无言以对,在面试官无数次的提示下才答了出来,结果显而易见,挂掉了 ...

  7. 详解 Redis 内存管理机制和实现

    Redis是一个基于内存的键值数据库,其内存管理是非常重要的.本文内存管理的内容包括:过期键的懒性删除和过期删除以及内存溢出控制策略. 最大内存限制 Redis使用 maxmemory 参数限制最大可 ...

  8. redis内存管理代码的目光

    zmalloc.h /* zmalloc - total amount of allocated memory aware version of malloc() * * Copyright (c) ...

  9. redis 内存管理与数据淘汰机制(转载)

    原文地址:http://www.jianshu.com/p/2f14bc570563?from=jiantop.com 最大内存设置 默认情况下,在32位OS中,Redis最大使用3GB的内存,在64 ...

随机推荐

  1. C++ 中的一些错觉

    1. 默认构造函数和不带参数的构造函数之间无联系 默认构造函数是编译器发现类不存在显式构造函数时自动生成的无参数的构造函数.同样,用户可以定义显示的无参数构造函数. 2. 在构造函数.析构函数中调用v ...

  2. hive、impala集成ldap

    1.概要 1.1 环境信息 hadoop:cdh5.10 os:centos6.7 user:root hive.impala已集成sentry 1.2 访问控制权限 这里通过使用openldap来控 ...

  3. 精华【分布式、微服务、云架构、dubbo+zookeeper+springmvc+mybatis+shiro+redis】分布式大型互联网企业架构!

    平台简介 Jeesz是一个分布式的框架,提供项目模块化.服务化.热插拔的思想,高度封装安全性的Java EE快速开发平台. Jeesz本身集成Dubbo服务管控.Zookeeper注册中心.Redis ...

  4. 史诗手册!微信小程序新手自学入门宝典!

    一.小程序官方指南 1:官方开发工具下载: https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=201714 0.12 ...

  5. C语言习题1.分别统计一下其中字母,数字,其他字符的个数。将统计的字母,数字,其他字符的个数以柱状图的形式打印

    从键盘上输入字符,(1)分别统计一下其中字母,数字,其他字符的个数, (2)将统计的字母,数字,其他字符的个数以柱状图的形式打印.例如 5 ***** *****     3 *****   **** ...

  6. 开涛spring3(6.5) - AOP 之 6.5 AspectJ切入点语法详解

    6.5.1  Spring AOP支持的AspectJ切入点指示符 切入点指示符用来指示切入点表达式目的,,在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的Aspect ...

  7. 开涛spring3(2.1) - IoC基础

    2.1.1  IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在 ...

  8. JQuery版评分控件

    Hi All, 分享一个学习JQuery做的一个评分控件. 需求:当鼠标移动到 ‘☆’ 上时,该字符左边的 ‘☆’ 变成 '★',该字符右边仍然是 ‘☆’, 并显示相应星星数的评价结果:当鼠标推出 ‘ ...

  9. 用R语言对一个信用卡数据实现logit,GBM,knn,xgboost

    Prepare the data 数据来自UCIhttp://archive.ics.uci.edu/ml/machine-learning-databases/credit-screening,一个 ...

  10. rPithon vs. rPython(转)

    Similar to rPython, the rPithon package (http://rpithon.r-forge.r-project.org) allows users to execu ...