業(yè)務場景
我們以訂單功能為例說明下:
生成訂單后一段時間不支付訂單會自動關(guān)閉朗鸠。最簡單的想法是設置定時任務輪詢般堆,但是每個訂單的創(chuàng)建時間不一樣,定時任務的規(guī)則無法設定视译,如果將定時任務執(zhí)行的間隔設置的過短爽撒,太影響效率入蛆。
還有一種想法,在用戶進入訂單界面的時候硕勿,判斷時間執(zhí)行相關(guān)操作哨毁。方式可能有很多,在這里介紹一種監(jiān)聽 Redis 鍵值對過期時間來實現(xiàn)訂單自動關(guān)閉源武。
實現(xiàn)思路
在生成訂單時挑庶,向 Redis 中增加一個 KV 鍵值對,K 為訂單號软能,保證通過 K 能定位到數(shù)據(jù)庫中的某個訂單即可,V 可為任意值举畸。
假設查排,生成訂單時向 Redis 中存放 K 為訂單號,V 也為訂單號的鍵值對抄沮,并設置過期時間為 30 分鐘跋核,如果該鍵值對在 30 分鐘過期后能夠發(fā)送給程序一個通知,或者執(zhí)行一個方法叛买,那么即可解決訂單關(guān)閉問題砂代。
實現(xiàn):通過監(jiān)聽 Redis 提供的過期隊列來實現(xiàn),監(jiān)聽過期隊列后率挣,如果 Redis 中某一個 KV 鍵值對過期了刻伊,那么將向監(jiān)聽者發(fā)送消息,監(jiān)聽者可以獲取到該鍵值對的 K椒功,注意捶箱,是獲取不到 V 的,因為已經(jīng)過期了动漾,這就是上面所提到的丁屎,為什么要保證能通過 K 來定位到訂單,而 V 為任意值即可旱眯。拿到 K 后晨川,通過 K 定位訂單证九,并判斷其狀態(tài),如果是未支付共虑,更新為關(guān)閉愧怜,或者取消狀態(tài)即可。
開啟 Redis key 過期提醒
修改 redis 相關(guān)事件配置看蚜。找到 redis 配置文件?redis.conf叫搁,查看?notify-keyspace-events?配置項,如果沒有供炎,添加?notify-keyspace-events Ex渴逻,如果有值,則追加?Ex音诫,相關(guān)參數(shù)說明如下:
K:keyspace?事件惨奕,事件以?keyspace@?為前綴進行發(fā)布
E:keyevent?事件,事件以?keyevent@?為前綴進行發(fā)布
g:一般性的竭钝,非特定類型的命令梨撞,比如del,expire香罐,rename等
$:字符串特定命令
l:列表特定命令
s:集合特定命令
h:哈希特定命令
z:有序集合特定命令
x:過期事件卧波,當某個鍵過期并刪除時會產(chǎn)生該事件
e:驅(qū)逐事件,當某個鍵因?maxmemore?策略而被刪除時庇茫,產(chǎn)生該事件
A:g$lshzxe的別名港粱,因此”AKE”意味著所有事件
引入依賴
在?pom.xml?中添加 web和redis?依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
相關(guān)配置
在配置文件中添加redis的依賴
spring:
redis:
host: localhost
port:6379
? ? database:0
控制層,為了方便測試旦签,添加redis數(shù)據(jù)的操作
@RestController
public class RedisController {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@GetMapping("/redis")
public String redis() {
String s = UUID.randomUUID().toString();
System.out.println("存放緩存信息查坪,緩存id:" + s + "過期時間為" + new Date()); stringRedisTemplate.opsForValue().set(s, s, 30, TimeUnit.SECONDS); return "success";
}
}
定義配置?RedisListenerConfig?實現(xiàn)監(jiān)聽 Redis key 過期時間
@Configuration
publicclassRedisListenerConfig{
@Bean
RedisMessageListenerContainercontainer(RedisConnectionFactory connectionFactory){
RedisMessageListenerContainer container =newRedisMessageListenerContainer();
? ? ? ? container.setConnectionFactory(connectionFactory);
return container;
? ? }
}
定義監(jiān)聽器?RedisKeyExpirationListener,實現(xiàn)?KeyExpirationEventMessageListener?接口宁炫,查看源碼發(fā)現(xiàn)偿曙,該接口監(jiān)聽所有 db 的過期事件?keyevent@*:expired"
@Component
public 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) {
// 獲取到失效的 key羔巢,進行取消訂單業(yè)務處理
String expiredKey = message.toString();
System.out.println("緩存過期:key為:" + expiredKey+ "過期時間為" + new Date());
}
}
操作結(jié)果
調(diào)用http://localhost:8080/redis望忆,以寫入數(shù)據(jù)
控制臺打印結(jié)果
存放緩存信息,緩存id:5a115c49-1229-4932-9d78-e0c7ca2eceb6過期時間為Thu Apr 16 09:23:20 CST 2020
緩存過期:key為:5a115c49-1229-4932-9d78-e0c7ca2eceb6過期時間為Thu Apr 16 09:23:50 CST 2020
已經(jīng)生效了
代碼已經(jīng)上傳竿秆,地址為https://gitee.com/huhao9527/spring-boot-redis-demo-01.git