前言
某web项目中需要快速存取部分非结构化数据,对数据的安全性要求不高,同时由于web项目有多台服务器同时提供服务,并通过nginx负载均衡,需要保证客户端从任意一台服务器中均能读取到完整的数据。因此不能简单的在每台服务器中安装redis缓存,同时为避免单点故障,需要部署多台redis服务组成主从集群,而默认的主从配置仅仅保证了数据的复制与安全,并没有提供节点失败时的请求转移,因此需要配合redis-sentinel服务。服务的安装与配置
Redis的安装
下载最新的redis.tar.gz包,解压后./configure即可。 若需要tcl包的话,再另外编译安装就行。
make
make install
Redis的主从配置
假设两台主机:192.168.0.1(主), 192.168.0.2(从)。则分别在两台主机安装redis后,编辑redis.conf 主服务器无需额外配置,从服务器增加slaveof=192.168.0.1 6379配置项,这样该从服务器上的redis会自动连接主服务器,并拷贝副本。
Sentinel的配置
当redis主从分配启动完成后,即可配置sentinel服务,编辑sentinel.conf文件,制定主服务器即可sentinel monitor mymaster 192.168.0.1 6379 1其中1代表投票的阈值,具体解释详见文档。
运行效果
实际运行例子中ip可能和上文不同,实例分别10.2.230.21和10.2.230.22启动服务
主、从Redis服务启动
1、主服务器启动2、从服务器启动
可以看到,从服务器自动从主服务器中同步了信息。 此时通过redis-cli查看info信息:redis-cli -h 'ip' info Replication 可以看到master服务中标明了slave的节点的数量,而从节点则标明了master的运行状态
Sentinel启动
此时启动sentinel服务从中可以看到master为10.2.230.21节点。
主从切换
1、手动关闭主节点可以看到sentinel将master切换为了10.2.230.22,而从节点切换为10.2.230.21 info查看22节点的信息,可以看到输出也是主节点的信息,此时从节点连接数为0(21还未恢复服务)。
2、重新开启主节点
可以看到sentinel查找到了21的redis服务
此时的info信息如下,可以看到21和22节点的主从身份已互换。
Java客户端Jedis代码编写
Jedis自带的JedisSentinelPool即可使用Sentinel架构下的Redis缓存服务。获取Jedis资源的代码如下: 第一步:建立连接池Set<String> IPS = new HashMap<String>();第二步:获取Jedis连接
IPS.add("192.168.0.1:26379");
JedisSentinelPool pool = new JedisSentinelPool("mymaster", IPS);
Jedis jedis = pool.getResource();第三步:使用Jedis缓存(在使用结束后记得关闭链接)
Jedis jedis=null;
try{
jedis = pool.getResource();
jedis.set("foo", "bar");
System.out.println(jedis.get("foo"));
} catch(Exception e){
logger.error(e);
} finally {
if(jedis != null)
jedis.close();
}
注意事项
新版本的redis中slave节点是只读的,因此对于缓存的读写操作不能仅仅通过pool.getResource()来获取Jedis实例进行读写,写操作需要保证自己连接的是master节点。由于不知道JedisSentinelPool有没有直接获取Master节点的功能,我目前的方法如下,通过直接指定ip和port进行连接,如果有更好的方法,欢迎各位回复。public synchronized String set(String key, String value) throws Exception{
Jedis jedis = null;
try {
String hostIp = pool.getCurrentHostMaster().getHost();
int hostPort = pool.getCurrentHostMaster().getPort();
WifiLogUtil.wifiLogInfo("Jedis set", "Server >> ["+hostIp+":"+hostPort+"]");
jedis =new Jedis(hostIp, hostPort);
if(value == null)
return jedis.set(key, value);
return jedis.set(key, new String(value.getBytes(), "UTF-8"));
} catch (Exception e) {
WifiLogUtil.wifiLogError(e, "jedis set", "set jedis error : "+e);
throw e;
} finally {
if(jedis != null)
jedis.close();
}
}