栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

redis源码之server接受客户端请求并处理(7)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

redis源码之server接受客户端请求并处理(7)

1.处理客户端请求

在server.c 的main方法中,在启动的时候针对网卡的各个ip会创建文件事件用于监听tcp请求。

tcp请求会交给acceptTcpHandler去处理。而readQueryFromClient就是用来处理文件读事件处理的。

对于做java的人来说,其实跟netty里对于连接事件,读事件,写事件监听与处理的模式一样。


2.处理读事件readQueryFromClient

方法所在地:networking.c文件中的readQueryFromClient方法

处理流程:

  1. nread = read(fd, c->querybuf+qblen, readlen); 读取buffer数据
    1. nread为-1表示读取失败
    2. nread == 0 表示客户端连接已关闭
  2. sdsIncrLen(c->querybuf,nread); c→querybuf是客户端传递过来的命令字符串sds,这里nread就是读取的命令长度,这里其实就是len进行修复
  3. processInputBufferAndReplicate 处理客户端命令


3.processInputBuffer 处理客户端命令

processInputBufferAndReplicate 中使用 processInputBuffer 来进行处理命令,replicationFeedSlavesFromMasterStream 是用来从节点进行同步的。

void processInputBufferAndReplicate(client *c) {
    if (!(c->flags & CLIENT_MASTER)) {
				// 处理请求
        processInputBuffer(c);
    } else {
        size_t prev_offset = c->reploff;
        processInputBuffer(c);
        size_t applied = c->reploff - prev_offset;
        if (applied) {
            // 从节点复制
						replicationFeedSlavesFromMasterStream(server.slaves,
                    c->pending_querybuf, applied);
            sdsrange(c->pending_querybuf,applied,-1);
        }
    }
}


4.processInputBuffer 处理命令

这里需要注意的是,请求处理分为3种

  1. PROTO_REQ_INLINE 处理单行命令(将resp命令拆解为命令数组)
  2. PROTO_REQ_MULTIBULK 处理多行命令(将resp命令拆解为命令数组)
  3. processCommand 处理命令
    1. strcasecmp(c->argv[0]->ptr,"quit") 校验是不是退出命令
    2. lookupCommand(c->argv[0]->ptr) 寻找命令
    3. 校验命令参数的个数与传递过来的命令是否匹配
    4. 校验是否授权
    5. 校验可用内存
    6. 磁盘写权限校验
    7. 从节点存活校验
    8. 是否只有只读从节点校验
    9. 发布订阅命令集校验
    10. lua脚本校验
    11. 执行命令
      1. queueMultiCommand 命令集处理
      2. call(c,CMD_CALL_FULL); 单命令处理


5.查找命令lookupCommand
struct redisCommand *lookupCommand(sds name) {
		// server.commands字典中获取名为name的redisCommand
    return dictFetchValue(server.commands, name);
}

server.commands 又是怎么来的呢?

// 创建数据字典
server.commands = dictCreate(&commandTableDictType,NULL);
// 往里面塞命令
populateCommandTable();

在populateCommandTable方法中,读取redisCommandTable 中的命令对象一一加入server.commands中

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;

        while(*f != '') {
            switch(*f) {
            case 'w': c->flags |= CMD_WRITE; break;
            case 'r': c->flags |= CMD_READONLY; break;
            case 'm': c->flags |= CMD_DENYOOM; break;
            case 'a': c->flags |= CMD_ADMIN; break;
            case 'p': c->flags |= CMD_PUBSUB; break;
            case 's': c->flags |= CMD_NOSCRIPT; break;
            case 'R': c->flags |= CMD_RANDOM; break;
            case 'S': c->flags |= CMD_SORT_FOR_SCRIPT; break;
            case 'l': c->flags |= CMD_LOADING; break;
            case 't': c->flags |= CMD_STALE; break;
            case 'M': c->flags |= CMD_SKIP_MONITOR; break;
            case 'k': c->flags |= CMD_ASKING; break;
            case 'F': c->flags |= CMD_FAST; break;
            default: serverPanic("Unsupported command flag"); break;
            }
            f++;
        }
        //向字典添加数据
        retval1 = dictAdd(server.commands, sdsnew(c->name), c);
        
        retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
        serverAssert(retval1 == DICT_OK && retval2 == DICT_OK);
    }
}


6.单命令处理call(c,CMD_CALL_FULL)

核心代码:

dirty = server.dirty; // 上次进行AOF存储后进行修改的数量,主要是用来辅助AOF存储的。
start = ustime(); // 开始执行命令时间
c->cmd->proc(c); // 处理命令
duration = ustime()-start; // 命令处理结束
dirty = server.dirty-dirty;  // 计算改变的数量

其实就是调用了redisCommand的proc函数来处理命令了。

这里不得不说一下redisCommand结构,每个命令都封装在这个结构体中。可以看一下第5条,命令

struct redisCommand {
	  //命令名 比如SET,GET这些的
	  char *name;
	  //命令处理函数
    redisCommandProc *proc;
    
    int arity;
    
    char *sflags; 
    
    int flags;    
		
    

    redisGetKeysProc *getkeys_proc; // 获取key的函数,当下面三种情况都无法获取到key时,使用这个函数
    
    int firstkey; 
    int lastkey;  
    int keystep;  
    //从服务器启动至今命令的执行时间  从服务器启动至今命令的执行次数
    long long microseconds, calls;
};


7.小结

这里解析到了执行redisCommand的proc函数来执行实际命令,那么接下来需要阅读的就是对各个命令源码的解析和数据结构的使用。今天就先到这了,下班~

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/877106.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号