Redis 源码剖析——客户端和服务器,springboot 入门程序
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
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);
// 清空命令参数
评论