redis 主从复制,哨兵模式 sentinel, 集群模式Redis Cluster

 

本文配置文件部分,进行了引用

http://www.javashuo.com/article/p-eapbamhf-gp.html

http://www.javashuo.com/article/p-kozpdeps-db.html

http://www.javashuo.com/article/p-ozppdxgj-kg.html

一个pc运行多个redis

只需复制一份配置文件,redis.conf,改掉其中的一些配置,使两个不冲突即可

# Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
# 启用守护进程后,Redis会把pid写到一个pidfile中,在/var/run/redis.pid
daemonize yes

# 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
# 这里多个配置文件不能相同
pidfile /var/run/redis.pid

# 指定Redis监听端口,默认端口为6379
# 如果指定0端口,表示Redis不监听TCP连接
# 端口不能相同
port 6379

# 指定本地数据库文件名,默认值为dump.rdb
# 工作目录.
# 指定本地数据库存放目录,文件名由上一个dbfilename配置项指定
# 这里路径和文件名的组合 不能冲突
dbfilename dump.rdb
dir ./

然后通过redis-server命令,后接不同的配置文件即可

实现主从复制

在作为从服务器的redis配置文件中加上如下配置即可

# 填写master的ip和端口号
slaveof 127.0.0.1 6379

之后便可以启动两个redis,输入info命令进行查看

此时从服务器只能读,不能写

哨兵模式

建立配置文件sentinel.conf,拷贝多个,此处使用3个即可

# 当前Sentinel服务运行的端口
# 多个配置文件 端口不能相同
port 26379  
# 哨兵监听的主服务器 
# 后面的数字代表当有足够数量的 Sentinel在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线 
# 主观下线和客观下线可以网上搜一下
sentinel monitor mymaster 127.0.0.1 6379 1
# 3s内mymaster无响应,则认为mymaster宕机了
sentinel down-after-milliseconds mymaster 3000
# 如果10秒后,mysater仍没启动过来,则启动failover  
sentinel failover-timeout mymaster 10000  
# 执行故障转移时, 最多有1个从服务器同时对新的主服务器进行同步
sentinel parallel-syncs mymaster 1

然后启动哨兵

redis-sentinel ../sentinel.conf

在人工关闭master之后

redis-cli -p 6379 shutdown

可以看到,从服务器的角色会更改为master

测试代码

package cluster;

/**
 * @program: MyMaven
 * @description: redis主从切换测试
 * @author: dengbin
 * @create: 2019-02-12 17:02
 **/

import java.util.HashSet;
import java.util.Set;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisSentinelPool;

public class Main {

    private static JedisSentinelPool pool = null;
    // 自带的哨兵模式 JedisSentinelPool, 并在一开始初始化连接池
    static {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            // 控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;
            // 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
            config.setMaxTotal(Integer.valueOf(1000));
            // 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
            config.setMaxIdle(Integer.valueOf(20));
            // 表示当borrow(引入)一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
            config.setMinEvictableIdleTimeMillis(Integer.valueOf(-1));
            // 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
            config.setTestOnBorrow(Boolean.valueOf(true));

            // master名称和配置文件中配置的要一样
            String master = "mymaster";
            //setinel客户端提供了master自动发现功能
            Set<String> sentinels = new HashSet<String>();
            sentinels.add("127.0.0.1:26379");
            sentinels.add("127.0.0.1:26380");

            pool = new JedisSentinelPool(master, sentinels, config);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 构建redis连接池
     *
     * @return JedisPool
     */
    public static JedisSentinelPool getPool() {
        return pool;
    }

    /**
     * 返还到连接池
     *
     * @param pool
     * @param redis
     */
    public static void returnResource(JedisSentinelPool pool, Jedis redis) {
        if (redis != null) {
            try {
                pool.returnResource(redis);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 测试redis线程池是否正常
     * @param args
     */
    public static void main(String[] args) {
        JedisSentinelPool pool = Main.getPool();
        Jedis redis = pool.getResource();
        System.out.println("redis = " + redis);
        System.out.println(redis.get("test"));
        if(redis != null){
            returnResource(pool,redis);
        }
    }
}

集群模式

此处采用六个redis服务器,一台机器启动多个redis在上文已经写了

在配置文件中更改如下配置,六个配置文件都需要更改,然后启动六台服务器

cluster-enabled  yes                      //开启集群  把注释#去掉
cluster-config-file  nodes_7000.conf      //集群的配置  配置文件首次启动自动生成,这个文件名可以和端口对应
cluster-node-timeout  5000                //请求超时  设置5秒够了

官方的redis cluster采用ruby语言,所以需要先安装ruby,自行百度

安装完之后,执行下面的命令

gem install redis   #安装redis接口

然后创建集群

# 最后面的1代表一个master一个cluster
redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 --cluster-replicas 1

可以看到如下提示,代表成功,分了三主三从,一共16384个slots

>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6382 to 127.0.0.1:6379
Adding replica 127.0.0.1:6383 to 127.0.0.1:6380
Adding replica 127.0.0.1:6384 to 127.0.0.1:6381
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: af4afba85db1489a66067d6007d024b0f1715806 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
M: e7e31b33a9d5174cad30bca246e38cb03d73aa76 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
M: ad88c6649777c407b7feb2170c5a2608693dddcf 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
S: 904bf19376035b3914ea8bc863c0fd8f6e71a306 127.0.0.1:6382
   replicates ad88c6649777c407b7feb2170c5a2608693dddcf
S: 27d8e610c2426f21937bd2190d14273c5b64a5f2 127.0.0.1:6383
   replicates af4afba85db1489a66067d6007d024b0f1715806
S: 780aa05b6f6bb1d23779bd05ec76593f0eb5729a 127.0.0.1:6384
   replicates e7e31b33a9d5174cad30bca246e38cb03d73aa76
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
..
>>> Performing Cluster Check (using node 127.0.0.1:6379)
M: af4afba85db1489a66067d6007d024b0f1715806 127.0.0.1:6379
   slots:[0-5460] (5461 slots) master
   1 additional replica(s)
S: 904bf19376035b3914ea8bc863c0fd8f6e71a306 127.0.0.1:6382
   slots: (0 slots) slave
   replicates ad88c6649777c407b7feb2170c5a2608693dddcf
M: e7e31b33a9d5174cad30bca246e38cb03d73aa76 127.0.0.1:6380
   slots:[5461-10922] (5462 slots) master
   1 additional replica(s)
S: 780aa05b6f6bb1d23779bd05ec76593f0eb5729a 127.0.0.1:6384
   slots: (0 slots) slave
   replicates e7e31b33a9d5174cad30bca246e38cb03d73aa76
S: 27d8e610c2426f21937bd2190d14273c5b64a5f2 127.0.0.1:6383
   slots: (0 slots) slave
   replicates af4afba85db1489a66067d6007d024b0f1715806
M: ad88c6649777c407b7feb2170c5a2608693dddcf 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
   1 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.

若看到如下报错信息,删除redis的备份文件-dump.rdb,同时在redis执行命令 flushdb,重新执行命令即可

 

[ERR] Node 127.0.0.1:6379 is not empty. Either the node already knows other nodes (check with CLUSTER NODES) or contains some key in database 0.

在命令行使用redis集群时,加上-c参数 redis-cli -c -p 6379,否则会提示error,因为这个key不在当前服务器,加上后,在进行操作时,会自动路由到对应的服务器上

连接上任意一台redis服务器,可通过如下命令查看当前节点信息

cluster nodes
780aa05b6f6bb1d23779bd05ec76593f0eb5729a 127.0.0.1:[email protected] slave e7e31b33a9d5174cad30bca246e38cb03d73aa76 0 1550108575491 6 connected
# myself代表当前自己连接的服务器
e7e31b33a9d5174cad30bca246e38cb03d73aa76 127.0.0.1:[email protected] myself,master - 0 1550108575000 2 connected 5461-10922
# 这台redis之前是master,被我手动关闭,现在成了fail,之前他的slave6383成了master
af4afba85db1489a66067d6007d024b0f1715806 127.0.0.1:[email protected] master,fail - 1550108554453 1550108553000 1 disconnected
27d8e610c2426f21937bd2190d14273c5b64a5f2 127.0.0.1:[email protected] master - 0 1550108577009 7 connected 0-5460
ad88c6649777c407b7feb2170c5a2608693dddcf 127.0.0.1:[email protected] master - 0 1550108575000 3 connected 10923-16383
904bf19376035b3914ea8bc863c0fd8f6e71a306 127.0.0.1:[email protected] slave ad88c6649777c407b7feb2170c5a2608693dddcf 0 1550108575593 4 connected

java测试代码

private static JedisCluster jedis;
    static {
        // 添加集群的服务节点Set集合
        Set<HostAndPort> hostAndPortsSet = new HashSet<HostAndPort>();
        // 添加节点
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6379));
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6380));
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6381));
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6382));
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6383));
        hostAndPortsSet.add(new HostAndPort("127.0.0.1", 6384));


        // Jedis连接池配置
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // 最大空闲连接数, 默认8个
        jedisPoolConfig.setMaxIdle(100);
        // 最大连接数, 默认8个
        jedisPoolConfig.setMaxTotal(500);
        //最小空闲连接数, 默认0
        jedisPoolConfig.setMinIdle(0);
        // 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间,  默认-1
        // 设置2秒
        jedisPoolConfig.setMaxWaitMillis(2000);
        //对拿到的connection进行validateObject校验
        jedisPoolConfig.setTestOnBorrow(true);
        jedis = new JedisCluster(hostAndPortsSet, jedisPoolConfig);
    }

    public static void main(String[] args){
        System.out.println(jedis.get("1"));
    }