本文为《Spring微服务实战(第2版)》的读书笔记。
“单体架构”并非贬义词,“微服务架构”也不是银弹。
首先,微服务架构作为一种分布式架构,必然会带来分布式架构的固有的难题,如分布式事务。其次,由于系统中存在诸多微服务,微服务架构需要企业内部强大的 DevOps 能力作为支撑,否则会给开发和运维带来更多的难题。每一种工具只有在合适的背景下,才能发挥出自己的优势。项目是否应该采用微服务架构,应该从产品使用人数、开发成本、企业内部 DevOps 能力、组织架构等多个方面进行思考,切不可盲目随大流。
欢迎迈入云世界,Spring
对微服务的思考需要围绕两个关键概念展开:分解(decomposing)和分离(unbundling)。当我们开始讨论微服务时,请记住:小型的、简单的、解耦的服务 = 可伸缩的、弹性的、灵活的应用程序。
Spring 的核心是建立在依赖注入的概念上的。依赖注入框架(dependency injection framework)允许你通过约定(以及注解)将应用程序中对象之间的关系外部化,而不是在对象内部彼此硬编码实例化代码,这使开发人员能更高效地管理大型 Java 项目。Spring 在应用程序的不同的Java 类之间充当中间人,管理着它们的依赖关系。
Spring Boot 的主要特性如下。
- 嵌入式 Web 服务器,用于避免应用程序部署的复杂性:Tomcat(默认)、Jetty 或 Undertow。这是 Spring Boot 的一个基本概念,所选的 Web 服务器是可部署 JAR 文件的一部分。对于 Spring Boot 应用程序,部署应用程序的唯一必要条件是在服务器上安装Java。
- 快速启动项目的建议配置(各种 starter 项目)。
- 尽可能地自动配置 Spring 功能。
- 可供生产使用的广泛特性(如度量、安全性、状态验证、外部化配置等)。
Spring Cloud 框架使得将微服务实施和部署到私有云或公有云变得更加简单。Spring Cloud 在一个公共框架中封装了多个流行的云管理微服务框架,并且让这些技术的使用和部署像为代码添加注解一样简便。mvn spring-boot:run
这个 Maven 命令使用 pom.xml 文件中定义的 Spring Boot 插件来使用嵌入式 Tomcat 服务器启动应用程序。注意 如果你从命令行运行命令,请确保你位于根目录中。根目录是包含 pom.xml 文件的目录。
云计算模型允许用户选择这些模型提供的信息和服务的控制级别。云计算模型以其首字母缩写而闻名,通常称为 XaaS——它是一个缩写,意思是“anything as a service”。最常见的云计算模型有以下几种,
- 基础设施即服务(Infrastructure as a Service,IaaS)——供应商提供的基础设施,向用户提供服务器、存储和网络等计算资源的访问。
- 容器即服务(Container as a Service,CaaS)——此模型介于 IaaS 和 PaaS 之间,它指的是一种基于容器的虚拟化形式。
- 平台即服务(Platform as a Service,PaaS)——此模型提供平台和环境,让用户专注于应用程序的开发、执行和维护,甚至可以使用供应商提供的工具来创建这些应用程序(例如操作系统、数据库管理系统、技术支持、存储、托管、网络等)。
- 函数即服务(Function as a Service,FaaS)——也称为无服务器架构,尽管名字如此,但这个架构并不意味着在没有服务器的情况下运行特定的代码,它真正的意思是在云中执行功能的一种方式,供应商在云中提供所有必需的服务器。
- 软件即服务(Software as a Service,SaaS)——也称为按需软件,此模型使用户无须进行任何部署或维护就可以使用特定的应用程序。
微服务架构的核心概念之一就是每个服务都被打包和部署为离散的独立制品。服务实例应该迅速启动,服务的每一个实例都是完全相同的。基于云的微服务的优势是以弹性的概念为中心。尽管构建单个微服务的概念很易于理解,但运行和支持健壮的微服务应用程序(尤其是在云中运行)不只是涉及为服务编写代码。
编写一个健壮的服务需要考虑几个主题。 - 大小适当——如何确保微服务的大小适当,这样才不会让微服务承担太多的职责。请记住,服务大小适当,就能快速更改应用程序,降低整个应用程序中断的总体风险。
- 位置透明——如何管理服务调用的物理细节。在一个微服务应用程序中,多个服务实例可以快速启动和关闭。
- 有弹性——如何通过绕过失败的服务,确保采取“快速失败”的方法来保护微服务消费者和应用程序的整体完整性。
- 可重复——如何确保提供的每个新服务实例与生产环境中的所有其他服务实例具有相同的配置和代码库。
- 可伸缩——如何建立一种通信,使服务之间的直接依赖关系最小化,并确保可以优雅地扩展微服务。
单体架构往往是简单、轻量级应用程序的理想选择,而微服务架构通常更适合开发复杂和逐渐演变的应用程序。最后,选择软件架构将完全取决于项目规模、时间和需求,以及其他因素。
Spring Boot 用于简化基于 REST 的 JSON 微服务的构建,其目标是让用户只需要少量注解,就能够快速构建微服务。
编写微服务很容易,但是完全可以将其用于生产则需要额外的深谋远虑。有几类微服务模式,包括核心开发模式、路由模式、客户端弹性模式、安全模式、日志记录和跟踪模式、应用程序度量模式以及构建/部署模式。
路由模式处理想要消费微服务的客户端应用程序如何发现服务的位置并将其路由到该服务这一问题。
要防止服务实例中的问题向上和向外级联暴露给服务的消费者,请使用客户端弹性模式。其中包括避免调用失败服务的断路器模式,可创建备用路径以便在服务失败时检索数据或执行特定操作的后备模式,可用于扩展和消除所有可能瓶颈或故障点场景的客户端负载均衡模式,以及限制并发调用服务数量以阻止性能差的调用对其他服务产生负面影响的舱壁模式。
OAuth2 是最常见的用户授权协议,是保护微服务架构的最佳选择。使用 Spring Cloud 探索微服务世界
在我们继续讨论微服务架构时,请务必牢记:一个系统分布得越广,它可能发生故障的地方就越多。
Spring Cloud 简化了项目的设置和配置,并为 Spring 应用程序中最常见的模式提供了解决方案。这样我们就可以专注于编写代码,而不会陷入配置构建和部署微服务应用程序的所有基础设施的细节中。
- Spring Cloud Config
Spring Cloud Config 通过集中式服务来处理应用程序配置数据的管理,因此应用程序配置数据(特别是环境特定的配置数据)与部署的微服务是完全分离的。这确保了无论启动多少个微服务实例,这些微服务实例始终具有相同的配置。Spring Cloud Config 拥有自己的属性管理存储库,但也可以与以下开源项目集成。
Git—— 一个开源版本控制系统,允许开发人员管理和跟踪任何类型的文本文件的更改。Spring Cloud Config 集成了 Git 后端存储库,能从存储库中读出应用程序的配置数据。
Consul—— 一种开源的服务发现工具,允许服务实例向该服务注册自己。服务客户端可以向 Consul 查询其服务实例的位置。Consul 还包括键值存储数据库,Spring Cloud Config用它来存储应用程序的配置数据。
Eureka—— 一个开源的 Netflix 项目,像 Consul 一样,提供类似的服务发现功能。Eureka同样有一个可以被 Spring Cloud Config 使用的键值数据库。 - Spring Cloud 服务发现
服务消费者通过逻辑名称而不是物理位置来调用服务器的业务逻辑。Spring Cloud 服务发现也处理服务实例在启动和关闭时的注册和注销。Spring Cloud 服务发现可以使用以下服务来实现:Consul;ZooKeeper;Eureka。 - Spring Cloud LoadBalancer 和 Resilience4j
通过使用 Resilience4j 库,你可以快速实现服务客户端弹性模式,如断路器、重试和舱壁等模式。虽然 Spring Cloud LoadBalancer 项目简化了与诸如 Eureka 这样的服务发现代理的集成,但它也为服务消费者提供了客户端对服务调用的负载均衡。这使得即使服务发现代理暂时不可用,客户端也可以继续进行服务调用。 - Spring Cloud API GatewayAPI 网关为微服务应用程序提供服务路由功能。
通过集中的服务调用,开发人员可以强制执行标准服务策略,如安全授权、验证、内容过滤和路由规则。 - Spring Cloud Stream
Spring Cloud Stream 是一种可让开发人员轻松地将轻量级消息处理集成到微服务中的支持技术。 - Spring Cloud SleuthSpring Cloud Sleuth 允许将唯一跟踪标识符集成到应用程序所使用的 HTTP 调用和消息通道(RabbitMQ、Apache Kafka)中。
Spring Cloud Sleuth 与日志聚合技术工具(如 ELK 技术栈)和跟踪工具(如Zipkin)结合时,能够展现出真正的威力。
ELK 技术栈是 3 个开源项目(Elasticsearch、Logstash 和 Kibana)名称的首字母缩写。
Elasticsearch 是搜索和分析引擎。Logstash 是服务器端数据处理管道,它消费数据并转换数据,以便将数据发送到“秘密存储点(stash)”。Kibana 是一个客户端用户界面,允许用户查询并可视化整个技术栈的数据。 - Spring Cloud SecuritySpring Cloud Security 是一个验证和授权框架,可以控制哪些人可以访问你的服务,以及他们可以用服务做什么。
DevOps 是 development(Dev)和 operations(Ops)的缩写。DevOps 指的是一种软件开发方法,它关注软件开发人员和 IT 运维人员之间的交流、协作和集成。DevOps 的主要目标是以较低的成本实现软件交付过程和基础设施变更的自动化。
微服务是小型的松耦合的分布式服务。
持续交付是一种软件开发实践。通过这种做法,交付软件的过程是自动化的,以允许向生产环境中进行短期交付。
容器是在虚拟机(virtual machine,VM)镜像上部署微服务的自然延伸。
为了应对创建云原生微服务的挑战,我们将使用 Heroku 的最佳实践指南,称为十二要素应用程序(twelve-factor app),来构建高质量的微服务。
这套方法是由几个 Heroku 开发人员在 2002 年创造的,主要目标是在构建微服务时提供 12种最佳实践。
十二要素应用程序宣言的最佳实践包括代码库、依赖、配置、后端服务、构建/发布运行、进程、端口绑定、并发、可任意处置、开发环境/生产环境等同、日志和管理进程。
使用 Spring Boot 构建微服务
架构师的故事:设计微服务架构
架构师在软件项目中的作用是提供待解决问题的工作模型。架构师的工作是提供脚手架,开发人员将根据这些脚手架构建他们的代码,使应用程序所有部件都组合在一起。在构建微服务时,项目的架构师主要关注以下 3 个关键任务:(1)分解业务问题;(2)建立服务粒度;(3)定义服务接口。
分解业务问题
分离业务域是一门艺术,而不是非黑即白的科学。你可以使用以下指导方针将业务问题识别和分解为备选的微服务。
- 描述业务问题,并注意你用来描述它的名词。
在描述问题时,反复使用的相同的名词通常意味着它们是核心业务域并且适合创建微服务。 - 注意动词。动词突出了动作,通常代表问题域的自然轮廓。
- 寻找数据内聚。在将业务问题分解成离散的部分时,要寻找彼此高度相关的数据部分。
建立服务粒度
开始的时候可以让微服务涉及的范围更广泛一些,然后再重构到更小的服务。重点关注服务如何相互交互。这有助于建立问题域的粗粒度接口。
如果微服务过于粗粒度,可能会看到以下现象。 - 一个服务承担过多的职责。
- 一个服务跨大量表来管理数据。
- 一个服务的测试用例太多。
如果微服务过于细粒度会怎么样呢?问题域的一部分微服务像兔子一样繁殖。如果一切都成为微服务,从服务中组合出业务逻辑就会变得既复杂又困难。
何时不要使用微服务:
微服务彼此间严重相互依赖。
微服务成为简单 CRUD(Create、Read、Update、Delete)服务的集合。
微服务是业务逻辑的表达,而不是数据源的抽象层。如果微服务除 CRUD 相关逻辑之外什么都不做,那么它们就可能过于细粒度了。定义服务接口
一般来说,可使用以下指导方针实现服务接口设计。 - 拥抱 REST 的理念。
- 使用 URI 来传达意图。
- 将 JSON 用于请求和响应。
- 使用 HTTP 状态码来传达结果。
何时不要使用微服务
- 构建分布式系统时的复杂性
因为微服务是分布式的、细粒度(小)的,所以它们在应用程序中引入了一层复杂性,而在单体应用程序中就不会出现这样的情况。微服务架构需要高度的运维成熟度。除非你的组织愿意投入高分布式应用程序获得成功所需的自动化和运维工作(监控、伸缩等),否则不要考虑使用微服务。 - 服务器或容器散乱
注意 必须对微服务的灵活性与运行所有这些服务器的成本进行权衡。 - 应用程序的类型
微服务是面向可复用性的,对构建需要高度弹性和可伸缩性的大型应用程序非常有用。
如果你正在构建小型的部门级应用程序,或具有较小用户群的应用程序,那么搭建一个分布式模型(如微服务)的复杂性可能就太昂贵了,不值得。 - 数据事务和一致性
注意 封装是面向对象编程的主要原则之一,为了在 Java 中实现封装,必须将类的变量声明为私有的,然后提供公共的 getter 和 setter 来读取和写入这些变量的值。
Lombok 是一个小型库,它允许我们减少在项目的 Java 类中编写的样板 Java 代码的数量。Lombok生成诸如 getter、setter、to string 方法、构造方法等代码。
以下指导方针有助于命名服务端点。
- 使用明确的 URL 名称来确立服务所代表的资源。
- 使用 URL 来确立资源之间的关系。
- 尽早建立 URL 的版本控制方案。
一种常见的模式是使用版本号作为前缀添加到所有端点上。DevOps 故事:构建运行时的严谨性
编写代码通常是很简单的,而保持代码运行却是困难的。我们将基于以下 4 条原则开始我们的微服务开发工作,并且在本书后面根据这些原则去构建。 - 微服务应该是独立的。
- 微服务应该是可配置的。
- 微服务实例需要对客户端透明。
- 微服务应该传达它的健康信息。
从 DevOps 的角度来看,必须预先解决微服务的运维需求,并将这 4 条原则转化为每次构建和部署微服务到环境中时发生的一套标准的生命周期事件。这 4 条原则可以映射到以下运维生命周期步骤。 - 服务装配——如何打包和部署服务以保证可重复性和一致性,以便相同的服务代码和运行时被完全相同地部署。
- 服务引导——如何将应用程序和环境特定的配置代码与运行时代码分开,以便可以在任何环境中快速启动和部署微服务实例,而无须人为干预。
- 服务注册/发现——部署一个新的微服务实例时,如何让新的服务实例可以被其他应用程序客户端发现。
- 服务监控——在微服务环境中,由于高可用性需求,运行同一服务的多个实例非常常见。
Spring Actuator 提供了开箱即用的运维端点,可帮助你了解和管理你的服务的健康状况。欢迎来到 Docker
容器不需要一个完整的操作系统,它可以复用底层的操作系统,这就减少了物理机器必须支持的负载、使用的存储空间以及启动应用程序所需的时间。因此,容器比虚拟机轻量得多。性能和可移植性是公司决策的关键概念。因此,了解我们将要使用的技术的好处是很重要的。在这种情况下,通过在微服务中使用容器,我们将获得以下好处。 - 容器可以在任何地方运行,这有助于开发和实现,并提高了可移植性。
- 容器提供了创建可预测环境的能力,这些环境与其他应用程序完全隔离。
- 容器可以比虚拟机更快地启动和停止,这使得它们成为云原生是可行的。
- 容器是可伸缩的,可以主动调度和管理,以优化资源利用,提高在其中运行的应用程序的性能和可维护性。
- 我们可以在最小数量的服务器上实现最大数量的应用程序。
Docker 引擎是整个 Docker 系统的核心部分。
Docker 引擎包含以下组件。 - Docker 守护进程—— 一个名为 dockerd 的服务器端进程,它允许我们创建和管理Docker镜像。REST API 用于向守护进程发送指令,而 CLI 客户端用于输入 Docker 命令。
- Docker 客户端—— Docker 用户通过客户端与 Docker 交互。当一个 Docker 命令运行时,客户端负责将指令发送给守护进程。
- Docker 注册中心——存储 Docker 镜像的位置。这些注册中心可以是公共的,也可以是私有的。
- Docker 镜像—— Docker 镜像都是只读模板,通过一些指令来创建 Docker 容器。这些镜像可以从 Docker Hub 中提取,你可以原样使用它们,也可以通过添加额外的指令来修改它们。此外,你还可以使用 Dockerfile 创建新的镜像。
- Docker 容器—— 一旦使用 docker run 命令创建并执行,Docker 镜像就会创建一个容器。应用程序及其环境运行在此容器内部。Docker 容器的启动、停止和删除可以通过Docker API 或 CLI 实现。
- Docker 数据卷(volume)—— Docker 数据卷是存储由 Docker 生成并被Docker 容器使用的数据的首选机制。
- Docker 网络—— Docker 网络允许我们将容器连接到任意多的网络上。我们可以把网络看作是与隔离容器通信的一种手段。Docker 包含 bridge、host、overlay、none 和macvlan 这 5 种网络驱动类型。
DockerfileDockerfile 是一个简单的文本文件,它包含了 Docker 客户端用来创建和准备镜像的指令和命令列表。该文件为你自动完成镜像创建过程。
创建 Dockerfile 后,运行 docker build 命令构建 Docker 镜像。在准备好Docker 镜像之后, 使用 docker run 命令创建容器。
Docker ComposeDocker Compose 通过允许我们创建便于设计和构建服务的脚本,简化了 Docker 的使用。使用 Docker Compose,你可以将多个容器作为一个服务运行,也可以同时创建不同的容器。要使用 Docker Compose,请遵循以下步骤。(1)如果你还没有安装 Docker Compose,请安装它。(2)创建一个 YAML 文件来配置你的应用程序服务。你应该将这个文件命名为docker-compose.yml。(3)使用 docker-compose config 命令检查文件的有效性。(4)使用 docker-compose up 命令启动服务。集成 Docker 与微服务
- 构建 Docker 镜像
Docker Maven 插件允许我们从我们的 Maven pom.xml 文件中管理 Docker 镜像和容器。
- 基础 Dockerfile
将把 Spring Boot JAR 文件复制到 Docker 镜像中,然后执行应用程序 JAR 文件。 - 多阶段构建 Dockerfile
多阶段构建允许我们丢弃对应用程序执行不重要的所有东西。例如,在 Spring Boot 中,我们不需要将整个目标文件夹复制到 Docker 镜像中,我们只需要复制运行 Spring Boot 应用程序所需的内容。这种方式将优化我们创建的 Docker 镜像。
要了解更多关于多阶段构建过程的信息,可以通过在你的微服务目标文件夹中执行以下命令来查看 Spring Boot 应用程序的 fat JAR:jar tf jar-file
。
如果目标文件夹中没有 JAR 文件,可以在项目的 pom .xml 文件的根目录下执行以下 Maven命令: mvn clean package 现在我们已经设置了 Maven 环境,下面来构建我们的Docker 镜像。为此,需要执行以下命令: mvn package dockerfile:build 注意 请核实你的本地机器上有至少 18.06.0 或更高版本的 Docker 引擎,以确保所有 Docker 代码示例都能成功运行。要查询 Docker 版本,可以执行 docker version 命令。
- 使用 Spring Boot 创建 Docker 镜像
- Buildpacks
Buildpacks 是提供应用程序和框架依赖项的一些工具,将我们的源代码转换为可运行的应用程序镜像。换句话说,Buildpacks 检测并获取应用程序运行所需的一切。
Spring Boot 2.3.0 使用 Cloud Native Buildpacks 为构建 Docker 镜像提供支持。
从Spring Boot微服务的根目录执行以下命令:./mvnw spring-boot:build-image
。 - 分层 JAR
Spring Boot 引入了一个称为分层 JAR 的新 JAR 布局。在这种格式中,/lib 和/classes 文件夹被划分为不同的层。创建这个分层是为了根据构建之间发生更改的可能性来分离代码,为构建留下必要的信息。如果你不想使用 Buildpacks,这是另一个很好的选择。
(1)向 pom.xml 文件添加分层配置。(2)打包应用程序。(3)使用 JAR 模式 layertools 执行 jarmode 系统属性。(4)创建 Dockerfile。(5)构建并运行镜像。
- 使用 Docker Compose 启动服务
它是一个服务编排工具,允许你将服务定义为一个组,然后将这些服务作为一个单元一起启动。此外,Docker Compose 还包括为每个服务定义环境变量的功能。Docker Compose 使用 YAML 文件来定义将要启动的服务。使用 Spring Cloud Config 服务器端控制配置
通过将配置信息与应用程序代码完全分离,开发人员和运维人员可以在不进行重新编译的情况下对配置进行更改。但这样做也会引入复杂性,因为现在存在另一个需要与应用程序一起管理和部署的软件制品。
作为基于云的微服务开发的最佳实践,我们应该考虑以下内容。 - 应用程序的配置与正在部署的实际代码完全分离。
- 构建不可变的应用程序镜像,它们在你的环境中提升时永远不会发生变化。
- 在服务器启动时通过环境变量注入应用程序配置信息,或者在微服务启动时通过集中式存储库读取应用程序配置信息。
关于管理配置(和复杂性)
遵循的 4 条原则: - 分离——我们需要将服务配置信息与服务的实际物理部署完全分开。
- 抽象——还需要将访问配置数据的功能抽象到一个服务接口中。
- 集中——因为基于云的应用程序实际可能会有数百个服务,所以最小化用于保存配置信息的不同存储库的数量至关重要。
- 稳定——因为应用程序的配置信息与部署的服务完全隔离并集中存放,所以至关重要的一点就是使用和实施的解决方案必须是高度可用和冗余的。
使用 Spring Cloud 配置服务器端(通常称为 Spring Cloud Config 服务器端或简称为服务器端),它完全适合我们的 Spring 微服务架构。我们选择这个解决方案的原因有以下几个。 - Spring Cloud Config 服务器端易于搭建和使用。
- Spring Cloud Config 与 Spring Boot 紧密集成。开发人员可以使用一些简单易用的注解来读取自己的所有应用程序的配置数据。
- Spring Cloud Config 服务器端提供多个后端用于存储配置数据。
构建 Spring Cloud Config 服务器端:
在你构建Config 服务时,它将成为在你的环境中运行的另一个微服务。一旦建立 Config 服务,服务的内容就可以通过基于 HTTP 的 REST 端点进行访问。应用程序配置文件的命名约定是“应用程序名称-环境名称.properties”或“应用程序名称-环境名称.yml”。
Spring 框架实现了一种用于解决问题的层次结构机制。当 Spring 框架解决问题时,它将先查找默认属性文件中定义的属性,然后用特定环境的值(如果存在)去覆盖默认值。
将 Spring Cloud Config 与 Spring Boot 客户端集成:
PostgreSQL,也称为 Postgres,被认为是企业级系统,是开源关系数据库管理系统(relational database management system,RDBMS)中最有趣和最先进的一种。与其他关系数据库相比,PostgreSQL 拥有诸多优点,但最主要的是它提供了一个完全免费的开放给任何人使用的许可证。第二个优点是,就其能力和功能而言,它允许我们在不增加查询复杂性的情况下处理更大量的数据。
Postgres 的一些主要特性。 - Postgres 采用多版本并发控制,它将数据库状态的快照添加到每个事务中,从而产生具有更好性能优势的一致事务。
- Postgres 在执行事务时不使用读锁。
- Postgres 有一个叫作热备份的功能,它允许客户端在服务器处于恢复或备用模式时在服务器中进行搜索。换句话说,Postgres 在不完全锁定数据库的情况下执行维护。
PostgreSQL 的一些主要特点。 - 它支持 C、C++、Java、PHP、Python 等语言。
- 它能够为许多客户端提供服务,同时从其表中无阻塞地传递相同的信息。
- 它支持使用视图,因此用户可以以不同于数据被存储的方式查询数据。
- 它是对象关系数据库,允许我们像处理对象一样处理数据,从而提供面向对象的机制。
- 它允许我们将 JSON 作为数据类型存储和查询。
在使用 Spring Cloud Config 的 Spring Boot 服务中,配置信息可以在以下任何一个文件中设置:bootstrap.yml、bootstrap.properties、application.yml 或 application.properties。
任何想要保留在本地服务(而不是存储在 Spring Cloud Config 中)的其他配置信息,都可以在本地 application.yml 文件中进行设置。通常情况下,存储在 application.yml 文件中的信息是你可能希望提供给服务的配置数据,即使 Spring Cloud Config 服务不可用。
Spring Data 和 JPA 框架提供访问数据库的基本 CRUD(Create、Replace、Update、Delete)方法。Spring Boot 应用程序只会在启动时读取它们的属性,因此 Config 服务器端中进行的属性更改不会被 Spring Boot 应用程序自动获取。不过,Spring Boot Actuator 提供了一个@RefreshScope 注解,允许开发团队访问/refresh 端点,这会强制 Spring Boot 应用程序重新读取其应用程序配置。我们需要注意一些有关@RefreshScope 注解的事情。注解只会重新加载应用程序配置中的自定义 Spring 属性。Spring Data 使用的数据库配置等不会被@RefreshScope 注解重新加载。
Spring Cloud Config 服务确实提供了一种称为 Spring Cloud Bus 的基于推送的机制,使 Spring Cloud Config 服务器端能够向所有使用服务的客户端发布哪里的配置发生了更改的消息。Spring Cloud Bus 需要一个额外的在运行的中间件:RabbitMQ。这是检测更改的非常有用的手段,但并不是所有的 Spring Cloud Config 后端都支持这种推送机制(例如,Consul 服务器)。
我们用过的用于处理应用程序配置刷新事件的一种技术是,刷新 Spring Cloud Config 中的应用程序属性,然后编写一个简单的脚本来查询服务发现引擎以查找服务的所有实例,并直接调用/refresh端点。你也可以重新启动所有服务器或容器来接收新的属性。
使用 Spring Cloud Config 服务集成 Vault:
后端存储库:HashiCorp Vault。Vault 是一个让我们可以安全地访问机密信息的工具。我们可以将机密信息定义为想要限制或控制访问的任何信息,如密码、证书、API 密钥等。要在我们的 Spring Config 服务中配置 Vault,必须添加一个 Vault profile。这个profile 支持与Vault 集成,并允许我们安全地存储微服务的应用程序属性。
保护敏感配置信息在默认情况下,Spring Cloud Config 服务器端在应用程序配置文件中以纯文本格式存储所有属性,包括像数据库凭据这样的敏感信息。将敏感凭据作为纯文本保存在源代码存储库中是一种非常糟糕的做法。
Spring Cloud Config 支持使用对称加密(共享秘密)密钥和非对称加密密钥(公钥/私钥)。非对称加密比对称加密更安全,因为它使用更现代和更复杂的算法。然而,有时使用对称密钥更方便,因为我们只需要在 Config 服务器端的 bootstrap.yml 文件中定义一个属性值。
注意 对称密钥的长度应该是 12 个或更多个字符,最好是一个随机的字符集。关于服务发现
基于云的微服务环境的解决方案是使用服务发现机制,这一机制具有以下特点。 - 高可用——服务发现需要能够支持“热”集群环境,在这种环境中,服务查找可以在服务发现集群中跨多个节点共享。
- 点对点——服务发现集群中的每个节点共享一个服务实例的状态。
- 负载均衡——服务发现需要在所有服务实例之间动态地对请求进行负载均衡,这能确保服务调用分布在由它管理的所有服务实例上。
- 有弹性——服务发现的客户端应该在本地缓存服务信息。本地缓存允许服务发现功能逐步降级,这样,如果服务发现服务变得不可用,应用程序仍然可以基于其本地缓存中维护的信息来运行和定位服务。
- 容错——服务发现需要检测出服务实例什么时候是不健康的,并从可以接收客户端请求的可用服务列表中移除该实例。服务发现应该在没有人为干预的情况下,对这些故障进行检测,并采取行动。
服务发现架构
- 服务注册——服务如何使用服务发现代理进行注册。
- 客户端查找服务地址——服务客户端如何查找服务信息。
- 信息共享——节点如何共享服务信息。
- 健康监测——服务如何将它们的健康信息传回服务发现代理。
使用 Spring Cloud 和 Netflix的 Eureka 服务发现引擎来实现服务发现模式。对于客户端负载均衡,我们将使用 Spring Cloud LoadBalancer。
注意 每次服务注册需要 30 秒的时间才能显示在 Eureka 服务中,这是因为 Eureka 需要从服务接收 3 次连续心跳包 ping,每次心跳包 ping 间隔 10 秒,然后才能使用这个服务。在部署和测试服务时,要牢记这一点。
通过 Spring Eureka 注册服务:在默认情况下,Eureka 注册通过主机名联系它的服务。这种方式在基于服务器的环境中运行良好,在这样的环境中,服务会被分配一个 DNS 支持的主机名。但是,在基于容器的部署(如 Docker)中,容器将以随机生成的主机名启动,并且该容器没有DNS 条目。如果你没有将 eureka.instance.preferIpAddress 设置为 true,那么你的客户端应用程序将无法正确地解析主机名的位置,因为该容器不存在 DNS 条目。设置 preferIpAddress 属性将通知 Eureka 服务,客户端想要通过 IP 地址进行通告。
基于云的微服务应该是短暂的和无状态的。它们可以随意启动和关闭,所以 IP 地址更适合这些类型的服务。
Eureka 的 REST API要通过 REST API 查看服务的所有实例,可以选择 GET 方法访问端点:http://<eureka service>:8070/eureka/apps/<APPID>
返回结果展示了在 Eureka 中注册的服务实例的 IP 地址以及服务状态。Eureka 服务返回的默认格式是 XML。
Eureka 仪表板:一旦 Eureka 服务启动完毕,我们就可以用浏览器访问 http://localhost:8070 来查看Eureka仪表板。Eureka 仪表板允许我们查看服务的注册状态。
使用服务发现来查找服务:
- 使用 Spring Discovery Client 查找服务实例Spring Discovery Client 提供了对 Load Balancer 及其注册服务的最低层次访问。使用Discovery Client,可以查询通过 Spring Cloud LoadBalancer 注册的所有服务以及这些服务对应的 URL。
- 使用带有 Load Balancer 功能的 Spring Rest 模板调用服务
这是通过Spring 与 Load Balancer 进行交互的更为常见的机制之一。要使用带有 Load Balancer 功能的 RestTemplate类,需要使用 Spring Cloud 注解@LoadBalanced 来定义一个 RestTemplate bean。 - 使用 Netflix Feign 客户端调用服务Netflix 的 Feign 客户端库是启用 Load Balancer 的 RestTemplate 类的替代方案。Feign 库采用不同的方法来调用 REST 服务。使用这种方法,开发人员首先定义一个 Java 接口,然后添加 Spring Cloud 注解,以映射 Spring Cloud LoadBalancer 将要调用的基于 Eureka 的服务。Spring Cloud 框架将动态生成一个代理类,用于调用目标 REST 服务。除了接口定义,开发人员不需要编写其他调用服务的代码。
当糟糕的事情发生时:使用 Spring Cloud 和 Resilience4j 的弹性模式
客户端弹性软件模式的重点是,在远程资源由于错误或表现不佳而失败时,保护远程资源(另一个微服务调用或数据库查询)的客户端免于崩溃。这些模式允许客户端快速失败,而不消耗诸如数据库连接和线程池之类的宝贵资源。它们还可以防止远程服务表现不佳的问题向客户端的消费者进行“上游”传播。
Resilience4j 是一个受 Hystrix 启发的容错库。它提供了以下模式,以提高网络问题或多个服务故障的容错能力。
- 断路器——当被调用的服务发生失败时,停止发出请求。
- 重试——在服务暂时失败时重试服务。
- 舱壁——限制传出的并发服务请求数以避免过载。
- 限流——限制一个服务在一定时间内接收的调用数。
- 后备——为失败的请求设置备用路径。
使用 Spring Cloud Gateway 进行服务路由
在像微服务这样的分布式架构中,需要确保跨多个服务调用的关键行为(如安全、日志记录和用户跟踪)的正常运行。要实现此功能,我们需要在所有服务中始终如一地强制这些特性,而不需要每个开发团队都构建自己的解决方案。虽然可以使用公共库或框架来帮助在单个服务中直接构建这些功能,但这样做会造成 3 个影响。 - 在每个服务中很难一致地实现这些功能。
- 将实现横切关注点(如安全性和日志记录)的责任推给各个开发团队,会大大增加有人没有正确实现或忘记实现这些功能的可能性。
- 可能会在所有服务中创建一个顽固的依赖。
为了解决这个问题,需要将这些横切关注点抽象成一个服务,该服务可以独立存在并充当我们的架构中所有微服务调用的过滤器和路由器。我们把这个服务称为网关(gateway)。我们的服务客户端不再直接调用微服务。取而代之的是,服务网关作为单个策略执行点(Policy Enforcement Point,PEP),所有调用都通过服务网关进行路由,然后被路由到最终目的地。
可以在服务网关中实现的横切关注点包括以下几个。 - 静态路由——服务网关将所有的服务调用放置在单个 URL 和 API 路由的后面。这简化了开发,因为我们只需要知道所有服务的一个服务端点就可以了。
- 动态路由——服务网关可以检查传入的服务请求,根据来自传入请求的数据,为服务调用者执行智能路由。
- 验证和授权——由于所有服务调用都经过服务网关进行路由,因此服务网关是检查服务调用者是否已经对自己进行了验证的自然场所。
- 度量数据收集和日志记录——当服务调用通过服务网关时,可以使用服务网关来收集度量数据和日志信息,还可以使用服务网关确保在用户请求上提供关键信息以确保日志统一。这并不意味着你不应该从单个服务中收集度量数据。相反,通过服务网关你可以集中收集许多基本度量数据,如服务调用次数和服务响应时间。
在构建服务网关实现时,要牢记以下几点。 - 在单独的服务组前面,负载均衡器很有用。在这种情况下,将负载均衡器放到多个服务网关实例的前面是一个恰当的设计,可确保服务网关实现可以根据需要伸缩。但是,将负载均衡器置于所有服务实例的前面并不是一个好主意,因为它会成为瓶颈。
- 要保持为服务网关编写的代码是无状态的。不要在内存中为服务网关存储任何信息。如果你不小心,就有可能限制网关的可伸缩性。然后,你需要确保数据在所有服务网关实例中被复制。
- 要保持为服务网关编写的代码是轻量的。服务网关是服务调用的“阻塞点”。具有多个数据库调用的复杂代码可能是服务网关中难以追踪的性能问题的根源。
Spring Cloud Gateway 是建立在 Spring 5、Project Reactor 和 Spring Boot 2.0 上的 API 网关实现。这个网关是一个非阻塞网关。
Spring Cloud Gateway 提供了一些功能,具体包括以下几个。 - 将应用程序中的所有服务的路由映射到单个 URL。但是,Spring Cloud Gateway 不局限于单个 URL。实际上,我们可以使用 Spring Cloud Gateway 定义多个路由入口点,使路由映射非常细粒度(每个服务端点都有自己的路由映射)。然而,第一个也是最常见的用例是构建一个单一的入口点,所有服务客户端调用都将经过这个入口点。
- 构建可以对通过网关的请求和响应进行检查和操作的过滤器。这些过滤器允许我们在代码中注入策略执行点,以一致的方式对所有服务调用执行大量操作。换句话说,这些过滤器允许我们修改传入的 HTTP 请求和传出的 HTTP 响应。
- 构建断言(predicate),断言是让我们可以在执行或处理请求之前检查请求是否满足一组给定条件的对象。Spring Cloud Gateway 包括一组内置的路由断言工厂(Route Predicate Factory)。
要开始使用 Spring Cloud Gateway,需要完成下面两件事。(1)建立一个 Spring Cloud Gateway 项目,并配置适当的 Maven 依赖项。(2)配置网关以便与 Eureka 进行通信。
本质上,Spring Cloud Gateway 是一个反向代理。反向代理是一个中间服务器,它位于尝试访问资源的客户端和资源本身之间。客户端甚至不知道它正与服务器进行通信。反向代理负责捕获客户端的请求,然后代表客户端调用远程资源。
在微服务架构的情况下,Spring Cloud Gateway(反向代理)从客户端接收微服务调用并将其转发给上游服务。服务客户端认为它只是在与网关通信。但事情并没有那么简单。要与上游服务进行沟通,网关必须知道如何将进来的调用映射到上游路由。Spring Cloud Gateway 有一些机制来做到这一点,包括: - 使用服务发现自动映射路由;
- 使用服务发现手动映射路由。
使用集成 Eureka 的 Spring Cloud Gateway 的优点在于,我们不仅可以拥有一个可以发出调用的单个端点,还可以添加和删除服务的实例,而无须修改网关。
注意 在我们使用自动路由映射时,网关只基于 Eureka 服务 ID 来公开服务,如果服务的实例没有在运行,网关将不会公开该服务的路由。然而,如果在没有使用 Eureka 注册服务实例的情况下,我们手动将路由映射到服务发现 ID,那么网关仍然会显示这条路由。如果我们尝试为不存在的服务调用路由,网关将返回 500 HTTP 错误。
动态重新加载路由的功能非常有用,因为它允许在不重启 Gateway 服务器的情况下更改路由的映射。
Spring Actuator 公开了基于 POST 的端点路由 actuator/gateway/refresh,其作用是让 Gateway 重新加载路由配置。
网关处理器(Gateway Handler),该处理器负责验证所请求的路径是否与它试图访问的特定路由的配置相匹配。如果所有内容都匹配,则它进入网关 Web 处理器(Gateway Web Handler),该处理器负责读取过滤器并将请求发送给这些过滤器进行进一步处理。一旦请求通过了所有过滤器,它就被转发到路由配置:一个微服务。
内置断言是让我们可以在执行或处理请求之前检查请求是否满足一组条件的对象。对于每个路由,我们可以设置多个断言工厂,它们通过逻辑与(AND)被使用和组合。
内置的过滤器工厂让我们可以在代码中注入策略执行点,并以一致的方式对所有服务调用执行大量操作。换句话说,这些过滤器允许我们修改传入的 HTTP 请求和传出的HTTP 响应。
Spring Cloud Gateway 支持以下两种类型的过滤器。 - 前置过滤器——前置过滤器在实际请求发送到目的地之前被调用。前置过滤器通常执行确保服务具有一致的消息格式(例如,关键的 HTTP 首部是否设置妥当)的任务,或者充当“看门人”,确保调用该服务的用户已通过验证(他们的身份与他们声称的一致)。
- 后置过滤器——后置过滤器在目标服务被调用并将响应发送回客户端后被调用。
保护微服务
保护微服务架构是一项复杂且费力的任务,涉及多个保护层,包括: - 应用程序层——确保有正确的用户控制,以便可以确认用户是他们所说的人,并且他们有权执行正在尝试执行的操作;
- 基础设施层——使服务保持运行、打补丁和更新,以最大限度地降低漏洞风险;
- 网络层——实现网络访问控制,使服务只能通过定义良好的端口进行访问,并且只让少量已授权的服务器访问。
本章只讨论如何在应用程序层中对用户进行身份认证和授权(列表中的第一个要点),另外两个主题是非常宽泛的安全主题,超出了本书的范围。此外,还有其他工具,如OWASP 依赖项检查项目(Dependency-Check Project),可以帮助识别漏洞。
为了实现身份认证和授权控制,我们将使用 Spring Cloud Security 模块和 Keycloak 来保护基于 Spring 的服务。Keycloak 是用于现代应用程序和服务的开源身份和访问管理软件。Keycloak是采用 Java 编写的开源软件,它支持安全断言标记语言(Security Assertion Markup Language, SAML)v2 和 OpenID Connect (OIDC)/ OAuth2 联合身份协议。
Keycloak 是为服务和应用提供的一个开源身份和访问管理的解决方案。Keycloak 的主要目标是在很少或没有代码的情况下,促进对服务和应用程序的保护。
我们经常发现开发人员混淆术语身份认证(authentication)和授权(authorization)的含义。身份认证是用户通过提供凭据来证明他们是谁的行为。授权决定是否允许用户做他们想做的事情。
一旦我们在 Keycloak 服务器端注册了一个客户端,并且建立了拥有角色的个人用户账户,就可以开始探索如何使用 Spring Security 和 Keycloak Spring Boot Adapter 来保护资源了。虽然创建和管理访问令牌是 Keycloak 服务器端的职责,但在 Spring 中,定义哪些用户角色有权执行哪些操作是在单个服务级别上发生的。
要创建受保护资源,我们需要执行以下操作: - 将相应的 Spring Security 和 Keycloak JAR 添加到我们要保护的服务中;
- 配置服务以指向我们的 Keycloak 服务器端;
- 定义什么和谁可以访问服务。
在构建用于生产用途的微服务时,应该围绕以下实践构建微服务安全。(1)对所有服务通信使用 HTTPS/安全套接字层(Secure Sockets Layer,SSL)。(2)对所有服务调用使用一个 API 网关。(3)为你的服务提供区域(如公共 API 和私有 API)。(4)通过封锁不需要的网络端口来限制微服务的攻击面。使用 Spring Cloud Stream 的事件驱动架构
事件驱动架构(event-driven architecture,EDA),也被称为消息驱动架构(message-driven architecture,MDA)。基于 EDA 的方法允许我们构建高度解耦的系统,它可以对变更做出反应,而不需要与特定的库或服务紧密耦合。当与微服务结合后,EDA 允许我们通过仅让服务监听由应用程序发出的事件流(消息)的方式,迅速地向应用程序中添加新功能。
Spring Cloud Stream 允许我们轻松实现消息发布和消费,同时屏蔽与底层消息传递平台相关的实现细节。
JSON 本身不支持版本控制。但是,如果我们需要版本控制,我们可以使用 Apache Avro。Avro是一个二进制协议,它内置了版本控制。Spring Cloud Stream 支持Apache Avro 作为消息传递协议。 - 使用消息传递在服务之间传达状态更改
当涉及传达状态时,消息队列充当许可证服务和组织服务之间的中介。这种方法提供了 4 个好处:松耦合、持久化、可伸缩性、灵活性。 - 消息传递架构的缺点
- 消息处理语义
在基于微服务的应用程序中使用消息,需要的不只是了解如何发布和消费消息。它要求我们了解应用程序消费有序消息时的行为是什么,以及如果消息没有按顺序处理会发生什么情况。如果严格要求来自单个客户的所有订单都必须按照接收的顺序进行处理,那么我们将需要有区别地建立和构造消息处理方式,而不是每条消息都可以被独立地消费。
这还意味着,如果我们正在使用消息传递来执行数据的严格状态转换,那么就需要在设计应用程序时考虑到消息抛出异常或者错误按无序方式处理的场景。
如果消息失败,是重试处理错误,还是就这么让它失败?如果其中一个客户消息失败,那么如何处理与该客户有关的未来消息?这些都是需要思考的重要问题。 - 消息可见性
在微服务中使用消息,通常意味着同步服务调用与异步服务处理的混合。消息的异步性意味着消息在发布或消费时可能不会被立刻接收或处理。 - 消息编排
基于消息的应用程序可能涉及多个不同服务的日志,在这些服务中,用户事务可以在不同的时间不按顺序执行。
我们可以通过 Spring Cloud Stream 使用多个消息平台,包括 Apache Kafka 项目和RabbitMQ,而平台的具体实现细节则被排除在应用程序代码之外。在应用程序中实现消息发布和消费是通过平台无关的Spring 接口实现的。
Kafka 是一种高性能的消息总线,允许我们将消息流从一个应用程序异步发送到一个或多个其他应用程序。Kafka 是用 Java 编写的,由于 Kafka 具有高可靠性和可伸缩性,在许多基于云的应用程序中,它已经成为事实上的标准消息总线。Spring Cloud Stream 还支持使用 RabbitMQ 作为消息总线。
使用 Spring Cloud Stream,有 4 个涉及发布消息和消费消息的组件:发射器(source);通道(channel);绑定器(binder);接收器(sink)。
云缓存与消息传递使用 Redis 作为分布式缓存与云中的微服务开发密切相关。我们可以使用 Redis 实现以下功能。
- 提高查找常用数据的性能。使用缓存,可以通过避免读取数据库来显著提高几个关键服务的性能。
- 减少持有数据的数据库表上的负载(和成本)。在数据库中访问数据可能是一项昂贵的工作。应用程序发出的每一次读取都是一次收费事件。使用 Redis 服务器通过主键读取要比访问数据库的成本效益更高。
- 增加弹性,以便在主数据存储或数据库存在性能问题时,服务能够优雅地降级。根据在缓存中保存的数据量,缓存解决方案可以帮助减少从访问数据存储中出现的错误的数量。
注意 在与缓存进行交互时,要特别注意异常处理。为了提高弹性,如果我们无法与Redis 服务器通信,我们绝对不会让整个调用失败。相反,我们会记录异常,并让调用转到组织服务。使用 Spring Cloud Sleuth和 Zipkin 进行分布式跟踪
Spring Cloud Sleuth 与关联 ID
关联 ID 是一个随机生成的唯一的数字或字符串,它在事务启动时被分配给一个事务。当一个事务流过多个服务时,关联 ID 从一个服务调用传播到另一个服务调用。
通过将Spring Cloud Sleuth 添加到我们的微服务中,我们可以: - 在关联 ID 不存在的情况下透明地创建一个关联 ID 并将其注入我们的服务调用中;
- 管理关联 ID 到出站服务调用的传播,以便自动添加事务的关联 ID;
- 将关联信息添加到 Spring 的 MDC 日志记录,以便 Spring Boot 默认的 SL4J 和Logback实现自动记录生成的关联 ID;
- 可选地,将服务调用中的跟踪信息发布到 Zipkin 分布式跟踪平台。注意 有了 Spring Cloud Sleuth,如果我们使用 Spring Boot 的日志记录实现,就可以将关联 ID 自动添加到我们的微服务的日志语句中。
Spring Cloud Sleuth 向每个日志条目添加了以下 4 条信息
(1)输入日志条目的服务的应用程序的名称。在默认情况下,Spring Cloud Sleuth 使用应用程序的名称(spring.application.name)作为在跟踪中写入的名称。(2)跟踪 ID(trace ID),它是关联 ID 的等价术语。它是表示整个事务的唯一编号。(3)跨度 ID(span ID),它是表示整个事务中某一部分的唯一 ID。参与事务的每个服务都将具有自己的跨度 ID。如果你集成 Zipkin 来可视化事务,那么跨度 ID 尤其重要。(4)输出一个 true/false 指示器,用于确定是否将跟踪信息发送到 Zipkin。在大容量服务中,生成的跟踪数据量可能是海量的,并且不会增加大量的价值。Spring Cloud Sleuth 让我们确定何时以及如何将事务发送给 Zipkin。日志聚合与 Spring Cloud Sleuth
选择 ELK 是因为: - 它是开源的;
- 它设置和使用都简单,对用户友好;
- 它是一套完整的工具,通过它我们可以搜索、分析和可视化不同服务生成的实时日志;
- 它允许我们集中所有日志记录,以识别服务器和应用程序问题。
为了设置 ELK 与我们的环境一起工作,我们需要采取以下措施。(1)在我们的服务中配置 Logback。(2)在 Docker 容器中定义并运行 ELK 技术栈应用程序。(3)配置 Kibana。(4)通过基于来自 Spring Cloud Sleuth 的关联 ID 发出查询来测试这一实现。
Logstash 对数据进行过滤、转换,并将数据传送到中央数据存储(在本例中为Elasticsearch)。Elasticsearch 以可搜索的格式索引和存储数据,以便 Kibana 日后查询。存储完数据后,Kibana使用 Elasticsearch 的索引模式来检索数据。此时,我们可以创建一个特定的查询索引,并输入一个 Spring Cloud Sleuth 跟踪ID,以查看来自不同服务的所有包含该跟踪 ID 的日志条目。一旦存储了数据,我们就可以通过访问 Kibana来查找实时日志。
Spring Cloud Sleuth 允许我们使用其跟踪 ID 和跨度 ID“装饰”HTTP 响应信息。
一个更简单的解决方案是编写一个在 HTTP 响应中注入跟踪 ID 的 Spring Cloud Gateway 过滤器。
Zipkin 是一个分布式跟踪平台,通过它我们可以跟踪跨多个服务调用的事务。Zipkin 能够让我们以图形方式查看事务占用的时间量,并分解在调用中涉及的每个微服务所用的时间。在微服务架构中,Zipkin 是识别性能问题的宝贵工具。
我们将 Docker 容器与 ELK 技术栈(Elasticsearch、Logstash 和 Kibana)集成在一起。这使我们能够转换、存储、可视化和查询来自我们的服务的日志数据。虽然统一的日志记录平台很重要,但通过事务的微服务来可视化地跟踪事务的能力也是一个有价值的工具。
Zipkin 让我们可以查看服务和事务流之间存在的依赖关系,并了解用户事务中涉及的每个微服务的性能特征。Spring Cloud Sleuth 很容易与 Zipkin 集成。Zipkin 自动从 HTTP 调用捕获跟踪数据,这将成为启用 Spring Cloud Sleuth 的服务中使用的入站/出站消息通道。部署微服务
基础设施即代码——最终被推向开发以及更高的环境中的软件制品是机器镜像。
不可变服务器—— 一旦建立了服务器镜像,服务器的配置和微服务就不会在供应过程之后被触碰。
凤凰服务器—— 服务器运行的时间越长,就越容易发生配置漂移。通过杀死运行微服务的服务器,并使其从服务器机器镜像中重新启动,这样可以减少配置漂移的发生,还可以尽早暴露其他问题。关于凤凰服务器的不变性与重生
这种死亡和复活的新服务器被 Martin Fowler 称为“凤凰服务器”,因为当旧服务器被杀死时,新服务器会从毁灭中再生。
凤凰服务器模式有两个基本的优点。首先,它暴露配置漂移并将配置漂移驱逐出你的环境。如果你不断地拆除并建立新服务器,那么你很有可能会提前暴露配置漂移。这对确保一致性有很大的帮助。其次,通过允许你发现服务器或服务在被杀死并重新启动后不能完全恢复的状况,凤凰服务器模式有助于提高弹性。请记住,在微服务架构中,服务应该是无状态的,服务器的死亡应该是一个微不足道的小插曲。随机地杀死和重新启动服务器可以很快暴露你在服务或基础设施中具有状态的情况。
构建/部署管道的自动化需要大量的脚本和配置才能正确进行。构建所需的工作量不容小觑。
环境特定的服务器配置应该在服务器建立时作为参数传入。
持续集成和持续交付持续集成(continuous integration,CI)是一系列软件开发实践,在这一系列软件开发实践中,团队成员在短时间内将他们的更改集成到存储库中,以检测可能的错误并分析他们创建的软件质量。这是通过使用包含执行测试代码的自动持续代码检查(构建)来实现的。另一方面,持续交付(continuous delivery,CD)是一种软件开发实践,在这种实践中,交付软件的流程是自动化的,允许在生产环境中进行短期交付。