redis3.x集群
客户端分片都不会共享数据,容易造成单点缓存丢失的问题,集群会自动在多个redis节点之间共享数据,不会造成单点问题
搭建redis集群
简历redis集群,至少需要3个master
实例,同时,为了使集群高可用,需要为每个master实例至少分配一个salve
- 下载redis-3.2.6.tar.gz ,下载地址
- 将下载好的压缩包赋值到指定的服务器上
- 解压安装
cd /usr/local/src
tar -zxvf redis-3.2.6.tar.gz
cd redis-3.2.6/
make && make install
- 创建目录,赋值配置文件
cd /usr/local
mkdir cluster-test
cs cluster-test
mkdir 7000 7001 7002 7003 7004 7005
cp /usr/local/redis-3.2.6/redis.conf 7000
cd 7000
vi redis.conf
修改7000目录下的reids.conf配置
bind 指定的ip
port 指定的端口
daemonize yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
appendonly yes
之后将该配置文件分别赋值到7001-7005中,并将配置文件中的port修改为7001-7005
daemonize指定redis是否在后台运行
cluster-enable指定是否启用集群方式
cluster-config-file指定了集群配置文件,该配置文件存储集群的一些信息,例如各个节点的状态信息,这些信息是不让开发或运维人员编辑的
cluster-node-timeout表示集群中某节点超过指定时间,就认为该节点失效了
- 启动6个实例
cd /usr/local/cluster-test/7000
redis-server redis.conf
之后,分别进入7001-7005的目录,启动服务
- 下载redis-3.2.2.gem ,下载地址
- 将下载好的redis-3.2.2.gem复制到服务器
scp redis-3.2.2.gem root@ip地址:/usr/local/
- 安装redis-3.2.2.gem
yum install ruby rubygems -y
cd /usr/local
gem install redis-3.2.2.gem
- 创建集群
cd /usr/local/redis-3.2.6/src/ ./redis-trib.rb create --replicas 1 ip地址:7000 ip地址:7001 ip地址:7002 ip地址:7003 ip地址:7004 ip地址:7005
使用了redis-3.2.6/src/下的redis-trib.rb命令来创建集群,其中--replicas 1
指定一个master有一个slave,之后是6个reids实例,这就是三主三从,执行命令后,显示如下信息表示成功
[ok] All nodes agree about slots configuration
- 使用redis-desktop-manager软件连接redis
spring boot 集成 JedisCluster
使用Jedis的JedisCluster操作集群版的redis
pom.xml
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.15</version>
</dependency>
在application.properties中配置redis集群信息
#配置集群信息
redis.cluster.servers=ip:port,...
#连接超时时间connectionTimeout,也是读取超时时间soTimeout
redis.cluster.commandTimeout=5000
spring boot中集成jedisCluster , 编写JedisClusterConfig代码
package com.stscode.common.configure;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class JedisClusterConfig {
@Autowired
private Environment env;
@Bean
public JedisCluster jedisCluster(){
String[] ipAddress = env.getProperty("redis.cluster.servers").split(",");
Set<HostAndPort> hostAndPortSet = new HashSet<HostAndPort>();
for (String address : ipAddress) {
String[] ipAndPort = address.split(":");
hostAndPortSet.add(new HostAndPort(ipAndPort[0].trim() , Integer.valueOf(ipAndPort[1])));
}
return new JedisCluster(hostAndPortSet , Integer.valueOf(env.getProperty("redis.cluster.commandTimeout")));
}
}
该类是一个核心类,通过集群的server节点集合和读取超时时间创建了一个JedisCluster单例,之后直接使用该实例直接操作Redis
接下来编写业务代码
package com.stscode.service.impl;
import com.alibaba.fastjson.JSON;
import com.stscode.common.configure.DbAndCacheContants;
import com.stscode.common.configure.utils.RedisUtils;
import com.stscode.dao.IUserDao;
import com.stscode.dao.impl.UserDaoImpl;
import com.stscode.domain.User;
import com.stscode.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
@Service("userService")
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Autowired
private JedisCluster jedisCluster;
@Override
public User selectByPrimaryKey(Long id) {
return userDao.selectByPrimaryKey(id);
}
/**
*
* @param id
* @return user
*/
@Override
public User getUser(Long id){
User user = null;
String userAsString = jedisCluster.get(DbAndCacheContants.USER_CACHE_PREFIX + id);
if(StringUtils.isNotBlank(userAsString)){
user = JSON.parseObject(userAsString , User.class);
return user;
}
user = userDao.selectByPrimaryKey(id);
if(user!=null){
jedisCluster.set(DbAndCacheContants.USER_CACHE_PREFIX + id , JSON.toJSONString(user));
}
return user;
}
}
该方法中不需要担心资源的释放问题了,在jedisCluster.get(String key)源码中可以看到其中finally中释放了资源(内部首先根据key获取一个Jedis,之后进行redis的get操作,执行完毕后,会将该jedis释放掉,所以不需要获取jedis,也不需要释放jedis)
使用 GuavaCache实现本地缓存
使用本地缓存: GoogleGuavaCache
pom.xml中引入
<!-- guava cache -->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>14.0.1</version>
</dependency>
在业务逻辑中使用本地缓存获取对象数据
package com.stscode.service.impl;
import com.alibaba.fastjson.JSON;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.stscode.common.configure.DbAndCacheContants;
import com.stscode.common.configure.utils.RedisUtils;
import com.stscode.dao.IUserDao;
import com.stscode.dao.impl.UserDaoImpl;
import com.stscode.domain.User;
import com.stscode.service.IUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@Service("userService")
public class UserServiceImpl implements IUserService {
@Autowired
private IUserDao userDao;
@Autowired
private RedisUtils redisUtils;
@Autowired
private JedisCluster jedisCluster;
/*
设置本地缓存对象
*/
LoadingCache<String , User> userCache = CacheBuilder.newBuilder()
.expireAfterWrite(20 , TimeUnit.MINUTES) //最久缓存20分钟
.maximumSize(1000) //设置最多缓存多少个数据
.build(new CacheLoader<String, User>() {
@Override
/*
如果本地缓存中没有user数据,
则会调用该load方法去从数据库获取,
然后自动存储到userCache中
*/
public User load(String s) throws Exception {
User user = null;
//切割key,并将id作为参数传入查询,并返回数据
user = userDao.selectByPrimaryKey(Long.valueOf(s.split(":")[1]));
if (user == null){
//如果user为空',则创建空user对象
user = new User();
}
return user; //返回
}
});
@Override
public User selectByPrimaryKey(Long id) {
return userDao.selectByPrimaryKey(id);
}
/**
*
* @param id
* @return user
*/
public User getUserForCache(Long id){
User user = null;
try {
//从本地缓存中获取数据,如果没有获取到,则调用内部的load方法从数据库中查询
user = userCache.get(DbAndCacheContants.USER_CACHE_PREFIX + id);
}catch(Exception ex){
ex.printStackTrace();
}
return user;
}
}
执行load方法时,会从数据库中获取信息,然后自动存储到userCache中,之后再从userCache中获取user数据,传给调用方,如果使用redis,我们从数据库获取数据后,还需要手动插入到Redis