`

redis 客户端实现

阅读更多
        Redis 服务器是典型的一对多服务器程序,通过使用由 I/O 多路复用技术实现的文件事件处理器,Redis 服务器使用单进程单线程的方式来处理命令请求,并与多个客户端进行网络通信。Redis 服务器状态结构 redisServer 的 clients 链表属性就保存了所有连接的客户端的状态结构,对客户端执行批量操作或者查找某个指定的客户端,都可以通过遍历 clients 链表来完成。
        对于每个连接的客户端,服务器都为之建立了相应的 redisClient 结构,其中保存了客户端当前的状态信息,以及执行相关功能时需要用到的数据结构,包括:
        * 客户端的套接字描述符。
        * 客户端的名字。
        * 客户端的标志值。
        * 指向客户端正在使用的数据库的指针,以及该数据库的号码。
        * 客户端当前要执行的命令、命令的参数、参数的个数,以及指向命令实现函数的指针。
        * 客户端的输入缓冲区和输出缓冲区。
        * 客户端的复制状态信息,以及进行复制所需的数据结构。
        * 客户端执行 BRPOP、BLPOP 等列表阻塞命令时使用的数据结构。
        * 客户端的事务状态,以及执行 WATCH 命令时使用的数据结构。
        * 客户端执行发布与订阅功能时用到的数据结构。
        * 客户端的身份验证标志。
        * 客户端的创建时间、和服务器最后一次通信的时间,以及输出缓冲区大小超出软性限制的时间。
        接下来就将对客户端状态中比较通用的那部分属性进行介绍,并讲述服务器创建并关闭各种不同类型的客户端的方法,至于那些和特定功能相关的属性则留待后面介绍相应功能时再行介绍。

        一、客户端的套接字描述符和名字
        客户端状态结构 redisClient 的 fd 属性记录了客户端正在使用的套接字描述符,根据客户端类型的不同,fd 属性的值可以是 -1 或者是大于 -1 的整数:
        1)伪客户端(fake client)的 fd 属性的值为 -1:目前 Redis 服务器会在两个地方用到伪客户端,一个是用于载入 AOF 文件并还原数据库状态,另一个是用于执行 Lua 脚本中包含的 Redis 命令。这种客户端不需要用到网络,自然也不需要记录套接字描述符。
        2)普通客户端的 fd 属性的值为大于 -1 的整数。
        客户端的名字记录在 redisClient 结构的 name 属性里面。默认情况下,一个连接到服务器的客户端是没有名字的,使用“CLIENT SETNAME”命令可以为客户端设置一个名字,让其身份变得更清晰。
        Redis 命令“CLIENT LIST”的输出中,fd 字段和 name 字段就对应该客户端的套接字描述符和名字。

        二、标志
        redisClient 结构的 flags 标志属性记录了客户端的角色,以及客户端目前所处的状态。flags 属性的值可以是单个标志,也可以是多个标志的二进制或,每个标志使用一个常量表示,一部分标志记录了客户端的角色:
        * REDIS_MASTER 和 REDIS_SLAVE:在主从服务器进行复制操作时,主服务器会成为从服务器的客户端,而从服务器也会成为主服务器的客户端。REDIS_MASTER 标志表示客户端代表的是一个主服务器,REDIS_SLAVE 标志则表示客户端代表的是一个从服务器。
        * REDIS_PRE_PSYNC:表示客户端代表的是一个版本低于 Redis2.8 的从服务器,主服务器不能使用 PSYNC 命令与这个从服务器进行同步。该标志只能在 REDIS_SLAVE 标志处于打开状态时使用。
        * REDIS_LUA_CLIENT:表示客户端是专门用于处理 Lua 脚本里面包含的 Redis 命令的伪客户端。
        而另外一部分标志则记录了客户端目前所处的状态:
        * REDIS_MONITOR:表示客户端正在执行 MONITOR 命令。
        * REDIS_UNIX_SOCKET:表示服务器使用 UNIX 套接字来连接客户端。
        * REDIS_BLOCKED:表示客户端正在被 BRPOP、BLPOP 等命令阻塞。
        * REDIS_UNBLOCKED:表示客户端已经从 REDIS_BLOCKED 标志所表示的阻塞状态中脱离出来,不再阻塞。
        * REDIS_MULTI:表示客户端正在执行事务。
        * REDIS_DIRTY_CAS 和 REDIS_DIRTY_EXEC:前者表示事务使用 WATCH 命令监视的数据库键已经被修改,后者表示事务在命令入队时出现了错误。这两个标志只能在打开了 REDIS_MULTI 标志的情况下使用,它们都表示事务的安全性已被破坏,只要其中任意一个被打开,EXEC 命令必然会执行失败。
        * REDIS_CLOSE_ASAP:表示客户端的输出缓冲区大小超出了服务器允许的范围,服务器会在下一次执行 serverCron 函数时关闭这个客户端,以免服务器的稳定性受到这个客户端影响。积存在输出缓冲区中的所有内容会直接被释放,不会返回给客户端。
        * REDIS_CLOSE_AFTER_REPLY:表示有用户对这个客户端执行了“CLIENT KILL”命令,或者客户端发送给服务器的命令请求中包含了错误的协议内容。服务器会将客户端积存在输出缓冲区中的所有内容发送给客户端,然后关闭客户端。
        * REDIS_ASKING:表示客户端向集群节点(运行在集群模式下的服务器)发送了 ASKING 命令。
        * REDIS_FORCE_AOF 和 REDIS_FORCE_REPL:前者强制服务器将当前执行的命令写入到 AOF 文件,后者强制主服务器将当前执行的命令复制给所有从服务器。执行 PUBSUB 命令会使客户端打开 REDIS_FORCE_AOF 标志(因为 PUBSUB 命令会使所有接收到订阅消息的客户端的状态发生改变),执行“SCRIPT LOAD”命令则会使客户端同时打开这两个标志(因为这个命令会修改服务器的状态)。
        * REDIS_MASTER_FORCE_REPLY:在主从服务器进行命令传播期间,从服务器需要向主服务器发送“REPLICATION ACK”命令,在发送该命令之前,从服务器必须打开主服务器对应的客户端的这个标志,否则发送操作会被拒绝执行。

        三、输入缓冲区、命令与命令参数及命令的实现函数
        redisClient 结构的 querybuf 属性表示客户端的输入缓冲区,用于保存客户端发送的命令请求序列。比如,在收到“SET key value”的命令请求后,querybuf 属性将是一个包含内容为“*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n”的 SDS 值。输入缓冲区的大小会根据输入内容动态地缩小或扩大,但最大不能超过 1GB,否则服务器将关闭这个客户端。
        在服务器将命令请求序列保存到 querybuf 属性后,就将对其内容进行分析,并将得出的命令参数以及参数个数分别保存到 redisClient 结构的 argv 数组属性和 argc 熟悉中。数组元素 argv[0] 就是要执行的命令,之后的项则是传给该命令的参数。argc 属性则负责记录 argv 数组的长度(包括 argv[0])。
        当服务器从协议内容中得出 argv 和 argc 属性的值之后,服务器将根据 argv[0] 的值(不区分大小写)在命令表中查找对应的命令实现函数。该表是一个字典,字典的键是一个 SDS 结构,保存了命令的名字,字典的值是命令所对应的 redisCommand 结构,该结构保存了命令的实现函数、命令的标志、命令应该给定的参数个数、命令的总执行次数和总消耗时长等统计信息。在成功找到 argv[0] 对应的 redisCommand 结构后,服务器就会将 redisClient 结构的 cmd 属性指向这个结构。之后,服务器就可以根据该结构,以及 argv 和 argc 属性中保存的命令参数信息来调用命令实现函数,执行客户端指定的命令。

        四、输出缓冲区
        执行命令所得的命令回复会被保存在客户端状态的输出缓冲区里面,每个客户端都有两个输出缓冲区可用:一个缓冲区的大小是固定的,用于保存那些长度比较小的回复;另一个则是可变的,用于保存那些长度比较大的回复。
        固定大小缓冲区由 redisClient 结构的 buf 和 bufpos 两个属性组成,buf 是一个大小为 REDIS_REPLY_CHUNK_BYTES(目前的默认值为 16*1024)字节的字节数组,而 bufpos 属性则记录了 buf 数组目前已使用的字节数量。
        当 buf 数组的空间已经用完,或者因为回复太大时,服务器就会开始使用可变大小缓冲区。可变大小缓冲区由 redisClient 结构的 reply 链表属性来表示。

        五、身份验证和时间
        redisClient 结构的 authenticated 属性用于记录客户端是否通过了身份验证:
        1)当其值为 0 时,表示客户端未通过身份验证,此时除了 AUTH 命令外,客户端发送的所有其他命令都会被服务器拒绝执行。
        2)当其值为 1 时,表示客户端已经通过 AUTH 命令通过了身份验证,这时客户端才可以正常发送命令请求。
        authenticated 属性仅在服务器启用了身份验证功能时使用。如果服务器没有启用这项功能,那么即使 authencicated 为 0,服务器也不会拒绝执行客户端发送的命令请求。关于服务器身份验证的更多信息可以参考配置文件对 requirepass 选项的相关说明。
        最后,客户端 redisClient 状态结构中还有几个和时间有关的属性:
        * ctime:记录了客户端创建的时间,可用之来计算客户端与服务器已经连接了多少秒,“CLIENT LIST”命令输出的 age 字段就记录了这个秒数。
        * lastinteraction:记录了客户端与服务器最后一次进行互动的时间。可用之来计算客户端的空转(idle)时间,“CLIENT LIST”命令输出的 idle 字段就记录了这个秒数。
        * obuf_soft_limit_reached_time:记录了输出缓冲区第一次到达软性限制的时间。

        六、客户端的创建与关闭
        服务器使用不同的方式来创建和关闭不同类型的客户端。
        如果是通过网络来连接服务器的普通客户端,那么在客户端使用 connect 函数连接到服务器时,服务器就会调用连接事件处理器为之创建相应的客户端状态,并将这个新的客户端状态添加到服务器状态结构 clients 链表的末尾。
        一个普通客户端可以因为多种原因而被关闭:
        1)客户端进程退出或者被杀死。
        2)如果客户端向服务器发送了带有不符合协议格式的命令请求,则会被服务器关闭。
        3)客户端成为了“CLIENT KILL”命令的目标。
        4)如果用户为服务器设置了 timeout 配置选项,那么当客户端的空转时间超过该值时,客户端将被关闭。不过 timeout 选项也有一些例外情况:如果客户端是主服务器(打开了 REDIS_MASTER 标志)、从服务器(打开了 REDIS_SLAVE 标志)、正在被 BLPOP 等命令阻塞(打开了 REDIS_BLOCKED 标志),或者正在执行 SUBSCRIBE、PSUBSCRIBE 等订阅命令时,那么即使客户端的空转时长超过了 timeout 的值,客户端也不会被关闭。
        5)客户端发送的命令请求的大小超过了输入缓冲区的限制(默认为 1GB)。
        6)要发送给客户端的命令回复的大小超过了输出缓冲区的限制大小。
        虽然理论上来说,可变大小的输出缓冲区应该可以保存任意长的命令回复,但是,为了避免客户端的回复过大,占用过多的服务器资源,服务器会时刻检查客户端的输出缓冲区的大小,并在缓冲区的大小超出范围时,执行相应的限制操作。
        服务器使用两种模式来限制客户端输出缓冲区的大小:
        1)硬性限制(hard limit):如果输出缓冲区的大小超出了该值,则服务器会立即关闭客户端。
        2)软性限制(soft limit):如果输出缓冲区的大小超过了软性限制设置的大小,但还没超过硬性限制,那么服务器将使用客户端状态的 obuf_soft_limit_reached_time 属性记录下客户端到达软性限制的起始时间,之后服务器会继续监视客户端:如果输出缓冲区的大小一直超出软性限制,并且持续时间超过服务器设置的时长,那么服务器将关闭客户端。
        使用 client-output-buffer-limit 选项可以为普通客户端、从服务器客户端,及执行发布与订阅功能的客户端分别设置不同的软性限制和硬性限制。

        七、Lua 脚本和 AOF 文件的伪客户端
        服务器会在初始化时创建负责执行 Lua 脚本中包含的 Redis 命令的伪客户端,并其关联在服务器状态结构 redisServer 的 lua_client 属性中。这个伪客户端在服务器运行的整个生命期中会一直存在,只有服务器被关闭时,它才会被关闭。
        服务器在载入 AOF 文件时,会创建用于执行 AOF 文件包含的 Redis 命令的伪客户端,并在载入完成后,关闭这个伪客户端。


参考书籍:《Redis 设计与实现》第 13 章——客户端。
分享到:
评论

相关推荐

    Python-一个redis客户端实现设计于使用micropython

    总的来说,`Python-一个redis客户端实现设计于使用micropython`这个项目是将强大的Redis功能引入到资源有限的设备中的尝试,为物联网和嵌入式领域的开发提供了更多可能性。通过优化和调整,我们可以在保持高效的同时...

    redis客户端连接工具 RedisDesktopManager

    下面将详细介绍Redis、Redis客户端以及RedisDesktopManager的相关知识点。 **一、Redis简介** 1. **什么是Redis**:Redis是一个开源的、基于键值对的数据存储系统,支持多种数据结构,如字符串、哈希、列表、集合...

    redis客户端(Mac)

    在Mac操作系统上,使用Redis客户端可以方便地与Redis服务器进行交互。本文将详细介绍如何在Mac上安装和使用Redis客户端。 首先,标题提到的是“redis客户端(Mac)”,这表明我们将讨论的是针对Mac OS设计的Redis...

    RedisConnect是基于C++11实现的简单易用的Redis客户端

    RedisConnect是基于C++11实现的简单易用的Redis客户端。源码只包含一个头文件与一个命令行工具源文件,无需编译安装,真正做到零依赖。自带连接池功能,调用Setup方法初始化连接池,然后执行Instance方法就可以获取...

    redis 免安装 redis客户端 redis-desktop-manager-0.8.8.384

    7. **消息订阅与发布**:Redis Pub/Sub 模块允许客户端订阅特定主题,发布者可以向这些主题发送消息,实现轻量级的消息队列功能。 接下来,关于“redis-desktop-manager-0.8.8.384.exe”文件,这是一个 Redis ...

    C++ Redis 客户端简单使用

    本篇文章将深入探讨如何在C++中简单使用Redis客户端。 首先,为了在C++中与Redis进行交互,我们需要一个支持C++的Redis客户端库。常见的选择有`hiredis`,这是一个轻量级的C库,同时也提供了C++绑定。另一个是`cpp-...

    nginx+tomcat+redis +redis客户端 等负载均衡资料

    在IT行业中,构建高效、可扩展的Web服务是至关重要的,而`nginx`、`tomcat`、`redis`和`redis客户端`等工具在这一过程中扮演着关键角色。这里我们将详细探讨这些技术及其在负载均衡中的应用。 首先,`nginx`是一款...

    redis客户端管理工具

    为了方便管理和操作Redis中的数据,有许多优秀的Redis客户端管理工具应运而生。这些工具提供了图形化界面或者命令行接口,使得开发者可以更高效地进行数据查看、编辑、备份以及监控等任务。以下是一些常见的Redis...

    redis客户端redis客户端

    - Redis 支持 Lua 脚本,客户端可以执行 Lua 脚本来实现更复杂的逻辑,确保操作的原子性。 8. **连接池管理** - 为优化性能,客户端通常使用连接池,复用已建立的连接,减少创建和销毁连接的开销。 9. **数据...

    redis客户端和redis绿色免安装版和redis插件

    本文将深入探讨Redis客户端、Redis绿色免安装版以及与Java开发相关的Redis插件。 首先,Redis客户端在开发和管理Redis服务器时起着至关重要的作用。`redis-desktop-manager-0.8.8.384.exe` 是一个名为Redis Desktop...

    Redis客户端

    **Redis客户端** Redis是一款开源、高性能的键值对存储系统,常被用于数据库、缓存和消息中间件等场景。Redis客户端是与Redis服务器进行交互的工具,它允许用户查看和管理存储在Redis中的数据。这里提到的是一个...

    redis客户端连接工具

    在实际应用中,为了方便管理和操作Redis服务器,我们通常会使用专门的Redis客户端连接工具。这里,我们重点讨论标题中提到的"redis客户端连接工具",特别是压缩包内的`redis-desktop-manager-0.8.3.3850.exe`。 ...

    redis客户端连接、spring boot整合、分布式锁.zip

    在本项目中,我们关注的是 Redis 的客户端连接、Spring Boot 整合以及分布式锁的实现。 首先,Redis 客户端连接是与 Redis 服务器进行交互的基础。常见的 Redis 客户端库有 Jedis 和 Lettuce。Jedis 是一个用 Java ...

    Redis客户端-Windows版

    该压缩包“Redis客户端(Windows)”,包含了一系列用于Windows平台的Redis客户端工具,这些工具可以帮助开发者在Windows环境中与Redis服务器进行交互,执行数据的读写、查看、管理等操作。以下是一些重要的知识点: ...

    PHP Swoole异步Redis客户端实现方法示例

    本文实例讲述了PHP Swoole异步Redis客户端实现方法。分享给大家供大家参考,具体如下: 使用版本:1.8.0及以上 使用条件: 1.开启async-redis php --ri swoole (如果没有开启,重新编译安装Swoole时加入 –...

    redis客户端及jar包.zip

    1. **Redis桌面管理器** (redis-desktop-manager-0.8.8.384.exe): 这是一款图形化用户界面的Redis客户端,允许用户直观地管理和操作Redis服务器。通过这个工具,你可以方便地查看、添加、编辑和删除Redis中的键值对...

    redis客户端

    Redis客户端是用于与Redis服务器进行交互的工具,它允许用户执行命令、查看数据结构、管理键空间以及进行数据备份和恢复等操作。Redis是一种高性能的键值存储系统,广泛应用于缓存、数据库、消息中间件等多种场景。...

    Redis客户端Java服务接口封装

    本文将围绕“Redis客户端Java服务接口封装”这一主题进行深入探讨,结合给定的`RedisClientTemplate.java`文件,我们将讨论如何在Java环境中高效地与Redis进行交互。 首先,`RedisClientTemplate`这个名字暗示它是...

    ServiceStack.Redis客户端dLL3.0

    ServiceStack.Redis客户端DLL3.0是一个重要的软件组件,主要用于为.NET开发者提供与Redis数据库的高效交互接口。Redis是一个开源、高性能、键值存储系统,常用于数据缓存、消息队列以及分布式服务等场景。...

Global site tag (gtag.js) - Google Analytics