Spring Boot Redis集群配置,这些配置文件缺一不可!
1.第一步
引入pom文件
<!-- redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
<optional>true</optional>
</dependency>
2.第二步,在yml文件中添加配置
spring
#redis
redis:
password: 123456 #redis密码
cluster:
nodes: 192.168.130.133:7001,192.168.130.133:7002,192.168.130.133:7003,192.168.130.133:7004,192.168.130.133:7005,192.168.130.133:7006 #redis节点,有多少写多少,以,号分隔
max-redirects: 6 #集群的数量
3.第三步,增加配置类代码
代码结构
4.RedisConfig
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.annotation.Scheduled;
import redis.clients.jedis.JedisPoolConfig;
import java.util.ArrayList;
import java.util.List;
/**
* @author 裤裤的程序猿
* @date 2019/12/4 21:41
*/
@Configuration
public class RedisConfig {
private static final Logger logger = LoggerFactory.getLogger(RedisConfig.class);
@Value("${spring.redis.cluster.nodes}")
private String nodes;
@Value("${spring.redis.cluster.max-redirects}")
private Integer maxRedirects;
@Value("${spring.redis.password}")
private String password;
/**
* JedisPoolConfig 连接池
* @return
*/
@Bean
public JedisPoolConfig jedisPoolConfig(){
JedisPoolConfig jedisPoolConfig=new JedisPoolConfig();
//最大空闲数
jedisPoolConfig.setMaxIdle(300);
//连接池的最大数据库连接数
jedisPoolConfig.setMaxTotal(1000);
//最大建立连接等待时间
jedisPoolConfig.setMaxWaitMillis(1000);
//逐出连接的最小空闲时间 默认1800000毫秒(30分钟)
jedisPoolConfig.setMinEvictableIdleTimeMillis(300000);
//每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3
jedisPoolConfig.setNumTestsPerEvictionRun(10);
//逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1
jedisPoolConfig.setTimeBetweenEvictionRunsMillis(30000);
//是否在从池中取出连接前进行检验,如果检验失败,则从池中去除连接并尝试取出另一个
jedisPoolConfig.setTestOnBorrow(true);
//在空闲时检查有效性, 默认false
jedisPoolConfig.setTestWhileIdle(true);
return jedisPoolConfig;
}
/**
* 配置工厂
* @param
* @return
*/
@Bean
public RedisClusterConfiguration jedisConfig() {
RedisClusterConfiguration config = new RedisClusterConfiguration();
String[] sub = nodes.split(",");
List<RedisNode> nodeList = new ArrayList<>(sub.length);
String[] tmp;
for (String s : sub) {
tmp = s.split(":");
// fixme 先不考虑异常配置的case
nodeList.add(new RedisNode(tmp[0], Integer.valueOf(tmp[1])));
}
config.setClusterNodes(nodeList);
config.setMaxRedirects(maxRedirects);
config.setPassword(RedisPassword.of(password));
return config;
}
/**
* shiro redis缓存使用的模板
* 实例化 RedisTemplate 对象
* @return
*/
@Bean("shiroRedisTemplate")
public RedisTemplate shiroRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new SerializeUtils());
redisTemplate.setValueSerializer(new SerializeUtils());
//开启事务
//redisTemplate.setEnableTransactionSupport(true);
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
@CacheEvict(allEntries = true, cacheNames = { "manufacturedGood", "rawMaterial"})
@Scheduled(fixedDelay = 60*1000)
public void cacheEvict() {
logger.info("[RedisCacheConfig][cacheEvict] start to clear cache");
}
}
RedisManager
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisCommands;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* @author 裤裤的程序猿
* @date 2019/12/4 21:34
*/
@Component
public class RedisManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
public void expire(String key,long time){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public Boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String ... key){
if(key!=null&&key.length>0){
if(key.length==1){
redisTemplate.delete(key[0]);
}else{
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 批量删除key
* @param keys
*/
public void del(Collection keys){
redisTemplate.delete(keys);
}
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
*/
public void set(String key,Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
*/
public void set(String key,Object value,long time){
if(time>0){
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
}else{
set(key, value);
}
}
/**
* 使用scan命令 查询某些前缀的key
* @param key
* @return
*/
public Set<String> scan(String key){
Set<String> execute = this.redisTemplate.execute(new RedisCallback<Set<String>>() {
@Override
public Set<String> doInRedis(RedisConnection connection) throws DataAccessException {
Set<String> binaryKeys = new HashSet<>();
Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(key).count(1000).build());
while (cursor.hasNext()) {
binaryKeys.add(new String(cursor.next()));
}
return binaryKeys;
}
});
return execute;
}
/**
* 使用scan命令 查询某些前缀的key 有多少个
* 用来获取当前session数量,也就是在线用户
* @param key
* @return
*/
public Long scanSize(String key){
long dbSize = this.redisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection connection) throws DataAccessException {
long count = 0L;
Cursor<byte[]> cursor = connection.scan(ScanOptions.scanOptions().match(key).count(1000).build());
while (cursor.hasNext()) {
cursor.next();
count++;
}
return count;
}
});
return dbSize;
}
/**
* redis锁
*/
public static final String UNLOCK_LUA;
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
private final Logger logger = LoggerFactory.getLogger(RedisManager.class);
/**
* 设置锁
* @param key
* @param expire 毫秒
* @return
*/
public boolean setLock(String key, long expire) {
try {
RedisCallback<String> callback = (connection) -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
String uuid = UUID.randomUUID().toString();
return commands.set(key, uuid, "NX", "PX", expire);
};
String result = redisTemplate.execute(callback);
return !StringUtils.isEmpty(result);
} catch (Exception e) {
logger.error("set redis occured an exception", e);
}
return false;
}
/**
* 获取锁的requestId
* @param key
* @return
*/
public String getLock(String key) {
try {
RedisCallback<String> callback = (connection) -> {
JedisCommands commands = (JedisCommands) connection.getNativeConnection();
return commands.get(key);
};
String result = redisTemplate.execute(callback);
return result;
} catch (Exception e) {
logger.error("get redis occured an exception", e);
}
return "";
}
/**
* 释放锁
* @param key
* @param requestId
* @return
*/
public boolean releaseLock(String key,String requestId) {
// 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除
try {
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> args = new ArrayList<>();
args.add(requestId);
// 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁
// spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本
RedisCallback<Long> callback = (connection) -> {
Object nativeConnection = connection.getNativeConnection();
// 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
// 集群模式
if (nativeConnection instanceof JedisCluster) {
return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
// 单机模式
else if (nativeConnection instanceof Jedis) {
return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);
}
return 0L;
};
Long result = redisTemplate.execute(callback);
return result != null && result > 0;
} catch (Exception e) {
logger.error("release lock occured an exception", e);
}
return false;
}
}
SerializeUtils
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.io.*;
/**
* @author zhaozhuang
* @date 2019/12/4 21:32
*/
public class SerializeUtils implements RedisSerializer {
private static Logger logger = LoggerFactory.getLogger(SerializeUtils.class);
public static boolean isEmpty(byte[] data) {
return (data == null || data.length == 0);
}
/**
* 序列化
* @param object
* @return
* @throws SerializationException
*/
@Override
public byte[] serialize(Object object) throws SerializationException {
byte[] result = null;
if (object == null) {
return new byte[0];
}
try {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(128);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream);
if (!(object instanceof Serializable)) {
throw new IllegalArgumentException(SerializeUtils.class.getSimpleName() + " requires a Serializable payload " +
"but received an object of type [" + object.getClass().getName() + "]");
}
objectOutputStream.writeObject(object);
objectOutputStream.flush();
result = byteStream.toByteArray();
} catch (Exception ex) {
logger.error("Failed to serialize",ex);
}
return result;
}
/**
* 反序列化
* @param bytes
* @return
* @throws SerializationException
*/
@Override
public Object deserialize(byte[] bytes) throws SerializationException {
Object result = null;
if (isEmpty(bytes)) {
return null;
}
try {
ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
ObjectInputStream objectInputStream = new ObjectInputStream(byteStream);
result = objectInputStream.readObject();
} catch (Exception e) {
logger.error("Failed to deserialize",e);
}
return result;
}
}
到这里就配置完成了,如果有什么不懂的,可以私信我哦!