Redis 从 3.0 开始 支持服务器端集群:通过HashSlot 分区,同时支持Master-slave 主从模式,别的不多说,这篇主要记录下我在集群部署时遇到的一些问题及解决办法:
环境:centos 6.4
软件:redis 3.0.7 , Ruby , jedis 2.8
请确保linux 可以联网!
开始部署Redis:
1. 下载Redis3.0.7 点击打开链接
2. 创建一个新目录用于Redis安装测试 mkdir /lbbhd
3. 将文件copy到一个新目录 cp /root/Downloads/redis-3.0.7.tar.gz /lbbhd
4. 解压 tar -zxvf redis-3.0.7.tar.gz
5. 安装 Redis cd redis-3.0.7 ; make ; cd src/ ; make install;
6. 在 redis-3.0.7 中 有一个默认的配置文件 redis.conf cat ../redis.conf 在这个配置文件中 有一段关于 REDIS CLUSTER 的配置项说明,有兴趣可以看下
我在这就不一一介绍了,官方文档有一份最简配置
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
首先 mkdir redis_clus1ter 创建一个集群文件,用于存储配置文件等
然后 cd redis_cluster ; mkdir 7000 7001 7002 7003 7004 7005 7006 7007 7008 创建9个文件夹 对应不同的Redis实例
将前文提到的 redis.conf 复制到7000 ~ 7008 文件夹下
文档提供的最简配置有点小问题,在 文件中 属性 cluster-config-file 设置的是相对路径,这个路径是相对于 Redis Server 启动文件来说的
举个例子,如图:
我在 7000 和 7001 两个文件夹里 都生成了redis.conf 文件,内容也只是 port 不同, 此时我们来启动一下Redis
首先启动 7000
如图 启动成功。 然后启动 7001....
如图 提示 每个不同的节点 要使用不同的 配置文件,这个文件指的就是 cluster-config-file
这个文件在哪呢?
就生成在redis src 目录下(这个文件会生成在当前目录下,例如在Desktop执行启动7001节点,那么这个文件就生成在桌面), 所以我们在这对redis.conf 做一下小小的修改,将相对路径nodes.conf 改为 /lbbhd/redis_cluster/7000/nodes.conf 绝对路径,7000为文件夹名字,7000~7008 , 这个文件不用管,redis自己会生成会更新,只需要提供一个路径就可以。
第二点修改 将redis 改为后台启动 daemonize 属性 设置为 yes。
将配置文件cp到 7000~7008 修改port 及 cluser-config-file 路径。
启动9个Redis实例:
---------------------------------------------------------- Redis 准备工作到此告一段落
Redis 集群 需要Ruby支持,下面安装相关软件:
yum install ruby
yum install rubygems
gem install redis
--------------------------------------------------------安装完成之后开始Redis 集群命令
cd /lbbhd/redis-3.0.7/src
./redis-trib.rb create --replicas 2 192.168.1.193:7000 192.168.1.193:7001 192.168.1.193:7002 192.168.1.193:7003 192.168.1.193:7004 192.168.1.193:7005 192.168.1.193:7006 192.168.1.193:7007 192.168.1.193:7008
输入 yes
集群完成!
刚才命令中的 --replicas属性 指定从节点个数,后边9个节点会根据指定的从节点分配每个Master的slave
前三个7000,7001,7002 为master 7003,7004为7000的slave 7005,7006为7001的slave 7007,7008为7002的slave;
连接7000 通过info 或 info replication 查看主从信息。
-----------------------------------------------------------集群测试
---------------------------------------------------主从测试
将有数据的master 关闭一个 ,redis是否能够另外的master 来代替,数据是否丢失
如上图。将7002关闭之后,redis集群自动在7007,7008 两个slave中选个一个升级为master,且存储在7002上的a7000依然可以查询;
注:Redis Cluster is not able to guarantee strong consistency. 在某些特殊情况下,Redis会存在丢失写的情况。
重新启动7002 ,7002会自动成为7008的slave。
错误:
(error) MOVED 5798 192.168.1.193:7005 使用 ./redis-cli -c -p port 启动 -c代表集群模式
关于 redis-trib.rb 命令:
➜ redis-3.0.7 src/redis-trib.rb
Usage: redis-trib <command> <options> <arguments ...>
create host1:port1 ... hostN:portN
--replicas <arg>
check host:port
fix host:port
reshard host:port
--from <arg>
--to <arg>
--slots <arg>
--yes
add-node new_host:new_port existing_host:existing_port
--slave
--master-id <arg>
del-node host:port node_id
set-timeout host:port milliseconds
call host:port command arg arg .. arg
import host:port
--from <arg>
help (show this help)
For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
可以通过check 来检查集群节点,如果某些节点出现问题,可以通过fix来修复
例如 (ERROR)CLUSTERDOWN the cluster is down ;
关于Redis3.0的集群暂告一段落,下半部分是关于 jedis2.8客户端 与 spring 整合的一些想法和操作。
---------------------------------------------------------------------------------jedis2.8 与 spring 整合----------------------------------------------------------
1.下载jedis2.8 下载地址点击打开链接
2. 2.8版本的jedis 需要 common-pool2 的支持
3. 创建一个java项目RedisDemo,导入需要的jar包
4. 测试
package com.zm.redis.demo;
import java.util.HashSet;
import java.util.Set;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
/**
* 根据jedis文档提供的代码操作Redis集群
*/
public class RedisBasicDemo {
public static void main(String[] args) {
test();
}
private static void test() {
Set<HostAndPort> jedisClusterNodes = new HashSet<HostAndPort>();
//在添加集群节点的时候只需要添加一个,其余同一集群的阶段会被自动加入
jedisClusterNodes.add(new HostAndPort("192.168.1.193", 7000));
JedisCluster jc = new JedisCluster(jedisClusterNodes);
jc.set("foo", "bar");
String value = jc.get("foo");
System.out.println(value);
}
}
5. 实际应用时,每次都new一个对象自然不可取。在创建JedisCluster对象的过程中,最终结果会得到两个Map对象:
private Map<String, JedisPool> nodes = new HashMap<String, JedisPool>();
private Map<Integer, JedisPool> slots = new HashMap<Integer, JedisPool>();
第一个map中 key 是 host:port (如 192.168.1.193:7000) 对应的value 是JedisPool;
第二个map中 key 是 前文说的HashSlot(16384个) 对应的value 是JedisPool;
简单来说 当jedis 执行get(key)操作时,首先通过JedisClusterCRC16.getSlot(key)求得 对应的HashSlot 然后得到对应的jedisPool,然后通过getResource()在池中获取一个jedis对象,用来操作Redis。
6. 多说了一些,管理对象首先想到的就是spring ,下边就是相关配置
交给Spring 管理的对象是 JedisCluster,该类没有getter,setter方法,所以使用构造方法注入;
public JedisCluster(Set<HostAndPort> nodes) {
this(nodes, DEFAULT_TIMEOUT);
}
public JedisCluster(Set<HostAndPort> nodes, int timeout) {
this(nodes, timeout, DEFAULT_MAX_REDIRECTIONS);
}
public JedisCluster(Set<HostAndPort> nodes, int timeout, int maxRedirections) {
this(nodes, timeout, maxRedirections, new GenericObjectPoolConfig());
}
public JedisCluster(Set<HostAndPort> nodes, final GenericObjectPoolConfig poolConfig) {
this(nodes, DEFAULT_TIMEOUT, DEFAULT_MAX_REDIRECTIONS, poolConfig);
}
public JedisCluster(Set<HostAndPort> nodes, int timeout, final GenericObjectPoolConfig poolConfig) {
this(nodes, timeout, DEFAULT_MAX_REDIRECTIONS, poolConfig);
}
public JedisCluster(Set<HostAndPort> jedisClusterNode, int timeout, int maxRedirections,
final GenericObjectPoolConfig poolConfig) {
super(jedisClusterNode, timeout, maxRedirections, poolConfig);
}
public JedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
int maxRedirections, final GenericObjectPoolConfig poolConfig) {
super(jedisClusterNode, connectionTimeout, soTimeout, maxRedirections, poolConfig);
}
如上代码 都是构造方法,可以根据需要自己设置
配置文件spring-config.xml
<!-- properties 文件位置 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:resources.properties</value>
</list>
</property>
</bean>
<!-- jedisPool 配置 简单配置,其余属性自行查找 org.apache.commons.pool2.impl.GenericObjectPoolConfig 等父类-->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxTotal" value="${pool.maxTotal}" />
<property name="maxIdle" value="${pool.maxIdle}" />
<property name="minIdle" value="${pool.minIdle}" />
</bean>
<!-- 程序中要使用的操作redis集群的对象 -->
<bean id="jedisClusterClient" class="redis.clients.jedis.JedisCluster">
<!-- 构造方法注入
节点集合(只写一个就可以,集群中其余的节点会自动加载进来) , 超时时间,最大重连次数,数据池配置对象
public JedisCluster(Set<HostAndPort> jedisClusterNode, int timeout, int maxRedirections,final GenericObjectPoolConfig poolConfig) -->
<constructor-arg name="jedisClusterNode" >
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="${redis.host}"/>
<constructor-arg name="port" value="${redis.port}"/>
</bean>
</set>
</constructor-arg>
<constructor-arg name="timeout" value="${redis.timeOut}"/>
<constructor-arg name="maxRedirections" value="${redis.maxRedirections}"/>
<constructor-arg name="poolConfig" ref="jedisPoolConfig"/>
</bean>
resources.properties
pool.maxTotal=5
pool.maxIdle=5
pool.minIdle=0
redis.timeOut= 2000
redis.maxRedirections = 5
redis.host = 192.168.1.193
redis.port = 7000
配置完成。
最后提供一个简单的Demo demo
还我的图!