现在很多公司,例如 Amazon、阿里 和Netflix,已经通过采用称为微服务架构模式的方式解决单体地狱问题。与其构建一个庞大的单体应用程序,不如将您的应用程序拆分为一组更小的、相互连接的服务。
服务通常实现一组不同的特性或功能,例如订单管理、客户管理等。每个微服务都是一个微型应用程序,具有自己的六边形架构,由业务逻辑和各种适配器组成。一些微服务会公开一个由其他微服务或应用程序客户端使用的 API。其他微服务可能会实现 Web UI。在运行时,每个实例通常是云虚拟机或 Docker 容器。
例如,打车软件系统的可能分解如下图所示:
示例乘车应用程序的微服务架构,每个微服务都提供一个 RESTful API
微服务架构模式
应用程序的每个功能区域现在都由其自己的微服务实现。此外,Web 应用程序被拆分为一组更简单的 Web 应用程序(例如,在我们的出租车打车示例中,一个用于乘客,一个用于司机)。这使得为特定用户、设备或特殊用例部署不同的体验变得更加容易。
每个后端服务都公开一个 REST API,大多数服务使用其他服务提供的 API。例如,Driver Management 使用通知服务器来告知可用的司机有关潜在行程的信息。UI 服务调用其他服务以呈现网页。服务也可能使用异步的、基于消息的通信。本系列稍后将更详细地介绍服务间通信。
一些 REST API 也暴露给司机和乘客使用的移动应用程序。但是,这些应用程序不能直接访问后端服务。相反,通信由称为API 网关的中介进行调解。API Gateway 负责负载均衡、缓存、访问控制、API 计量和监控等任务,并且可以使用 NGINX 有效地实现API 网关。
在 Y 轴上将功能分解为微服务的“Scale Cube
微服务架构模式对应于Scale Cube的 Y 轴缩放,这是来自优秀书籍The Art of Scalability 的可扩展性的 3D 模型。另外两个缩放轴是 X 轴缩放,它包括在负载均衡器后面运行应用程序的多个相同副本,以及 Z 轴缩放(或数据分区),其中请求的属性(例如,主键行或客户身份)用于将请求路由到特定服务器。
应用程序通常一起使用这三种类型的缩放。Y 轴缩放将应用程序分解为微服务,如本节第一张图所示。在运行时,X 轴扩展在负载均衡器后面运行每个服务的多个实例,以提高吞吐量和可用性。一些应用程序还可能使用 Z 轴缩放来划分服务。下图显示了旅行管理服务如何与在 Amazon EC2 上运行的 Docker 一起部署。
用于搭车服务的示例微服务应用程序,部署在 Docker 容器中并由负载均衡器提供前端
在运行时,行程管理服务由多个服务实例组成。每个服务实例都是一个 Docker 容器。为了实现高可用性,容器在多个云虚拟机上运行。在服务实例前面是一个负载均衡器,例如 NGINX,它在实例之间分配请求。负载平衡器还可以处理其他问题,例如缓存、访问控制、API 计量和监控。
微服务架构模式显着影响应用程序和数据库之间的关系。每个服务都有自己的数据库模式,而不是与其他服务共享单个数据库模式。一方面,这种方法与企业级数据模型的想法不一致。此外,它通常会导致某些数据重复。但是,如果您想从微服务中受益,那么每个服务都有一个数据库模式是必不可少的,因为它可以确保松散耦合。下图显示了示例应用程序的数据库架构。
乘车服务示例微服务应用程序中的数据库架构
每个服务都有自己的数据库。此外,服务可以使用最适合其需求的数据库类型,即所谓的多语言持久性架构。例如,寻找靠近潜在乘客的司机的司机管理必须使用支持有效地理查询的数据库。
从表面上看,微服务架构的模式类似于 SOA。使用这两种方法,架构都由一组服务组成。然而,考虑微服务架构模式的一种方式是,它是一种 SOA,没有Web 服务规范和企业服务总线 (ESB) 的商业化和感知包袱。基于微服务的应用程序更喜欢更简单、轻量级的协议,例如 REST,而不是 WS-*。他们也非常避免使用 ESB,而是在微服务本身中实现类似 ESB 的功能。微服务架构模式也拒绝 SOA 的其他部分,例如规范模式的概念。
微服务的好处
微服务架构模式有许多重要的好处。首先,它解决了业务复杂性问题。它将原本庞大的单体应用程序分解为一组服务。虽然功能总量不变,但应用程序已被分解为可管理的业务或服务。每个服务都有一个以 RPC 或消息驱动的 API 形式定义好的边界。微服务架构模式强制执行一定程度的模块化,在实践中,使用单体代码库很难实现。因此,单个服务的开发速度要快得多,并且更容易理解和维护。
其次,这种架构使每个服务都可以由专注于该服务的团队独立开发。开发人员可以自由选择任何有意义的技术,前提是服务遵守 API 合同。当然,大多数组织都希望避免完全的无政府状态并限制技术选择。然而,这种自由意味着开发人员不再有义务使用在新项目开始时可能已经过时的技术。在编写新服务时,他们可以选择使用当前技术。此外,由于服务相对较小,使用当前技术重写旧服务变得可行。
第三,微服务架构模式使每个微服务都可以独立部署。开发人员永远不需要协调其服务本地更改的部署。这些类型的更改可以在经过测试后立即部署。例如,UI 团队可以执行 A/B 测试并快速迭代 UI 更改。微服务架构模式使持续部署成为可能。
最后,微服务架构模式使每个服务都可以独立扩展。您可以仅部署满足其容量和可用性限制的每个服务的实例数量。此外,您可以使用最符合服务资源要求的硬件。例如,您可以在 EC2 计算优化实例上部署 CPU 密集型图像处理服务,并在 EC2 内存优化实例上部署内存数据库服务。
微服务的缺点
正如弗雷德布鲁克斯在将近 30 年前所写的那样,没有灵丹妙药。与其他所有技术一样,微服务架构也有缺点。一个缺点是名称本身。*微*服务一词过分强调服务规模。事实上,有些开发人员主张构建极其细粒度的 10-100 LOC 服务。虽然小型服务更可取,但重要的是要记住它们是达到目的的手段,而不是主要目标。微服务的目标是充分分解应用程序,以促进敏捷应用程序的开发和部署。
微服务的另一个主要缺点是由于微服务应用程序是分布式系统这一事实而产生的复杂性。开发人员需要选择并实现基于消息传递或 RPC 的进程间通信机制。此外,他们还必须编写代码来处理部分失败,因为请求的目的地可能很慢或不可用。虽然这些都不是火箭科学,但它比在模块通过语言级方法/过程调用相互调用的单体应用程序中复杂得多。
微服务的另一个挑战是分区数据库架构。更新多个业务实体的业务事务相当普遍。这些类型的事务在单体应用程序中实现起来很简单,因为只有一个数据库。但是,在基于微服务的应用程序中,您需要更新由不同服务拥有的多个数据库。使用分布式事务通常不是一种选择,不仅仅是因为CAP 定理。当今许多高度可扩展的 NoSQL 数据库和消息传递代理根本不支持它们。您最终不得不使用基于最终一致性的方法,这对开发人员来说更具挑战性。
测试微服务应用程序也复杂得多。例如,使用 Spring Boot 这样的现代框架,编写一个启动单体 Web 应用程序并测试其 REST API 的测试类是微不足道的。相比之下,服务的类似测试类将需要启动该服务和它所依赖的任何服务(或至少为这些服务配置存根)。再一次,这不是火箭科学,但重要的是不要低估这样做的复杂性。
微服务架构模式的另一个主要挑战是实现跨多个服务的更改。例如,假设您正在实现一个需要更改服务 A、B 和 C 的故事,其中 A 依赖于 B,B 依赖于 C。在单体应用程序中,您可以简单地更改相应的模块,集成更改,并一次性部署它们。相反,在微服务架构模式中,您需要仔细计划和协调对每个服务的更改的推出。例如,您需要更新服务 C,然后是服务 B,最后是服务 A。幸运的是,大多数更改通常只影响一项服务,而需要协调的多服务更改相对较少。
部署基于微服务的应用程序也更加复杂。单一应用程序只需部署在传统负载均衡器后面的一组相同服务器上。每个应用程序实例都配置有基础设施服务的位置(主机和端口),例如数据库和消息代理。相比之下,微服务应用程序通常由大量服务组成。例如,根据Adrian Cockcroft的说法,Hailo 拥有 160 种不同的服务,而 Netflix 拥有超过 600 种服务*[编辑——Hailo 已被 MyTaxi 收购。]*. 每个服务将有多个运行时实例。这是需要配置、部署、扩展和监控的更多活动部件。此外,您还需要实现服务发现机制(将在稍后的文章中讨论),使服务能够发现它需要与之通信的任何其他服务的位置(主机和端口)。传统的基于故障单的手动操作方法无法扩展到这种复杂程度。因此,成功部署微服务应用程序需要开发人员更好地控制部署方法,以及高度自动化。
实现自动化的一种方法是使用现成的 PaaS,例如Cloud Foundry。PaaS 为开发人员提供了一种部署和管理微服务的简单方法。它使他们免受诸如采购和配置 IT 资源之类的顾虑。同时,配置 PaaS 的系统和网络专业人员可以确保符合最佳实践和公司政策。另一种自动化微服务部署的方法是开发本质上是您自己的 PaaS。一个典型的起点是将集群解决方案(如Kubernetes与 Docker 等技术结合使用。在本系列的后面,我们将了解基于软件的应用程序交付 NGINX Plus 等方法可以轻松处理微服务级别的缓存、访问控制、API 计量和监控,可以帮助解决这个问题。
总结
构建复杂的应用程序本质上是困难的。单体架构只对简单、轻量级的应用程序有意义。如果您将它用于复杂的应用程序,您最终将陷入痛苦的世界。尽管存在缺陷和实施挑战,但微服务架构模式是复杂、不断发展的应用程序的更好选择。