Sentinel的客户端

如果要做到应用程序(客户端)对Redis的failover透明Transparent),客户端需要监控sentinel的频道信息,并自动连接新的主节点。官方提供了一个专门的topic来讲解这个问题:Guidelines for Redis clients with support for Redis Sentinel,而一些常用的开发语言也已经有了整合sentinel的redis driver:

Python:Redis

Java:Jedis

这里以Python为例,首先安装redis-py。

$ pip install redis
1
$pipinstallredis

简单连接

>>> import redis
>>> r = redis.StrictRedis(host='localhost', port=6379, db=0)
>>> r.set('foo', 'bar')
True
>>> r.get('foo')
'bar'
1
2
3
4
5
6
>>>importredis
>>>r=redis.StrictRedis(host='localhost',port=6379,db=0)
>>>r.set('foo','bar')
True
>>>r.get('foo')
'bar'

哨兵支持

>>> from redis.sentinel import Sentinel
>>> sentinel = Sentinel([('127.0.0.1', 26379),('127.0.0.1', 26380)], socket_timeout=0.1)
>>> sentinel.discover_master('mymaster')
('127.0.0.1', 6379)
>>> sentinel.discover_slaves('mymaster')
[('127.0.0.1', 6380)]
1
2
3
4
5
6
>>>fromredis.sentinelimportSentinel
>>>sentinel=Sentinel([('127.0.0.1',26379),('127.0.0.1',26380)],socket_timeout=0.1)
>>>sentinel.discover_master('mymaster')
('127.0.0.1',6379)
>>>sentinel.discover_slaves('mymaster')
[('127.0.0.1',6380)]
然后可以从Sentinel实例创建Redis客户端连接,可以连接到Redis Master(用于写入操作)或Redis Slave(用于只读操作)。
>>> master = sentinel.master_for('mymaster', socket_timeout=0.1)
>>> slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
>>> master.set('foo', 'bar')
>>> slave.get('foo')
'bar'
1
2
3
4
5
>>>master=sentinel.master_for('mymaster',socket_timeout=0.1)
>>>slave=sentinel.slave_for('mymaster',socket_timeout=0.1)
>>>master.set('foo','bar')
>>>slave.get('foo')
'bar'
如果Redis有密码,在这里需要改成这样:
master = sentinel.master_for('mymaster', socket_timeout=0.1, password='12345')
1
master=sentinel.master_for('mymaster',socket_timeout=0.1,password='12345')

master对象和slave对象是正常的StrictRedis实例,其连接池绑定到Sentinel实例。当Sentinel支持的客户端尝试建立连接时,它首先查询Sentinel服务器以确定要连接的适当主机。如果没有找到服务器,则会引发MasterNotFoundError或SlaveNotFoundError,这两个异常都是ConnectionError的子类。当尝试连接到从站客户端时,Sentinel连接池将遍历从站列表,直到找到可以连接的从站。如果没有从站可以连接,则与主站建立连接。

下面使用Python驱动来测试Redis高可用以及Sentinel高可用,环境为一主一从两个哨兵:

$ ps aux | grep redis | grep -v grep
root 13518 0.0 0.1 136936 8092 ? Sl 00:43 0:08 redis-sentinel *:26379 [sentinel]
root 14065 0.0 0.1 136936 8084 pts/3 Sl 01:59 0:04 redis-sentinel *:26380 [sentinel]
root 14099 0.0 0.1 138984 9760 ? Ssl 02:00 0:03 redis-server 127.0.0.1:6380
root 14280 0.0 0.0 136936 7672 ? Ssl 02:06 0:02 redis-server 127.0.0.1:6379
1
2
3
4
5
$psaux|grepredis|grep-vgrep
root135180.00.11369368092?Sl00:430:08redis-sentinel*:26379[sentinel]
root140650.00.11369368084pts/3Sl01:590:04redis-sentinel*:26380[sentinel]
root140990.00.11389849760?Ssl02:000:03redis-server127.0.0.1:6380
root142800.00.01369367672?Ssl02:060:02redis-server127.0.0.1:6379
测试程序如下:
from redis.sentinel import Sentinel
import time
while True:
sentinel = Sentinel([('127.0.0.1', 26379),('127.0.0.1', 26380)], socket_timeout=0.1)
time.sleep(0.5)
print sentinel.discover_master('mymaster')
print sentinel.discover_slaves('mymaster')
master = sentinel.master_for('mymaster', socket_timeout=0.1)
print master.set('foo', 'bar')
print master.get('foo')
slave = sentinel.slave_for('mymaster', socket_timeout=0.1)
slave.get('foo')
1
2
3
4
5
6
7
8
9
10
11
12
fromredis.sentinelimportSentinel
importtime
whileTrue:
sentinel=Sentinel([('127.0.0.1',26379),('127.0.0.1',26380)],socket_timeout=0.1)
time.sleep(0.5)
printsentinel.discover_master('mymaster')
printsentinel.discover_slaves('mymaster')
master=sentinel.master_for('mymaster',socket_timeout=0.1)
printmaster.set('foo','bar')
printmaster.get('foo')
slave=sentinel.slave_for('mymaster',socket_timeout=0.1)
slave.get('foo')

测试结果:

一、发生主从切换时,业务大概会有1-2s的中断后恢复正常。

二、当哨兵挂掉一个时(两个哨兵),业务无感知,但是是建立在主从不会切换的情况下(此时一个哨兵无法完成故障转移)。