redis哨兵实现高可用

redis 主从只能保证数据备份,引入哨兵实现高可用

哨兵的缺陷:redis哨兵机制还是建立在redis的一主多从上面,其实不能解决redis主服务器的压力(因为这种架构下master还是只有一个,master不但要进行replication,还要进行来自客户端的write,要解决这个问题就要使用redis cluster。就拿一主两从来说,三个redis节点,至少需要三个sentinel(一个sentinel存在单点失败问题,sentinel需要互相监控,两个sentinel存在脑裂问题),因此最少使用三个sentinel,也就是说一主两从实现高可用就需要六个节点来实现,成本太高,六个节点不如采用cluster。一般来说哨兵消耗的资源比较少,公司不太可能使用一个独立的节点来部署一个sentinel,因此经常把一个sentinel同一个master和slave部署在一起。

环境准备三个节点:
master,sentinel,10.35.78.63
slave-1,sentinel-1,10.35.78.64
slave-2, sentinel-2, 10.35.78.69

首先实现一主两从:

master:

bind 0.0.0.0
port 6379
daemonize yes
masterauth "lujihui66"
slave-serve-stale-data yes
slave-read-only yes
slave-priority 100
requirepass "lujihui66" #密码
min-slaves-to-write 1
min-slaves-max-lag 10

slave-1:

bind 0.0.0.0
port 6379
daemonize yes
slaveof 10.35.78.63 6379  	#成为10.35.78.63的slave
masterauth "lujihui66"    	# master的验证密码
slave-read-only yes
slave-priority 100
requirepass "lujihui66" 	#密码和master一样,因为slave可能升级为master。master通过requirepass设置自身的密码,不提供密码无法连接到这个master。slave通过masterauth来设置访问master时的密码
min-slaves-to-write 1
min-slaves-max-lag 10

slave-2:(采用命令行的方式修改配置文件,sentinel就是通过该方式修改slave的配置文件的)

slaveof 10.35.78.63 6379
config set masterauth lujihui66
config set requirepass lujihui66
config rewrite

min-slaves-to-write 1
min-slaves-max-lag 10

检查哨兵状态,两个slave都正常

10.35.78.63:6379> INFO replication
# Replication
role:master
connected_slaves:2
slave0:ip=10.35.78.64,port=6379,state=online,offset=34598,lag=0
slave1:ip=10.35.78.69,port=6379,state=online,offset=34598,lag=1
master_repl_offset:34736
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:34735

部署sentinel

配置sentinel.conf,三个sentinel配置都一样

port 26379
bind 0.0.0.0
daemonize yes
logfile "/main/server/redis/redis-3.2.11/sentinel_26379.log"
sentinel monitor agvmaster 10.35.78.63 6379 2  #sentinel只需要配置需要监视的master,因为通过master可以知道slave的信息
sentinel auth-pass agvmaster lujihui66
sentinel down-after-milliseconds agvmaster 5000

任意一sentinel节点查看sentinel状态

# redis-cli  -p 26379
sentinel_masters:1(sentinel检测到1个master)
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=agvmaster,status=ok,address=10.35.78.63:6379,slaves=2,sentinels=3(两个slave,3个sentinel)
sentinel.log解析
+reset-master <instance details> -- 当master被重置时.
    +slave <instance details> -- 当检测到一个slave并添加进slave列表时.
    +failover-state-reconf-slaves <instance details> -- Failover状态变为reconf-slaves状态时
    +failover-detected <instance details> -- 当failover发生时
    +slave-reconf-sent <instance details> -- sentinel发送SLAVEOF命令把它重新配置时
    +slave-reconf-inprog <instance details> -- slave被重新配置为另外一个master的slave,但数据复制还未发生时。
    +slave-reconf-done <instance details> -- slave被重新配置为另外一个master的slave并且数据复制已经与master同步时。
    -dup-sentinel <instance details> -- 删除指定master上的冗余sentinel时 (当一个sentinel重新启动时,可能会发生这个事件).
    +sentinel <instance details> -- 当master增加了一个sentinel时。
    +sdown <instance details> -- 进入SDOWN状态时;
    -sdown <instance details> -- 离开SDOWN状态时。
    +odown <instance details> -- 进入ODOWN状态时。
    -odown <instance details> -- 离开ODOWN状态时。
    +new-epoch <instance details> -- 当前配置版本被更新时。
    +try-failover <instance details> -- 达到failover条件,正等待其他sentinel的选举。
    +elected-leader <instance details> -- 被选举为去执行failover的时候。
    +failover-state-select-slave <instance details> -- 开始要选择一个slave当选新master时。
    no-good-slave <instance details> -- 没有合适的slave来担当新master
    selected-slave <instance details> -- 找到了一个适合的slave来担当新master
    failover-state-send-slaveof-noone <instance details> -- 当把选择为新master的slave的身份进行切换的时候。
    failover-end-for-timeout <instance details> -- failover由于超时而失败时。
    failover-end <instance details> -- failover成功完成时。
    switch-master <master name> <oldip> <oldport> <newip> <newport> -- 当master的地址发生变化时。通常这是客户端最感兴趣的消息了。
    +tilt -- 进入Tilt模式。
    -tilt -- 退出Tilt模式。
关闭master,并查看其中一个sentinel.log的消息:

10870:X 01 Dec 06:50:00.418 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
10870:X 01 Dec 06:50:00.418 # Sentinel ID is 873ac8a0e86a975eec0deed7c96cf53803e917ea
10870:X 01 Dec 06:50:00.418 # +monitor master agvmaster 10.35.78.63 6379 quorum 2
10870:X 01 Dec 06:50:00.419 * +slave slave 10.35.78.64:6379 10.35.78.64 6379 @ agvmaster 10.35.78.63 6379
10870:X 01 Dec 06:50:00.421 * +slave slave 10.35.78.69:6379 10.35.78.69 6379 @ agvmaster 10.35.78.63 6379
10870:X 01 Dec 06:50:01.862 * +sentinel sentinel 346b1232388b470deedc0719ff12694491985506 10.35.78.64 26379 @ agvmaster 10.35.78.63 6379
10870:X 01 Dec 06:51:19.953 * +sentinel sentinel aeca5db11e31e94779ff1eac03589dded9dce7b2 10.35.78.69 26379 @ agvmaster 10.35.78.63 6379
10870:X 01 Dec 07:02:50.063 # +sdown master agvmaster 10.35.78.63 6379   (关闭master后开始检测到异常)
10870:X 01 Dec 07:02:50.116 # +odown master agvmaster 10.35.78.63 6379 #quorum 	2/2  ###############(2票,超过半数sentinel已经认为master离线了,从subject down转化为object down,开始选举)
10870:X 01 Dec 07:02:50.116 # +new-epoch 1
10870:X 01 Dec 07:02:50.116 # +try-failover master agvmaster 10.35.78.63 6379    #########(failover:故障转移是一个过程,修改各个redis节点的配置,让另外的slave成为新的master的slave,并同步新的数据,然后重新上线)
10870:X 01 Dec 07:02:50.120 # +vote-for-leader 873ac8a0e86a975eec0deed7c96cf53803e917ea 1
10870:X 01 Dec 07:02:50.121 # aeca5db11e31e94779ff1eac03589dded9dce7b2 voted for aeca5db11e31e94779ff1eac03589dded9dce7b2 1
10870:X 01 Dec 07:02:50.127 # 346b1232388b470deedc0719ff12694491985506 voted for aeca5db11e31e94779ff1eac03589dded9dce7b2 1
10870:X 01 Dec 07:02:51.227 # +config-update-from sentinel aeca5db11e31e94779ff1eac03589dded9dce7b2 10.35.78.69 26379 @ agvmaster 10.35.78.63 6379
10870:X 01 Dec 07:02:51.227 # +switch-master agvmaster 10.35.78.63 6379 10.35.78.69 6379             #####################(master的权利从78.63交给了78.69)
10870:X 01 Dec 07:02:51.228 * +slave slave 10.35.78.64:6379 10.35.78.64 6379 @ agvmaster 10.35.78.69 6379
10870:X 01 Dec 07:02:51.228 * +slave slave 10.35.78.63:6379 10.35.78.63 6379 @ agvmaster 10.35.78.69 6379      ##################(78.63变成了slave)
10870:X 01 Dec 07:02:56.272 # +sdown slave 10.35.78.63:6379 10.35.78.63 6379 @ agvmaster 10.35.78.69 6379	#######################(sentinel检测到78.63还处于sdown)

问题:
重新上线以后master的ip地址变成10.35.78.69,而原来的master78.63变成slave,不能再接收客户端的写请求,怎么解决?java客户端连接redis是通过Jedis来实现的,java代码用的时候只要创建Jedis对象就可以建多个Jedis连接池来连接redis,应用程序再直接调用连接池即可连接Redis。而Redis为了保障高可用,服务一般都是Sentinel部署方式,当Redis服务中的主服务挂掉之后,会仲裁出另外一台Slaves服务充当Master。这个时候,我们的应用即使使用了Jedis 连接池,Master服务挂了,我们的应用将还是无法连
接新的Master服务,为了解决这个问题, Jedis也提供了相应的Sentinel实现,能够在Redis Sentinel主从切换时候,通知我们的应用,把我们的应用连接到新的Master服务。

Redis Sentinel的使用也是十分简单的,只是在JedisPool中添加了Sentinel和MasterName参数,JRedis Sentinel底层基于Redis订阅实现Redis主从服务的切换通知,当Reids发生主从切换时,Sentinel会发送通知主动通知Jedis进行连接的切换,JedisSentinelPool在每次从连接池中获取链接对象的时候,都要对连接对象进行检测,如果此链接和Sentinel的Master服务连接参数不一致,则会关闭此连接,重新获取新的Jedis连接对象。

import java.util.HashSet;
import java.util.Set;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;

**##开发需要在配置文件配置sentinel的ip和端口**
示例1public class test1 {

	public static void main(String[] args) {
        Set<String> sentinels = new HashSet<String>();
        String hostAndPort1 = "10.35.78.63:26379";
        String hostAndPort2 = "10.35.78.64:26379";
		String hostAndPort3 = "10.35.78.69:26379";
        sentinels.add(hostAndPort1);
        sentinels.add(hostAndPort2);
		sentinels.add(hostAndPort3);
		
		

        String clusterName = "agvmaster";
        String password = "lujihui66";

        JedisSentinelPool redisSentinelJedisPool = new JedisSentinelPool(clusterName,sentinels,password);

        Jedis jedis = null;
        try {
            jedis = redisSentinelJedisPool.getResource();
            jedis.set("date", "1207");
            System.out.println(jedis.get("date"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            redisSentinelJedisPool.returnBrokenResource(jedis);
        }

        redisSentinelJedisPool.close();                	
	
	}
}


2020-12-7 10:45:59 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Trying to find master from available Sentinels...
2020-12-7 10:46:00 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Redis master running at 10.35.78.63:6379, starting Sentinel listeners...
2020-12-7 10:46:00 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 10.35.78.63:6379

此时将10.35.78.63的网卡关闭ifdown eth0,再次运行java程序
示例2:
运行刚才的java程序,发现数据仍然可以写入,redis的主备已经切换
2020-12-7 15:07:26 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Trying to find master from available Sentinels...
2020-12-7 15:07:26 redis.clients.jedis.JedisSentinelPool initSentinels
信息: Redis master running at 10.35.78.69:6379, starting Sentinel listeners...
2020-12-7 15:07:26 redis.clients.jedis.JedisSentinelPool initPool
信息: Created JedisPool to master at 10.35.78.69:6379(##################master已经变成10.35.78.692020-12-7 15:07:28 redis.clients.jedis.JedisSentinelPool$MasterListener run**(#################提示10.35.78.63的sentinel失去连接)**
严重: Lost connection to Sentinel at 10.35.78.63:26379. Sleeping 5000ms and retrying.
redis.clients.jedis.exceptions.JedisConnectionException: java.net.SocketTimeoutException: connect timed out
	at redis.clients.jedis.Connection.connect(Connection.java:207)
	at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
	at redis.clients.jedis.Connection.setTimeoutInfinite(Connection.java:94)
	at redis.clients.jedis.Jedis.subscribe(Jedis.java:2678)
	at redis.clients.jedis.JedisSentinelPool$MasterListener.run(JedisSentinelPool.java:291)
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.PlainSocketImpl.doConnect(Unknown Source)
	at java.net.PlainSocketImpl.connectToAddress(Unknown Source)
	at java.net.PlainSocketImpl.connect(Unknown Source)
	at java.net.SocksSocketImpl.connect(Unknown Source)
	at java.net.Socket.connect(Unknown Source)
	at redis.clients.jedis.Connection.connect(Connection.java:184)
	... 4 more

ps

redis.conf中两个参数可以降低主备切换时数据丢失的概率:
min-slaves-to-write 1
min-slaves-max-lag 10
sentinel底层通信原理可见博客:https://www.cnblogs.com/williamjie/p/9505782.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值