写点什么

Redis 源码剖析——客户端和服务器,springboot 入门程序

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:1820 字

    阅读完需:约 6 分钟

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 */


// 统计信息


long long microseconds, calls;


};

客户端的创建和关闭

当客户端向服务器发出 connect 请求的时候,服务器的事件处理器就会对这个事件进行处理,创建相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构 clients 链表的末尾


/*


* 创建一个新客户端


*/


redisClient *createClient(int fd){


// 分配空间


redisClient *c = zmalloc(sizeof(redisClient));


// 当 fd 不为 -1 时,创建带网络连接的客户端


// 如果 fd 为 -1 ,那么创建无网络连接的伪客户端


// 因为 Redis 的命令必须在客户端的上下文中使用,所以在执行 Lua 环境中的命令时


// 需要用到这种伪终端


if (fd != -1) {


// 非阻塞


anetNonBlock(NULL,fd);


// 禁用 Nagle 算法


anetEnableTcpNoDelay(NULL,fd);


// 设置 keep alive


if (server.tcpkeepalive)


anetKeepAlive(NULL,fd,server.tcpkeepalive);


// 绑定读事件到事件 loop (开始接收命令请求)


if (aeCreateFileEvent(server.el,fd,AE_READABLE,


readQueryFromClient, c) == AE_ERR)


{


close(fd);


zfree(c);


return NULL;


}


}


// 初始化各个属性


// 默认数据库


selectDb(c,0);


// 套接字


c->fd = fd;


...


listSetFreeMethod(c->pubsu


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


b_patterns,decrRefCountVoid);


listSetMatchMethod(c->pubsub_patterns,listMatchObjects);


// 如果不是伪客户端,那么添加到服务器的客户端链表中


if (fd != -1) listAddNodeTail(server.clients,c);


// 初始化客户端的事务状态


initClientMultiState(c);


// 返回客户端


return c;


}


对于客户端的启动程序,其大致的逻辑是:读取本地配置,连接服务器获取服务器的配置,获取本地输入的命令并发送到服务器


一个普通客户端可以因为多种原因而被关闭:


  • 如果客户端进程退出或者被杀死,那么客户端与服务器之间的网络连接将被关闭,从而造成客户端被关闭。

  • 如果客户端向服务器发送了带有不符合协议格式的命令请求,那么这个客户端也会被服务器关闭。

  • 如果客户端成为了 CLIENT KLLL 命令的目标,那么它也会被关闭。


关闭客户端的底层实现:


/*


* 释放客户端


*/


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


* accumulated arguments. */


// 关闭套接字,并从事件处理器中删除该套接字的事件


if (c->fd != -1) {


aeDeleteFileEvent(server.el,c->fd,AE_READABLE);


aeDeleteFileEvent(server.el,c->fd,AE_WRITABLE);


close(c->fd);


}


// 清空回复缓冲区


listRelease(c->reply);


// 清空命令参数

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
Redis源码剖析——客户端和服务器,springboot入门程序