Redis 设计与实现 (四)--事件、客户端
事件
一、文件事件
文件事件处理器使用I/O多路复用程序来同时监听多个套接字,
监听套接字,分配对应的处理事件。
四个组成部分:套接字 、I/O多路复用 、 文件事件分派器 、 事件处理器
连接应答处理器:redis服务器初始化,将连接应答处理器和服务器监听套接字的事件惯量,当客户端使用connect 函数链接服务器,套接字产生事件,触发连接应答处理器。
命令请求处理器:客户端向服务器发送命令请求的时候,套接字产生事件,触发命令请求处理器处理请求。
命令回复处理器:服务器命令回复传给客户端,服务器将命令回复处理器和事件关联,客户端准备接收回传命令后,触发事件引发回复处理器执行。
二、时间事件
当被监听的套接字准备好执行链接应答、读取、写入、关闭等操作时,
与操作相对性的文件事件就会产生。
定时事件:指定时间执行一次程序
周期事件:隔一定时间执行一次程序
id -- 服务器为时间事件创建的全局唯一ID,递增
when -- 毫秒,记录事件的触发时间
timeProc -- 时间事件处理器
客户端
struct redisServer {
/* General */
pid_t pid; /* Main process pid. */
char *configfile; /* Absolute config file path, or NULL */
int hz; /* serverCron() calls frequency in hertz */
redisDb *db;
dict *commands; /* Command table */
dict *orig_commands; /* Command table before command renaming. */
aeEventLoop *el;
unsigned lruclock:REDIS_LRU_BITS; /* Clock for LRU eviction */
int shutdown_asap; /* SHUTDOWN needed ASAP */
int activerehashing; /* Incremental rehash in serverCron() */
char *requirepass; /* Pass for AUTH command, or NULL */
char *pidfile; /* PID file path */
int arch_bits; /* 32 or 64 depending on sizeof(PORT_LONG) */
int cronloops; /* Number of times the cron function run */
char runid[REDIS_RUN_ID_SIZE+]; /* ID always different at every exec. */
int sentinel_mode; /* True if this instance is a Sentinel. */
/* Networking */
int port; /* TCP listening port */
int tcp_backlog; /* TCP listen() backlog */
char *bindaddr[REDIS_BINDADDR_MAX]; /* Addresses we should bind to */
int bindaddr_count; /* Number of addresses in server.bindaddr[] */
char *unixsocket; /* UNIX socket path */
mode_t unixsocketperm; /* UNIX socket permission */
int ipfd[REDIS_BINDADDR_MAX]; /* TCP socket file descriptors */
int ipfd_count; /* Used slots in ipfd[] */
int sofd; /* Unix socket file descriptor */
int cfd[REDIS_BINDADDR_MAX];/* Cluster bus listening socket */
int cfd_count; /* Used slots in cfd[] */
list *clients; /* List of active clients */
list *clients_to_close; /* Clients to close asynchronously */
list *slaves, *monitors; /* List of slaves and MONITORs */
redisClient *current_client; /* Current client, only used on crash report */
int clients_paused;
一、输入缓冲区
客户端状态的输入缓冲区(sds querybuf)用于保存客户端发送的命令请求
命令:robj **argv 命令参数
int argc 命令参数个数
从服务器协议中分析参数和个数,根据argv[0]查找对应的命令函数
二、输出缓冲区
执行命令后的回复被保存再客户端状态的输出缓冲区里面。
--固定缓冲区,长度较小的回复
--可变缓冲区,长度较大的回复
三、身份验证
int authenticated
==0 未验证 ; ==1 客户端通过了验证
四、时间
timt_t ctime --创建客户端的时间,可计算连接时长(秒)
timt_t lastinteraction --客户端和服务器最后一次通信时间,可用来计算客户端空转时间
time_t obuf_soft_limit_reached_time --输出缓冲区第一次到达软性限制的时间
五、客户端的创建与关闭
服务器使用不同的方式来创建和关闭不同类型的客户端
5.1 创建普通客户端
5.2 关闭普通客户端
--客户端进程退出或者杀死
--客户端向服务器发送不符合协议格式的命令请求
--客户端设置了timeout 配置选项
--客户端发送的命令请求大小超过了输出缓冲区限制大小,默认1G (硬性限制和软性限制)
5.3 Lua脚本的伪客户端
服务器初始化创建负责执行Lua脚本中包含redis命令的伪客户端
5.4 AOF 文件的伪客户端
服务器再载入AOF文件时,创建用于执行AOF文件包含的redis命令的伪客户端,并在载入完成后关闭。
六、服务器
6.1 命令请求的执行过程
客户端发送命令 set key value 收到回复 ok
1、客户端发送 set key value
2、服务端接收处理客户端的命令请求,产生回复ok
3、将命令回复给客户端
4、客户端接收回复,打印展示
6.2 发送命令请求
用户-> 请求-> 客户端-> 协议转换-> 服务器
6.3读取命令请求
读取命令->保存到客户状态的缓冲区->分析命令->调用命令执行器
6.4 将命令回复发给客户端
6.5客户端接收并打印命令回复
服务器->客户端->用户
七、serverCron函数
serverCron 默认100毫秒执行一次,管理服务器资源。
7.1、更新服务器时间缓存
因为获取系统当前时间操作比较频繁,所以缓存系统时间。
所以缓存当前时间:
unixtime //秒级
mstime //毫秒
7.2、更新LRU时钟
lruclock 服务器的LRU时钟
7.3、更新服务器每秒执行命令次数
trackOperationsPerSecond //估算服务器再最近一秒钟的处理请求数量
7.4、更新服务器内存峰值记录
程序会查看当前使用的内存的数量
7.5、处理sigterm信号
7.6、管理客户端资源
超时--释放资源
超出输入缓冲区长度--释放资源
7.7、管理服务器资源
删除过期键,收缩字典
7.8、执行延迟的bgrewriteaof
执行延迟的重写aof操作
7.9、检查持久化操作的运行状态
rdb_child_pid -- bgsave命令子进程id
aof_child_pid --bgrewriteaof命令子进程id
可以检查是否有正在执行以上命令。
7.10 AOF缓冲区的内容写入AOF文件
启动持久化,会将缓冲区内容写入到aof文件
7.11 关闭异步客户端
7.12 增加 cronloops 计数器值
cronloops --记录serverCron函数执行次数
八、初始化服务器
redis 服务器启动到接受客户端命令,需要一系列初始化和设置过程。
8.1 初始化服务器状态结构
创建一个 redisServer结构,并赋值默认值initServerConfig函数。
struct redisServer
void initServerConfig(void) {
int j;
getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE);
server.configfile = NULL;
server.hz = REDIS_DEFAULT_HZ;
server.runid[REDIS_RUN_ID_SIZE] = '\0';
server.arch_bits = (sizeof(PORT_LONG) == ) ? : ;
server.port = REDIS_SERVERPORT;
server.tcp_backlog = REDIS_TCP_BACKLOG;
server.bindaddr_count = ;
server.unixsocket = NULL;
server.unixsocketperm = REDIS_DEFAULT_UNIX_SOCKET_PERM;
server.ipfd_count = ;
server.sofd = -;
server.dbnum = REDIS_DEFAULT_DBNUM;
server.verbosity = REDIS_DEFAULT_VERBOSITY;
WIN32_ONLY(setLogVerbosityLevel(server.verbosity);)
server.maxidletime = REDIS_MAXIDLETIME;
server.tcpkeepalive = REDIS_DEFAULT_TCP_KEEPALIVE;
server.active_expire_enabled = ;
server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
server.saveparams = NULL;
server.loading = ;
server.logfile = zstrdup(REDIS_DEFAULT_LOGFILE);
server.syslog_enabled = REDIS_DEFAULT_SYSLOG_ENABLED;
server.syslog_ident = zstrdup(REDIS_DEFAULT_SYSLOG_IDENT);
POSIX_ONLY(server.syslog_facility = LOG_LOCAL0;)
server.daemonize = REDIS_DEFAULT_DAEMONIZE;
server.aof_state = REDIS_AOF_OFF;
server.aof_fsync = REDIS_DEFAULT_AOF_FSYNC;
server.aof_no_fsync_on_rewrite = REDIS_DEFAULT_AOF_NO_FSYNC_ON_REWRITE;
server.aof_rewrite_perc = REDIS_AOF_REWRITE_PERC;
server.aof_rewrite_min_size = REDIS_AOF_REWRITE_MIN_SIZE;
server.aof_rewrite_base_size = ;
server.aof_rewrite_scheduled = ;
server.aof_last_fsync = time(NULL);
server.aof_rewrite_time_last = -;
server.aof_rewrite_time_start = -;
server.aof_lastbgrewrite_status = REDIS_OK;
server.aof_delayed_fsync = ;
server.aof_fd = -;
server.aof_selected_db = -; /* Make sure the first time will not match */
server.aof_flush_postponed_start = ;
server.aof_rewrite_incremental_fsync = REDIS_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
server.aof_load_truncated = REDIS_DEFAULT_AOF_LOAD_TRUNCATED;
server.pidfile = zstrdup(REDIS_DEFAULT_PID_FILE);
server.rdb_filename = zstrdup(REDIS_DEFAULT_RDB_FILENAME);
server.aof_filename = zstrdup(REDIS_DEFAULT_AOF_FILENAME);
server.requirepass = NULL;
server.rdb_compression = REDIS_DEFAULT_RDB_COMPRESSION;
server.rdb_checksum = REDIS_DEFAULT_RDB_CHECKSUM;
server.stop_writes_on_bgsave_err = REDIS_DEFAULT_STOP_WRITES_ON_BGSAVE_ERROR;
server.activerehashing = REDIS_DEFAULT_ACTIVE_REHASHING;
server.notify_keyspace_events = ;
server.maxclients = REDIS_MAX_CLIENTS;
server.bpop_blocked_clients = ;
server.maxmemory = REDIS_DEFAULT_MAXMEMORY;
server.maxmemory_policy = REDIS_DEFAULT_MAXMEMORY_POLICY;
server.maxmemory_samples = REDIS_DEFAULT_MAXMEMORY_SAMPLES;
server.hash_max_ziplist_entries = REDIS_HASH_MAX_ZIPLIST_ENTRIES;
server.hash_max_ziplist_value = REDIS_HASH_MAX_ZIPLIST_VALUE;
server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
server.list_max_ziplist_value = REDIS_LIST_MAX_ZIPLIST_VALUE;
server.set_max_intset_entries = REDIS_SET_MAX_INTSET_ENTRIES;
server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
server.hll_sparse_max_bytes = REDIS_DEFAULT_HLL_SPARSE_MAX_BYTES;
server.shutdown_asap = ;
server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;
server.repl_timeout = REDIS_REPL_TIMEOUT;
server.repl_min_slaves_to_write = REDIS_DEFAULT_MIN_SLAVES_TO_WRITE;
server.repl_min_slaves_max_lag = REDIS_DEFAULT_MIN_SLAVES_MAX_LAG;
server.cluster_enabled = ;
server.cluster_node_timeout = REDIS_CLUSTER_DEFAULT_NODE_TIMEOUT;
server.cluster_migration_barrier = REDIS_CLUSTER_DEFAULT_MIGRATION_BARRIER;
server.cluster_slave_validity_factor = REDIS_CLUSTER_DEFAULT_SLAVE_VALIDITY;
server.cluster_require_full_coverage = REDIS_CLUSTER_DEFAULT_REQUIRE_FULL_COVERAGE;
server.cluster_configfile = zstrdup(REDIS_DEFAULT_CLUSTER_CONFIG_FILE);
server.lua_caller = NULL;
server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
server.lua_client = NULL;
server.lua_timedout = ;
server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);
server.next_client_id = ; /* Client IDs, start from 1 .*/
server.loading_process_events_interval_bytes = (**);
server.lruclock = getLRUClock();
resetServerSaveParams();
appendServerSaveParams(*,); /* save after 1 hour and 1 change */
appendServerSaveParams(,); /* save after 5 minutes and 100 changes */
appendServerSaveParams(,); /* save after 1 minute and 10000 changes */
/* Replication related */
server.masterauth = NULL;
server.masterhost = NULL;
server.masterport = ;
server.master = NULL;
server.cached_master = NULL;
server.repl_master_initial_offset = -;
server.repl_state = REDIS_REPL_NONE;
server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
server.repl_serve_stale_data = REDIS_DEFAULT_SLAVE_SERVE_STALE_DATA;
server.repl_slave_ro = REDIS_DEFAULT_SLAVE_READ_ONLY;
server.repl_down_since = ; /* Never connected, repl is down since EVER. */
server.repl_disable_tcp_nodelay = REDIS_DEFAULT_REPL_DISABLE_TCP_NODELAY;
server.repl_diskless_sync = REDIS_DEFAULT_REPL_DISKLESS_SYNC;
server.repl_diskless_sync_delay = REDIS_DEFAULT_REPL_DISKLESS_SYNC_DELAY;
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
server.master_repl_offset = ;
/* Replication partial resync backlog */
server.repl_backlog = NULL;
server.repl_backlog_size = REDIS_DEFAULT_REPL_BACKLOG_SIZE;
server.repl_backlog_histlen = ;
server.repl_backlog_idx = ;
server.repl_backlog_off = ;
server.repl_backlog_time_limit = REDIS_DEFAULT_REPL_BACKLOG_TIME_LIMIT;
server.repl_no_slaves_since = time(NULL);
/* Client output buffer limits */
for (j = ; j < REDIS_CLIENT_TYPE_COUNT; j++)
server.client_obuf_limits[j] = clientBufferLimitsDefaults[j];
/* Double constants initialization */
R_Zero = 0.0;
R_PosInf = 1.0/R_Zero;
R_NegInf = -1.0/R_Zero;
R_Nan = R_Zero/R_Zero;
/* Command table -- we initiialize it here as it is part of the
* initial configuration, since command names may be changed via
* redis.conf using the rename-command directive. */
server.commands = dictCreate(&commandTableDictType,NULL);
server.orig_commands = dictCreate(&commandTableDictType,NULL);
populateCommandTable();
server.delCommand = lookupCommandByCString("del");
server.multiCommand = lookupCommandByCString("multi");
server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop");
/* Slow log */
server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
/* Latency monitor */
server.latency_monitor_threshold = REDIS_DEFAULT_LATENCY_MONITOR_THRESHOLD;
/* Debugging */
server.assert_failed = "<no assertion failed>";
server.assert_file = "<no file>";
server.assert_line = ;
server.bug_report_start = ;
server.watchdog_period = ;
}
设置服务器运行ID
设置服务器的默认运行频率
设置服务器的默认配置文件路径
设置服务器运行架构
设置服务器默认的端口号
设置服务器的默认rdb持久化条件和aof持久化条件
初始化服务器的LRU时钟
创建命令表
8.2 载入配置选项
用户可以通过给定的配置参数或者指定配置文件路径覆盖服务器默认配置
8.3 初始化服务器的数据结构
server.clients 链表 , 记录所有与服务器连接的客户端状态结构
server.db 数组,包含所有数据库
server.pubsub_channels 字典,保存频道订阅信息
server.pubsub_patterns 字段,保存模式订阅信息
server.lua 执行lua脚本的lua环境
server.slowlog 属性,保存慢查询的日志
8.4 还原数据库状态
开启AOF持久化,aof文件来还原
没有开启AOF,使用本地RDB文件还原
Redis 设计与实现 (四)--事件、客户端的更多相关文章
- 【笔记】《Redis设计与实现》chapter13 客户端
服务器为每个客户端建立相应的redis.h/redisClient结构,这个结构保存了客户端当前的状态信息 客户端的套接字描述符 客户端的名字 客户端的标志值 只想客户端正在使用的数据库的指针,以及该 ...
- Redis设计与实现2.1:数据库和事件
数据库和事件 这是<Redis设计与实现>系列的文章,系列导航:Redis设计与实现笔记 数据库 数据库的结构定义在 redis.h/redisServer 这个结构体中,这个结构体有许多 ...
- 【Redis】四、Redis设计原理及相关问题
(六)Redis设计原理及相关问题 通过前面关于Redis五种数据类型.相关高级特性以及一些简单示例的使用,对Redis的使用和主要的用途应该有所掌握,但是还有一些原理性的问题我们在本部分做一个探 ...
- 《Redis设计与实现》读书笔记
<Redis设计与实现>读书笔记 很喜欢这本书的创作过程,以开源的方式,托管到Git上进行创作: 作者通读了Redis源码,并分享了详细的带注释的源码,让学习Redis的朋友轻松不少: 阅 ...
- 《Redis设计与实现》
<Redis设计与实现> 基本信息 作者: 黄健宏 丛书名: 数据库技术丛书 出版社:机械工业出版社 ISBN:9787111464747 上架时间:2014-6-3 出版日期:2014 ...
- 探索Redis设计与实现10:Redis的事件驱动模型与命令执行过程
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- 探索Redis设计与实现9:数据库redisDb与键过期删除策略
本文转自互联网 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial ...
- Redis设计原理简介
学完MySQL InnoDB之后,又开始学习和研究Redis. 首先介绍下书:<Redis设计与实现>第二版 黄健宏著,机械工业出版社,388页,基于redis3.0版本.版本有点低,这个 ...
- Redis | 第5章 Redis 中的持久化技术《Redis设计与实现》
目录 前言 1. RDB 持久化 1.1 RDB 文件的创建与载入 1.2 自动间隔性保存 1.2.1 设置保存条件 1.2.2 dirty 计数器和 lastsave 属性 1.2.3 检查保存条件 ...
- 《Redis设计与实现》知识点目录
Redis设计与实现 第一部分 数据结构与对象 第二章 简单动态字符串 p8 简单动态字符串SDS 2.1 SDS的定义 p9 每个sds.h/sdshdr结构表示一个SDS值 2.2 SDS与C字符 ...
随机推荐
- h5解决键盘谈起,输入框失去焦点
- buaaoo_second_improvement
你不优化,我不优化,那大家就都是满分啦 (一)写在最前 电梯问题由于和实际关联比较紧密,所以实际上可以操作的空间比较多. 但第二单元的电梯,需要实现捎带:第三单元的电梯,需要实现楼层限制.人数限制.三 ...
- (转载)C# 枚举 FlagsAttribute用法
这是读过几篇文章后发现整理的最完整的一篇文章 转载地址:枚举特性FlagsAttribute的用法 先看官方的解释:指示可以将枚举作为位域(即一组标志)处理. 看起来并不好理解,到底什么是作为位域处理 ...
- GitHub上受欢迎的Android UI Library
GitHub上受欢迎的Android UI Library 内容 抽屉菜单 ListView WebView SwitchButton 按钮 点赞按钮 进度条 TabLayout 图标 下拉刷新 Vi ...
- nginx 常用命令
-?,-h : this help -v : show version and exit -V : show version and c ...
- nginx 将请求全部指向到一个页面
个人需要,所以试写了下,使用的是nginx. 文件目录 --------------------- 作者:z8853797 来源:CSDN 原文:https://blog.csdn.net/z8853 ...
- Java基础知识总结(超级经典)
Java基础知识总结(超级经典) 写代码: 1,明确需求.我要做什么? 2,分析思路.我要怎么做?1,2,3. 3,确定步骤.每一个思路部分用到哪些语句,方法,和对象. 4,代码实现.用具体的java ...
- Codeforces 126B. Password (KMP)
<题目链接> 题目大意:给定一个字符串,从中找出一个前.中.后缀最长公共子串("中"代表着既不是前缀,也不是后缀的部分). 解题分析:本题依然是利用了KMP中next数 ...
- 本机是wifi,虚拟机无法连接外网问题
1.首先看自己本机的各网口是否都启动. 2.在虚拟机中的虚拟网络编辑器中,选择桥接模式,并选择对应第一步的WLAN端口. 3.在虚拟机设置中选择自定义,选择第二部中选的VMnet2即可上网了.
- Core Location实现定位
2013/4/22记录: 注意:如非必要,不应该多次轮询位置信息i 使用时,可根据需要制定精度,通过仔细制定所需的绝对最低精度级别,可以纺织不必要的电池消耗. 导入:CoreLocation.f ...