要知道redis是有消息的發(fā)布和訂閱功能的,我們可以利用它的發(fā)布和訂閱功能非常簡單地實(shí)現(xiàn)一些比較實(shí)用的功能穆刻。
打個(gè)比方秉扑,如何實(shí)現(xiàn)自動(dòng)關(guān)閉超時(shí)未支付的訂單吸占?
我們通常的做法是寫一個(gè)定時(shí)器巧鸭,定時(shí)去掃描未支付的訂單瓶您,當(dāng)發(fā)現(xiàn)下單時(shí)間超過我們?cè)O(shè)置的閾值時(shí)就去關(guān)閉訂單麻捻。 這樣做有一個(gè)問題:訂單可能會(huì)延時(shí)關(guān)閉纲仍,假如設(shè)置5分鐘掃描一次未支付訂單,未支付訂單有效時(shí)間是15分鐘贸毕,那么就有可能一些訂單到了(15+5)分鐘-1秒才會(huì)被關(guān)閉郑叠。
那么這個(gè)時(shí)候使用redis發(fā)布訂閱功能就非常方便了,我們只需要在用戶下訂單的時(shí)候把訂單號(hào)作為key寫入redis明棍,并設(shè)置一個(gè)15分鐘的有效期乡革。然后訂閱這個(gè)key的過期事件,如果用戶在15分鐘之內(nèi)支付了訂單我們就直接刪除這個(gè)key。如果到了15分鐘key自動(dòng)過期了沸版,我們就會(huì)接收到redis的消息通知嘁傀,這個(gè)時(shí)候就可以直接關(guān)閉訂單了。
開啟redis消息訂閱
打開redis.config配置文件视粮,搜索notify-keyspace-events
就會(huì)看到redis發(fā)布訂閱的配置:
# It is possible to select the events that Redis will notify among a set
# of classes. Every class is identified by a single character:
#
# K Keyspace events, published with __keyspace@<db>__ prefix.
# E Keyevent events, published with __keyevent@<db>__ prefix.
# g Generic commands (non-type specific) like DEL, EXPIRE, RENAME, ...
# $ String commands
# l List commands
# s Set commands
# h Hash commands
# z Sorted set commands
# x Expired events (events generated every time a key expires)
# e Evicted events (events generated when a key is evicted for maxmemory)
# A Alias for g$lshzxe, so that the "AKE" string means all the events.
#
# The "notify-keyspace-events" takes as argument a string that is composed
# of zero or multiple characters. The empty string means that notifications
# are disabled.
#
# Example: to enable list and generic events, from the point of view of the
# event name, use:
#
# notify-keyspace-events Elg
#
# Example 2: to get the stream of the expired keys subscribing to channel
# name __keyevent@0__:expired use:
#
# notify-keyspace-events Ex
redis接收事件類型一共有兩種细办,keyspace
和keyevent
。keyspace
是key觸發(fā)的具體操作蕾殴,keyevent
為操作影響的鍵名笑撞。g,$,l,s,h,z,x,e,A
表示監(jiān)聽什么樣的事件。
舉個(gè)例子我們配置訂閱類型為KEx
钓觉,我們就可以接收到兩種key過期后產(chǎn)生的消息茴肥。
notify-keyspace-events "KEx" #設(shè)置監(jiān)聽類型
重啟redis。
開始監(jiān)聽redis消息訂閱
開啟三個(gè)客戶端:
訂閱redis key為kname
的事件
127.0.0.1:6379> subscribe __keyspace@0__:kname
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyspace@0__:kname"
3) (integer) 1
訂閱redis key 的過期事件
127.0.0.1:6379> subscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyevent@0__:expired"
3) (integer) 1
設(shè)置kname為zhangsan過期時(shí)間為2秒
127.0.0.1:6379> set kname "zhansan" ex 2
兩秒后訂閱的客戶端分別收到了redis的消息通知
127.0.0.1:6379> subscribe __keyspace@0__:kname
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyspace@0__:kname"
3) (integer) 1
1) "message"
2) "__keyspace@0__:kname"
3) "expired" #監(jiān)聽到kname過期了
127.0.0.1:6379> subscribe __keyevent@0__:expired
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "__keyevent@0__:expired"
3) (integer) 1
1) "message"
2) "__keyevent@0__:expired"
3) "kname" #監(jiān)聽到有一個(gè)過期的key為kname
上面這個(gè)訂閱到一個(gè)redis庫的事件荡灾,要想訂閱所有的redis庫就需要使用通配符了瓤狐。
psubscribe __key*@*__:* # 這里注意通配的命令是p開頭
在springboot里如何訂閱redis事件
添加pom依賴
<dependencies>
<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>
</dependencies>
yaml文件配置
server:
port: 8098
spring:
application:
name: redis-subscribe-service
redis:
host: 127.0.0.1
port: 6379
password: red123456
編寫訂閱service
package com.me.binf.service;
import org.springframework.stereotype.Service;
@Service
public class MessageReceiver {
//接收消息的方法
public void receiveMessage(String message){
//message接收到的過期key
System.out.println("Redis 監(jiān)聽到過期的key有:"+message);
}
}
添加redis配置
package com.me.binf.config;
import com.icodingedu.supermall.service.MessageReceiver;
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;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;
@Configuration
public class RedisMessageConfig {
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter){
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//訂閱觸發(fā)的通道
container.addMessageListener(listenerAdapter,
new PatternTopic("__keyevent@0__:expired"));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(MessageReceiver receiver){
return new MessageListenerAdapter(receiver,"receiveMessage");
}
}
設(shè)置kname過期后接收到消息:
Redis 監(jiān)聽到過期的key有:kname