上一篇文章我们简单介绍了redis api 的使用方式,本节的内容,我们讲一下他是如何保证高可用的。
测试版本是redis-3.2.11,从官网拉去到源码后,进行编译make,就会在src目录下看到很多可执行文件,例如redis-server,redis-cli,redis-sentinel等大家耳熟能详的。
首先我们解释一下主从的区别:主对于数据可读可写。从默认是只读不写的。当从连接上主时,主会将数据同步到从上。详细的内容我们可以参考一下Redis主从同步原理-SYNC这篇文章讲的还是很详细的。
哨兵:当主挂掉,哨兵可以通过选举从从服务器中选择一台作为主,提供高可用的方案。注意当原来的主恢复时,也将成为现在主的从服务器。
概念解释完了,我们来搭建环境。首先我们搭建的是主从。从最基本的一主一从开始。
主的配置相较于默认的配置文件修改的点,主要修改的是存放数据的目录而已
bind 127.0.0.1
port 6379
pidfile "/Users/olifer/middle/redis/master/redis_6379.pid"
dir "/Users/olifer/middle/redis/master"
配置好了我们启动服务redis-server redis.conf , 如果看到下面的文字,说明启动成功
783:M 02 Dec 19:19:39.829 # Server started, Redis version 3.2.11
783:M 02 Dec 19:19:39.829 * The server is now ready to accept connections on port 6379
从的配置也相对较为简单,端口改一下6380,最为主要的是配置slaveof,指明他的主是谁
bind 127.0.0.1
port 6380
pidfile "/Users/olifer/middle/redis/slave/redis_6380.pid"
dir "/Users/olifer/middle/redis/slave1"
slaveof 127.0.0.1 6379
配置好了我们启动服务redis-server redis.conf , 如果看到下面的文字,说明启动成功
900:S 02 Dec 19:22:16.682 # Server started, Redis version 3.2.11
900:S 02 Dec 19:22:16.682 * The server is now ready to accept connections on port 6380
900:S 02 Dec 19:22:16.684 * Connecting to MASTER 127.0.0.1:6379
900:S 02 Dec 19:22:16.684 * MASTER <-> SLAVE sync started
通过slave的日志我们可以看出,启动成功,暴露端口为6380,并且连接上了自己的主6379,并且开始了同步数据。
这时候我们可以看一下主的日志,同样也能得到类似的信息,从主的日志可以看出,从确实连接上了主,并且爱是同步数据到从服务器,这个数据同步的过程我们会在下一篇文章中讲述。
783:M 02 Dec 19:22:16.685 * Slave 127.0.0.1:6380 asks for synchronization
783:M 02 Dec 19:22:16.685 * Full resync requested by slave 127.0.0.1:6380
783:M 02 Dec 19:22:16.685 * Starting BGSAVE for SYNC with target: disk
783:M 02 Dec 19:22:16.685 * Background saving started by pid 901
901:C 02 Dec 19:22:16.685 * DB saved on disk
783:M 02 Dec 19:22:16.760 * Background saving terminated with success
783:M 02 Dec 19:22:16.761 * Synchronization with slave 127.0.0.1:6380 succeeded
目前为止我们一个redis的主从就这么搭建好了,是不是超级简单,搭建好了,我们就需要验证功能了。我们使用redis-cli连接到主上,也就是6379。当然你也可以不用指定-p,因为默认的就是6379,这样就可以进入到redis shell的操作界面。我们简单的set一个值
redis-cli -p 6379
127.0.0.1:6379> set name olifer
OK
这时候我们验证一下从服务器的数据有没有同步过来。
redis-cli -p 6380
127.0.0.1:6380> get name
"olifer"
和我们预想的是一致的,我们从从的服务器上可以获取到主的数据,更为确切的说,是主从数据进行了准实时的同步,所以可以从从服务器上获取到数据。
我们可以往从服务器上设置值吗?实践是检验真理的唯一的方式,试一下
127.0.0.1:6380> set age 123
(error) READONLY You can't write against a read only slave.
发现报错了,slave默认情况下是不能写数据,只能读取数据。当然这个也是可以设置的,在从的配置文件中有这个一个配置
slave-read-only yes
从名字我们就看出来他是只读的,如果我们想slave也可以写,我们可以修改此配置为no,之后我们重启一下,看结果就可以了。
127.0.0.1:6380> set age 123
OK
所以一些有用的配置大家还是属性一下吧。
现在大家思考一个问题,主从结构只能保证数据有备份,但是如果主挂掉了,怎么办,整个服务就不可用了,这显然是个大问题。当然redis也帮我们考虑到了这一点。解决方案就是哨兵。他可以作为一个第三方,监控着我们主节点的状态,当发现主挂掉后,就会从这个主的从服务器中进行选举,推选出一个新主,同时slave也切换成这个新主的slave,如果原来的主重启成功了也不会再次成为主了,而是作为slave挂到新主上。现在我们再逐步进行验证。因为涉及到选举所以一个slave肯定是不够的,所以最少是两个,上面我们的6380已经是一个从了,所以我们再新建一个6381的从服务器。配置如下
bind 127.0.0.1
port 6381
pidfile "/Users/olifer/middle/redis/slave/redis_6381.pid"
dir "/Users/olifer/middle/redis/slave2"
slaveof 127.0.0.1 6379
启动redis-server redis-conf后出现下面的页面,说明配置成功
1229:S 02 Dec 19:57:08.243 # Server started, Redis version 3.2.11
1229:S 02 Dec 19:57:08.243 * DB loaded from disk: 0.000 seconds
1229:S 02 Dec 19:57:08.243 * The server is now ready to accept connections on port 6381
1229:S 02 Dec 19:57:08.243 * Connecting to MASTER 127.0.0.1:6379
下面我们再配置哨兵,其实也是特别的简单,我们可以从redis的源文件中找到sentinel.conf配置文件,他就是哨兵的配置
port 26379
sentinel monitor mymaster 127.0.0.1 6379 1
sentinel down-after-milliseconds mymaster 5000
sentinel parallel-syncs mymaster 2
port不用多说,就是一个端口号。我们说一下这个配置,sentinel monitor mymaster 127.0.0.1 6379 1,我们给监控的主节点起了个名字叫做mymaster,他的位置是127.0.0.1,端口是6379,主挂掉后我们要通过选举获得最新的主,所以1代表了法定的人数,也就是有1台服务器同意这个提案就可以。这个配置sentinel down-after-milliseconds mymaster 5000,表示master或slave 5s(默认30秒)不能使用后标记为s_down状态。sentinel parallel-syncs mymaster 2,指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步,配置完成后,开始启动
redis-sentinel sentinel.conf
1385:X 02 Dec 20:12:44.725 * Increased maximum number of open files to 10032 (it was originally set to 7168).
_._
_.-``__ ''-._
_.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit
.-`` .-```. ```\/ _.,_ ''-._
( ' , .-` | `, ) Running in sentinel mode
|`-._`-...-` __...-.``-._|'` _.-'| Port: 26379
| `-._ `._ / _.-' | PID: 1385
`-._ `-._ `-./ _.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' | http://redis.io
`-._ `-._`-.__.-'_.-' _.-'
|`-._`-._ `-.__.-' _.-'_.-'|
| `-._`-._ _.-'_.-' |
`-._ `-._`-.__.-'_.-' _.-'
`-._ `-.__.-' _.-'
`-._ _.-'
`-.__.-'
1385:X 02 Dec 20:12:44.726 # Sentinel ID is 104e563c9931120931c3594973cab424509c2646
1385:X 02 Dec 20:12:44.726 # +monitor master mymaster 127.0.0.1 6379 quorum 1
1385:X 02 Dec 20:12:44.727 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:12:44.728 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
从这个日志中我们可以看出,记录了master的信息(127.0.0.1 6379),也记录了slave的信息(127.0.0.1 6380,127.0.0.1 6381).我们可以进入查看一下
redis-cli -p 26379
就可以查看一下master,slave包括哨兵的信息
sentinel master mymaster //查看master的状态
sentinel slaves mymaster //查看salves的状态
sentinel sentinels mymaster //查看哨兵的状态
info sentinel //查看哨兵信息
我们的环境配置好了,就可以验证一些问题了。第一个问题当主挂掉能否自动切换,从slave中挑选一台作为主。下面我们就把master shutdown.我们可以从哨兵的日志中发现一段这样的日志
1385:X 02 Dec 20:26:49.199 # +sdown master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.199 # +odown master mymaster 127.0.0.1 6379 #quorum 1/1
1385:X 02 Dec 20:26:49.199 # +new-epoch 1
1385:X 02 Dec 20:26:49.199 # +try-failover master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.208 # +vote-for-leader 104e563c9931120931c3594973cab424509c2646 1
1385:X 02 Dec 20:26:49.208 # +elected-leader master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.208 # +failover-state-select-slave master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.274 # +selected-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.274 * +failover-state-send-slaveof-noone slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:49.379 * +failover-state-wait-promotion slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:50.309 # +promoted-slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:50.309 # +failover-state-reconf-slaves master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:50.391 * +slave-reconf-sent slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:51.332 * +slave-reconf-inprog slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:52.392 * +slave-reconf-done slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:52.447 # +failover-end master mymaster 127.0.0.1 6379
1385:X 02 Dec 20:26:52.447 # +switch-master mymaster 127.0.0.1 6379 127.0.0.1 6380
1385:X 02 Dec 20:26:52.447 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6380
1385:X 02 Dec 20:26:52.447 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
1385:X 02 Dec 20:26:57.489 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
每个sentinel会向其它sentinal、master、slave定时发送消息,以确认对方是否“活”着,如果发现对方在指定时间(可配置)内未回应,则暂时认为对方已挂(所谓的“主观认为宕机” Subjective Down,简称SDOWN)。若“哨兵群”中的多数sentinel,都报告某一master没响应,系统才认为该master”彻底死亡”(即:客观上的真正down机,Objective Down,简称ODOWN),通过一定的vote算法,从剩下的slave节点中,选一台提升为master,然后自动修改相关配置。到最后通过选举决定了6380作为新的主,同时将6379(已经挂掉)与6381(存活)作为 6380 的slave。这时候我们看一下各个节点的配置文件。首先看一下6380,原来配置文件中的slaveof 127.0.0.1 6379 已经删掉了,说明他已经不是从节点了,那么他就变成主节点了。在看一下6381 ,他的配置已经从原来的slaveof 127.0.0.1 6379 变成了 slaveof 127.0.0.1 6380。看一下哨兵的配置文件,最下面多了几行
# Generated by CONFIG REWRITE
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1
sentinel known-slave mymaster 127.0.0.1 6379
sentinel known-slave mymaster 127.0.0.1 6381
sentinel current-epoch 1
说明哨兵也已经做好了切换。6379这时候在哨兵的眼里就是一个slave了,所以他重启后,不可能变成主了,只能是从了。我们现在启动6379 ,在哨兵的日志中增加了下面两行
1385:X 02 Dec 20:40:09.929 # -sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
1385:X 02 Dec 20:40:19.872 * +convert-to-slave slave 127.0.0.1:6379 127.0.0.1 6379 @ mymaster 127.0.0.1 6380
在6379的redis-conf文件的最后面,增加了两行
# Generated by CONFIG REWRITE
slaveof 127.0.0.1 6380
所以总的过程就是当哨兵发现主挂掉后,会通过选举选出新主,slave的关系进行切换。整个过程顺滑、完整。不得不说做的很漂亮啊。