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切換資料庫進行操作完成,如有錯漏,請大家指正。