基于redis key失效机制实现状态实时更新

基于redis key失效事件通知机制来处理状态实时更新

在我们的业务中,有这样一个场景,在手机端实时采集用用户经纬度,判断用户是否在某个场景(小区、商场等)内,如果再场景内则变更用户任务状态为”执行中”,当用户离开场景超过20分钟,需要将用户任务状态更改为”离场”状态。

一般,我们更新状态,要么定时去扫描数据库,要么就是触发某个事件,在最开始,有想到几种方案:
1、定时(采用quartz定时执行作业,去扫描数据库)
2、用hangfire、rabbitmq等实现延迟执行
3、redis key失效事件,实时处理
经过评估,最后选择了redis key失效机制来处理这个业务。

redis key失效事件监听

redis自2.8之后就提供Keyspace Notifications功能,允许客户订阅Pub/Sub。

开启事件通知

默认情况下,redis是没有开启事件通知的,所以我们需要手动配置:

1
2
打开redis.conf配置文件
搜索notify-keyspace-events,该配置默认是被注释掉的,需要将其修改为notify-keyspace-events Ex ,然后重启便可生效

值说明:

1
2
3
4
5
6
7
8
9
10
11
# K    键空间通知,以__keyspace@<db>__为前缀
# E 键事件通知,以__keysevent@<db>__为前缀
# g del , expipre , rename 等类型无关的通用命令的通知, ...
# $ String命令
# l List命令
# s Set命令
# h Hash命令
# z 有序集合命令
# x 过期事件(每次key过期时生成)
# e 驱逐事件(当key在内存满了被清除时生成)
# A g$lshzxe的别名,因此”AKE”意味着所有的事件

spring boot实现消息监听器类

pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
   <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@Component
public class RedisMsgPubSubListener extends JedisPubSub {

private Logger logger = LoggerFactory.getLogger(getClass());

@Autowired
ITaskService taskService;

@Override
public void unsubscribe() {
super.unsubscribe();
}

@Override
public void unsubscribe(String... channels) {
super.unsubscribe(channels);
}

@Override
public void subscribe(String... channels) {
super.subscribe(channels);
}

@Override
public void psubscribe(String... patterns) {
super.psubscribe(patterns);
}

@Override
public void punsubscribe() {
super.punsubscribe();
}

@Override
public void punsubscribe(String... patterns) {
super.punsubscribe(patterns);
}

@Override
public void onMessage(String channel, String message) {
System.out.println("channel:" + channel + "receives message :" + message);
String key = message;
}

@Override
public void onPMessage(String pattern, String channel, String message) {

}

@Override
public void onSubscribe(String channel, int subscribedChannels) {


}

@Override
public void onPUnsubscribe(String pattern, int subscribedChannels) {

}

@Override
public void onPSubscribe(String pattern, int subscribedChannels) {

}

@Override
public void onUnsubscribe(String channel, int subscribedChannels) {

}
}

创建一个Runner:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class RedisApplicationRunner implements ApplicationRunner {

@Autowired
RedisMsgPubSubListener redisMsgPubSubListener;

@Autowired
JedisPool jedisPool;



@Override
public void run(ApplicationArguments applicationArguments) throws Exception {

Jedis jedis = jedisPool.getResource();
// jedis.set("zhcj:mobile:13548074395:2018122222212","1");
// jedis.expire("zhcj:mobile:13548074395:2018122222212",10);
jedis.subscribe(redisMsgPubSubListener, "__keyevent@0__:expired");

}

}

最终效果:

You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×