现代C++软件架构:方法与实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.4 服务和微服务

由于单体架构存在这些缺点,人们提出了其他的架构方式。一个常见的想法是将解决方案分成多个相互通信的服务,并将开发工作分配给不同的团队,每个团队负责一个服务。每个团队的工作边界都是清晰的,而不是像单体架构那样耦合在一起。

面向服务的架构(Service-Oriented Architecture,SOA)意味着业务功能被模块化,并作为单独的服务供客户使用。每个服务都应该有一个能够自我描述的接口,并隐藏实现细节,例如内部架构、技术或所使用的编程语言。这允许多个团队根据自己的喜好开发服务,这意味着在服务的底层,每个团队都可以使用适合自己的技术。假设你有两个开发团队(一个精通C#,另一个精通C++),那么他们可以开发两个彼此通信的服务(一个用C#实现,一个用C++实现)。

SOA的支持者提出了一份宣言,该宣言主要包括以下几点:

❑业务价值高于技术战略。

❑战略目标高于特定项目的收益。

❑内在互操作性高于定制的集成。

❑共享的服务高于特定目标的实现。

❑灵活性高于优化。

❑不断演进的提炼高于在最开始追求完美。

尽管此宣言没有绑定特定的技术栈、实现方式或服务类型,但最常见的两种服务类型是SOAP和REST。除此之外,最近还有第三个类型越来越受欢迎:基于gRPC的服务。

微服务

顾名思义,在微服务这种软件开发模式中,应用程序被分割为一组松耦合的服务,这些服务使用轻量级协议进行通信。微服务模式类似于UNIX的理念,即一个程序应该只有一个目的。根据UNIX的理念,应将这些程序组合到UNIX的管道(pipeline)中来解决复杂的问题。类似地,基于微服务的系统也由许多微服务和支持服务组成。

我们先看一下这种架构风格的优缺点。

1. 微服务的优缺点

微服务架构中服务规模小意味着它们的开发、部署速度更快,也更容易理解。由于这些服务是相互独立构建的,因此编译其新版本所需的时间可以大大减少。因此,在处理这种架构风格时,可以更容易地使用快速的原型设计和开发。这反过来又缩短了开发周期(lead-time),从而可以更快地对业务需求进行评估。

微服务架构的其他优点有:

❑模块化,这是这种架构风格固有的特点。

❑易测试。

❑替换系统模块(如单个服务、数据库、消息代理或云厂商)时更灵活。

❑可与旧系统集成:不需要迁移整个应用程序,只需要迁移当前开发的部分。

❑支持分布式开发:开发团队可以并行地处理多个微服务。

❑可伸缩性:一个微服务可以独立于其他微服务进行扩展。

另外,微服务也有一些缺点:

❑需要成熟的DevOps方法并依赖于CI/CD自动化。

❑更难调试,并且需要更好的监控和分布式追踪机制。

❑对较小的应用程序来说,额外的开销(需要用辅助服务的话)可能会超过带来的好处。

现在,我们来讨论一下这种架构风格的微服务的特点。

2. 微服务的特点

由于微服务架构相对比较新,所以对微服务没有统一的定义。根据Martin Fowler的说法,微服务有如下几个基本特点:

❑每个服务都应该是一个可更换、可升级的独立组件。这与更容易的部署和服务之间的松耦合有关,与之相反的是,在单体应用程序中组件是以库的形式存在的。在后一种情况下,当替换某个库时,通常必须重新部署整个应用程序。

❑每个服务都应该由一个跨职能的团队来开发,并专注于特定的业务能力。听说过康威(Conway)定律吗?

“任何组织设计的系统(广义的),其系统结构都是该组织通信结构的副本。”

——Melvyn Conway,1967

如果没有跨职能的团队,最终会得到一个个软件筒仓(software silo)。团队间没有沟通会减少很多麻烦,最终成功地交付。

❑每个服务都应该是一个产品,它的整个生命周期由开发团队负责。这与项目思维形成对比,在项目思维中,所开发的软件将交由别人维护。

❑服务应该有智能终端并使用仅作转存的管道,而不是相反(即管道不必智能)。这与传统服务相反,传统服务通常依赖于企业服务总线(Enterprise Service Bus,ESB)的逻辑,后者通常管理消息的路由并根据业务规则对消息进行转换。在微服务中,可以通过在服务中存储逻辑来提高内聚性,避免与消息组件耦合。使用消息队列,如ZeroMQ,也有助于实现这个目标。

❑服务应该以一种去中心化的方式进行管理。单体应用程序通常使用特定的技术栈来编写。当单体应用程序被拆分成微服务时,每个微服务都可以选择适合自己特定需求的技术栈。管理和确保每个微服务全天候(24小时×7)运行的是负责这个特定服务的团队,而不是中央部门。亚马逊、Netflix和Facebook等公司都采用这种方法,它们发现,让开发者确保自己负责的服务稳定有利于确保整体系统的高质量。

❑服务应该以一种去中心化的方式来管理它们的数据。每个微服务都可以选择适合其需求的数据库,而不是只有一个数据库。拥有去中心化的数据可能会给数据更新带来一些挑战,但具有更好的扩展性。这就是微服务经常以无事务方式协作并提供最终一致性的原因。

❑服务所使用的基础设施应该被自动管理。要高效地处理数十个微服务,需要有持续的集成和持续的交付(Continuous Intergration and Continuous Delivery,CI/CD),否则,部署这些服务将是地狱级难度。自动运行所有的测试将为你节省大量的时间和精力。在此基础上实施持续部署将缩短反馈循环,使客户能更快地使用新功能。

❑微服务应为其所依赖的其他服务的失败做好准备。在具有如此多可插拔组件的分布式部署环境中,一些组件偶尔出现故障是正常的。你的服务应该能够优雅地处理此类故障。熔断器或舱壁等模式(见4.3.4节)可以帮助你实现这一点。为了使架构具有弹性,还必须能够有效地恢复失败的服务,甚至提前预判这种崩溃。实时监控延迟、吞吐量和资源使用情况对此至关重要。了解Netflix的Simian Army工具包对于创建弹性架构是非常有用的。

❑微服务架构应该为不断演进做好准备。在设计微服务和它们之间的协作时,应考虑好如何方便地替换单个微服务,甚至一组微服务。正确地设计服务是很棘手的,尤其是把曾经是一整个大模块的复杂代码变成微服务之间复杂的通信方案,这很难管理——所谓的“意大利面条式”整合。这意味着与传统服务或单体架构相比,架构师的经验和技术栈扮演着更重要的角色。

除此之外,很多(但不是所有)微服务共有的其他特点有:

❑使用独立的进程,进程间通过网络通信。

❑使用与技术无关的协议(如HTTP和JSON)。

❑保持服务的小规模和较低的运行时开销。

现在,你应该很好地了解了基于微服务的系统的特点,所以我们来对比一下这种架构与其他架构。

3. 微服务和其他架构风格

微服务可以单独用作一种架构模式。然而,它们经常与其他架构相结合,如云原生计算(cloud-native computing)、无服务器应用(serverless application),以及大多数轻量级应用容器(lightweight application container)。

面向服务的架构带来了低耦合和高内聚。如果使用得当,微服务也可以做到。然而,它可能具有一定的挑战性,因为它需要良好的直觉来将系统划分为大量的微服务。

微服务和上述其他架构有很多相似之处,因为它们都可以使用基于SOAP、REST或gRPC的消息传递机制,并且可以使用消息队列等技术来实现事件驱动。

微服务也有一些成熟的模式来帮助实现所需的质量属性,例如容错(例如通过隔离故障组件实现),但是为了拥有高效的架构,你还必须决定如何实现API网关、服务注册、负载均衡、容错、监控、配置管理,以及使用的技术栈。

4. 微服务扩展

微服务的扩展与单体应用程序不同。在单体应用程序中,整个功能由单个进程处理。扩展应用程序意味着要在不同的机器上复制此进程。这种扩展没有考虑到哪些功能会被大量使用,以及哪些功能不需要额外的资源。

对于微服务,每个功能都作为一个单独的服务来处理,使用一个单独的进程。扩展基于微服务的应用程序时,只有需要更多资源的部分才被复制到不同的机器上。采用这种方法,可以更好地利用资源。

5. 如何过渡到微服务

大多数公司都存在一些单体程序,虽然不想立即使用微服务进行重写,但仍希望过渡到微服务架构。在这种情况下,通过添加越来越多的与单体程序交互的服务,可以逐步往微服务迁移。可以创建新的功能作为微服务,也可以删除单体程序中的某些部分,将其转为微服务。

更多关于微服务的细节,包括如何从零开始构建微服务,可参考第13章。