redis--初始化服务器

服务器初始化

从启动Redis服务器,到服务器可以接受外来客户端的网络连接这段时间,Redis需要执行一系列初始化操作。

整个初始化过程可以分为以下六个步骤:

  1. 初始化服务器全局状态。
  2. 载入配置文件。
  3. 创建守护进程。
  4. 初始化服务器功能模块。
  5. 载入数据。
  6. 开始事件循环。

以下各个小节将介绍Redis服务器初始化的各个步骤。

服务器初始化状态全局

redis.h/redisServer 结构记录了和服务器相关的所有数据,这个结构主要包含以下信息:

  • 服务器中的所有数据库。
  • 命令表:在执行命令时,根据字符来查找相应命令的实现函数。
  • 事件状态。
  • 服务器的网络连接信息:套接字地址,端口,以及套接字描述符。
  • 所有已连接客户端的信息。
  • 日志(日志)和慢查询日志(slowlog)的选项和相关信息。
  • 服务器配置选项:比如要创建多少个数据库,是否将服务器进程作为daemon进程来运行,最大连接多少个客户端,压缩结构(zip structure)的实体数量,等等。
  • 统计信息:比如键有多少次命令,不命中,服务器的运行时间,内存占用,等等。
  • 数据持久化(AOF和RDB)的配置和状态。

- 奴隶信息

- 掌握信息

  • 实现订阅与发布(发布/订阅)功能所需的数据结构。

- 是否运行集群及相关选项。

  • Lua脚本的运行环境及相关选项。

- 调试信息选项

/ *服务器对象* /

struct  redisServer  { 
        / * General * /

        //配置文件路径
        char  * configfile ;            / *绝对配置文件路径,或NULL * /

        // serverCron()调用频率
        int  hz ;                      / * serverCron()以赫兹调用频率* /

        //数据库对象
        redisDb  * db ;

        //支持的命令列表
        dict  * commands ;              / *命令表* /

        //没有转化的命令
        dict  * orig_commands ;         / *命令重命名前的命令表。* /

        //事件
        aeEventLoop  * el ;

        //每分钟增加一次
        unsigned  lruclock :22 ;        / *每分钟时钟递增,对于LRU * /

        unsigned  lruclock_padding :10 ;

        int  shutdown_asap ;           / * SHUTDOWN需要尽快* /

        int  activerehashing ;         / * serverCron()中的增量重新散列* /

        //验证密码
        char  * requirepass ;           / *传递AUTH命令,或NULL * /

        char  * pidfile ;               / * PID文件路径* /

        int  arch_bits ;               / * 32或64取决于sizeof(长)* /

        int  cronloops ;               / * cron函数运行的次数* /

        char  runid [ REDIS_RUN_ID_SIZE + 1 ];   / * ID在每个执行官总是不同的。* /

        int  sentinel_mode ;           / *如果此实例是Sentinel,则为True。* /


        / * Networking * / 
        int  port ;                    / * TCP侦听端口* / 
        int  tcp_backlog ;             / * TCP听()积压* / 
        炭 * bindaddr [ REDIS_BINDADDR_MAX ];  / *地址我们应该绑定到* / 
        int  bindaddr_count ;          / * server.bindaddr [] * / 
        char  * unixsocket中的地址数;            / * UNIX套接字路径* / 
        mode_t  unixsocketperm ;       / * UNIX套接字权限* / 
        int  ipfd [ REDIS_BINDADDR_MAX ];  / * TCP套接字文件描述符* / 
        int ipfd_count ;              / *在ipfd [] * / 
        int  sofd中使用了插槽;                    / * Unix套接字文件描述符* / 
        int  cfd [ REDIS_BINDADDR_MAX ]; / *集群总线监听套接字* / 
        int  cfd_count ;               在CFD [] * / / *用于时隙
        //连接客户端
        列表 * 客户端;               / *活跃客户
        列表 * / list * clients_to_close ;      / *客户端异步关闭* / 
        list  * slave , * monitors ;     / *从属列表和MONITOR * / 
        redisClient  *current_client ;  / *当前客户端,仅用于崩溃报告* / 
        int  clients_paused ;          / *如果客户端当前暂停,
        则为 True * / mstime_t clients_pause_end_time ;  / *我们撤消clients_paused * / 
        char  neterr [ ANET_ERR_LEN ]的时间;    / *用于anet.c * /错误缓冲器
        字典 * migrate_cached_sockets ; / * MIGRATE缓存套接字* /


        / * RDB / AOF加载信息* / 
        int  loading ;                 / *如果为true * / 
        off_t  loading_total_bytes ,
        我们正在从磁盘加载数据; off_t  loading_loaded_bytes ; 
        time_t  loading_start_time ; 
        off_t  loading_process_events_interval_bytes ; 
        / *快速指针经常查找命令* / 
        struct  redisCommand  * delCommand , * multiCommand , * lpushCommand , * lpopCommand ,
        * rpopCommand ;


        / *字段仅用于统计* / 
        time_t  stat_starttime ;           / *服务器启动时间* / 
        long  long  stat_numcommands ;      / *已处理命令数* / 
        long  long  stat_numconnections ;   / *收到的连接数* / 
        long  long  stat_expiredkeys ;      / *过期密钥数* / 
        long  long  stat_evictedkeys ;      / *被驱逐的密钥数量(maxmemory)* / 
        long  long  stat_keyspace_hits ;    / *成功查找密钥的次数* / 
        long  long  stat_keyspace_misses ; / *键的查找失败次数* / 
        size_t  stat_peak_memory ;         / *最大使用内存记录* / 
        long  long  stat_fork_time ;        / *执行最新fork()* / 
        long  long  stat_rejected_conn所需的时间;    / *由于maxclients * / 
        long  long  stat_sync_full而拒绝客户端;        / *带有从站的完整resyncs的数量。* / 
        long  long  stat_sync_partial_ok ;  / *已接受的PSYNC请求数。* / 
        long  long  stat_sync_partial_err ; / *未接受的PSYNC请求数。* /

        //保存慢日志命令
        列表 * slowlog ;                   / * SLOWLOG命令列表* / 
        long  long  slowlog_entry_id ;      / * SLOWLOG当前条目ID * / 
        long  long  slowlog_log_slower_than ;  / * SLOWLOG时间限制(记录)* / 
        unsigned  long  slowlog_max_len ;      / * SLOWLOG记录的最大项目数* / 
        / *以下两项用于跟踪
        每秒操作*的
        瞬时“负载” 。* / long  long  ops_sec_l​​ast_sample_time ;  / *最后一个样本的时间戳(以ms为单位)* / 
        long  long  ops_sec_l​​ast_sample_ops;   / * numcommands in last sample * / 
        long  long  ops_sec_samples [ REDIS_OPS_SEC_SAMPLES ]; 
        int  ops_sec_idx ;


        / *配置* / 
        int  详细程度;                   / * Loglevel in redis.conf * / 
        int  maxidletime ;                 / *客户端超时(以秒为单位)* / 
        int  tcpkeepalive ;                / *如果非零则设置SO_KEEPALIVE。* / 
        int  active_expire_enabled ;       / *可以禁用以进行测试。* / 
        size_t  client_max_querybuf_len ;  / *限制客户端查询缓冲区长度* / 
        int  dbnum ;                       / *已配置的DB总数* / 
        int  daemonize ;                   / *如果作为守护进程运行,则为True * / 
        clientBufferLimitsConfig  client_obuf_limits [REDIS_CLIENT_LIMIT_NUM_CLASSES ];


        / * AOF持久性* / 
        int  aof_state ;                   / * REDIS_AOF_(ON | OFF | WAIT_REWRITE)* / 
        int  aof_fsync ;                   / *种类的fsync()策略* / 
        char  * aof_filename ;              / * AOF文件的名称* / 
        int  aof_no_fsync_on_rewrite ;     / *如果重写是在prog中,请不要fsync。* / 
        int  aof_rewrite_perc ;            / *如果%增长> M并且...... * / 
        off_t  aof_rewrite_min_size ,     则重写AOF ; / * AOF文件至少为N个字节。* / 
        off_t  aof_rewrite_base_size ;     / *最新启动或重写时的AOF大小。* / 
        off_t aof_current_size ;          / * AOF当前大小。* / 
        int  aof_rewrite_scheduled ;       / * BGSAVE终止后重写。* / 
        pid_t  aof_child_pid ;             / * PID如果重写过程* / 
        list  * aof_rewrite_buf_blocks ;    / *在AOF重写期间保持更改。* / 
        sds  aof_buf ;       / * AOF缓冲区,在进入事件循环之前写入* / 
        int  aof_fd ;        / *当前所选AOF文件的文件描述符* / 
        int  aof_selected_db ;  / *当前在AOF中选择的数据库* / 
        time_t  aof_flush_postponed_start ; / *推迟AOF flush的UNIX时间* / 
        time_t  aof_last_fsync ;             / *最后一次fsync()* / 
        time_t的 UNIX时间aof_rewrite_time_last ;    / *最后一次AOF重写运行所用的时间。* / 
        time_t  aof_rewrite_time_start ;   / *当前AOF重写开始时间。* / 
        int  aof_lastbgrewrite_status ;    / * REDIS_OK或REDIS_ERR * / 
        unsigned  long  aof_delayed_fsync ;   / *延迟AOF fsync()计数器* / 
        int  aof_rewrite_incremental_fsync ; / * fsync在重写时递增?* / 
        int  aof_last_write_status ;       / * REDIS_OK或REDIS_ERR * /
        int  aof_last_write_errno ;        / *如果aof_last_write_status是ERR,则有效* /


        / * RDB持久性* / 
        long  long  dirty ;                 / *从上次保存更改DB * / 
        long  long  dirty_before_bgsave ;   / *用于在失败的BGSAVE * / 
        pid_t  rdb_child_pid 上恢复脏;             / * RDB保存子的PID * / 
        struct  saveparam  * saveparams ;    / *为RDB保存点数组* / 
        int  saveparamslen ;               / *保存点数* / 
        char  * rdb_filename ;              / * RDB文件的名称* / 
        int  rdb_compression ;             / *在RDB中使用压缩?* / 
        int rdb_checksum ;                / *使用RDB校验和?* / 
        time_t  lastsave ;                 / *上次成功保存的Unix时间* / 
        time_t  lastbgsave_try ;           / *上次尝试的Unix时间bgsave * / 
        time_t  rdb_save_time_last ;       / *上次RDB保存运行所用的时间。* / 
        time_t  rdb_save_time_start ;      / *当前RDB保存开始时间。* / 
        int  lastbgsave_status ;           / * REDIS_OK或REDIS_ERR * / 
        int  stop_writes_on_bgsave_err ;   / *不允许写入如果不能BGSAVE * / 
        / * AOF /复制中的命令传播* / 
        redisOpArray  also_propagate;     / *传播的附加命令。* /


        / * Logging * / 
        char  * logfile ;                   / *日志文件的路径* / 
        int  syslog_enabled ;              / *是否启用了syslog?* / 
        char  * syslog_ident ;              / * Syslog ident * / 
        int  syslog_facility ;             / * Syslog工具* /


        / *复制(master)* / 
        int  slaveseldb ;                  / *复制输出中的最后一个SELECTed DB * / 
        long  long  master_repl_offset ;    / *全局复制偏移量* / 
        int  repl_ping_slave_period ;      / * Master每N秒ping一次奴隶* / 
        char  * repl_backlog ;              / *部分同步的复制积压* / 
        long  long  repl_backlog_size ;     / * Backlog循环缓冲区大小* / 
        long  long  repl_backlog_histlen ;  / *积压实际数据长度* / 
        long  long  repl_backlog_idx ;     / * Backlog循环缓冲区当前偏移量* / 
        long  long  repl_backlog_off ;      / * 
        积压缓冲区
        中第一个字节的复制偏移量。* / time_t  repl_backlog_time_limit ;  / *积压
        释放
        后没有奴隶的时间。* / time_t  repl_no_slaves_since ;     / *从那时起我们就没有奴隶。
        仅在server.slaves len为0时有效。* / 
        int  repl_min_slaves_to_write ;    / *写入的最小从站数。* / 
        int  repl_min_slaves_max_lag ;     / * <count>奴隶的最大滞后写入。* / 
        int  repl_good_slaves_count;      / *滞后<= max_lag的从站数。* /


        / *复制(从属)* / 
        char  * masterauth ;                / * AUTH,密码为master * / 
        char  * masterhost ;                / * master * / 
        int  masterport的主机名;                  / * master的端口* / 
        int  repl_timeout ;                / *主机空闲N秒后超时* / 
        redisClient  * 主机;      / *作为此slave * / 
        redisClient  * cached_master的主服务器的客户端;  / *缓存的主要重用于PSYNC。* / 
        int  repl_syncio_timeout ;  / *同步I / O调用超时* / 
        int repl_state ;           / *如果实例是从属的复制状态* / 
        off_t  repl_transfer_size ;  / *在同步期间从主机读取的RDB的大小。* / 
        off_t  repl_transfer_read ;  / *在同步期间从主设备读取的RDB数量。* / 
        off_t  repl_transfer_last_fsync_off ;  / *上次fsync-ed时偏移。* / 
        int  repl_transfer_s ;      / * Slave  - > Master SYNC socket * / 
        int  repl_transfer_fd ;     / * Slave  - > Master SYNC临时文件描述符* / 
        char  * repl_transfer_tmpfile ;  / * Slave-> master SYNC临时文件名* / 
        time_t repl_transfer_lastio ;  / *最新读取的Unix时间,用于超时* / 
        int  repl_serve_stale_data ;  / *在链接断开时提供过时的数据?* / 
        int  repl_slave_ro ;           / *奴隶只读?* / 
        time_t  repl_down_since ;  / *与master的链接
        断开的Unix时间* / int  repl_disable_tcp_nodelay ;    / * SYNC后禁用TCP_NODELAY?* / 
        int  slave_priority ;              / *在INFO中报告并由Sentinel使用。* / 
        char  repl_master_runid [ REDIS_RUN_ID_SIZE + 1 ];   / * PSYNC的主运行ID。* /
        long  long  repl_master_initial_offset ;          / *掌握PSYNC偏移量。* /


        / *复制脚本缓存。* / 
        dict  * repl_scriptcache_dict ;         / * SHA1所有从属都知道。* / 
        list  * repl_scriptcache_fifo ;         / *先进先出LRU驱逐。* / 
        int  repl_scriptcache_size ;           / *最大元素数。* / 
        / *同步复制。* / 
        list  * clients_waiting_acks ;          / *等待WAIT命令的客户端。* / 
        int  get_ack_from_slaves ;             / *如果为true,我们发送REPLCONF GETACK。* /


        / *限制* / 
        unsigned  int  maxclients ;         / *最大并发客户端数* / 
        unsigned  long  long  maxmemory ;    / *要使用的最大内存字节数* / 
        int  maxmemory_policy ;            / *关键驱逐政策* / 
        int  maxmemory_samples ;           / *随机抽样的精确度* /


        / *阻塞的客户端* / 
        unsigned  int  bpop_blocked_clients ;  / *列表阻止的客户端数量* / 
        list  * unblocked_clients ;  / *在下一个循环之前解锁的客户端
        列表 * / list * ready_keys ;         / * BLPOP&co * / 
        / * 
的readyList结构列表排序参数 -  qsort_r()仅在BSD下可用,因此我们        必须将此状态设为全局,以便将其传递给sortCompare()* / 
        int  sort_desc ; 
        int  sort_alpha ; 
        int  sort_bypattern ; 
        int  sort_store ;


        / * Zip结构配置,有关更多信息,请参阅redis.conf * / 
        size_t  hash_max_ziplist_entries ; 
        size_t  hash_max_ziplist_value ; 
        size_t  list_max_ziplist_entries ; 
        size_t  list_max_ziplist_value ; 
        size_t  set_max_intset_entries ; 
        size_t  zset_max_ziplist_entries ; 
        size_t  zset_max_ziplist_value ; 
        time_t  unixtime ;         / *每个cron周期对Unix时间进行采样。* / 
        很 长的 mstime ;        / *与'unixtime'相似,但分辨率为毫秒。* /


        / * Pubsub * / 
        dict  * pubsub_channels ;   / *将频道映射到订阅客户
        列表 * / list * pubsub_patterns ;   / * pubsub_patterns * / 
        int  notify_keyspace_events的列表;  / *通过Pub / Sub传播的事件。这是
        REDIS_NOTIFY ...标志的xor。* /


        / * Cluster * / 
        int  cluster_enabled ;       / *群集是否已启用?* / 
        mstime_t  cluster_node_timeout ;  / *群集节点超时。* / 
        char  * cluster_configfile ;  / *群集自动生成的配置文件名。* / 
        struct  clusterState  * cluster ;   / *集群的状态* / 
        int  cluster_migration_barrier ;  / *群集副本迁移障碍。* /


        / * Scripting * / 
        lua_State  * lua ;  / * Lua翻译。我们只为所有客户使用一个* / 
        redisClient  * lua_client ;    / *“假客户端”从Lua * / 
        redisClient  * lua_caller 查询Redis ;    / *现在运行EVAL的客户端,或者NULL * / 
        dict  * lua_scripts ;          / * SHA1的字典 - > Lua脚本* / 
        mstime_t  lua_time_limit ;   / *脚本超时(以毫秒为单位)* / 
        mstime_t  lua_time_start ;   / *脚本的开始时间,毫秒时间* / 
        int  lua_write_dirty ;  / *如果在
        执行当前脚本
        期间调用了写入命令,则为True 。* / int  lua_random_dirty ;  / *如果在
        执行当前脚本
        期间调用了随机命令,则为True 。* / int  lua_timedout ;      / *如果我们达到脚本
        执行
        的时间限制,则为True 。* / int  lua_kill ;          / *如果为true,则杀死脚本。* /


        / *断言和错误报告* / 
        char  * assert_failed ; 
        char  * assert_file ; 
        int  assert_line ; 
        int  bug_report_start ;  / *如果已记录错误报告标题,则为True。* / 
        int  watchdog_period ;   / *软件看门狗周期,单位为ms。0 =关* / 
};

程序创建一个的redisServer结构的实例变量server,调用函数initServerConfig(),将server的各个属性初始化为默认值。

server变量的初始化完成之后,程序进入服务器初始化的下一步:读入配置文件。

入读文件配置

在初始化服务器的上一步中,程序为server变量(也即是服务器状态)的各个属性设置了默认值,但这些默认值有时候并不是最合适的:

  • 用户可能想使用AOF持久化,而不是默认的RDB持久化。
  • 用户可能想用其他端口来运行Redis,以避免端口冲突。
  • 用户可能不想使用默认的16个数据库,而是分配更多或更少数量的数据库。
  • 用户可能想对默认的内存限制措施和回收策略做调整。

等等。

为了让使用者按自己的要求配置服务器,Redis允许用户在运行服务器时,提供相应的配置文件(配置文件)或者显示的选项(选项),Redis在server初始化完变量之后,会读入配置文件和选项,然后根据这些配置来对server变量的属性值做相应的修改:

  1. 如果单纯执行redis-server命令,那么服务器以默认的配置来运行Redis。

  2. 另一方面,如果给Redis服务器送入一个配置文件,那么Redis将按配置文件的设置来更新服务器的状态。

    比如说,通过命令,Redis会根据文件的内容来对服务器状态做相应的修改。redis-server /etc/my-redis.confmy-redis.conf

  3. 除此之外,还可以显式地给服务器传入选项,直接修改服务器配置。

    举个例子,通过命令,可以让Redis服务器端口变更为。redis-server --port 1008610086

  4. 当然,同时使用配置文件和显式选项也是可以的,如果文件和选项有冲突的地方,那么优先使用选项所指定的配置值。

    举个例子,如果运行命令,并且也指定了选项,那么服务器将优先使用(实际上是选项指定的值覆盖了配置文件中的值)。redis-server /etc/my-redis.conf --port 10086my-redis.confport--port 10086

其实在读入配置文件前,还要判断是不是sentinel,如果sentinel,还需要通过initSentinelConfig()和initSentinel()初始化,才通过resetServerSaveParams()重置param选项,通过loadServerConfig(configfile,options)读入配置文件和显选项。

创建守护进程

Redis默认不以daemon进程的方式运行。

若服务器初始化进行到这一步时,程序将创建守护进程来运行Redis,并创建相应的pid文件。

服务器初始化模块功能

在这一步,初始化程序完成两件事:

  • server变量的数据结构子属性分配内存。
  • 初始化这些数据结构。

为数据结构分配内存,并初始化这些数据结构,等同于对相应的功能进行初始化。

比如说,当为订阅与发布所需的链表分配内存之后,订阅与发布功能就处于就绪状态,随时可以为Redis所用了。

在这一步,initServer()完成的主要动作如下:

  • 初始化Redis进程的信号功能。
  • 初始化日志功能。
  • 初始化客户端功能。
  • 初始化共享对象。
  • 初始化事件功能。
  • 初始化网络连接。
  • 初始化数据库。
  • 初始化订阅与发布功能。
  • 初始化各个统计变量。
  • 关联服务器常规操作(cron job)到时间事件,关联客户端应答处理器到文件事件。
  • 如果AOF功能已打开,那么打开或创建AOF文件。
  • 设置内存限制。
  • 初始化Lua脚本环境。
  • 初始化慢查询功能。
  • 初始化后台操作线程。

完成这一步之后,服务器redisAsciiArt()打印出Redis的ASCII LOGO,服务器版本等信息,表示所有功能模块已经就绪,可以等待被使用了:

               _._
          _.-``__'' -  ._
     _--``` `_。''-._ Redis 3.0.beta(7a47887b / 1)32位
 。-``。-```。```\ / _。,_'' -  ._
(',.-`|`,)以独立模式运行
|`-._`-...-`__...-。``-._ |'`_.-'| 港口:6379
| `-._`._ / _.-'| PID:6717
 `-._` -._` - 。/ _.-'_.-'
|`-._`-._`-.__.-'_ .-'_ .-'|
| `-._`-._ _.-'_ .-'| http://redis.io
 `-._` -._`-.__.-'_ .-'_.-'
|`-._`-._`-.__.-'_ .-'_ .-'|
| `-._`-._ _.-'_ .-'|
 `-._` -._`-.__.-'_ .-'_.-'
     `-._` -.__.-'_.-'
         `-._ _.-'
             `-.__.-”

虽然所有功能已经就绪,但这时服务器的数据库还是一片空白,程序还需要将服务器上一次执行时记录的数据载入到当前服务器中,服务器的初始化才算真正完成。

数据载入

在这一步,如果不为sentinel,程序需要将持久化在RDB或者AOF文件里的数据,载入到服务器进程里面。

如果服务器有启用AOF功能的话,那么使用AOF文件来还原数据;否则,程序使用RDB文件来还原数据。

当执行完这一步时,服务器打印出一段载入完成信息:

[ 6717 ]  22  Feb  11 :59 :14.830  *  从磁盘加载的DB  :0.068 秒    

如果是集群,还要检查数据的一致性。

事件开始循环

到了这一步,服务器的初始化已经完成,程序打开事件循环,开始接受客户端连接。

以下是服务器在这一步打印的信息:

[ 6717 ]  22  年2月 11 :59 :14.830  *  的 服务器 是 现在 准备 要 接受 连接 的 端口 6379

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值