基于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"); } }
最终效果: