go-redis源码解析:如何实现sentinel高可用

go-redis里,sentinel只用来获取master和从节点的ip地址,在获取master和replica节点ip时,如果sentinel不可用,那么会换其他的sentinel重试,并将可用的sentinel换到第一个

1. 用于获取master节点

  1. 先通过读锁获取c.sentinel,使用c.sentinel获取主节点信息

  2. 如果上面sentinel为空或者出错未获取到主节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取主节点信息

    1. 分读锁和读写锁这两步的原因是,提高并发性能
  3. 如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理

  4. 最后如果所有的sentinel都不可用,则报错

1.1. 核心高可用代码

img

1.2. 方法内所有处理逻辑

func (c *sentinelFailover) replicaAddrs(ctx context.Context, useDisconnected bool) ([]string, error) {// 1. 先通过读锁获取c.sentinel,使用c.sentinel获取主节点信息c.mu.RLock()sentinel := c.sentinelc.mu.RUnlock()if sentinel != nil {// 通过哨兵查询从节点addrs, err := c.getReplicaAddrs(ctx, sentinel)if err != nil {if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}// Continue on other errorsinternal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s",c.opt.MasterName, err)} else if len(addrs) > 0 {return addrs, nil}}// 2. 如果上面sentinel为空或者出错未获取到主节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取主节点信息// 分读锁和读写锁这两步的原因是,提高并发性能c.mu.Lock()defer c.mu.Unlock()if c.sentinel != nil {addrs, err := c.getReplicaAddrs(ctx, c.sentinel)if err != nil {_ = c.closeSentinel()if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}// Continue on other errorsinternal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s",c.opt.MasterName, err)} else if len(addrs) > 0 {return addrs, nil} else {// No error and no replicas._ = c.closeSentinel()}}// 3. 如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理var sentinelReachable boolfor i, sentinelAddr := range c.sentinelAddrs {sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr))replicas, err := sentinel.Replicas(ctx, c.opt.MasterName, c.opt.ServerVersion).Result()if err != nil {_ = sentinel.Close()if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}internal.Logger.Printf(ctx, "sentinel: Replicas master=%q failed: %s",c.opt.MasterName, err)continue}sentinelReachable = trueaddrs := parseReplicaAddrs(replicas, useDisconnected)if len(addrs) == 0 {continue}// Push working sentinel to the top.c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]c.setSentinel(ctx, sentinel)return addrs, nil}if sentinelReachable {return []string{}, nil}// 4. 最后如果所有的sentinel都不可用,则报错return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable")
}

2. 用于获取replica从节点

和获取主节点方法处理逻辑类似

  1. 先通过读锁获取c.sentinel,使用c.sentinel获取从节点信息

  2. 如果上面sentinel为空或者出错未获取到从节点,那么可能在此中间有地方重新设置了c.sentinel,继续用读写锁,使用c.sentinel获取从节点信息

    1. 分读锁和读写锁这两步的原因是,提高并发性能
  3. 如果以上两步的sentinel都有问题,那么遍历所有的sentinelAddr,创建sentinel进行处理

  4. 最后如果所有的sentinel都不可用,则报错

func (c *sentinelFailover) replicaAddrs(ctx context.Context, useDisconnected bool) ([]string, error) {c.mu.RLock()sentinel := c.sentinelc.mu.RUnlock()if sentinel != nil {// 通过哨兵查询从节点addrs, err := c.getReplicaAddrs(ctx, sentinel)if err != nil {if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}// Continue on other errorsinternal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s",c.opt.MasterName, err)} else if len(addrs) > 0 {return addrs, nil}}c.mu.Lock()defer c.mu.Unlock()if c.sentinel != nil {addrs, err := c.getReplicaAddrs(ctx, c.sentinel)if err != nil {_ = c.closeSentinel()if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}// Continue on other errorsinternal.Logger.Printf(ctx, "sentinel: Replicas name=%q failed: %s",c.opt.MasterName, err)} else if len(addrs) > 0 {return addrs, nil} else {// No error and no replicas._ = c.closeSentinel()}}var sentinelReachable boolfor i, sentinelAddr := range c.sentinelAddrs {sentinel := NewSentinelClient(c.opt.sentinelOptions(sentinelAddr))replicas, err := sentinel.Replicas(ctx, c.opt.MasterName, c.opt.ServerVersion).Result()if err != nil {_ = sentinel.Close()if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {return nil, err}internal.Logger.Printf(ctx, "sentinel: Replicas master=%q failed: %s",c.opt.MasterName, err)continue}sentinelReachable = trueaddrs := parseReplicaAddrs(replicas, useDisconnected)if len(addrs) == 0 {continue}// Push working sentinel to the top.c.sentinelAddrs[0], c.sentinelAddrs[i] = c.sentinelAddrs[i], c.sentinelAddrs[0]c.setSentinel(ctx, sentinel)return addrs, nil}if sentinelReachable {return []string{}, nil}return []string{}, errors.New("redis: all sentinels specified in configuration are unreachable")
}

2.1. 使用sentinel获取主节点和从节点的大部分代码都类似,为什么不合并成一个方法

2.1.1. 语义清晰

对于外部调用方来讲,并不关心内部实现是否类似,要的只是获取主节点和从节点信息,分开两个方法,语义很清晰

2.1.2. 方便改动

如果底层合并成一个方法,很容易改动获取master节点的方法,影响到获取从节点的逻辑,造成bug。这也给我们一个启示,不要害怕代码重复,要更关注代码可改动性。如果看着代码类似就合并,很容易造成后面代码改不动,改一个地方,影响其他很多地方

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

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

相关文章

模板进阶:非类型模板参数,类模板特化,模板的编译分离

1. 非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。 非类型形参,就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常…

【Unity数据交互】如何Unity中读取Ecxel中的数据

👨‍💻个人主页:元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 专栏交流🧧&…

2024/7/7周报

文章目录 摘要Abstract文献阅读题目问题本文贡献问题描述图神经网络Framework实验数据集实验结果 深度学习MAGNN模型相关代码GNN为什么要用GNN?GNN面临挑战 总结 摘要 本周阅读了一篇用于多变量时间序列预测的多尺度自适应图神经网络的文章,多变量时间序…

Vulkan 学习(1)---- Vulkan 基本概念和发展历史

目录 Vulkan及其演化史Vulkan 基本概念基本术语 Vulkan 的原理Vulkan应用程序Vulkan的编程模型硬件初始化窗口展示表面资源设置流水线设置描述符和描述符缓冲池基于SPIR-V的着色器流水线管理指令的记录队列的提交 Vulkan及其演化史 目前主流的图形渲染API有OpenGL、OpenGL ES、…

ROS——多个海龟追踪一个海龟实验

目标 通过键盘控制一个海龟(领航龟)的移动,其余生成的海龟通过监听实现追踪定期获取领航龟和其余龟的坐标信息,通过广播告知其余龟,进行相应移动其余龟负责监听 疑惑点(已解决) int main(int…

【感谢告知】本账号内容调整,聚焦于Google账号和产品的使用经验和问题案例分析

亲爱的各位朋友: 感谢您对本账号的关注和支持! 基于对朋友们需求的分析和个人兴趣的转变,该账号从今天将对内容做一些调整,有原来的内容改为Google(谷歌)账号和产品的使用经验,以及相关问题的…

判断是否为完全二叉树

目录 分析 分析 1.完全二叉树的概念:对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。 2.思路:可以采…

Redis源码整体结构

一 前言 Redis源码研究为什么先介绍整体结构呢?其实也很简单,作为程序员的,要想对一个项目有快速的认知,对项目整体目录结构有一个清晰认识,有助于我们更好的了解这个系统。 二 目录结构 Redis源码download到本地之后,对应结构如下: 从上面的截图可以看出,Redis源码一…

pdf怎么转换成图片格式文件,pdf文档怎么转换成图片格式

在数字化时代,pdf文件转换成图片格式是一种常见的操作,无论是在工作还是日常生活中,我们总会遇到需要将pdf文件转换为图片的需求。这可能是因为图片格式更易于分享、展示或编辑。那么,如何高效地将pdf转换成图片呢?本文…

C++初学者指南-4.诊断---未定义行为检测器

C初学者指南-4.诊断—未定义行为检测器 未定义行为检测器(UBSAN) 适用编译器:clang,g在运行时检测许多类型的未定义行为 解引用空指针从未对齐的指针读取整数溢出被0除 … 在代码中加入额外的指令:在调试构建中增加运行时约25% 示例:有符号整形溢出 …

RabbitMq - Java客户端基础【简单案例 +Work模型】

目录 1、前置知识 1.1、AMQP怎么理解 1.2、Spring AMQP是什么 1.3、为什么要了解Spring-AMQP? 2、使用Spring-AMQP实现一个发消息案例 3、Work模型 问题: 优化: 小结:Work模型的使用: 1、前置知识 1.1、AMQP怎…

【论文阅读】-- Visual Traffic Jam Analysis Based on Trajectory Data

基于轨迹数据的可视化交通拥堵分析 摘要1 引言2 相关工作2.1 交通事件检测2.2 交通可视化2.3 传播图可视化 3 概述3.1 设计要求3.2 输入数据说明3.3 交通拥堵数据模型3.4 工作流程 4 预处理4.1 路网处理4.2 GPS数据清理4.3 地图匹配4.4 道路速度计算4.5 交通拥堵检测4.6 传播图…

2024世界人工智能大会,神仙打架

B站:啥都会一点的研究生公众号:啥都会一点的研究生 AI圈最近又发生了啥新鲜事? 该栏目以周更频率总结国内外前沿AI动态,感兴趣的可以点击订阅合集以及时收到最新推送 B站首秀世界人工智能大会,展示自研AI技术与AIGC…

生成式人工智能如何改变软件开发:助手还是取代者?

生成式人工智能如何改变软件开发:助手还是取代者? 生成式人工智能(AIGC)正在引领软件开发领域的技术变革。从代码生成、错误检测到自动化测试,AI工具在提高开发效率的同时,也引发了对开发者职业前景的讨论…

【shell编程小项目】

目录 一、项目拓扑二、要求三、shell编程 一、项目拓扑 二、要求 环境准备: 准备两个虚拟机,按照环境配置好对应的 IP 地址和对应的主机名和 SSH 密钥登录在 workstation.exam.com 节点实现如下需求: 1、编写 Shell 脚本,要求代码…

【web APIs】快速上手Day04(Dom节点)

目录 Web APIs - 第4天日期对象实例化方法案例-页面显示时间时间的另外一个写法 时间戳三种方式获取时间戳案例-毕业倒计时效果 节点操作DOM节点查找节点父节点查找案例-关闭广告子节点查找兄弟关系查找 增加节点创建节点追加节点案例-学成在线案例渲染克隆节点 删除节点 M端事…

ESP32 步进电机精准控制:打造高精度 DIY 写字机器人,实现流畅书写体验

摘要: 想让你的 ESP32 不再仅仅是控制灯光的工具吗? 本文将带你使用 ESP32 开发板、步进电机和简单的机械结构打造一个能够自动写字的机器人。我们将深入浅出地讲解硬件连接、软件代码以及控制逻辑,并提供完整的项目代码和电路图,即使是 Ardu…

在mac下 Vue2和Vue3并存 全局Vue2环境创建Vue3新项目(Vue cli2和Vue cli4)

全局安装vue2 npm install vue-cli -g自行在任意位置创建一个文件夹vue3,局部安装vue3,注意不要带-g npm install vue/cli安装完成后,进入目录,修改vue为vue3 找到vue3/node-moudles/.bin/vue,把vue改成vue3。 对环境变量进行配置…

码垛机:物流自动化的关键设备

在数字化浪潮席卷全球的今天,物流行业正迎来前所未有的变革。其中,码垛机作为物流自动化的关键设备,正在逐步改变着传统仓库的运营模式,引领着物流自动化的新篇章。 一、码垛机:物流自动化的得力助手 码垛机是一种能够…

【手写数据库内核组件】0201 哈希表hashtable的实战演练,多种非加密算法,hash桶的冲突处理,查找插入删除操作的代码实现

hash表原理与实战 ​专栏内容: postgresql使用入门基础手写数据库toadb并发编程 个人主页:我的主页 管理社区:开源数据库 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 文章目录 hash表…