如何使用redis做一个定时任务?

本文介绍了如何在项目中使用Redis键空间通知功能,动态创建以小时为单位的定时任务。通过监听Redis中key的过期事件,配合存储的执行间隔,重新设置key的有效期来达到定时任务的目的。配置Redis开启键空间通知,设置相应的事件类型如'E'(keyevent)和'x'(过期事件),并在Java项目中创建监听器容器和过期事件监听器,实现key过期后的回调处理。文章最后提出了可能遇到的问题,如Redis故障、大量定时任务的内存占用等,并鼓励读者自行解决这些问题。
该文章已生成可运行项目,

前言

前一阵子遇到了一个头疼两天的问题,如何在项目运行中动态自定义一个以小时为单位的定时任务?我后来想到了redis键空间,用来监听过期的key,结合存储的执行间隔,重新存储有效期的key。

正文

查了查大佬们的文章,简单学习后,了解了redis键空间的用法和基本的知识。

什么是redis键空间?

Redis的键空间通知:keyspace notifications
这个功能是在2.8版本后加入的,在客户端通过订阅鸡汁,来接收那些以某种方式改变了redis数据空间的事件通知。
比如说,改变key的命令;所有在0号库过期的key;

事件类型

改变redis数据空间的每个操作,键空间通知都会发送两个不同的事件。
比如在1号数据库,执行 del zhangsan 的操作,将会触发两个消息:

1、keyevent@1:del zhangsan
2、keyspace@1:zhangsan del

keyspace是对所有zhangsan的key操作进行监听的。

keyevent是对所有执行成功的del操作进行监听,
如果有订阅者订阅监听了他,则会收到zhangsan返回的通知。

我就是利用了可以接收某个库过期的key这个操作,完成了这个定时任务。
而这个功能是可以通过redis配置来实现的,下面来说下到底怎么修改这个配置。

redis配置

因为键空间通知会消耗一定的CPU时间,所以在默认情况下,redis是关闭这个功能的,我们需要手动修改redis.conf来修改此功能的开启状态。

找到配置中 # notify-keyspace-events去掉注释,然后重新启动redis就可以了,easy吧?

这条配置,是需要在后面加多个指定的字符组成,来代表其不同的功能:

K:keyspace事件,事件以__keyspace@<库名>__为前缀进行发布;

E:keyevent事件,事件以__keyevent@<库名>__为前缀进行发布;

g:一般性的,非特定类型的命令,比如del,expire,rename等;

$:字符串特定命令;

l:列表特定命令;

s:集合特定命令;

h:哈希特定命令;

z:有序集合特定命令;

x:过期事件,当某个键过期并删除时会产生该事件;

e:驱逐事件,当某个键因maxmemore策略而被删除时,产生该事件;

A:g$lshzxe的别名,因此”AKE”意味着所有事件。

这些选项中,至少要包含K或者E,否则就没啥用了
比如我在这个定时任务中,使用了E和x:notify-keyspace-events Ex

话不多说了,上菜!
@Configuration
public class RedisConfiguration {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, String> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());
        template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

    /**
     * 监听key过期事件
     */
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        return container;
    }

    /**
     * 指定监听库
     */
    @Bean
    public ChannelTopic expiredTopic() {
        return new ChannelTopic("__keyevent@1__:expired");
    }

}

expiredTopic()方法,就可以指定某个库,某个事件,expired就是过期啦。

@Component
@Slf4j
public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener {

    public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }

	/**
     * 失效后回调函数
     * @param message
     * @param pattern
     */
    @SneakyThrows
    @Override
    public void onMessage(Message message, byte[] pattern) {
        super.onMessage(message, pattern);
        String msg = new String(message.getBody(), "UTF-8");
        String channel = new String(message.getChannel(), "UTF-8");

        log.info("Redis-Listener Channel:"+channel+" Listen to the key:"+msg);

        //msg字符串处理,获取想要的key名字....

		//具体的业务处理

		//想要定时,那就继续把key存到redis,结合过期时间
        
    }
    
}

正菜就是这些,具体业务还是需要结合不同项目,这里就不单独写我的了,至于怎么重新存储redis,这就不再多说了,工具类一找一大堆的哇!

总结

测试类我就不写出来了,自己动动手,马上就明白了这个redis键空间的用法。
可能这个功能会再以后运行中出现一些问题,在这里提几个,提前有所准备吧!比如说:
1、redis挂了?
2、一般来说这种动态定时不是很多,可如果很多,占内存或者影响速度怎么办?
3、反复启停定时,是否影响redis的其它操作?如果影响了,怎么办?
4、redis的订阅发布模式是fire and forget模式,发了就扔了,所以说收不到咋办,定时不存啦?
5、如果订阅发布的客户端over了又重连,岂不是事件都丢了?

好啦,大概提几个可能遇到的,我就不单独去说了,毕竟我也还没遇到咧!我这个项目比较小,问题也不大。但真的也要留心,任何一个错误,都不是小事!

希望对大家有点帮助!我是左小涩,一个独自在大城市努力的年轻人。

本文章已经生成可运行项目
使用Redis实现库存功能可参考以下方法: - **实现高并发扣减库存与秒杀功能**:在秒杀场景中,可借助Redis实现高并发扣减库存。同时采用兜底方案,通过定时任务扫描类似`product:1001:users`这样的键,清理超时未支付的订单,并且恢复相应的库存[^1]。 - **满足多集群多线程等场景要求**:要确保在多集群、多线程以及多进程环境下,库存数据具有可见性和互斥性。可以利用Redis的`SETNX`命令来完成互斥操作,其效率较高,还具备高可用性,并且能拓展主从机制。而相比之下,MySQL虽然安全性和高可用性尚可,但性能一般,可使用`x`锁锁住某条数据作为全局锁,通过报异常回滚来释放锁[^2]。 - **Redis持久化设置**:为了防止Redis重启导致库存数据丢失,需要开启AOF + RDB混合模式进行持久化操作[^1]。 - **集群方案**:采用Redis Cluster分片存储不同商品的库存,这样能避免单节点出现瓶颈问题[^1]。 - **监控报警机制**:通过`INFO`命令对Redis的内存、命中率等指标进行监控,同时设置库存阈值告警,以便及时发现和处理异常情况[^1]。 以下是一个简单的Python示例代码,用于模拟Redis扣减库存的操作: ```python import redis # 连接到Redis r = redis.Redis(host='localhost', port=6379, db=0) # 初始化库存 product_id = 'product:1001' initial_stock = 100 r.set(product_id, initial_stock) # 扣减库存 def decrease_stock(product_id, quantity): with r.pipeline() as pipe: while True: try: # 开始事务 pipe.watch(product_id) stock = int(pipe.get(product_id)) if stock < quantity: pipe.unwatch() return False pipe.multi() pipe.decrby(product_id, quantity) pipe.execute() return True except redis.WatchError: continue # 测试扣减库存 if decrease_stock(product_id, 10): print("库存扣减成功") else: print("库存不足") ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿昊丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值