本篇简述springboot集成redis,基于redis哨兵模式。
(假定springboot脚手架已搭建完成并成功运行,可参考历史分享springboot+mybatis)
org.springframework.boot
spring-boot-starter-data-redis
org.springframework.data
spring-data-redis
2.1 yml
#spring
spring:
#redis
redis:
host: 192.168.2.9
port: 6379
password: CacheDB123
jedis:
pool:
max-active: 8
max-wait: 6000
max-idle: 5
min-idle: 1
# 哨兵配置
sentinel:
master: mymaster
nodes: 192.168.2.9:6379,192.168.2.10:6379
2.2 RedisTemplate config
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
/**
* redis模板
* @param connectionFactory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer
3.1 RedisService
import java.util.concurrent.TimeUnit;
public interface RedisService {
/** 缓存的有效时长 **/
long TIME_ONE_SECOND = 1; // 1秒
long TIME_ONE_MINUTE = 60 * TIME_ONE_SECOND; // 1分
long TIME_ONE_HOUR = 60 * TIME_ONE_MINUTE; // 1小时
long TIME_ONE_DAY = 24 * TIME_ONE_HOUR; // 1天
long TIME_ONE_MONTH = 30 * TIME_ONE_DAY; // 1个月
/**
* 获取缓存剩余时间
* @param keys
* @return 秒
*/
long getExpire(String... keys);
/**
* 缓存数据 并 设置有效期
* @param value
* @param exp
* @param unit
* @param keys
* @param
* @return
*/
boolean setByKey(V value, long exp, TimeUnit unit, String... keys);
/**
* 缓存数据 并 设置有效期(默认时间单位为秒)
* @param value
* @param exp
* @param keys
* @param
* @return
*/
boolean setByKey(V value, long exp, String... keys);
/**
* 缓存数据(默认存储一个月)
* @param value
* @param keys
* @param
* @return
*/
boolean setByKey(V value, String... keys);
/**
* incr key
* 原子递增
* @param key
* @param value
* @return
*/
long increment(String key, long value);
/**
* incr key
* 原子递增
* @param key
* @param value
* @param exp
* @return
*/
long increment(String key, long value, long exp);
/**
* rpush = right push (rpush key value)
* 添加元素到List集合的尾部(默认存储时间单位为秒)
* @param key
* @param value 一个元素 或 一个List集合
* @param exp
* @param
* @return
*/
boolean rpush(String key, V value, long exp);
/**
* lpush = left push (lpush key value)
* 添加元素到List集合的头部(默认存储时间单位为秒)
* @param key
* @param value 一个元素 或 一个List集合
* @param exp
* @param
* @return
*/
boolean lpush(String key, V value, long exp);
/**
* lrange = list range (lrange key 0 -1 即获取全部列表数据)
* 获取List集合数据
* @param key
* @param
* @return
*/
V lrange(String key);
/**
* sadd key value
* 添加元素到Set集合(默认存储时间单位为秒)
* @param key
* @param value 一个元素 或 一个Set集合
* @param exp
* @param
* @return
*/
boolean sadd(String key, V value, long exp);
/**
* smembers key
* 获取Set集合数据
* @param key
* @param
* @return
*/
V smembers(String key);
/**
* zadd key score value (score为有序索引序列)
* 添加元素到ZSet集合(有序)(默认存储时间单位为秒)
* @param key
* @param value 一个元素 或 一个Set集合
* @param exp
* @param
* @return
*/
boolean zadd(String key, V value, long exp);
/**
* zrange key 0 -1
* 获取ZSet集合数据
* @param key
* @param
* @return
*/
V zrange(String key);
/**
* 获取Redis缓存数据
* @param keys
* @param
* @return
*/
V getByKey(String... keys);
/**
* 删除Redis缓存数据
* @param keys
* @return
*/
boolean delByKey(String... keys);
/**
* 通过 Redis 发布订阅消息
* @param channel
* @param message
*/
void convertAndSend(String channel, Object message);
}
3.2 RedisServiceImpl
import com.alibaba.fastjson.JSON;
import com.example.demo.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class RedisServiceImpl implements RedisService {
@Autowired
private RedisTemplate redisTemplate;
/**
* 拼接缓存数据的key
* @param keys
* @return
*/
private String getMemKey(String... keys){
if(keys == null || keys.length == 0){
return null;
}
if(keys.length == 1){
return keys[0];
}
StringBuffer memKey = new StringBuffer();
Arrays.stream(keys).forEach(key -> {
memKey.append(":");
memKey.append(key);
});
return memKey.toString().replaceFirst(":", "");
}
/**
* 设置缓存有效期
* @param key
* @param exp 默认一个月
* @param unit 默认为秒
* @return
*/
private boolean expireByKey(String key, long exp, TimeUnit unit){
if (exp == 0) {
exp = RedisService.TIME_ONE_MONTH;
}
if (unit == null) {
unit = TimeUnit.SECONDS;
}
return redisTemplate.expire(key, exp, unit);
}
@Override
public long getExpire(String... keys) {
String memKey = this.getMemKey(keys);
Long expire = redisTemplate.getExpire(memKey);
log.info("获取缓存数据剩余有效期:key-{}, value-{}", memKey, expire);
return expire == null ? 0 : expire;
}
@Override
public boolean setByKey(V value, long exp, TimeUnit unit, String... keys) {
String memKey = this.getMemKey(keys);
redisTemplate.opsForValue().set(memKey, value);
return this.expireByKey(memKey, exp, unit);
}
@Override
public boolean setByKey(V value, long exp, String... keys) {
return setByKey(value, exp, TimeUnit.SECONDS, keys);
}
@Override
public boolean setByKey(V value, String... keys) {
return setByKey(value, 0, keys);
}
@Override
public long increment(String key, long value) {
// 永不过期
return redisTemplate.opsForValue().increment(key, value);
}
@Override
public long increment(String key, long value, long exp) {
long afterIncrementValue = redisTemplate.opsForValue().increment(key, value);
this.expireByKey(key, exp, TimeUnit.SECONDS);
return afterIncrementValue;
}
@Override
public boolean rpush(String key, V value, long exp) {
redisTemplate.opsForList().rightPush(key, value);
return this.expireByKey(key, exp, TimeUnit.SECONDS);
}
@Override
public boolean lpush(String key, V value, long exp) {
redisTemplate.opsForList().leftPush(key, value);
return this.expireByKey(key, exp, TimeUnit.SECONDS);
}
@Override
public V lrange(String key) {
log.debug("获取缓存数据:key-{}", key);
return (V) redisTemplate.opsForList().range(key, 0, -1);
}
@Override
public boolean sadd(String key, V value, long exp) {
redisTemplate.opsForSet().add(key, value);
return this.expireByKey(key, exp, TimeUnit.SECONDS);
}
@Override
public V smembers(String key) {
log.debug("获取缓存数据:key-{}", key);
return (V) redisTemplate.opsForSet().members(key);
}
@Override
public boolean zadd(String key, V value, long exp) {
redisTemplate.opsForZSet().add(key, value, Math.random());
return this.expireByKey(key, exp, TimeUnit.SECONDS);
}
@Override
public V zrange(String key) {
log.debug("获取缓存数据:key-{}", key);
return (V) redisTemplate.opsForZSet().range(key, 0, -1);
}
@Override
@SuppressWarnings("unchecked")
public V getByKey(String... keys) {
String memKey = this.getMemKey(keys);
V value = (V) redisTemplate.opsForValue().get(memKey);
log.info("获取缓存数据:key-{}, value-{}", memKey, JSON.toJSONString(value));
return value;
}
@Override
public boolean delByKey(String... keys) {
return redisTemplate.opsForValue().getOperations().delete(this.getMemKey(keys));
}
@Override
public void convertAndSend(String channel, Object message) {
redisTemplate.convertAndSend(channel, message);
}
}
写在最后:
简单提下缓存三点问题:
缓存中间件Redis的生产应用模式,一般有主从模式,sentinel哨兵模式,cluster集群模式。后面有空了会在服务运维专栏聊聊各种模式的搭建及测试应用。