我们现在是,简单地说,Redis的

了解更多

为你的同步和异步通信需求选择什么- Redis流,Redis Pub/Sub, Kafka,等等。



返回博客

让我们来谈谈通信工具和模式。随着Redis的引入流,我们现在有另一种通信模式除了Redis的发布/订阅和其他工具,如卡夫卡和RabbitMQ的考虑。在这篇文章中,我会引导你通过各种通信模式的定义性特征,我将简要介绍用于实现每个最流行的工具。最后,我将离开你的小外卖,希望能帮助您构建更好的解决方案更快。

同步通信

在此上下文中,同步意味着所有参与者都需要同时处于活动状态,以便能够进行通信。最简单的形式是服务A和服务B执行直接远程过程调用(RPC),例如通过从服务A调用服务B的HTTP REST端点。如果服务B脱机,服务A将无法与B通信,因此A将需要实现内部故障恢复过程,这在大多数情况下意味着进行适当的降级。例如,如果无法访问Netflix的推荐服务,Netflix的“下一个观看”部分可以随机显示节目样本。

这些服务只会很好地降级,因为对于更敏感的用例(例如,支付服务请求订单服务开始处理支付订单),我将在下面描述的其他异步机制更常见。虽然RPC范例在一对一通信中工作得很好,但您偶尔需要支持一对多或多对多。此时,您有两个主要选择:无代理工具或代理工具。

Brokerless

无代理意味着参与者仍然是直接连接的,但是可以选择使用与RPC不同的模式。在这个类别中,我们有这样的库ZeroMQ最近nanoMsg.它们被正确地描述为“在类固醇上的TCP套接字”。在实践中,您将库导入到代码中,并使用它实例化一个连接,该连接可以使用各种内置的消息路由机制,如Pub/Sub、Push/Pull、Dealer/Router等。

撮合

撮合意味着与会者连接到相同的服务,其作用,顾名思义,作为一个中央的代理来实现整个路由信息的机制。虽然这种体系结构通常被描述为星形,与经纪人作为星形的中心,代理本身可以是(并且通常是)在群集系统。

在这一类别中,Redis的发布/订阅是独一无二的,据我所知。您仍然可以使用工具,像NATS或RabbitMQ的持久性用于该用途的情况下,因为它们允许你关闭持久性,但只有纯粹的同步消息经纪人,我知道的是Redis的。所不同的是不只是在坚持,但在可靠的传送(即,应用程序级别的ACK)对的总体思路发射后不管。RabbitMQ的默认为以前的行为而Redis的发布/订阅的重点只是在做工作的最低限度的金额发射后不管。你可以想像,这在性能的影响(有作为毕竟免费的午餐没有这样的事情),但可靠的交付并不适用于更广泛的使用情况。

由于Streams在Redis 5版本之前是不可用的,一些人选择使用Pub/Sub,因为他们想要更好的交付保证,现在正在进行切换。因此,如果你正在构建一个新的应用程序,或者对当前使用发布/订阅的应用程序不满意,那么考虑Redis Streams,如果你需要的是“发布/订阅,但有在断开连接时不丢失消息的恢复能力”。

优点和缺点

无代理工具是你能想到的最快的通信方法,甚至比Redis Pub/Sub还快。他们不幸的是不能抽象复杂,如需要每个参与者知道所有其他人的位置以连接到它们,或复杂的失败场景,你通常不需要处理代理系统中(例如,参与者mid-fanout死去的情况)。

在这种情况下,使用Redis Pub/Sub的好处在于不必放弃太多的吞吐量,而得到一个简单的、无处不在的基础设施和一个小的集成面。你只需要一个Redis客户端为你的语言,并可以使用发布(P)订阅传递信息。

异步通信

当然,异步意味着即使不是所有的参与者都在同一时间,通信仍然可以发生。要启用此模式,必须持久化消息,否则在出现故障时将无法保证传递。这类工具主要由基于队列或基于流的解决方案组成。

基于队列异步通信

这是执行异步通信和大多数面向服务的架构(SOA)的基础的“传统”的方式。我们的想法是,当一个服务需要与另一通信时,它留下的消息在中央系统中的其他服务将在稍后回升。在实践中,这些消息的收件箱就像是任务队列。

这些系统的另一个期望是,任务彼此独立。在由多个相同的消费者并行处理,这意味着它们可以(几乎总是),通常被称为工人。此属性还使独立的失败,这是许多工作负载一个很好的功能。作为一个例子,是无法处理来自一个用户(可能的由于缺少配置文件信息或其他琐碎的问题),付款也不能阻止所有用户的整个支付处理流水线。

这类工具中最知名的是RabbitMQ,其次是大量使用AMQP(兔子的本地协议)或MQTT(类似的开放标准)的其他工具和云服务。通常的做法是通过框架来使用RabbitMQ,框架提供了一种简单的方式来实现各种重试策略(例如,指数回退和死信),以及一个让在特定客户端生态系统中处理消息更习惯化的接口。其中一些框架是“不起眼的”任务队列,比如Sidekiq(红宝石),芹菜(Python),Dramatiq(Python)等。其他的是“更严肃的”企业服务总线(esb),比如NServiceBus(c#),MassTransit(c#),Apache Synapse(Java)或Mulesoft(Java)。

这个模式的简单版本(任务队列)也可以直接使用Redis Lists来实现。Redis的阻塞和原子操作使得构建定制解决方案非常容易。特别值得一提的是苦厄,它在JavaScript任务队列的漂亮实现中使用了Redis。

基于流的异步通信

首先,值得注意的是,使用流的最简单方式只是作为一种存储形式。流是不可变的、只能追加的时间定向条目系列,许多类型的数据自然适合这种格式。例如,传感器读数或日志包含的值本质上是按创建时间建立索引的,并且只能追加(您不能更改过去的值)。它们还具有相当规则的结构(因为它们倾向于保持相同的字段集),流可以利用这个属性来提高空间效率。这种类型的数据很适合流,因为访问数据的最直接方式是检索给定的时间范围,而流可以以非常有效的方式做到这一点。

回到我们的通信用例,所有流实现也允许客户端跟踪流,在添加新条目时接收实时更新。这有时被称为观察或订阅流。使用Streams作为通信工具最简单的方法是将你原本通过Pub/Sub发布的内容推送到流中,基本上创建一个可恢复的Pub/Sub。每个订阅者只需要记住它处理过的最后一个入口id,所以如果出现崩溃或断开连接,它可以很容易地恢复。

通过流实现服务对服务通信也是可能的(有时是更好的),进入流媒体架构.这里的主要概念是,我们之前描述的任务/消息,现在将是一个事件。在基于队列的设计中,任务被另一个希望它做一些事情的服务推到服务的队列中,但在流架构中,相反的情况发生了:每个服务都将状态更新推到自己的流中,然后被其他服务观察。

这种设计上的变化有许多微妙的含义。例如,您可以稍后添加新服务,并让它们浏览整个流历史。在队列中,这是不可能的,因为任务一旦完成就会被删除,而这些系统中通常表达的通信方式现在允许这样做(考虑命令式和功能性)。

流具有双重性质:数据结构和通信模式。有些数据很自然地适合于此(例如,日志),服务之间的通信不一定要基于任务队列。完全拥抱这种双重本性的实践被称为事件的采购

事件的采购,您将业务模型定义为无穷无尽的事件流,并让业务逻辑和其他服务对其作出反应。完成这种转换并不总是容易的,但当处理诸如“对象X在时间Y时的状态是什么?”,否则,如果没有适当的审计记录,就很难回答或直接不可能。

也许您的普通移动应用程序不需要事件源,但对于需要处理客户个人数据、运输和其他“混乱”领域的企业软件来说,它可能会有很大的帮助。万博电竞客服

要实现这些类型的模式,有很多工具可以使用。当然,我们应该从房间里的大象开始:Apache Kafka,以及Apache Pulsar(来自雅虎)和其他语言的Kafka的重新实现,加上一些SaaS产品。最后,还有一个新来者:Redis Streams。

Apache的卡夫卡对Redis的流

首先,注意Redis所谓的“流”,Kafka称之为“主题分区”,在Kafka中,流是一个完全不同的概念,围绕Kafka主题的内容进行处理。

也就是说,就表达能力而言,这两个系统是等价的:您可以在任意一个系统上实现相同的应用程序,而无需对数据建模方式进行任何实质性的更改。一旦你深入到实际细节中,差异就会开始出现,它们是很多的和实质性的。

卡夫卡已经存在了很长一段时间,人们已经成功地构建可靠的流架构它是检验真理的唯一来源。但是,如果你打开在开发和操作,以及需要亚毫秒延迟尝试新的技术,价值简单,那么Redis的流可以填写你的架构非常相似的地方。

当我说简单,我是认真的。如果你从未尝试过Redis Streams,即使你计划在生产中使用Kafka,我建议你尝试用Redis Streams原型你的应用程序,因为它确实需要几分钟在你的笔记本电脑上启动和运行。

具体使用情况格局

让我们考虑几个例子,看看哪种模式能最好地解决哪些问题。

Brokerless同步通信

如果你想让几个客户端设备(如电话、Arduino)在局域网内相互通信,或者与计算机上运行的程序通信,那么找到有效解决方案的最短路径可能是“增强的TCP连接”。

安排同步通信

一个IRC风格的聊天应用程序(即,没有历史),或挥发性日志/事件插件和播放实时处理管道与撮合方式效果很好。本杰明警长谈到这最后的用例RedisConf19旧金山幻灯片).

基于队列异步通信

Web爬行程序经常依赖于这种模式,以及许多无法立即响应请求而完成操作的Web服务。例如,想想YouTube上的视频编码。

基于流的异步通信

这种技术最适用于日志处理、物联网(IoT)设备和微服务,以及懒汉风格的聊天应用程序(即历史记录)。

Redis的与世界

在结束之前,我想留给你们最后一个考虑。Redis真正的超级力量在于它不是只是发布/订阅消息传递系统、队列或流服务。它也不仅仅是一个通用数据库。实际上,只要有足够的毅力,您就可以在关系DBMS上实现上面描述的每一个模式,但是有一些实际的原因说明这不是一个好主意。

复述,提供了一个真正的Pub/Sub fire-and-forget系统,以及真正的流数据类型。此外,Redis的模块,Redis的也支持许多不同的数据类型的实际实现。让我来映射这种说法回到我们的坚持和不持续的聊天应用程序使用情况。

IRC-style聊天软件

以上,我得出结论的Pub / Sub本来是正确的选择,因为这种类型的聊天应用程序的只需要发送消息到连接的客户端。但是,要实现这个应用程序,甚至一个简单的版本,你还不得不考虑全球频道列表以及每个频道的用户列表,该列表。你在哪里存储状态?你如何保持它最新的,尤其是当服务实例意外死亡?在Redis的,答案很简单:排序集合,到期键和原子操作。如果您使用RabbitMQ的,你需要一个DBMS。

Slack-style聊天软件

对话可以很自然地以消息流的形式表达。我们甚至不需要将事件源引入其中,因为它已经是该数据类型的原生结构。那么为什么在这个例子中Redis胜过Kafka呢?就像以前一样,你的聊天系统将不仅仅是一个消息流。有不同的渠道和状态以不同的方式最好地表现出来。还有“用户X正在输入……”功能:信息是不稳定的,您希望将其发送给所有参与者,但只在他们连接时。如果你正在使用Kafka,你将需要旋转一个Pub/Sub系统。

外卖

在分布式系统中,当您需要协调时,通常需要共享状态,反之亦然。忽视这一事实往往会导致过于复杂的解决方案。Redis非常理解这一点,这也是其独特设计背后的原因之一。如果你接受这个原则,你会发现一些困难的问题偶尔可以在几个命令中解决,只要有正确的原语,这正是Redis给你的。例如,这是如何以事务方式将一个条目添加到流中,将任务推到队列(开头),并发布到Pub/Sub:


XADD logs:service1 * level error request -id 42 stack-trace "…"
LPUSH actions-queue“重启= service1”
发布实时通知“service1中新增错误事件!”
执行

要得到一个有效的解决方案,您需要克服7个并发问题。
的情节Redis的朝圣者主场迎战世界

我希望这给你,通常是由分布式系统用于通信的主要模式的理解。您需要将两个服务连接在一起接下来的时间,这将帮助您浏览选项。如果你喜欢谈论类固醇TCP连接和流媒体架构,可随时到达我的Twitter@croloris

Redis Streams数据类型是Redis的一个伟大特性,将成为许多应用程序的构建块,特别是现在Redis有一个模块池,添加了新的成熟的功能时间序列搜索.想要了解更多关于Redis Streams的信息,请点击这里介绍博客文章由Antirez,以及官方文档.但是不要忘记,流是不正确的工具每一个工作:有时你需要Pub/Sub,或简单的阻塞操作的Redis Lists(或排序集,Redis的有太).

Baidu