现在web项目无处不在使用缓存技术,redis的身影可谓无处不在。但是又有多少项目使用到的是redis的集群?大概很多项目只是用到单机版的redis吧。作为缓存的一块,set ,get数据。用的不亦乐乎。但是对于高可用系统来说,数据集群是很有必要的。
我们看单机版的redis配置。
springBoot引入maven依赖
-
<dependency>
-
<groupId>org.springframework.boot
</groupId>
-
<artifactId>spring-boot-starter-cache
</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>org.springframework.boot
</groupId>
-
<artifactId>spring-boot-starter-data-redis
</artifactId>
-
</dependency>
-
<dependency>
-
<groupId>com.google.code.gson
</groupId>
-
<artifactId>gson
</artifactId>
-
</dependency>
-
-
<dependency>
-
<groupId>org.springframework.session
</groupId>
-
<artifactId>spring-session-data-redis
</artifactId>
-
</dependency>
-
-
<dependency>
-
<groupId>redis.clients
</groupId>
-
<artifactId>jedis
</artifactId>
-
</dependency>
配置springboot yml文件 redis连接
-
spring:
-
redis:
-
host: 127.0.0.1
-
port: 6379
-
password: 123456
-
maxIdle: 20
-
minIdle: 10
-
maxTotal: 100
-
database: 2
-
busiDb: 9
-
boeDb: 2
-
eximportDb: 5
-
session:
-
store-type: redis
-
cache:
-
type: redis
操作redis的工具类:
-
import java.util.ArrayList;
-
import java.util.Calendar;
-
import java.util.Collection;
-
import java.util.Collections;
-
import java.util.LinkedHashMap;
-
import java.util.List;
-
import java.util.Map;
-
import java.util.Set;
-
import java.util.concurrent.TimeUnit;
-
-
import javax.annotation.Resource;
-
-
import org.apache.commons.lang.StringUtils;
-
import org.slf4j.Logger;
-
import org.slf4j.LoggerFactory;
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
-
import org.springframework.dao.DataAccessException;
-
import org.springframework.data.redis.connection.RedisConnection;
-
import org.springframework.data.redis.core.RedisCallback;
-
import org.springframework.data.redis.core.RedisTemplate;
-
import org.springframework.data.redis.core.ValueOperations;
-
import org.springframework.stereotype.Component;
-
-
import com.google.gson.Gson;
-
-
import cn.ctg.common.enums.EnumType;
-
import cn.ctg.common.util.constants.Constant;
-
import cn.hutool.core.convert.Convert;
-
import cn.hutool.core.thread.ThreadUtil;
-
import cn.hutool.core.util.RandomUtil;
-
import cn.hutool.core.util.StrUtil;
-
import redis.clients.jedis.Jedis;
-
import redis.clients.jedis.JedisCluster;
-
-
/**
-
* Redis工具类
-
*
-
*/
-
@Component
-
public
class
RedisUtils {
-
private
final
Logger
logger
= LoggerFactory.getLogger(
this.getClass());
-
@Autowired
-
private RedisTemplate redisTemplate;
-
-
@Resource(name = "redisTemplate")
-
private ValueOperations<String, String> valueOperations;
-
-
@Autowired
-
private RedisExtendService redisExtendService;
-
-
/** 加分布式锁的LUA脚本 */
-
private
static
final
String
LOCK_LUA
=
-
"if redis.call('setNx',KEYS[1],ARGV[1])==1 then return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end";
-
-
/** 计数器的LUA脚本 */
-
private
static
final
String
INCR_LUA
=
-
"local current = redis.call('incr',KEYS[1]);" +
-
" local t = redis.call('ttl',KEYS[1]); " +
-
"if t == -1 then " +
-
"redis.call('expire',KEYS[1],ARGV[1]) " +
-
"end; " +
-
"return current";
-
-
/** 解锁的LUA脚本 */
-
private
static
final
String
UNLOCK_LUA
=
-
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
-
private
static
final
Long
SUCCESS
=
1L;
-
-
/** 互斥锁过期时间(分钟) */
-
private
static
final
long
MUTEX_WAIT_MILLISECONDS
=
50;
-
/** 编号规则生成线程等待次数 ((10 * 1000) / 50) + 1 */
-
public
static
final
long
RULE_CODE_THREAD_WAIT_COUNT
=
200;
-
/** 互斥锁等待时间(毫秒) */
-
private
static
final
long
MUTEX_EXPIRE_MINUTES
=
3;
-
-
/**
-
* 不设置过期时长
-
*/
-
public
final
static
long
NOT_EXPIRE
= -
1;
-
-
/**
-
* 默认过期时长,单位:秒
-
*/
-
public
final
static
long
DEFAULT_EXPIRE
=
7200;
// 2小时
-
-
/**
-
* 会员卡缓存失效时间 2小时
-
*/
-
public
final
static
long
CARD_DEFAULT_EXPIRE
=
7200;
-
-
/**
-
* 默认过期时长,1天
-
*/
-
public
final
static
long
DEFAULT_A_DAY
=
86400;
-
-
/**
-
* 默认过期时长,1分钟
-
*/
-
public
final
static
long
DEFAULT_A_MIN
=
60 ;
-
-
/**
-
* 默认过期时长,2分钟
-
*/
-
public
final
static
long
DEFAULT_TWO_MIN
=
120 ;
-
-
/**
-
* 保存数据
-
*
-
* @param key
-
* @param value
-
* @param expire 过期时间,单位s
-
*/
-
public
void
set
(String key, Object value, long expire) {
-
String
valueJson
= toJson(value);
-
valueOperations.set(key, valueJson);
-
if (expire != NOT_EXPIRE) {
-
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
-
}
-
redisExtendService.redisDataChange(key, valueJson, EnumType.CRUD_TYPE.CREATE.getValue());
-
}
-
-
/**
-
* 判断key是否存在
-
*
-
* @param key
-
*/
-
public Boolean
hasKey
(String key) {
-
if (StringUtils.isNotBlank(key)) {
-
return valueOperations.getOperations().hasKey(key);
-
}
-
return Boolean.FALSE;
-
}
-
-
/**
-
* @param key
-
* @param value
-
*/
-
public
void
set
(String key, Object value) {
-
set(key, value, NOT_EXPIRE);
-
}
-
-
public <T> T
get
(String key, Class<T> clazz, long expire) {
-
String
value
= Convert.toStr(valueOperations.get(key));
-
if (expire != NOT_EXPIRE) {
-
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
-
}
-
return value ==
null ?
null : fromJson(value, clazz);
-
}
-
-
/**
-
* 批量从Redis中获取数据
-
*
-
* @param valueMap 需要存储的数据集合
-
* @param expire 过期时间,秒
-
* @return java.util.List<T> 返回值
-
*/
-
public
void
batchSet
(Map<String, String> valueMap, long expire) {
-
valueOperations.multiSet(valueMap);
-
if (expire != NOT_EXPIRE) {
-
for (String key : valueMap.keySet()) {
-
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
-
}
-
}
-
}
-
-
/**
-
* 批量删除
-
*
-
* @param keys 需要删除的KEY集合
-
* @return void
-
*/
-
public
void
batchDelete
(Collection<String> keys) {
-
redisTemplate.delete(keys);
-
}
-
-
/**
-
* 批量从Redis中获取数据
-
*
-
* @param keyList 需要获取的Key集合
-
* @param clazz 需要转换的类型
-
* @return java.util.List<T> 返回值
-
*/
-
public <T> Map<String, T>
batchGet
(List<String> keyList, Class<T> clazz) {
-
List<String> objectList = valueOperations.multiGet(keyList);
-
Map<String, T> map =
new
LinkedHashMap<>(objectList.size());
-
for (
int
i
=
0; i < keyList.size(); i++) {
-
String
value
= Convert.toStr(objectList.get(i));
-
if (!String.class.equals(clazz)) {
-
map.put(keyList.get(i), fromJson(value, clazz));
-
}
else {
-
map.put(keyList.get(i), (T)value);
-
}
-
}
-
return map;
-
}
-
-
public <T> T
get
(String key, Class<T> clazz) {
-
return get(key, clazz, NOT_EXPIRE);
-
}
-
-
/**
-
* 使用 父编码+当前编码获取+集团+语言 获取名称
-
*
-
* @param code 父编码
-
* @param dictCode 当前编码
-
* @param language 语言 UserUtils.getLanguage()
-
* @param groupId 集团ID
-
*/
-
public String
get
(String code, String dictCode, String language, String groupId) {
-
if (StringUtils.isBlank(dictCode)) {
-
return
"";
-
}
-
String
key
= RedisKeys.getSysDictKey(code, dictCode, language, groupId);
-
return get(key, NOT_EXPIRE);
-
}
-
-
public String
get
(String key, long expire) {
-
String
value
= Convert.toStr(valueOperations.get(key));
-
if (expire != NOT_EXPIRE) {
-
redisTemplate.expire(key, expire, TimeUnit.SECONDS);
-
}
-
return value;
-
}
-
-
public String
get
(String key) {
-
return get(key, NOT_EXPIRE);
-
}
-
-
public
void
delete
(String key) {
-
redisTemplate.delete(key);
-
redisExtendService.redisDataChange(key,
"", EnumType.CRUD_TYPE.DELETE.getValue());
-
}
-
-
/**
-
* Object转成JSON数据
-
*/
-
private String
toJson
(Object object) {
-
if (object
instanceof Integer || object
instanceof Long || object
instanceof Float || object
instanceof Double
-
|| object
instanceof Boolean || object
instanceof String) {
-
return String.valueOf(object);
-
}
-
return
new
Gson().toJson(object);
-
}
-
-
/**
-
* JSON数据,转成Object
-
*/
-
private <T> T
fromJson
(String json, Class<T> clazz) {
-
return
new
Gson().fromJson(json, clazz);
-
}
-
-
/**
-
* 获取分布式锁,默认过期时间3分钟
-
*
-
* @param key 锁的KEY
-
* @return java.lang.Boolean true为获取到锁,可以下一步业务, false为没有获取到锁
-
*/
-
public Boolean
setMutexLock
(String key) {
-
return setMutexLockAndExpire(key, getMutexLockExpireMinutes(), TimeUnit.MINUTES);
-
}
-
-
/**
-
* 获取分布式锁,带Redis事务
-
*
-
* @param key 锁的KEY
-
* @param timeout 锁时效时间,默认单位:秒
-
* @param unit 锁失效时间单位,为null则默认秒
-
* @return java.lang.Boolean true为获取到锁,可以下一步业务, false为没有获取到锁
-
*/
-
public Boolean
setMutexLockAndExpire
(String key, long timeout, TimeUnit unit) {
-
return setMutexLockAndExpire(key, Constant.RESULT_1, timeout, unit);
-
}
-
-
/**
-
* 获取分布式锁,带Redis事务
-
* 适用于同一业务,不同的请求用不同的锁,把value当成
-
* @param key 锁的KEY
-
* @param value 锁的值,一定要跟解锁的值一样,否则会导致无法解锁
-
* @param timeout 锁时效时间,默认单位:秒
-
* @param unit 锁失效时间单位,为null则默认秒
-
* @return java.lang.Boolean true为获取到锁,可以下一步业务, false为没有获取到锁
-
*/
-
public Boolean
setMutexLockAndExpire
(String key, String value, long timeout, TimeUnit unit) {
-
value = StrUtil.appendIfMissing(StrUtil.prependIfMissing(value,
"\""),
"\"");
-
Long
result
= executeLua(key, value, LOCK_LUA, timeout, unit, Long.class);
-
return SUCCESS.equals(result);
-
}
-
-
/**
-
* 解锁
-
*
-
* @param key 锁的Key
-
* @return boolean
-
*/
-
public
boolean
unlock
(String key) {
-
return unlock(key, Constant.RESULT_1);
-
}
-
-
/**
-
* 解锁
-
*
-
* @param key 锁的Key
-
* @param value 锁的value,一定要跟加锁的value一致,否则会认为不是同一个锁,不会释放
-
* @return boolean
-
*/
-
public
boolean
unlock
(String key, String value) {
-
value = StrUtil.appendIfMissing(StrUtil.prependIfMissing(value,
"\""),
"\"");
-
-
Long
result
= executeLua(key, value, UNLOCK_LUA,
null,
null, Long.class);
-
-
return SUCCESS.equals(result);
-
}
-
-
/**
-
* 获取等待锁,如果没有获取到锁就一直等待获取,直到超过waitTime的时间
-
*
-
* @param key 锁的key
-
* @param timeout 锁的超时时间
-
* @param unit 锁的超时时间单位
-
* @param waitTime 获取锁时的等待时间,一直等不超时则填-1,单位:毫秒
-
* @return java.lang.Boolean true为获取到锁,可以下一步业务, false为没有获取到锁
-
*/
-
public Boolean
setMutexWaitLock
(String key, long timeout, TimeUnit unit, long waitTime) {
-
long
start
= System.currentTimeMillis();
-
while (
true) {
-
boolean
result
= setMutexLockAndExpire(key, timeout, unit);
-
if (result) {
-
return
true;
-
}
else {
-
long
current
= System.currentTimeMillis();
-
// 超过等待时间还没获取到锁则返回false
-
if (waitTime >
0 && (current - start > waitTime)) {
-
logger.warn(
"redis分布式锁获取失败,key[{}],等待时间[{}]", key, waitTime);
-
return
false;
-
}
-
// 等待100毫秒后重试
-
ThreadUtil.sleep(
100);
-
}
-
}
-
}
-
-
public
long
getMutexLockExpireMinutes
() {
-
return MUTEX_EXPIRE_MINUTES;
-
}
-
-
/**
-
* 获取自增序列号
-
*
-
* @param key 序列号的KEY
-
* @param seq 自增值,默认自增1
-
* @return java.lang.Long 自增后的值
-
*/
-
public Long
incr
(String key, Long seq, long timeout, TimeUnit unit) {
-
return executeLua(key,
null, INCR_LUA, timeout, unit, Long.class);
-
}
-
-
/**
-
* 执行LUA脚本
-
*
-
* @param key redisKey
-
* @param value 值
-
* @param lua lua脚本
-
* @param timeout 超时时间
-
* @param unit 超时单位
-
* @param clazz 返回值类型
-
* @return T 返回值
-
*/
-
public <T> T
executeLua
(String key, Object value, String lua, Long timeout, TimeUnit unit, Class<T> clazz){
-
// 有时间单位则转成秒,否则默认秒
-
if (unit !=
null) {
-
timeout = unit.toSeconds(timeout);
-
}
-
List<String> args =
new
ArrayList<>(
2);
-
if(value !=
null){
-
args.add(Convert.toStr(value));
-
}
-
if(timeout !=
null){
-
args.add(Convert.toStr(timeout));
-
}
-
//spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本异常,此处拿到原redis的connection执行脚本
-
T
result
= (T)redisTemplate.execute(
new
RedisCallback<T>() {
-
@Override
-
public T
doInRedis
(RedisConnection connection)
throws DataAccessException {
-
Object
nativeConnection
= connection.getNativeConnection();
-
// 集群模式和单点模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行
-
// 集群
-
if (nativeConnection
instanceof JedisCluster) {
-
return (T) ((JedisCluster) nativeConnection).eval(lua, Collections.singletonList(key), args);
-
}
-
-
// 单点
-
else
if (nativeConnection
instanceof RedisProperties.Jedis) {
-
return (T) ((Jedis) nativeConnection).eval(lua, Collections.singletonList(key), args);
-
}
-
return
null;
-
}
-
});
-
return result;
-
}
-
-
-
-
public
void
expire
(String key, long timeout, TimeUnit unit) {
-
try {
-
redisTemplate.expire(key, timeout, unit);
-
}
catch (Exception e) {
-
logger.error(
"设置缓存过期时间失败,key={},timeout={},unit={}", key, timeout, unit, e);
-
}
-
}
-
-
/**
-
* 获取互斥线程等待时间
-
*
-
* @return
-
*/
-
public
long
getMutexThreadWaitMilliseconds
() {
-
return MUTEX_WAIT_MILLISECONDS;
-
}
-
-
public Set<String>
getKeys
(String key) {
-
return redisTemplate.keys(key);
-
}
-
-
/**
-
* 获取随机秒数 如:getRandomTime(30, 7)返回30天到第37天的随机秒数,即时效时间最小为30天,最大为37天
-
*
-
* @param afterDays N天之后
-
* @param rangeDay 日期范围
-
* @return java.lang.Long 秒数
-
*/
-
public
static Long
getRandomTime
(int afterDays, int rangeDay) {
-
Calendar
calendar
= Calendar.getInstance();
-
long
curTime
= calendar.getTimeInMillis();
-
calendar.add(Calendar.DAY_OF_MONTH, afterDays);
-
long
minTime
= calendar.getTimeInMillis();
-
calendar.add(Calendar.DAY_OF_MONTH, rangeDay);
-
long
maxTime
= calendar.getTimeInMillis();
-
long
randomTime
= RandomUtil.randomLong(minTime, maxTime);
-
return (randomTime - curTime) /
1000;
-
}
-
-
/**
-
* 获取30天内的随机秒数
-
*
-
* @return long 返回1天后30天内的随机秒数
-
*/
-
public
static
long
getRandomTime
() {
-
return getRandomTime(
1,
30);
-
}
-
-
-
public
void
setnx
(String key,String value){
-
-
-
}
-
-
-
}
说说使用集群的情况。
redis的主从复制。
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(Master),后者称为从节点(Slave);数据的复制是单向的,只能由主节点到从节点。
默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。
redis哨兵模式。
哨兵(sentinel):是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的 Master 并将所有 Slave 连接到新的 Master。所以整个运行哨兵的集群的数量不得少于3个节点。
选三台redis搭建集群
添加springBootmaven依赖
-
<!--redis-->
-
<dependency>
-
<groupId>org.springframework.boot
</groupId>
-
<artifactId>spring-boot-starter-data-redis
</artifactId>
-
</dependency>
-
<!--连接池依赖-->
-
<dependency>
-
<groupId>org.apache.commons
</groupId>
-
<artifactId>commons-pool2
</artifactId>
-
</dependency>
-
<!--web-->
-
<dependency>
-
<groupId>org.springframework.boot
</groupId>
-
<artifactId>spring-boot-starter-web
</artifactId>
-
</dependency>
-
<!--lombok-->
-
<dependency>
-
<groupId>org.projectlombok
</groupId>
-
<artifactId>lombok
</artifactId>
-
<version>1.18.24
</version>
-
</dependency>
集群yml文件配置
-
server:
-
port: 3035
-
spring:
-
redis:
-
# redis哨兵配置
-
sentinel:
-
# 主节点名称
-
master: mymaster
-
nodes:
-
- 192.168.200.150:6379
-
- 192.168.200.150:6380
-
- 192.168.200.150:6381
-
# # 集群的部署方式
-
# cluster:
-
# nodes:
-
# - 192.168.200.150:6379
-
# - 192.168.200.150:6380
-
# - 192.168.200.150:6381
-
# # #最大重定向次数(由于集群中数据存储在多个节点,所以在访问数据时需要通过转发进行数据定位)
-
# max-redirects: 2
-
# lettuce:
-
# pool:
-
# max-idle: 10 # 连接池中的最大空闲连接
-
# max-wait: 500 # 连接池最大阻塞等待时间(使用负值表示没有限制)
-
# max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
-
# min-idle: 0 # 连接池中的最小空闲连接
-
-
# 服务应用名
-
application:
-
name: redis-cluster
-
logging:
-
pattern:
-
console: '%date{yyyy-MM-dd HH:mm:ss.SSS} | %highlight(%5level) [%green(%16.16thread)] %clr(%-50.50logger{49}){cyan} %4line -| %highlight(%msg%n)'
-
level:
-
root: info
-
io.lettuce.core: debug
-
org.springframework.data.redis: debug
配置读写分离
-
import com.fasterxml.jackson.annotation.JsonAutoDetect;
-
import com.fasterxml.jackson.annotation.PropertyAccessor;
-
import com.fasterxml.jackson.databind.ObjectMapper;
-
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
-
import io.lettuce.core.ReadFrom;
-
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
-
import org.springframework.context.annotation.Bean;
-
import org.springframework.context.annotation.Configuration;
-
import org.springframework.context.annotation.Primary;
-
import org.springframework.data.redis.connection.RedisConnectionFactory;
-
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
-
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
-
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
-
import org.springframework.data.redis.core.RedisTemplate;
-
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
-
import org.springframework.data.redis.serializer.StringRedisSerializer;
-
-
import java.text.SimpleDateFormat;
-
import java.util.HashSet;
-
-
-
@Configuration
-
public
class
RedisConfiguration {
-
-
/**
-
*
-
* 配置redis序列化json
-
* @param redisConnectionFactory
-
* @return
-
*/
-
@Bean
-
@Primary
//若有相同类型的Bean时,优先使用此注解标注的Bean
-
public RedisTemplate<String, Object>
redisTemplate
(RedisConnectionFactory redisConnectionFactory) {
-
// 为了开发方便,一般直接使用<String, Object>
-
RedisTemplate<String, Object> template =
new
RedisTemplate<>();
-
template.setConnectionFactory(redisConnectionFactory);
-
-
// 配置具体的序列化方式
-
// JSON解析任意对象
-
Jackson2JsonRedisSerializer
jackson2JsonRedisSerializer
=
new
Jackson2JsonRedisSerializer(Object.class);
-
ObjectMapper
om
=
new
ObjectMapper();
-
// 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public
-
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
-
// 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常
-
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
-
// 设置日期格式
-
om.setDateFormat(
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm:ss"));
-
jackson2JsonRedisSerializer.setObjectMapper(om);
-
// String的序列化
-
StringRedisSerializer
stringRedisSerializer
=
new
StringRedisSerializer();
-
-
-
//key采用String的序列化
-
template.setKeySerializer(stringRedisSerializer);
-
//hash的key也采用String的序列化
-
template.setHashKeySerializer(stringRedisSerializer);
-
//value的序列化方式采用jackson
-
template.setValueSerializer(jackson2JsonRedisSerializer);
-
//hash的value序列化方式采用jackson
-
template.setHashValueSerializer(jackson2JsonRedisSerializer);
-
//设置所有配置
-
template.afterPropertiesSet();
-
-
return template;
-
}
-
-
/**
-
* 配置读写分离
-
* @param redisProperties
-
* @return
-
*/
-
@Bean
-
public RedisConnectionFactory
lettuceConnectionFactory
(RedisProperties redisProperties) {
-
// 配置哨兵节点以及主节点
-
RedisSentinelConfiguration
redisSentinelConfiguration
=
new
RedisSentinelConfiguration(
-
redisProperties.getSentinel().getMaster(),
new
HashSet<>(redisProperties.getSentinel().getNodes())
-
);
-
-
// 配置读写分离
-
LettucePoolingClientConfiguration
lettuceClientConfiguration
= LettucePoolingClientConfiguration.builder()
-
// 读写分离,这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择
-
// MASTER 仅读取主节点
-
// MASTER_PREFERRED 优先读取主节点,如果主节点不可用,则读取从节点
-
// REPLICA_PREFERRED 优先读取从节点,如果从节点不可用,则读取主节点
-
// REPLICA 仅读取从节点
-
// NEAREST 从最近节点读取
-
// ANY 从任意一个从节点读取
-
.readFrom(ReadFrom.REPLICA_PREFERRED)
-
.build();
-
-
return
new
LettuceConnectionFactory(redisSentinelConfiguration, lettuceClientConfiguration);
-
}
-
-
}
再使用工具类操作就行。
-
-
package com.vinjcent.utils;
-
-
import org.springframework.beans.factory.annotation.Autowired;
-
import org.springframework.data.redis.core.RedisTemplate;
-
import org.springframework.stereotype.Component;
-
-
import java.time.LocalDateTime;
-
import java.time.ZoneOffset;
-
import java.time.format.DateTimeFormatter;
-
import java.util.Arrays;
-
import java.util.List;
-
import java.util.Map;
-
import java.util.Set;
-
import java.util.concurrent.TimeUnit;
-
-
-
@Component
-
public
final
class
RedisUtils {
-
-
private
final RedisTemplate<String, Object> redisTemplate;
-
-
/**
-
* 可按自己需求生成"起始时间戳"
-
*/
-
private
static
final
long
BEGIN_TIMESTAMP
=
1640995200L;
-
-
/**
-
* 用于时间戳左移32位
-
*/
-
public
static
final
int
MOVE_BITS
=
32;
-
-
@Autowired
-
public
RedisUtils
(RedisTemplate<String, Object> redisTemplate) {
-
this.redisTemplate = redisTemplate;
-
}
-
-
//=============================common===================================
-
-
/**
-
* 指定缓存失效时间
-
* @param key 键
-
* @param time 时间(秒)
-
* @return whether the key has expired
-
*/
-
public
boolean
expire
(String key, long time){
-
try {
-
if(time >
0){
-
redisTemplate.expire(key, time, TimeUnit.SECONDS);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 指定缓存失效时间(自定义时间单位)
-
* @param key 键
-
* @param time 时间(秒)
-
* @return whether the key has expired
-
*/
-
public
boolean
expire
(String key, long time, TimeUnit unit){
-
try {
-
if(time >
0){
-
redisTemplate.expire(key, time, unit);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 根据key获取过期时间(默认获取的是秒单位)
-
* @param key 键(不能为null)
-
* @return the remaining time, "0" means never expire
-
*/
-
public
long
getExpire
(String key){
-
Long
time
= redisTemplate.getExpire(key, TimeUnit.SECONDS);
-
if (time !=
null) {
-
return time;
-
}
-
return -
1L;
-
}
-
-
/**
-
* 根据key获取过期时间(自定义时间单位)
-
* @param key 键(不能为null)
-
* @return the remaining time, "0" means never expire
-
*/
-
public
long
getExpire
(String key, TimeUnit unit){
-
Long
time
= redisTemplate.getExpire(key, unit);
-
if (time !=
null) {
-
return time;
-
}
-
return -
1L;
-
}
-
-
/**
-
* 判断key是否存在
-
* @param key 键
-
* @return whether the key exist
-
*/
-
public
boolean
hasKey
(String key) {
-
Boolean
flag
= redisTemplate.hasKey(key);
-
try {
-
return Boolean.TRUE.equals(flag);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 删除缓存
-
* @param key 键,可以传递一个值或多个
-
*/
-
public
void
del
(String... key) {
-
if(key !=
null && key.length >
0){
-
if (key.length ==
1){
-
redisTemplate.delete(key[
0]);
-
}
else {
-
redisTemplate.delete(Arrays.asList(key));
-
}
-
}
-
}
-
-
-
//=============================String===================================
-
-
/**
-
* 普通缓存获取(泛型)
-
* @param key key键
-
* @return the value corresponding the key
-
*/
-
public Object
get
(String key){
return key ==
null ?
null : redisTemplate.opsForValue().get(key);}
-
-
-
/**
-
* 普通缓存获取(泛型)
-
* @param key key键
-
* @return the value corresponding the key
-
* @param targetType 目标类型
-
* @param <T> 目标类型参数
-
* @return the generic value corresponding the key
-
*/
-
public <T> T
get
(String key, Class<T> targetType){
return key ==
null ?
null : JsonUtils.objParse(redisTemplate.opsForValue().get(key), targetType);}
-
-
/**
-
* 普通缓存放入
-
* @param key 键
-
* @param value 值
-
* @return whether true or false
-
*/
-
public
boolean
set
(String key, Object value){
-
try {
-
redisTemplate.opsForValue().set(key,value);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 普通缓存放入并设置时间
-
* @param key 键
-
* @param value 值
-
* @param time 时间(秒) --- time要大于0,如果time小于0,将设置为无期限
-
* @return whether true or false
-
*/
-
public
boolean
set
(String key, Object value, long time){
-
try {
-
if(time >
0){
-
redisTemplate.opsForValue().set(key,value,time,TimeUnit.SECONDS);
-
}
else {
-
set(key,value);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 普通缓存放入并设置时间和时间单位
-
* @param key 键
-
* @param value 值
-
* @param time 时间(秒) --- time要大于0,如果time小于0,将设置为无期限
-
* @param timeUnit 时间单位
-
* @return whether true or false
-
*/
-
public
boolean
set
(String key, Object value, long time, TimeUnit timeUnit){
-
try {
-
if(time >
0){
-
redisTemplate.opsForValue().set(key, value, time, timeUnit);
-
}
else {
-
set(key,value);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 递增
-
* @param key 键
-
* @param delta 要增加几(大于0)
-
* @return the value after increment
-
*/
-
public
long
incr
(String key, long delta){
-
if(delta <
0){
-
throw
new
RuntimeException(
"递增因子必须大于0");
-
}
-
Long
increment
= redisTemplate.opsForValue().increment(key, delta);
-
return increment !=
null ? increment :
0L;
-
}
-
-
/**
-
* 递减
-
* @param key 键
-
* @param delta 要增加几(小于0)
-
* @return the value after decrement
-
*/
-
public
long
decr
(String key, long delta){
-
if(delta <
0){
-
throw
new
RuntimeException(
"递减因子必须大于0");
-
}
-
Long
increment
= redisTemplate.opsForValue().increment(key, delta);
-
return increment !=
null ? increment :
0L; }
-
-
//=============================Map===================================
-
-
/**
-
* 根据hashKey获取hash列表有多少元素
-
* @param key 键(hashKey)
-
* @return the size of map
-
*/
-
public
long
hsize
(String key) {
-
try {
-
return redisTemplate.opsForHash().size(key);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
/**
-
* HashGet 根据"项 中的 键 获取列表"
-
* @param key 键(hashKey)能为null
-
* @param item 项不能为null
-
* @return the value of the corresponding key
-
*/
-
public Object
hget
(String key, String item){
return redisTemplate.opsForHash().get(key, item);}
-
-
/**
-
* 获取HashKey对应的所有键值
-
* @param key 键(hashKey)
-
* @return 对应的多个键值
-
*/
-
public Map<Object, Object>
hmget
(String key) {
return redisTemplate.opsForHash().entries(key);}
-
-
/**
-
* 获取HashKey对应的所有键值
-
* @param key 键(hashKey)
-
* @param keyType 键类型
-
* @param valueType 值类型
-
* @param <K> 键类型参数
-
* @param <V> 值类型参数
-
* @return a map
-
*/
-
public <K, V> Map<K, V>
hmget
(String key, Class<K> keyType, Class<V> valueType) {
-
return JsonUtils.mapParse(redisTemplate.opsForHash().entries(key), keyType, valueType);}
-
-
/**
-
* HashSet 存入多个键值对
-
* @param key 键(hashKey)
-
* @param map map 对应多个键值对
-
*/
-
public
void
hmset
(String key, Map<String, Object> map) {
-
try {
-
redisTemplate.opsForHash().putAll(key,map);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
}
-
-
}
-
-
/**
-
* HashSet存入并设置时间
-
* @param key 键(hashKey)
-
* @param map 对应多个键值
-
* @param time 时间(秒)
-
* @return whether true or false
-
*/
-
public
boolean
hmset
(String key, Map<String, Object> map, long time){
-
try {
-
redisTemplate.opsForHash().putAll(key,map);
-
if (time >
0){
-
expire(key,time);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 向一张hash表中放入数据,如果不存在将创建
-
* @param key 键(hashKey)
-
* @param item 项
-
* @param value 值
-
* @return whether true or false
-
*/
-
public
boolean
hset
(String key, String item, Object value){
-
try {
-
redisTemplate.opsForHash().put(key, item, value);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 向一张hash表中放入数据,如果不存在将创建,并设置有效时间
-
* @param key 键(hashKey)
-
* @param item 项
-
* @param value 值
-
* @param time 时间(秒) 注意: 如果已经在hash表有时间,这里将会替换所有的时间
-
* @return whether true or false
-
*/
-
public
boolean
hset
(String key, String item, Object value, long time){
-
try {
-
redisTemplate.opsForHash().put(key, item, value);
-
if (time >
0){
-
expire(key,time);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 放入map集合数据,如果不存在将创建
-
* @param key 键(hashKey)
-
* @param value map集合
-
* @param <K> map集合键参数类型
-
* @param <V> map集合值参数类型
-
* @return whether true or false
-
*/
-
public <K, V>
boolean
hsetMap
(String key, Map<K, V> value) {
-
try {
-
redisTemplate.opsForHash().putAll(key, value);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 获取key对应的所有map键值对
-
* @param key 键(hashKey)
-
* @return the Map
-
*/
-
public Map<Object, Object>
hgetMap
(String key) {
-
try {
-
return redisTemplate.opsForHash().entries(key);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
-
/**
-
* 获取key对应的所有map键值对(泛型)
-
* @param key 键(hashKey)
-
* @param keyType 键类型
-
* @param valueType 值类型
-
* @param <K> 键类型参数
-
* @param <V> 值类型参数
-
* @return the Map
-
*/
-
public <K, V> Map<K, V>
hgetMap
(String key, Class<K> keyType, Class<V> valueType) {
-
try {
-
return JsonUtils.mapParse(redisTemplate.opsForHash().entries(key), keyType, valueType);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
* 删除hash表中的值
-
* @param key 键(hashKey) 不能为null
-
* @param item 项可以是多个 不能为null
-
*/
-
public
void
hdel
(String key, Object... item){
-
redisTemplate.opsForHash().delete(key,item);
-
}
-
-
/**
-
* 判断hash表是否有该项的值
-
* @param key 键(hashKey)不能为null
-
* @param item 项不能为null
-
* @return whether true or false
-
*/
-
public
boolean
hHasKey
(String key, String item){
-
return redisTemplate.opsForHash().hasKey(key, item);
-
}
-
-
/**
-
* hash递增,如果不存在,就会创建一个,并把新增后的值返回
-
* @param key 键(hashKey)
-
* @param item 项
-
* @param by 要增加几(大于0)
-
* @return the value of the corresponding key after increment in one Map
-
*/
-
public
double
hincr
(String key, String item, double by){
-
return redisTemplate.opsForHash().increment(key, item, by);
-
}
-
-
/**
-
* hash递减
-
* @param key 键(hashKey)
-
* @param item 项
-
* @param by 要减少几(小于0)
-
* @return the value of the corresponding key after decrement in one Map
-
*/
-
public
double
hdecr
(String key, String item, double by){
-
return redisTemplate.opsForHash().increment(key, item, -by);
-
}
-
-
//=============================Set===================================
-
-
/**
-
* 根据key获取Set中的所有值
-
* @param key 键
-
* @return all values in one Set
-
*/
-
public Set<Object>
sGet
(String key){
-
try {
-
return redisTemplate.opsForSet().members(key);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
* 根据value从一个Set集合中查询一个值,是否存在
-
* @param key 键
-
* @param value 值
-
* @return whether true or false
-
*/
-
public
boolean
sHasKey
(String key, Object value){
-
try {
-
Boolean
flag
= redisTemplate.opsForSet().isMember(key, value);
-
return Boolean.TRUE.equals(flag);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 将数据放入set缓存
-
* @param key 键
-
* @param values 值
-
* @return the number of adding successfully
-
*/
-
public
long
sSet
(String key, Object... values){
-
try {
-
Long
nums
= redisTemplate.opsForSet().add(key, values);
-
return nums !=
null ? nums :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
/**
-
* 将set数据放入缓存,并设置有效时间
-
* @param key 键
-
* @param time 时间(秒)
-
* @param values 值,可以是多个
-
* @return the number of adding successfully
-
*/
-
public
long
sSetAndTime
(String key, long time, Object... values){
-
try {
-
Long
count
= redisTemplate.opsForSet().add(key, values);
-
if(time >
0){
-
expire(key, time);
-
}
-
return count !=
null ? count :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
/**
-
* 获取set缓存的长度
-
* @param key 键
-
* @return the size of the Set
-
*/
-
public
long
sGetSetSize
(String key){
-
try {
-
Long
size
= redisTemplate.opsForSet().size(key);
-
return size !=
null ? size :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
/**
-
* 移除值为values的
-
* @param key 键
-
* @param values 值(可以是多个)
-
* @return the number of removal
-
*/
-
public
long
setRemove
(String key, Object... values){
-
try {
-
Long
nums
= redisTemplate.opsForSet().remove(key, values);
-
return nums !=
null ? nums :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0;
-
}
-
}
-
-
//=============================List===================================
-
-
/**
-
* 获取list列表数据
-
* @param key 键
-
* @return all values of one List
-
*/
-
public List<Object>
lget
(String key) {
-
try {
-
return redisTemplate.opsForList().range(key,
0, -
1);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
/**
-
* 获取list列表数据(泛型)
-
* @param key 键
-
* @param targetType 目标类型
-
* @param <T> 目标类型参数
-
* @return all values of one List
-
*/
-
public <T> List<T>
lget
(String key, Class<T> targetType) {
-
try {
-
return JsonUtils.listParse(redisTemplate.opsForList().range(key,
0, -
1), targetType);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
* 获取list缓存的长度
-
* @param key 键
-
* @return the length of the List
-
*/
-
public
long
lGetListSize
(String key){
-
try {
-
Long
size
= redisTemplate.opsForList().size(key);
-
return size !=
null ? size :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
/**
-
* 通过索引获取list中的值
-
* @param key 键
-
* @param index 索引 index >= 0 时, 0:表头, 1:第二个元素,以此类推... index < 0 时, -1:表尾, -2:倒数第二个元素,以此类推
-
* @return the value of the specified index in one List
-
*/
-
public Object
lgetIndex
(String key, long index){
-
try {
-
return redisTemplate.opsForList().index(key, index);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
* 通过索引获取list中的值(泛型)
-
* @param key 键
-
* @param index 索引 index >= 0 时, 0:表头, 1:第二个元素,以此类推... index < 0 时, -1:表尾, -2:倒数第二个元素,以此类推
-
* @return the value of the specified index in one List
-
* @param targetType 目标类型
-
* @param <T> 目标类型参数
-
* @return the generic value of the specified index in one List
-
*/
-
public <T> T
lgetIndex
(String key, long index, Class<T> targetType) {
-
try {
-
return JsonUtils.objParse(redisTemplate.opsForList().index(key, index), targetType);
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
null;
-
}
-
}
-
-
/**
-
* 将list放入缓存
-
* @param key 键
-
* @param value 值
-
* @return whether true or false
-
*/
-
public
boolean
lSet
(String key, Object value){
-
try {
-
redisTemplate.opsForList().rightPush(key, value);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 将list放入缓存
-
* @param key 键
-
* @param value 值
-
* @param time 时间(秒)
-
* @return whether true or false
-
*/
-
public
boolean
lSet
(String key, Object value, long time){
-
try {
-
redisTemplate.opsForList().rightPush(key, value);
-
if (time >
0){
-
expire(key, time);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 将list集合放入缓存
-
* @param key 键
-
* @param values 值
-
* @return whether true or false
-
*/
-
public <T>
boolean
lSet
(String key, List<T> values){
-
try {
-
Long
nums
= redisTemplate.opsForList().rightPushAll(key, values);
-
return nums !=
null;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 将list集合放入缓存,并设置有效时间
-
* @param key 键
-
* @param values 值
-
* @param time 时间(秒)
-
* @return whether true or false
-
*/
-
public
boolean
lSet
(String key, List<Object> values, long time){
-
try {
-
redisTemplate.opsForList().rightPushAll(key, values);
-
if (time >
0){
-
expire(key, time);
-
}
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 根据索引修改list中的某条数据
-
* @param key 键
-
* @param value 值
-
* @param index 索引
-
* @return whether true or false
-
*/
-
public
boolean
lUpdateIndex
(String key, Object value, long index){
-
try {
-
redisTemplate.opsForList().set(key, index, value);
-
return
true;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
false;
-
}
-
}
-
-
/**
-
* 移除N个值为value
-
* @param key 键
-
* @param value 值
-
* @param number 移除多少个
-
* @return 返回移除的个数
-
*/
-
public
long
lRemove
(String key, Object value, long number){
-
try {
-
Long
count
= redisTemplate.opsForList().remove(key, number, value);
-
return count !=
null ? count :
0L;
-
}
catch (Exception e) {
-
e.printStackTrace();
-
return
0L;
-
}
-
}
-
-
//=============================Lock===================================
-
-
/**
-
* 解决缓存加锁问题
-
* @param key 锁名称
-
* @param value 锁值
-
* @param timeout 超时时间
-
* @param unit 时间单位
-
* @param <T> 锁值的数据类型
-
* @return 返回加锁成功状态
-
*/
-
public <T>
boolean
tryLock
(String key, T value, long timeout, TimeUnit unit) {
-
Boolean
flag
= redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);
-
return Boolean.TRUE.equals(flag);
-
}
-
-
/**
-
* 解决缓存解锁操作
-
* @param key 锁名称
-
* @return 返回解锁成功状态
-
*/
-
public
boolean
unLock
(String key) {
-
Boolean
flag
= redisTemplate.delete(key);
-
return Boolean.TRUE.equals(flag);
-
}
-
-
/**
-
* 全局生成唯一ID策略
-
* 设计: 符号位(1位) - 时间戳(32位) - 序列号(31位)
-
* @param keyPrefix key的前缀
-
* @return 返回唯一ID
-
*/
-
public
long
globalUniqueKey
(String keyPrefix) {
-
-
// 1. 生成时间戳
-
LocalDateTime
now
= LocalDateTime.now();
-
-
// 东八区时间
-
long
nowSecond
= now.toEpochSecond(ZoneOffset.UTC);
-
// 相减获取时间戳
-
long
timestamp
= nowSecond - BEGIN_TIMESTAMP;
-
-
// 2. 生成序列号(使用日期作为redis自增长超2^64限制,灵活使用年、月、日来存储)
-
// 获取当天日期
-
String
date
= now.format(DateTimeFormatter.ofPattern(
"yyyy:MM:dd"));
-
// 自增长
-
Long
increment
= redisTemplate.opsForValue().increment(
"icr:" + keyPrefix +
":" + date);
-
long
count
= increment !=
null ? increment :
0L;
-
-
// 3. 拼接并返回(使用二进制或运算)
-
return timestamp << MOVE_BITS | count;
-
}
-
-
}
-
-
-
转载:https://blog.csdn.net/dongjing991/article/details/128949033
查看评论