咱們以訂單功能爲例說明下:生成訂單後一段時間不支付訂單會自動關閉。最簡單的想法是設置定時任務輪詢,可是每一個訂單的建立時間不同,定時任務的規則沒法設定,若是將定時任務執行的間隔設置的太短,太影響效率。還有一種想法,在用戶進入訂單界面的時候,判斷時間執行相關操做。方式可能有不少,在這裏介紹一種監聽 Redis 鍵值對過時時間來實現訂單自動關閉。整理了一份Java面試寶典完整版PDF面試
在生成訂單時,向 Redis 中增長一個 KV 鍵值對,K 爲訂單號,保證經過 K 能定位到數據庫中的某個訂單便可,V 可爲任意值。假設,生成訂單時向 Redis 中存放 K 爲訂單號,V 也爲訂單號的鍵值對,並設置過時時間爲 30 分鐘,若是該鍵值對在 30 分鐘過時後可以發送給程序一個通知,或者執行一個方法,那麼便可解決訂單關閉問題。實現:經過監聽 Redis 提供的過時隊列來實現,監聽過時隊列後,若是 Redis 中某一個 KV 鍵值對過時了,那麼將向監聽者發送消息,監聽者能夠獲取到該鍵值對的 K,注意,是獲取不到 V 的,由於已通過期了,這就是上面所提到的,爲何要保證能經過 K 來定位到訂單,而 V 爲任意值便可。拿到 K 後,經過 K 定位訂單,並判斷其狀態,若是是未支付,更新爲關閉,或者取消狀態便可。redis
修改 redis 相關事件配置。找到 redis 配置文件 redis.conf,查看 notify-keyspace-events 配置項,若是沒有,添加 notify-keyspace-events Ex,若是有值,則追加 Ex,相關參數說明以下:spring
K:keyspace 事件,事件以 keyspace@ 爲前綴進行發佈數據庫
E:keyevent 事件,事件以 keyevent@ 爲前綴進行發佈ide
g:通常性的,非特定類型的命令,好比del,expire,rename等spring-boot
$:字符串特定命令spa
l:列表特定命令code
s:集合特定命令xml
h:哈希特定命令token
z:有序集合特定命令
x:過時事件,當某個鍵過時並刪除時會產生該事件
e:驅逐事件,當某個鍵因 maxmemore 策略而被刪除時,產生該事件
在 pom.xml 中添加 org.springframework.boot:spring-boot-starter-data-redis 依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
定義配置 RedisListenerConfig 實現監聽 Redis key 過時時間
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.RedisMessageListenerContainer; @Configuration public class RedisListenerConfig { @Bean RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory) { RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory); return container; } }
定義監聽器 RedisKeyExpirationListener,實現KeyExpirationEventMessageListener 接口,查看源碼發現,該接口監聽全部 db 的過時事件 keyevent@*:expired"整理了一份Java面試寶典完整版PDF
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; /** * 監聽全部db的過時事件__keyevent@*__:expired" */ @Component public class RedisKeyExpirationListener extends KeyExpirationEventMessageListener { public RedisKeyExpirationListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } /** * 針對 redis 數據失效事件,進行數據處理 * @param message * @param pattern */ @Override public void onMessage(Message message, byte[] pattern) { // 獲取到失效的 key,進行取消訂單業務處理 String expiredKey = message.toString(); System.out.println(expiredKey); } }