2012-08-04 77 views
13

我想實現基於redis的會話存儲,我將會話數據放入redis中。但我不知道如何處理session-expire。我可以循環所有redis鍵(sessionid)並且忽略lastaccess和maxidle數據,因此我需要將所有密鑰加載到客戶端,並且可能有1000m會話密鑰,並且可能會導致非常池I/O性能。
我想讓redis管理到期,但是當key過期時沒有監聽器或回調,所以不可能老虎HttpSessionListener。 有什麼建議嗎?如何處理基於redis的會話過期?

+0

不是在Redis中,但你可能想看看它是如何在Tarantool中完成的: https://github.com/mailru/tntlua/blob/master/expirationd.lua 簡而言之,在Tarantool中你可以在數據庫中運行自己的Lua腳本,並在其中設置自己的過期策略。不需要外部守護進程。 – Kostja 2013-05-03 11:38:15

回答

33

因此,您需要在Redis會話過期時通知您的應用程序。

雖然Redis不支持此功能,但您可以使用一些技巧來實現它。

更新:從2.8.0版本,Redis的不支持此http://redis.io/topics/notifications

首先,人們都在考慮這個問題:這個還在討論中,但它可能會被添加到Redis的未來版本。請參見下面的問題:

現在,這裏有一些解決方案可以與目前Redis的版本使用。

解決方案1:修補的Redis

實際上,增加一個簡單的通知時執行的Redis密鑰過期並不難。它可以通過將10行添加到Redis源代碼的db.c文件中來實現。這裏有一個例子:

https://gist.github.com/3258233

這個簡短的帖子補丁的#expired列表中的關鍵,如果密鑰已過期,並用「@」字符(任意選擇)開始。它可以很容易地適應您的需求。

然後使用EXPIRE或SETEX命令爲會話對象設置過期時間,然後編寫一個循環BRPOP的小守護程序以從「#expired」列表中出列,並在您的應用。

重要的一點是要了解到期機制如何在Redis中起作用。實際上有兩種到期的不同路徑,兩者都同時激活:

  • 懶惰(被動)機制。每次訪問密鑰時都可能發生到期。

  • 主動機制。內部工作會定期(隨機)對設置了過期時間的許多密鑰進行採樣,試圖找出過期的密鑰。

請注意,上述補丁適用於兩個路徑。

後果是Redis過期時間不準確。如果所有密鑰都已過期,但只有一個即將過期,並且未被訪問,則活動的過期作業可能需要幾分鐘的時間才能找到密鑰並過期。如果您在通知中需要一些準確性,這不是要走的路。

解決方法2:用zsets

這裏的想法是不依靠Redis的密鑰過期機制,而是通過一個附加的索引加上輪詢守護進程模擬它模擬到期。它可以與未修改的Redis 2.6版本一起使用。

每次會話添加到Redis的,你可以運行:

MULTI 
SET <session id> <session content> 
ZADD to_be_expired <current timestamp + session timeout> <session id> 
EXEC 

的to_be_expired排序set只是訪問應該到期的第一密鑰的有效途徑。守護程序可以輪詢使用以下Lua的服務器端腳本to_be_expired:

local res = redis.call('ZRANGEBYSCORE',KEYS[1], 0, ARGV[1], 'LIMIT', 0, 10) 
if #res > 0 then 
    redis.call('ZREMRANGEBYRANK', KEYS[1], 0, #res-1) 
    return res 
else 
    return false 
end 

的命令來啓動腳本是:

EVAL <script> 1 to_be_expired <current timestamp> 

守護程序將獲得最多10個項目。對於它們中的每一個,都必須使用DEL命令刪除會話,並通知應用程序。如果實際處理了一個項目(即Lua腳本的返回非空),守護程序應該立即循環,否則可引入1秒的等待狀態。

得益於Lua腳本,可以同時啓動多個輪詢守護程序(腳本保證給定的會話只處理一次,因爲這些鍵由Lua腳本本身從to_be_expired中移除)。

解決方案3:使用外部分佈式計時器

另一種解決方案是依靠外部分佈式定時器。 beanstalk lightweight queuing system對此很有可能

每次在系統中添加會話時,應用程序都會將會話ID過帳到豆桶隊列,延遲時間與會話超時相對應。守護進程正在監聽隊列。當它可以使項目出列時,這意味着會話已經過期。它只需在Redis中清理會話並通知應用程序。

+0

令人驚歎的答案 - 非常感謝!你能否澄清這句話:「守護進程應立即循環,否則可引入1秒等待狀態」。在這種情況下,循環意味着什麼 - 爲什麼/在哪裏等待1秒等待? – 2013-07-02 14:05:14

+0

守護進程是常駐程序,有時候會在系統中進行某些活動。由於它們一直在運行,大部分代碼都被封裝在主循環中。現在守護進程還需要一個等待狀態,以避免循環時佔用100%的CPU。沒有與Redis的zset關聯的阻塞命令(與列表中的BLPOP/BRPOP不同),所以如果沒有返回任何內容,它必須通過輪詢和睡眠來模擬。 – 2013-07-02 20:37:02

+2

這已經在redis中實現。這些問題已經結束。如果有人更新了這個答案,那將會很好。 – 2014-04-19 14:44:41