redis分布式

来源:互联网 发布:2016coc双王升级数据 编辑:程序博客网 时间:2024/05/17 07:30

概述

Redis哨兵为Redis提供了高可用性。可以使用哨兵模式创建一个可以不用人为干预而应对各种故障的Redis部署。

哨兵模式还提供了其他的附加功能,如监控,通知,为客户端提供配置。

下面是在宏观层面上哨兵模式的功能列表:

  • 监控:哨兵不断的检查master和slave是否正常的运行。
  • 通知:当监控的某台Redis实例发生问题时,可以通过API通知系统管理员和其他的应用程序。
  • 自动故障转移:如果一个master不正常运行了,哨兵可以启动一个故障转移进程,将一个slave升级成为master,其他的slave被重新配置使用新的master,并且应用程序使用Redis服务端通知的新地址。
  • 配置提供者:哨兵作为Redis客户端发现的权威来源:客户端连接到哨兵请求当前可靠的master的地址。如果发生故障,哨兵将报告新地址。

特性

Redis哨兵是一个分布式系统:

哨兵自身设计成和多个哨兵进程一起合作运行。这样做的好处有:

  • 当多个哨兵对一个master不再可用达成一致时执行故障检测。这会降低错误判断的概率。
  • 只要大多数哨兵正常运行就可以,增强系统健壮性。毕竟在故障系统里单点故障没有什么意义。

哨兵部署和故障转移过程

下面以部署三个哨兵为例(配置quorum = 2):

  • Box:代表服务器
  • Redis:代表Redis实例
  • Sentinel:代表Redis哨兵实例
  • Client:代表连接到Redis的客户端
  • Redis1:为master
  • Redis2和Redis3为Redis1的Slave
  • 其中三个哨兵都监控Master1;

下面是Box1主机发生故障之后的转移过程:

  1. Sentinel2检测到Box1故障,标记状态为SDOWN;
  2. Sentinel3检测到Box1故障,标记状态为SDOWN;
  3. 因标记SDOWN状态数量 >= 配置的quorum ,标记Redis实例状态为ODOWN,触发故障转移;
  4. 通过算法选举出一个Leader(如Sentinel2);
  5. Sentinel2请求其他Sentinel(Sentinel3)的授权(注意这里需要得到半数以上的哨兵授权);
  6. Sentinel2得到授权后通过算法选举出合适的Slave(如Slave2)晋升为Master;
  7. Sentinel更改配置监听新Master,并把其他的Redis配置为新Master的Slave;
  8. 通知客户端连接新Master。

使用

使用之前需要知道的事情

  • 一个健壮的部署至少需要三个哨兵实例
  • 三个哨兵实例最好放在三个不同的物理机上
  • 使用的客户端必须支持哨兵(一般流行的都支持)

服务端配置(运维部同事需仔细看看)

sentinel.conf(注意所有哨兵配置都相同)
# port <sentinel port>
#
# 哨兵实例运行端口
port 26379
# sentinel monitor <master-name> <ip> <redis-port> <quorum>
#
# 配置监控master
#
# <master-name>:配置监控master实例名字
# <ip>:Redis master所在IP
# <redis-port>:Redis master运行端口
# <quorum>: 至少<quorum>个同意,master才能进入o_down状态触发故障转移
#
# 示例:
sentinel monitor mymaster 192.168.45.136 6379 2
#
# sentinel auth-pass <master-name> <password>
# 配置连接master的授权密码
#
# <master-name>:配置监控master实例名字
# <password>:Redis master密码
#
# 示例:
sentinel auth-pass mymaster 123456
# sentinel down-after-milliseconds <master-name> <milliseconds>
#
# 配置master被认为是不可用的时间,超过这个时间即标为S_DOWN
#
# <master-name>:监控master实例名字
# <milliseconds>:毫秒,默认30秒
#
# 示例:
sentinel down-after-milliseconds mymaster 1000
# sentinel failover-timeout <master-name> <milliseconds>
#
# 设置故障转移超时时间,默认3分钟
#
# <master-name>:master实例名字
# <milliseconds>:毫秒,默认3分钟
#
# 示例:
sentinel failover-timeout mymaster 5000
# sentinel parallel-syncs <master-name> <numslaves>
#
# 故障转移的同时,重新配置slave指向的数量。
#
# <master-name>:master实例名字
# <numslaves>:Slave数量
#
# 示例:
sentinel parallel-syncs mymaster 1
# sentinel client-reconfig-script <master-name> <script-path>
#
# 发生故障转移后执行的脚本
# 输出参数:<master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
# <master-name>:master实例名字
# <script-path>:shell脚本绝对路径
#
# 示例:
sentinel client-reconfig-script mymaster /home/mjw/testfunc.sh
redis.conf
//配置master授权密码
masterauth 123456
//配置master密码
requirepass 123456
//配置slave(注意master不用此配置)
slaveof <masterip> <masterport>
 

 

客户端代码示例

使用哨兵代码非常简单,只需要在客户端改动一下连接方式即可(注意连接的是哨兵的地址

连接哨兵客户端示例
public static void testSentinel(){
        Set<String> sentinels = new HashSet<String>();
        //注意,这里是哨兵的地址
        sentinels.add("192.168.45.142:26379");
        sentinels.add("192.168.45.143:26379");
        sentinels.add("192.168.45.144:26379");
         
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMinIdle(1);
        config.setTestOnBorrow(true);
         
        //mymaster:哨兵配置监控的Redis实例名字
        //123456:密码
        JedisSentinelPool pool = new JedisSentinelPool("mymaster", sentinels,config,"123456");
         
             
            for(int i = 0;i < 100000;i++){
                Jedis jedis = null;
                try {
                    Thread.sleep(1000);
                    jedis = pool.getResource();
                    jedis.set("testSentinel" + i, "testSentinelValue" + i);
                    System.out.println(jedis.get("testSentinel" + i));
                     
                catch (Exception e) {
                    System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
                    System.out.println("error!"+ e.getMessage());
                }finally{
                    if (jedis != null) {
                        try {
                            jedis.close();
                        catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
         
    }