未加星标

redis-cli源码分析 数据库 数据库学习 Redis cli源码分析

字体大小 | |
[数据库(综合) 所属分类 数据库(综合) | 发布者 店小二04 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

redis-cli源码分析,redis-cli中有两个重要的结构体,一个是redisContext与一个是config

typedef struct redisContext {
int err; /* Error flags, 0 when there is no error */
char errstr[128]; /* String representation of error when applicable */
int fd;//socket fd
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
} redisContext;
redisContext跟redis-cli与redis server的tcp连接相关配置
config是redis-cli运行的配置文件,有初始化默认配置,也可以通过运行的时候传递参数修改配置
static struct config {
aeEventLoop *el;
const char *hostip;
int hostport;
const char *hostsocket;
int numclients;
int liveclients;
int requests;
int requests_issued;
int requests_finished;
int keysize;
int datasize;
int randomkeys;
int randomkeys_keyspacelen;
int keepalive;
int pipeline;
int showerrors;
long long start;
long long totlatency;
long long *latency;
const char *title;
list *clients;
int quiet;
int csv;
int loop;
int idlemode;
int dbnum;
sds dbnumstr;
char *tests;
char *auth;
} config;
在main函数中,首先对config结构体中的配置进行初始化。
其中,config结构体中sds是redis自定义的字符串数据结构,定义为typedef char* sds
配置spectrum_palette和spectrum_palette size两个全局变量怀疑与cli命令行中的提示颜色有关
使用isatty函数判断是否连接的是终端,从而配置config.output为OUTPUT_RAW或者OUTPUT_STANDARD
使用parseOption(argc,argv)通过运行程序携带参数对config进行进一步配置
config配置完后开始根据配置配模式
1. latency mode
2. latency distribution mode
3. slave mode
4. get RDB mode
5. Pipe mode
6. stat mode
7. scan mode
8. LRU mode
9. Intrinsic latency mode
如果没有任何附加命令,则运行默认的交互模式(interactive mode)
/* Ignore SIGPIPE in interactive mode to force a reconnect */
signal(SIGPIPE, SIG_IGN);//忽略sigpipe,如果尝试连接两次已经关闭的socket会导致sigpipe,告诉进程这个连接已经断开了,不要再写了,一般客户端默认退出,但是此处忽略SIGPIPE信号
/* Note that in repl mode we don’t abort on connection error.
* A new attempt will be performed for every command send. */
cliConnect(0);
repl();
使用cliConnect(0)连接redis server服务器
cliConnect(0)中,使用anetKeepAlive运用TCP_KEEPALIVE选项防止超时
连接上redis server之后,调用repl(),开始读取stdin运行redis命令
redpl中,首先设置是否接收多命令形式,然后初始化设置处理commond 命令行的回调函数
通过循环调用linenoise函数,得到输入的commond命令
linenoise有三种模式noTTY,unSuportedTerm,raw模式,一般使用的是raw模式
在linenoiseraw模式中,使用linenoiseEdit函数对每次输入进行处理,从而达到较为友好的交互
static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt)
{
struct linenoiseState l;
/* Populate the linenoise state that we pass to functions implementing
* specific editing functionalities. */
l.ifd = stdin_fd;
l.ofd = stdout_fd;
l.buf = buf;
l.buflen = buflen;
l.prompt = prompt;
l.plen = strlen(prompt);
l.oldpos = l.pos = 0;
l.len = 0;
l.cols = getColumns(stdin_fd, stdout_fd);
l.maxrows = 0;
l.history_index = 0;
/* Buffer starts empty. */
l.buf[0] = '\0';
l.buflen--; /* Make sure there is always space for the nulterm */
/* The latest history entry is always our current buffer, that
* initially is just an empty string. */
linenoiseHistoryAdd("");
if (write(l.ofd,prompt,l.plen) == -1) return -1;
while(1) {
char c;
int nread;
char seq[3];
nread = read(l.ifd,&c,1);
if (nread <= 0) return l.len;
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == 9 && completionCallback != NULL) {c = completeLine(&l);/* Return on errors */if (c < 0) return l.len;/* Read next character when 0 */if (c == 0) continue;
}
switch(c) {
case ENTER: /* enter */history_len--;free(history[history_len]);if (mlmode) linenoiseEditMoveEnd(&l);if (hintsCallback) { /* Force a refresh without hints to leave the previous * line as the user typed it after a newline. */ linenoiseHintsCallback *hc = hintsCallback; hintsCallback = NULL; refreshLine(&l); hintsCallback = hc;}return (int)l.len;
case CTRL_C: /* ctrl-c */errno = EAGAIN;return -1;
case BACKSPACE: /* backspace */
case 8: /* ctrl-h */linenoiseEditBackspace(&l);break;
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */if (l.len > 0) { linenoiseEditDelete(&l);} else { history_len--; free(history[history_len]); return -1;}break;
case CTRL_T: /* ctrl-t, swaps current character with previous. */if (l.pos > 0 && l.pos < l.len) { int aux = buf[l.pos-1]; buf[l.pos-1] = buf[l.pos]; buf[l.pos] = aux; if (l.pos != l.len-1) l.pos++; refreshLine(&l);}break;
case CTRL_B: /* ctrl-b */linenoiseEditMoveLeft(&l);break;
case CTRL_F: /* ctrl-f */linenoiseEditMoveRight(&l);break;
case CTRL_P: /* ctrl-p */linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);break;
case CTRL_N: /* ctrl-n */linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);break;
case ESC: /* escape sequence *//* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */if (read(l.ifd,seq,1) == -1) break;if (read(l.ifd,seq+1,1) == -1) break;/* ESC [ sequences. */if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ if (read(l.ifd,seq+2,1) == -1) break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ linenoiseEditDelete(&l); break; } } } else { switch(seq[1]) { case 'A': /* Up */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV); break; case 'B': /* Down */ linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT); break; case 'C': /* Right */ linenoiseEditMoveRight(&l); break; case 'D': /* Left */ linenoiseEditMoveLeft(&l); break; case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; } }}/* ESC O sequences. */else if (seq[0] == 'O') { switch(seq[1]) { case 'H': /* Home */ linenoiseEditMoveHome(&l); break; case 'F': /* End*/ linenoiseEditMoveEnd(&l); break; }}break;
default:if (linenoiseEditInsert(&l,c)) return -1;break;
case CTRL_U: /* Ctrl+u, delete the whole line. */buf[0] = '\0';l.pos = l.len = 0;refreshLine(&l);break;
case CTRL_K: /* Ctrl+k, delete from current to end of line. */buf[l.pos] = '\0';l.len = l.pos;refreshLine(&l);break;
case CTRL_A: /* Ctrl+a, go to the start of the line */linenoiseEditMoveHome(&l);break;
case CTRL_E: /* ctrl+e, go to the end of the line */linenoiseEditMoveEnd(&l);break;
case CTRL_L: /* ctrl+l, clear screen */linenoiseClearScreen();refreshLine(&l);break;
case CTRL_W: /* ctrl+w, delete previous word */linenoiseEditDeletePrevWord(&l);break;
}
}
return l.len;
}
在得到cli命令字符串后,先对把这些命令存到历史文件里面,需要对该命令进行语义的分析,包括quit,exit,restart,connect,clear等等,然后进行相对应的操作。
如果是与redis server相关的命令,则通过issueCommandRepeat函数发送和接收数据
static int issueCommandRepeat(int argc, char **argv, long repeat) {
while (1) {
config.cluster_reissue_command = 0;
if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {cliConnect(1);/* If we still cannot send the command print error. * We'll try to reconnect the next time. */if (cliSendCommand(argc,argv,repeat) != REDIS_OK) { cliPrintContextError(); return REDIS_ERR;}
}
/* Issue the command again if we got redirected in cluster mode */
if (config.cluster_mode && config.cluster_reissue_command) {cliConnect(1);
} else { break;
}
}
return REDIS_OK;
}

本文数据库(综合)相关术语:系统安全软件

主题: Redis数据服务器数据结构UT数据库RYEDIKEEWord
分页:12
转载请注明
本文标题:redis-cli源码分析 数据库 数据库学习 Redis cli源码分析
本站链接:http://www.codesec.net/view/531025.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 数据库(综合) | 评论(0) | 阅读(37)