SpringBoot整合redis 使用RedisTemplate
在数据结构,原理分析,适用场景 三篇中分别了解了redis的基本数据结构,基本原理分析,各种数据类型的适用场景,本次我们进行java操作redis.之前用的spring整合redis使用jedis操作,个人觉得有点麻烦,现在常使用springBoot,操作更方便,所以在这里学习使用springBoot整合redis 使用redistemplate的方式进操作redis api;
1.pom依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2.application.yml配置
spring:
redis:
host: 120.55.166.123
port: 60053
password: 123456
# lettuce:
pool:
#连接超时时间
timeout: 5000
jedis:
pool:
#最大连接数
max-active: 8
#最大阻塞时间
max-wait: 6000
#最大空闲数
max-idle: 8
#最小空闲数
min-idle: 0
3.配置redisTemplate
主要是redisTemplate的序列化配置:
@Configuration
public class RedisConfig {
/**
* 这里注入基础 redisTemplate
*/
@Autowired
private RedisTemplate redisTemplate;
/**
* 配置 redisTemplate
* @return
*/
@Bean
public RedisTemplate stringSerializerRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
/**
* 配置redisTemplate 的key value的序列化策略 可以修改为其他的 fastjson gjson等
*/
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
return redisTemplate;
}
}
4.操作redis API 工具类
@Component
public class RedisUtil {
private static final Long SUCCESS = 1L;
//这里注入的是我们配置过的redisTemplate
@Autowired
private RedisTemplate redisTemplate;
//---------------------- common --------------------------
/**
* 指定缓存失效时间
*
* @param key key值
* @param time 缓存时间
*/
public void expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
} else {
throw new Exception("设置的时间不能为0或者小于0!!");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 判断key是否存在
*
* @param key 传入ke值
* @return true 存在 false 不存在
*/
public Boolean existsKey(String key) {
return redisTemplate.hasKey(key);
}
/**
* 判断key存储的值类型
*
* @param key key值
* @return DataType[string、list、set、zset、hash]
*/
public DataType typeKey(String key) {
return redisTemplate.type(key);
}
/**
* 删除指定的一个数据
*
* @param key key值
* @return true 删除成功,否则返回异常信息
*/
public Boolean deleteKey(String key) {
try {
redisTemplate.delete(key);
return true;
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
/**
* 删除多个数据
*
* @param keys key的集合
* @return true删除成功,false删除失败
*/
public Boolean deleteKey(Collection<String> keys) {
try {
redisTemplate.delete(keys);
return true;
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("删除失败!", ex);
}
return false;
}
//-------------------- String ----------------------------
/**
* 普通缓存放入
*
* @param key 键值
* @param value 值
* @return true成功 要么异常
*/
public Boolean setString(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception ex) {
//throw MyExceptionUtils.mxe("插入缓存失败!", ex);
}
return false;
}
/**
* 普通缓存获取
*
* @param key 键
* @return 值
*/
public Object getString(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 设置缓存存在时间
*
* @param key key值
* @param value value值
* @param time 时间 秒为单位
* @return 成功返回true,失败返回异常信息
*/
public boolean setString(String key, Object value, long time) {
try {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
return true;
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("插入缓存失败!", ex);
}
return false;
}
//-----------------------------hash----------------------------------
/**
* 设置hash值,并设置过期时间
*
* @param key
* @param hk
* @param hv
* @param time
* @return
*/
public Boolean setHash(String key, Object hk, Object hv, long time) {
redisTemplate.opsForHash().put(key, hk, hv);
redisTemplate.expire(key, time, TimeUnit.SECONDS);
return true;
}
public Boolean setHash(String key, Map map, long time) {
redisTemplate.opsForHash().putAll(key, map);
redisTemplate.expire(key, time, TimeUnit.SECONDS);
return true;
}
/**
* 获取hash的值
*
* @param key
* @param hk
* @return
*/
public Object getHash(String key, String hk) {
return key == null ? null : (hk == null ? null : redisTemplate.opsForHash().get(key, hk));
}
/**
* hash累加
*/
public Long hincrease(String key, String hk, long l) {
return redisTemplate.opsForHash().increment(key, hk, l);
}
//----------------------------- list ------------------------------
/**
* 将list放入缓存
*
* @param key key的值
* @param value 放入缓存的数据
* @return true 代表成功,否则返回异常信息
*/
public Boolean setList(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("插入List缓存失败!", ex);
}
return false;
}
/**
* 将Object数据放入List缓存,并设置时间
*
* @param key key值
* @param value 数据的值
* @param time 缓存的时间
* @return true插入成功,否则返回异常信息
*/
public Boolean setList(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForList().rightPush(key, value);
expire(key, time);
return true;
}
return false;
} catch (Exception ex) {
//throw MyExceptionUtils.mxe("插入List缓存失败!", ex);
}
return false;
}
/**
* 将list集合放入List缓存,并设置时间
*
* @param key key值
* @param value 数据的值
* @param time 缓存的时间
* @return true插入成功,否则返回异常信息
*/
public Boolean setListAll(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForList().rightPushAll(key, value);
this.expire(key, time);
return true;
}
return false;
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("插入List缓存失败!", ex);
}
return false;
}
/**
* 根据索引获取缓存List中的内容
*
* @param key key的值
* @param start 索引开始
* @param end 索引结束 0 到 -1代表所有值
* @return 返回数据
*/
public List<Object> getList(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("获取缓存List中的内容失败了!", ex);
}
return null;
}
/**
* 删除List缓存中多个list数据
*
* @param key key值
* @param count 移除多少个
* @param value 可以传null 或者传入存入的Value的值
* @return 返回删除了多少个
*/
public long deleteListIndex(String key, long count, Object value) {
try {
return redisTemplate.opsForList().remove(key, count, value);
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("删除List中的内容失败了!", ex);
}
return 0l;
}
/**
* 获取List缓存的数据
*
* @param key key值
* @return 返回长度
*/
public long getListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception ex) {
// throw MyExceptionUtils.mxe("获取List长度失败", ex);
}
return 0l;
}
//----------------------set-------------------
/**
* 判断是否包含在Set中
*
* @param key
* @param o
*/
public void isContainsKey(String key, HashSet o) {
redisTemplate.opsForSet().isMember(key, o);
}
//-----------------------lock----------------------
/**
* 获取分布式锁
*
* @param lockKey 锁
* @param requestId 请求标识 唯一的 保证释放锁的时候是同一个
* @param expireTime 单位秒 有效时间 防止死锁 一定时间内自动释放锁
* @param waitTimeout 单位毫秒 一定时间内重试 竞争
* @return 是否获取成功
*/
public boolean tryLock(String lockKey, String requestId, long expireTime, long waitTimeout) {
// 当前时间
long nanoTime = System.nanoTime();
try {
//这里我们使用lua脚本进行判断 key是否存在 (锁是否被占)
String script="if redis.call('setNx',KEYS[1],ARGV[1])==1\n" +
"then\n" +
"if redis.call('get',KEYS[1])==ARGV[1]\n" +
"then return redis.call('expire',KEYS[1],ARGV[2])\n" +
"else return 0 " +
"end\n"+
"else return 0\n"+
"end";
int count = 0;
do {
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId, String.valueOf(expireTime));
if (SUCCESS.equals(result)) {
return true;
}
//休眠500毫秒
Thread.sleep(200L);
count++;
} while ((System.nanoTime() - nanoTime) < TimeUnit.MILLISECONDS.toNanos(waitTimeout));
} catch (Exception e) {
}
return false;
}
/**
* 释放锁
*
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public boolean releaseLock(String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey), requestId);
if (SUCCESS.equals(result)) {
return true;
}
return false;
}
}
5.测试Controller
@RestController
public class RedisTestController {
@Autowired
private RedisUtil redisUtil;
/**
* 删除一个key
* @param key
* @return
*/
@RequestMapping("/delete")
public Boolean deleteKey(String key) {
return redisUtil.deleteKey(key);
}
/**
* 新增一个key
* @param name
* @return
*/
@RequestMapping("/set")
public Boolean newSetName(String name) {
return redisUtil.setString("2", name);
}
/**
* 获取一个key
* @param key
* @return
*/
@RequestMapping("/get")
public Object newGetName(String key) {
return redisUtil.getString(key);
}
/**
* 模拟分布式锁
*/
@RequestMapping("/lock")
public Boolean lock(String key) {
for (int i = 0; i < 10; i++) {
new Thread(()->{
while (true) {
//获取锁
String requestId = UUID.randomUUID().toString();
boolean distributedLoke = redisUtil.tryLock("distributedLoke", requestId, 2, 1000);
if (distributedLoke) { //获取成功
System.out.println(Thread.currentThread().getName() + "--->>>>>获取锁成功");
try {
Thread.sleep(1000);
//释放锁
boolean releaseLock = redisUtil.releaseLock("distributedLoke", requestId);
if (releaseLock) {
System.out.println(Thread.currentThread().getName() + "--->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>成功地释放了锁");
} else {
System.out.println(Thread.currentThread().getName() + "--->>>>>失败释放锁");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "--->>>>>获取锁失败");
}
}
}).start();
}
return true;
}
}
6.测试启动类
@SpringBootApplication
public class TestDemo {
public static void main(String[] args) {
SpringApplication.run(TestDemo.class,args);
}
}
至此 基操作已经完成
7.发布与订阅
订阅者
订阅者在redisConfig中增加@Bean的注入
@Configuration
public class RedisConfig {
/**
* 这里注入基础 redisTemplate
*/
@Autowired
private RedisTemplate redisTemplate;
/**
* 配置 redisTemplate
* @return
*/
@Bean
public RedisTemplate stringSerializerRedisTemplate() {
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
/**
* 配置redisTemplate 的key value的序列化策略 可以修改为其他的 fastjson gjson等
*/
redisTemplate.setKeySerializer(stringSerializer);
redisTemplate.setValueSerializer(stringSerializer);
redisTemplate.setHashKeySerializer(stringSerializer);
redisTemplate.setHashValueSerializer(stringSerializer);
return redisTemplate;
}
/**
* redis消息监听器容器
* 可以添加多个监听不同话题的redis监听器,只需要把消息监听器和相应的消息订阅处理器绑定,该消息监听器
* 通过反射技术调用消息订阅处理器的相关方法进行一些业务处理
* @param connectionFactory
* @param listenerAdapter
* @return
*/
//MessageListenerAdapter 表示监听频道的不同订阅者
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter2, MessageListenerAdapter listenerAdapter){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//订阅多个频道 符合规则的全部都被订阅 也可以指定名称
//PatternTopic 可以订阅到 test1 test2 test3 .....
container.addMessageListener(listenerAdapter2,new PatternTopic("test*"));
//ChannelTopic 只会订阅到mytest
container.addMessageListener(listenerAdapter,new ChannelTopic("mytest"));
//序列化对象(特别注意:发布的时候需要设置序列化;订阅方也需要设置序列化)
RedisSerializer<String> stringSerializer = new StringRedisSerializer();
/*
Jackson2JsonRedisSerializer seria = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
stringSerializer.setObjectMapper(objectMapper);*/
container.setTopicSerializer(stringSerializer);
return container;
}
/**
* 表示监听一个频道 listenerAdapter
* @param receiver
* @return
*/
@Bean(value = "listenerAdapter")
MessageListenerAdapter listenerAdapter(MessageReceiveOne receiver){
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“MessageReceiveTwo ” getMessage 方法名 规定 getMessage一定有参数 String message 用于接收订阅到消息
return new MessageListenerAdapter(receiver,"getMessage");
}
/**
* 表示监听一个频道 listenerAdapter2
* @param receiver
* @return
*/
@Bean(value = "listenerAdapter2")
MessageListenerAdapter listenerAdapter2(MessageReceiveTwo receiver){
//这个地方 是给messageListenerAdapter 传入一个消息接受的处理器,利用反射的方法调用“MessageReceiveOne ”
System.out.println("初始化消息适配器进来了2");
return new MessageListenerAdapter(receiver,"getMessage");
}
}
举例 MessageReceiveOne.class
@Service
public class MessageReceiveOne {
public void getMessage(String message){
/**
* 具体的业务处理
*/
System.out.println("这里是消息处理业务逻辑1:"+message);
}
}
发布端
发布端很容易操作 使用redisTemplate api 就可以了
@Autowired
RedisTemplate redisTemplate;
/**
* 发送uuid进行测试
* @return
*/
@RequestMapping("/message")
public String message() {
String message = UUID.randomUUID().toString();
redisTemplate.convertAndSend("mytest", message);
redisTemplate.convertAndSend("test1", message);
return "success";
}
测试 我恶魔你往 mytest 和test1 这两个通道发送消息
如果配置正确 那么 MessageReceiveOne MessageReceiveTwo都将会进行具体的业务处理
测试如图
至此redis的api操作 基本完成 有什么问题欢迎 评论