Redis服务器是典型的一对多服务器程序:一个服务器可以与多个客户端建立网络连接。这篇文章将通过源码看看客户端和服务器的底层数据结构和工作过程
在Redis这种一对多的服务模式下,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的命令请求,并向客户端返回命令回复。通过使用由I/O多路复用技术实现的文件事件处理器,Redis服务器使用单线程单进程的方式来处理命令请求,并与多个客户端进行网络通信。
客户端
客户端数据结构
客户端底层的数据结构如下:
typedef struct redisClient {
uint64_t> Client incremental unique>/
// 套接字描述符
int fd;
redisDb db;
int dictid;
// 客户端名字
robj name; / As set by CLIENT SETNAME /
// 输入缓冲区,保存客户端发送的命令请求
sds querybuf;
size_t querybuf_peak; / Recent (100ms or more) peak of querybuf>/
// 命令和命令参数
int argc;
robj *argv;
// 命令实现函数字典
struct redisCommand cmd, lastcmd;
int reqtype;
int multibulklen; / number of multi bulk arguments left to read /
long bulklen; / length of bulk argument in multi bulk request /
list reply;
unsigned long reply_bytes; / Tot bytes of objects in reply list /
int sentlen; / Amount of bytes already sent in the current
buffer or object being sent. /
// 创建客户端时间
time_t ctime; / Client creation time /
// 客户端和服务器最后一次进行互动的时间
time_t lastinteraction; / time of the last interaction, used for timeout /
time_t obuf_soft_limit_reached_time;
// 标志,记录客户端的角色
int flags; / REDIS_SLAVE | REDIS_MONITOR | REDIS_MULTI ... /
// 标志是否通过身份验证
int authenticated; / when requirepass is non-NULL /
... // 其他相关属性
/* Response buffer */
// 回应缓冲区
int bufpos;
char buf[REDIS_REPLY_CHUNK_BYTES];
} redisClient;
在客户端的各个属性中:
fd表示套接字描述符,伪客户端的fd属性的值为-1:伪客户端处理的命令请求来源于AOF文件或者Lua脚本,而不是网络,所以这种客户端不需要套接字连接;普通客户端的fd属性的值为大于-1的整数
命令和命令参数是对输入缓冲的命令进行解析以后获得命令和参数。
cmd 是命令的实现函数的数组,命令实现函数的结构如下:
struct redisCommand {
// 命令名称
char name;
// 命令执行函数
redisCommandProc proc;
// 参数个数
int arity;
// 字符串表示flag
char sflags; / Flags as string representation, one char per flag. /
// 实际flag
int flags; / The actual flags, obtained from the 'sflags' field. */
...
// 指定哪些参数是key
int firstkey; /* The first argument that's a key (0 = no keys) */
int lastkey; /* The last argument that's a key */
int keystep; /* The step between first and last key */
释放客户端 /
void freeClient(redisClient c){
listNode *ln;
...
/ Free the query buffer /
sdsfree(c->querybuf);
c->querybuf = NULL;
/ Deallocate structures used to block on blocking ops. /
if (c->flags & REDIS_BLOCKED) unblockClient(c);
dictRelease(c->bpop.keys);
/ UNWATCH all the keys /
// 清空 WATCH 信息
unwatchAllKeys(c);
listRelease(c->watched_keys);
/ Unsubscribe from all the pubsub channels /
// 退订所有频道和模式
pubsubUnsubscribeAllChannels(c,0);
pubsubUnsubscribeAllPatterns(c,0);
dictRelease(c->pubsub_channels);
listRelease(c->pubsub_patterns);
/* Close socket, unregister events, and remove list of replies and