Redis单机主从切换部署说明
准备工作
redis.io下载部署包 :redis-3.2.8.tar.gz
新建主从目录
make -p /usr/local/redis/master/
make -p /usr/local/redis/slave/
分别在两个目录下面安装redis
tar –zxf redis-3.2.8.tar.gz
cd redis-3.2.8
make #编译
make test #期望全部测试通过
可能出现问题:缺少gcc或者tcl组件,使用命令yum install gcc或者 yum install tcl
修改配置参数
修改master_redis配置
cd /usr/local/redis/master/redis-3.2.8/
vi redis.conf
bind 127.0.0.1 —-> bind 本机IP(绑定地址)
daemonize no —-> daemonize yes(不影响当前会话,启动过程隐藏,守护进程)
protected-mode yes —> protected-mode no(关闭保护模式,其他服务器可访问)
修改slave_redis配置
cd /usr/local/redis/slave/redis-3.2.8/
vi redis.conf
bind 127.0.0.1 —-> bind 本机IP(绑定地址)
daemonize no —-> daemonize yes(不影响当前会话,启动过程隐藏,守护进程)
protected-mode yes —> protected-mode no(关闭保护模式,其他服务器可访问)
port 6379 —> port 6380(修改端口)
slaveof master_redis所在机器IP 6379
pidfile /var/run/redis_ 6379.pid —-> pidfile /var/run/redis_ 6380.pid
redis以守护进程方式运行时,系统默认会把pid写入/var/run/redis.pid,可以通过pidfile指 定pid文件
启动master_ redis和slave_ redis并使用客户端连接
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-server /usr/local/redis/master/redis-3.2.8/redis.conf(加载配置文件)
./redis-cli -h IP -p 6379 (客户端连接master_redis)
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-server /usr/local/redis/slave/redis-3.2.8/redis.conf(加载配置文件)
./redis-cli -h IP -p 6380 (客户端连接slave_redis)
使用 info 命令查看redis主从信息
master_redis客户端连接下执行: info replication
显示:role:master connected_slaves:1
slave_redis客户端连接下执行: info replication
显示:role:slave master_ link_ status:up
测试主从复制,读写分离
master_redis客户端连接下执行:set name zhangsan
master_redis客户端连接下执行:get name 结果: zhangsan
slave_redis客户端连接下执行:get name 结果: zhangsan
slave_redis客户端连接下执行:set name lisi
(error)READONLY You can`t write against a read only slave.(slave_redis只读)配置主从切换
准备sentinel.conf配置文件
#守护进程,隐藏启动,不影响当前session
daemonize yes
#关闭保护模式,类似于防火墙的功能
protected-mode no
#sentinel 端口默认26379
port
#哨兵监控的主redis 的IP 和端口,会自动监控到slave
#sentinel monitor <master-name> <ip> <redis-port> <quorum>
#告诉sentinel去监听地址为ip:port的一个master,quorum是一个数字,指明当有多少个sentinel认为一个master失效时,master才算真正失效
sentinel monitor master1 IP 6379 1
#master被当前sentinel实例认定为“失效”的间隔时间.
#sentinel down-after-milliseconds <mastername> <millseconds>
#如果当前sentinel与master直接的通讯中(不断发送程序包,并接受响应),在指定时间内没有响应或者响应错误代码,那么当前sentinel就认为master失效
sentinel down-after-milliseconds master1 5000
#当failover(故障转移)开始后,在此时间内仍然没有触发任何failover操作,当前sentinel将会认为此次failover失败
sentinel failover-timeout master1 15000
#当新master产生时,可以同时进行slaveof到新master并进行“SYNC”(同步)的slave个数。(建议使用默认值1)
#在salve执行salveof与同步时,将会终止客户端请求。此值较大,意味着“集群”终止客户端请求的时间总和和较大.
#此值较小,意味着“集群”在故障转移期间,多个salve向客户端提供服务时仍然使用旧数据.
sentinel parallel-syncs master1 1
配置主从切换
ps -ef | grep redis kill掉当前所有redis进程
rm -f /usr/local/redis/master/redis-3.2.8/sentinel.conf
rm -f /usr/local/redis/slave/redis-3.2.8/sentinel.conf
将准备好的sentinel.conf分别放置于对应目录下面(替换刚刚删除的两个conf文件)
cd /usr/local/redis/slave/redis-3.2.8/
vi sentinel.conf 修改 port 26379 —> port 26380
重新启动主从redis
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-server /usr/local/redis/master/redis-3.2.8/redis.conf
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-server /usr/local/redis/slave/redis-3.2.8/redis.conf
启动主从redis的sentinel(哨兵)
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-sentinel /usr/local/redis/master/redis-3.2.8/sentinel.conf
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-sentinel /usr/local/redis/slave/redis-3.2.8/sentienl.conf
ps -ef | grep redis 此时应该有四个进程(redis主从 + 两个哨兵)
使用客户端查看哨兵监控情况
使用客户端连接两个sentinel
cd /usr/local/redis/master/redis-3.2.8/src/
./redis-cli -h IP 26379
cd /usr/local/redis/slave/redis-3.2.8/src/
./redis-cli -h IP 26380
使用 info sentinel查看稍定监控详情,显示name=master1,status=ok,address=IP:6379(两个哨兵共同监控master_redis)
测试主从自动切换(具体操作参考上面命令)
kill 掉master_redis服务
然后使用客户端连接slave_redis
使用info replication 查看slave_ redis连接信息,会发现,slave_ redis已经升级为master_ redis
再使用客户端重新连接sentinel,使用info sentinel命令查看两个哨兵监控信息,会发现监控地址变成了address=IP:6380
重新启动kill 掉的master_ redis服务,启动后客户端连接,使用info replication命令查看,会发现role:slave (重新启动后自动变成slave_ redis)Java代码实现redis主从
public class JedisUtil {
private final static String REDIS_HOST = "172.20.1.47";
private final static Integer REDIS_PORT = 6379;
private final static Integer REDIS_MaxActive = 200;
private final static Integer REDIS_MaxIdle = 1000;
private final static Integer REDIS_MaxWait = 512;
private final static Integer REDIS_ConnTimeout = 2000;
private final static Integer REDIS_RetryNum = 3;
private final static String SENTINEL_HOST_1 = "172.20.1.47:26381";
private final static String SENTINEL_HOST_2 = "172.20.1.47:26380";
private final static String CLUSTER_NAME = "master1";
/**
* 私有构造器.
*/
private JedisUtil() {
}
private static Map<String, JedisSentinelPool> maps = new HashMap<String, JedisSentinelPool>();
/**
* 获取连接池.
*
* @return 连接池实例
*/
private static JedisSentinelPool getPool() {
String key = REDIS_HOST + ":" + REDIS_PORT;
Set<String> sentinels = new HashSet<String>();
String hostAndPort1 = SENTINEL_HOST_1;
String hostAndPort2 = SENTINEL_HOST_2;
sentinels.add(hostAndPort1);
sentinels.add(hostAndPort2);
String clusterName = CLUSTER_NAME;
JedisSentinelPool redisSentinelJedisPool = null;
if (!maps.containsKey(key)) {
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(REDIS_MaxActive);
config.setMaxIdle(REDIS_MaxIdle);
config.setMaxWaitMillis(REDIS_MaxWait);
config.setTestOnBorrow(true);
config.setTestOnReturn(true);
try {
/**
* 如果你遇到 java.net.SocketTimeoutException: Read timed out exception的异常信息 请尝试在构造JedisPool的时候设置自己的超时值. JedisPool默认的超时时间是2秒(单位毫秒)
*/
redisSentinelJedisPool = new JedisSentinelPool(clusterName, sentinels, config, REDIS_ConnTimeout);
maps.put(key, redisSentinelJedisPool);
} catch (Exception e) {
e.printStackTrace();
}
} else {
redisSentinelJedisPool = maps.get(key);
}
return redisSentinelJedisPool;
}
/**
* 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例 没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。
*/
private static class RedisUtilHolder {
/**
* 静态初始化器,由JVM来保证线程安全
*/
private static JedisUtil instance = new JedisUtil();
}
/**
* 当getInstance方法第一次被调用的时候,它第一次读取 RedisUtilHolder.instance,导致RedisUtilHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静
* 态域,从而创建RedisUtil的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。 这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。
*/
public static JedisUtil getInstance() {
return RedisUtilHolder.instance;
}
/**
* 获取Redis实例.
*
* @return Redis工具类实例
*/
public Jedis getJedis() {
Jedis jedis = null;
int count = 0;
do {
try {
jedis = getPool().getResource();
} catch (Exception e) {
e.printStackTrace();
// 销毁对象
if (jedis != null) {
jedis.close();
}
}
count++;
} while (jedis == null && count < REDIS_RetryNum);
return jedis;
}
/**
* 释放redis实例到连接池.
*
* @param jedis redis实例
*/
public void closeJedis(Jedis jedis) {
if (jedis != null) {
jedis.close();
}
}