Redis通过Sentinel可以使用zk实现主从从切换,客户端怎么实

利用redis-sentinel实现redis主从高可用设计
redis最简单的使用方式就是启动一个redis实例,然后客户端直接连到这个redis实例进行操作,很多业务场景单个redis实例便可以满足内存及请求的需求。然而在实际应用当中,尤其是在线上环境中,可用性非常重要,显然单一的redis实例可用性就很难保证,无论是进程挂了还是机器挂了等等各种原因很容易就会导致单一redis实例不可用。为了提供高可用的能力,redis本身提供了redis-sentinel服务。
redis-sentinel工作方式
详细的redis-sentinel内容可以参考官网的文档。在这里我们简单的介绍一下:
redis服务需要主从部署,一个主节点,至少一个从节点
至少部署三个redis-sentinel实例
[crayon-5a1ab9cdb1
]+--------+
+--------+
| Master |---------+---------| Slave
+--------+
+--------+
+------------+--------------+
+-----------+ +-----------+ +-----------+
| Sentinel1 | | Sentinel2 | | Sentinel3 |
+-----------+ +-----------+ +-----------+
[crayon-5a1ab9cdb1&&]+--------+&&&&&&&&&&&&&&&&&& +--------+| Master |---------+---------| Slave&&|+--------+&&&&&&&& |&&&&&&&& +--------+&&&&&&&&&&&&&&&&&& |&&&&&&+------------+--------------+&&&&&&&&&&|&&&&&&&&&&&&|&&&&&&&&&&&&&&|+-----------+ +-----------+ +-----------+| Sentinel1 | | Sentinel2 | | Sentinel3 |+-----------+ +-----------+ +-----------+
每个Sentinel都会和Master、Slave以及其它Sentinel通讯,检测各实例的存活情况,如果发现Master实例挂掉了,则各Sentinel之间会协商根据规则选出一个Slave来变为Master。这样就使得redis服务可以继续使用。
从服务端来讲,redis-sentinel引入解决了单个redis实例挂掉之后不能提供服务的问题。但是对客户端来说由于redis-sentinel的引入,可能导致redis的主从切换,因此直接给客户端redis实例地址的话则可能因为主从的切换而导致redis地址不是预期的地址。为了完全的实现高可用我们还需要解决这个问题,那解决这个问题常见的就有以下三种方案:
客户端方案
客户端方案
在redis客户端中支持redis-sentinel,通过redis-sentinel来发现redis实例。
简化服务端部署,只需部署redis实例和redis-sentinel即可
可以在客户端做配置,实现读写分离功能
需要客户端支持redis-sentinel,很多流行的当前在用的客户端都不支持
对于已经使用的现有代码,需要修改业务使用客户端部分的代码
一些第三方应用使用redis,而我们可能无法修改这种应用的代码
采用LVS虚IP,在redis-sentinel发生主从切换后也执行虚IP漂移操作。
对客户端完全透明,现有业务代码无需做任何更改
服务端部署稍微复杂,LVS部署需要root权限
LVS自身高可用需要有备机,在隔离部署时备机不能被利用,存在资源浪费
单一虚IP,只能规定为redis主节点或redis从节点,想要支持读写分离的话得部署多个虚ip,同时需要客户端支持
在客户端和redis服务端之间加一层代理,代理通过redis-sentinel来发现redis实例
对客户端完全透明,现有业务代码无需做任何更改
可以在代理层做配置,实现读写分离功能
redis服务有性能损失
代理自身存在单点问题
在实际应用环境中,要选择一种高可用方案除了考虑高可用本身更应考虑以下几点:
维护简单,这包括服务的部署、升级、迁移等
从现有使用方式迁移到高可用使用方式改造小
因此综合的来看,选择代理方案是一种比较折衷稳妥的方案,原因主要如下:
客户端方案,在已有大量服务在运行的情况下,需要全部重新改造一遍,这个难度很大,而且对第三方应用来说可能是无法更改的
LVS方案,部署麻烦问题比较突出
代理方案对后端服务来说,维护起来简单
那么选用代理方案的话,也需要正视它的缺点:
redis服务性能损失:考察一个服务的性能主要就是两个指标,吞吐和延迟。对于一款高性能的代理来说,吞吐几乎可以做到和redis差不多,即便是有差距的话,获益于代理的无状态特性,可以直接横向扩容来弥补;而对于延迟,从客户端来讲,由于多个中间环节,几乎延迟总会增加,但是从整个业务层面来讲,因为代理的引入而增加的redis请求耗时几乎都可以忽略不计。
代理自身的单点问题:要解决这个问题有两个手段,一是再在代理前面架一层LVS,二是提供多个代理,在客户端实现可以访问多个后端。在这两种选择中,推荐第二种,因为代理本身无状态,可以很容易的部署多个代理,通过修改业务客户端代码,实现对代理的负载均衡和失效及恢复。
支持redis-sentinel的代理
不错,直接给出了推荐的解决方案
关于伯乐小组
这里有好的话题,有启发的回复和值得信任的圈子。
新浪微博:
推荐微信号
(加好友请注明来意)
- 好的话题、有启发的回复、值得信赖的圈子
- 分享和发现有价值的内容与观点
- 为IT单身男女服务的征婚传播平台
- 优秀的工具资源导航
- 翻译传播优秀的外文文章
- 国内外的精选博客文章
- UI,网页,交互和用户体验
- 专注iOS技术分享
- 专注Android技术分享
- JavaScript, HTML5, CSS
- 专注Java技术分享
- 专注Python技术分享
& 2017 伯乐在线实现功能描述:
&&&&&&& redis服务器进行Master-slaver-slaver-....主从配置,通过2台sentinel进行failOver故障转移,自动切换,采用该代码完全可以直接用于实际生产环境。
&&&&&& 题外话:
&&&&&&&& 一般来说这样的部署足以支持数以百万级的用户,但如果数量实在是太高,此时redis的Master-Slaver主从不一定能够满足,因此进行redis的分片。
&&&&&&& 本文不讲解redis的分片,但如果你使用了,需要注意的按照另一篇文章的介绍:Sentinel&Jedis看上去是个完美的解决方案,这句话只说对了一半,
&&&&&&&& 在无分片的情况是这样,但我们的应用使用了数据分片-sharing,数据被平均分布到4个不同的实例上,每个实例以主从结构部署,Jedis没有提供
&&&&&&&& 基于Sentinel的ShardedJedisPool,也就是说在4个分片中,如果其中一个分片发生主从切换,应用所使用的ShardedJedisPool无法获得通知,所有
&&&&&&&& 对那个分片的操作将会失败。文章中提出了解决方案,请参考《》
&&&&&&& 该代码模拟多线程向redis中set/get。
1、maven依赖配置
&dependency&
&groupId&org.springframework.data&/groupId&
&artifactId&spring-data-redis&/artifactId&
&version&1.4.1.RELEASE&/version&
&/dependency&
&dependency&
&groupId&redis.clients&/groupId&
&artifactId&jedis&/artifactId&
&version&2.6.2&/version&
&/dependency&
&dependency&
&groupId&mons&/groupId&
&artifactId&commons-lang3&/artifactId&
&version&3.3.2&/version&
&/dependency&
2、redis.properties
# Redis settings
#sentinel1的IP和端口
im.hs.server.redis.sentinel1.host=192.168.62.154
im.hs.server.redis.sentinel1.port=26379
#sentinel2的IP和端口
im.hs.server.redis.sentinel2.host=192.168.62.153
im.hs.server.redis.sentinel2.port=26379
#sentinel的鉴权密码
im.hs.server.redis.sentinel.masterName=155Master
im.hs.server.redis.sentinel.password=hezhixiong
#最大闲置连接数
im.hs.server.redis.maxIdle=500
#最大连接数,超过此连接时操作redis会报错
im.hs.server.redis.maxTotal=5000
im.hs.server.redis.maxWaitTime=1000
im.hs.server.redis.testOnBorrow=true
#最小闲置连接数,spring启动的时候自动建立该数目的连接供应用程序使用,不够的时候会申请。
im.hs.server.redis.minIdle=300
3、spring-redis.xml
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance& xmlns:p=&http://www.springframework.org/schema/p&
xmlns:context=&http://www.springframework.org/schema/context&
xmlns:jee=&http://www.springframework.org/schema/jee& xmlns:tx=&http://www.springframework.org/schema/tx&
xmlns:aop=&http://www.springframework.org/schema/aop&
xsi:schemaLocation=&
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd&&
&!-- Spring自动将该包目录下标记为@Service的所有类作为spring的Bean --&
&context:component-scan base-package=&com.gaojiasoft.test.redis& /&
&context:property-placeholder location=&classpath:conf/redis/redis.properties& /&
&bean id=&poolConfig& class=&redis.clients.jedis.JedisPoolConfig&&
&property name=&maxTotal& value=&${im.hs.server.redis.maxTotal}& /&
&property name=&minIdle& value=&${im.hs.server.redis.minIdle}& /&
&property name=&maxWaitMillis& value=&${im.hs.server.redis.maxWaitTime}& /&
&property name=&maxIdle& value=&${im.hs.server.redis.maxIdle}& /&
&property name=&testOnBorrow& value=&${im.hs.server.redis.testOnBorrow}& /&
&property name=&testOnReturn& value=&true& /&
&property name=&testWhileIdle& value=&true& /&
&bean id=&sentinelConfiguration&
class=&org.springframework.data.redis.connection.RedisSentinelConfiguration&&
&property name=&master&&
&bean class=&org.springframework.data.redis.connection.RedisNode&&
&property name=&name& value=&${im.hs.server.redis.sentinel.masterName}&&&/property&
&/property&
&property name=&sentinels&&
&bean class=&org.springframework.data.redis.connection.RedisNode&&
&constructor-arg name=&host&
value=&${im.hs.server.redis.sentinel1.host}&&&/constructor-arg&
&constructor-arg name=&port&
value=&${im.hs.server.redis.sentinel1.port}&&&/constructor-arg&
&bean class=&org.springframework.data.redis.connection.RedisNode&&
&constructor-arg name=&host&
value=&${im.hs.server.redis.sentinel2.host}&&&/constructor-arg&
&constructor-arg name=&port&
value=&${im.hs.server.redis.sentinel2.port}&&&/constructor-arg&
&/property&
&bean id=&connectionFactory&
class=&org.springframework.data.redis.connection.jedis.JedisConnectionFactory& p:password=&${im.hs.server.redis.sentinel.password}&&
&constructor-arg name=&sentinelConfig& ref=&sentinelConfiguration&&&/constructor-arg&
&constructor-arg name=&poolConfig& ref=&poolConfig&&&/constructor-arg&
&bean id=&redisTemplate& class=&org.springframework.data.redis.core.StringRedisTemplate&&
&property name=&connectionFactory& ref=&connectionFactory& /&
&/beans&&strong&
4、RedisServiceImpl.java
package com.gaojiasoft.test.
import java.util.concurrent.LinkedBlockingQ
import java.util.concurrent.ThreadPoolE
import java.util.concurrent.TimeU
import mons.lang3.concurrent.BasicThreadF
import org.slf4j.L
import org.slf4j.LoggerF
import org.springframework.beans.factory.annotation.A
import org.springframework.dao.DataAccessE
import org.springframework.data.redis.connection.RedisC
import org.springframework.data.redis.core.RedisC
import org.springframework.data.redis.core.RedisT
import org.springframework.stereotype.S
@Service(&redisService&)
public class RedisServiceImpl {
private Logger logger = LoggerFactory.getLogger(&RedisServiceImpl&);
@Autowired
RedisTemplate&?, ?& redisT
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
256, 256, 30L, TimeUnit.SECONDS,
new LinkedBlockingQueue&Runnable&(),
new BasicThreadFactory.Builder().daemon(true)
.namingPattern(&redis-oper-%d&).build(),
new ThreadPoolExecutor.CallerRunsPolicy());
public void set(final String key, final String value) {
redisTemplate.execute(new RedisCallback&Object&() {
public Object doInRedis(RedisConnection connection)
throws DataAccessException {
connection.set(
redisTemplate.getStringSerializer().serialize(key),
redisTemplate.getStringSerializer().serialize(value));
logger.debug(&save key:& + key + &,value:& + value);
public String get(final String key) {
return redisTemplate.execute(new RedisCallback&String&() {
public String doInRedis(RedisConnection connection)
throws DataAccessException {
byte[] byteKye = redisTemplate.getStringSerializer().serialize(
if (connection.exists(byteKye)) {
byte[] byteValue = connection.get(byteKye);
String value = redisTemplate.getStringSerializer()
.deserialize(byteValue);
logger.debug(&get key:& + key + &,value:& + value);
logger.error(&valus does not exist!,key:&+key);
public void delete(final String key) {
redisTemplate.execute(new RedisCallback&Object&() {
public Object doInRedis(RedisConnection connection) {
connection.del(redisTemplate.getStringSerializer().serialize(
* 线程池并发操作redis
* @param keyvalue
public void mulitThreadSaveAndFind(final String keyvalue) {
executor.execute(new Runnable() {
public void run() {
set(keyvalue, keyvalue);
get(keyvalue);
} catch (Throwable th) {
// 防御性容错,避免高并发下的一些问题
logger.error(&&, th);
5、RedisTest.java&& (Junit测试用例)
package com.gaojiasoft.test.
import org.junit.T
import org.springframework.context.ConfigurableApplicationC
import org.springframework.context.support.ClassPathXmlApplicationC
public class RedisTest {
private static ConfigurableApplicationC
RedisServiceI
public void testSave() throws InterruptedException {
context = new ClassPathXmlApplicationContext(
&classpath:conf/redis/spring-redis.xml&);
service = (RedisServiceImpl) context.getBean(&redisService&);
int i = 1;
while (true) {
Thread.sleep(1);
service.mulitThreadSaveAndFind(&& + i);
} catch (Exception e) {
e.printStackTrace();
本文已收录于以下专栏:
相关文章推荐
Spring-Data-Redis项目(简称SDR)是对Redis的Key-Value数据存储操作提供了更高层次的抽象,提供了一个对几种主要的redis的Java客户端(例如:jedis,jre...
研究Redis也有一段时间了,在前面的Redis系列文章中,介绍了Redis的安装,集群配置,及节点的增加和删除,但是并未实际的使用到项目中,趁这周末时间,参照项目中实际的使用场景,做了一个Redis...
1.default.properties配置文件:
# Redis settings
#sentinel_node_1
redis.sentinel1.host=192.168.20.105
Jedis是Java最常用的用于连接Redis的客户端。也是Redis官方推荐的Redis连接客户端之一。一般在项目中是使用Spring来管理jedis的连接池的,利用Spring ioc容器来管理。...
一、一主二从三sentinel配置
1、master:127.0.0.1:6379
2、slave1:127.0.0.1:6380
3、slave2:127.0.0.1:6381
4、sentinel...
redis一主多从的spring配置
在上一篇博客中,讨论了如何在Spring Boot中使用Redis实现缓存。
但是数据库显然单点是不够的,那么如何用redis数据库做缓存集群呢?我们今天就来研究一下。
Redis 支持 ...
Redis主从复制和集群配置
redis主从复制
1、redis的复制功能是支持多个数据库之间的数据同步。一类是主数据库(master)一类是从数据库(slave)...
阅读本文您会学会:
1.redis 主从备份并自动切换(master slaver)模式的搭建
2.java 中 配合redis主从备份的方法
【实现目标】:
master redis 正常运...
1.redis配置文件
配置文件示例:
127.0.0.1:.0.1:.0.1:.0.1:.0.1:6383这里,对每一...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Redis单点时,当一台机器挂机了,redis的服务完全停止,这时就会影响其他服务的正常运行。下面利用redis sentinel做一个主从切换的集群管理。
下面两段官方的说辞:
Redis Sentinel provides high availability
for Redis. In practical terms this means that using Sentinel you can create a Redis deployment that resists without human intervention to certain kind of failures.
Redis Sentinel also provides other collateral tasks such as monitoring, notifications and acts as a configuration provider for clients.
环境配置:
由于我这次配置没有太多的机器,参考前面的主从搭建,测试环境就两台Linux机器,所以用了一台Windows机器做了一个slave。
Windows只支持64位,所以要找64位的机器,参考此博客http://blog.csdn.net/renfufei/article/details/,安装包从此链接上下载/MSOpenTech/redis/releases,有zip免安装的,也有msi的,根据自己需要下载,我下载的zip,解压默认配置即可。
windows这台是slave,启动命令:
将当前机器加入主从命令: ./redis-cli -p 6379 slaveof 192.168.0.149 6379
集群配置最少需要三台机器,那么我就两台Linux机器,一台Windows机器分别安装同样的redis的环境
192.168.0.148 &(redis sentinel 集群监控)
192.168.0.149 &(redis 主)
192.168.9.68 &(windows redis 从)
启动主和从,然后在主查看Replication信息
[root@JfbIpadServer02 /usr/local/redis/bin]#./redis-cli
-h 192.168.0.149 info Replication
# Replication
role:master & &#代表当前主机为master
connected_slaves:1 & #有一台slave
slave0:ip=192.168.0.68,port=6379,state=online,offset=50959,lag=1 &#slave的IP和端口
master_repl_offset:50959
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:50958
相看从机器的Replication信息
D:\Programs\Redis-x64-3.0.501&redis-cli
-h 192.168.0.68 info Replication
# Replication
role:slave
master_host:192.168.0.149
master_port:6379
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:61166
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:18167
配置redis sentinel集群监控服务&
1.添加一份redis sentinel 配置文件,在192.168.0.148上,配置此文件。
jfb-test:/usr/local/src/redis-3.0.7 #
cp sentinel.conf /usr/local/redis/bin/
jfb-test:/usr/local/src/redis-3.0.7 # cd /usr/local/redis/bin
jfb-test:/usr/local/redis/bin # vi sentinel.conf&
Redis source distribution contains a file called&sentinel.conf&that
is a self-documented example configuration file you can use to configure Sentinel, however a typical minimal configuration file looks like the following:
sentinel monitor mymaster 192.168.0.149 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
2,启动redis sentinel
有配置文件了,那么启动redis
sentinel做redis集群监听。
sentinel 集群监听启动,观察redis sentinel 日志信息
jfb-test:/usr/local/redis/bin
# ./redis-sentinel sentinel.conf --sentinel
这里很清楚地看到,从的redis加入了集群,上面加了红框的那行。
执行以下命令,查看redis主从信息。
jfb-test:/usr/local/redis/bin # ./redis-cli
-h 192.168.0.148 -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=192.168.0.149:6379,slaves=1,sentinels=1
那么表示一切都正常了。你的redis sentinel集群已经配置成功!
3,故障演示
执行以下命令使用主的redis(149)服务停止 stopRedis。查看sentinel机器的日志,发现master发生了转移。
这张图片很清晰地反应到,redis sentinel
监控到主的redis(149)服务停止,然后自动把从的redis(68)切换到主。
再执行以下命令,查看redis主从信息。
jfb-test:/usr/local/redis/bin # ./redis-cli -h 192.168.0.148 -p 26379 info Sentinel
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
master0:name=mymaster,status=ok,address=<span style="color:#cc8.0.68:6379,slaves=1,sentinels=1
4,恢复启动原主Redis
当我们已经发现,一台redis发生故障了,可能会收到一些故障信息,那么再把服务已关闭的redis恢复服务状态,会发生怎么样的情况呢?
&&#43;slave slave 192.168.0.149:.0.149 6379 @ mymaster 192.168.0.68 6379
redis sentinel 集群服务,会把上次主redis重新加入服务中,但是他再以不是主的redis了,变成从的reids。
5,恢复原主,这是手动操作,现实中的自动切换不用操作这步,在现在的主Redis
68上的的执行下面的命令,将149变成主。
D:\Programs\Redis-x64-3.0.501&redis-cli
-p 6379 slaveof 192.168.0.149 6379
参考资料:
http://redis.io/topics/sentinel
本文已收录于以下专栏:
相关文章推荐
集群的操作命令
CLUSTER INFO 打印集群的信息
CLUSTER NODES 列出集群当前已知的所有节点(node),以及这些节点的相关信息。
CLUSTER M...
redis的主从(master-slave)就是为了数据冗余备份、保证数据的安全、提高性能,在这里主要讲解一下其主从切换的两种方式,有不对之处,还请各位指教。
首先搭建一个简单的master-s...
环境描述:
主redis:192.168.10.1 6379
从redis:192.168.10.2 6380
一、主从配置
1、将主从redis配置文件redis.conf中的aemon...
Redis Sentinel
Sentinel(哨兵)是用于监控redis集群中Master状态的工具,其已经被集成在redis2.4+的版本中
一、Sentinel作用:1):Master状态...
7.Redis主从切换--Sentinel
Sentinel(哨兵)是用于监控redis集群中Master状态的工具,其已经被集成在redis2.4+的版本中
一、Sentinel作用
之前的结构是这样的:
主:192.168.10.13:30001
从: 192.168.10.14:30004
主:192.168.10.14:30003
从: 192.168.10.15:300...
8.1 主从同步原理像MySQL一样,Redis是支持主从同步的,而且也支持一主多从以及多级从结构。主从结构,一是为了纯粹的冗余备份,二是为了提升读性能,比如很消耗性能的SORT就可以由从服务器来承担...
1. 安装Redis3.0
yum -y install cpp binutils glibc glibc-kernheaders glibc-common glibc-devel gcc make...
三大数据库
更专业、更强悍、适合不同用户群体获取【下载地址】
【新录针对本系统的视频教程,手把手教开发一个模块,快速掌握本系统】
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)java客户端Jedis在2.2.2及以上版本实现了对Sentinel的支持,只要是通过命令:
redis-cli -h 192.168.110.71 -p 6000 &sentinel get-master-addr-by-name shard_a
1) &192.168.110.71&
查询分片shard_a的主服务器地址,实现代码如下:
private HostAndPort initSentinels(Set&String& sentinels, final String masterName) {
HostAndPort master =
boolean sentinelAvailable =
(&Trying to find master from available Sentinels...&);
for (String sentinel : sentinels) {
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(&:&)));
log.fine(&Connecting to Sentinel & + hap);
Jedis jedis =
jedis = new Jedis(hap.getHost(), hap.getPort());
List&String& masterAddr = jedis.sentinelGetMasterAddrByName(masterName);
// connected to sentinel...
sentinelAvailable =
if (masterAddr == null || masterAddr.size() != 2) {
log.warning(&Can not get master addr, master name: & + masterName + &. Sentinel: & + hap
master = toHostAndPort(masterAddr);
log.fine(&Found Redis master at & + master);
} catch (JedisConnectionException e) {
log.warning(&Cannot connect to sentinel running @ & + hap + &. Trying next one.&);
} finally {
if (jedis != null) {
jedis.close();
if (master == null) {
if (sentinelAvailable) {
// can connect to sentinel, but master name seems to not
// monitored
throw new JedisException(&Can connect to sentinel, but & + masterName
+ & seems to be not monitored...&);
throw new JedisConnectionException(&All sentinels down, cannot determine where is &
+ masterName + & master is running...&);
(&Redis master running at & + master + &, starting Sentinel listeners...&);
for (String sentinel : sentinels) {
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(&:&)));
MasterListener masterListener = new MasterListener(masterName, hap.getHost(), hap.getPort());
masterListeners.add(masterListener);
masterListener.start();
关于在java应用中如何使用,请参考如下代码实例:
package cn.slimsmart.redis.demo.
import java.util.HashS
import java.util.S
import redis.clients.jedis.J
import redis.clients.jedis.JedisSentinelP
public class JedisSentinelTest {
public static void main(String[] args) {
Set&String& sentinels = new HashSet&String&();
sentinels.add(&192.168.100.90:6000&);
sentinels.add(&192.168.110.71:6000&);
* masterName 分片的名称
* sentinels Redis Sentinel 服务地址列表
JedisSentinelPool poolA = new JedisSentinelPool(&shard_a&, sentinels);
JedisSentinelPool poolB = new JedisSentinelPool(&shard_b&, sentinels);
//获取Jedis主服务器客户端实例
Jedis jedisA = poolA.getResource();
jedisA.set(&key&, &abc&);
Jedis jedisB = poolB.getResource();
jedisB.set(&key&, &xyz&);
System.out.println(&jedisA key:&+jedisA.get(&key&));
System.out.println(&jedisB key:&+jedisB.get(&key&));
//输出结果
//jedisA key:abc
//jedisB key:xyz
由上面代码可以看出jedis的JedisSentinelPool需要指定分片名称即主服务名称(masterName),这样根据需求硬编码将缓存数据添加到对应的分片中,无法自动分片。若使用ShardedJedisPool,当分片发生主从切换无法获得通知,所有对那个分片的操作将会失败。下面我们介绍一个开源项目sharded-jedis-sentinel-pool,能及时感知所有分片主从切换行为,进行连接池重建。
项目源码:
一、ShardedJedisSentinelPool实现分析
1.构造函数
public ShardedJedisSentinelPool(final GenericObjectPoolConfig poolConfig, List&String& masters, Set&String& sentinels)&
类&#20284;之前的Jedis Pool的构造方法,需要参数poolConfig提供诸如maxIdle,maxTotal之类的配置,masters是一个List,用来保存所有分片Master在Sentinel中配置的名字(注意master的顺序不能改变,因为Shard算法是依据分片位置进行计算,如果顺序错误将导致数据存储混乱),sentinels是一个Set,其中存放所有Sentinel的地址(&#26684;式:IP:PORT,如127.0.0.1:26379),顺序无关;
2.初始化连接池
在构造函数中,通过方法
private List&HostAndPort& initSentinels(Set&String& sentinels, final List&String& masters)&
取得当前所有分片的master地址(IP&PORT),对每个分片,通过顺次连接Sentinel实例,获取该分片的master地址,如果无法获得,即所有Sentinel都无法连接,将休眠1秒后继续重试,直到取得所有分片的master地址,代码块如下:&
for (String masterName : masters) {
HostAndPort master =
boolean fetched =
while (!fetched && sentinelRetry & MAX_RETRY_SENTINEL) {
for (String sentinel : sentinels) {
final HostAndPort hap = toHostAndPort(Arrays.asList(sentinel.split(&:&)));
log.fine(&Connecting to Sentinel & + hap);
Jedis jedis = new Jedis(hap.getHost(), hap.getPort());
master = masterMap.get(masterName);
if (master == null) {
List&String& hostAndPort = jedis.sentinelGetMasterAddrByName(masterName);
if (hostAndPort != null && hostAndPort.size() & 0) {
master = toHostAndPort(hostAndPort);
log.fine(&Found Redis master at & + master);
shardMasters.add(master);
masterMap.put(masterName, master);
jedis.disconnect();
} catch (JedisConnectionException e) {
log.warning(&Cannot connect to sentinel running @ & + hap + &. Trying next one.&);
if (null == master) {
log.severe(&All sentinels down, cannot determine where is & + masterName + & master is running... sleeping 1000ms, Will try again.&);
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
sentinelRetry++;
// Try MAX_RETRY_SENTINEL times.
if (!fetched && sentinelRetry &= MAX_RETRY_SENTINEL) {
log.severe(&All sentinels down and try & + MAX_RETRY_SENTINEL + & times, Abort.&);
throw new JedisConnectionException(&Cannot connect all sentinels, Abort.&);
}通过private void initPool(List&HostAndPort& masters)初始化连接池,到此连接池中的所有连接都指向分片的master。
3.监控每个Sentinel
public ShardedJedisSentinelPool(final GenericObjectPoolConfig poolConfig, List&String& masters, Set&String& sentinels)
最后,会为每个Sentinel启动一个Thread来监控Sentinel做出的更改:&
protected class MasterListener extends Thread
该线程的run方法通过Jedis Pub/Sub API(实现JedisPubSub接口,并通过jedis.subscribe进行订阅)向Sentinel实例订阅“&#43;switch-master”频道,当Sentinel进行主从切换时,该线程会得到新Master地址的通知,通过master name判断哪个分片进行了切换,将新master地址替换原来位置的地址,并调用initPool(List masters)进行Jedis连接池重建;后续所有通过该连接池取得的连接都指向新Master地址,对应用程序透明。
二、应用实例
package cn.slimsmart.redis.demo.
import java.util.ArrayL
import java.util.HashS
import java.util.L
import java.util.S
import mons.pool2.impl.GenericObjectPoolC
import redis.clients.jedis.ShardedJ
public class ShardedJedisSentinelTest {
public static void main(String[] args) {
//连接池配置
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
//分片配置
List&String& masters = new ArrayList&String&();
masters.add(&shard_a&);
masters.add(&shard_b&);
//sentinel服务节点
Set&String& sentinels = new HashSet&String&();
sentinels.add(&192.168.100.90:6000&);
sentinels.add(&192.168.110.71:6000&);
//创建分片连接池
ShardedJedisSentinelPool pool = new ShardedJedisSentinelPool(poolConfig, masters, sentinels);
ShardedJedis jedis =
jedis = pool.getResource();
jedis.set(&key_sharded&, &abc&);
System.out.println(jedis.get(&key_sharded&));
} finally {
if (jedis != null){
pool.returnResource(jedis);
pool.destroy();
}运行后,通过redis客户端查看:
redis 192.168.110.71:6379& get key_sharded
redis 192.168.110.71:6380& get key_sharded
可以看出key_sharded被添加到shard_a分片中。
集成spring配置参考:
&?xml version=&1.0& encoding=&UTF-8&?&
&beans xmlns=&http://www.springframework.org/schema/beans&
xmlns:xsi=&http://www.w3.org/2001/XMLSchema-instance& xmlns:p=&http://www.springframework.org/schema/p&
xmlns:tx=&http://www.springframework.org/schema/tx& xmlns:aop=&http://www.springframework.org/schema/aop&
xmlns:context=&http://www.springframework.org/schema/context&
xsi:schemaLocation=&http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd&
default-lazy-init=&false& default-autowire=&byName&&
&bean id=&shardedJedisPool& class=&cn.slimsmart.redis.demo.sentinel.ShardedJedisSentinelPool&&
&constructor-arg index=&0& ref=&jedisPoolConfig& /&
&constructor-arg index=&1&&
&value&shard_a&/value&
&value&shard_b&/value&
&/constructor-arg&
&constructor-arg index=&2&&
&value&192.168.100.90:6000&/value&
&value&192.168.110.71:6000&/value&
&/constructor-arg&
&bean id=&jedisPoolConfig& class=&redis.clients.jedis.JedisPoolConfig&&
&property name=&maxTotal& value=&200& /&
&property name=&maxIdle& value=&100& /&
&property name=&maxWaitMillis& value=&5000& /&
&property name=&testOnBorrow& value=&true& /&
参考文章:
本文已收录于以下专栏:
相关文章推荐
linux防火墙没有关闭,关闭问题解决
关闭防火墙:service iptables stop
All sentinels down, cannot determine where is mymaster master is running
解决方式:
禁用虚拟机===...
spring-data-redis和jedis集成代码总体结构
project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xs...
在上篇文章中我们已经实现了Redis基于Sentinel的主从切换了,那么我们怎么在java程序中来使用呢,下面我就来简单的介绍一下。首先我们需要引入java中操作redis的jar包,我项目是使用m...
某web项目中需要快速存取部分非结构化数据,对数据的安全性要求不高,同时由于web项目有多台服务器同时提供服务,并通过nginx负载均衡,需要保证客户端从任意一台服务器中均能读取到完整的数据。因...
write by Yin Mingjun,引用请注明。
前面介绍了如何使用sentinel创建一个高可用的主备,但是如果没有可靠的客户端支持,sentinel的使用会很繁琐,好在有je...
本文主要介绍一种通过Jedis&Sentinel实现Redis集群高可用方案,该方案需要使用Jedis2.2.2及以上版本(强制),Redis2.8及以上版本(可选,Sentinel最早出现在Redi...
自Redis增加Sentinel集群工具以来,本博主就从未尝试过使用该工具。最近在调研目前主流的Redis集群部署方案,所以详细地看了一遍官方对于Sentinel的介绍并在自己的台式机上完成了三Red...
Redis 命令参考 >>
本文档翻译自: http://redis.io/topics/sentinel 。
Redis 的 Sentine...
研究Redis也有一段时间了,在前面的Redis系列文章中,介绍了Redis的安装,集群配置,及节点的增加和删除,但是并未实际的使用到项目中,趁这周末时间,参照项目中实际的使用场景,做了一个Redis...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 mongodb主从复制实现 的文章

 

随机推荐