微服务

微服务架构可视为面向服务架构的一个子集。

从根本上说,微服务是由独立的小进程组成的复杂应用程序,这些小进程通过语言无关的应用程序接口进行通信,使每个服务都能相互访问。微服务可以作为服务单独部署。

在微服务中,业务逻辑被分离成独立的松散耦合服务。微服务的一个关键原则是,每个数据库都应拥有自己的数据库,这对于确保微服务不会彼此紧密耦合至关重要。

通过降低单个服务的复杂性,我们可以减少该服务的故障点。从理论上讲,让单个服务遵守 "单一责任原则"(Single Responsibility Principle),就能更容易地进行调试,并降低整个应用程序出现故障的几率。

在计算机科学中,CAP 定理规定,在给定的分布式计算机系统中,不可能同时保证一致性、可用性和分区容忍性。

想象一下,两个分布式数据库都包含一个用户的电子邮件地址。如果我们要更新这个电子邮件地址,我们没有办法同时更新两个数据库中的电子邮件地址,也没有办法将两个数据集重新整合在一起。在分布式系统中,我们必须延迟访问数据以验证数据的一致性,或者提供一个未更新的数据副本。

这就给传统的数据库事务处理带来了困难。因此,在微服务架构中处理数据的最佳方法是使用最终一致的事件驱动架构。

每当有变化时,每个服务都会发布一个事件,其他服务可以订阅该事件。当收到事件时,数据就会相应地更新。这样,应用程序就能在多个服务之间保持数据一致性,而无需使用分布式事务。

为了了解如何在微服务之间的通信中实现这种进程间通信架构,请参阅本章的消息队列模式(RabbitMQ 入门)部分。

在这种情况下,减轻这种限制的一个简单方法是使用时间验证系统来验证数据是否一致。因此,我们为了一致性和分区容忍性而放弃了可用性。

如果你能预见到在特定的微服务架构中会出现这个问题,那么最好的办法通常是将需要满足 CAP 定理的服务集中到一个服务中。

让我们来看看由以下微服务组成的披萨外卖网络应用程序:

  • User

  • Deals

  • Recipe

  • Cart

  • Billing

  • Payments

  • Restaurant

  • Delivery

  • Pizza

  • Reviews

  • Frontend microservice

在这个例子中,我们可以有以下用户旅程:

  1. 用户使用用户微服务进行身份验证。

  2. 用户可以使用 Deals 微服务选择优惠。

  3. 用户使用配方微服务选择要订购的披萨。

  4. 使用购物车微服务将选定的披萨添加到购物车。

  5. 通过计费微服务优化计费凭证。

  6. 用户使用付款微服务付款。

  7. 使用餐厅微服务将订单发送给餐厅。

  8. 餐厅烹制好食物后,外卖微服务会派司机上门取餐并送餐。

  9. 一旦送餐微服务显示食物已经送达,用户就会被邀请使用 "评论" 微服务完成评论("评论" 微服务会使用 "用户" 微服务通知用户)。

  10. 使用前端微服务将网页前端封装在一起。

前端微服务可以是一个简单的微服务,它消耗其他微服务并将内容呈现给网络前端。前端可以通过 REST 与其他微服务通信,也可以通过浏览器中的 JavaScript 客户端实现,还可以是一个 PHP 应用程序,只充当其他微服务 API 的消费者。

无论哪种方式,在 API 的前端消费者和后端之间放置一个网关通常都是一个好主意。这样我们就可以在确定与微服务的通信之前放置一些中间件;例如,我们可以使用网关查询用户微服务,在允许访问购物车微服务之前检查用户是否获得授权。

如果使用 JavaScript 直接与微服务通信,当网络前端试图与不同主机名/端口的微服务通信时,可能会出现跨源问题。

为了换取网关的这种便利性,您可能会感觉到网关的缺点,即您将有另一个系统需要操心,并需要额外的响应时间(不过,如果您想提高网关的性能,可以在网关级别添加缓存)。

如果增加一个网关,我们的架构现在可以是这样的:

image 2023 10 31 12 18 58 491

PHP 中越来越多地出现了微框架,如 Lumen、Silex 和 Slim;这些都是面向 API 的框架,可以轻松构建微服务来支持我们的应用程序。尽管如此,您最好还是采用更轻量级的方法,只需在需要时通过 Composer 调入所需的组件即可。

请记住,添加另一种技术或框架会增加整体情况的复杂性。不仅要考虑实施新解决方案的技术原因,还要考虑这将如何有利于客户和架构。微服务不是增加不必要复杂性的借口: Keep It Simple, Stupid