【源码】Redis命令处理过程
本文基于社区版Redis 4.0.8
1、命令解析
void processInputBuffer(client *c) {
......
//循环遍历输入缓冲区,获取命令参数,调用processMultibulkBuffer解析命令参数和长度
while(sdslen(c->querybuf)) {
if (c->reqtype == PROTO_REQ_INLINE) {
if (processInlineBuffer(c) != C_OK) break;//处理telnet方式的内联命令
} else if (c->reqtype == PROTO_REQ_MULTIBULK) {
if (processMultibulkBuffer(c) != C_OK) break; //解析命令参数和长度暂存到客户端结构体中
} else {
serverPanic("Unknown request type");
}
}
}
//解析命令参数和长度暂存到客户端结构体中
int processMultibulkBuffer(client *c) {
......
//定位到行尾
newline = strchr(c->querybuf,'\r');
......
//解析命令请求参数数目,并存储在客户端对象的c->multibulklen字段
serverAssertWithInfo(c,NULL,c->querybuf[0] == '*');
ok = string2ll(c->querybuf+1,newline-(c->querybuf+1),&ll);
......
c->multibulklen = ll;
pos = (newline-c->querybuf)+2;//记录已解析命令的请求长度resp的长度
/* Setup argv array on client structure */
//分配请求参数存储空间
c->argv = zmalloc(sizeof(robj*)*c->multibulklen);
// 开始循环解析每个请求参数
while(c->multibulklen) {
......
newline = strchr(c->querybuf+pos,'\r');
if (c->querybuf[pos] != '$') {
return C_ERR;
}
ok = string2ll(c->querybuf+pos+1,newline-(c->querybuf+pos+1),&ll);
pos += newline-(c->querybuf+pos)+2;
c->bulklen = ll;//字符串参数长度暂存在客户端对象的bulklen字段
//读取该长度的参数内容,并创建字符串对象,同时更新待解析参数multibulklen
c->argv[c->argc++] =createStringObject(c->querybuf+pos,c->bulklen);
pos += c->bulklen+2;
c->multibulklen--;
}
}
2、命令调用
void processInputBuffer(client *c) {
......
//调用processCommand来处理命令
if (processCommand(c) == C_OK) {
......
}
}
//处理命令函数
int processCommand(client *c) {
//校验是否是quit命令
if (!strcasecmp(c->argv[0]->ptr,"quit")) {
addReply(c,shared.ok);
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
return C_ERR;
}
//调用lookupCommand,查看该命令是否存在
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
if (!c->cmd) {
flagTransaction(c);
addReplyErrorFormat(c,"unknown command '%s'",
(char*)c->argv[0]->ptr);
return C_OK;
}
//检查用户权限
if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
{
flagTransaction(c);
addReply(c,shared.noautherr);
return C_OK;
}
//还有很多检查,不一一列举,比如集群/持久化/复制等
......
/* 真正执行命令 */
if (c->flags & CLIENT_MULTI &&
c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
{
queueMultiCommand(c);
//将结果写入outbuffer
addReply(c,shared.queued);
}
}
// 调用execCommand执行命令
void execCommand(client *c) {
......
call(c,CMD_CALL_FULL);//调用call执行命令
......
}
//调用execCommand调用call执行命令
void call(client *c, int flags) {
......
start = ustime();
c->cmd->proc(c);//执行命令
duration = ustime()-start;
//如果是慢查询,记录慢查询
if (flags & CMD_CALL_SLOWLOG && c->cmd->proc != execCommand) {
char *latency_event = (c->cmd->flags & CMD_FAST) ?
"fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
//记录到慢日志中
slowlogPushEntryIfNeeded(c,c->argv,c->argc,duration);
}
//更新统计信息:当前命令执行时间和调用次数
if (flags & CMD_CALL_STATS) {
c->lastcmd->microseconds += duration;
c->lastcmd->calls++;
}
}
3、返回结果
int processCommand(client *c) {
......
//将结果写入outbuffer
addReply(c,shared.queued);
......
}
//将结果写入outbuffer
void addReply(client *c, robj *obj) {
//调用listAddNodeHead将客户端添加到服务端结构体的client_pending_write链表,以便后续能快速查找出哪些客户端有数据需要发送
if (prepareClientToWrite(c) != C_OK) return;
//然后添加字符串到输出缓冲区
if (_addReplyToBuffer(c,obj->ptr,sdslen(obj->ptr)) != C_OK)
//如果添加失败,则添加到输出链表中
_addReplyObjectToList(c,obj);
}
void beforeSleep(struct aeEventLoop *eventLoop) {
......
/* Handle writes with pending output buffers. */
handleClientsWithPendingWrites();
......
}
//回复客户端命令函数
int handleClientsWithPendingWrites(void) {
listIter li;
listNode *ln;
int processed = listLength(server.clients_pending_write);
listRewind(server.clients_pending_write,&li);
while((ln = listNext(&li))) {
client *c = listNodeValue(ln);
c->flags &= ~CLIENT_PENDING_WRITE;
listDelNode(server.clients_pending_write,ln);
/* 发送客户端数据 */
if (writeToClient(c->fd,c,0) == C_ERR) continue;
/* If there is nothing left, do nothing. Otherwise install
* the write handler. */
//如果数据量很大,一次性没有发送完成,则进行添加文件事件,监听当前客户端socket文件描述符的可写事件即可
if (clientHasPendingReplies(c) &&
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
sendReplyToClient, c) == AE_ERR)
{
freeClientAsync(c);
}
}
return processed;
}
【源码】Redis命令处理过程的更多相关文章
- 跟着大彬读源码 - Redis 3 - 服务器如何响应客户端请求?(下)
继续我们上一节的讨论.服务器启动了,客户端也发送命令了.接下来,就要到服务器"表演"的时刻了. 1 服务器处理 服务器读取到命令请求后,会进行一系列的处理. 1.1 读取命令请求 ...
- Redis 命令执行过程(上)
今天我们来了解一下 Redis 命令执行的过程.在之前的文章中<当 Redis 发生高延迟时,到底发生了什么>我们曾简单的描述了一条命令的执行过程,本篇文章展示深入说明一下,加深读者对 R ...
- Dubbo 源码分析 - 服务调用过程
注: 本系列文章已捐赠给 Dubbo 社区,你也可以在 Dubbo 官方文档中阅读本系列文章. 1. 简介 在前面的文章中,我们分析了 Dubbo SPI.服务导出与引入.以及集群容错方面的代码.经过 ...
- Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令
Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...
- SOFA 源码分析 —— 服务引用过程
前言 在前面的 SOFA 源码分析 -- 服务发布过程 文章中,我们分析了 SOFA 的服务发布过程,一个完整的 RPC 除了发布服务,当然还需要引用服务. So,今天就一起来看看 SOFA 是如何引 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- 源码分析HotSpot GC过程(一)
«上一篇:源码分析HotSpot GC过程(一)»下一篇:源码分析HotSpot GC过程(三):TenuredGeneration的GC过程 https://blogs.msdn.microsoft ...
- 源码分析HotSpot GC过程(三):TenuredGeneration的GC过程
老年代TenuredGeneration所使用的垃圾回收算法是标记-压缩-清理算法.在回收阶段,将标记对象越过堆的空闲区移动到堆的另一端,所有被移动的对象的引用也会被更新指向新的位置.看起来像是把杂陈 ...
- Redis 命令执行过程(下)
在上一篇文章中<Redis 命令执行过程(上)>中,我们首先了解 Redis 命令执行的整体流程,然后细致分析了从 Redis 启动到建立 socket 连接,再到读取 socket 数据 ...
- workerman源码分析之启动过程
PHP一直以来以草根示人,它简单,易学,被大量应用于web开发,非常可惜的是大部分开发都在简单的增删改查,或者加上pdo,redis等客户端甚至分布式,以及规避语言本身的缺陷.然而这实在太委屈PHP了 ...
随机推荐
- contrastive CAM
目录 概 主要内容 一个有趣的应用 > Prabhushankar M., Kwon G., Temel D. and AlRegib G. Contrastive explanation in ...
- Vue的安装及使用(Vue的三种安装使用方式)
vue是一个JavaMVVM库,是一套用于构建用户界面的渐进式框架,是初创项目的首选前端框架.它是以数据驱动和组件化的思想构建的,采用自底向上增量开发的设计.它是轻量级的,它有很多独立的功能或库,我们 ...
- Java Web程序设计笔记 • 【第7章 会话跟踪技术】
全部章节 >>>> 本章目录 7.1 会话跟踪技术概述 7.1.1 为什么需要会话跟踪 7.1.2 会话的状态和跟踪 7.1.3 会话跟踪技术 7.1.4 会话跟踪工作流程 ...
- Java初学者作业——添加程序断点,以Debug模式运行程序,观察变量的交换
返回本章节 返回作业目录 需求说明: 定义两个变量,分别用于存储学生的语文成绩和数学成绩,通过程序将这两个变量的值进行交换,输出变量之前的值和交换后变量中的值. 添加断点,观察变量值的交换过程. 实现 ...
- 浅议像素化与体素化Part.1——平面图形像素化
什么是像素化 学计算机的人往往都比较清楚图形和图像的区别,而且往往能够从数据结构的角度理解这两者的区别,一般来说,图形是由几何空间中的基本图元所组成,表现为用外部轮廓线条勾勒成的矢量图.例如由计算机绘 ...
- 深入浏览器工作原理和JS引擎(V8引擎为例)
浏览器工作原理和JS引擎 1.浏览器工作原理 在浏览器中输入查找内容,浏览器是怎样将页面加载出来的?以及JavaScript代码在浏览器中是如何被执行的? 大概流程可观察以下图: 首先,用户在浏览器搜 ...
- Oracle 查询NULL字段/空字符串
简单记录一下: 工作中需要查询某个字段值为空的数据, 最开始查询的时候按照以前的思路 : 1.where 字段名=NULL,点击F8,未查到结果: 2.where 字段名='',点击F8,未查到结果: ...
- PowerShell 教程
随笔分类 - 教程 转载自:https://www.cnblogs.com/XiaoCY/category/1065141.html PowerShell 管道符之Where-Object的使用方法 ...
- Docker 容器化的 SonarQube 服务在不同主机之间的迁移
安装 docker-ce // https://docs.docker.com/install/linux/docker-ce/centos/ $ sudo yum remove docker \ d ...
- SQLAlchemy完全入门
最近想要学习SQLAlchemy, 发现网上的中文文档大多是机翻的, 读起来特别变扭, 因此对照着最新的英文文档梳理了一遍, 写下来记录一下 目前SQLAlchemy的版本为1.4.x, 风格处于1. ...