在前一段时间写了一个使用springboot监听redis过期键的文章,这个我自己使用虚拟机测试是可以使用的,但是,当我实际使用的时候却出现了问题,代码明明没有问题,但是在键过期后他就是没有通知,我真是百思不得其解,又通过查各种资料整理了一个更完整的版本,以及实现一个简单的定时器。下面给大家展示一下,上代码!
修改配置文件
修改配置文件跟之前的文章步骤一样,这里就不赘述了,大家可以去看一下上一篇文章。文章连接:文章传送门
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
编写消息处理类
我使用监听redis的目的就是为了能够在用户vip到期时自动发送一个邮件来提醒用户,还有就是在用户封禁时间结束时,给用户发送一个提醒邮件,来提醒用户封号时间已结束,里面的代码大家可以根据自己的需要自行编写。
还有就是我将用户的vip过期信息存到了编号为 1 的数据库,将用户封号信息存到了编号为 2 的数据库。
package com.video.utils;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.video.mapper.UserMapper;
import com.video.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.Topic;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
@Slf4j
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
@Resource
private UserMapper userMapper;
private static final Topic KEYEVENT_EXPIRED_TOPIC1 = new PatternTopic("__keyevent@1__:expired");
private static final Topic KEYEVENT_EXPIRED_TOPIC2 = new PatternTopic("__keyevent@2__:expired");
public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
super(listenerContainer);
}
//设置监听的数据库
@Override
protected void doRegister(RedisMessageListenerContainer listenerContainer) {
listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC1);
listenerContainer.addMessageListener(this, KEYEVENT_EXPIRED_TOPIC2);
}
@Override
public void onMessage(Message message, byte[] pattern) {
//得到数据库信息
String channel = new String(message.getChannel(), StandardCharsets.UTF_8);
//得到过期的键
String key = new String(message.getBody(),StandardCharsets.UTF_8);
//判断是否是数据库1中的键过期
if("__keyevent@1__:expired".equals(channel)){
//判断是否是用户vip过期信息的键失效,若是则进行修改用户vip字段的操作
if("vipOutTime:".equals(key.substring(0, 11))){
int id = Integer.parseInt(key.substring(11));
LambdaUpdateWrapper<User> userWrapper = new LambdaUpdateWrapper<>();
userWrapper.eq(User::getId,id)
.set(User::getVip,0);
int i = userMapper.update(null, userWrapper);
if(i>0){
User user = userMapper.selectById(id);
log.info("用户昵称为:"+user.getName()+"的VIP已过期!");
}else {
log.info("使用监听redis过期键修改用户vip状态出错!");
}
}
}
if("__keyevent@2__:expired".equals(channel)){
if("userBanned:".equals(key.substring(0, 11))){
//封号结束,向用户邮箱发送邮件
int id = Integer.parseInt(key.substring(11));
User user = userMapper.selectById(id);
String head = "封号结束通知";
String body = "尊敬的"+user.getName()+"您好!" +
"您的账号封禁时间已结束,您可以继续正常使用该账号,但请规范使用账号以免再次封号!";
int i = 0;
try {
i = SendMailVerify.MailMessage(user.getEmail(), head, body);
}catch (Exception e){
log.info("发送邮件出错!");
return;
}
if(i==1){
log.info("用户昵称为:"+user.getName()+"的账号解封通知已发送!");
}else if(i==0){
log.info("用户解封邮件发送失败!");
return;
}else {
log.info("邮件信息不完整,未能发送!");
return;
}
LambdaUpdateWrapper<User> wrapper=new LambdaUpdateWrapper<>();
wrapper.eq(User::getId,id).set(User::getUserState,0)
.set(User::getCloseTime, LocalDateTime.now());
int update = userMapper.update(null, wrapper);
if(update>0){
log.info("用户昵称为:"+user.getName()+"的账号解除封禁成功!");
}else {
log.info("用户昵称为:"+user.getName()+"的账号解除封禁失败!");
}
}
}
log.info("redis key 过期:pattern={},channel={},key={}",new String(pattern),channel,key);
}
}
redis切换数据库
因为我是将不同的信息存到了redis不同的数据库,所以需要有一个redis切换数据库的操作。
第一步:导入依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
这个jar包提供了一个选择数据库的方法,比较方便。
第二步,编写模板
package com.video.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.Jedis;
@Configuration
public class JedisConfig {
@Bean
public Jedis getJedis1(){
//redis端口号默认都是6379,这个端口号若是没有修改的话,不加也是可以的,大家可以自己尝试一下
Jedis jedis=new Jedis("ip地址",redis端口号);
//密码是必须要写的,没有密码的要先设置一个密码,不设置密码亲测是连接失败的
jedis.auth("redis连接密码");
//这个主要是确定redis已经连接成功了
if(!"PONG".equals(jedis.ping())){
return null;
}
//这个就是切换到了编号 1 的数据库上
jedis.select(1);
return jedis;
}
@Bean
public Jedis getJedis2(){
//redis端口号默认都是6379,这个端口号若是没有修改的话,不加也是可以的,大家可以自己尝试一下
Jedis jedis=new Jedis("ip地址",redis端口号);
//密码是必须要写的,没有密码的要先设置一个密码,不设置密码亲测是连接失败的
jedis.auth("redis连接密码");
//这个主要是确定redis已经连接成功了
if(!"PONG".equals(jedis.ping())){
return null;
}
//这个就是切换到了编号 2 的数据库上
jedis.select(2);
return jedis;
}
}
这个是使用了一个数据库一个连接连接模板的思路,没有使用动态连接redis数据库,也是偷了个懒,相当于多创建了一个连接redis的对象。但是这样没有并发问题,就是数据库不需要在切换回去,这一个redis连接对象一直都是对我们设定好的数据库进行操作,不会对其他的连接造成干扰。
第三步,使用
@Resource
private JedisConfig jedisConfig;
Jedis jedis = jedisConfig.getJedis2();
使用的话直接使用jedis就可以进行简单的向redis数据库中存储和删除,包括设置有过期时间的键相关的操作了。
当用户充值vip成功后,向数据库1中存入包含用户信息的、有过期时间的键,这样当这个键过期之后就会被监听redis过期键的类中的相关方法捕获,执行相关的操作,这也是使用redis实现一个简单的定时器的核心!
编写redis配置类
只需要在redis的配置类中加入下面的代码就可以了。
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisMessageListenerContainer container() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
return container;
}
@Bean
public KeyExpiredListener keyExpiredListener() {
return new KeyExpiredListener(this.container());
}
以上就是这次对于使用springboot使用监听redis过期键实现简单定时器的步骤了,关于之前的那个为什么不行,我还没有弄清楚,等弄明白之后再进行整理吧。