通过 Redis 构建一个响应式架构

标签: | 发表时间:2021-08-16 07:33 | 作者:
出处:https://juejin.cn

通过 Redis 构建一个响应式架构

Redis 是我遇到过的最强大、最通用的技术之一。遗憾的是,大多数人都只是将其作为一个优秀的缓存解决方案来使用。

为此,我们需要去改变这个现状。

我特别想通过本文告诉你,如何构建一个以 Redis 为核心的响应式架构。尤其是当你因为一些其它的需求(比如高性能的缓存)已经将 Redis 作为你整个应用基础设施的一部分时,这会是一个巨大的优势。

我在本文所描述的内容,你可以按照自己的想法采取各种手段来实现,说实话,在这一点上任何选择都是有效的。出于个人观点,我更倾向于使用 Node.js,但这也只是我自己的想法,你可以选择最适合你的方案。

构建一个响应式架构

首先要了解的问题是什么是响应式架构,以及为什么我们要构建一个响应式架构而不是采用其他更传统的方案?

简单来说,一个响应式架构就是让每一个逻辑都在满足所有预设条件的情况下被执行 —— 我想我应该给 “简单” 这个词加一个引号。

换个其他的说法:为了让你的逻辑在某个特定事件发生后被触发,通常会有两种实现方案:

  • 定期检查某种标志,直到它被打开,这意味着事件发生。

  • 停下来等待,直到某个东西通知你的服务,事件被触发。

第二个是面向对象编程中观察者模式的关键。被观察的对象让所有订阅其内部状态的人知道它更新了。

我们在这里要做的是,将这种来源于面向对象(OOP)的设计模式推导到架构级的设计中。因此,这里我所谈及的不是程序内的一些逻辑,而是架构级别的,一旦触发响应条件,就运行某项服务。

这是分配和扩展平台最有效的方式,原因在于:

  • 你不必浪费时间和流量去轮询一个特定数据源的标志(或任何你觉得应该轮询的东西)。此外,如果你使用的基础设施是按流量付费的,不必要的轮询可能会产生额外的费用,在目标服务上增加不需要的工作,如果在你的代码等待轮询的时间里发生了多个事件,你最终可能还需要聚合这些事件。

  • 你可以通过增加新的服务,并行工作,并以尽可能快的速度捕捉事件,来增强服务的处理能力。

  • 平台更加稳定。通过响应式工作,你可以确保你的服务以最佳速度运行,而不必担心由于客户的数据过载而崩溃。

响应式架构本质上是异步的,所以任何试图与之交流的客户端应用,也需要适应相同的响应范式。你可以通过 HTTP 得到一个来自外部的 REST API,但是你得到的响应结果可能并不是你想要的答案。例如,你可能会得到一个 ”200 OK“ 的响应,意味着你的请求已经收到。为了让你的应用程序得到实际的结果,它必须订阅包含这种响应的特定事件。

请记住这一点,否则,你可能会花很长时间来调试为什么没有得到你想要的响应结果。

接下来我们需要什么?

既然如此,我们需要什么来使我们的平台/架构成为一个响应式的平台/架构呢?可以肯定的是,这不是 ReactJS。我们需要一个消息代理,一个能在多个服务之间集中分配消息的东西。

对于可以充当代理的东西,我们需要确保我们的代码知道它在哪里,以及他所需要的事件类型,以此来确保订阅到某些事件。

在此之后,一个通知将被发送到我们的服务,同时触发我们的业务逻辑。

听起来是不是很容易?那是因为它本就如此!

那么 Redis 是如何发挥作用的呢?

Redis 不仅仅是一个存储在内存上的键值对存储引擎,事实上,它有三个我喜欢的特性,也正因如此,我才愿意使用 Redis 来搭建基于不同预期行为的响应式架构。

这三个特点分别是:

  • 发布/订阅。Redis 内部维护着一个消息队列,它允许我们发送消息,并将它们分发到每个订阅的进程。这是一种“发后即忘”类型的约定,这意味着如果没有在线的监听器,那么消息就会丢失。所以在使用时要考虑到这一点。
  • 键空间通知。这可能是 Redis 中我最喜欢的功能。他们是由 Redis 自己创建的事件,并分发给每个决定订阅它们的进程。这个功能和键空间的变化有关,也即存储在 Redis 里面的数据发生的任何变化。例如,当你删除或更新一个键时,或者当它的 TTL 计数器达到 0 自动删除时。这使你能够设定有时间限制的事件。比如说,你是否曾经需要在 "某事 "发生 3 天后触发一点逻辑?通过这种方法就可以实现。
  • Redis 流。这是 Redis 数据类型的混合物,混合了键空间通知和发布/订阅,所有这些都放在一起,工作得很好。Redis 流试图模仿 tail -f命令在你的终端上的行为。如果你从来没有见过这个命令,说明这是一个*nix 命令,它向你显示一个文件的最后一行,并保持监听该文件的变化,每新增一行时,终端会立即显示。Redis 流也是同样的道理。如果使用得当,那么将会是一个强有力的工具。 你可以阅读此处了解更多

所有这些特性都使得你可以以各种方式与你的业务逻辑进行适配,根据你所期望的行为类型,解决其中的一个或全部。

让我们快速看一些例子,以便知道该怎么使用以及在什么时候使用。

经典案例,基于事件的消息

最简单的例子是,每个微服务都在等待发生什么事情。要触发的事件,该事件可能来自外部,即系统的用户或客户端。

如上图所示,可以把中央的红色管看作是 Redis 的发布/订阅流程或阻塞队列,这是一个更可靠的发布/订阅模式的自定义实现。

整个过程从步骤 1 开始,由 Client App提交请求,到步骤 9 由 Client App得到响应通知结束。其余的呢?我不关心,客户端 App 也不需要关心。

这种模式的好处之一就是使得架构对客户端来说成为一个黑盒。一个请求可以触发数百个事件,也可能只触发一个,但是行为都是一样的:一旦准备好响应,它就会被传递给客户端。而不是让客户端知道需要多长时间或者需要多久检查一次是否准备好。这些在这里都不重要。

记住以下几点:

  • 一条信息由其订阅者发布到一个“频道”。如果你想发布不同类型的主题,建议你创建多个不同的频道。另外,如果你需要额外的粒度来区分哪个消费者必须负责处理某个特定的消息,那么这些细节就需要成为消息的一部分。这是因为一个通道的所有订阅者都会得到相同的消息,所以如果有多个进程侦听和获取相同的消息,那么最终可能会重新执行相同的操作。例如,在 Redis 中可以用消息的 ID 作为一个标志,以确保第一个创建它的进程将负责处理该事件,而其他进程则可以忽略它。这是一个可靠的方法,因为在 Redis 中设置一个键是一个原子过程,所以并发不会在其中起作用。
  • 如果没有订阅者监听某个特定的频道,则发布的消息将丢失。如果你使用发布/订阅模式,就会出现这种情况,因为它是在“发后即忘”机制下工作的。如果你想确保你的信息在被处理之前一直在那里,你可以使用“阻塞队列”的方式。这种解决方案包括直接在 Redis 的键空间上创建一个列表(即一个正常的值列表),并让进程订阅键空间通知。这样他们就可以决定如何处理插入的数据(比如忽略、处理、删除等)。
  • 如果你要发送一个复杂的消息,例如 JSON,那么它需要被序列化。这是因为对于阻塞队列和发布/订阅来说,你能发送的唯一东西是一个字符串。不过如果你想不经过序列化就发送复杂的数据类型,他们允许你使用 Redis Streams。当然,这里的限制是,你只能使用 Redis 自带的数据类型而不是那些编程语言的数据类型。

接下来让我们来看看,如果你的事件是基于时间触发的,会发生什么。

基于时间触发

响应式架构的另一个常见行为是,能够在预定义的时间过后触发某些事件。例如:在发现数据问题 10 分钟后触发警报。或等待 30 分钟后触发物联网设备停止发送数据的警报。

这些行为通常与现实世界的限制有关,需要一些时间来解决,或者甚至可以通过“等待一点时间”并重新启动倒计时来解决(就像物联网设备的连接不可靠)。

对于这种情况,整体架构保持不变,唯一的区别是中央通信枢纽必须使用 来自 Redis 的键空间通知

你看,这里就有两个关于 Redis 的主要特点,你需要了解一下:

  1. 当你设置一个键值对时,你可以选择定义一个 TTL(生存时间),单位是秒。这就变成了一个倒计时,一旦达到 0,这个键就会自动销毁。
  2. 当你订阅一个键空间时(这也适用于 pub/sub,但我们在这里不使用),你可以使用一个模式进行订阅。换句话说,你可以订阅 "last_connection_time_of_device100002" 这个键的事件,而不是订阅 "last_connection_time_of_device"。然后,每一个创建的、符合该模式的键都会在它发生变化时通知你。

考虑到这两点,你可以创建订阅这些特定键的服务,并在它们被删除后做出反应(即事件被触发时)。同时,生产者不断地更新键,这也重置了 TTL 计时器。因此,如果你要追踪一个设备最后一次发送心跳的时间,你可以像我上面展示的那样,为每个设备准备一个密钥,并且在每次收到新的心跳时不断更新这个密钥。一旦 TTL 过了,就意味着你在配置的时间内没有收到新的心跳。你的订阅进程将只收到密钥名称,所以如果你只需要设备的 ID,你可以像我展示的那样构造你的密钥,并解析名称以捕获所需的信息。

影子键技术

另一方面,如果你在该键中保存了一个复杂的结构(如果你需要这么做的话),你将不得不改变这种方法。这是因为当 TTL 过期时,键就会被删除,里面的数据也会被删除,所以你无法获取到它。这时,你可以使用一种叫做“影子键”的技术来代替。

影子键,本质上是一个用来触发事件的键,但它实际上是对包含你所需数据的实际密钥的影子。所以回到我们的例子,考虑生产者每次收到心跳时都会更新 2 个键:

  • “last_connection_time_of_device100002” 是最后一次从设备收到有效载荷的 unix 时间戳。
  • “device_data_id100002” 是与设备的额外信息。

在这两个 key 中,只有第一个有 TTL,第二个没有。因此,当你收到过期通知时,你将从过期的 key 中获取 ID(last_connection_time_of_device100002),并使用它来读取第二个 key 的内容。然后,如果有必要的话,你可以删除这个密钥,或者把它留在那里,这取决于你的使用情况。

这里唯一需要考虑的是,如果你把 Redis 配置为集群模式,键空间的通知不会在整个集群中广播。这意味着,你必须确保你的消费者连接到每个节点。通知如果没有人接收就会丢失,这也是这项技术的唯一缺点,但是在你花费数天时间去调试你的异步逻辑前,了解这一点对你来说会有很大帮助(我是过来人)。


正如你所看到的,这两种情况下的复杂性都被降低了 —— 我们只需要确保已经订阅了正确的事件或频道。如果你试图用一个普通的 SQL 数据库来做这件事,你必须通过代码实现非常复杂的逻辑,以便有效地确定何时有新的数据进入,或何时有一条信息被删除。

相反,刚才所提的所有复杂性都被 Redis 抽象化了,你唯一需要担心的,就是编写好你的业务逻辑。对我来说,这就是最重要的事情。

你是否将 Redis 用于缓存之外的其他场景?在评论区与其他人分享你的经验,我很想知道你们是如何使用 Redis 的。

相关 [redis 建一 响应式] 推荐:

通过 Redis 构建一个响应式架构

- -
通过 Redis 构建一个响应式架构. Redis 是我遇到过的最强大、最通用的技术之一. 遗憾的是,大多数人都只是将其作为一个优秀的缓存解决方案来使用. 我特别想通过本文告诉你,如何构建一个以 Redis 为核心的响应式架构. 尤其是当你因为一些其它的需求(比如高性能的缓存)已经将 Redis 作为你整个应用基础设施的一部分时,这会是一个巨大的优势.

Redis 负载监控——redis-monitor

- - ITeye资讯频道
redis-monitor是一个Web可视化的 redis 监控程序. 使用 Flask 来开发的,代码结构非常简单,适合移植到公司内网使用. redis 服务器信息,包括 redis 版本、上线时间、 os 系统信息等等. 实时的消息处理信息,例如处理 command 数量、连接总数量等. 内存占用、 cpu 消耗实时动态图表.

Redis 起步

- - 博客园_首页
Rdis和JQuery一样是纯粹为应用而产生的,这里记录的是在CentOS 5.7上学习入门文章:. Redis是一个key-value存储系统. 和Memcached类似,但是解决了断电后数据完全丢失的情况,而且她支持更多无化的value类型,除了和string外,还支持lists(链表)、sets(集合)和zsets(有序集合)几种数据类型.

redis 配置

- - 谁主沉浮
# 当配置中需要配置内存大小时,可以使用 1k, 5GB, 4M 等类似的格式,其转换方式如下(不区分大小写). # 内存配置大小写是一样的.比如 1gb 1Gb 1GB 1gB. # daemonize no 默认情况下,redis不是在后台运行的,如果需要在后台运行,把该项的值更改为yes. # 当redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid,你可以配置到其他地址.

Cassandra代替Redis?

- - Tim[后端技术]
最近用Cassandra的又逐渐多了,除了之前的360案例,在月初的QCon Shanghai 2013 篱笆网也介绍了其使用案例. 而这篇 百万用户时尚分享网站feed系统扩展实践文章则提到了Fashiolista和Instagram从Redis迁移到Cassandra的案例. 考虑到到目前仍然有不少网友在讨论Redis的用法问题,Redis是一个数据库、内存、还是Key value store?以及Redis和memcache在实际场景的抉择问题,因此简单谈下相关区别.

redis 部署

- - CSDN博客云计算推荐文章
一、单机部署 tar xvf redis-2.6.16.tar.gz cd redis-2.6.16 make make PREFIX=/usr/local/redis install  #指定安装目录为/usr/local/redis,默认安装安装到/usr/local/bin. # chkconfig: 2345 80 10       #添加redhat系列操作系统平台,开机启动需求项(运行级别,开机时服务启动顺序、关机时服务关闭顺序) # description:  Starts, stops redis server.

nagios 监控redis

- - C1G军火库
下载check_redis.pl. OK: REDIS 2.6.12 on 192.168.0.130:6379 has 1 databases (db0) with 49801 keys, up 3 days 14 hours - connected_clients is 1, blocked_clients is 0 | connected_clients=1 blocked_clients=0.

转 redis vs memcached

- - 数据库 - ITeye博客
传统MySQL+ Memcached架构遇到的问题.   实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的不断增加,和访问量的持续增长,我们遇到了很多问题:.   1.MySQL需要不断进行拆库拆表,Memcached也需不断跟着扩容,扩容和维护工作占据大量开发时间.

Redis优化

- - 数据库 - ITeye博客
键名:尽量精简,但是也不能单纯为了节约空间而使用不易理解的键名. 键值:对于键值的数量固定的话可以使用0和1这样的数字来表示,(例如:male/female、right/wrong). 当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能,不过一般都要持久化比较安全,而且是快照和aof同时使用比较安全.

笔记--redis

- - 移动开发 - ITeye博客
接着准备面试内容,今天学习了下redis,继续我的笔记加深印象. 1.为什么要使用redis.  答:主要是 性能和 并发两个方面,另外redis也可以做分布式锁和消息队列等其他功能. 但是如果只是为了分布式锁这些其他功能,完全还有其他中间件(如zookpeer等)代替,并不是非要使用redis.