微服务体系结构样式
微服务体系结构由一系列小型的自治服务组成。 每个服务都是自包含服务,并且应实现单个业务功能。
什么是微服务?
-
微服务具有规模小、独立和松散耦合的特点。 一个小规模的开发人员团队就能编写和维护一个服务。
-
每个服务都是一个单独的基本代码,可由小型开发团队管理。
-
服务可独立部署。 团队可以更新现有服务,而无需重新生成和重新部署整个应用程序。
-
服务负责暂留自己的数据或外部状态。 这一点与传统模型不同,后者由单独的数据层处理数据暂留。
-
服务通过定义完善的 API 相互通信。 每个服务的内部实现细节均对其他服务隐藏。
-
服务无需共享相同的技术堆栈、库或框架。
除了服务本身,典型微服务体系结构中还会出现其他组件:
管理/业务流程. 该组件负责将服务放置在节点上、确定故障,以及跨节点重新均衡服务等等。 该组件通常是一种现成的技术(例如 Kubernetes),而不是自定义构建的内容。
API 网关。 API 网关是客户端的入口点。 客户端会调用 API 网关,网关再将调用转发到后端上的相应服务,而不是由客户端直接调用服务。
使用 API 网关的优点如下:
-
它分离了客户端与服务。 无需更新所有客户端,便可对服务进行版本控制或重构。
-
服务可以使用对 Web 不友好的消息传递协议,比如 AMQP。
-
API 网关可执行身份验证、日志记录、SSL 终止和负载均衡等其他跨领域功能。
优点
-
敏捷性。 由于微服务是独立部署的,因此我们可以更轻松地管理 bug 修复和功能发布。 无需重新部署整个应用程序即可更新服务,出现问题时可回滚更新。 在很多传统应用程序中,如果在应用程序的一个部件中发现 bug,它会阻止整个发布流程。 新功能可能会被搁置,要等到 bug 修补程序后才能进行集成、测试和发布。
-
小型专属团队。 微服务应该足够小,单个功能团队就能构建、测试和部署。 即使团队规模不大,也能大幅提升敏捷性。 而由于沟通效率更慢、管理开销更高且敏捷性更低,大型团队的工作效率往往更低。
-
小型代码库。 在整体式应用程序,代码依赖项往往会随着时间的推移而变得混杂。因此,要在多个位置更改代码才能添加新功能。 如果不共享代码或数据存储,则微服务体系结构可将依赖项减到最少,使新功能的添加变得更容易。
-
混合技术。 团队可以选择最适合其服务的技术,并适当地使用混合技术栈。
-
错误隔离。 单个微服务在不可用的情况下不会中断整个应用程序,但前提是所有上游微服务能够正确处理故障(例如,通过实施熔断机制)。
-
可伸缩性。 服务可独立扩展,这样你就能横向扩展需要更多资源的子系统,而不横向扩展整个应用程序。 借助 Kubernetes 或 Service Fabric 等业务流程协调程序,你可将更高密度的服务打包到一台主机中,从而提高资源的利用效率。
-
数据隔离。 执行架构更新要容易得多,因为只会影响单个微服务。 在整体应用程序中,更新架构可能极具挑战性,因为应用程序的不同部分可能都要获取相同的数据,对架构进行任何更改都会带来风险。
挑战
微服务的优势并非没有代价。 下面是在开始使用微服务体系结构之前需要考虑的一些挑战。
-
复杂性。 与同等的单一式应用程序相比,微服务应用程序具有更多移动部件。 每个服务更简单,但整个系统作为整体来说更复杂。
-
开发和测试。 编写依赖于其他独立服务的小型服务时需要的方法与编写传统的整体式或分层式应用程序时的方法不同。 现有工具并非总是能够处理服务依赖关系。 跨服务边界进行重构可能很困难。 测试服务依赖关系也有一定难度,尤其是在应用程序快速发展之时。
-
缺乏监管。 用于生成微服务的分散式方法具有一定优势,但也可能导致许多问题。 用户在生成过程中可能采用了许多不同的语言和框架,从而使应用程序变得难以维护。 这种情况下可以实施一些项目范围内的标准,不过分限制团队的灵活性。 这尤其适用于日志记录等跨领域功能。
-
网络拥塞和延迟。 使用大量小型的精细服务可能会增加服务间的通信量。 此外,如果服务依赖关系链变得太长(服务 A 调用 B,B 调用 C...),额外延迟可能会成为一个问题。 用户需要精心设计 API。 应避免过于繁琐的 API,考虑使用序列化格式,并找到可以使用异步通信模式的地方。
-
数据完整性。 每个微服务负责自己的数据暂留。 因此,数据一致性可能是个挑战。 如果可能,请采用最终一致性。
-
管理。 成功使用微服务需要有成熟的 DevOps 区域性。 跨服务的关联日志记录可能很难。 通常情况下,日志记录必须为单个用户操作关联多个服务调用。
-
版本控制。 对某个服务的更新不应中断依赖于它的其他服务。 多个服务可在任意给定时间更新,因此,若不精心设计,可能会遇到向后或向前兼容性问题。
-
技能集。 微服务是一种高度分布式系统。 请仔细评估团队是否具有成功使用微服务所需的技能和经验。
最佳实践
-
围绕业务域对服务建模。
-
分散所有资源。 单个团队负责设计和生成服务。 避免共享代码或数据架构。
-
拥有数据的服务应当有专用的数据存储。 为每个服务和数据类型使用最合适的存储。
-
服务通过设计完善的 API 进行通信。 避免泄露实现细节。 API 应对域建模,而不是对服务的内部实现建模。
-
避免服务之间耦合。 耦合的原因包括共享的数据库架构和严格的通信协议。
-
将身份验证和 SSL 终止等跨领域操作分流到网关。
-
让网关不必了解域。 网关应处理和路由客户端请求,而无需了解业务规则或域逻辑。 否则,网关会变成一个从属物,从而导致服务之间耦合。
-
服务应具有松散耦合和高功能内聚的特点。 应当将可能会一起更改的函数打包并部署在一起。 如果它们驻留在不同的服务中,这些服务最终会紧密耦合,因为一个服务中的更改将需要更新其他服务。 两个服务之间的通信过于频繁可能是紧密耦合和低内聚的征兆。
-
隔离故障。 使用复原策略可防止某个服务中的故障级联。