序言
Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案。实际上这意味着你可以使用Sentinel模式创建一个可以不用人为干预而应对各种故障的Redis部署。
sentinel的分布式特性
关于sentinel的稳定版本
当前的哨兵版本是sentinel 2。它是基于最初哨兵的实现,使用更健壮的和更简单的预算算法(在这个文档里有解释)重写的。
Redis2.8和Redis3.0附带稳定的哨兵版本。他们是Redis的两个最新稳定版本。
在不稳定版本的分支上执行新的改进,且有时一些新特性一旦被认为是稳定的就会被移植到Redis2.8和Redis3.0分支中。
Redis2.6附带Redis sentinel 1,它是弃用的不建议使用。
运行sentinel
然而在启动哨兵时必须使用一个配置文件,因为这个配置文件将用于系统保存当前状态和在重启时重新加载。哨兵会在没有指定配置文件或指定的配置文件不可写的时候拒绝启动。
Redis 哨兵默认监听26379 TCP端口,所以为了哨兵的正常工作,你的26379端口必须开放接收其他哨兵实例的IP地址的连接。否则哨兵不能通信和商定做什么,故障转移将永不会执行。
部署哨兵之前需要了解的基本事情
sentinel + Redis实例不保证在故障期间保留确认的写入,因为Redis使用异步复制。然而有方式部署哨兵使丢失数据限制在特定时刻,虽然有更安全的方式部署它。
没有HA设置是安全的,如果你不经常的在开发环境测试,在生产环境他们会更好。你可能会有一个明显的错误配置只是当太晚的时候。
Sentinel的配置
Redis源码发布包包含一个sentinel.conf的文件,默认的配置文件中有关于各个配置项的详细解释,一个典型的最小的配置文件就像下面的配置:
quorun:本文叫做票数,Sentinel需要协商同意master是否可到达的数量。
这一行用于告诉Redis监控一个master叫做mymaster,它的地址在127.0.0.1,端口为6379,票数是2。
Sentinel的“仲裁会”
前面我们谈到,主从故障转移时,需要的sentinel认可的票数达到设置的值才可以。
配置版本号
为什么要先获得大多数sentinel的认可时才能真正去执行failover呢?
配置传播
一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置通过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置。
一个faiover要想被成功实行,sentinel必须能够向选为master的slave发送SLAVE OF NO ONE
命令,然后能够通过INFO
命令看到新master的配置信息。
新配在集群中相互传播的方式,就是为什么我们需要当一个sentinel进行failover时必须被授权一个版本号的原因。
每个sentinel使用##发布/订阅##的方式持续地传播master的配置版本信息,配置传播的##发布/订阅##管道是:__sentinel__:hello
。
因为每一个配置都有一个版本号,所以以版本号最大的那个为标准。
这意味着sentinel集群保证了第二种活跃性:一个能够互相通信的sentinel集群最终会采用版本号最高且相同的配置。
SDOWN和ODOWN的更多细节
sentinel对于不可用有两种不同的看法,一个叫主观不可用(SDOWN),另外一个叫客观不可用(ODOWN)。
SDOWN是sentinel自己主观上检测到的关于master的状态。
当sentinel发送PING后,以下回复都被认为是合法的,除此之外,其它任何回复(或者根本没有回复)都是不合法的。
正如之前已经解释过了,真正进行failover需要一个授权的过程,但是所有的failover都开始于一个ODOWN状态。
ODOWN状态只适用于master,对于不是master的redis节点sentinel之间不需要任何协商,slaves和sentinel不会有ODOWN状态。
Sentinel之间和Slaves之间的自动发现机制
通过向名为__sentinel__:hello
的管道中发送消息来实现。
同样,你也不需要在sentinel中配置某个master的所有slave的地址,sentinel会通过询问master来得到这些slave的地址的。
网络隔离时的一致性
redis sentinel集群的配置的一致性模型为最终一致性,集群中每个sentinel最终都会采用最高版本的配置。然而,在实际的应用环境中,有三个不同的角色会与sentinel打交道:
下面有个简单的例子,有三个主机,每个主机分别运行一个redis和一个sentinel:
+-------------+
| Sentinel 1 | <--- Client A | Redis 1 (M) | +-------------+ | | +-------------+ | +------------+ | Sentinel 2 |-----+-- / partition / ----| Sentinel 3 | <--- Client B | Redis 2 (S) | | Redis 3 (M)| +-------------+ +------------+
Sentinel集群的特性保证了sentinel1和sentinel2得到了关于master的最新配置。但是sentinel3依然持着的是就的配置,因为它与外界隔离了。
当网络恢复以后,我们知道sentinel3将会更新它的配置。但是,如果客户端所连接的master被网络隔离,会发生什么呢?
客户端将依然可以向redis3写数据,但是当网络恢复后,redis3就会变成redis的一个slave,那么,在网络隔离期间,客户端向redis3写的数据将会丢失。
因为redis采用的是异步复制,在这样的场景下,没有办法避免数据的丢失。然而,你可以通过以下配置来配置redis3和redis1,使得数据不会丢失。
Sentinel状态持久化
无failover时的配置纠正
即使当前没有failover正在进行,sentinel依然会使用当前配置去设置监控的master。特别是:
Slave选举与优先级
当一个sentinel准备好了要进行failover,并且收到了其他sentinel的授权,那么就需要选举出一个合适的slave来做为新的master。
就会被认为失去选举资格。符合上述条件的slave才会被列入master候选人列表,并根据以下顺序来进行排序:
一个redis无论是master还是slave,都必须在配置中指定一个slave优先级。要注意到master也是有可能通过failover变成slave的。
如果一个redis的slave优先级配置为0,那么它将永远不会被选为master。但是它依然会从master哪里复制数据。
Sentinel和Redis身份验证
当一个master配置为需要密码才能连接时,客户端和slave在连接时都需要提供密码。
master通过requirepass
设置自身的密码,不提供密码无法连接到这个master。
slave通过masterauth
来设置访问master时的密码。
但是当使用了sentinel时,由于一个master可能会变成一个slave,一个slave也可能会变成master,所以需要同时设置上述两个配置项。
Sentinel API
Sentinel默认运行在26379端口上,sentinel支持redis协议,所以可以使用redis-cli客户端或者其他可用的客户端来与sentinel通信。
Sentinel命令
SENTINEL reset <pattern>
重置名字匹配该正则表达式的所有的master的状态信息,清楚其之前的状态信息,以及slaves信息。SENTINEL failover <master name>
强制sentinel执行failover,并且不需要得到其他sentinel的同意。但是failover后会将最新
动态修改Sentinel配置
从redis2.8.4开始,sentinel提供了一组API用来添加,删除,修改master的配置。
需要注意的是,如果你通过API修改了一个sentinel的配置,sentinel不会把修改的配置告诉其他sentinel。你需要自己手动地对多个sentinel发送修改配置的命令。
SENTINEL MONITOR <name> <ip> <port> <quorum>
这个命令告诉sentinel去监听一个新的masterSENTINEL SET objects-cache-master down-after-milliseconds 1000
只要是配置文件中存在的配置项,都可以用SENTINEL SET
命令来设置。这个还可以用来设置master的属性,比如说quorum(票数),而不需要先删除master,再重新添加master。例如:
增加或删除Sentinel
删除一个sentinel显得有点复杂:因为sentinel永远不会删除一个已经存在过的sentinel,即使它已经与组织失去联系很久了。要想删除一个sentinel,应该遵循如下步骤:
删除旧master或者不可达slave
sentinel永远会记录好一个Master的slaves,即使slave已经与组织失联好久了。这是很有用的,因为sentinel集群必须有能力把一个恢复可用的slave进行重新配置。
并且,failover后,失效的master将会被标记为新master的一个slave,这样的话,当它变得可用时,就会从新master上复制数据。
发布/订阅
客户端可以向一个sentinel发送订阅某个频道的事件的命令,当有特定的事件发生时,sentinel会通知所有订阅的客户端。需要注意的是客户端只能订阅,不能发布。
订阅频道的名字与事件的名字一致。例如,频道名为sdown 将会发布所有与SDOWN相关的消息给订阅者。
如果想要订阅所有消息,只需简单地使用PSUBSCRIBE *
以下是所有你可以收到的消息的消息格式,如果你订阅了所有消息的话。第一个单词是频道的名字,其它是数据的格式。
<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>
如果这个redis实例是一个master,那么@之后的消息就不会显示。
TILT 模式
BUSY状态
Sentinel部署示例
既然你知道了sentinel的基本信息,你可以很想知道应该将Sentinel放置在哪里,需要多少Sentinel进程等等。这个章节展示了几个部署示例。
我们为了图像化展示配置示例使用字符艺术,这是不同符号的意思:
+--------------------+
| This is a computer | | or VM that fails | | independently. We | | call it a "box" | +--------------------+
+-------------------+
| Redis master M1 | | Redis Sentinel S1 | +-------------------+
+-------------+ +-------------+
| Sentinel S1 |---------------| Sentinel S2 | +-------------+ +-------------+
+-------------+ +-------------+
| Sentinel S1 |------ // ------| Sentinel S2 | +-------------+ +-------------+
注意永远不会显示的设置只是使用了两个哨兵,因为为了启动故障转移,Sentinel总是需要和其他大多数的Sentinel通信。
实例1,只有两个Sentinel,不要这样做
+----+ +----+
| M1 |---------| R1 | | S1 | | S2 | +----+ +----+ Configuration: quorum = 1
注意为了排列不同的故障转移需要少数服从多数,并且稍后向所有的Sentinel传播最新的配置。还要注意上面配置的故障转移的能力,没有任何协定,非常危险:
+----+ +------+
| M1 |----//-----| [M1] | | S1 | | S2 | +----+ +------+
例2:使用三个盒子的基本设置
这是个非常简单的设置,它有简单调整安全的优势。它基于三个盒子,每个盒子同时运行一个Redis实例和一个Sentinel实例。
+----+
| M1 | | S1 | +----+ | +----+ | +----+ | R2 |----+----| R3 | | S2 | | S3 | +----+ +----+ Configuration: quorum = 2
如果M1故障,S2和S3将会商定故障并授权故障转移,使客户端可以继续。
+----+
| M1 | | S1 | [- C1 (writes will be lost) +----+ | / / +------+ | +----+ | [M2] |----+----| R3 | | S2 | | S3 | +------+ +----+
这个问题可以使用下面的Redis主从复制特性减轻,它可在master检查到它不再能传输它的写入操作到指定数量的slave的时候停止接收写入操作。
使用上面的配置(请查看自带的redis.conf示例了解更多信息)一个Redis实例,当作为一个master,如果它不能写入至少1个slave将停止接收写入操作。(N个Slave以上不通就停止接收)
由于主从复制是异步的不能真实的写入,意味着slave断开连接,或者不再向我们发送异步确认的指定的max-lag秒数。(判定连接不通的超时时间)
在上面的示例中使用这个配置,老master M1将会在10秒钟后变为不可用。当分区恢复时,Sentinel配置将指向新的一个,客户端C1将能够获取到有效的配置并且将使用新master继续工作。
然而天下没有免费的午餐,这种改进,如果两个slave挂掉,master将会停止接收写入操作。这是个权衡。
例三:Sentinel在客户端盒子里
有时我们只有两个Redis盒子可用,一个master和一个slave。在例二中的配置在那样的情况下是不可行的,所谓我们可以借助下面的,Sentinel放置在客户端:
+----+ +----+ | M1 |----+----| R1 | | S1 | | | S2 | +----+ | +----+ | +------------+------------+ | | | | | | +----+ +----+ +----+ | C1 | | C2 | | C3 | | S1 | | S2 | | S3 | +----+ +----+ +----+ Configuration: quorum = 2
所以这是个有效的设置但是在例二中的设置有像更容易管理HA系统的优点, 并有能力限制master接收写入的时间。
例4:少于3个客户端的Sentinel客户端
在例3中如果客户端少于3个就不能使用。在这个案例中我们使用一个混合的设置:
+----+ +----+ | M1 |----+----| R1 | | S1 | | | S2 | +----+ | +----+ | +------+-----+ | | | | +----+ +----+ | C1 | | C2 | | S3 | | S4 | +----+ +----+ Configuration: quorum = 3
这里和例3非常类似,但是这里我们在4个盒子里运行四个哨兵。如果M1故障其他的三个哨兵可以执行故障转移。
https://redis.io/topics/sentinel
http://redis.majunwei.com/topics/sentinel.html
https://segmentfault.com/a/1190000002680804
https://segmentfault.com/a/1190000002685515