Redis源代码分析(三十三)--- redis-cli.cclient命令行接口的实现(2)
今天学习完了命令行client的兴许内容,总体感觉就是环绕着2个东西转,config和mode。为什么我会这么说呢,请继续往下看,client中的配置结构体和之前我们所学习的配置结构体,不是指的同一个概念。cli中的结构体除了主要的ip,Portport号,还有就是各种mode的配置了。
/* Redis配置结构体 */ static struct config { char *hostip; int hostport; char *hostsocket; long repeat; long interval; int dbnum; int interactive; int shutdown; int monitor_mode; int pubsub_mode; int latency_mode; int latency_history; int cluster_mode; int cluster_reissue_command; int slave_mode; int pipe_mode; int pipe_timeout; int getrdb_mode; int stat_mode; int scan_mode; int intrinsic_latency_mode; int intrinsic_latency_duration; char *pattern; char *rdb_filename; int bigkeys; int stdinarg; /* get last arg from stdin. (-x option) */ char *auth; int output; /* output mode, see OUTPUT_* defines */ sds mb_delim; char prompt[128]; char *eval; int last_cmd_type; } config;里面少说也有10个mode模式了吧。我们先倒过来,看看cli的主程序执行的流程。也就是main函数的执行步骤:
/*main函数主程序操作*/ int main(int argc, char **argv) { int firstarg; //首先初始化client配置操作 config.hostip = sdsnew("127.0.0.1"); config.hostport = 6379; config.hostsocket = NULL; config.repeat = 1; config.interval = 0; config.dbnum = 0; config.interactive = 0; config.shutdown = 0; config.monitor_mode = 0; config.pubsub_mode = 0; config.latency_mode = 0; config.latency_history = 0; config.cluster_mode = 0; config.slave_mode = 0; config.getrdb_mode = 0; config.stat_mode = 0; config.scan_mode = 0; config.intrinsic_latency_mode = 0; config.pattern = NULL; config.rdb_filename = NULL; config.pipe_mode = 0; config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT; config.bigkeys = 0; config.stdinarg = 0; config.auth = NULL; config.eval = NULL; config.last_cmd_type = -1; if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL)) config.output = OUTPUT_RAW; else config.output = OUTPUT_STANDARD; config.mb_delim = sdsnew("\n"); cliInitHelp(); //依据用户输入的參数,配置config firstarg = parseOptions(argc,argv); argc -= firstarg; argv += firstarg; //配置设置完成,依据配置中的模式设置,调用对应的mode方法 /* Latency mode */ if (config.latency_mode) { if (cliConnect(0) == REDIS_ERR) exit(1); latencyMode(); } /* Slave mode */ if (config.slave_mode) { if (cliConnect(0) == REDIS_ERR) exit(1); slaveMode(); } ....后面的代码与此同样。所以就省略了,步骤简单来说。就是设置配置。依据配置启动对应的模式,以下说说,里面的主要几种模式
1.statMode:
/* statMode主要输出一些读取数据统计的一些信息 */ static void statMode(void) { redisReply *reply; long aux, requests = 0; int i = 0; while(1) { char buf[64]; int j; reply = reconnectingInfo(); if (reply->type == REDIS_REPLY_ERROR) { printf("ERROR: %s\n", reply->str); exit(1); } if ((i++ % 20) == 0) { printf( "------- data ------ --------------------- load -------------------- - child -\n" "keys mem clients blocked requests connections \n"); } /* Keys */ aux = 0; for (j = 0; j < 20; j++) { long k; sprintf(buf,"db%d:keys",j); k = getLongInfoField(reply->str,buf); if (k == LONG_MIN) continue; aux += k; } sprintf(buf,"%ld",aux); printf("%-11s",buf); /* Used memory */ aux = getLongInfoField(reply->str,"used_memory"); bytesToHuman(buf,aux); printf("%-8s",buf); /* Clients */ aux = getLongInfoField(reply->str,"connected_clients"); sprintf(buf,"%ld",aux); printf(" %-8s",buf); /* Blocked (BLPOPPING) Clients */ aux = getLongInfoField(reply->str,"blocked_clients"); sprintf(buf,"%ld",aux); printf("%-8s",buf); ....客户端当前的数据统计信息。
2.latencyMode中会用到的測试硬件计算性能的方法:
/* This is just some computation the compiler can't optimize out. * Should run in less than 100-200 microseconds even using very * slow hardware. Runs in less than 10 microseconds in modern HW. */ /* 普通的计算操作。測试硬件计算的速度快慢 */ unsigned long compute_something_fast(void) { unsigned char s[256], i, j, t; int count = 1000, k; unsigned long output = 0; for (k = 0; k < 256; k++) s[k] = k; i = 0; j = 0; while(count--) { i++; j = j + s[i]; t = s[i]; s[i] = s[j]; s[j] = t; output += s[(s[i]+s[j])&255]; } return output; }帮助命令的输出文档是由以下的函数输出的:
/* 帮助命令的输出文档 */ static void usage(void) { sds version = cliVersion(); fprintf(stderr, "redis-cli %s\n" "\n" "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n" " -h <hostname> Server hostname (default: 127.0.0.1).\n" " -p <port> Server port (default: 6379).\n" " -s <socket> Server socket (overrides hostname and port).\n" " -a <password> Password to use when connecting to the server.\n" " -r <repeat> Execute specified command N times.\n" " -i <interval> When -r is used, waits <interval> seconds per command.\n" " It is possible to specify sub-second times like -i 0.1.\n" " -n <db> Database number.\n" " -x Read last argument from STDIN.\n" " -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n" " -c Enable cluster mode (follow -ASK and -MOVED redirections).\n" " --raw Use raw formatting for replies (default when STDOUT is\n" " not a tty).\n" " --no-raw Force formatted output even when STDOUT is not a tty.\n" " --csv Output in CSV format.\n" " --latency Enter a special mode continuously sampling latency.\n" " --latency-history Like --latency but tracking latency changes over time.\n" " Default time interval is 15 sec. Change it using -i.\n" " --slave Simulate a slave showing commands received from the master.\n" " --rdb <filename> Transfer an RDB dump from remote server to local file.\n" " --pipe Transfer raw Redis protocol from stdin to server.\n" " --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n" " no reply is received within <n> seconds.\n" " Default timeout: %d. Use 0 to wait forever.\n" " --bigkeys Sample Redis keys looking for big keys.\n" " --scan List all keys using the SCAN command.\n" " --pattern <pat> Useful with --scan to specify a SCAN pattern.\n" " --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n" " The test will run for the specified amount of seconds.\n" " --eval <file> Send an EVAL command using the Lua script at <file>.\n" " --help Output this help and exit.\n" " --version Output version and exit.\n" "\n" "Examples:\n" " cat /etc/passwd | redis-cli -x set mypasswd\n" " redis-cli get mypasswd\n" " redis-cli -r 100 lpush mylist x\n" " redis-cli -r 100 -i 1 info | grep used_memory_human:\n" " redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n" " redis-cli --scan --pattern '*:12345*'\n" "\n" " (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n" "\n" "When no command is given, redis-cli starts in interactive mode.\n" "Type \"help\" in interactive mode for information on available commands.\n" "\n", version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT); sdsfree(version); exit(1); }在命令里面,会因为2个概念,1个叫一般性的Command命令另一个是CommandGroup命令组的概念。举个样例,比方list。set等常常会用到的一些命令,后面能够接好多种參数的命令。属性命令组命令。一般CONFIG GET,这样的功能非常单一的命令我们就叫他为普通的命令,Dump,Exist啊等等这些命令都是普通的命令,CommandGroup的命令不是非常多就以下这么几个:
/* 全部的命令组 */ static char *commandGroups[] = { "generic", "string", "list", "set", "sorted_set", "hash", "pubsub", "transactions", "connection", "server", "scripting", "hyperloglog" };也是最最经常使用的命令。在redis的系统中。