哨兵客户端连接1、Redis Sentinel的客户端2、Redis Sentinel客户端基本实现原理3、Java操作Redis Sentinel1、Redis Sentinel的客户端Sentinel节点集合具备了监控、通知、自动故障转移、配置提供者若干功能也就是说实际上最了解主节点信息的就是Sentinel节点集合而各个主节点可以通过master-name进行标识的所以无论是哪种编程语言的客户端如果需要正确地连接Redis Sentinel必须有Sentinel节点集合和masterName两个参数。2、Redis Sentinel客户端基本实现原理实现一个Redis Sentinel客户端的基本步骤如下1)遍历Sentinel节点集合获取一个可用的Sentinel节点后面会介绍Sentinel节点之间可以共享数据所以从任意一个Sentinel节点获取主节点信息都是可以的如图所示。2)通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息如图所示。3)验证当前获取的“主节点”是真正的主节点这样做的目的是为了防止故障转移期间主节点的变化如图所示。4)保持和Sentinel节点集合的“联系”​时刻获取关于主节点的相关“信息”​如图所示。从上面的模型可以看出Redis Sentinel客户端只有在初始化和切换主节点时需要和Sentinel节点集合进行交互来获取主节点信息所以在设计客户端时需要将Sentinel节点集合考虑成配置相关节点信息和变化发现服务。上述过程只是从客户端设计的角度进行分析在开发客户端时要考虑的细节还有很多但是这些问题并不需要深究下面将介绍如何使用Java的Redis客户端操作Redis Sentinel。3、Java操作Redis Sentinel我们依然使用Jedis2.8.2以下简称Jedis作为Redis的Java客户端Jedis能够很好地支持Redis Sentinel并且使用Jedis连接Redis Sentinel也很简单按照RedisSentinel的原理需要有masterName和Sentinel节点集合两个参数。Jedis的连接池JedisPool为了不与之相混淆Jedis针对RedisSentinel给出了一个JedisSentinelPool很显然这个连接池保存的连接还是针对主节点的。Jedis给出很多构造方法其中最全的如下所示publicJedisSentinelPool(StringmasterName,SetStringsentinels,finalGenericObjectPoolConfigpoolConfig,finalintconnectionTimeout,finalintsoTimeout,finalStringpassword,finalintdatabase,finalStringclientName)具体参数含义如下masterName——主节点名。sentinels——Sentinel节点集合。poolConfig——common-pool连接池配置。connectTimeout——连接超时。soTimeout——读写超时。password——主节点密码。database——当前数据库索引。clientName——客户端名。例如要想通过简单的几个参数获取JedisSentinelPool可以直接按照下面方式进行JedisSentinelPool的初始化。JedisSentinelPooljedisSentinelPoolnewJedisSentinelPool(masterName,sentinelSet,poolConfig,timeout);此时timeout既代表连接超时又代表读写超时password为空database默认使用0clientName为空。具体可以参考JedisSentinelPool源码。和JedisPool非常类似我们在使用JedisSentinelPool时也要尽可能按照common-pool的标准模式进行代码的书写。Jedisjedisnull;try{jedisjedisSentinelPool.getResource();// jedis command}catch(Exceptione){logger.error(e.getMessage(),e);}finally{if(jedis!null)jedis.close();}jedis.close()是和第4章介绍的一样并不是关闭Jedis连接。JedisSentinelPool和JedisPool一样尽可能全局只有一个。Jedis源码中的JedisSentinelPool介绍一下JedisSentinelPool的实现过程下面给出的代码就是JedisSentinelPool的初始化方法。publicJedisSentinelPool(StringmasterName,SetStringsentinels,finalGenericObjectPoolConfigpoolConfig,finalintconnectionTimeout,finalintsoTimeout,finalStringpassword,finalintdatabase,finalStringclientName){…HostAndPortmasterinitSentinels(sentinels,masterName);initPool(master);…}下面的代码就是JedisSentinelPool初始化代码的重要函数initSentinels(Setsentinelsfinal StringmasterName)包含了Sentinel 节点集合和masterName参数用来获取指定主节点的ip地址和端口。privateHostAndPortinitSentinels(SetStringsentinels,finalStringmasterName){// 主节点HostAndPortmasternull;// 遍历所有sentinel节点for(Stringsentinel:sentinels){// 连接sentinel节点HostAndPorthaptoHostAndPort(Arrays.asList(sentinel.split(:)));JedisjedisnewJedis(hap.getHost(),hap.getPort());// 使用sentinel get-master-addr-by-name masterName获取主节点信息ListStringmasterAddrjedis.sentinelGetMasterAddrByName(masterName);// 命令返回列表为空或者长度不为2继续从下一个sentinel节点查询if(masterAddrnull||masterAddr.size()!2){continue;}// 解析masterAddr获取主节点信息mastertoHostAndPort(masterAddr);// 找到后直接跳出for循环break;}if(masternull){// 直接抛出异常thrownewException();}// 为每个sentinel节点开启主节点switch的监控线程for(Stringsentinel:sentinels){finalHostAndPorthaptoHostAndPort(Arrays.asList(sentinel.split(:)));MasterListenermasterListenernewMasterListener(masterName,hap.getHost(),hap.getPort());masterListener.start();}// 返回结果returnmaster;}具体过程如下1)遍历Sentinel节点集合找到一个可用的Sentinel节点如果找不到就从Sentinel节点集合中去找下一个如果都找不到直接抛出异常给客户端newJedisException(Can connect to sentinel, but masterName seems to be not monitored…)2)找到一个可用的Sentinel节点执行sentinelGetMasterAddrByName(masterName)找到对应主节点信息ListStringmasterAddrjedis.sentinelGetMasterAddrByName(masterName);3)JedisSentinelPool中没有发现对主节点角色验证的代码这是因为get-master-addr-by-name master-name这个API本身就会自动获取真正的主节点例如故障转移期间​。4)为每一个Sentinel节点单独启动一个线程利用Redis的发布订阅功能每个线程订阅Sentinel节点上切换master的相关频道switch-master。for(Stringsentinel:sentinels){finalHostAndPorthaptoHostAndPort(Arrays.asList(sentinel.split(:)));MasterListenermasterListenernewMasterListener(masterName,hap.getHost(),hap.getPort());masterListener.start();}下面代码就是MasterListener的核心监听代码代码中比较重要的部分就是订阅Sentinel节点的switch-master频道它就是Redis Sentinel在结束对主节点故障转移后会发布切换主节点的消息Sentinel节点基本将故障转移的各个阶段发生的行为都通过这种发布订阅的形式对外提供开发者只需订阅感兴趣的频道即可​这里我们比较关心的是switch-master这个频道。JedissentinelJedisnewJedis(sentinelHost,sentinelPort);// 客户端订阅Sentinel节点上switch-master(切换主节点)频道sentinelJedis.subscribe(newJedisPubSub(){OverridepublicvoidonMessage(Stringchannel,Stringmessage){String[]switchMasterMsgmessage.split( );if(switchMasterMsg.length3){// 判断是否为当前masterNameif(masterName.equals(switchMasterMsg[0])){// 发现当前masterName发生switch使用initPool重新初始化连接池initPool(toHostAndPort(switchMasterMsg[3],switchMasterMsg[4]));}}}},switch-master);