redis是怎么監(jiān)控失效的key

redis是怎么監(jiān)控失效的key

先來看一個問題:

如何處理訂單過期自動取消,比如下單30分鐘未支付自動更改訂單狀態(tài)?

解決方案:

可以利用redis天然的key自動過期機制,下單時將訂單id寫入redis,過期時間30分鐘,30分鐘后檢查訂單狀態(tài),如果未支付,則進行處理但是key過期了redis有通知嗎?答案是肯定的。

開啟redis key過期提醒

修改redis相關(guān)事件配置。找到redis配置文件redis.conf,查看“notify-keyspace-events”的配置項,如果沒有,添加“notify-keyspace-events Ex”,如果有值,添加Ex,相關(guān)參數(shù)說明如下:

K:keyspace事件,事件以__keyspace@<db>__為前綴進行發(fā)布;????????? E:keyevent事件,事件以__keyevent@<db>__為前綴進行發(fā)布;????????? g:一般性的,非特定類型的命令,比如del,expire,rename等;???????? $:字符串特定命令;????????? l:列表特定命令;????????? s:集合特定命令;????????? h:哈希特定命令;????????? z:有序集合特定命令;????????? x:過期事件,當(dāng)某個鍵過期并刪除時會產(chǎn)生該事件;????????? e:驅(qū)逐事件,當(dāng)某個鍵因maxmemore策略而被刪除時,產(chǎn)生該事件;????????? A:g$lshzxe的別名,因此”AKE”意味著所有事件。</db></db>

redis測試:

打開一個redis-cli ,監(jiān)控db0的key過期事件

127.0.0.1:6379&gt;?PSUBSCRIBE?__keyevent@0__:expired Reading?messages...?(press?Ctrl-C?to?quit) 1)?"psubscribe" 2)?"__keyevent@0__:expired" 3)?(integer)?1

打開另一個redis-cli ,發(fā)送定時過期key

127.0.0.1:6379&gt;?setex?test_key?3?test_value

觀察上一個redis-cli ,會發(fā)現(xiàn)收到了過期的keytest_key,但是無法收到過期的value?test_value

127.0.0.1:6379&gt;?PSUBSCRIBE?__keyevent@0__:expired Reading?messages...?(press?Ctrl-C?to?quit) 1)?"psubscribe" 2)?"__keyevent@0__:expired" 3)?(integer)?1 1)?"pmessage" 2)?"__keyevent@0__:expired" 3)?"__keyevent@0__:expired" 4)?"test_key"

在springboot中使用

pom 中添加依賴

<!-- redis --> ????????<dependency> ????????????<groupid>org.springframework.boot</groupid> ????????????<artifactid>spring-boot-starter-data-redis</artifactid> ????????</dependency>

定義配置RedisListenerConfig

import?edu.zut.ding.listener.RedisExpiredListener;import?org.springframework.context.annotation.Bean;import?org.springframework.context.annotation.Configuration;import?org.springframework.data.redis.connection.RedisConnectionFactory;import?org.springframework.data.redis.listener.PatternTopic;import?org.springframework.data.redis.listener.RedisMessageListenerContainer;/** ?*?@Author?lsm ?*?@Date?2018/10/27?20:56 ?*/@Configurationpublic?class?RedisListenerConfig?{????@Bean ????RedisMessageListenerContainer?container(RedisConnectionFactory?connectionFactory)?{  ????????RedisMessageListenerContainer?container?=?new?RedisMessageListenerContainer(); ????????container.setConnectionFactory(connectionFactory);//????????container.addMessageListener(new?RedisExpiredListener(),?new?PatternTopic("__keyevent@0__:expired")); ????????return?container; ????} }

定義監(jiān)聽器,實現(xiàn)KeyExpirationEventMessageListener接口,查看源碼發(fā)現(xiàn),該接口監(jiān)聽所有db的過期事件keyevent@*:expired”

import?edu.zut.ding.constants.SystemConstant;import?edu.zut.ding.enums.OrderState;import?edu.zut.ding.service.OrderService;import?org.springframework.beans.factory.annotation.Autowired;import?org.springframework.data.redis.connection.Message;import?org.springframework.data.redis.listener.KeyExpirationEventMessageListener;import?org.springframework.data.redis.listener.RedisMessageListenerContainer;import?org.springframework.stereotype.Component;/** ?*?監(jiān)聽所有db的過期事件__keyevent@*__:expired" ?*?@author?lsm ?*/@Componentpublic?class?RedisKeyExpirationListener?extends?KeyExpirationEventMessageListener?{????public?RedisKeyExpirationListener(RedisMessageListenerContainer?listenerContainer)?{????????super(listenerContainer); ????}????/** ?????*?針對redis數(shù)據(jù)失效事件,進行數(shù)據(jù)處理 ?????*?@param?message ?????*?@param?pattern ?????*/ ????@Override ????public?void?onMessage(Message?message,?byte[]?pattern)?{????????//?用戶做自己的業(yè)務(wù)處理即可,注意message.toString()可以獲取失效的key ????????String?expiredKey?=?message.toString();????????if(expiredKey.startsWith("Order:")){????????????//如果是Order:開頭的key,進行處理 ????????} ????} }

或者打開RedisListenerConfig中 container.addMessageListener(new RedisExpiredListener(), new PatternTopic(“__keyevent@0__:expired”)); 注釋,再定義監(jiān)聽器,監(jiān)控__keyevent@0__:expired事件,即db0過期事件。這個地方定義的比較靈活,可以自己定義監(jiān)控什么事件。

import?org.springframework.data.redis.connection.Message;import?org.springframework.data.redis.connection.MessageListener;/** ?*?@author?lsm ?*/public?class?RedisExpiredListener?implements?MessageListener?{????/** ?????*?客戶端監(jiān)聽訂閱的topic,當(dāng)有消息的時候,會觸發(fā)該方法; ?????*?并不能得到value,?只能得到key。 ?????*?姑且理解為:?redis服務(wù)在key失效時(或失效后)通知到j(luò)ava服務(wù)某個key失效了,?那么在java中不可能得到這個redis-key對應(yīng)的redis-value。 ?????*??????*?解決方案: ?????*??創(chuàng)建copy/shadow?key,?例如?set?vkey?"vergilyn";?對應(yīng)copykey:?set?copykey:vkey?""?ex?10; ?????*??真正的key是"vkey"(業(yè)務(wù)中使用),?失效觸發(fā)key是"copykey:vkey"(其value為空字符為了減少內(nèi)存空間消耗)。 ?????*??當(dāng)"copykey:vkey"觸發(fā)失效時,?從"vkey"得到失效時的值,?并在邏輯處理完后"del?vkey" ?????*? ?????*?缺陷: ?????*??1:?存在多余的key;?(copykey/shadowkey) ?????*??2:?不嚴謹,?假設(shè)copykey在?12:00:00失效,?通知在12:10:00收到,?這間隔的10min內(nèi)程序修改了key,?得到的并不是?失效時的value. ?????*??(第1點影響不大;?第2點貌似redis本身的Pub/Sub就不是嚴謹?shù)??失效后還存在value的修改,?應(yīng)該在設(shè)計/邏輯上杜絕) ?????*??當(dāng)"copykey:vkey"觸發(fā)失效時,?從"vkey"得到失效時的值,?并在邏輯處理完后"del?vkey" ?????*? ?????*/ ????@Override ????public?void?onMessage(Message?message,?byte[]?bytes)?{????????byte[]?body?=?message.getBody();//?建議使用:?valueSerializer ????????byte[]?channel?=?message.getChannel(); ????????System.out.print("onMessage?&gt;&gt;?"?); ????????System.out.println(String.format("channel:?%s,?body:?%s,?bytes:?%s" ????????????????,new?String(channel),?new?String(body),?new?String(bytes))); ????}  }

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點贊14 分享