dot Redis 8 已推出——而且它是开源的

了解更多

如何在基础设施微服务中使用 Redis

早在 2019 年,我写了一篇关于如何在 Redis 中创建事件存储的文章。我解释说,Redis Streams 非常适合作为事件存储,因为它们允许您以不可变的仅追加机制(如事务日志)存储事件。现在,随着该博客中介绍的示例 OrderShop 应用程序的更新,我将演示如何使用 Redis 作为消息队列,进一步展示了 Redis Enterprise 超越缓存的众多用例。

快速了解微服务、基础设施服务和分布式系统

Redis 是创建消息队列和事件存储等基础设施服务的出色解决方案,但在使用微服务架构创建分布式系统时,需要考虑一些事项。关系型数据库通常适用于单体应用,但只有像 Redis 这样的 NoSQL 数据库才能满足微服务架构所需的扩展性和可用性要求。 

分布式系统意味着分布式状态。根据CAP 定理,一个软件实现只能提供这三个属性中的两个:一致性、可用性 分区容错性(因此得名 CAP)。因此,为了使您的实现具有容错性,您必须在可用性和一致性之间做出选择。如果您选择可用性,最终将实现最终一致性,这意味着数据将保持一致,但仅在经过一段时间后。选择一致性会影响性能,因为需要在整个分布式系统中同步和隔离写入操作。

溯源,它将业务实体(如订单或客户)的状态作为一系列状态变更事件持久化,优先选择可用性而不是一致性。它使得写入操作变得简单,但读取操作成本更高,因为如果它们跨越多个服务,可能需要额外的机制,例如读模型。

分布式系统中的通信可以是中介式或无中介式。无中介式风格广为人知,HTTP 是其最著名的例子。中介式方法顾名思义,在消息的发送者和接收者之间有一个中介。它解耦了发送者和接收者,实现了同步和异步通信。这使得行为更具弹性,因为消息消费者不必在消息发送时可用。中介式通信还允许发送者和接收者独立扩展。

(更多信息,请参阅我们关于如何选择您的同步和异步通信需求——Redis Streams、Redis Pub/Sub、Kafka 等的文章)

OrderShop:一个示例电子商务实现

微服务架构的“Hello World”是OrderShop,这是一个使用事件驱动方法实现的简单电子商务系统。这个示例应用使用了简单的领域模型,但它达到了应用程序的目的。

OrderShop 使用Docker Compose进行编排。所有网络通信都通过gRPC完成。核心组件是事件存储消息队列:每个服务都仅通过 gRPC 连接到它们。OrderShop 是一个 Python 示例实现。您可以在 GitHub 上查看 OrderShop 源代码

(注意:此代码不是生产就绪的,仅用于演示目的!)

运行与体验

  1. 使用 docker-compose up 启动应用
  2. 打开浏览器,访问 https://:5000/
    1. 查看事件并浏览状态
  3. 使用 python -m unittest tests/unit.py 运行客户端
  4. 在浏览器中打开新标签页,访问 https://:8001/
    1. 使用 redis:6379 连接到测试数据库
  5. 使用 docker-compose down 停止应用

OrderShop v2 架构

在这种情况下,服务器架构由多个服务组成。状态分布在多个领域服务中,但存储在一个事件存储中。读模型组件集中了读取和缓存状态的逻辑,如下图所示

上图显示了 OrderShop v2 的应用架构和数据流。

命令和查询通过消息队列组件通信,而事件通过事件存储组件通信,事件存储组件也充当事件总线。

基础设施服务

在 OrderShop v2 中,所有单播通信都通过消息队列组件进行。为此,我将使用Redis Lists,特别是将两个列表组合成一个所谓的“可靠”。它同步处理简单命令(例如,单个实体操作),但异步处理长时间运行的命令(例如,批量、邮件),并原生支持对同步消息的响应。

事件存储基于Redis Streams。领域服务(只是演示 OrderShop 功能的模拟服务)订阅了以事件主题(即实体名称)命名的事件流,并将事件发布到这些流中。每个事件都是一个流条目,事件时间戳充当 ID。流中已发布事件的总和构成了整个系统的状态。

应用服务

读模型使用领域模型将从事件存储中推导出的实体缓存在 Redis 中。如果不考虑缓存,它是无状态的。

API 网关也是无状态的,并在 5000 端口提供 REST-API 服务。它终止 HTTP 连接,并将它们路由到读模型用于读取状态(查询),或路由到专门的领域服务用于写入状态(命令)。读写操作之间的这种概念分离是一种模式,称为命令查询职责分离 (CQRS)。

领域服务

领域服务通过消息队列API 网关接收写操作。成功执行后,它们将每个事件发布到事件存储。相反,所有读操作都由从事件存储获取状态的读模型处理。

CRM 服务(客户关系管理服务)是无状态的——它订阅了来自事件存储的领域事件,并使用邮件服务向客户发送电子邮件。

核心领域实体是订单。它有一个名为“status”的字段,其状态转换使用状态机执行,如下图所示。

上图显示了订单可能处于的状态。

这些转换在几个事件处理器中完成,这些处理器订阅了领域事件(SAGA 模式),例如

def order_created(self, _item):
        if _item.event_action != 'entity_created':
            return

        order = json.loads(_item.event_data)
        rsp = send_message('read-model', 'get_entity', {'name': 'cart', 'id': order['cart_id']})
        cart = rsp['result']
        result = self._decr_from_cart(cart)
        order['status'] = 'IN_STOCK' if result else 'OUT_OF_STOCK'
        self.event_store.publish('order', create_event('entity_updated', order))
def billing_created(self, _item):
        if _item.event_action != 'entity_created':
            return

        billing = json.loads(_item.event_data)
        rsp = send_message('read-model', 'get_entity', {'name': 'order', 'id': billing['order_id']})
        order = rsp['result']
        if order['status'] != 'IN_STOCK':
            return

        order['status'] = 'CLEARED'
        self.event_store.publish('order', create_event('entity_updated', order))

客户端

客户端是使用 Python 的单元测试框架模拟的。目前实现了 10 个单元测试。请查看 tests/unit.py 以获取更多详情。

一个简单的 UI 在 5000 端口提供服务,用于查看事件和浏览状态(使用WebSockets)。

还提供了 RedisInsight 容器,用于检查 Redis 实例。打开网页浏览器,访问 https://:8001/,并使用 redis:6379 连接到测试数据库。

上方的动画展示了 RedisInsight,这是功能非常强大的 Redis GUI。

结论

Redis 不仅是领域层(例如,目录搜索)和应用层(例如,HTTP 会话存储)中的强大工具,而且也是基础设施层(例如,事件存储或消息队列)中的强大工具。在这些层中全面使用 Redis 可以减少运维开销,并让开发者重用他们已经熟悉的技术。

您可以看看代码并尝试实现它。我希望这能帮助您了解 Redis 在领域和基础设施服务中的多功能性和灵活性,并证明它可以用于缓存之外的更多用途。 

请在 Twitter 上告诉我进展:@martinez099