当前位置:网站首页>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 ,所以一时可能无法全部理解,不过具体的方法可以在后面慢慢细研究,今天主要是学习一个大概的流程。
边栏推荐
- 阿里 Seata 新版本终于解决了 TCC 模式的幂等、悬挂和空回滚问题
- JMeter distributed pressure measurement
- 若依框架代码生成详解
- Volatile keyword and its function
- 正方形数组的数目(DAY 81)
- 数据库概论 - MySQL的简单介绍
- mysql出现不存在错误
- Unity game, the simplest solution of privacy agreement! Just 3 lines of code! (Reprinted)
- Spark Learning Notes (VI) -- spark core core programming RDD action operator
- 【常用搜索问题】111
猜你喜欢

Worthington木瓜蛋白酶解离系统解决方案

图解用户登录验证流程,写得太好了!

Win10/win11 lossless expansion of C disk space, cross disk consolidation of C and e disks

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

Worthington果胶酶的特性及测定方案

Plato farm has a new way of playing, and the arbitrage eplato has secured super high returns

vector 转 svg 方法

Comprehensive care analysis lyriq Ruige battery safety design

若依框架代码生成详解

Boom 3D new 2022 audio enhancement app
随机推荐
《稻盛和夫给年轻人的忠告》阅读笔记
Common events of window objects
How many implementation postures of delay queue? Daily essential skills!
客户案例 | 关注老年用户体验,银行APP适老化改造要避虚就实
字节一面:TCP 和 UDP 可以使用同一个端口吗?
带你了解什么是 Web3.0
impala 执行计划详解
Best practices of opentelemetry in service grid architecture
SAFE-ARC/WARNER电源维修XENON氙灯电源维修分析
redis入门练习
【flask】服务端获取客户端的请求头信息
[flask] the server obtains the request header information of the client
DTS搭载全新自研内核,突破两地三中心架构的关键技术|腾讯云数据库
Yilingsi T35 FPGA drives LVDS display screen
Boom 3D new 2022 audio enhancement app
Explain工具实际操作
pip3 设置阿里云
Database usage security policy
延时队列的几种实现姿势?日常必备技能!
Deep learning vocabulary embedded, beam search