springboot使用redis实现定时任务

在前一段时间写了一个使用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过期键实现简单定时器的步骤了,关于之前的那个为什么不行,我还没有弄清楚,等弄明白之后再进行整理吧。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值