您好,登錄后才能下訂單哦!
Redis key過期的方式有二:被動方式和主動方式
當clients試圖訪問設置了過期時間且已過期的key時,為主動過期方式。
但僅是這樣是不夠的,以為可能存在一些key永遠不會被再次訪問到,這些設置了過期時間的key也是需要在過期后被刪除的。因此,Redis會周期性的隨機測試一批設置了過期時間的key并進行處理。測試到的已過期的key將被刪除。典型的方式為,Redis每秒做10次如下的步驟:
1.隨機測試100個設置了過期時間的key
2.刪除所有發現的已過期的key
3.若刪除的key超過25個則重復步驟1
此為主動方式
@Test public void testRedisEvent() { Jedis jedis = newJedis("192.168.92.134", 8000); jedis.psubscribe(newJedisPubSub() { public voidonPMessage(String pattern, String channel, Stringmessage) { System.err.println(pattern); System.err.println(channel); System.err.println(message); } },"__keyevent@*__:expired"); }
當監聽過期key時,則redis將此key通過pub/sub來發送事件。
由于codis不支持的命令包含了pub/sub,則codis不再支持此監聽事件。
DelayQueue介紹:Delayed 元素的一個×××阻塞隊列,只有在延遲期滿時才能從中提取元素。該隊列的頭部 是延遲期滿后保存時間最長的 Delayed元素。如果延遲都還沒有期滿,則隊列沒有頭部,并且 poll將返回 null。當一個元素的 getDelay(TimeUnit.NANOSECONDS)方法返回一個小于等于 0 的值時,將發生到期。即使無法使用 take或 poll移除未到期的元素,也不會將這些元素作為正常元素對待。例如,size方法同時返回到期和未到期元素的計數。此隊列不允許使用 null 元素。
代碼如下:
public class RedisEvent<K, V> implements Runnable{ public DelayQueue<DelayItem<Pair<K, V>>> q = new DelayQueue<DelayItem<Pair<K, V>>>(); public void run() { while (true){ DelayItem<Pair<K, V>> delayItem = null; try { delayItem = q.take(); if (delayItem != null) { // 超時對象處理 Pair<K, V> pair = delayItem.getItem(); System.out.println("key : " + pair.first + ", value : " + pair.second); } } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { RedisEvent<String, String> re = new RedisEvent<String, String>(); //過期key,value Pair<String, String> pair1 = new Pair<String, String>("key1", "value1"); //2秒過期 DelayItem<Pair<String,String>> d1 = new DelayItem<Pair<String,String>>(pair1, TimeUnit.SECONDS.toNanos(2)); re.q.add(d1); System.out.println(re.q.poll()); Thread.sleep(1000 * 1); System.out.println(re.q.poll()); //2秒后可以拿到數據 Thread.sleep(1000 * 2); DelayItem<Pair<String,String>> delayItem = re.q.poll(); System.out.println(delayItem); Pair<String, String> pair =delayItem.getItem(); System.out.println("key : " + pair.first + ", value : " + pair.second); System.out.println(re.q.poll()); }
輸出結果:
null null com.eric100.delayqueue.DelayItem@5054c2b8 key : key1, value : value1 null
此方案優缺點:
優點:自己實現,簡單易用,方便修改、擴展
缺點:當過期事件過多時且過期時間過長,需要DelayQueue存放過多key,消耗jvm內存
需要queue支持delay job,目前了解的有:Beanstalkd,sidekiq
Beanstalkd介紹:
Beanstalk,一個高性能、輕量級的分布式內存隊列系統,最初設計的目的是想通過后臺異步執行耗時的任務來降低高容量Web應用系統的頁面訪問延遲,支持過有9.5 million用戶的Facebook Causes應用。
Beanstalkd設計里面的核心概念:
◆ job
一個需要異步處理的任務,是Beanstalkd中的基本單元,需要放在一個tube中。
◆ tube
一個有名的任務隊列,用來存儲統一類型的job,是producer和consumer操作的對象。
◆ producer
Job的生產者,通過put命令來將一個job放到一個tube中。
◆ consumer
Job的消費者,通過reserve/release/bury/delete命令來獲取job或改變job的狀態。
Beanstalkd中一個job的生命周期如圖2所示。一個job有READY, RESERVED, DELAYED, BURIED四種狀態。當producer直接put一個job時,job就處于READY狀態,等待consumer來處理,如果選擇延遲 put,job就先到DELAYED狀態,等待時間過后才遷移到READY狀態。consumer獲取了當前READY的job后,該job的狀態就遷移到RESERVED,這樣其他的consumer就不能再操作該job。當consumer完成該job后,可以選擇delete, release或者bury操作;delete之后,job從系統消亡,之后不能再獲取;release操作可以重新把該job狀態遷移回READY(也 可以延遲該狀態遷移操作),使其他的consumer可以繼續獲取和執行該job;有意思的是bury操作,可以把該job休眠,等到需要的時候,再將休 眠的job kick回READY狀態,也可以deleteBURIED狀態的job。正是有這些有趣的操作和狀態,才可以基于此做出很多意思的應用,比如要實現一個循環隊列,就可以將RESERVED狀態的 job休眠掉,等沒有READY狀態的job時再將BURIED狀態的job一次性kick回READY狀態。
優點:不需要自己實現,使用第三方queue
缺點:
1. Beanstalkd目前沒有提供主從同步+故障切換機制,在應用中有可能成為單點的風險。在實際應用中,可以使用數據庫為job提供持久化存儲。
2. 需要花費成本學習第三方queue
將過期key,value放入數據庫中,使用線程定時掃描數據庫。
啟動三個線程:
線程1:根據過期時間,掃描狀態為delay且過期時間已到的事件,將狀態改為ready
線程2:處理狀態為ready的事件,將ready狀態改為done
線程3:刪除done狀態的事件
優點:數據持久化,不會丟失事件數據
缺點:線程定時掃描,過期事件存在延遲處理
由于codis不支持pub/sub,則重新添加redis服務,此服務只用來做過期事件處理
優點:實現方式與原來相同,不需要修改任何代碼
缺點:需要添加機器來做redis服務,主備需采用keepalive等處理
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。