精华内容
下载资源
问答
  • 指令:netsh winsock reset 作用:这个命令可以重新初始化网络环境,以解决由于软件冲突、病毒原因造成的参数错误问题。

    指令:netsh winsock reset

    作用:这个命令可以重新初始化网络环境,以解决由于软件冲突、病毒原因造成的参数错误问题。

    展开全文
  • 一个Redis服务器从启动到能够接受客户端的命令请求,需要经过一系列的初始化和设置过程,比如初始化服务器状态,接受用户指定的服务器配置,创建相应的数据结构和网络连接等等。通过分析main函数我们可以了解这些...

    redis的main函数在redis.c文件的最后(3933行)。一个Redis服务器从启动到能够接受客户端的命令请求,需要经过一系列的初始化和设置过程,比如初始化服务器状态,接受用户指定的服务器配置,创建相应的数据结构和网络连接等等。通过分析main函数我们可以了解这些流程。下面我们就简要分析一下Redis服务器启动流程。

    int main(int argc, char **argv) {
        struct timeval tv;
    
        /* We need to initialize our libraries, and the server configuration. */
        // 初始化库
    #ifdef INIT_SETPROCTITLE_REPLACEMENT
        spt_init(argc, argv);
    #endif
        setlocale(LC_COLLATE,"");
        zmalloc_enable_thread_safeness();
        zmalloc_set_oom_handler(redisOutOfMemoryHandler);
        srand(time(NULL)^getpid());
        gettimeofday(&tv,NULL);
        dictSetHashFunctionSeed(tv.tv_sec^tv.tv_usec^getpid());
    
        // 检查服务器是否以 Sentinel 模式启动
        server.sentinel_mode = checkForSentinelMode(argc,argv);
    
        // 初始化服务器
        initServerConfig();
    
        /* We need to init sentinel right now as parsing the configuration file
         * in sentinel mode will have the effect of populating the sentinel
         * data structures with master nodes to monitor. */
        // 如果服务器以 Sentinel 模式启动,那么进行 Sentinel 功能相关的初始化
        // 并为要监视的主服务器创建一些相应的数据结构
        if (server.sentinel_mode) {
            initSentinelConfig();
            initSentinel();
        }
    
        // 检查用户是否指定了配置文件,或者配置选项
        if (argc >= 2) {
            int j = 1; /* First option to parse in argv[] */
            sds options = sdsempty();
            char *configfile = NULL;
    
            /* Handle special options --help and --version */
            // 处理特殊选项 -h 、-v 和 --test-memory
            if (strcmp(argv[1], "-v") == 0 ||
                strcmp(argv[1], "--version") == 0) version();
            if (strcmp(argv[1], "--help") == 0 ||
                strcmp(argv[1], "-h") == 0) usage();
            if (strcmp(argv[1], "--test-memory") == 0) {
                if (argc == 3) {
                    memtest(atoi(argv[2]),50);
                    exit(0);
                } else {
                    fprintf(stderr,"Please specify the amount of memory to test in megabytes.\n");
                    fprintf(stderr,"Example: ./redis-server --test-memory 4096\n\n");
                    exit(1);
                }
            }
    
            /* First argument is the config file name? */
            // 如果第一个参数(argv[1])不是以 "--" 开头
            // 那么它应该是一个配置文件
            if (argv[j][0] != '-' || argv[j][1] != '-')
                configfile = argv[j++];
    
            /* All the other options are parsed and conceptually appended to the
             * configuration file. For instance --port 6380 will generate the
             * string "port 6380\n" to be parsed after the actual file name
             * is parsed, if any. */
            // 对用户给定的其余选项进行分析,并将分析所得的字符串追加稍后载入的配置文件的内容之后
            // 比如 --port 6380 会被分析为 "port 6380\n"
            while(j != argc) {
                if (argv[j][0] == '-' && argv[j][1] == '-') {
                    /* Option name */
                    if (sdslen(options)) options = sdscat(options,"\n");
                    options = sdscat(options,argv[j]+2);
                    options = sdscat(options," ");
                } else {
                    /* Option argument */
                    options = sdscatrepr(options,argv[j],strlen(argv[j]));
                    options = sdscat(options," ");
                }
                j++;
            }
            if (configfile) server.configfile = getAbsolutePath(configfile);
            // 重置保存条件
            resetServerSaveParams();
    
            // 载入配置文件, options 是前面分析出的给定选项
            loadServerConfig(configfile,options);
            sdsfree(options);
    
            // 获取配置文件的绝对路径
            if (configfile) server.configfile = getAbsolutePath(configfile);
        } else {
            redisLog(REDIS_WARNING, "Warning: no config file specified, using the default config. In order to specify a config file use %s /path/to/%s.conf", argv[0], server.sentinel_mode ? "sentinel" : "redis");
        }
    
        // 将服务器设置为守护进程
        if (server.daemonize) daemonize();
    
        // 创建并初始化服务器数据结构
        initServer();
    
        // 如果服务器是守护进程,那么创建 PID 文件
        if (server.daemonize) createPidFile();
    
        // 为服务器进程设置名字
        redisSetProcTitle(argv[0]);
    
        // 打印 ASCII LOGO
        redisAsciiArt();
    
        // 如果服务器不是运行在 SENTINEL 模式,那么执行以下代码
        if (!server.sentinel_mode) {
            /* Things not needed when running in Sentinel mode. */
            // 打印问候语
            redisLog(REDIS_WARNING,"Server started, Redis version " REDIS_VERSION);
        #ifdef __linux__
            // 打印内存警告
            linuxOvercommitMemoryWarning();
        #endif
            // 从 AOF 文件或者 RDB 文件中载入数据
            loadDataFromDisk();
            // 启动集群?
            if (server.cluster_enabled) {
                if (verifyClusterConfigWithData() == REDIS_ERR) {
                    redisLog(REDIS_WARNING,
                        "You can't have keys in a DB different than DB 0 when in "
                        "Cluster mode. Exiting.");
                    exit(1);
                }
            }
            // 打印 TCP 端口
            if (server.ipfd_count > 0)
                redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
            // 打印本地套接字端口
            if (server.sofd > 0)
                redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
        } else {
            sentinelIsRunning();
        }
    
        /* Warning the user about suspicious maxmemory setting. */
        // 检查不正常的 maxmemory 配置
        if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
            redisLog(REDIS_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
        }
    
        // 运行事件处理器,一直到服务器关闭为止
        aeSetBeforeSleepProc(server.el,beforeSleep);
        aeMain(server.el);
    
        // 服务器关闭,停止事件循环
        aeDeleteEventLoop(server.el);
    
        return 0;
    }
    

    初始化

    初始化库:

    在这里插入图片描述
    void spt_init(int argc,char *argv[])定义在setproctitle.c中的第148行(这个源程序文件很多工程是库文件,Linux/Darwin setproctitle)。通过这个宏定义可以看出是考虑移植性的,检查是否可以使用setproctitle函数,BSD系统本身支持该函数,对于Linux和osx我们提供相应的实现版本。这段定义在config.h中。
    在这里插入图片描述
    程序的环境由一组格式为“名字=值”的字符串组成。程序可以通过environ变量直接访问这个字符串数组。详细可参考Linux程序设计第4章Linux环境第122页。

    void spt_init(int argc, char *argv[]) {
            char **envp = environ;   
    	char *base, *end, *nul, *tmp;
    	int i, error;
    
    	if (!(base = argv[0]))
    		return;
    
    	nul = &base[strlen(base)];
    	end = nul + 1;
    
    	for (i = 0; i < argc || (i >= argc && argv[i]); i++) {
    		if (!argv[i] || argv[i] < end)
    			continue;
    
    		end = argv[i] + strlen(argv[i]) + 1;
    	}
    
    	for (i = 0; envp[i]; i++) {
    		if (envp[i] < end)
    			continue;
    
    		end = envp[i] + strlen(envp[i]) + 1;
    	}
    
    	if (!(SPT.arg0 = strdup(argv[0])))
    		goto syerr;
    
    #if __GLIBC__
    	if (!(tmp = strdup(program_invocation_name)))
    		goto syerr;
    
    	program_invocation_name = tmp;
    
    	if (!(tmp = strdup(program_invocation_short_name)))
    		goto syerr;
    
    	program_invocation_short_name = tmp;
    #elif __APPLE__
    	if (!(tmp = strdup(getprogname())))
    		goto syerr;
    
    	setprogname(tmp);
    #endif
    
    
    	if ((error = spt_copyenv(envp)))
    		goto error;
    
    	if ((error = spt_copyargs(argc, argv)))
    		goto error;
    
    	SPT.nul  = nul;
    	SPT.base = base;
    	SPT.end  = end;
    
    	return;
    syerr:
    	error = errno;
    error:
    	SPT.error = error;
    } /* spt_init() */
    

    char *setlocale(int category, const char *locale) 用于设置或读取地域化信息。

    zmalloc_enable_thread_safeness(); //使能zmalloc线程安全模式
    zmalloc_set_oom_handler(redisOutOfMemoryHandler); //设置内存溢出句柄
    在这里插入图片描述

    srand(time(NULL)^getpid()); 设置srand函数的种子

    设置字典哈希函数种子
    在这里插入图片描述

    初始化服务器

    在这里插入图片描述
    第一步检查服务器是否以Sentinel模式启动,checkForSentinelMode函数来检查命令行上设置的sentinel模式,最后设置server.sentinel_mode标志。
    在这里插入图片描述
    初始化服务器是创建一个struct redisServer类型的实例变量server作为服务器的状态,并为结构中的各个属性设置默认值。初始化server变量的工作由redis.c/initServerConfig函数完成。redis.c/initServerConfig函数设置主要工作:

    • 设置服务器的运行IDgetRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
    • 设置默认配置文件路径为NULL
    • 设置服务器的默认运行频率REDIS_DEFAULT_HZ
    • 设置服务器的运行架构32位还是64位
    • 设置默认服务器端口号、tcp_baklog等相关项
    • 设置服务器log文件路径及syslog配置
    • 设置服务器的默认RDB持久化和AOF持久化条件及一系列服务器设置
    • 初始化LRU时间时钟
    • 初始化并设置保存条件
    • 初始化复制相关的状态
    • 初始化 PSYNC 命令所使用的 backlog
    • 设置客户端的输出缓冲区限制
    • 初始化浮点常量
    • 初始化命令表,在这里初始化是因为接下来读取 .conf 文件时可能会用到这些命令
    • 初始化慢查询日志
    • 初始化调试项

    可以看到clientBufferLimitsConfig的成员表示的是客户端缓存区的硬限制、软限制及软限制时限。
    在这里插入图片描述
    在这里插入图片描述
    命令表是一个字典,字典的键是一个个命令名字,比如set、get、del等等;而字典的值则是一个个redisCommand结构,每个redisCommand结构记录了一个Redis命令的实现信息。从初始化中可以看出,为server的命令表分配了两个字典,一个是受到rename配置选项作用的命令表,一个是无rename配置命令作用的命令表。
    在这里插入图片描述
    该字典使用的哈希函数,键比较函数以及键销毁函数
    在这里插入图片描述
    其中最重要的是命令的名字、实现函数以及参数个数。实现函数的返回值的宏定义为typedef void redisCommandProc(redisClient *c)
    在这里插入图片描述
    在这里插入图片描述
    populateCommandTable();用于根据redis.c文件顶部的命令列表,创建命令表
    在这里插入图片描述

    /* Populates the Redis Command Table starting from the hard coded list
     * we have on top of redis.c file. 
     *
     * 根据 redis.c 文件顶部的命令列表,创建命令表
     */
    void populateCommandTable(void) {
        int j;
    
        // 命令的数量
        int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
    
        for (j = 0; j < numcommands; j++) {
            
            // 指定命令
            struct redisCommand *c = redisCommandTable+j;
    
            // 取出字符串 FLAG
            char *f = c->sflags;
    
            int retval1, retval2;
    
            // 根据字符串 FLAG 生成实际 FLAG
            while(*f != '\0') {
                switch(*f) {
                case 'w': c->flags |= REDIS_CMD_WRITE; break;
                case 'r': c->flags |= REDIS_CMD_READONLY; break;
                case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
                case 'a': c->flags |= REDIS_CMD_ADMIN; break;
                case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
                case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
                case 'R': c->flags |= REDIS_CMD_RANDOM; break;
                case 'S': c->flags |= REDIS_CMD_SORT_FOR_SCRIPT; break;
                case 'l': c->flags |= REDIS_CMD_LOADING; break;
                case 't': c->flags |= REDIS_CMD_STALE; break;
                case 'M': c->flags |= REDIS_CMD_SKIP_MONITOR; break;
                case 'k': c->flags |= REDIS_CMD_ASKING; break;
                default: redisPanic("Unsupported command flag"); break;
                }
                f++;
            }
    
            // 将命令关联到命令表
            retval1 = dictAdd(server.commands, sdsnew(c->name), c);
    
            /* Populate an additional dictionary that will be unaffected
             * by rename-command statements in redis.conf. 
             *
             * 将命令也关联到原始命令表
             *
             * 原始命令表不会受 redis.conf 中命令改名的影响
             */
            retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
    
            redisAssert(retval1 == DICT_OK && retval2 == DICT_OK);
        }
    }
    

    从redisCommandTable数组取出数据,写入命令字典,根据sflags的属性标识分析得出二进制标识,并将其置入flags中。通过dictAdd将命令关联到命令表和原始命令表。
    在这里插入图片描述

    初始化常用命令的快捷链接
    在这里插入图片描述

    载入配置选项

    服务器在用initServerConfig函数初始化完server变量之后,就会开始载入用户给定的配置参数和配置文件,并根据用户设定的配置,对server变量相关属性的值进行修改。如果用户没有为属性的相应选项设置新的值,那么服务器就沿用之前initServerConfig函数为属性设置的默认值。

    • 检查用户是否指定了配置文件,或者配置选项
    • 对用户给定的其余选项进行分析,并将分析所得的字符串追加稍后载入的配置文件的内容之后
      在这里插入图片描述
    • 将服务器设置为守护进程,如果服务器是守护进程,那么创建 PID 文件,为服务器进程设置名字

    初始化服务器数据结构

    在之前执行initServerConfig函数初始化server状态时,程序只创建了命令表一个数据结构,不过除了命令表之外,服务器状态还包含其他数据结构,比如:

    • server.clients链表,这个链表记录了所有与服务器相连的客户端的状态结构,链表的每个节点都包含了一个redisClient结构实例

    • server.ds数组,数组中包含了服务器的所有数据库

    • 用于保存频道订阅信息的server.pubsub_channels字典,以及用于保存模式订阅信息的server.pusub_patterns链表

    • 用于执行Lua脚本的Lua环境server.lua

    • 用于保存慢查询日志的server.slowlog属性
      当初始化服务器进行到这一步,服务器将调用initServer函数,为以上提到的数据结构分配内存,并在有需要时,为这些数据结构设置或者关联初始值。
      在这里插入图片描述
      分析initServer函数

    • 为服务器设置进程信号处理器:SIGHUP、SIGPIPE设置为SIG_IGN忽略。setupSignalHandlers是通过sigaction函数为信号设置信号处理函数。
      在这里插入图片描述
      在这里插入图片描述
      这个信号处理器负责在服务器接到SIGTERM信号时,打开服务器状态的shutdown_asap标识。每次serverCron函数运行时,程序都会对服务器状态的shutdown_asap属性进行检查,并根据属性的值决定是否关闭服务器。
      在这里插入图片描述

    • 设置syslog
      在这里插入图片描述

    • 初始化并创建数据结构
      在这里插入图片描述

    • 创建共享对象:这些对象包含Redis服务器经常用到的一些值,比如包含OK回复的字符串对象,包含ERR回复的字符串对象,包含整数1到10000的字符串对象等等,服务器通过重用这些共享对象来避免反复创建相同的对象。

    • 初始化事件处理器状态
      在这里插入图片描述

    /*
     * 初始化事件处理器状态
     */
    aeEventLoop *aeCreateEventLoop(int setsize) {
        aeEventLoop *eventLoop;
        int i;
    
        // 创建事件状态结构
        if ((eventLoop = zmalloc(sizeof(*eventLoop))) == NULL) goto err;
    
        // 初始化文件事件结构和已就绪文件事件结构数组
        eventLoop->events = zmalloc(sizeof(aeFileEvent)*setsize);
        eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
        if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
        // 设置数组大小
        eventLoop->setsize = setsize;
        // 初始化执行最近一次执行时间
        eventLoop->lastTime = time(NULL);
    
        // 初始化时间事件结构
        eventLoop->timeEventHead = NULL;
        eventLoop->timeEventNextId = 0;
    
        eventLoop->stop = 0;
        eventLoop->maxfd = -1;
        eventLoop->beforesleep = NULL;
        if (aeApiCreate(eventLoop) == -1) goto err;
    
        /* Events with mask == AE_NONE are not set. So let's initialize the
         * vector with it. */
        // 初始化监听事件
        for (i = 0; i < setsize; i++)
            eventLoop->events[i].mask = AE_NONE;
    
        // 返回事件循环
        return eventLoop;
    
    err:
        if (eventLoop) {
            zfree(eventLoop->events);
            zfree(eventLoop->fired);
            zfree(eventLoop);
        }
        return NULL;
    }
    
    • 打开TCP监听端口,用于等待客户端的命令请求,打开UNIX本地端口
      -
      这里创建时间事件serverCron
      在这里插入图片描述
      这里创建TCP连接关联连接应答处理器用于接收并应答客户端的connect()调用
      在这里插入图片描述
    • 创建并初始化数组结构
    • 创建PUBSUB相关结构
    • 为serverCron函数创建时间事件,等待服务器正式运行时执行serverCron函数
      在这里插入图片描述
    • 如果AOF持久化功能已经打开,那么打开现有的AOF文件,如果AOF文件不存在,那么创建并打开一个新的AOF文件,为AOF写入做好准备。
    • 初始化服务器的后台I/O模块(bio),为将来的I/O操作做好准备
      在这里插入图片描述

    还原数据库状态

    在完成了对服务器状态server变量的初始化之后,服务器需要载入RDB文件或者AOF文件,并根据文件记录的内容来还原服务器的数据库状态。根据服务器是否用了AOF持久化功能,服务器载入数据时使用的目标文件会有所不同:

    • 如果服务器启用了AOF持久化功能,那么服务器使用AOF文件来还原数据库状态。
    • 相反地,如果服务器没有启用AOF持久化功能,那么服务器使用RDB文件来还原数据库状态。

    在这里插入图片描述

    运行事件处理器

    在这里插入图片描述

    命令请求执行过程

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    接收客户端数据和读取客户端的查询缓冲区

    readQueryFromClient函数读取客户端的查询缓冲区内容
    在这里插入图片描述
    在这里插入图片描述
    从查询缓存重读内容,创建参数,并执行命令函数会执行到缓存中的所有内容都被处理完为止。当客户端与服务器之间的连接套接字因为客户端的写入而变得可读时,服务器将调用命令请求处理器来执行一下操作:
    读取套接字中协议格式的命令请求,并将其保存到客户端状态的输入缓冲区里面。
    在这里插入图片描述

    查找命令实现和执行预备操作

    这个函数会调用processCommand,对输入缓冲区中的命令请求进行分析,提取出命令请求中包含的命令参数,以及命令参数的个数,然后分别将参数和参数个数保存到客户端的argv属性和argc属性里面。命令执行器要做的第一件事就是根据客户端状态的argv[0]参数,在命令表中查找参数所指定的命令,并将找到的命令保存到客户端状态的cmd属性里面。

    • 特别处理quit命令
    • 检查客户端状态的cmd指针是否指向NULL,如果是的话,那么说明用户输入的命令名字找不到相应的命令实现,服务器不再执行后续步骤,并向客户端返回一个错误。
    • 根据客户端cmd属性指向的redisCommand结构的arity属性,检查命令请求所给定的参数个数是否正确,当参数个数不正确时,不再执行后续步骤,直接向客户端返回一个错误。
    • 检查客户端是否已经通过身份认证,未通过身份验证的客户端只能执行AUTH命令,如果未通过身份验证的客户端试图执行除AUTH命令之外的其他命令,那么服务器将向客户端返回一个错误。
    • 如果开启了集群模式,那么在这里进行转向操作。不过,如果有以下情况出现,那么节点不进行转向:命令的发送者是本节点的主节点,命令没有key参数。
    • 如果服务器打开了maxmemory功能,那么在执行命令之前,先检查服务器的内存占用情况,并在有需要时进行内存回收,从而使得接下来的命令可以顺利执行。如果内存回收失败,那么不再执行后续步骤,向客户端返回一个错误。
    • 如果服务器上一次执行BGSAVE命令时出错,并且服务器打开了stop-writes-on-bgsave-error功能,而且服务器即将要执行的命令是一个写命令,那么服务器将拒绝执行这个命令,并向客户端返回一个错误。
    • 如果这个服务器是一个只读 slave 的话,那么拒绝执行写命令。
    • 如果客户端当前正在用SUBSCRIBE命令订阅频道,或者正在用PSUBSCRIBE命令订阅模式,那么服务器只会执行客户端发来的SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE、PUNSUBSCRIBE四个命令,其他命令都会被服务器拒绝。
    • 如果服务器正在进行数据载入,那么客户端发送的命令必须带有l标识(比如INFO、SHUTDOWN、PUBLISH等)才会被服务器执行,其他命令都会被服务器拒绝。
    • 如果服务器因为执行Lua脚本而超时并进入阻塞状态,那么服务器只会执行客户端发来的SHUTDOWN nosave命令和SCRIPT KILL命令,其他命令都会被服务器拒绝。
    • 如果客户端正在执行事务,那么服务器只会执行客户端发来的EXEC、DISCARD、MULTI、WATCH四个命令,其他命令都会被放进事务队列中。
    /* If this function gets called we already read a whole
     * command, arguments are in the client argv/argc fields.
     * processCommand() execute the command or prepare the
     * server for a bulk read from the client.
     *
     * 这个函数执行时,我们已经读入了一个完整的命令到客户端,
     * 这个函数负责执行这个命令,
     * 或者服务器准备从客户端中进行一次读取。
     *
     * If 1 is returned the client is still alive and valid and
     * other operations can be performed by the caller. Otherwise
     * if 0 is returned the client was destroyed (i.e. after QUIT). 
     *
     * 如果这个函数返回 1 ,那么表示客户端在执行命令之后仍然存在,
     * 调用者可以继续执行其他操作。
     * 否则,如果这个函数返回 0 ,那么表示客户端已经被销毁。
     */
    int processCommand(redisClient *c) {
        /* The QUIT command is handled separately. Normal command procs will
         * go through checking for replication and QUIT will cause trouble
         * when FORCE_REPLICATION is enabled and would be implemented in
         * a regular command proc. */
        // 特别处理 quit 命令
        if (!strcasecmp(c->argv[0]->ptr,"quit")) {
            addReply(c,shared.ok);
            c->flags |= REDIS_CLOSE_AFTER_REPLY;
            return REDIS_ERR;
        }
    
        /* Now lookup the command and check ASAP about trivial error conditions
         * such as wrong arity, bad command name and so forth. */
        // 查找命令,并进行命令合法性检查,以及命令参数个数检查
        c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
        if (!c->cmd) {
            // 没找到指定的命令
            flagTransaction(c);
            addReplyErrorFormat(c,"unknown command '%s'",
                (char*)c->argv[0]->ptr);
            return REDIS_OK;
        } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
                   (c->argc < -c->cmd->arity)) {
            // 参数个数错误
            flagTransaction(c);
            addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
                c->cmd->name);
            return REDIS_OK;
        }
    
        /* Check if the user is authenticated */
        // 检查认证信息
        if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
        {
            flagTransaction(c);
            addReply(c,shared.noautherr);
            return REDIS_OK;
        }
    
        /* If cluster is enabled perform the cluster redirection here.
         *
         * 如果开启了集群模式,那么在这里进行转向操作。
         *
         * However we don't perform the redirection if:
         *
         * 不过,如果有以下情况出现,那么节点不进行转向:
         *
         * 1) The sender of this command is our master.
         *    命令的发送者是本节点的主节点
         *
         * 2) The command has no key arguments. 
         *    命令没有 key 参数
         */
        if (server.cluster_enabled &&
            !(c->flags & REDIS_MASTER) &&
            !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0))
        {
            int hashslot;
    
            // 集群已下线
            if (server.cluster->state != REDIS_CLUSTER_OK) {
                flagTransaction(c);
                addReplySds(c,sdsnew("-CLUSTERDOWN The cluster is down. Use CLUSTER INFO for more information\r\n"));
                return REDIS_OK;
    
            // 集群运作正常
            } else {
                int error_code;
                clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&error_code);
                // 不能执行多键处理命令
                if (n == NULL) {
                    flagTransaction(c);
                    if (error_code == REDIS_CLUSTER_REDIR_CROSS_SLOT) {
                        addReplySds(c,sdsnew("-CROSSSLOT Keys in request don't hash to the same slot\r\n"));
                    } else if (error_code == REDIS_CLUSTER_REDIR_UNSTABLE) {
                        /* The request spawns mutliple keys in the same slot,
                         * but the slot is not "stable" currently as there is
                         * a migration or import in progress. */
                        addReplySds(c,sdsnew("-TRYAGAIN Multiple keys request during rehashing of slot\r\n"));
                    } else {
                        redisPanic("getNodeByQuery() unknown error.");
                    }
                    return REDIS_OK;
    
                // 命令针对的槽和键不是本节点处理的,进行转向
                } else if (n != server.cluster->myself) {
                    flagTransaction(c);
                    // -<ASK or MOVED> <slot> <ip>:<port>
                    // 例如 -ASK 10086 127.0.0.1:12345
                    addReplySds(c,sdscatprintf(sdsempty(),
                        "-%s %d %s:%d\r\n",
                        (error_code == REDIS_CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
                        hashslot,n->ip,n->port));
    
                    return REDIS_OK;
                }
    
                // 如果执行到这里,说明键 key 所在的槽由本节点处理
                // 或者客户端执行的是无参数命令
            }
        }
    
        /* Handle the maxmemory directive.
         *
         * First we try to free some memory if possible (if there are volatile
         * keys in the dataset). If there are not the only thing we can do
         * is returning an error. */
        // 如果设置了最大内存,那么检查内存是否超过限制,并做相应的操作
        if (server.maxmemory) {
            // 如果内存已超过限制,那么尝试通过删除过期键来释放内存
            int retval = freeMemoryIfNeeded();
            // 如果即将要执行的命令可能占用大量内存(REDIS_CMD_DENYOOM)
            // 并且前面的内存释放失败的话
            // 那么向客户端返回内存错误
            if ((c->cmd->flags & REDIS_CMD_DENYOOM) && retval == REDIS_ERR) {
                flagTransaction(c);
                addReply(c, shared.oomerr);
                return REDIS_OK;
            }
        }
    
        /* Don't accept write commands if there are problems persisting on disk
         * and if this is a master instance. */
        // 如果这是一个主服务器,并且这个服务器之前执行 BGSAVE 时发生了错误
        // 那么不执行写命令
        if (((server.stop_writes_on_bgsave_err &&
              server.saveparamslen > 0 &&
              server.lastbgsave_status == REDIS_ERR) ||
              server.aof_last_write_status == REDIS_ERR) &&
            server.masterhost == NULL &&
            (c->cmd->flags & REDIS_CMD_WRITE ||
             c->cmd->proc == pingCommand))
        {
            flagTransaction(c);
            if (server.aof_last_write_status == REDIS_OK)
                addReply(c, shared.bgsaveerr);
            else
                addReplySds(c,
                    sdscatprintf(sdsempty(),
                    "-MISCONF Errors writing to the AOF file: %s\r\n",
                    strerror(server.aof_last_write_errno)));
            return REDIS_OK;
        }
    
        /* Don't accept write commands if there are not enough good slaves and
         * user configured the min-slaves-to-write option. */
        // 如果服务器没有足够多的状态良好服务器
        // 并且 min-slaves-to-write 选项已打开
        if (server.repl_min_slaves_to_write &&
            server.repl_min_slaves_max_lag &&
            c->cmd->flags & REDIS_CMD_WRITE &&
            server.repl_good_slaves_count < server.repl_min_slaves_to_write)
        {
            flagTransaction(c);
            addReply(c, shared.noreplicaserr);
            return REDIS_OK;
        }
    
        /* Don't accept write commands if this is a read only slave. But
         * accept write commands if this is our master. */
        // 如果这个服务器是一个只读 slave 的话,那么拒绝执行写命令
        if (server.masterhost && server.repl_slave_ro &&
            !(c->flags & REDIS_MASTER) &&
            c->cmd->flags & REDIS_CMD_WRITE)
        {
            addReply(c, shared.roslaveerr);
            return REDIS_OK;
        }
    
        /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
        // 在订阅于发布模式的上下文中,只能执行订阅和退订相关的命令
        if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
            &&
            c->cmd->proc != subscribeCommand &&
            c->cmd->proc != unsubscribeCommand &&
            c->cmd->proc != psubscribeCommand &&
            c->cmd->proc != punsubscribeCommand) {
            addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
            return REDIS_OK;
        }
    
        /* Only allow INFO and SLAVEOF when slave-serve-stale-data is no and
         * we are a slave with a broken link with master. */
        if (server.masterhost && server.repl_state != REDIS_REPL_CONNECTED &&
            server.repl_serve_stale_data == 0 &&
            !(c->cmd->flags & REDIS_CMD_STALE))
        {
            flagTransaction(c);
            addReply(c, shared.masterdownerr);
            return REDIS_OK;
        }
    
        /* Loading DB? Return an error if the command has not the
         * REDIS_CMD_LOADING flag. */
        // 如果服务器正在载入数据到数据库,那么只执行带有 REDIS_CMD_LOADING
        // 标识的命令,否则将出错
        if (server.loading && !(c->cmd->flags & REDIS_CMD_LOADING)) {
            addReply(c, shared.loadingerr);
            return REDIS_OK;
        }
    
        /* Lua script too slow? Only allow a limited number of commands. */
        // Lua 脚本超时,只允许执行限定的操作,比如 SHUTDOWN 和 SCRIPT KILL
        if (server.lua_timedout &&
              c->cmd->proc != authCommand &&
              c->cmd->proc != replconfCommand &&
            !(c->cmd->proc == shutdownCommand &&
              c->argc == 2 &&
              tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
            !(c->cmd->proc == scriptCommand &&
              c->argc == 2 &&
              tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))
        {
            flagTransaction(c);
            addReply(c, shared.slowscripterr);
            return REDIS_OK;
        }
    
        /* Exec the command */
        if (c->flags & REDIS_MULTI &&
            c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
            c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
        {
            // 在事务上下文中
            // 除 EXEC 、 DISCARD 、 MULTI 和 WATCH 命令之外
            // 其他所有命令都会被入队到事务队列中
            queueMultiCommand(c);
            addReply(c,shared.queued);
        } else {
            // 执行命令
            call(c,REDIS_CALL_FULL);
    
            c->woff = server.master_repl_offset;
            // 处理那些解除了阻塞的键
            if (listLength(server.ready_keys))
                handleClientsBlockedOnLists();
        }
    
        return REDIS_OK;
    }
    

    调用命令执行器,执行客户端指定的命令

    在这里插入图片描述
    call命令会调用redis.c的2441行的c->cmd->proc(c),被调用的命令实现函数会执行指定的操作,并产生相应的命令回复,这些回复会被保存在客户端状态的输出缓冲区里面,之后实现函数还会为客户端的套接字关联命令回复处理器,这个处理器负责将命令回复返回给客户端。当客户端套接字变为可写状态时,服务器就会执行命令回复处理器,将保存在客户端输出缓冲区中的命令回复发送给客户端。

    在这里插入图片描述

    执行后续工作

    在call函数中服务器还需要执行一些后续工作:

    • 如果服务器开启了慢查询日志功能,那么慢查询日志模块会检查是否需要为刚刚执行完的命令请求添加一条新的慢查询日志。
    • 根据刚刚执行命令所耗费的时长,更新被执行命令的redisCommand结构的milliseconds属性,并将命令的redisCommand结构的calls计数器的值增一。
    • 如果服务器开启了AOF持久化功能,那么AOF持久化模块会将刚刚执行的命令请求写入到AOF缓冲区里面。
    • 如果有其他从服务器正在复制当前这个服务器,那么服务器会将刚刚执行的命令传播给所有从服务器。

    底层相关文件:setproctitle.c(为了兼容部分系统没有实现提供的副本)

    展开全文
  • 查询路由元素 选择可以使用的路由元素 需要将网络服务提供者的:虚拟路由和安全同时启用 { "createnetworkresponse" : {"errorcode":530,"cserrorcode":4390,"errortext":"Provider VirtualRouter is eithe...
    image

     

     

    image

     

    image

     

    image

     

    image

     

    查询路由元素

    image

     

    选择可以使用的路由元素

    image

     

    需要将网络服务提供者的:虚拟路由和安全同时启用

    image

     

    { "createnetworkresponse" : {"errorcode":530,"cserrorcode":4390,"errortext":"Provider VirtualRouter is either not enabled or doesn't support service Dhcp in physical network id=221"} }

    image

     

    说网络服务提供方案没有准备好,无法更新虚拟机路由状态

    image

    该状态在物理网络架构中可以查看

    image

     

     

    列出配置选项目

    image

    转载于:https://www.cnblogs.com/heidsoft/p/3436296.html

    展开全文
  • CentOS 最小化安装后初始化网络

    千次阅读 2015-07-28 19:54:01
    找出哪个包提供了ifconfig命令。 输入以下命令: yum provides ifconfig  或者你也可以使用以下命令:yum whatprovides ifconfig  这里,“provides”或者“whatprovides”开关用于找出某个包提供了某些功能或...

    Minimal的安装也带来了一些缺点,系统默认提供的网络管理工具 NetworkManager 实际上没有安装在 Minimal 的系统上。这样我们就不得不手工编辑网卡的配置文件。典型的网卡配置文件(/etc/sysconfig/network-script/ifcfg-X)格式如下:

    
    
    Centos 6的Minimal下,默认onboot="no",并且设置了依赖 NetworkManager 的选项,NM_CONTROLLED="yes"。这个配置看起来有点可笑,因为minimal的情况下并没有安装 NetworkManger。所以我们需要修改为:
    ONBOOT="yes" MM_Controlled="no"
    同时,如果需要自动获取IP,我们还应该加上 

              BOOTPROTO="dhcp"


    找出哪个包提供了ifconfig命令。

    输入以下命令:     yum provides ifconfig
         或者你也可以使用以下命令:yum whatprovides ifconfig

        这里,“provides”或者“whatprovides”开关用于找出某个包提供了某些功能或文件。


    可以看到是 net-tools


    然后安装

    yum install net-tools



    yum groupinstall "Development Tools"


    相同的步骤可以安装wget和gcc 

    展开全文
  • 从配置文件中读取初始化信息与网络有关的配置命令主要有两个:listen和sever_name。首先先了解这两个命令的用法。listenlisten命令设置nginx监听地址,nginx从这里接受请求。对于IP协议,这个地址就是addr
  • 插上网线后,使用ip addr命令查看网络情况,发现eno1的描述中有 static up,表示该网口被使用 ip配置 编辑网络配置文件vi /etc/sysconfig/network-scripts/ifcfg-eno1 TYPE=Ethernet PROXY_METHOD=none BROWSER_ONLY...
  • centos 7 的查看IP命令是ip add 最后一行00:0c:29:58:27:57就是本地网卡mac地址,这个后面需要用到: ifcfg-eno16777736这个是本地网卡名字,后面也要用到的; 二、配置网卡开启网络连接 1 2 ...
  • 网络连接的三种方式 桥接模式:相当于在物理主机与虚拟网卡之间架设了一座桥梁,从而可以通过物理主机的网卡访问外网。 虚拟机就相当于一台真正的机器,直接连接到外网 NAT模式:让VM虚拟机的网络服务发挥路由器的...
  • 安装好 启动之前 在终端输入命令 sudo -b /Applications/Parallels\ Desktop.app/Contents/MacOS/prl_client_app 需要输入开机密码完成命令 执行完毕之后 再打开 windows10 如果还是不行 关闭windows 退出parallels...
  • 我们为什么要学习Python C编程?第一个原因,用Python进行项目开发时,很多时候都需要用Python C来扩展...本门课程是网络上第一部讲解Python C编程的课程,课程深入、详细、完整讲解Python C编程技术,让你学习Pytho...
  • 网络子系统27_桥接子系统初始化

    千次阅读 2013-10-01 20:00:46
    //模块初始化函数 1.1 static int __init br_init(void) { //转发数据库初始化 br_fdb_init(); //桥接子系统中有关netfilter的初始化 ... //向socket的ioctl注册回调函数,处理对网桥的io命令 brioctl_set(br...
  • NIC设备在内核中相关联的net_device结构初始化,并添加到内核网络设备数据块中注册之后,用户才能通过用户空间的命令开启设备,使其可用。设备的注册和注销是由内核完成的,而设备的开启和关闭是由用户控制的。网络...
  • 网络初始化失败:您的虚拟机将继续正常运作,但将无法连接网络 这是我的软件版本: 起初我以为是我的虚拟机坏了,结果重装了一遍虚拟机,没有任何用处。我开始在网上找方法,只能找到那个使用命令行打开软件的...
  • 引导期间的内核选项Linux运行用户将内核配置选项传递给引导记录,引导记录再把选项传给内核,进而通过引导配置参数微调内核。...加载模块时,也会用到parse_args解析命令列参数。注册关键字内核组件利用定义在inclu...
  • 1.在命令提示符中输入netca2.在弹出的欢迎界面中按如下选择3.选择"Add"后单击"Next"4.输入监听器的名称后,单击"Next"5.选择TCP协议后,单击"Next"6.选择标准的端口号,然后单击”Next"7.选择No,单击“Next"8.单击”...
  • Platform: Rockchip OS: Android 6.0 Kernel: 3.10.92 Netd: Network Daemon....负责网络配置,操作,管理,查询等功能. ...说白了就是接收framework命令往下发,接收kernel命令往上发. 路径: s
  • #Linux ulimit命令用于控制shell程序的资源。连接数设置为最大 ulimit -n 65535 #删除系统日志 rm -rf /var/log/syslog #关闭 /tmp /var/tmp 目录不可更改属性 chattr -iua /tmp/ chattr -iua /var/tmp/ #关闭防火墙...
  • 设置linux网络 1)零时设置ip地址 由于centos7默认没有ifconfig命令所以为了使用方便我们先安装net-tool使我们能使用ifconfig命令查看ip地址 ·挂载系统光盘 [root@localhost ~]# mount /dev/cdrom /media/...
  • 使用Angular初始化项目

    2020-09-18 23:52:15
    使用Angular初始化项目 ng new 项目名称 当执行一半的时候,ctral+c+c打断它,因为网络原因会有一些错误,所以我们自动 cd 切换到刚生成的项目中,执行命令 cnpm install 启动Angular应用程序 执行下面命令 ng ...
  • 之前解决联网的办法是在终端通过命令启动parallels desktop的方法解决联网的问题,但是相对比较麻烦,而且还是无法解决Parallels Desktop 16 破解版不能连接USB设备的问题。 今天小编为大家提供一个更好的方法解决...
  • 网络初始化  1.ip地址的修改(临时生效)  使用ifconfig命令  ifconfig 网卡名 ip地址 子网掩码 [root@localhost /]# ifconfig eno16777736 192.168.100.112/24  1.ip地址的修改(永久生效)  修改/etc/...
  • 缘由:这个问题是因为直接从PDF上复制命令,会出现一些问题。 比如: kubeadm init --kubernetes-version=$(kubeadm version -o short) --pod-network- cidr=10.244.0.0/16 #PDF自动将cidr前面加了回车 wget ...
  • Parallels Desktop 16 在最新的macOS Big Sur 11.0系统上无法联网,并且无法连接USB设备,在网络上搜索解决联网的办法是在终端通过命令: $ sudo -b /Applications/Parallels\ Desktop.app/Contents/MacOS/prl_client...
  • ubuntu安卓开发环境初始化

    千次阅读 2017-08-12 15:22:17
    (1) 启动控制终端的方法 如下图,点击左边第 1 个图标,在...      (2) 安装、配置网络服务: 执行以下命令安装 ftp、ssh、nfs 服务 sudo apt-get update // 这个命令在安装Ubuntu后只需要执行一次 sudo apt-get ins

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,548
精华内容 619
关键字:

初始化网络命令