当前位置:网站首页>Redis源码学习(33),命令执行过程
Redis源码学习(33),命令执行过程
2022-07-27 01:49:00 【无痕之意】
经过我们长时间的不懈努力,终于将数据类型和数据结构相关的源代码学习结束,今天开始新一阶段的学习,本节要学习的内容是命令的执行过程,探究我们平常输入的一个 Redis 命令到底是怎么执行的。
1 入口函数
我们知道 c 语言一般都会有个入口函数 main 函数,所以我们先从入口函数下手。
1.1 主要代码
该函数在 redis.c 文件中。
int main(int argc, char **argv) {
...
initServerConfig();// 初始化配置
...
initServer();// 初始化服务器
...
aeMain(server.el); // 主循环方法
}
源代码中 main 函数有很多代码,为了更好研究执行过程,我们先看看主要的几个方法。
2 命令加载
// redis.c
void initServerConfig(void) {
int j;
...
server.commands = dictCreate(&commandTableDictType,NULL);
server.orig_commands = dictCreate(&commandTableDictType,NULL);
populateCommandTable();
...
}
// redis.c
void populateCommandTable(void) {
int j;
int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
for (j = 0; j < numcommands; j++) {
struct redisCommand *c = redisCommandTable+j;
char *f = c->sflags;
int retval1, retval2;
retval1 = dictAdd(server.commands, sdsnew(c->name), c);
/* Populate an additional dictionary that will be unaffected * by rename-command statements in redis.conf. */
retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);
}
}
在初始化配置的时候,redis 会创建两个全局属性用来存储可以执行的命令 server.commands 和 server.orig_commands, 然后通过 populateCommandTable 方法循环将命令全部添加到两个全局属性中。
3 创建事件驱动
3.1 创建事件驱动
// redis.c
void initServer(void) {
...
// 创建epoll
server.el = aeCreateEventLoop(server.maxclients+REDIS_EVENTLOOP_FDSET_INCR);
// 创建事件驱动,绑定事件 acceptTcpHandler
for (j = 0; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
redisPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
}
在 initServer 方法中,我们可以看到 redis 创建了 epoll 对象用来处理网络 io 事件,并且对每个对象绑定了 acceptTcpHandler 方法。
3.2 acceptTcpHandler
// networking.c
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
int cport, cfd, max = MAX_ACCEPTS_PER_CALL;
char cip[REDIS_IP_STR_LEN];
REDIS_NOTUSED(el);
REDIS_NOTUSED(mask);
REDIS_NOTUSED(privdata);
while(max--) {
cfd = anetTcpAccept(server.neterr, fd, cip, sizeof(cip), &cport);
if (cfd == ANET_ERR) {
if (errno != EWOULDBLOCK)
redisLog(REDIS_WARNING,
"Accepting client connection: %s", server.neterr);
return;
}
redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
acceptCommonHandler(cfd,0);
}
}
3.3 acceptCommonHandler
// networking.c
static void acceptCommonHandler(int fd, int flags) {
redisClient *c;
if ((c = createClient(fd)) == NULL) {
redisLog(REDIS_WARNING,
"Error registering fd event for the new client: %s (fd=%d)",
strerror(errno),fd);
close(fd); /* May be already closed, just ignore errors */
return;
}
...
}
3.4 createClient
redisClient *createClient(int fd) {
redisClient *c = zmalloc(sizeof(redisClient));
// 绑定读事件 readQueryFromClient
if (aeCreateFileEvent(server.el,fd,AE_READABLE,
readQueryFromClient, c) == AE_ERR)
{
close(fd);
zfree(c);
return NULL;
}
}
3.5 小结
熟悉 epoll 的知道一般连接有 3 种类型的事件,连接事件、读事件、写事件,在结合上面的代码我们可以总结出以下知识点。
- redis 在初始化的时候会给每个连接事件绑定一个 acceptTcpHandler 方法。
- acceptTcpHandler 方法用来初始化连接,并且调用 createClient 来创建一个客户端信息。
- createClient 方法会给这个客户端的连接套接字绑定 readQueryFromClient 方法用来专门处理读信息。
4 主循环
4.1 aeMain
// ae.c
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS);
}
}
初始化全部结束之后,会进入 aeMain 方法进行主循环逻辑中,调用 aeProcessEvents 处理事件。
4.2 aeProcessEvents
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
{
...
// 如果是读事件,则调用读事件绑定方法
if (fe->mask & mask & AE_READABLE) {
rfired = 1;
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);
}
...
}
在主循环中,主要是调用 aeProcessEvents 来处理各种事件,里面关于每个连接的事件绑定的事件是通过 aeCreateFileEvent 方法来绑定的。
5 执行命令
5.1 获取输入内容
// networking.c
void readQueryFromClient(aeEventLoop *el, int fd, void *privdata, int mask) {
...
// 处理输入 buffer
processInputBuffer(c);
...
}
// networking.c
void processInputBuffer(redisClient *c) {
/* Keep processing while there is something in the input buffer */
while(sdslen(c->querybuf)) {
/* Multibulk processing could see a <= 0 length. */
if (c->argc == 0) {
resetClient(c);
} else {
/* Only reset the client when the command was executed. */
if (processCommand(c) == REDIS_OK)
resetClient(c);
/* freeMemoryIfNeeded may flush slave output buffers. This may result * into a slave, that may be the active client, to be freed. */
if (server.current_client == NULL) break;
}
}
}
5.2 处理命令
// redis.c
int processCommand(redisClient *c) {
...
// 获取命令
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
...
// 执行命令
call(c,REDIS_CALL_FULL);
}
// redis.c
void call(redisClient *c, int flags) {
...
c->cmd->proc(c);
...
}
6 总结
- 函数入口在 redis.c 的 main 方法中。
- 命令存储在 server.command 中。
- 连接事件会绑定 acceptTcpHandler 方法。
- 读事件会绑定 readQueryFromClient 方法。
- 整体流程:绑定连接事件、绑定读事件、触发读事件、分析输入内容、获取命令、找到命令对应执行方法、执行命令。
7 补充
本节主要是学习一个命令如何完整执行的,所以里面涉及的代码和方法比较多,里面还涉及了 epoll ,所以一时可能无法全部理解,不过具体的方法可以在后面慢慢细研究,今天主要是学习一个大概的流程。
边栏推荐
猜你喜欢

代码审查金字塔

Spark Learning Notes (VI) -- spark core core programming RDD action operator

网络安全/渗透测试工具AWVS14.9下载/使用教程/安装教程

Mysql: summary of common sub database and sub table schemes of Internet companies

Indexing best practices

Hcip 13th day notes

185. All employees with the top three highest wages in the Department (mandatory)

索引最佳实践

架构基本概念和架构本质

Portraiture5 new and upgraded leather filter plug-in artifact
随机推荐
Deep learning vocabulary embedded, beam search
Spark: calculate the average value of the same key in different partitions (entry level - simple implementation)
Unity game, the simplest solution of privacy agreement! Just 3 lines of code! (Reprinted)
unity游戏,隐私协议最简单解决方案!仅3行代码就搞定!(转载)
图解用户登录验证流程,写得太好了!
代码审查金字塔
Data Lake (20): Flink is compatible with iceberg, which is currently insufficient, and iceberg is compared with Hudi
记录一次,php程序访问系统文件访问错误的问题
mysql如何优化
最大连续子序列(DAY 77)
Quick sequencing and optimization
二叉树(DAY 82)
Best practices of opentelemetry in service grid architecture
Oracle有没有分布式数据库?
安全员及环保员岗位职责
微信小程序生成Excel
Practice of online problem feedback module (XV): realize the function of online updating feedback status
Add support for @data add-on in idea
【flask】服务端获取客户端的请求头信息
排列与二进制(吉,大)(DAY 84)