(三十三)— redis-cli.c客户端命令行接口的实现(2)
最后更新于:2022-04-01 20:21:26
今天学习完了命令行客户端的后续内容,整体感觉就是围绕着2个东西转,config和mode。为什么我会这么说呢,请继续往下看,客户端中的配置结构体和之前我们所学习的配置结构体,不是指的同一个概念,cli中的结构体除了基本的ip,Port端口号,还有就是各种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;
//首先初始化客户端配置操作
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 Server hostname (default: 127.0.0.1).\n"
" -p Server port (default: 6379).\n"
" -s Server socket (overrides hostname and port).\n"
" -a Password to use when connecting to the server.\n"
" -r Execute specified command N times.\n"
" -i When -r is used, waits seconds per command.\n"
" It is possible to specify sub-second times like -i 0.1.\n"
" -n Database number.\n"
" -x Read last argument from STDIN.\n"
" -d 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 Transfer an RDB dump from remote server to local file.\n"
" --pipe Transfer raw Redis protocol from stdin to server.\n"
" --pipe-timeout In --pipe mode, abort with error if after sending all data.\n"
" no reply is received within 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 Useful with --scan to specify a SCAN pattern.\n"
" --intrinsic-latency Run a test to measure intrinsic system latency.\n"
" The test will run for the specified amount of seconds.\n"
" --eval Send an EVAL command using the Lua script at .\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的系统中。
';