1. 程式人生 > >Redis實現key過期監聽,並操作redis的多個數據庫,整合到SpringBoot

Redis實現key過期監聽,並操作redis的多個數據庫,整合到SpringBoot

最近來了個新的需求,需要使用定時器完成,本想以為用個@Scheduled()就輕易搞定的,詳細瞭解後,事情卻並沒有這麼簡單......。所以接到需求後,需要找產品明確明確再次明確,才開工,不然的話你本以為做好的工作卻是一場空。

業務場景邏輯解析:第一個請求進來,需要把請求引數暫時儲存下來,並觸發一個定時器。如果第二個請求在定時器未過期期間進來,去拿第一次請求的引數和第二次請求的引數進行對比,選出一個更合理的引數進行操作。如果第二個請求在定時器過期時還未過來,則過期觸發取第一次請求的引數來執行操作。如果在定時器過期後,第二個請求才過來,還是需要拿第一次引數和第二次引數進行對比,若第二次引數更合理,則再次執行。

業務與技術相結合:因為第一次請求引數只是暫時儲存下來,自然而然就想到放到快取裡。把第一次請求的引數儲存在redis的0號庫和1號庫裡頭,並且以一定的規則命名key值,使得兩次請求的key值是一樣的,0號庫設定過期時間為30S,1號庫則設定過期時間為1天。啟用0號庫的key過期監聽,每次請求或者過期觸發,都去比較引數,若執行則加一個執行標識在value裡,這樣就可以完成以上的業務場景。

以上只是一時興起,回憶一下做的功能,下面進入主題。

一、Redis啟動key過期監聽,基於spring

redis可以實現類似於訊息中介軟體的一個釋出/訂閱的功能,在spring裡釋出和訂閱的方式有多種方式,我只展示其中的一種(key過期)。

①釋出key過期訊息通道

key的過期監聽,其實就是釋出了一個訊息通道,一旦有key過期了,就會把該key推送出去。當然,在該通道釋出之前,我們就必須進行訂閱。

這個key過期釋出的通道,redis有提供一個專門開啟的開關,在redis.conf裡進行配置:

(當然,我們也可以自己釋出其他訊息通道)

預設是不開啟(開啟對CUP有消耗):

notify-keyspace-events ""

開啟後:

notify-keyspace-events "Ex"

然後重啟redis服務,就可以生效。

開啟這個key過期釋出的訊息通道後,我們就編寫程式該通道進行訂閱。

*redis.conf是在redis安裝目錄下

②訂閱key過期訊息通道

基於springBoot,一啟動就進行訂閱key過期訊息通道,新建一個類,並註解@Component,並且實現CommandLineRunner介面,這樣當spring容器載入完之後,就會馬上執行該元件

package com.appscomm.device.common;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;



@Component
public class RedisSubscribeThread implements CommandLineRunner {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public void run(String... strings) throws Exception {
        redisTemplate.execute(new RedisCallback() {
            @Override
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                connection.pSubscribe(new MessageListener() {
                    @Override
                    public void onMessage(Message message, byte[] pattern) {

                     System.out.println("key:"+message);                

                    //業務處理


                    }
                },"*@0*".getBytes());
                return null;
            }
        });
    }

}

稍作解析:

[email protected],spring的元件註解,會加入到bean容器

2.CommandLineRunner,spring容器啟動完後進行載入

3."*@0*".getBytes(),監聽redis0號庫的所有釋出訊息(因為只有1一個地方用到,所以只有一個)

自此,已經key過期監聽已經實現。測試:

redisTemplate.opsForValue().set("keyTest","123",5,TimeUnit.SECONDS);

預設插入是redis的0號庫

5秒鐘後,key過期的列印結果:

二、操作redis的多個數據庫

基於spring,切換redis的資料庫進行儲存和獲取:

通過redisTemplate來進行獲取。

        JedisConnectionFactory factory =(JedisConnectionFactory)redisTemplate.getConnectionFactory();
        factory.setDatabase(0);
        JedisConnection jedisConnectionZero=factory.getConnection(); //獲取redis的0號庫連線
        factory.setDatabase(1);
        JedisConnection jedisConnectionOne=factory.getConnection();  //獲取redis的1號庫連線

        try {

        jedisConnectionZero.setEx("testKey0".getBytes(),30,"aaa".getBytes()); //存到redis的0號庫
        jedisConnectionOne.setEx("testKey1".getBytes(),50,"bbb".getBytes());//存到redis的1號庫

        String testKey0Value=new String(jedisConnectionZero.get("testKey0".getBytes())); //從redis的0號庫取出
        String testKey1Value=new String(jedisConnectionOne.get("testKey1".getBytes()));  //從redis的1號庫取出

        System.out.println("0號庫的key為testKey0對應的Value值:"+testKey0Value);
        System.out.println("1號庫的key為testKey1對應的Value值:"+testKey1Value);

        }catch (Exception e){
            System.out.println("redis存取錯誤!")
        }finally {
            if(jedisConnectionZero!=null){
                jedisConnectionZero.close();
            }
            if(jedisConnectionOne!=null){
                jedisConnectionOne.close();
            }
        }


        

稍作解析:

redisTemplate獲取JedisConnectionFactory 工廠物件,再獲取jedis連線,每次切換資料庫,都資料重新獲取jedis的連線

最後記得把jedis的連線關閉!!!

測試結果:

自此,redis切換資料庫進行操作完成,如有錯漏,請大家指正。