Redis如何简化实现微服务的设计模式 – thenewstack


本文讨论Redis如何简化微服务中设计模式的实现:例如有界上下文,异步消息传递,基于编排的sagas,事件源,CQRS,遥测等。
微服务架构继续变得越来越流行,但是却被广泛误解。尽管大多数概念上都同意微服务应该是细粒度的且面向业务的,但通常对于架构的权衡和复杂性缺乏认识。例如,对于DevOps架构师来说,将微服务与Kubernetes相关联是很常见的事情,而对于应用程序开发人员来说,将实现归结为使用Spring Boot的情况很常见。尽管这些技术是相关的,但是容器和开发框架都无法独自克服微服务架构的陷阱,尤其是在数据层。
Martin FowlerChris Richardson以及其他思想领袖早已解决与微服务架构和定义的相关特性权衡,成功指导实施。包括的原则又:隔离、自主团队授权、拥抱最终一致性以及基础设施的自动化。虽然遵循这些原则可以避免早期采用者和DIY者所遭受的痛苦,但将它们合并到体系结构中的复杂性扩大了对最佳实践和设计模式的需求,尤其是随着实现扩展到数百个微服务时。
随着Redis迅速成为微服务体系结构的主要组成部分,值得讨论如何简化设计模式的实现-例如有限上下文,异步消息传递,基于编排的sagas,事件源,CQRS,遥测等。
 
设计模式:受限上下文/有界上下文/界限上下->域驱动设计
我们的第一个挑战是从逻辑上将业务划分为多个微子域,以便每个人都可以由一个授权的小型自治团队提供支持。每个子域的范围应受其团队管理其支持的微服务生命周期的能力的约束-从开始到后期制作。这种从临时项目到自主域所有权的转变激发了对微服务设计各个方面的责任感,并赋予了敏捷的决策能力-从而缩短了产品上市时间。
考虑一下前缀“微”,这暗示了在有限的业务子域中支持微服务的整个生命周期所需的团队规模。
在我们的模型体系结构的上下文中,让我们从付款处理域开始,开始组织设计过程,该域包括欺诈检测,付款,结算等。由于这个范围对于一个小型团队来说可能太复杂了,因此,我们选择将其所有权范围缩小到欺诈检测子域。

欺诈检测由工作流的前三个微服务组成-包括数字身份,统计分析和基于AI的交易风险评分。由于他们的范围可能仍然太小而无法管理,因此让我们将欺诈检测进一步分为两个子域-最终看起来更易于管理。
在非常高的层次上,我们刚刚遵循的过程称为域驱动设计(DDD),该过程受推荐的模式支持,该模式将每个微服务的范围和所有权声明绑定到一个称为bounded context的业务子域。但是请稍等-Redis适合放置在哪里?
注意,每个微服务都有其自己的专用数据库以进行隔离。拥有蓝色边界上下文的授权自治团队选择RediSearch支持其“ Authenticate Digital Identity”微服务,并选择RedisBloom支持其“概率欺诈检测检查点”微服务。同时,拥有紫色边界上下文的另一个团队选择RedisAI实时支持“交易风险评分”。
尽管每个微服务都需要自己的最佳数据模型来处理其独特的数据访问模式和SLA,但使用Redis就不必需要评估、内置、管理三个不同的数据库。实际上,使用Redis Enterprise,他们可以在单个多租户群集中部署所有三个Redis数据库,而无需考虑其发布周期。
 
设计模式:异步消息传递->服务间通信
既然我们已经为每个微服务确定了一个有限的上下文和最佳数据模型,那么下一个挑战就是在不破坏对隔离的合规性的情况下实现它们之间的通信。这可以通过拥抱最终的一致性来解决,后者假定服务间通信的接收端上的微服务在出站传输期间将不可用,但是,一旦恢复可用性,便可以使用该消息。

服务间通信的推荐模式是使用发布-订阅消息代理作为其事件分发中心的异步消息传递。在这种模式下,生产者可以发布事件,而不必了解是否有任何消费者在收听,并且(以相同的方式)该事件的消费者可以在方便时对其做出反应或完全忽略它。这通常是事件驱动架构的基础。
由于我们已经选择Redis作为多个微服务的主要数据库,因此我们可以通过使用Redis Streams来实现此模式,从而简化架构。Redis Streams是不可变的按时间排序的日志数据结构,它允许生产者将异步消息发布到多个订阅的使用者。这确保了发布事件的微服务将与使用它们的微服务保持脱钩状态,因此不会对可用性和发布周期产生交叉依赖。此外,Redis Streams可以配置为处理不同的交付保证,支持消费者群体以及本质上类似于Kafka的其他细微差别-也是跨微服务架构的主要内容。
 
设计模式:基于编排的Saga->分布式事务
现在,我们已经启用了服务间通信,接下来的挑战是处理跨越多个有界上下文的事务,而又不会破坏对隔离的合规性。在过去,这是很简单的实现,因为事务范围内的所有操作都是针对单个RDBMS执行的,该RDBMS提供行锁定,死锁检测和回滚功能。一旦数据跨多个数据库分布,两阶段提交协议(2PC)就成为分布式事务的标准。但是,尽管这两种方法都可行,但在设计时并没有考虑到最终的一致性。
如果我们假设在分布式事务期间将不存在依赖项,那么我们还应该假设频繁的回滚将导致整个系统的零星不可用性—这既不是云本机,也不是缩短产品上市时间。
这可以通过放宽对ACID保证的严格要求来解决,ACID保证已经支持了大多数传统体系结构数十年来的关系数据库。尽管关系数据库在微服务体系结构中占有一席之地,但它们的相关性却变得越来越重要。例如,如果不要求引用完整性,那么为什么没有授权的自治团队会选择使用NoSQL数据库优化其微服务,该数据库专门用于处理其特定的数据访问模式和SLA。

回想一下,我们的付款处理工作流程由多个微服务组成,这些微服务组织到单独的有界上下文中,并由Redis(NoSQL数据库)支持。在这种情况下,推荐的处理分布式事务的模式是基于编排的Saga,它执行一系列隔离的本地事务,并发布事件,从而促进工作流阶段之间的过渡。
参与Saga的每个微服务都将仅侦听其自己的与工作流相关的事件,该事件将通知它执行本地数据库事务,然后将其自己的事件发布给消息代理。这种事件驱动的编排可以包括补偿用于回滚的微服务和用于复杂业务流程的决策服务。
值得注意的是,在基于编排的Saga中,没有中央协调器,这避免了将参与的微服务的发布周期耦合在一起。但是,它并不总是正确的解决方案。在某些情况下,强一致性是绝对必要的,例如帐户转帐。在这种情况下,基于编排的Saga可能会更适合,或者在同一有限上下文中依靠微服务之间的2PC。
 
设计模式:事务发件箱和消息中继->一致性
既然我们已经设计了跨越多个有界上下文的事务,那么我们的下一个挑战是减轻微服务的数据库和消息代理之间的不一致风险,即使两者都使用Redis。回想一下,在前两种设计模式中,每个微服务都在本地提交到其数据库,然后发布了一个事件。如果使用双写模式的某些变体来实现此目的,则通信可能会丢失并且分布式事务可能会变得孤立(尤其是在云环境中)。

可以将代码复杂性添加到每个微服务中,以处理各种失败和不一致的情况,但是请考虑将这种工作累加到数百个团队中,并考虑不正确实施的风险-所有这些都不会增加业务价值。

为了避免各种应用程序级别实现的风险和成本,建议的模式是事务发件箱和消息重播。Redis通过使用Redis Streams作为事务发件箱,并使用RedisGears作为消息中继,简化并支持这两种模式的组合实现,称为后。在Redis中,辅助线程可以侦听更改的数据事件,按时间顺序持久地存储它们,并在可用时将其发布到消息代理。可以通过基础架构自动化在每个Redis数据库上统一启用或升级此功能。
 
设计模式:遥测->可观察性
现在,我们已经减轻了主数据库和辅助数据平台之间不一致的风险,我们的下一个挑战是衡量整个体系结构及其支持的业务交易中微服务的运行状况,即可观察性
观测是充满了数以百计的分布式系统中的一个必须具备的孤立和最终一致的部件。
可观察性建立在三大支柱上:指标,日志记录和可追溯性。我们将首先关注指标,这些指标通常存储在时间序列数据模型中,该数据模型可以处理大量按时间顺序排列的事件和时间点查询。最佳地,可以实时跟踪指标,以便可以检测到SLA / SLO异常,并在发生异常时可以缓解这些异常。

为了观察分布式系统的运行状况,我们首先需要它的数据。推荐的模式是遥测,它是从远程源自动收集和传输数据以进行监视。Redis通过其后功能将数据无缝推入另一个Redis数据模型RedisTimeSeries中,从而简化了该模式的实现。注意,使用Redis,我们只需要一个平台即可实现此模式。
现在,RedisTimeSeries中提供了指标,我们可以跨多个维度实时查询它们-业务KPI,应用程序SLA / SLO,基础架构利用率等。使用RedisInsight可视化基础架构级别的指标。以及使用用于GrafanaRedis数据源可视化业务级别指标。
 
设计模式:事件源->审核和重播
既然我们已经对度量标准数据实施了遥测,那么我们的下一个挑战是启用可观察性的其余支柱-日志记录和可追溯性。与指标不同,时间序列数据模型将无法使日志的固有属性受益,因为它们无法汇总或缩减采样。相反,它们需要一个不可变且按时间排序的数据结构,该结构可用于按事件发生的顺序审核,恢复或重放事件链。
由于微服务需要隔离,因此它们不能依赖共享的RDBMS来维护捕获整体中所有事件的事务日志。因此,推荐的模式是事件源,它在微服务数据库级别的不可变且按时间排序的日志中记录每个更改的数据事件。这种模式在大多数事件驱动的体系结构中很常见。

通常使用消息代理和事件存储来实现事件源。回想一下,我们已经实现了使用RedisGears捕获更改数据事件并将其存储在Redis Streams中的模式,Redis Streams是一个不可变的按时间顺序排列的日志数据结构。因此,Redis可用作数据库,消息代理和事件存储。
Redis Streams还可以通过允许外部进程作为隔离的消费者组订阅其事件流,从而超出单个微服务的范围来简化事件源。这允许在业务流程,域甚至架构级别进行观察。例如,工作流仲裁器或系统范围的分析平台。
现在,我们已经在Redis Streams中捕获了更改数据事件,我们可以使用可观察性的不同过滤器(微服务ID,事务关联ID等)以本地方式可视化它们。
 
设计模式:命令查询责任隔离(CQRS)->性能

请注意,当我们定义与欺诈相关的有界上下文时,我们省去了付款处理工作流程的最后阶段。这是因为其授权的自治团队选择了非Redis数据库来支持其微服务。

因此,现在让我们假设“批准| 基于磁盘的数据库支持“拒绝付款”微服务,该数据库并未针对查询性能进行优化。由于它大概具有强大的耐用性保证,因此它是记录保存的合理选择-但是,如果其有限上下文还包含需要此数据进行查询的微服务,该怎么办。这意味着我们的下一个挑战是在Redis不是记录系统时优化查询性能。
推荐的模式是CQRS,它将数据集的写(命令)和读(查询)责任分开。通过使用单独的数据库来实现CQRS,可以优化数据结构或数据模型,以适应隔离区两侧的数据访问模式及其各自的SLA。由于我们的目标是优化性能,因此数据复制的方向通常会从基于磁盘的数据库(例如MongoDB,Cassandra,RDBMS等)导入Redis。
这就是要抓到的地方–要实现这种模式,我们将需要解决近实时连续数据复制问题,保持异构数据库之间最终的一致性,并转换数据以避免Command和Query数据结构之间的阻抗不匹配。这听起来应该很熟悉,因为我们是在Redis是源数据库时执行此操作的-回忆事务发件箱和消息中继模式。但是,由于在这种情况下,Redis是目标,而其他大多数数据库都不支持后写操作,因此我们需要外部实现来复制更改后的数据事件。
在这种情况下,我们可以通过使用可以与Command和Query数据库集成的Change Data Capture(CDC)框架来简化CQRS的实现。CDC框架通常使用事务日志拖尾轮询发布者模式来扫描Command数据库上的更改数据事件,并将它们作为转换后的有效负载复制到Query数据库。请注意,这与在缓存旁模式中使用Redis有何不同,因为它不会在微服务级别耦合数据库—保持隔离。
 
设计模式:共享数据->可重用性
既然我们已经解决了在Redis不在记录系统中时优化性能的问题,那么我们的下一个挑战是处理微服务之间的共享数据,这些服务由不同的受限上下文或微服务体系结构之外的数据库分隔。
后者的一个真实示例是一个扼杀程序从单片架构迁移到微服务架构。在这种情况下,作为混合云部署的一部分,微服务的数据库可能依赖于外部记录系统多年,甚至无限期依赖。当我们引入CQRS模式时,我们实际上已经解决了这个问题,但是让我们扩展问题说明,使其包含共享相同数据和数据访问模式的微服务。
在这种情况下,以下是一些适用的模式,其中Redis简化了实现:



这是要抓住的地方-尽管这些模式解决了一些有限上下文之间的共享数据,但它们都无法在全球范围内扩展。

对于全局数据,建议的模式是API网关的隔离数据库。由于该数据库可能会被流过体系结构的每个事务访问,因此我们必须将业务连续性,可伸缩性和性能视为选择该数据库的关键成功标准。幸运的是,这是Redis Enterprise在数千个生产部署中大放异彩的地方。
Redis Enterprise是关键任务会话数据,身份验证令牌和临时数据存储的事实上的标准,具有亚毫秒级的规模性能,并具有Active-Active跨集群复制和多可用性区域部署的99.999%SLA。