通过 Redis 构建一个响应式架构
mhr18 2025-05-25 14:12 1 浏览 0 评论
本文翻译自Building a Reactive Architecture Around Redis[1],如果觉得本文对你有所帮助,不妨点个赞支持一下
Redis 是我遇到过的最强大、最通用的技术之一。遗憾的是,大多数人都只是将其作为一个优秀的缓存解决方案来使用。
为此,我们需要去改变这个现状。
我特别想通过本文告诉你,如何构建一个以 Redis 为核心的响应式架构。尤其是当你因为一些其它的需求(比如高性能的缓存)已经将 Redis 作为你整个应用基础设施的一部分时,这会是一个巨大的优势。
我在本文所描述的内容,你可以按照自己的想法采取各种手段来实现,说实话,在这一点上任何选择都是有效的。出于个人观点,我更倾向于使用 Node.js,但这也只是我自己的想法,你可以选择最适合你的方案。
构建一个响应式架构
首先要了解的问题是什么是响应式架构,以及为什么我们要构建一个响应式架构而不是采用其他更传统的方案?
简单来说,一个响应式架构就是让每一个逻辑都在满足所有预设条件的情况下被执行 —— 我想我应该给 “简单” 这个词加一个引号。
换个其他的说法:为了让你的逻辑在某个特定事件发生后被触发,通常会有两种实现方案:
o
定期检查某种标志,直到它被打开,这意味着事件发生。
o
停下来等待,直到某个东西通知你的服务,事件被触发。
第二个是面向对象编程中观察者模式的关键。被观察的对象让所有订阅其内部状态的人知道它更新了。
我们在这里要做的是,将这种来源于面向对象(OOP)的设计模式推导到架构级的设计中。因此,这里我所谈及的不是程序内的一些逻辑,而是架构级别的,一旦触发响应条件,就运行某项服务。
这是分配和扩展平台最有效的方式,原因在于:
o
你不必浪费时间和流量去轮询一个特定数据源的标志(或任何你觉得应该轮询的东西)。此外,如果你使用的基础设施是按流量付费的,不必要的轮询可能会产生额外的费用,在目标服务上增加不需要的工作,如果在你的代码等待轮询的时间里发生了多个事件,你最终可能还需要聚合这些事件。
o
你可以通过增加新的服务,并行工作,并以尽可能快的速度捕捉事件,来增强服务的处理能力。
o
平台更加稳定。通过响应式工作,你可以确保你的服务以最佳速度运行,而不必担心由于客户的数据过载而崩溃。
响应式架构本质上是异步的,所以任何试图与之交流的客户端应用,也需要适应相同的响应范式。你可以通过 HTTP 得到一个来自外部的 REST API,但是你得到的响应结果可能并不是你想要的答案。例如,你可能会得到一个 ”200 OK“ 的响应,意味着你的请求已经收到。为了让你的应用程序得到实际的结果,它必须订阅包含这种响应的特定事件。
请记住这一点,否则,你可能会花很长时间来调试为什么没有得到你想要的响应结果。
接下来我们需要什么?
既然如此,我们需要什么来使我们的平台/架构成为一个响应式的平台/架构呢?可以肯定的是,这不是 ReactJS。我们需要一个消息代理,一个能在多个服务之间集中分配消息的东西。
对于可以充当代理的东西,我们需要确保我们的代码知道它在哪里,以及他所需要的事件类型,以此来确保订阅到某些事件。
在此之后,一个通知将被发送到我们的服务,同时触发我们的业务逻辑。
听起来是不是很容易?那是因为它本就如此!
那么 Redis 是如何发挥作用的呢?
Redis 不仅仅是一个存储在内存上的键值对存储引擎,事实上,它有三个我喜欢的特性,也正因如此,我才愿意使用 Redis 来搭建基于不同预期行为的响应式架构。
这三个特点分别是:
o发布/订阅[2]。Redis 内部维护着一个消息队列,它允许我们发送消息,并将它们分发到每个订阅的进程。这是一种“发后即忘”类型的约定,这意味着如果没有在线的监听器,那么消息就会丢失。所以在使用时要考虑到这一点。o键空间通知。这可能是 Redis 中我最喜欢的功能。他们是由 Redis 自己创建的事件,并分发给每个决定订阅它们的进程。这个功能和键空间的变化有关,也即存储在 Redis 里面的数据发生的任何变化。例如,当你删除或更新一个键时,或者当它的 TTL 计数器达到 0 自动删除时。这使你能够设定有时间限制的事件。比如说,你是否曾经需要在 "某事 "发生 3 天后触发一点逻辑?通过这种方法就可以实现。oRedis 流。这是 Redis 数据类型的混合物,混合了键空间通知和发布/订阅,所有这些都放在一起,工作得很好。Redis 流试图模仿 tail -f 命令在你的终端上的行为。如果你从来没有见过这个命令,说明这是一个*nix 命令,它向你显示一个文件的最后一行,并保持监听该文件的变化,每新增一行时,终端会立即显示。Redis 流也是同样的道理。如果使用得当,那么将会是一个强有力的工具。你可以阅读此处了解更多[3]。
所有这些特性都使得你可以以各种方式与你的业务逻辑进行适配,根据你所期望的行为类型,解决其中的一个或全部。
让我们快速看一些例子,以便知道该怎么使用以及在什么时候使用。
经典案例,基于事件的消息
最简单的例子是,每个微服务都在等待发生什么事情。要触发的事件,该事件可能来自外部,即系统的用户或客户端。
如上图所示,可以把中央的红色管看作是 Redis 的发布/订阅流程或阻塞队列,这是一个更可靠的发布/订阅模式的自定义实现。
整个过程从步骤 1 开始,由 Client App 提交请求,到步骤 9 由 Client App 得到响应通知结束。其余的呢?我不关心,客户端 App 也不需要关心。
这种模式的好处之一就是使得架构对客户端来说成为一个黑盒。一个请求可以触发数百个事件,也可能只触发一个,但是行为都是一样的:一旦准备好响应,它就会被传递给客户端。而不是让客户端知道需要多长时间或者需要多久检查一次是否准备好。这些在这里都不重要。
记住以下几点:
o一条信息由其订阅者发布到一个“频道”。如果你想发布不同类型的主题,建议你创建多个不同的频道。另外,如果你需要额外的粒度来区分哪个消费者必须负责处理某个特定的消息,那么这些细节就需要成为消息的一部分。这是因为一个通道的所有订阅者都会得到相同的消息,所以如果有多个进程侦听和获取相同的消息,那么最终可能会重新执行相同的操作。例如,在 Redis 中可以用消息的 ID 作为一个标志,以确保第一个创建它的进程将负责处理该事件,而其他进程则可以忽略它。这是一个可靠的方法,因为在 Redis 中设置一个键是一个原子过程,所以并发不会在其中起作用。o如果没有订阅者监听某个特定的频道,则发布的消息将丢失。如果你使用发布/订阅模式,就会出现这种情况,因为它是在“发后即忘”机制下工作的。如果你想确保你的信息在被处理之前一直在那里,你可以使用“阻塞队列”的方式。这种解决方案包括直接在 Redis 的键空间上创建一个列表(即一个正常的值列表),并让进程订阅键空间通知。这样他们就可以决定如何处理插入的数据(比如忽略、处理、删除等)。o如果你要发送一个复杂的消息,例如 JSON,那么它需要被序列化。这是因为对于阻塞队列和发布/订阅来说,你能发送的唯一东西是一个字符串。不过如果你想不经过序列化就发送复杂的数据类型,他们允许你使用 Redis Streams。当然,这里的限制是,你只能使用 Redis 自带的数据类型而不是那些编程语言的数据类型。
接下来让我们来看看,如果你的事件是基于时间触发的,会发生什么。
基于时间触发
响应式架构的另一个常见行为是,能够在预定义的时间过后触发某些事件。例如:在发现数据问题 10 分钟后触发警报。或等待 30 分钟后触发物联网设备停止发送数据的警报。
这些行为通常与现实世界的限制有关,需要一些时间来解决,或者甚至可以通过“等待一点时间”并重新启动倒计时来解决(就像物联网设备的连接不可靠)。
对于这种情况,整体架构保持不变,唯一的区别是中央通信枢纽必须使用来自 Redis 的键空间通知[4]。
你看,这里就有两个关于 Redis 的主要特点,你需要了解一下:
1.当你设置一个键值对时,你可以选择定义一个 TTL(生存时间),单位是秒。这就变成了一个倒计时,一旦达到 0,这个键就会自动销毁。2.当你订阅一个键空间时(这也适用于 pub/sub,但我们在这里不使用),你可以使用一个模式进行订阅。换句话说,你可以订阅 "last_connection_time_of_device100002" 这个键的事件,而不是订阅 "last_connection_time_of_device"。然后,每一个创建的、符合该模式的键都会在它发生变化时通知你。
考虑到这两点,你可以创建订阅这些特定键的服务,并在它们被删除后做出反应(即事件被触发时)。同时,生产者不断地更新键,这也重置了 TTL 计时器。因此,如果你要追踪一个设备最后一次发送心跳的时间,你可以像我上面展示的那样,为每个设备准备一个密钥,并且在每次收到新的心跳时不断更新这个密钥。一旦 TTL 过了,就意味着你在配置的时间内没有收到新的心跳。你的订阅进程将只收到密钥名称,所以如果你只需要设备的 ID,你可以像我展示的那样构造你的密钥,并解析名称以捕获所需的信息。
影子键技术
另一方面,如果你在该键中保存了一个复杂的结构(如果你需要这么做的话),你将不得不改变这种方法。这是因为当 TTL 过期时,键就会被删除,里面的数据也会被删除,所以你无法获取到它。这时,你可以使用一种叫做“影子键”的技术来代替。
影子键,本质上是一个用来触发事件的键,但它实际上是对包含你所需数据的实际密钥的影子。所以回到我们的例子,考虑生产者每次收到心跳时都会更新 2 个键:
o“last_connection_time_of_device100002” 是最后一次从设备收到有效载荷的 unix 时间戳。o“device_data_id100002” 是与设备的额外信息。
在这两个 key 中,只有第一个有 TTL,第二个没有。因此,当你收到过期通知时,你将从过期的 key 中获取 ID(last_connection_time_of_device100002),并使用它来读取第二个 key 的内容。然后,如果有必要的话,你可以删除这个密钥,或者把它留在那里,这取决于你的使用情况。
这里唯一需要考虑的是,如果你把 Redis 配置为集群模式,键空间的通知不会在整个集群中广播。这意味着,你必须确保你的消费者连接到每个节点。通知如果没有人接收就会丢失,这也是这项技术的唯一缺点,但是在你花费数天时间去调试你的异步逻辑前,了解这一点对你来说会有很大帮助(我是过来人)。
References
[1] Building a Reactive Architecture Around Redis: https://blog.bitsrc.io/building-a-reactive-architecture-around-redis-bc53662b81c8
[2] 发布/订阅: https://redis.io/topics/pubsub
[3] 你可以阅读此处了解更多: https://blog.logrocket.com/why-are-we-getting-streams-in-redis-8c36498aaac5/
[4] 来自 Redis 的键空间通知: https://redis.io/topics/notifications
相关推荐
- MySQL数据库中,数据量越来越大,有什么具体的优化方案么?
-
个人的观点,这种大表的优化,不一定上来就要分库分表,因为表一旦被拆分,开发、运维的复杂度会直线上升,而大多数公司和开发人员是欠缺这种能力的。所以MySQL中几百万甚至小几千万的表,先考虑做单表的优化。...
- Redis的Bitmap(位图):签到打卡、用户在线状态,用它一目了然
-
你是不是每天打开APP,第一时间就是去“签到打卡”?或者在社交软件里,看到你的朋友头像旁边亮着“在线”的绿灯?这些看似简单的功能背后,都隐藏着一个有趣而高效的数据结构。如果让你来设计一个签到系统:用户...
- 想知道有多少人看了你的文章?Redis HyperLogLog几KB就搞定!
-
作为一名内容创作者,你每天最期待的,除了文章阅读量蹭蹭上涨,是不是还特别想知道,到底有多少个“独立用户”阅读了你的文章?这个数字,我们通常称为“UV”(UniqueVisitors),它比总阅读量更...
- Redis的“HyperLogLog”:统计网站日活用户,省内存又高效的神器
-
你可能从未听过这个拗口的名字——“HyperLogLog”,它听起来就像是某个高深莫测的数学公式。但请相信我,理解它的核心思想并不难,而且一旦你掌握了它,你会发现它在处理大数据统计问题时,简直就是“救...
- 阿里云国际站:为什么我的云服务器运行缓慢?
-
本文由【云老大】TG@yunlaoda360撰写一、网络性能瓶颈带宽不足现象:上传/下载速度慢,远程连接卡顿。排查:通过阿里云控制台查看网络流量峰值是否接近带宽上限34。解决:升级带宽(如从1M提...
- Java 近期新闻:Jakarta EE 11和Spring AI更新、WildFly 36.0 Beta、Infinispan
-
作者|MichaelRedlich译者|明知山策划|丁晓昀OpenJDKJEP503(移除32位x86移植版本)已从“ProposedtoTarget”状态进入到“T...
- 腾讯云国际站:怎样设置自动伸缩应对流量高峰?
-
云计算平台服务以阿里云为例:开通服务与创建伸缩组:登录阿里云控制台,找到弹性伸缩服务并开通。创建伸缩组时,选择地域与可用区,定义伸缩组内最小/最大实例数,绑定已有VPC虚拟交换机。实例模板需...
- 【案例分享】如何利用京东云建设高可用业务架构
-
本文以2022年一个实际项目为基础,来演示在京东云上构建高可用业务的整个过程。公有云及私有云客户可通过使用京东云的弹性IAAS、PAAS服务,创建高可用、高弹性、高可扩展、高安全的云上业务环境,提升业...
- Spring Security在前后端分离项目中的使用
-
1文章导读SpringSecurity是Spring家族中的一个安全管理框架,可以和SpringBoot项目很方便的集成。SpringSecurity框架的两大核心功能:认证和授权认证:...
- Redis与Java集成的最佳实践
-
Redis与Java集成的最佳实践在当今互联网飞速发展的时代,缓存技术的重要性毋庸置疑。Redis作为一款高性能的分布式缓存数据库,与Java语言的结合更是如虎添翼。今天,我们就来聊聊Redis与Ja...
- Redis在Java项目中的应用与数据持久化
-
Redis在Java项目中的应用与数据持久化Redis简介:为什么我们需要它?在Java项目中,Redis就像一位不知疲倦的快跑选手,总能在关键时刻挺身而出。作为一个内存数据库,它在处理高并发请求时表...
- Redis 集群最大节点个数是多少?
-
Redis集群最大节点个数取决于Redis的哈希槽数量,因为每个节点可以负责多个哈希槽。在Redis3.0之前,Redis集群最多支持16384个哈希槽,因此最大节点数为16384个。但是在Redi...
- Java开发岗面试宝典:分布式相关问答详解
-
今天千锋广州Java小编就给大家分享一些就业面试宝典之分布式相关问题,一起来看看吧!1.Redis和Memcache的区别?1、存储方式Memecache把数据全部存在内存之中,断电后会挂掉,数据不...
- 当Redis内存不足时,除了加内存,还有哪些曲线救国的办法?
-
作为“速度之王”的Redis,其高性能的秘密武器之一就是将数据存储在内存中。然而,内存资源是有限且昂贵的。当你的Redis实例开始告警“内存不足”,或者写入请求被阻塞时,最直接的解决方案似乎就是“加内...
- 商品详情页那么多信息,Redis的“哈希”如何优雅存储?
-
你每天网购时,无论是打开淘宝、京东还是拼多多,看到的商品详情页都琳琅满目:商品名称、价格、库存、图片、描述、评价数量、销量。这些信息加起来,多的惊人。那么问题来了:这些海量的商品信息,程序是去哪里取出...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- MySQL数据库中,数据量越来越大,有什么具体的优化方案么?
- Redis的Bitmap(位图):签到打卡、用户在线状态,用它一目了然
- 想知道有多少人看了你的文章?Redis HyperLogLog几KB就搞定!
- Redis的“HyperLogLog”:统计网站日活用户,省内存又高效的神器
- 阿里云国际站:为什么我的云服务器运行缓慢?
- Java 近期新闻:Jakarta EE 11和Spring AI更新、WildFly 36.0 Beta、Infinispan
- 腾讯云国际站:怎样设置自动伸缩应对流量高峰?
- 【案例分享】如何利用京东云建设高可用业务架构
- Spring Security在前后端分离项目中的使用
- Redis与Java集成的最佳实践
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)