Redis Cluster集群主从方案(附Jedis Cluster教程)

Redis Cluster集群主从方案(附Jedis Cluster教程)

本文介绍一种通过Jedis和Cluster实现Redis集群(主从)的高可用方案,该方案需要使用Jedis2.8.0(推荐),Redis3.0及以上版本(强制).

附:
Redis Cluster集群主从方案:http://wosyingjun.iteye.com/blog/2289220
Redis Sentinel主从高可用方案:http://wosyingjun.iteye.com/blog/2289593

一、Redis集群介绍

  • Redis 集群是一个提供在多个Redis间节点间共享数据的程序集。
  • Redis集群并不支持处理多个keys的命令,因为这需要在不同的节点间移动数据,从而达不到像Redis那样的性能,在高负载的情况下可能会导致不可预料的错误.
  • Redis 集群通过分区来提供一定程度的可用性,在实际环境中当某个节点宕机或者不可达的情况下继续处理命令.
  • Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么: 节点 A 包含 0 到 5500号哈希槽. 节点 B 包含5501 到 11000 号哈希槽. 节点 C 包含11001 到 16384号哈希槽.

二、Redis Cluster集群的优势:

  • 自动分割数据到不同的节点上。
  • 整个集群的部分节点失败或者不可达的情况下能够继续处理命令。
  • 客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  • 支持主从复制模型。(slave->master 选举,集群容错)

三、Redis Cluster集群的主从复制模型:

为了使在部分节点失败或者大部分节点无法通信的情况下集群仍然可用,所以集群使用了主从复制模型,假如有A,B,C三个节点的集群,在没有复制模型的情况下,如果节点B失败了,那么整个集群就会以为缺少5501-11000这个范围的槽而不可用.
然而如果在集群创建的时候我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了。

四、Redis Cluster模型图

Redis Cluster集群主从方案(附Jedis Cluster教程)

五、Redis的安装以及cluster配置

下载redis最新的安装包
wget http://download.redis.io/releases/redis-3.0.7.tar.gz
解压
tar xzf redis-3.0.7.tar.gz 
安装支持包tcl
yum install tcl 编译原文件
make
创建集群相关文件(方便管理)
mkdir cluster 
cd cluster
mkdir 6379 6380 6381 6382 6383 6384 
cluster下面每个目录中都创建一个redis.conf文件. 注意修改文件中的端口号:

port 6379
cluster-enabled yes
cluster-config-file nodes_6379.conf
cluster-node-timeout 5000
appendonly yes

把redis-server的可执行文件复制到cluster下面的各个目录, 然后打开6个shell终端,进入各个目录,启动每个实例, 命令如下: 
./redis-server redis.conf

检查6个服务是否都启动了
Redis Cluster集群主从方案(附Jedis Cluster教程)

搭建集群
./redis-trib.rb create --replicas 1 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

–replicas 1 表示我们希望为集群中的每个主节点创建一个从节点。
此时报错,发现缺少相应的ruby环境,如下安装相应环境:
yum install ruby
yum install rubygems
gem install redis

安装好环境后再次搭建集群
./redis-trib.rb create --replicas 1 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 Redis Cluster集群主从方案(附Jedis Cluster教程)

检测集群节点相关信息 
./redis-trib.rb check 127.0.0.1:6379 

Redis Cluster集群主从方案(附Jedis Cluster教程)

可以发现系统将前3个服务做了主节点,后3个服务作为了从节点,并且是相对应的。

进入某个节点验证
./redis-cli -c -h 127.0.0.1 -p 6379

Redis Cluster集群主从方案(附Jedis Cluster教程)

可以发现程序根据key对16384取模的值为3488,根据hash槽的分布跳转到了对应的节点上。 可见集群环境正常运行

六、Jedis Cluster教程

<?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:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<!-- Jedis链接池配置,注意:Jedis版本建议升级到最新 -->
	<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
		<property name="maxTotal" value="100" />
		<property name="maxIdle" value="20" />
		<property name="minIdle" value="10" />
		<property name="blockWhenExhausted" value="true"></property>
		<property name="maxWaitMillis" value="3000" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="testWhileIdle" value="true" />
		<property name="minEvictableIdleTimeMillis" value="60000" />
		<property name="timeBetweenEvictionRunsMillis" value="30000" />
		<property name="numTestsPerEvictionRun" value="-1" />
	</bean>

	<!-- JedisCluster -->
	<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
		<constructor-arg index="0">
			<set>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.111" />
					<constructor-arg index="1" value="7111" type="int" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.112" />
					<constructor-arg index="1" value="7112" type="int" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.113" />
					<constructor-arg index="1" value="7113" type="int" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.114" />
					<constructor-arg index="1" value="7114" type="int" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.115" />
					<constructor-arg index="1" value="7115" type="int" />
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg index="0" value="192.168.1.116" />
					<constructor-arg index="1" value="7116" type="int" />
				</bean>
			</set>
		</constructor-arg>
		<constructor-arg index="1" value="2000" type="int"></constructor-arg>
		<constructor-arg index="2" value="100" type="int"></constructor-arg>
		<constructor-arg index="3" ref="jedisPoolConfig"></constructor-arg>
	</bean>

</beans>
package wusc.edu.demo.redis;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import redis.clients.jedis.JedisCluster;


public class RedisClusterSpringTest {
	private static final Log log = LogFactory.getLog(RedisClusterSpringTest.class);

	public static void main(String[] args) {
		try {
			ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring/spring-context.xml");
			context.start();
			
			JedisCluster jedisCluster = (JedisCluster) context.getBean("jedisCluster");
			int num = 1000;
			String key = "yingjun";
			String value = "";
			for (int i=1; i <= num; i++){
				// 存数据
				//jedisCluster.set(key+i, "yingjun"+i);
				//jedisCluster.setex(key+i, 60, "yingjun"+i);
				
				// 取数据
				value = jedisCluster.get(key+i); 
				log.info(key+i + "=" + value);
				
				// 删除数据
				//jedisCluster.del(key+i); 
				//value = jedisCluster.get(key+i); 
				//log.info(key+i + "=" + value);
			}

			context.stop();
		} catch (Exception e) {
			log.error("==>RedisSpringTest context start error:", e);
			System.exit(0);
		} finally {
			log.info("===>System.exit");
			System.exit(0);
		}
	}
}

相关推荐