Redis设计与实现-哨兵

news2025/4/2 19:31:47

哨兵模式

  • 1、启动并初始化sentinel
    • 1.1 初始化服务器
    • 1.2 使用Sentinel代码
    • 1.3 初始化sentinel状态
    • 1.4 初始化sentinel状态的master属性
    • 1.5 创建连向主服务器的网络连接
  • 2、获取主服务器信息
  • 3、获取从服务器的信息
  • 4、向主从服务器发送信息
  • 5、接受主从服务器的频道信息
  • 6、检测主观下线状态
  • 7、检查客观下线状态
  • 8、选择领头的sentinel
  • 9、故障转移
    • 9.1 选择新主服务器
    • 9.2 修改从服务器复制目标
    • 9.3 将旧的主服务器转换为从服务器

如有侵权,请联系~
如有错误,也欢迎批评指正~
本篇文章大部分是来自学习《Redis设计与实现》的笔记

哨兵(Sentinel)是Redis的高可用解决方案:由一个或者多个哨兵实例组成的哨兵系统,可以监视任意多个主服务器以及这些主服务器所属下的所有从服务器,并且在被监视的主服务器下线时自动的将从某个服务器升级为新的主服务器,然后将新的主服务器替换已经下线的主服务器继续处理命令请求。

1、启动并初始化sentinel

启动一个Sentinel可以使用如下命令:

redis-sentinel /path/your/sentinel.conf
redis-server /path/your/sentinel.conf --sentinel

启动一个sentinel服务器,需要执行如下步骤:

  1. 初始化服务器
  2. 将普通的Redis服务器使用的代码代替为Sentinel专用代码
  3. 初始化Sentinel状态
  4. 根据给定的配置文件,初始化Sentinel的监视主服务器列表
  5. 创建与主服务器的网络连接

1.1 初始化服务器

因为Sentinel哨兵实例本质上就是一个运行在特殊模式下的Redis服务器,所以这一步初始化服务器就是初始化一个redis实例,参考Redis设计与实现-底层实现。
但是毕竟两者工作的方式不一样,所以初始化过程还是有所不一样,例如哨兵初始化是不需要RDB文件或者AOF文件还原数据库。

1.2 使用Sentinel代码

将普通redis使用的代码替换为sentinel哨兵使用的代码。例如,redis的服务端端口号为6379,哨兵使用26379.

define REDIS_SERVERPORT 6379
define REDIS_SENTINEL_PORT 26379

哨兵的命令表也和redis服务器的不同,哨兵对客户端提供的命令PING、SENTINEL、INFO、SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE和PUNSUBSCRIBE,不对外提供set get等redis的基础命令。

1.3 初始化sentinel状态

接下来会初始化哨兵的状态sentinelState结构,SentinelState 是 Redis Sentinel 内部用来表示全局状态的数据结构。它包含了所有监控的主节点、从节点、哨兵实例以及配置信息。

typedef struct sentinelState {
    uint64_t current_epoch;       // 当前的纪元(epoch),用于故障转移选举
    dict *masters;                // 主节点字典,key 为被监控主节点名称,value 为 sentinelRedisInstance
    int tilt;                     // 是否处于倾斜模式(Tilt Mode)
    int running_scripts;          // 当前正在运行的脚本数量
    mstime_t tilt_start_time;     // 倾斜模式开始的时间
    mstime_t previous_time;       // 上一次时间戳,用于检测时间跳跃
    list *scripts_queue;          // 脚本队列,用于执行用户定义的脚本
} sentinelState;

1.4 初始化sentinel状态的master属性

sentinel状态会保存所有监控的主服务器的信息master,而这个master的key就是主服务器的名字,value就是sentinelRedisInstance。这一步就是初始化master字典。为什么只保存主服务器信息?因为从服务器是动态的,sentinel会通过主服务器进行获取。

typedef struct sentinelRedisInstance {
    int flags;                    // 实例标志位(如主节点、从节点、哨兵等)
    char *name;                   // 实例名称(仅主节点有名称)
    redisAsyncContext *cc;        // 与该实例的命令连接
    redisAsyncContext *pc;        // 与该实例的发布/订阅连接
    mstime_t last_pub_time;       // 上一次发送 PUBLISH 消息的时间
    mstime_t last_hello_time;     // 上一次收到 HELLO 消息的时间
    mstime_t last_master_down_time; // 上一次检测到主节点下线的时间
    mstime_t down_after_period;   // 判定实例下线的时间阈值
    mstime_t info_refresh;        // INFO 命令刷新的时间间隔
    dict *renamed_commands;       // 重命名的 Redis 命令映射
    int parallel_syncs;           // 故障转移后允许同时同步的从节点数量
    int quorum;                   // 判定主节点不可用所需的最小哨兵数量
    list *sentinels;              // 监控该主节点的所有哨兵实例
    list *slaves;                 // 主节点的所有从节点
    struct sentinelRedisInstance *master; // 如果是从节点,指向其主节点
    ...
} sentinelRedisInstance;

1.5 创建连向主服务器的网络连接

哨兵sentinel会向主服务器建立网络连接,针对于一个主服务器需要建立两个网络连接:

  • 用于命令回复的网络连接,主要用来向主服务器发送命令请求,接受命令回复
  • 用于订阅功能的网络连接,用来订阅主服务器的_sentinel_:hello频道

2、获取主服务器信息

sentinel哨兵会每10秒向监控的主服务器发送INFO命令,并通过主服务器返回的INFO命令回复进行分析更新相应主服务器的sentinelRedisInstance。INFO命令主服务器返回的结果如下:

# server部分
redis_version:6.2.6                  # Redis 版本号
redis_mode:standalone                # Redis 模式 (standalone 或 cluster)
os:Linux 5.4.0-42-generic x86_64     # 操作系统信息
arch_bits:64                         # 架构位数 (3264)
process_id:12345                     # Redis 进程 ID
run_id:abcdef1234567890abcdef1234567890abcdef1234567890abcdef12 # 运行 ID
tcp_port:6379                        # Redis 服务端口
uptime_in_seconds:123456             # Redis 运行时间(秒)
uptime_in_days:1                     # Redis 运行时间(天)
hz:10                                # Redis 内部定时器频率

# client部分
connected_clients:10                 # 当前连接的客户端数量
client_recent_max_input_buffer:2     # 最大输入缓冲区大小
client_recent_max_output_buffer:0    # 最大输出缓冲区大小
blocked_clients:0                    # 被阻塞的客户端数量

# replication部分
role:master                          # 角色:主节点
connected_slaves:2                   # 已连接的从节点数量
slave0:ip=192.168.1.2,port=6379,state=online,offset=12345678,lag=0
slave1:ip=192.168.1.3,port=6379,state=online,offset=12345678,lag=1
master_replid:abcdef1234567890abcdef1234567890abcdef1234567890abcdef12
master_replid2:00000000000000000000000000000000000000000000000000000000
master_repl_offset:12345678          # 主节点的复制偏移量
second_repl_offset:-1                # 第二个复制 ID 的偏移量
repl_backlog_active:1                # 是否启用了复制积压缓冲区
repl_backlog_size:1048576            # 复制积压缓冲区大小
repl_backlog_first_byte_offset:12345 # 积压缓冲区的第一个字节偏移量
repl_backlog_histlen:12345           # 积压缓冲区的历史长度

....

通过分析上述命令回复会更新对应主服务器的sentinelRedisInstance,这个命令回复也会返回从服务器的信息。如果有新增的从服务器就会为从服务器创建新的sentinelRedisInstance,添加到主服务的sentinelRedisInstance.slaves属性中。

3、获取从服务器的信息

当有新的从服务器出现的时候,sentinel会在该主服务下的slaves中添加一个sentinelRedisInstance实例,并且和从服务器创建命令连接和订阅连接。
命令创建完成之后,sentinel默认10秒通过命令连接向从服务器发一次info命令,并且返回:

# Replication
role:slave                   # 角色为从节点
master_host:127.0.0.1        # 主节点IP
master_port:6379             # 主节点端口
master_link_status:up        # 与主节点的连接状态(up/down)
master_last_io_seconds_ago:1 # 上次与主节点通信的秒数(反映复制延迟)
master_sync_in_progress:0    # 是否正在全量同步(0=否,1=是)
slave_repl_offset:123456     # 当前复制偏移量(用于评估数据同步进度)
slave_priority:100           # 从节点优先级(故障转移时影响选举)
slave_read_only:1            # 是否只读模式(默认1# Server
redis_version:6.2.6         # Redis版本
os:Linux 5.4.0-80-generic   # 运行的操作系统
uptime_in_seconds:86400     # 运行时间(秒)
# Clients
connected_clients:3         # 当前客户端连接数

根据从服务器返回的数据,更新sentinelRedisInstance实例。

4、向主从服务器发送信息

sentinel每两秒就会通过订阅连接向主从服务器发送信息。当 Sentinel 在 sentinel:hello 频道上发布消息时,消息内容通常包含以下信息:

publish __sentinel__:hello <sentinel-ip>,<sentinel-port>,<runid>,<current_epoch>,<master-name>,<master-ip>,<master-port>,<master-config-epoch>
  • sentinel-ip 和 sentinel-port:当前 Sentinel 的地址。
  • runid:当前 Sentinel 的运行 ID。
  • current_epoch:当前的配置纪元(用于协调故障转移)。
  • master-name:被监控的主节点名称。
  • master-ip 和 master-port:主节点的地址。
  • master-config-epoch:主节点的配置纪元。

5、接受主从服务器的频道信息

有关频道的订阅关系,针对于一个主从服务Redis集群会分配单独的订阅频道,即不同redis主服务器之间的频道是相互独立的。针对于同一个频道,不仅所有的哨兵和redis主从服务器都订阅这个频道,而且他们也都可以向这个频道发布消息。
所以通过这个频道 redis服务器可以向监听自己的所有sentinel发送消息。
在这里插入图片描述
通过上图可以知道,一个哨兵sentinel发送消息之后除了redis服务器可以接收到数据之外,其他监听这个redis主服务的sentinel也会收到消息。
一个sentinel发送消息之后,sentinel的处理:

  • 如果发现接收到的sentinel runId和自己的一样,说明是自己发送的,则丢弃
  • 如果发现不一样,则根据发送的消息解析出sentinel和主服务器的信息。通过主服务器的信息在自己的sentinelServer的master字典中找到这个主服务器的sentinelRedisInstance实例,然后查看该实例下sentinels【监听这个主服务器的其他sentinel】是否存在。如果存在则更新数据;否则创建实例,并且与这个sentinel彼此创建命令连接。

sentinel和redis服务器的命令连接和订阅关系。【本图没有画各个sentinel与redis服务器的命令连接】(sentinel之间只有命令连接,没有订阅)
在这里插入图片描述

6、检测主观下线状态

默认情况下,sentinel会每秒都会向与其建立命令连接的所有实例【主服务器、从服务器以及与其相连的sentinel】发送ping命令,并根据实例返回的命令回复是否有限【有效回复+PONG、- LOADING、- MASTERDOWN】进行判断是否在线。

sentinel会根据配置文件中down-after-milliseconds参数判断该实例是否为主观下线。即一个实例连续down-after-milliseconds毫秒内一直无效回复,则标记为主观下线,在该实例的sentinelRedisInstance的flags属性设置为sri_s_down标识。

注意每个sentinel配置文件中的down-after-milliseconds值可能不同,有的可能为1000,有的可能为5000。所以,一个实例下线并不一定所有的sentinel都同时认为下线。

当一个 Sentinel 实例判断另一个 Sentinel 实例为主观下线后,它会执行以下操作:

  • 记录主观下线状态:当前 Sentinel 会将目标 Sentinel 标记为 主观下线,并记录其状态。
    这一状态仅存在于当前 Sentinel 的视角中,并不会立即影响系统的整体行为。
  • 通知其他 Sentinel 实例:通知其他 Sentinel 实例自己对目标 Sentinel 的判断结果。其他 Sentinel 实例可以接收到这些消息,但它们会根据自己的健康检查结果独立判断目标 Sentinel 是否也处于主观下线状态。
  • 不触发客观下线

7、检查客观下线状态

当sentinel判断一个主服务器为主观下线之后,为了进一步判断是否真的下线,会问其他sentinel哨兵是否下线。当超过quorum个sentinel认为该主服务器下线,则就会将这个redis主服务器标记为客观下线。【从服务器不需要进行判断客观下线,影响比较小】

SENTINEL IS-MASTER-DOWN-BY-ADDR 是 Redis Sentinel 提供的一个命令,用于检查某个主节点是否被标记为下线(主观下线或客观下线),并参与领导者选举的协商过程。sentinel通过命令连接进行发送的,不是通过hello频道发送的。

SENTINEL IS-MASTER-DOWN-BY-ADDR命令:

SENTINEL IS-MASTER-DOWN-BY-ADDR <master-ip> <master-port> <current-epoch> <runid>

参数说明:

  • master-ip和 master-port:指定要检查的主节点的 IP 地址和端口号。
  • current-epoch:当前的配置纪元(configuration epoch)。配置纪元是一个递增的数字,用于协调多个 Sentinel 实例之间的状态变更和领导者选举。
  • runid:如果该参数为 *,表示当前 Sentinel 只是询问其他 Sentinel 是否认为主节点已下线。
    如果该参数为具体的运行 ID(如当前 Sentinel 的 runid),表示当前 Sentinel 希望竞选成为领导者。

目标 Sentinel 返回包含三个参数的响应:

  • down_state:
    • 1 表示目标 Sentinel 认为主节点已下线;
    • 0 表示认为主节点在线。
  • leader_runid:
    • 若为 *,表示回复仅用于主节点状态检测;
    • 若为具体 runid,表示目标 Sentinel 同意该 runid 对应的 Sentinel 成为故障转移的领导者。
  • leader_epoch: 目标 Sentinel 的局部领头配置纪元(仅当 leader_runid 不为 * 时有效)

sentinel会根据该命令的回复判断,是否超过quorum个sentinel回复了一下线。客观下线则会将该redis主服务器实例的sentinelRedisInstance的flags属性设置为sri_o_down标识。

同样,不同的sentinel可能配置文件不一样,从而会出现quorum大小不一样,可能出现有的sentinel认为主服务器已经下线,有的认为没有客观下线。

8、选择领头的sentinel

只要判断主机已经客观下线之后,监听这个主机的各个sentinel进行协商,选择一个领头的sentinel,由这个领头的sentinel负责故障转移。

  • 源哨兵期望成为领头的sentinel,所以源sentinel向目标sentinel发送sentinel is-master-down-by-addr命令,并且runid为源sentinel的runid。
  • 目标sentinel会根据先到先得,如果没有在这个记元投递给其他sentinel,就会投递给这个sentinel,回复的leader_runid和leader_epoch分别为源sentinel的runid和目标sentinel的epoch。否则就会拒绝
  • 源sentinel接收到命令回复之后,会判断epoch和自己的epoch是否一样以及回复的runid是否等于自己的epoch。
  • 如果某个sentinel超过半数以上,则会成为领头sentinel执行故障转移。如果都没有满足这一条件的,则过一段时间再进行选举。每次选举epoch都会加1,无论是否成功。

9、故障转移

在选择了领头sentinel之后,由领头sentinel负责故障转移。进行故障转移主要步骤为:从那些可用的从服务器中选择主服务器、将其他从服务器复制新的主服务器、将已经下线的主服务器也作为新主服务器的从服务器。

9.1 选择新主服务器

领头sentinel从所有从服务器中选择状态良好、数据完整的从服务器作为新主服务器,并向这个从服务器发送slave no one命令,将这服务器设置为主服务器。选择主服务的步骤大致如下:

  1. 从所有的从服务器中删除处于下线的服务器
  2. 从所有的从服务器中删除5秒内没有回复info命令的服务器
  3. 然后从剩余的从服务器根据【 优先级 > 复制偏移量大 > 运行runId最小】的原则选择作为从服务器作为主服务器
    在这里插入图片描述
    在这里插入图片描述

9.2 修改从服务器复制目标

将其他从服务器复制新的主服务器,领头sentinel会向其他的从服务器发送slave <新主服务器ip> <新主服务器port>。
在这里插入图片描述

9.3 将旧的主服务器转换为从服务器

接下来就是将旧的主服务器转换为从服务器,当旧的主服务器重新启动之后,sentinel会向它发送slave <新主服务器ip> <新主服务器port>命令。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2324312.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++进阶——封装哈希表实现unordered_map/set

与红黑树封装map/set基本相似&#xff0c;只是unordered_map/set是单向迭代器&#xff0c;模板多传一个HashFunc。 目录 1、源码及框架分析 2、模拟实现unordered_map/set 2.1 复用的哈希表框架及Insert 2.2 iterator的实现 2.2.1 iteartor的核心源码 2.2.2 iterator的实…

【算法day25】 最长有效括号——给你一个只包含 ‘(‘ 和 ‘)‘ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。

32. 最长有效括号 给你一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长有效&#xff08;格式正确且连续&#xff09;括号子串的长度。 https://leetcode.cn/problems/longest-valid-parentheses/ 2.方法二&#xff1a;栈 class Solution { public:int longestValid…

Jenkins + CICD流程一键自动部署Vue前端项目(保姆级)

git仓库地址&#xff1a;参考以下代码完成,或者采用自己的代码。 南泽/cicd-test 拉取项目代码到本地 使用云服务器或虚拟机采用docker部署jenkins 安装docker过程省略 采用docker部署jenkins&#xff0c;注意这里的命令&#xff0c;一定要映射docker路径&#xff0c;否则无…

一款超级好用且开源免费的数据可视化工具——Superset

认识Superset 数字经济、数字化转型、大数据等等依旧是如今火热的领域&#xff0c;数据工作有一个重要的环节就是数据可视化。 看得见的数据才更有价值&#xff01; 现如今依旧有多数企业号称有多少多少数据&#xff0c;然而如果这些数据只是呆在冷冰冰的数据库或文件内则毫无…

RedHatLinux(2025.3.22)

1、创建/www目录&#xff0c;在/www目录下新建name和https目录&#xff0c;在name和https目录下分别创建一个index.htm1文件&#xff0c;name下面的index.html 文件中包含当前主机的主机名&#xff0c;https目录下的index.htm1文件中包含当前主机的ip地址。 &#xff08;1&…

【C++篇】类与对象(上篇):从面向过程到面向对象的跨越

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对C感兴趣的…

智慧运维平台:赋能未来,开启高效运维新时代

在当今数字化浪潮下&#xff0c;企业IT基础设施、工业设备及智慧城市系统的复杂度与日俱增&#xff0c;传统人工运维方式已难以满足高效、精准、智能的管理需求。停机故障、低效响应、数据孤岛等问题直接影响企业运营效率和成本控制。大型智慧运维平台&#xff08;AIOps, Smart…

基于大语言模型的智能音乐创作系统——从推荐到生成

一、引言&#xff1a;当AI成为音乐创作伙伴 2023年&#xff0c;一款由大语言模型&#xff08;LLM&#xff09;生成的钢琴曲《量子交响曲》在Spotify冲上热搜&#xff0c;引发音乐界震动。传统音乐创作需要数年专业训练&#xff0c;而现代AI技术正在打破这一壁垒。本文提出一种…

Reactive编程:什么是Reactive编程?Reactive编程思想

文章目录 **1. Reactive编程概述****1.1 什么是Reactive编程&#xff1f;****1.1.1 Reactive编程的定义****1.1.2 Reactive编程的历史****1.1.3 Reactive编程的应用场景****1.1.4 Reactive编程的优势** **1.2 Reactive编程的核心思想****1.2.1 响应式&#xff08;Reactive&…

深度剖析:U盘突然无法访问的数据拯救之道

一、引言 在数字化办公与数据存储日益普及的当下&#xff0c;U盘凭借其小巧便携、即插即用的特性&#xff0c;成为了人们工作、学习和生活中不可或缺的数据存储工具。然而&#xff0c;U盘突然无法访问这一棘手问题却时常困扰着广大用户&#xff0c;它不仅可能导致重要数据的丢失…

蓝桥杯-特殊的三角形(dfs/枚举/前缀和)

思路分析 深度优先搜索&#xff08;DFS&#xff09;思路 定义与参数说明 dfs 函数中&#xff0c;last 记录上一条边的长度&#xff0c;用于保证新选边长度大于上一条边&#xff0c;实现三边互不相等 。cnt 记录已选边的数量&#xff0c;当 cnt 达到 3 时&#xff0c;就构成了…

一文详解k8s体系架构知识

0.云原生 1.k8s概念 1. k8s集群的两种管理角色 Master&#xff1a;集群控制节点&#xff0c;负责具体命令的执行过程。master节点通常会占用一股独立的服务器&#xff08;高可用部署建议用3台服务器&#xff09;&#xff0c;是整个集群的首脑。 Master节点一组关键进程&#xf…

wx162基于springboot+vue+uniapp的在线办公小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

Baklib内容中台的核心优势是什么?

智能化知识管理引擎 Baklib的智能化知识管理引擎通过多源数据整合与智能分类技术&#xff0c;实现企业知识资产的自动化归集与动态更新。系统内置的语义分析算法可自动识别文档主题&#xff0c;结合自然语言处理技术生成结构化标签体系&#xff0c;大幅降低人工标注成本。针对…

【C++】C++11介绍列表初始化右值引用和移动语义

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. C11简介2. 统一的列表初始化2.1&#xff5b;&#xff5d;初始化2.2 std::initializer_list 3. 声明3.1 auto3.2 decltype3.3 nullptr 4. 范围for循环4.1 范围for的语法4.2 范围for的使用条件 5. STL中一些变化6. 右…

搜广推校招面经六十一

美团推荐算法 一、ANN算法了解么&#xff1f;说几种你了解的ANN算法 ANN 近似最近邻搜索&#xff08;Approximate Nearest Neighbor Search&#xff09;算法 1.1. KD-Tree&#xff08;K-Dimensional Tree&#xff0c;K 维树&#xff09; 类型: 空间划分数据结构适用场景: 低…

人工智能与软件工程结合的发展趋势

AI与软件工程的结合正在深刻改变软件开发的流程、工具和方法&#xff0c;其发展方向涵盖了从代码生成到系统维护的整个生命周期。以下是主要的发展方向和技术趋势&#xff1a; 1. 软件架构体系的重构 从“面向过程”到“面向目标”的架构转型&#xff1a; AI驱动软件设计以目标…

nacos 外置mysql数据库操作(docker 环境)

目录 一、外置mysql数据库原因&#xff1a; 二、数据库准备工作 三、构建nacos容器 四、效果展示 一、外置mysql数据库原因&#xff1a; 想知道nacos如何外置mysql数据库之前&#xff0c;我们首先要知道为什么要外置mysql数据库&#xff0c;或者说这样做有什么优点和好处&am…

【数电】半导体存储电路

组合逻辑电路输入和输出之间是确定关系&#xff0c;与之前的历史记录没有任何关系。时序逻辑电路则有相应的存储元件&#xff0c;要把之前的状态保存起来。 要构成时序逻辑电路&#xff0c;必须要有相应的存储元件&#xff0c;第五章讲述相应的存储元件 一、半导体存储电路概…

Jenkins插件安装失败如何解决

问题&#xff1a;安装Jenkins时候出现插件无法安装的情况。 测试环境&#xff1a; 操作系统&#xff1a;Windows11 Jenkins&#xff1a;2.479.3 JDK&#xff1a;17.0.14&#xff08;21也可以&#xff09; 解决办法一&#xff1a; 更换当前网络&#xff0c;局域网、移动、联通…