赞
踩
在前一段时间写了一个使用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切换数据库的操作。
<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的配置类中加入下面的代码就可以了。
@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过期键实现简单定时器的步骤了,关于之前的那个为什么不行,我还没有弄清楚,等弄明白之后再进行整理吧。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。