开发者社区 > 云原生 > 微服务 > 正文

微服务开发的 10 个最佳实践

1. 领域驱动设计:

开发微服务的首要挑战是将大型、复杂的应用程序分割成小型、自主、独立的可部署模块。如果微服务没有以正确的方式进行分割,将会出现紧耦合的微服务,这些微服务将具有单体架构的所有缺点,并具有分布式单体架构的所有复杂性。幸运的是,已经有一个解决方案可以在这方面提供很大的帮助。Eric Evans 当时是一名软件工程顾问,他在不同公司的业务应用程序中遇到了关于软件复杂性的反复出现的问题,于是在 2004 年出版的《领域驱动设计:处理软件核心的复杂性》一书中总结了他的宝贵见解。该书概述了三个核心概念:

软件开发团队应该与业务部门或领域专家密切合作。

架构师 / 开发人员和领域专家应该首先进行战略设计:找到有界的上下文和相关的核心域以及普遍存在的语言、子域、上下文映射。

然后,架构师 / 开发人员应该进行战术设计,将核心域分解为细粒度的构建块:实体、值对象、聚合、聚合根。

领域驱动设计的详细讨论超出了这篇文章的讨论范围,但是你应该读一下起初的 DDD 书籍 Eric Evans 的《领域驱动设计:处理复杂的软件(蓝皮书)( Domain Driven Design: Tackling Complexity in the Heart of Software (Blue Book))》,或者更现代一点儿的 DDD 书籍 Vaughn Vernon 的《实现领域驱动设计(红皮书)( Implementing Domain Driven Design (Red Book))》。如果将一个大型系统划分为核心域和子域,然后将核心域和子域映射到一个或多个微服务,那么我们将得到理想的松耦合微服务。

2. 每个微服务一个数据库

在将复杂的应用程序拆分为多个微服务模块之后,下一个挑战出现了,如何处理数据库?我们是否应该在微服务之间共享数据库?这个问题的答案是一把双刃剑。一方面,在微服务之间共享数据库将导致微服务之间的强耦合,这与微服务架构的目标正好相反。即使是数据库中的一个小更改也需要团队之间的协调同步。此外,在一个服务中管理数据库的事务和锁就已经足够具有挑战性了。而在多个分布式微服务之间管理事务和锁更是一项艰巨的任务。另一方面,如果每个微服务都有自己的数据库或私有表,那么在微服务之间交换数据就打开了挑战的潘多拉盒子。因此,许多著名的软件工程师都提倡在微服务之间共享数据库,将其作为一种实用的解决方案。然而,在我看来,微服务是关于可持续和长期的软件开发的。因此,每个微服务都应该有自己的数据库(或者私有表)。

3. 微前端

不幸的是,大多数的后端开发人员对前端开发有一种过时的看法,认为前端开发很简单。由于大多数软件架构师都是后端开发人员,他们很少关注前端,而前端在架构设计中往往被忽视。通常在微服务项目中,后端与它们的数据库被很好地模块化,但只有一个整体前端。在最好的情况下,他们考虑用最热门的单页面应用(React、 Angular、Vue)其中之一来开发独体前端。这种方法的主要问题是,前端的单体架构和后端单体架构一样糟糕,正如我前一篇文章所述。另外,当前端因为浏览器的变化而需要更新时,它就需要一个大的更新(这就是为什么那么多公司仍然使用过时的 Angular 1 框架的原因)。网络是简单的,但非常强大,并天生提供了穿透力。开发基于单页面应用的微前端有很多方法:使用 iFrame、Web 组件或借助于(Angular/React)元素。

4. 持续交付

微服务架构的关键卖点之一是每个微服务都可以独立部署。如果你有一个系统,例如 100 个微服务,并且只需要更改一个微服务,那么你可以只更新一个微服务,而不需要修改其他 99 个微服务。但是,在没有自动化的情况下独立部署 100 个微服务(DevOps、CI/CD)是一项艰巨的任务。要充分利用微服务的这一特性,需要 CI/CD 和 DevOps。使用没有 CI/CD、DevOps 的微服务架构,自动化就像购买最新的保时捷,然后用手刹去驾驶它。难怪微服务专家 Martin Fowler 将 CI/CD 列为使用微服务架构的三个先决条件之一。

5. 可观察性

微服务架构的主要缺点之一是,软件开发变得简单,而牺牲了运维。使用单体架构,监视应用程序要简单得多。但是许多微服务在容器上运行,整个系统的可观察性变得非常重要和复杂。甚至日志记录也变得很复杂,要将来自许多容器 / 机器的日志聚集到一个中心位置。幸运的是,市场上已经有很多企业级的解决方案了。例如,ELK/Splunk 提供微服务的日志记录。Prometheus/App Dynamics 提供工业级监控。微服务世界中另一个非常重要的可观察性工具是 Tracing。通常,对一个微服务的一个 API 请求会导致对其他微服务的几个级联调用。要分析微服务系统的延迟,需要测量每个微服务的延迟。Zipkin/Jaeger 为微服务提供了出色的跟踪支持。

6. 统一技术栈

微服务架构告诉我们,对于一个微服务,采用最适合该微服务的编程语言和框架。这种说法不能照字面理解。有时,一个微服务可能需要一个新的技术栈,例如 CPU 密集 / 高性能任务,可能会选择如 c++ /Rust 之类的编程语言。如果微服务与机器学习一起工作,那么 Python 可能是更好的选择。但是在没有任何理由的情况下使用不同的编程语言 / 框架会导致过多的编程语言和框架而没有任何实际的好处。思考这么一个应用场景,一个微服务是使用 Spring Boot + Kotlin+ React + MySQL 开发的,另一个用的是 JakartaEE + Java + Angular + PostgreSQL,下一个是 Scala + Play Framework + VueJS + Oracle,则需要大量的工作去维持这些不同的编程语言、数据库、框架,而没有太多的收益。

7. 异步通信

微服务架构中最具挑战性的设计决策之一是服务之间如何通信和共享数据。当每个微服务都有自己的数据存储时,这一点就更为重要了。通常,一个微服务可以单独存在,但是它不能单独实现所有的业务目标。所有微服务为了实现业务目标而在一起工作,为了在一起工作,它们需要交换数据或触发其他微服务来执行任务。在微服务之间进行通信的最简单和最常见的方式是通过同步的 REST API,这是一种实用但临时的解决方案。如果服务 A 同步调用服务 B,服务 B 同步调用服务 C,服务 C 同步调用服务 D,那么延迟就会增加。此外,由于微服务主要是分布式系统,它们有可能会失败。通常,同步微服务会导致级联失败,即一个服务的失败会导致其他服务的失败。微服务之间的同步通信也导致了微服务之间的紧密耦合。对于长期解决方案,微服务应该异步通信。微服务之间的异步通信有很多方式:通过消息队列,例如 Kafka,通过异步的 REST (ATOM)或 CQRS。

8. 微服务优先

许多专家认为,对于新项目,最好从松耦合的单体架构开始,因为微服务架构需要大量的初始工作来设置运维。在他们看来,一旦项目变得足够成熟,“漂亮的”设计就可以很容易地转化为微服务。然而,在我看来,这种方法在大多数情况下都会失败。实际上,实体内部的模块将是紧耦合的,这将使其很难转换成微服务。而且,一旦应用程序投入生产,在不破坏应用程序的情况下将其转换为微服务将变得更加困难。因此,我的建议是,如果最终有使用微服务架构的计划,那么就从微服务开始。

9. 基础设施优于类库

在微服务软件开发的早期,Netflix 主要使用 Java 编程来开发微服务。他们还开发了许多类库(Netflix 的 OSS 栈,包括 Hystrix、Zuul)。许多公司通过 Netflix 跟进并开始使用 Netflix OSS。后来,许多公司(包括 Netflix)发现,Java 实际上并不是开发微服务的语言,因为它体积庞大,而且冷启动问题严重。Netflix 后来转向 Polyglot 微服务范式,并决定不再进一步开发 Netflix OSS,这导致了追随者公司陷入困境。因此,与其大量投资于特定语言的类库(如基于 Java 的 Netflix OSS),不如使用框架(如服务网格、API 网关)。

10. 组织考虑

大约 50 年前(1967 年),Melvin Conway 观察到公司的软件架构受到组织结构的限制(Conway 定律)。尽管这一观察发现已有 50 年历史,但麻省理工学院(MIT)和哈佛商学院(Harvard Business School)最近发现,这一法则在当今仍然有效。如果一个组织计划开发微服务架构,那么它应该使团队规模更为恰当(两个“美国”比萨团队:7±2 人)。此外,团队应该是跨职能的,最好有前端 / 后端开发人员、运维工程师和测试人员。微服务架构只有在高层管理者也相应地改变他们的观点和愿景的情况下才能发挥作用。

展开
收起
游客pklijor6gytpx 2020-01-03 14:59:12 3342 0
1 条回答
写回答
取消 提交回答
  • 技术架构师 阿里云开发者社区技术专家博主 CSDN签约专栏技术博主 掘金签约技术博主 云安全联盟专家 众多开源代码库Commiter

    写的很完善很完全。DDD领域驱动模型,很不错

    2020-01-04 22:16:45
    赞同 展开评论 打赏

为微服务建设降本增效,为微服务落地保驾护航。

相关电子书

更多
微服务治理技术白皮书 立即下载
微服务与Serverless 立即下载
EDAS4.0 助力企业一站实现微服务架构转型与 K8s 容器化升级 立即下载