UDN-企业互联网技术人气社区

板块导航

浏览  : 1038
回复  : 1

[讨论交流] Redis Sentinel主从高可用方案(附Jedis Sentinel教程)

[复制链接]
泡泡兔的头像 楼主
发表于 2016-7-24 14:20:05 | 显示全部楼层 |阅读模式
  一、Sentinel介绍

  Sentinel是Redis的高可用性(HA)解决方案,由一个或多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进行下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。Redis提供的sentinel(哨兵)机制,通过sentinel模式启动redis后,自动监控master/slave的运行状态,基本原理是:心跳机制+投票裁决

  • 监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。
  • 提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。
  • 自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。

  二、Sentinel的主从原理
2.png


2.png


2.png


2.png

  之前介绍过为什么Jedis要用2.2.2及以上版本,因为主从实例地址(IP PORT)是不同的,当故障发生进行主从切换后,应用程序无法知道新地址,故在Jedis2.2.2中新增了对Sentinel的支持,应用通过redis.clients.jedis.JedisSentinelPool.getResource()取得的Jedis实例会及时更新到新的主实例地址。

  三、Redis Sentinel配置

  这里我采用2个哨兵,1个主redis,2个从redis的方式,配置文件如下:
2.png

  sentinel_63791.conf 配置:
  1. port 63791
  2. daemonize yes
  3. logfile "/var/log/sentinel_63791.log"
  4. #master-1
  5. sentinel monitor master-1 192.168.78.99 6379 2
  6. sentinel down-after-milliseconds master-1 5000
  7. sentinel failover-timeout master-1 18000
  8. sentinel auth-pass master-1 hundsun@1
  9. sentinel parallel-syncs master-1 1
复制代码

  sentinel_63792.conf 配置:
  1. port 63792
  2. daemonize yes
  3. logfile "/var/log/sentinel_63792.log"
  4. #master-1
  5. sentinel monitor master-1 192.168.78.99 6379 2
  6. sentinel down-after-milliseconds master-1 5000
  7. sentinel failover-timeout master-1 18000
  8. sentinel auth-pass master-1 hundsun@1
  9. sentinel parallel-syncs master-1 1
复制代码

  redis_master_6379.conf 配置

  在原配置文件中作如下修改:
  1. port 6379
  2. daemonize yes
  3. requirepass hundsun@1
  4. masterauth hundsun@1
复制代码

  redis_slave_6380.conf 配置:

  在原配置文件中作如下修改:
  1. port 6380
  2. daemonize yes
  3. requirepass hundsun@1
  4. slaveof 192.168.78.99 6379
  5. masterauth hundsun@1
复制代码

  redis_slave_6381.conf 配置:

  在原配置文件中作如下修改:
  1. port 6381
  2. daemonize yes
  3. requirepass hundsun@1
  4. slaveof 192.168.78.99 6379
  5. masterauth hundsun@1
复制代码

  按如下顺序依次启动服务:
  1. ./redis-server ../conf/redis_master_6379.conf
  2. ./redis-server ../conf/redis_slave_6381.conf   
  3. ./redis-server ../conf/redis_slave_6382.conf   
  4. ./redis-sentinel ../conf/sentinel_63791.conf
  5. ./redis-sentinel ../conf/sentinel_63792.conf
复制代码

  查看进程是否都已经启动:
2.png

  查看master的状态:
2.png

  查看slave的状态:
2.png

  查看sentinel的状态:
2.png

  接下来验证redis sentinel的主从切换:

  首先关闭主redis(6379)服务(shutdown)。

  查看哨兵,发现端口号为6380的从服务变成了主服务,sentinel自动完成了故障切换。
2.png

  启动刚才被shutdown的6379服务并查看,发现它变成了从服务。
2.png

  四、Jedis Sentinel教程

  Maven依赖:
  1.     <dependency>
  2.         <groupId>redis.clients</groupId>
  3.         <artifactId>jedis</artifactId>
  4.         <version>2.8.0</version>
  5.     </dependency>
  6.     <!-- spring-redis -->
  7.     <dependency>
  8.         <groupId>org.springframework.data</groupId>
  9.         <artifactId>spring-data-redis</artifactId>
  10.         <version>1.6.4.RELEASE</version>
  11.     </dependency>
复制代码

  redis的配置文件:
  1. #redis config
  2. redis.pass=hundsun@1
  3. redis.pool.maxTotal=105
  4. redis.pool.maxIdle=10
  5. redis.pool.maxWaitMillis=60000
  6. redis.pool.testOnBorrow=true

  7. sentinel1.ip=192.168.78.99
  8. sentinel1.port=63791

  9. sentinel2.ip=192.168.78.99
  10. sentinel2.port=63792
复制代码

  Spring的配置文件:
  1. <!-- Redis 配置 -->
  2. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
  3.     <property name="maxTotal" value="${redis.pool.maxTotal}" />
  4.     <property name="maxIdle" value="${redis.pool.maxIdle}" />
  5.     <property name="maxWaitMillis" value="${redis.pool.maxWaitMillis}" />
  6.     <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" />
  7. </bean>

  8. <bean id="sentinelConfiguration"
  9.     class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  10.     <property name="master">
  11.         <bean class="org.springframework.data.redis.connection.RedisNode">
  12.             <property name="name" value="master-1"></property>
  13.         </bean>
  14.     </property>
  15.     <property name="sentinels">
  16.         <set>
  17.             <bean class="org.springframework.data.redis.connection.RedisNode">
  18.                 <constructor-arg name="host" value="${sentinel1.ip}"></constructor-arg>
  19.                 <constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
  20.             </bean>
  21.             <bean class="org.springframework.data.redis.connection.RedisNode">
  22.                 <constructor-arg name="host" value="${sentinel2.ip}"></constructor-arg>
  23.                 <constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
  24.             </bean>
  25.         </set>
  26.     </property>
  27. </bean>

  28. <!-- Jedis ConnectionFactory连接配置 -->
  29. <bean id="jedisConnectionFactory"
  30.     class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  31.     <property name="password" value="${redis.pass}"></property>
  32.     <property name="poolConfig" >
  33.         <ref bean="jedisPoolConfig"/>
  34.     </property>
  35.     <constructor-arg name="sentinelConfig" ref="sentinelConfiguration"></constructor-arg>
  36. </bean>

  37. <!-- redisTemplate配置,redisTemplate是对Jedis的对redis操作的扩展,有更多的操作,封装使操作更便捷 -->
  38. <bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
  39.     <property name="connectionFactory" ref="jedisConnectionFactory" />
  40. </bean>
复制代码

  代码中直接用redisTemplate调用:
  1.     @Override
  2.     public boolean add(final KeyToken tkey) {
  3.         boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {

  4.             @Override
  5.             public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
  6.                 RedisSerializer<String> serializer = getRedisSerializer();
  7.                 byte[] key = serializer.serialize(tkey.getIndex());
  8.                 byte[] name = serializer.serialize(tkey.getExpire_time());
  9.                 return connection.setNX(key, name);
  10.             }

  11.         });
  12.         return result;
  13.     }
复制代码

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部