题外话

如需转载文章,请保留文章出处(blog.csdn.net/knight_zhou)。因为我的很多文章一般是会进行更新的。也避免百度搜出来一大推相似的文章,却找不到原创博主。

本文主题

我们要实现分布式事务,而我们目前已经有的中间件是Reis和Kafka,因为Kafka不支持分布式事务。所以我们想借助Redis去实现。而实现的思路是使用

Redis键通知功能 notify-keyspace-events  去实现的。

分布式事务是什么?

开始本文之前,我们先来看什么是分布式事务。在讲分布式事务之前,我们先来看看什么是程序的本地事务。因为我们的业务技术栈主要是Java微服务,并且采用的都是Spring Boot的微服务。我们知道Spring Boot 微服务里面有个注解是 @EnableTransactionManagement ,其实这个就是本地事务。因为不同的微服务去维护各自的事务。但是这样就不是分布式事务了。

而分布式事务就不同,比如有5个微服务。要么5个微服务的一类操作要么全部执行成功要么全部失败。所以对于整个业务的微服务来讲,这就是分布式事务了,所以这也就需要分布式事务去实现了。

Redis怎么去实现分布式事务?

解题思路分析:

比如我们有5个微服务,每个微服务做完这一类操作之后就更新一下Redis的字符串key。(这就有点类似我们去哪里游玩,比如我们去黄山玩,有些人就喜欢打卡,然后提上自己的名字, xxx 到此一游,表示你已经来过这里)当然这个字符串的key是一个固定前缀开头的。当某一个微服务,长时间比如1个小时都没有更新key的话,那么就认定这个微服务超时,也就是失败了。

所以我们需要对Redis的特定的key进行监听,比如我们设置Redis的key的超时时间是2个小时,也就是5个微服务同时执行完这一类操作的时间是2个小时,我们只需要Redis告诉我,key失效的时候能通知我就行了。

当然这个时候你会想,为什么要等Redis主动通知我了?我自己不能去轮询Redis去看是否已经失效了吗?当然这种方式是可以,但是这种方式就比较傻,而且很耗机器性能。所以就像websocket一样,主动推送才是最科学的方式。

聪明的你可能有会这样提出,既然微服务不想去设置轮询,那么不是可以这样做: 首先我们程序设置了Redis的key的失效时间,失效时间程序是知道的,比如假定我们的失效时间是5分钟,那么我们5分钟之后就查就可以了,并且每个微服务做完这一类操作

之后就会更新Redis的key,那么程序只需要在之前我们设定的时间去查一次就可以了,去查那个key的值是不是等于最后一个微服务修改的值就可以了。

关于Redis的事件通知

要实现Redis key的失效事件通知,我们可以使用 notify-keyspace-events  去实现。我们先看看这个配置有哪些参数,具体是什么含义。我们先来看看官方文档:

官方文档: Redis keyspace notifications | Redis

中文翻译文档: 键空间通知(keyspace notification) — Redis 命令参考

IMPORTANT Keyspace notifications is a feature available since 2.8.0

键空间通知功能是  2.8.0以来的一个特性

默认情况下,keyspace事件通知是禁用的,因为这个功能虽然不太合理,但会消耗一些CPU能量。通知可以使用 redis.conf 的 notify-keyspace-events 或通过配置进行启用。

字符

发送的通知

K

键空间通知,所有通知以 __keyspace@<db>__ 为前缀

E

键事件通知,所有通知以 __keyevent@<db>__ 为前缀

g

DEL 、 EXPIRE 、 RENAME 等类型无关的通用命令的通知

$

字符串命令的通知

l

列表命令的通知

s

集合命令的通知

h

哈希命令的通知

z

有序集合命令的通知

x

过期事件:每当有过期键被删除时发送

e

驱逐(evict)事件:每当有键因为 maxmemory 政策而被删除时发送

m 键丢失事件: 访问不存在的键时生成的事件

A

参数 g$lshzxe 的别名,将参数设为字符串 AKE 表示发送所有类型的通知。

输入的参数中至少要有一个 K 或者 E , 否则的话, 不管其余的参数是什么, 都不会有任何通知被分发。

如: 

  • notify-keyspace-events "Ex"  表示对过期事件进行通知发送;

键空间通知和键事件通知有什么区别了?

  • 键空间通知:“某个键执行了什么命令”的通知称为键空间通知(key-space notification)
  • 键事件通知:键事件通知(key-event notification)关注的是“某个命令被什么键执行了”。

过期事件测试

首先我们先开启过期事件通知,命令如下:

[www@me03-common ~]$ redis-cli -p 6305
127.0.0.1:6305> auth xx
OK
127.0.0.1:6305> config get notify-keyspace-events
1) "notify-keyspace-events"
2) ""
127.0.0.1:6305> config set notify-keyspace-events Ex
OK
127.0.0.1:6305> config get notify-keyspace-events
1) "notify-keyspace-events"
2) "xE"
127.0.0.1:6305> 

然后我们再开一个客户端 B 进行订阅如下:

[www@me03-common ~]$ redis-cli -p 6305
127.0.0.1:6305> auth zxx
OK
127.0.0.1:6305>

然后我们执行set一个key 并设置失效事件为10s试一试。

127.0.0.1:6305> setex name 10 tom
OK
127.0.0.1:6305>

然后客户端B 进行检查如下:

127.0.0.1:6305> 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) "name"

到此成功得到了事件通知。

Python代码实现

首先定义一个RedisHelper类,连接Redis,定义相应的频道,定义发布(publish)及订阅(subscribe)方法。文件名为: RedisHelper.py

import redis

# 定义监听的事件
key_channel = "__keyevent@0__:expired"

class RedisHelper(object):
    def __init__(self):
        self.__conn = redis.Redis(host='yy.cn',port=6305,password="zhoulong")#连接Redis
        self.channel = '__keyevent@0__:expired'        #定义名称

    def publish(self,msg):      #定义发布方法
        self.__conn.publish(self.channel,msg)
        return True

    def subscribe(self):#定义订阅方法
        pub = self.__conn.pubsub()
        pub.subscribe(self.channel)
        pub.parse_response()
        return pub

新增订阅的文件 diingyue.py:

from  RedisHelper import RedisHelper

obj = RedisHelper()

redis_sub = obj.subscribe()   # 调用订阅方法

while True:
    msg = redis_sub.parse_response()
    print(msg)

测试结果如下:

127.0.0.1:6305> setex name 10 tom
OK
127.0.0.1:6305>

查看Python代码的返回:

Logo

更多推荐