
📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建工设优化。文章内容兼具广度深度、大厂技术方案,对待技术喜欢推理加验证,就职于知名金融公司后端高级工程师。
📫 热衷分享,喜欢原创~ 关注我会给你带来一些不一样的认知和成长。
🏆 2022博客之星TOP3 | CSDN博客专家 | 后端领域优质创作者 | CSDN内容合伙人
🏆 InfoQ(极客邦)签约作者、阿里云专家 | 签约博主、51CTO专家 | TOP红人、华为云享专家
🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~
🍅 文末获取联系 🍅 👇🏻 精彩专栏推荐订阅收藏 👇🏻
专栏系列(点击解锁)
学习路线(点击解锁)
知识定位
全面讲解MySQL知识与企业级MySQL实战 🔥计算机底层原理🔥
本文深入浅出讲解什么是Redis Sentinel 哨兵模式、哨兵模式架构。剖析Redis Sentinel实现原理,Sentinel领导者节点选举原理,主节点选举原理,最后附Redis接入哨兵模式实战。
由于Redis的主从复制模式并不具备自动恢复功能,当主服务器关闭时,需要手动将从服务器切换到主服务器。在这个过程中,不仅需要人工干预,而且服务器将在一段时间内不可用,数据安全也将无法保证。因此,主从模式的可用性较低,不适合在线生产环境。
参考资料:【Redis】Redis高可用之Cluster主从模式详解
Redis 了一种高度可用的解决方案,即 Redis Sentinel 模式,用来弥补主从模式的不足。Sentinel可以被视为一个特殊的Redis服务器,Sentinel也可以是一个单机的或集群。Sentinel(哨兵)需要监控并获取主机的工作状态是否正常,当主机发生故障时,Sentinel将自动执行故障切换,并将其监控的从服务器升级到主服务器,以确保系统的高可用性。
哨兵节点的功能描述:
监控(Monitoring):哨兵节点会持续检查主节点和从节点是否正常运行
自动故障切换( Automatic failover) :当主节点无法正常工作时,哨兵自动故障切换操作,将故障主节点的一个从节点升级成为主节点,并让其他从节点复制新的主结点。
配置提供( Configuration provider ):当客户端初始化时,通过连接哨兵获取当前 Redis 服务的主节点地址。
通知(Notification):哨兵可以将故障结果发送给客户端。
Redis Sentinel 模式,由 Sentinel 节点和数据节点两部分组成:
Sentinel 节点(绿色):哨兵系统由一个或多个哨兵节点组成,Sentinel 节点是特殊的Redis节点,它不存储数据和监视数据节点。
数据节点(红色、黄色):主节点和从节点都是数据节点。

Redis 哨兵模式通过哨兵节点完成对数据节点的监控、下线、故障转移。

Redis Sentinel 通过三个计划的监视任务发现并监视每个节点:
1、每10秒,每个Sentinel节点将向主节点和从节点发送信息命令,以获取最新结构
2.、每2秒,每个Sentinel节点将发送_Sentinel_:hello 通道发送 Sentinel 对主节点的判断和当前Sentinel 的信息
3、每秒,每个Sentinel节点将向主节点、从节点和其他 Sentinel 发送ping命令,以进行心跳检测,以确认这些节点当前是否可访问
主观下线:哨兵节点认为某个节点有问题,客观下线:超过一定数量的哨兵节点认为主节点有问题。
主观下线,每个 Sentinel 节点将每1秒向主节点和从节点的其他 Sentinel 发送 ping 命令,以进行心跳检测。当这些节点在几毫秒后停机后无法有效响应时,Sentinel 节点将对此节点做出故障决策为主观下线。
客观下线,当Sentinel的主观下线节点是主节点时,Sentinel节点将通过 sentinel is- master-down-by-addr命令,请求其他 Sentinel 结点判断主节点。当数量超过<quorum>时,Sentinel 节点认为主节点确实存在故障,然后 Sentinel 将做出客观的下线决策
哨兵节点之间将进行领导者选举,选择一个Sentinel 节点作为领导者,所选的Sentinel负责故障切换。
1、从节点中选择一个节点作为新的主节点
2、Sentinel领导者节点将在第一步中选择的从节点上执行 slaveof no one 命令,使其成为主节点
3、Sentinel领导者节点将向剩余的从节点发送命令,使其成为新主节点的从节点
4、Sentinel领导者节点集合会将原始主节点更新为从节点,恢复时会命令它复制新的主节点
Redis使用了 Raft 算法 实现领导者选举。

1、每个哨兵节点都有资格成为领导者,在确认主节点主观下线时候,Sentinel节点将通过sentinel is-master-down-by-addr命令向其他Sentinel节点发送请求将自己设置为领导者。
2、如果接收到命令的 Sentinel 节点未同意过其他Sentinel的命令,它将同意该请求,否则将拒绝。
3、如果哨兵节点发现其投票数大于或等于 max(quorum,num(sentinels)/2+1),它将成为领导者。
首先过滤主观下线、断开连接、在5秒内未对Sentinel节点ping响应作出响应的节点。
选择具有最高从属优先级 slave-priority 的从节点列表,如果存在则返回,如果不存在则选择具有最大副本偏移量(复制的最完整)的从节点,如果存在则返回,如果不存在则继续。选择具有最小runid的从节点。
首先过滤主观下线、断线、5秒内没有回复过 Sentinel 节点ping响应、与主节点失联超过 down-after-milliseconds*10 秒的节点。
选择slave-priority(从节点优先级)最高的从节点列表,如果存在则返回,不存在则继续。选择复制偏移量最大的从节点(复制的最完整),如果存在则返回,不存在则继续。选择runid最小的从节点。

哨兵模式,基于哨兵集群实现主从切换
<bean id="configBean" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location"> <value>redis.properties</value> </property>
</bean>
<!-- 哨兵配置 -->
<bean id="sentinelConfig" class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
<constructor-arg name="master" value="${redis.masterName}"/>
<constructor-arg name="sentinelHostAndPorts">
<set>
<value>${redis.hostAndPort1}</value>
<value>${redis.hostAndPort2}</value>
<value>${redis.hostAndPort3}</value>
</set>
</constructor-arg>
</bean>
<!-- JedisPool连接池 -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大分配的对象数 -->
<property name="maxTotal" value="${redis.pool.maxActive}"/>
<!-- 最大能够保持idel状态的对象数 -->
<property name="maxIdle" value="${redis.pool.maxIdle}"/>
<!-- 当池内没有返回对象时,最大等待时间 -->
<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
<!-- 测试池化连接有效性 -->
<property name="testWhileIdle" value="${redis.pool.testWhileIdle}"/>
<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
<property name="minIdle" value="${redis.pool.minIdle}"/>
<property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
<property name="minEvictableIdleTimeMillis" value="${redis.pool.minEvictableIdleTimeMillis}"/>
<property name="timeBetweenEvictionRunsMillis" value="${redis.pool.timeBetweenEvictionRunsMillis}"/>
<property name="softMinEvictableIdleTimeMillis" value="${redis.pool.softMinEvictableIdleTimeMillis}"/>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="usePool" value="true"/>
<property name="password" value="${redis.pass}"/>
<property name="timeout" value="${redis.timeout}"/>
<property name="poolConfig" ref="jedisPoolConfig"/>
<constructor-arg index="0" ref="sentinelConfig"/>
</bean>
<!--SpringRedis序列化配置 -->
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
<bean id="jsonRedisSerializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<property name="keySerializer" ref="stringRedisSerializer"/>
<property name="valueSerializer" ref="jsonRedisSerializer"/>
</bean>
<bean id="cacheExecutor" class="com.wanlitong.mcmmessage.util.cache.RedisExecutorImpl">
<property name="redisTemplate" ref="redisTemplate"/>
<property name="systemPrefix" value="${redis.systemPrefix}"/>
</bean>
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende
最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru
我在app/helpers/sessions_helper.rb中有一个帮助程序文件,其中包含一个方法my_preference,它返回当前登录用户的首选项。我想在集成测试中访问该方法。例如,这样我就可以在测试中使用getuser_path(my_preference)。在其他帖子中,我读到这可以通过在测试文件中包含requiresessions_helper来实现,但我仍然收到错误NameError:undefinedlocalvariableormethod'my_preference'.我做错了什么?require'test_helper'require'sessions_hel
给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最
一、引擎主循环UE版本:4.27一、引擎主循环的位置:Launch.cpp:GuardedMain函数二、、GuardedMain函数执行逻辑:1、EnginePreInit:加载大多数模块int32ErrorLevel=EnginePreInit(CmdLine);PreInit模块加载顺序:模块加载过程:(1)注册模块中定义的UObject,同时为每个类构造一个类默认对象(CDO,记录类的默认状态,作为模板用于子类实例创建)(2)调用模块的StartUpModule方法2、FEngineLoop::Init()1、检查Engine的配置文件找出使用了哪一个GameEngine类(UGame
了解Rails缓存如何工作的人可以真正帮助我。这是嵌套在Rails::Initializer.runblock中的代码:config.after_initializedoSomeClass.const_set'SOME_CONST','SOME_VAL'end现在,如果我运行script/server并发出请求,一切都很好。然而,在我的Rails应用程序的第二个请求中,一切都因单元化常量错误而变得糟糕。在生产模式下,我可以成功发出第二个请求,这意味着常量仍然存在。我已通过将以上内容更改为以下内容来解决问题:config.after_initializedorequire'some_cl
我经常迷上ruby的一件事是递归模式。例如,假设我有一个数组,它可能包含无限深度的数组作为元素。所以,例如:my_array=[1,[2,3,[4,5,[6,7]]]]我想创建一个方法,可以将数组展平为[1,2,3,4,5,6,7]。我知道.flatten可以完成这项工作,但这个问题是作为我经常遇到的递归问题的一个例子-因此我试图找到一个更可重用的解决方案。简而言之-我猜这种事情有一个标准模式,但我想不出任何特别优雅的东西。任何想法表示赞赏 最佳答案 递归是一种方法,它不依赖于语言。您在编写算法时要考虑两种情况:再次调用函数的情
我的rails3.1.6应用程序中有一个自定义访问器方法,它为一个属性分配一个值,即使该值不存在。my_attr属性是一个序列化的哈希,除非为空白,否则应与给定值合并指定了值,在这种情况下,它将当前值设置为空值。(添加了检查以确保值是它们应该的值,但为简洁起见被删除,因为它们不是我的问题的一部分。)我的setter定义为:defmy_attr=(new_val)cur_val=read_attribute(:my_attr)#storecurrentvalue#makesureweareworkingwithahash,andresetvalueifablankvalueisgiven