1. 程式人生 > >Redis-使用redis-trib構建叢集

Redis-使用redis-trib構建叢集

Redis3.0 及其之後的版本提供了 redis-cluster 叢集支援,用於在多個redis節點間共享資料,以提高服務的可用性。

構建 redis-cluster 叢集可以通過 redis-trib.rb 工具來完成。redis-trib.rb 是redis官方提供的一個叢集管理工具,整合在redis安裝包的 src 目錄下。redis-trib.rb 封裝了redis提供的叢集命令,使用簡單、便捷。

因為 redis-trib.rb 是由ruby語言編寫的,所以使用該工具需要ruby語言環境的支援。

$ ruby -v
ruby 2.3.1p112 (2016-04-26) [x86_64-linux
-gnu]

redis-cluster叢集

1、配置

要啟用redis-cluster叢集,需要先修改redis配置檔案叢集配置部分的內容

redis.conf

################################ REDIS CLUSTER  ###############################
# 啟用redis-cluster叢集
cluster-enabled yes

# 叢集節點配置檔案
# 該檔案無需手工修改,由redis自動維護(建立和更新)
# 需要注意,單機執行多例項時,確保該檔案沒有被其他例項覆蓋(不允許重名)
cluster-config-file nodes-6377.
conf # 節點超時時長(毫秒) cluster-node-timeout 15000

為了方便進行演示,這裡分別以埠 637763786379 各啟用一個例項來代表不同的redis伺服器

ps aux | grep redis

2、建立叢集

建立叢集使用 redis-tribcreate 命令完成,create 命令的格式為:

 create host1:port1 ... hostN:portN

預設情況下,ruby 是無法識別redis的,直接執行 redis-trib.rb create IP:PORT 將會報錯

$ ./redis-trib.rb create 192.168.206.128:6377
192.168.206.128:6378 192.168.206.128:6379 /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- redis (LoadError) from /usr/lib/ruby/2.3.0/rubygems/core_ext/kernel_require.rb:55:in `require' from ./redis-trib.rb:25:in `<main>'

所以需要先為ruby安裝redis第三方介面,執行命令 gem install redis 即可

$ sudo gem install redis
[sudo] password for zhangcs: 
Fetching: redis-4.0.1.gem (100%)
Successfully installed redis-4.0.1
Parsing documentation for redis-4.0.1
Installing ri documentation for redis-4.0.1
Done installing documentation for redis after 1 seconds
1 gem installed

此時再使用 create 就可以將637763786379 這3臺伺服器構建成一個叢集了

建立叢集

有一點需要注意的是,redis-cluster叢集至少需要3個可用節點。

3、檢視叢集

使用 info 命令指定叢集上任一節點的地址便可以檢視叢集狀態

叢集狀態

主從複製模型

剛才說到,redis-cluster至少需要保證3個節點可用。那麼為了避免節點宕機導致服務不可用,我們就需要新增主從配置,為叢集中的節點增加從節點;使其在主節點宕機時,能夠將從節點提升為主節點代替原來的主節點進行工作。

在非叢集的單節點環境中,配置主從關係的方法大致有 2 種:

1、修改從伺服器配置檔案 redis.confslaveof <masterip> <masterport> 選項;

2、在從伺服器上使用slaveof 命令直接指定主伺服器。

然而在redis-cluster叢集環境中,啟用了cluster配置的情況下slaveof 是不可用的。

假定有 6381 伺服器配置 slaveof 指定主伺服器 6377 ,同時該伺服器啟用了redis-cluster配置

################################# REPLICATION #################################
slaveof 192.168.206.128 6377

################################ REDIS CLUSTER  ###############################
cluster-enabled yes
cluster-config-file nodes-6381.conf
cluster-node-timeout 15000

那麼此時 6381 埠的伺服器將無法成功啟動

$ ./redis-server /usr/local/redis/6381/redis.conf 
*** FATAL CONFIG FILE ERROR ***
Reading the configuration file, at line 283
>>> 'slaveof 192.168.206.128 6377'
slaveof directive not allowed in cluster mode

顯然我們無法使用原有的主從配置方法對叢集進行配置。此時我們需要藉助於 redis-trib.rb 工具來進行從節點的新增操作,先將節點加入叢集中,然後再宣告節點為原有節點的從節點。

1、啟用服務後先將該節點加入叢集,直接使用redis-trib.rb的 add-node 命令即可實現:

redis-trib add-node new_host:new_port existing_host:existing_port

新增節點

此時通過 redis-trib info 能夠檢視到該節點已經成功加入了叢集中,並且該節點並沒有分配雜湊槽

叢集狀態

2、宣告該節點為叢集中某一節點的從節點,需要使用客戶端進入該節點(此處即為新增的從節點 6381)進行設定,使用 cluster replicate 命令指定所屬主節點ID。

主節點ID可以使用客戶端連線到集群后通過命令 cluster nodes 檢視 :

節點資訊

使用客戶端連線新增的從節點 6381 ,指定主節點 6377 在叢集中的ID ,宣告為 6377 節點的從節點

$ ./redis-cli -h 192.168.206.128 -c -p 6381
192.168.206.128:6381> cluster replicate e10dde558fb46fe8ae6fe66e54ef56032fbcce0f
OK

至此就完成了叢集中一個節點的主從配置,檢視 6377 節點能夠看到其包含一個從節點:

叢集狀態

查詢出 6377 埠服務對應的PID,然後通過 kill 將服務關閉,使該節點在叢集上不可用;此時檢視叢集資訊,能夠發現從節點 6381 自動提升為主節點,頂替了不可用的 6377 節點。

關閉服務

叢集狀態

重新啟動已宕機的服務後,該節點將會被當做從節點新增到管理原先的雜湊槽分配範圍的節點上。這裡也就是新增到了 6381 節點上,6381 節點管理的雜湊槽就是原先由 6377 節點所管理的

重啟宕機的伺服器

客戶端連線 redis-cluster

客戶端在連線 redis 伺服器時帶上引數 -c 即為連線到cluster叢集

$ redis-cli -h 192.168.206.128 -c -p 6377
192.168.206.128:6377> set name zhangcs
-> Redirected to slot [5798] located at 192.168.206.128:6378
OK
192.168.206.128:6378>

可以看到,在 6377 埠的伺服器上儲存一個string型別的鍵值對 name = zhangsan 的時,操作被重定向到了 6378 埠的伺服器上,而 name = zhangsan 這個鍵值對最終也被儲存在了 6378 埠的伺服器裡。

同理,在獲取資料時,也會重定向到對應資料實際儲存的伺服器上,然後在該伺服器上進行操作。

$ ./redis-cli -h 192.168.206.128 -c -p 6377
192.168.206.128:6378> get name
"zhangcs"
192.168.206.128:6378>
單獨連線叢集上的節點

需要注意的是,節點在加入集群后,如果不宣告引數 -c 連線叢集而是單獨連線叢集上的節點,那麼在操作時如果需要重定向到其他的伺服器,是無法成功重定向然後完成操作的。

例如鍵值對 name = zhangsan 儲存在 6378 埠的伺服器上,此時如果我們單獨連線到 6377 埠的伺服器上進行操作,那麼該操作是無法成功的。

$ redis-cli -h 127.0.0.1 -p 6377
127.0.0.1:6377> get name
(error) MOVED 5798 127.0.0.1:6378
127.0.0.1:6377> set name lisi
(error) MOVED 5798 127.0.0.1:6378
127.0.0.1:6377>

而如果無需重定向,則能成功完成操作。

$ redis-cli -h 127.0.0.1 -p 6378
127.0.0.1:6378> get name
"zhangsan"
127.0.0.1:6378>

Spring整合 redis-cluster

1、連線池配置

@Bean
public JedisPoolConfig jedisPoolConfig() {
  JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
  jedisPoolConfig.setMaxTotal(100);
  jedisPoolConfig.setMaxIdle(10);
  jedisPoolConfig.setMaxWaitMillis(1500);
  return jedisPoolConfig;
}

2、JedisCluster物件配置

@Bean
public JedisCluster jedisCluster() {
  Set<HostAndPort> nodes = new HashSet<HostAndPort>();
  nodes.add(new HostAndPort("192.168.206.128", 6377));
  nodes.add(new HostAndPort("192.168.206.128", 6378));
  nodes.add(new HostAndPort("192.168.206.128", 6379));
  nodes.add(new HostAndPort("192.168.206.128", 6381));
  JedisCluster jedisCluster = new JedisCluster(nodes, jedisPoolConfig());
  return jedisCluster;
}

3、JedisCluster物件的使用

@Autowired
private JedisCluster jedisCluster;

@Test
public void testCluster() {
    Assert.assertNotNull(jedisCluster);

    String result = jedisCluster.set("name", "zhangsan");
    Assert.assertNotNull(result);
    Assert.assertEquals("OK", result);

    String name = jedisCluster.get("name");
    Assert.assertNotNull(name);
    Assert.assertEquals("zhangsan", name);
}
spring-cache整合
@Configuration
@ComponentScan(basePackages = {"org.pro.service"})
// 啟用快取
@EnableCaching
public class RootConfig {

    /**
     * jedis連線池配置
     * */
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(1000);
        jedisPoolConfig.setMaxIdle(10);
        jedisPoolConfig.setMaxWaitMillis(1500);
        return jedisPoolConfig;
    }

    /**
     * redis-cluster配置
     * */
    @Bean
    public RedisClusterConfiguration redisClusterConfiguration() {
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();

        Set<RedisNode> nodes = new HashSet<>();
        nodes.add( new RedisNode("127.0.0.1", 6377));
        nodes.add( new RedisNode("127.0.0.1", 6378));
        nodes.add( new RedisNode("127.0.0.1", 6379));
        redisClusterConfiguration.setClusterNodes(nodes);
        redisClusterConfiguration.setMaxRedirects(4);
        return redisClusterConfiguration;
    }

    /**
     * jedis連線工廠
     * */
    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(redisClusterConfiguration(), jedisPoolConfig());
        jedisConnectionFactory.setTimeout(15000);
        return jedisConnectionFactory;
    }

    /**
     * redis模板
     * */
    @Bean
    public RedisTemplate redisTemplate() {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setValueSerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setHashValueSerializer(stringRedisSerializer);

        return redisTemplate;
    }

    /**
     * redis快取管理器
     * */
    @Bean
    public RedisCacheManager redisCacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate());
        return redisCacheManager;
    }

}