redis系列(六)客户端和服务器

  • redis 客户端
  • redis 服务器

         之前的内容主要介绍的是 Redis 的数据结构以及单机数据库的一些实现,接下来我会介绍多机数据库的实现以及一些独立功能的实现。这篇博客比较简单,主要是刚开始的时候不必对 redis 内部的一些有太清楚的了解。待基本上对 redis 有一个初步的了解之后,在对 redis 内部的一些原理,函数以及源码进行研究,这个我会在后续的时候将内容补上,所以,现在先做一个初步的了解即可。源码、思想、和原理是任何框架的核心,这个需要慢慢的去研究和学习。

    1.  redis 客户端

          redis 服务器是典型的一对多服务器程序:一个服务器可以和多个客户端建立网络连接,每个客户端可以向服务器发送命令请求,而服务器则接收并处理客户端发送的请求,并向客户端返回相应的信息。

          redis 服务器通过使用 I/O 多路复用技术实现的文件处理系统,Redis 服务器使用单线程单进程的方式处理命令请求,并与多个客户端进行网络通信。

          对于每一个与服务器进行连接的客户端,服务器都为这些客户端建立相应的 redis.h/redisClient 结构,这个结构保存了客户端当前的状态信息,以及执行相关功能时需要用到的数据结构。

      1.1  客户端属性

           客户端属性分为两种,通用的属性:无论客户端执行什么工作,都会用到这些属性。 特殊功能的属性:这是一类和特殊功能相关的属性。我主要介绍的是通用属性,至于特殊属性,我会在介绍特殊功能的时候介绍。

  • 套接字描述符

          我们都知道套接字是网络通信端点,包含了进行网络通信必要的五种信息:源 ip 地址,源端口,目标 ip 地址,目标端口,以及连接使用的协议。

          Redis 客户端状态的 fd 属性记录了客户端正在使用的套接字描述符。根据客户端类型的不同,fd 属性的值可以是 -1,或者是大于 -1 的整数。

          伪客户端:我在上一篇博客中说到过伪客户端其实就是没有网络连接的伪客户端,所以这中客户端不需要套接字 ,Redis 服务器中有两个地方用到伪客户端,一个是用于载入 AOF 文件并还原数据库状态,另一个是在执行 Lua 脚本中包含的 Redis。这个时候的 fd 是 -1。

          普通客户端:普通客户端的套接字描述符的值必须是大于 -1 的值。

  • 名字

          在默认无情况下,一个连接到服务武器的客户端是没有名字的,只有在使用 CLIENT setname 的时候,客户端才会有一个名字,让客户端的身份变得更清晰,这个属性记录在客户端状态的name属性中。具体的一些操作,我会在之后介绍集群的时候通过实实在在的例子来说明。

  • 标志

          这个属性在集群中很重要,它记录了客户端的角色和客户端目前所处的状态。它在 redisClient 中的 flags 中记录,flags 可以是单个标志,也可以是多个标志,每个标志使用一个常量表示,一部分记录了客户端的角色,一部分记录了客户端的状态。比如,我们在做主从的时候,需要一个 master 和一个 slave ,而这个角色就是在这里做的标志。至于客户端的状态,读者可以自己进行查阅。

  • 输入缓冲区

          输入缓冲区,我在上一篇博客中也做了介绍,它在 redisClient 的 querybuf 中记录,输入缓冲区的大小会 根据输入内容的大小动态的缩小或者扩大,但它的最大不能超过 1GB ,否则服务器会将这个客户端关闭。

  • 命令与命令参数

          服务器将客户端发出的请求保存到 querybuf 中之后,redis 服务器会对放出请求的命令内容进行分析,并将得出的命令参数和命令参数的个数分别保存到 argv 和 argc 中。argv 属性是一个数组,其中 argv[ 0 ] 放的是要执行的命令,而其之后存放的是参数。

  • 命令的实现函数

          我们知道执行一条命令其实就是执行服务器的一个函数,redis 中有一个命令列表,其中存放着 redis 服务器的所有命令,这个命令列表是一个字典表,key 为命令的名称,而 value 存放的是一个 redisCommand 结构,当服务器分析并得到 argv 和 argc 属性值后,服务器会根据 argv[ 0 ] 来找到命令列表中所对应的函数结构,然后将 服务器状态中的 cmd 属性指向这个结构,之后服务器就可以通过这些信息,调用实现函数,执行客户端发出的请求命令。

  • 输出缓冲区

          输入有对应的缓冲区,输入也同样拥有对应的输出缓冲区,执行完命令之后,服务器会返回一个命令回复,这个回复就保存在客户端状态的输出缓冲区内。每个客户端都有两个缓冲区可用,一个大小固定的,一个大小可变的。大小固定的存放的是那些长度比较小的,比如 OK、简单的字符串;大小可变得存放的是一些比较复杂的回复。

  • 身份验证,

          客户端状态的 authenticated 属性记录了客户端是否通过了身份验证:如果其值为 0 ,那么表示客户端未通过身份验证,这种客户端只能执行  AUTO 命令,其他所有的命令都会被服务器拒绝,客户端通过该命令通过身份验证之后,这个值就会变为 1。

  • 时间

          比如 ctime  记录了客户端创建的时间,lastinteraction 属性记录了客户端与服务器最后一次进行交互的时间。

    2.  服务器

         Redis 服务器负责与多个客户端进行连接,处理客户端发送的命令请求,在数据库中保存客户端执行命令所产生的数据,并通过资源管理来维持服务器的运转。

     2.1  命令请求的过程

  • 发送命令请求

          Redis 服务器的请求命令来自 Redis客户端,当用户键入一条命令的时候,客户端会将这个命令请求转换成协议格式,然后通过连接到服务器的套接字,将协议格式的命令请求发送给服务器。

  • 读取命令请求

          首先,他会读取套接字中协议格式的命令请求,并保存到输入缓冲区内;之后,对输入缓冲区内的命令进行解析,获得命令请求参数和参数的个数,并分别将参数和参数的个数放到 argv 和 argc 中。然后,执行命令执行器,获得 argv[ 0 ] 内的命令,在命令列表中找到对应的命令,并通过 cmd 指向这个对象,其最后指向的其实是一个命令结构,接着它会进行一系列的判断,判断当前的这个命令是否可行,比如说看看这个 cmd 指向的对象是不是一个null ,命令的参数和格式是否正确,用户的身份是否经过验证,以及是否开启了事物和监视等等。只有经过这一些准备之后才会开始执行这个命令;执行一个命令其实归根到底就是调用了一个函数。

  • 命令执行器

          当服务器与客户端之间的连接套接字 标识符因为客户端的写入变得可读的时候,服务器将 调用命令请求处理器来执行一些操作。

  • 将命令回复发送给客户端

          命令实现函数会将命令回复保存到客户端的输出缓冲区内,并为客户端的套接字关联命令回复处理器。

  • 客户端接收并打印命令回复

          这个就简单了,就是打印而已。

      2.2  初始化服务器

  • 初始化服务器状态结构

          初始化 server 变量的工作由 redis.c/initServerConfig 函数完成,以下是这个函数最开头的一部分代码。

         

          getRandomHexChars(server.runid,REDIS_RUN_ID_SIZE):设置服务器的运行id

          server.configfile = NULL:设置默认配置文件路径

          server.hz = REDIS_DEFAULT_HZ:设置默认服务器频率

          server.runid[REDIS_RUN_ID_SIZE] = '\0':为运行id加上结尾字符

          server.arch_bits = (sizeof(long) == 8) ? 64 : 32  设置服务器的运行架构

          server.port = REDIS_SERVERPORT:设置默认服务器端口

  • 载入配置文件

          在启动 redis 服务器的时候,我们可以通过修改服务器的给定的一些配置参数或者指定配置文件来修改服务器的默认配置。

          比如说我们输入 redis-server --port 910317 就可以将 redis 服务器的端口修改为 910317

  • 初始化服务器数据结构

           在执行 initServerConfig 函数初始化 server 状态时,程序只键了命令表一个数据结构,实际上 redis 还有很多的数据结构。比如 server.clients 链表,这个链表记录了所有与服务器相连的状态结构,链表的每个节点都包含了一个 redisClients 结构实例。再比如说,server.db 数组,数组中包含了服务器的所有数据库。还有一个数据结构值得提一下,那就是 server.pubsub_channels 字典,它是用于保存频道订阅信息的,以及用于保存模式订阅信息的 server.pubsub_patterns。

          当初始化服务器进行到这一步的时候,服务器将调用 initServer 函数,为以上提到的数据结构分配内存,并在需要的时候,为这些数据结构设置或者关联初始化值。

          你可能会奇怪,为什么服务器不在调用 initSserverConfig 的时候就将服务器的数据结构一同初始化,这是因为必须先载入用户指定的配置选项,才能正确的对数据结构进行初始化,如果服务器在进行 initServerConfig 的时候就对数据结构初始化完毕,那么一旦用户通过配置选项修改了和数据结构相关的服务状态属性,服务器就需要重新修改调整已创建的数据结构,这样是会影响效率的。

  • 还原数据库状态

          在完成对服务器状态 server 变量的初始化之后,服务器需要载入 RDB 文件或者 AOF 文件,并根据文件记录的内容来还原服务器的数据库状态。

  • 执行事件循环

          这个就不多做介绍了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值