不为难自己,迟早会委屈自己!

Redis设计与实现-客户端服务端与事件

事件
  1. redis服务器是事件驱动的,事件分为文件事件与时间事件
    • 文件事件是服务器通过套接字与客户端连接,两者之间的通信会产生相应的文件事件,服务器监听并处理这些事件完成网络操作;
    • 时间事件是指redis服务器的定时操作
  2. redis基于reactor模式的文件事件处理器以单线程方式运行,并使用I/O多路复用程序来监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。;
  3. 文件事件处理器由四部分组成:
    • 套接字
    • I/O多路复用程序:监听多个套接字产生的文件事件并放到一个队列,然后以有序同步,每次一个套接字的方式把产生对应事件的套接字传送给文件事件分派器,待事件处理器处理完后再传送下一个;
    • 文件事件分派器:接收I/O多路复用程序传来的套接字,并根据套接产生的事件类型,调用相应的事件处理器处理;
    • 事件处理器:处理具体事件。如命令请求、回复、连接应答等。
  4. 如果一个套接字即可读又可写,服务器会先读后写。
  5. 时间事件分为定时事件与周期性事件,一个时间事件有3个属性:全局唯一ID、到达时间when、事件处理器timeProc;
  6. 服务器将所有时间事件都放在一个无序链表中,每当时间事件执行器运行时,遍历此链表,查找已到达的事件并调用相应的事件处理器。
  7. redis服务器变等待处理文件事件,并检查当前是否有已经可以处理的时间事件了,对它们的处理是同步有序原子的,不会中途中断事件处理也不会进行抢占,因此各事件处理器要尽可能地减少阻塞时间。比如写入字节超预设常量大小,命令服务器会主动用break跳出写循环并将余下的数据留到下次再写。

以上文字来自Dimmacro,转载请说明来源:http://www.cnblogs.com/dimmacro/ 

客户端
  1. 每个建立连接的客户端,服务端都会为期建立相应的redisClient结构,并追加到redisServer数据机构中用来保存客户端的clients链表中;
  2. 客户端一般有三种:
    • 普通通过connect函数连接到服务器,并有连接事件处理器创建的;
    • Lua脚本伪客户端,服务器初始化时创建并一直存在直到关闭服务器;
    • AOF伪客户端,执行载入AOF时使用,载入完成后会被关闭
  3. redisClient数据结构中重要的一些属性及说明:
    • int fd:表示客户端类型,-1表示伪客户端,大于-1表示普通客户端;
    • name:客户端名字,需要使用CLIENT setname命令设置了才有;
    • flags:客户端目前所处的状态,如主服务器、正在执行事务、阻塞等多种状态;
    • sds querybuf:输入缓冲区用于保存客户端发送过来的命令请求,最大不能超过1GB,否则服务器会关闭此客户端;
    • robj **argv:服务器对命令请求解析后得到的命令及参数,argv[0]表示命令,其他项表示参数
    • struct redisCommand *cmd:保存该命令对应的实现函数,需要的参数,命令总执行次数及消耗时间等;
    • 保存执行命令所得到的回复结果的输出缓冲区,一个固定大小用于短小的回复,最大16KB,一个可变大小用于大数据量的回复;
    • int authenticated:身份验证结果,0表示未通过,1表示通过
    • ctime:客户端被创建的时间戳;
    • lastinteraction:客户端与服务区最后一次交互的时间;
    • obuf_soft_limit_reached_time:输出缓冲区第一次达到软性限制的时间
  4. 客户端被关闭的几种情况:
    • 客户端进程退出或被杀死;
    • 客户端发送了非法命令请求,被服务端关闭;
    • 客户端处于非主从服务器状态,或阻塞订阅状态,且超过timeout设置的时间时,被服务端关闭;
    • 发送命令请求超过1GB;
    • 命令回复超过输出缓冲区的硬性限制,或超过软性限制时间内一致大于软性限制的大小
  5.  

以上文字来自Dimmacro,转载请说明来源:http://www.cnblogs.com/dimmacro/ 

服务端
    1. redis服务端启动初始化的先后过程:
      • 由redis.c/initServerConfig函数初始化服务器状态结构server(此结构为struct redisServer类型的实例变量),并为结构中的各个属性设置默认值,包括设置服务器的运行ID、运行频率、配置文件路径、运行架构、默认端口号、持久化条件、LRU时钟以及创建命令表等一般属性;
      • 载入用户给定配置参数及配置文件,并对server对应默认属性进行更新;
      • 调用initServer函数为以下数据结构分配内存并根据需要为其设置或管理初始化值:
        • server.clients链表:记录所有与服务器相连的客户端的状态结构;
        • serer.db数组:包含所有数据库;
        • 保存频道订阅信息的server.pubsub_channels字典以及保存模式订阅信息的server.pubsub_patterns链表;
        • server.lua:执行lua脚本的lua环境;
        • server.slowlog:保存慢查询日志
      • initServer还会进行一些非常重要的设置,包括:
        • 为服务器设置进程信号处理器;
        • 创建“OK”,“ERR"等常用共享对象,以避免重复创建;
        • 打开服务器监听端口,为监听套接字关联应答事件处理器,等待服务器正式运行时接收客户端的连接;
        • 打开或新建AOF文件,为AOF写入做好准备;
        • 初始化后台I/O模块(bio),为将来的I/O做好准备
      • 载入RDB或AOF文件还原数据库状态
      • 打印ready to accept connections on port XXX,开始接收客户端的连接请求并处理;    
    2. 客户端发送请求命令到得到回复处理的全执行过程:
      • 客户端将redis命令转化成协议格式并发送到服务端;
      • 服务端读取协议格式数据并保存到客户端状态的输入缓冲区;
      • 对输入缓冲区的数据jinx 分析,提取出命令、参数及参数个数;
      • 根据命令在命令表中查找对应的redisCommand对象;
      • 预备及执行前的检测工作:包括参数个数、用户身份、服务器内存占用量、订阅检查、服务器是否正载入、是否开监视器等;
      • 执行redisCommand里的方法,并把回复信息保存到客户端状态的输出缓冲区;
      • 执行后回复前的检测工作:是否增加到慢查询、更新命令耗时及计数器、根据需要写到AOF、必要的命令传播到从服务器;
      • 将输入缓冲区里的回复数据发送到客户端
    3. serverCon函数默认每隔100毫秒执行一次,负责管理服务器资源,其主要执行以下操作:
      • 更新服务器时间缓存unixtime、mstime;
      • 更新LRU时钟;
      • 更新服务器每秒执行命令次数;
      • 更新服务器内存峰值记录;
      • 处理关闭sigterm信号,主要为了关闭前做一些持久化;
      • 管理客户端资源,包括检查客户端与服务端之间连接是否超时、释放过长的客户端缓冲区;
      • 管理数据库资源,调用databaseCron函数对一些数据库检查,删除过期键,并根据需要进行字典收缩;
      • 执行因bgsave被延迟的bgrewriteaof;
      • 检查持久化操作的运行状态,包括是否完成、是否正在进行或是否需要进行持久化等;
      • 如果开启了AOF,将AOF缓冲区中的内容写到AOF文件;
      • 关闭输出缓冲区大小超过显示的客户端;
      • 增加记录serverCron函数执行次数的cronloops计数器值

以上文字来自Dimmacro,转载请说明来源:http://www.cnblogs.com/dimmacro/ 

posted on 2016-12-07 18:45  Dimmacro  阅读(1007)  评论(1编辑  收藏  举报