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字符 ...
随机推荐
- linux常用命令及使用技巧(二)
ls显示指定工作目录下的内容,同windows中的dir命令 pwd命令显示当前工作目录 date命令,显示或修改系统时间与日期 passwd命令,设置用户密码 su命令改变用户身份 clear命令, ...
- OPPO F9 Pro在哪里打开usb调试模式的完美方法
经常我们使用pc通过数据线连接到安卓手机的时候,如果手机没有开启USB调试模式,pc则没能够成功读到我们的手机,此情况我们需要找处理方法将手机的USB调试模式开启,今天我们介绍OPPO F9 Pro如 ...
- 使用X509Certificate2类操作证书文件
public class CertHelper { string pfxPath = @"E:\开发辅助项目\cert\taisuyuanqing.pfx"; string cer ...
- Java实现基于token认证
随着互联网的不断发展,技术的迭代也非常之快.我们的用户认证也从刚开始的用户名密码转变到基于cookie的session认证,然而到了今天,这种认证已经不能满足与我们的业务需求了(分布式,微服务).我们 ...
- 【Linux】Ubuntu安装Mysql 8.0
1.下载Mysql的安装配置,http://dev.mysql.com/downloads/repo/apt/ 2.执行配置配置文件 sudo dpkg -i mysql-apt-config_0.* ...
- Windows环境下应用Java代码操作Linux资源
>>>>>>>>>>>>>>>>>>>>>>>>> ...
- SOUI视频教程
第1讲-开发环境搭建 http://my.tv.sohu.com/us/282307706/89333168.shtml 第2讲- 基本应用程序框架代码解读 http://my.tv.sohu.com ...
- Linux从入门到进阶全集——【第十四集:Shell基础命令】
1,Shell就是命令行执行器 2,作用:将外层引用程序的例如ls ll等命令进行解释成01表示的二进制代码给内核,从而让硬件执行:硬件的执行结果返回给shell,shell解释成我们能看得懂的代码返 ...
- C++第四课:类的使用(二)[个人见解]
前面说到C++类的名字自定义要有含义,成员函数名也同样如此. 一个好的程序员除了让自己能看懂代码外,那是能力,也能让别人看懂,那是本事. 我们来看第一个特性:继承! 什么是继承? 小编不说概念性的定义 ...
- go 结构体
结构体声明 type Employee struct { ID int Name string Address string DoB time.Time Position string Salary ...