转!!redis集群基于zset和key失效事件监听机制 实现延迟队列定时任务

史上最全的延迟任务实现方式汇总!附代码(强烈推荐)

https://zhuanlan.zhihu.com/p/130994601

 

Redis集群下的过期监听事件notify-keyspace-events

http://events.jianshu.io/p/a95a4da09bec

 

SpringBoot整合Redis,订阅、发布、过期事件

https://mp.weixin.qq.com/s?__biz=MzU2NjcyMTM0Mg==&mid=2247485252&idx=1&sn=90e3b578904f155733ca1980f52ba0f2&chksm=fca96915cbdee0031cdfae63af6509c11047956c228afdf69cfec73e8a71fffd1b853e33d5fc&scene=21#wechat_redirect

 

 1)基于redis-key失效事件,实现延迟任务

package com.cmcc.open.ota.config.redisson;

import io.lettuce.core.RedisURI;
import io.lettuce.core.cluster.RedisClusterClient;
import io.lettuce.core.cluster.models.partitions.RedisClusterNode;
import io.lettuce.core.cluster.pubsub.RedisClusterPubSubAdapter;
import io.lettuce.core.cluster.pubsub.StatefulRedisClusterPubSubConnection;
import io.lettuce.core.cluster.pubsub.api.async.NodeSelectionPubSubAsyncCommands;
import io.lettuce.core.cluster.pubsub.api.async.PubSubAsyncNodeSelection;
import io.lettuce.core.pubsub.RedisPubSubAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @Description:  redis key 失效 事件监听
 */
@SuppressWarnings("rawtypes")
@Component
public class ClusterSubscriber extends RedisPubSubAdapter implements ApplicationRunner {

    private static Logger log = LoggerFactory.getLogger(ClusterSubscriber.class);

    //过期事件监听
//    private static final String EXPIRED_CHANNEL = "__keyevent@*__:expired";
    private static final String EXPIRED_CHANNEL = "__keyevent@0__:expired";

//    @Value("${spring.redis.cluster.nodes}")
//    private String clusterNodes;

    @Value("${spring.redis.password}")
    private String password;

    @Autowired
    private RedisProperties redisProperties;


    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("过期事件,启动监听......");
        //项目启动后就运行该方法
        startListener();
    }

    /**
     * 启动监听
     */
    @SuppressWarnings("unchecked")
    public void startListener() {
        //redis集群监听
        List<String> nodes = redisProperties.getCluster().getNodes();
        String[] redisNodes = nodes.toArray(new String[nodes.size()]);

        //监听其中一个端口号即可
        RedisURI redisURI = RedisURI.create("redis://" + redisNodes[0]);
        redisURI.setPassword(password);
        RedisClusterClient clusterClient = RedisClusterClient.create(redisURI);



        StatefulRedisClusterPubSubConnection<String, String> pubSubConnection = clusterClient.connectPubSub();
        //redis节点间消息的传播为true
        pubSubConnection.setNodeMessagePropagation(true);
        //过期消息的接受和处理
        pubSubConnection.addListener(new RedisClusterPubSubAdapter(){
            @Override
            public void message(RedisClusterNode node, Object channel, Object message) {
                String msg = message.toString();
                log.info("过期事件的监听" + msg);
                //TODO
            }
        });

        //异步操作
        PubSubAsyncNodeSelection<String, String> masters = pubSubConnection.async().masters();
        NodeSelectionPubSubAsyncCommands<String, String> commands = masters.commands();
        //设置订阅消息类型,一个或多个
        commands.subscribe(EXPIRED_CHANNEL);
    }
}

 

2)基于redis-zset实现延迟任务

package com.cmcc.open.ota.config;

import com.alibaba.excel.util.CollectionUtils;
import com.cmcc.open.ota.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.Set;

@Slf4j
@Service
public class DelayTaskDemo implements ApplicationRunner {
    @Autowired
    private RedisUtil redisUtil;

    //2.基于redis-zset
    public void runDelayTaskBaseRedisZset(String zsetKey) {
        while (true) {
            log.info("DelayTask check");
            Instant nowInstant = Instant.now();
            long lastSecond = nowInstant.plusSeconds(-1).getEpochSecond();
            long nowSecond = nowInstant.getEpochSecond();
            Set<String> data = redisUtil.zsetRangeByScore(zsetKey, lastSecond, nowSecond);
            if (!CollectionUtils.isEmpty(data)) {
                for (String msg : data) {
                    log.info("msg={}", msg.toString());
                }
            }
            redisUtil.zsetremoveRangeByScore(zsetKey, lastSecond, nowSecond);
            try {
                Thread.sleep(1000l);
            } catch (InterruptedException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        String zsetKey = "test123";
        Instant nowInstant = Instant.now();
        log.info("now={}", nowInstant);
        log.info("now +1s ={}", nowInstant.plusSeconds(1).getEpochSecond());
        redisUtil.addZset(zsetKey, "order3", nowInstant.plusSeconds(3).getEpochSecond());
        redisUtil.addZset(zsetKey, "order5", nowInstant.plusSeconds(5).getEpochSecond());
        redisUtil.addZset(zsetKey, "order7", nowInstant.plusSeconds(7).getEpochSecond());
        runDelayTaskBaseRedisZset(zsetKey);
    }
}

其中RedisUtil工具类

@Component
    public final class RedisUtil {

    @Autowired
    private RedisTemplate redisTemplate;

    // =============================common============================

    public boolean addZset(String zsetKey, String value, double score) {
        return redisTemplate.opsForZSet().add(zsetKey, value, score);
    }

    public Set<String> zsetRangeByScore(String zsetKey, double min, double max) {
        return redisTemplate.opsForZSet().rangeByScore(zsetKey, min, max);
    }

    public Long zsetremoveRangeByScore(String zsetKey, double min, double max) {
        return redisTemplate.opsForZSet().removeRangeByScore(zsetKey, min, max);
    }

}

 

posted @ 2023-01-17 16:45  乌云de博客  阅读(509)  评论(0编辑  收藏  举报