redis分布式方案redis cluster的介绍和实践

简要介绍

redis cluster是redis官方提供的分布式解决方案。主要作用有两点:

  1. 将数据根据分区规则分布到不同的redis节点上,降低单个redis节点的读写压力。
  2. 内置提供高可用支持,集群中的每个主节点可以设置多个从节点,主节点故障后,从节点可以自动替换继续保证redis的使用。

分布式的方案要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分到多个节点上,每个节点负责整体数据的一个子集。

Redis Cluser采用的分区方案叫做虚拟槽分区,所有数据的键值根据哈希函数映射到0~16383整数槽内,计算式:slot=CRC16(key)&16383。所有的槽(slot)分布到各个节点上,根据数据的键值计算所属的槽确定其存储在哪一个集群的节点中。

接下来部署一个简单的redis cluster,通过部署的过程理解redis cluster到底是怎么回事以及怎么使用。

拓扑结构

redis cluster因为是分布式方案,自然要求多个节点。另外为了保证集群的高可用性,会给每一个主节点配置一个或者多个从节点。主节点故障时从节点可以自动替换上去,不至于因为单个节点的故障而导致集群的故障。

接下来我们使用docker部署6个redis节点组成的redis cluster,三主三从。因为在同一台机器上模拟,所以节点的区别主要体现在端口不一致。下文各个节点名称分别使用 “redis-端口号” 来区分。

每个节点的相关信息如下:

节点名称节点角色端口配置文件
redis-6380主节点63806380redis-6380.conf nodes-6380.conf
redis-6381主节点63816381redis-6381.conf nodes-6381.conf
redis-6382主节点63826382redis-6382.conf nodes-6382.conf
redis-6383从节点6383(所属主节点6380)6383redis-6383.conf nodes-6383.conf
redis-6384从节点6384(所属主节点6381)6384redis-6384.conf nodes-6384.conf
redis-6385从节点6385(所属主节点6382)6385redis-6385.conf nodes-6385.conf

其拓扑结构如下:
在这里插入图片描述

部署redis cluster过程

redis创建

redis本身内置提供cluster的功能。不需要额外安装其他组件。redis部署的时候和单机的redis部署并没有太大的区别,主要区别体现在配置文件当中的cluster部分配置要打开。如下给出了 6380 节点的配置。其他节点也只要修改相应的地址和端口信息即可。

redis-6380.conf:

# 节点端口
port 6380
# 开启集群模式
cluster-enabled yes
# 节点超时时间,单位毫秒
cluster-node-timeout 15000
# 集群内部配置文件,这份配置文件会记录集群当中的节点信息。由redis自动维护,不要手动去修改,防止破坏集群的相关配置。
cluster-config-file "nodes-6380.conf"
#主节点密码,部分节点会成为从节点
masterauth password

使用docker部署各个容器,命令如下:

尤其要注意的是redis使用docker部署集群的时候必须使用host模式。

docker run --net=host --name redis-6380 -v /data/redis-cluster/conf/redis-6380.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6380.conf:/data/nodes-6380.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf


docker run --net=host --name redis-6381 -v /data/redis-cluster/conf/redis-6381.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6381.conf:/data/nodes-6381.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf

docker run --net=host --name redis-6382 -v /data/redis-cluster/conf/redis-6382.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6382.conf:/data/nodes-6382.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf


docker run --net=host --name redis-6383 -v /data/redis-cluster/conf/redis-6383.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6383.conf:/data/nodes-6383.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf


docker run --net=host --name redis-6384 -v /data/redis-cluster/conf/redis-6384.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6384.conf:/data/nodes-6384.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf


docker run --net=host --name redis-6385 -v /data/redis-cluster/conf/redis-6385.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6385.conf:/data/nodes-6385.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf

6个redis容器部署完成

在这里插入图片描述

进入其中一个容器当中,使用cluster nodes命令查看cluster的节点信息。也可以使用cluster info查看集群的状态。

在这里插入图片描述

可见当前的redis cluster中只有一个节点。各个节点还需要通过握手的过程相互通信,然后组成一个可以相互通信的集群。

握手

节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。节点握手是集群彼此通信的第一步,由客户端发起命令:cluster meet {ip} {port}

登录到其中的一个节点中,使用cluster meet将所有的节点连接起来。
在这里插入图片描述
再来看节点信息,可见6个节点全部能够发现了。
在这里插入图片描述

分配槽

前面说了,redis cluster根据分区规则将数据分布到不同的redis节点上,降低单个redis节点的读写压力。

redis cluster将数据的分区分为16384个槽,数据存取的时候根据哈希算法将key值计算出对应的值(0-16383),然后去对应的槽里取值。这16384个槽需要全部分配到redis节点上,如果有槽没有分配,则redis不能够使用。

接下来将槽分配到我们部署的三个主节点中。

redis-cli -h 127.0.0.1 -p 6380 -a password cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 6381 -a password cluster addslots {5462..10924}
redis-cli -h 127.0.0.1 -p 6382 -a password cluster addslots {10925..16383}

再来看节点信息,可以看见三个主节点信息末尾多了分配的槽的信息。
在这里插入图片描述

设置从节点

为了保证集群的高可用性,每个redis主节点需要设置从节点。登录从节点,使用cluster replicate命令设置其所属主节点,主节点的标识是cluster nodes结果中给redis节点生成的标识。这个标识同样会写在cluster-config-file文件中,这个文件不删除,redis启动的时候会去这个文件中读取标识和集群信息继续使用。

redis-cli -h 127.0.0.1 -p 6383 -a password cluster replicate 389c66679b44ea421ac685b3b44b64e9a7a32e5c
redis-cli -h 127.0.0.1 -p 6384 -a password cluster replicate e65d837c859c8ad19ee1962829ee7e9fa20cdfc7
redis-cli -h 127.0.0.1 -p 6385 -a password cluster replicate 0304888a12fe7f7fae5df5f444cc4a8832e8d170

再看cluster nodes节点信息,可见redis-6383,redis-6384,redis-6885三个节点都显示为slave,并且有所属的主节点的标识信息。
在这里插入图片描述
至此,一个简单的redis-cluster搭建完成。它由6个节点构成,redis-6380,redis-6381,redis-6382这3个主节点负责处理槽和相关数据,redis-6383,redis-6384,redis-6385这3个从节点负责故障转移。

连接测试

前文说过,数据根据键值通过哈希算法计算得到0-16383的整数值,这些slot已在分配槽的过程当中分配到不同的节点。

根据 cluster keyslot {key}可以计算键值的哈希值。
在这里插入图片描述

前文slot分配如下

redis节点slot范围
redis-63800…5461
redis-63815462…10924
redis-638210925…16383

key为“name”的数据所属slot为5798,数据会在redis-6381当中存储。

如果在别的节点操作数据,会得到 MOVED 的结果,显示该数据应该存储在哪个节点。

在这里插入图片描述

在redis-6381执行数据的存储

在这里插入图片描述
也可以使用 redis-cli -c加上参数登录redis客户端,在数据存取节点不正确时,客户端会自动做重定向。
在这里插入图片描述
接下测试一下主从节点的故障转移,把 redis-6381 节点停掉。redis-6384是其从节点,观察日志可以看出,刚开始是连接失败,而后redis-6384成为了主节点,替代redis-6381继续提供服务。
在这里插入图片描述

查看各个节点的状态,可见redis-6384成为了主节点并且将分配给redis-6381的槽“5462-10924”分配给了redis-6384。同时数据也转移到了redis-6384上。

在这里插入图片描述

将redis-6381节点启动,查看集群节点信息,如下图。可见重新启动的节点自动成为了redis-6384的从节点,作为故障转移的备用节点。
在这里插入图片描述

使用redis-trib.rb搭建集群

redis-trib.rb是redis官方提供的一个工具,在redis的源码包的src目录下。可以下载源码包获得该工具

wget http://download.redis.io/releases/redis-3.0.6.tar.gz

该工具是使用ruby开发的,使用该工具前需要安装ruby的环境

sudo apt-get install ruby

我安装了这个工具之后会出现一些使用上的问题,应该是我安装的问题。建议可以下载一个redis-trib.rb 的docker镜像来使用。

当redis-trib.rb可使用后,我们讲讲这个工具的使用方法。

使用redis-trib.rb创建集群之前同样要先准备好各个redis节点。同我们手工配置的方式一致,提供各个节点的配置文件,然后创建各个节点。配置文件和创建节点的方式前面已经讲述。

有几点区别以及注意事项:

  1. redis不能配置密码,否则会出现 can’t connect to node 的错误。可以在使用工具部署好之后再添上密码。
  2. redis节点不能有集群配置的信息,也不能有数据

准备好各个节点之后,我们使用redis-trib.rb创建集群,命令如下:

redis-trib.rb create --replicas 1 172.17.0.1:6380 172.17.0.1:6381 172.17.0.1:6382 172.17.0.1:6383 172.17.0.1:6384 172.17.0.1:6385

--replicas 1表示每个节点有一个从节点,redis-trib.rb会自动分配主从关系。

执行结果如下:
在这里插入图片描述
可见一个命令就把我们之前做的 握手,分配槽,主从节点的配置等等工作都完成了。工具的使用非常方便。

我们可以继续使用 cluster nodes查看节点信息
在这里插入图片描述
或者redis-trib.rb提供了check方法,检查整个集群。

redis-trib.rb check 172.17.0.1:6380

在这里插入图片描述

可见检查结果显示提示集群所有的槽都已分配到节点。

集群扩容和收缩

集群可对现有的拓扑结构进行调整,就是节点的新增和减少。下面讲讲集群扩容(新增节点)和收缩(减少节点)的方式。

扩容

扩容的操作步骤如下:

  1. 部署创建好新的节点
  2. 将新的节点加入当前集群
  3. 将原有节点上的槽和数据迁移一部分到新节点上

现在加入两个新的redis节点,

节点角色端口配置文件
主节点63866386redis-6386.conf nodes-6386.conf
从节点6387(所属主节点6386)6387redis-6387.conf nodes-6387.conf

前两个步骤不再赘述,和前面说的集群的搭建是一致的。

  1. 准备两个节点的配置文件

  2. 创建docker容器

    docker run --net=host --name redis-6386 -v /data/redis-cluster/conf/redis-6386.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6386.conf:/data/nodes-6386.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf
    
    docker run --net=host --name redis-6387 -v /data/redis-cluster/conf/redis-6387.conf:/usr/local/etc/redis/redis.conf -v /data/redis-cluster/conf/nodes-6387.conf:/data/nodes-6387.conf -d 192.168.168.98:5000/redis redis-server /usr/local/etc/redis/redis.conf
    
  3. 两个新节点加入集群

    cluster meet 172.17.0.1 6386
    cluster meet 172.17.0.1 6387
    

在这里插入图片描述
新加入的节点如果是作为从节点使用,则直接给指定所属主节点即可。

如果是要作为主节点分担读写数据的压力,那么需要做槽和数据的迁移。下面演示手动迁移指定槽 5798 的步骤

  1. 目标节点准备导入槽5798的数据

    在这里插入图片描述

  2. 源节点准备导出槽5798数据

    在这里插入图片描述

  3. 批量获取槽5798对应的键

    在这里插入图片描述

  4. 使用migrate批量迁移键

    这里出现一个问题,因为6386设置了密码,下面的结果会显示无权限访问。但是设置了密码参数确显示格式不对。所以我暂时把6386的密码认证关了,进行的键迁移。

    在这里插入图片描述

  5. 最后,通知所有主节点槽5798指派给目标节点6386

    127.0.0.1:6386> cluster setslot 5798 node 30c5e546f7d2ced550057d6b113ed982301bcd33
    127.0.0.1:6380> cluster setslot 5798 node 30c5e546f7d2ced550057d6b113ed982301bcd33
    127.0.0.1:6384> cluster setslot 5798 node 30c5e546f7d2ced550057d6b113ed982301bcd33
    127.0.0.1:6382> cluster setslot 5798 node 30c5e546f7d2ced550057d6b113ed982301bcd33
    

    然后可以在6386看到槽的分布,5798这个槽都分配给了6386。并且 name 这个值也迁移了过来

    在这里插入图片描述

  6. 同样的,可以将6387设置为6386的从节点

    redis-cli -h 127.0.0.1 -p 6387 -a password cluster replicate 30c5e546f7d2ced550057d6b113ed982301bcd33
    

    在这里插入图片描述
    至此,集群的一个简单扩容完成。

收缩

收缩就意味着把节点去掉。我们可以根据扩容的流程,反其道而行之即可。现在我们把5798这个槽迁回6381。

#分别导出,导入5798槽

127.0.0.1:6384> cluster setslot 5798 importing ccb75e92d2c4d99196cff6749e427207577f1e3b
OK
127.0.0.1:6386> cluster setslot 5798 migrating 30c5e546f7d2ced550057d6b113ed982301bcd33
OK

#将数据进行迁移
127.0.0.1:6386> migrate 172.17.0.1 6384 "" 0 5000 keys name
OK

#通知所有节点5798槽所属的节点
127.0.0.1:6386>  cluster setslot 5798 node ccb75e92d2c4d99196cff6749e427207577f1e3b
127.0.0.1:6380>  cluster setslot 5798 node ccb75e92d2c4d99196cff6749e427207577f1e3b
127.0.0.1:6384>  cluster setslot 5798 node ccb75e92d2c4d99196cff6749e427207577f1e3b
127.0.0.1:6382>  cluster setslot 5798 node ccb75e92d2c4d99196cff6749e427207577f1e3b

槽迁移完成之后,6386,6387节点就没有了数据。可以使用cluster forget node-id移除出集群当中

127.0.0.1:6384> cluster forget 30c5e546f7d2ced550057d6b113ed982301bcd33
OK
127.0.0.1:6384> cluster forget b240523731efca286c9aff97d12e0794d49258a5
OK

至此收缩完成。

redis-trib.rb的槽迁移

上面我们手动执行执行过集群的扩容和收缩。扩容和收缩的过程因为涉及到槽的迁移以及槽中数据迁移非常的复杂。幸好使用redis-trib.rb工具提供了槽的迁移功能,这里介绍一下。

以下命令可以容易的进行不同节点之间的槽迁移

docker run --rm -it zvelo/redis-trib reshard 172.17.0.1:6380
redis-trib.rb reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout
<arg> --pipeline <arg>

命令的参数说明如下:

  • host:port:必传参数,集群内任意节点地址,用来获取整个集群信息。
  • –from:制定源节点的id,如果有多个源节点,使用逗号分隔,如果是all源节点变为集群内所有主节点,在迁移过程中提示用户输入。
  • –to:需要迁移的目标节点的id,目标节点只能填写一个,在迁移过程中提示用户输入。
  • –slots:需要迁移槽的总数量,在迁移过程中提示用户输入。
  • –yes:当打印出reshard执行计划时,是否需要用户输入yes确认后再执行reshard。
  • –timeout:控制每次migrate操作的超时时间,默认为60000毫秒。
  • ·–pipeline:控制每次批量迁移键的数量,默认为10。

下面我们来测试一下,先看当前节点的槽分布:

在这里插入图片描述
例如我们要从redis-6382中迁移100个槽给redis-6381。执行命令如下

redis-trib.rb reshard 172.17.0.1:6380 

用redis-trib.rb执行命令的时候会有错误出现,是我安装的工具的问题。于是我下了一个redis-trib的docker镜像来执行这个命令

在这里插入图片描述在这里插入图片描述
再查看各个节点的槽分布:
在这里插入图片描述
和上文迁移前的槽分布对比,可见redis-6382的槽少了100个,而redis-6381的节点多了100个。

客户端连接

基本原理

客户端初始化连接时,将redis cluster 的slot在各个节点中的分配情况获取到并且保存在客户端的缓存当中。客户端在执行数据的增删查改时,首先计算键值的slot值,再根据slot值找到要操作的节点,获取该节点的连接后执行数据的操作。

下面给出java连接redis cluster的示例代码。

Jedis直接连接以及异常转移测试

引入依赖,要注意早期版本的jedis不支持有密码的cluster的操作。

<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>

示例代码:

package test;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

public class JedisClusterDemo {
	
	public static JedisCluster jedisCluster;
	
	public static void initJedisCluster() {
		Set<HostAndPort> hostAndPortsSet = new HashSet<HostAndPort>();
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6380));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6381));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6382));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6383));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6384));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6385));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6386));
		hostAndPortsSet.add(new HostAndPort("172.17.0.1", 6387));	
		GenericObjectPoolConfig config = new GenericObjectPoolConfig();
		//参数: redis节点集合, 连接超时时间, 数据操作超时时间, 重试次数, 密码, 连接池配置
		jedisCluster = new JedisCluster(hostAndPortsSet, 10000, 10000, 5, "password", config);
	}
	
	public static void main(String args[]) {
		initJedisCluster();
		int incre = 0;
		while (true) {
			try {
				incre++;
				Thread.currentThread().sleep(10000);
				jedisCluster.set("number"+incre, String.valueOf(incre));
				String value = jedisCluster.get("number"+incre);
				System.out.println(value);
			} catch (Exception e) {
				e.printStackTrace();
			} 
		}
	}
}

控制台显示程序执行的结果:

在这里插入图片描述
查看各个redis节点可见数据分布在各个节点当中

在这里插入图片描述
然后我们停掉其中一个主节点模拟故障转移

如下图,可见redis-6384节点断开了连接,redis-6381节点成为了主节点
在这里插入图片描述
而我们的程序会出现拒绝连接的异常,在故障转移完成之后继续正常运行
在这里插入图片描述
redis-6381中的数据正常,部分是从redis-6384节点中同步过来的
在这里插入图片描述

spring程序连接以及异常转移测试

依赖配置

<dependency>
	<groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>4.0.8.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>4.0.8.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.0.8.RELEASE</version>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.8.0.M1</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.8.4</version>
</dependency>		

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:context="http://www.springframework.org/schema/context"
       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"
	   xmlns:cache="http://www.springframework.org/schema/cache    
    	http://www.springframework.org/schema/cache
    	http://www.springframework.org/schema/cache/spring-cache.xsd">
    	
    <!-- jedis连接池的配置 -->	
	<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig" >  
         <property name="maxIdle" value="10000" />  
         <property name="maxWaitMillis" value="10000" />  
         <property name="testOnBorrow" value="true" />  
    </bean >  

    <!-- cluster连接的配置, 注入各个节点 -->
	<bean id="redisClusterConfiguration"   class="org.springframework.data.redis.connection.RedisClusterConfiguration">
    	<property name="clusterNodes">
            <set>
            	<bean id="clusterRedisNodes1"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6380" type="int" />
				</bean>
            	<bean id="clusterRedisNodes2"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6381" type="int" />
				</bean>
            	<bean id="clusterRedisNodes3"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6382" type="int" />
				</bean>
            	<bean id="clusterRedisNodes4"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6383" type="int" />
				</bean>
            	<bean id="clusterRedisNodes5"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6384" type="int" />
				</bean>
            	<bean id="clusterRedisNodes6"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6385" type="int" />
				</bean>
            	<bean id="clusterRedisNodes7"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6386" type="int" />
				</bean>
            	<bean id="clusterRedisNodes8"   class="org.springframework.data.redis.connection.RedisNode"> 
			    	<constructor-arg name="host" value="172.17.0.1" />
			        <constructor-arg name="port" value="6387" type="int" />
				</bean>
            </set>
    	</property>
	</bean>
	<!-- 配置redis连接生成器,主要将cluster的节点信息和redis连接池配置作为构造函数参数注入。另外添加上主节点password的配置 -->
    <bean id="connectionFactory"  class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >  
         <property name="password" value="password" />
         <property name="timeout" value="30000" ></property>
		 <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"></constructor-arg>
    	 <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>  
    </bean >  
    
    <!-- RedisTemplate配置,提供一些数据的序列化和反序列化配置 -->
    <bean id="keySerializer" class="org.springframework.data.redis.serializer.GenericToStringSerializer">
        <constructor-arg index="0" type="java.lang.Class" value="java.lang.Object" />
    </bean>
    <bean id="serializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer">
    </bean>

    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="connectionFactory" />
        <property name="defaultSerializer" ref="serializer" />
        <property name="keySerializer" ref="keySerializer" />
        <property name="hashKeySerializer" ref="keySerializer" />
    </bean>
</beans>

代码示例:

public class Main {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext context 
            = new ClassPathXmlApplicationContext("spring-redis.xml");
		RedisTemplate<String, String> template 
            = (RedisTemplate<String, String>) context.getBean("redisTemplate");
		int incre = 0;
		while (true) {
			try {
				incre++;
				Thread.currentThread().sleep(3000);				
				template.opsForValue().set("springnumber"+incre, String.valueOf(incre));
				String number = template.opsForValue().get("springnumber"+incre);
				System.out.println("key:"+("springnumber"+incre)+"; value:"+number);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
		}
	}
}

程序的执行结果
在这里插入图片描述
各个redis节点中的数据分布如下:
在这里插入图片描述
模拟redis-6380的宕机,查看故障转移的结果如下图。可见redis-6380断开了连接,而redis-6383节点接替了其继续提供服务。
在这里插入图片描述
程序执行的输出如下图,也可看出出现了连接的异常,而后恢复正常。
在这里插入图片描述
查看redis-6383节点的数据正常
在这里插入图片描述

结论

redis cluster能够正常的提供分布式以及高可用的解决方案。搭建时可以使用redis-trib.rb工具进行搭建,可以很大幅度的提高效率。

在对于已有的redis cluster进行扩容或者收缩时要慎重。在实验过程当中出现过一些问题导致数据的丢失。

在使用redis cluster时,要注意客户端jar包的差异。早期的jar包对于密码的支持受限。

在使用redis cluster时也会存在一些限制。如每个节点只能使用db0,因为分布式存储在不同节点,对于redis的事务支持和批量操作也仅限于同一个节点上。在使用时要注意这些问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值