zoukankan      html  css  js  c++  java
  • Memcached源码分析——process_command函数解析

    以下为个人笔记

    /**
     * process_command 在memcached中是用来处理用户发送的命令的,
     * 包括get set,add,delete,replace,stats,flush_all等常用的和不常用的命令,全在这进行处理的。
     * 一共有两个参数,conn *c 和字符串指针 char *command ;
     * 关于conn结构体就先不说了,反正它是整个memcached中最重要的结构体就行了,等以后研究明白了再说,先从整体上领会精神吧。
     * 这里我想说一下的是, memcached 和 redis 在处理命令上的想法还是有很大差别的,
     * 在 redis 里面,你要是想看一下一共支持多少命令,每个命令对应的函数,很方便,都在一个名叫 redisCommandTable 的结构体数组里面,一目了然;
     * 但是 memcached 却不是这样,我刚开始也是按照看 redis 源码的方式去找 memcached 中的,但是找了很久也没有发现,原因就是作者把支持的所有命令都散落在下面这个函数中了。
     * 先说一下 memcached 是怎么从 command 字符串中分解出具体的命令和对应的参数的。
     * 在函数中用到了一个结构体数组: tokens[MAX_TOKENS] ,它其实是用来存放分析完的 command 结果用的,分析工作在函数 tokenize_command 中进行。
     * 如:有一个命令 "get aaaaaaaaaa" ,分析完以后存在tokens中的就是
     * tokens[3] = {{value:"get",length:3},{value:"aaaaaaaaaa",length:10},{value:NULL,length:0}};
     * 函数 tokenize_command 返回的是一个int型数据 ntokens,记录了 tokens 的大小,表示从 command 命令中分解出了几条数据,
     * 当然 ntokens 的值会比实际中 command 中包含的数据多1,因为字符串结尾的''也要占一样。
     * 
     * 下面说说以 "get aaaaaaaaaa" 命令为例,具体的命令分析和函数调用过程:
     * 当一条命令 "get aaaaaaaaaa" 传到 process_command 中之后,先调用负责解析命令的函数tokenize_command,
     * 将解析后的命令存储在tokens数组中,结果如上面的tokens[3],并返回ntokens,说明command中包含几个字段(这里得到的是3),
     * 然后根据字段的数目进行判断应该到哪个条件语句去进行比对,当确认之后,就会跳到对应的条件语句中
     * 所以这里应该到 tokenize_command 下面的第一个if语句中,然后使用tokens[0].value,也就是tokens数组中存储的get命令和字符串"get"进行比较,
     * 匹配,则调用对应的函数,这里调用process_get_command(c, tokens, ntokens, false);
     * 然后 process_command 的使命就结束了。
     * 以下代码在memcached-1.4.22/memcached.c
     */
    static void process_command(conn *c, char *command) {
    
        token_t tokens[MAX_TOKENS];
        size_t ntokens;
        int comm;
    
        assert(c != NULL);
    
        MEMCACHED_PROCESS_COMMAND_START(c->sfd, c->rcurr, c->rbytes);
    
        if (settings.verbose > 1)
            fprintf(stderr, "<%d %s
    ", c->sfd, command);
    
        /*
         * for commands set/add/replace, we build an item and read the data
         * directly into it, then continue in nread_complete().
         */
    
        c->msgcurr = 0;
        c->msgused = 0;
        c->iovused = 0;
        if (add_msghdr(c) != 0) {
            out_of_memory(c, "SERVER_ERROR out of memory preparing response");
            return;
        }
    
        ntokens = tokenize_command(command, tokens, MAX_TOKENS);
        if (ntokens >= 3 &&
            ((strcmp(tokens[COMMAND_TOKEN].value, "get") == 0) ||
             (strcmp(tokens[COMMAND_TOKEN].value, "bget") == 0))) {
    
            process_get_command(c, tokens, ntokens, false);
    
        } else if ((ntokens == 6 || ntokens == 7) &&
                   ((strcmp(tokens[COMMAND_TOKEN].value, "add") == 0 && (comm = NREAD_ADD)) ||
                    (strcmp(tokens[COMMAND_TOKEN].value, "set") == 0 && (comm = NREAD_SET)) ||
                    (strcmp(tokens[COMMAND_TOKEN].value, "replace") == 0 && (comm = NREAD_REPLACE)) ||
                    (strcmp(tokens[COMMAND_TOKEN].value, "prepend") == 0 && (comm = NREAD_PREPEND)) ||
                    (strcmp(tokens[COMMAND_TOKEN].value, "append") == 0 && (comm = NREAD_APPEND)) )) {
    
            process_update_command(c, tokens, ntokens, comm, false);
    
        } else if ((ntokens == 7 || ntokens == 8) && (strcmp(tokens[COMMAND_TOKEN].value, "cas") == 0 && (comm = NREAD_CAS))) {
    
            process_update_command(c, tokens, ntokens, comm, true);
    
        } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "incr") == 0)) {
    
            process_arithmetic_command(c, tokens, ntokens, 1);
    
        } else if (ntokens >= 3 && (strcmp(tokens[COMMAND_TOKEN].value, "gets") == 0)) {
    
            process_get_command(c, tokens, ntokens, true);
    
        } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "decr") == 0)) {
    
            process_arithmetic_command(c, tokens, ntokens, 0);
    
        } else if (ntokens >= 3 && ntokens <= 5 && (strcmp(tokens[COMMAND_TOKEN].value, "delete") == 0)) {
    
            process_delete_command(c, tokens, ntokens);
    
        } else if ((ntokens == 4 || ntokens == 5) && (strcmp(tokens[COMMAND_TOKEN].value, "touch") == 0)) {
    
            process_touch_command(c, tokens, ntokens);
    
        } else if (ntokens >= 2 && (strcmp(tokens[COMMAND_TOKEN].value, "stats") == 0)) {
    
            process_stat(c, tokens, ntokens);
    
        } else if (ntokens >= 2 && ntokens <= 4 && (strcmp(tokens[COMMAND_TOKEN].value, "flush_all") == 0)) {
            time_t exptime = 0;
    
            set_noreply_maybe(c, tokens, ntokens);
    
            pthread_mutex_lock(&c->thread->stats.mutex);
            c->thread->stats.flush_cmds++;
            pthread_mutex_unlock(&c->thread->stats.mutex);
    
            if (!settings.flush_enabled) {
                // flush_all is not allowed but we log it on stats
                out_string(c, "CLIENT_ERROR flush_all not allowed");
                return;
            }
    
            if(ntokens == (c->noreply ? 3 : 2)) {
                settings.oldest_live = current_time - 1;
                item_flush_expired();
                out_string(c, "OK");
                return;
            }
    
            exptime = strtol(tokens[1].value, NULL, 10);
            if(errno == ERANGE) {
                out_string(c, "CLIENT_ERROR bad command line format");
                return;
            }
    
            /*
              If exptime is zero realtime() would return zero too, and
              realtime(exptime) - 1 would overflow to the max unsigned
              value.  So we process exptime == 0 the same way we do when
              no delay is given at all.
            */
            if (exptime > 0)
                settings.oldest_live = realtime(exptime) - 1;
            else /* exptime == 0 */
                settings.oldest_live = current_time - 1;
            item_flush_expired();
            out_string(c, "OK");
            return;
    
        } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "version") == 0)) {
    
            out_string(c, "VERSION " VERSION);
    
        } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "quit") == 0)) {
    
            conn_set_state(c, conn_closing);
    
        } else if (ntokens == 2 && (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0)) {
    
            if (settings.shutdown_command) {
                conn_set_state(c, conn_closing);
                raise(SIGINT);
            } else {
                out_string(c, "ERROR: shutdown not enabled");
            }
    
        } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "slabs") == 0) {
            if (ntokens == 5 && strcmp(tokens[COMMAND_TOKEN + 1].value, "reassign") == 0) {
                int src, dst, rv;
    
                if (settings.slab_reassign == false) {
                    out_string(c, "CLIENT_ERROR slab reassignment disabled");
                    return;
                }
    
                src = strtol(tokens[2].value, NULL, 10);
                dst = strtol(tokens[3].value, NULL, 10);
    
                if (errno == ERANGE) {
                    out_string(c, "CLIENT_ERROR bad command line format");
                    return;
                }
    
                rv = slabs_reassign(src, dst);
                switch (rv) {
                case REASSIGN_OK:
                    out_string(c, "OK");
                    break;
                case REASSIGN_RUNNING:
                    out_string(c, "BUSY currently processing reassign request");
                    break;
                case REASSIGN_BADCLASS:
                    out_string(c, "BADCLASS invalid src or dst class id");
                    break;
                case REASSIGN_NOSPARE:
                    out_string(c, "NOSPARE source class has no spare pages");
                    break;
                case REASSIGN_SRC_DST_SAME:
                    out_string(c, "SAME src and dst class are identical");
                    break;
                }
                return;
            } else if (ntokens == 4 &&
                (strcmp(tokens[COMMAND_TOKEN + 1].value, "automove") == 0)) {
                process_slabs_automove_command(c, tokens, ntokens);
            } else {
                out_string(c, "ERROR");
            }
        } else if (ntokens > 1 && strcmp(tokens[COMMAND_TOKEN].value, "lru_crawler") == 0) {
            if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "crawl") == 0) {
                int rv;
                if (settings.lru_crawler == false) {
                    out_string(c, "CLIENT_ERROR lru crawler disabled");
                    return;
                }
    
                rv = lru_crawler_crawl(tokens[2].value);
                switch(rv) {
                case CRAWLER_OK:
                    out_string(c, "OK");
                    break;
                case CRAWLER_RUNNING:
                    out_string(c, "BUSY currently processing crawler request");
                    break;
                case CRAWLER_BADCLASS:
                    out_string(c, "BADCLASS invalid class id");
                    break;
                }
                return;
            } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "tocrawl") == 0) {
                uint32_t tocrawl;
                 if (!safe_strtoul(tokens[2].value, &tocrawl)) {
                    out_string(c, "CLIENT_ERROR bad command line format");
                    return;
                }
                settings.lru_crawler_tocrawl = tocrawl;
                out_string(c, "OK");
                return;
            } else if (ntokens == 4 && strcmp(tokens[COMMAND_TOKEN + 1].value, "sleep") == 0) {
                uint32_t tosleep;
                if (!safe_strtoul(tokens[2].value, &tosleep)) {
                    out_string(c, "CLIENT_ERROR bad command line format");
                    return;
                }
                if (tosleep > 1000000) {
                    out_string(c, "CLIENT_ERROR sleep must be one second or less");
                    return;
                }
                settings.lru_crawler_sleep = tosleep;
                out_string(c, "OK");
                return;
            } else if (ntokens == 3) {
                if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "enable") == 0)) {
                    if (start_item_crawler_thread() == 0) {
                        out_string(c, "OK");
                    } else {
                        out_string(c, "ERROR failed to start lru crawler thread");
                    }
                } else if ((strcmp(tokens[COMMAND_TOKEN + 1].value, "disable") == 0)) {
                    if (stop_item_crawler_thread() == 0) {
                        out_string(c, "OK");
                    } else {
                        out_string(c, "ERROR failed to stop lru crawler thread");
                    }
                } else {
                    out_string(c, "ERROR");
                }
                return;
            } else {
                out_string(c, "ERROR");
            }
        } else if ((ntokens == 3 || ntokens == 4) && (strcmp(tokens[COMMAND_TOKEN].value, "verbosity") == 0)) {
            process_verbosity_command(c, tokens, ntokens);
        } else {
            out_string(c, "ERROR");
        }
        return;
    }

    以上为个人笔记

  • 相关阅读:
    C# 解析json
    Java环境配置
    Wpf学习(五) 数据绑定Binding【小达原创】
    WPF学习(四) 样式 【小达原创】
    jQuery-- 格式化时间
    leetcode -- Nim Game
    Git 学习笔记--拉取远程分支到本地
    Git 学习笔记--删除错误提交的commit
    iOS开发-- 开发中遇到的问题汇总
    Javascript--数组转换成字符串
  • 原文地址:https://www.cnblogs.com/lrxing/p/4273387.html
Copyright © 2011-2022 走看看