Post

SpringCloud 与 Twelve-factor app (SaaS的最佳实践宣言)

本博文为书本第二章 Exploring the microservices world with Spring Cloud 的读书笔记。

书中第二章介绍了SpringCloud的基本概念和 将要用到的组件。
然后介绍了对于构建微服务的 Twelve-factor app 宣言,以其作为最佳实践的指导。
最后介绍了本书具体构建的项目背景并创建一个简单的微服务骨架。

TL;DR

  • SpringCloud 是一个来自Netflix/HashiCorp等公司的开源技术栈的集合, 用于构建微服务架构。通过使用简单的注解搭配一些配置信息,即可使用这些历经考验的组件。
  • 云原生应用指的是用可拓展的组件构建(例如容器技术),以微服务架构部署,通过DevOps实践在虚拟化环境中运行的应用。
  • DevOps是 development(Dev)和operations(Ops)的缩写,指的是一种软件开发方法论,其集中于软件开发与IT运营(运维) 的高效沟通、协作与集成整合,旨在通过自动化和监控来加速软件交付,并用更低的成本完成基础设施的改变。
  • 12factor app 宣言旨在提供微服务架构的最佳实践
  • 其涵盖了这些方面: codebase, dependencies, configuration, backing services, build/release runs, processes, port binding, concurrency, disposability, dev/prod parity, logs, and admin processes

书中对于DevOps的描述为Methodology(方法论),12factor app的描述为Manifesto(宣言), ChatGPT对这两个词的异同的解释为:方法论是一种方法和规则的体系,用于指导实践和工作,而宣言则是一种公开表达观点和立场的声明。在软件开发领域,” Agile Manifesto”(敏捷宣言)是一份宣言,提出了敏捷开发的价值观和原则,而敏捷方法论是一套基于这些原则的实践方法和技术。


将会使用到的SpringCloud组件

微服务在解决传统单体项目的问题上有很多优势,但是也带来了更多的挑战. 一个系统越是分布式化,那么就有越多的地方可能出错。 相较于对付一个单一的应用,现在我们面临的是一个多个独立组建互相沟通的生态系统。 这也是为什么开发者在构建微服务架构时,经常遇到不同的管理与同步上的问题。

为了防止潜在的失败,书中介绍使用SpringCloud进行微服务架构的构建。其提供了一系列的组件来快速构建微服务架构。这些组件来自于Netflix/HashiCorp等公司的开源技术栈。其往往是历经考验的,而且在SpringCloud中使用起来也很简单。

当然技术栈本身的发展是日新月异的,所以这里主要介绍这些组件所要解决的问题,这将会是一个高抽象层次的概述,而不涉及具体的技术细节.

什么是SpringCloud

从头开始实现所有的微服务架构模式将会是一项巨大的工作。不过幸运的是,Spring团队已经集成了大量经过实战检验的开源项目到了一个Spring子项目中,并统称为SpringCloud. SpringCloud就是针对云原生应用开发的工具集合,其提供了开源组件的同时保持了Spring核心的简单性,这使得开发者可以专注于业务逻辑而不是底层微服务架构的实现细节。

下图展示了图中将会使用到了不同的SpringCloud组件

技术栈一览

SpringCloud Config

使用SpringCloudConfig来对应用程序的配置文件(特别是与环境相关的配置)进行单独的管理。 这是为了确保 无论有多少个微服务实例,其配置都是一致的。

SpringCloudConfig原生提供了一个配置管理仓库,但也支持与一些其他的开源项目进行集成,例如

  • Git
  • Consul
  • Eureka

SpringCloud Service Discovery

通过云服务发现,可以将微服务节点的物理位置(IP/服务器名称)进行抽象,进行用逻辑名称进行访问。SpringCloud服务还负责在服务节点重启或宕机后的服务的重注册与取消注册.

Eureka也支持服务的负载均衡,不过负载均衡也可以在客户端实现,这样可以减少对服务发现的依赖。

可以使用以下的开源项目进行服务发现

  • Consul
  • Zookepper
  • Eureka

书中为了最大化受众,使用了Eureka作为服务发现的组件。但也在最后的附录中介绍了如何使用另外两个组件进行服务发现的配置。

SpringCloud LoadBalancer and Resilience4j

SpringCloud包装了Resilience4j库与LoadBalance项目来让开发者可以轻松的使用负载均衡与断路器、重试、bulkhead等模式的实现。 同时其也简化了与Eureka的集成,并且支持在客户端进行负载均衡,这样可以减少对服务发现的负载均衡实现的耦合。

SpringCloud API Gateway

就如名字一样,为所有的微服务节点进行服务路由. 且通过API Gateway作为访问微服务的前门,其可以对请求进行过滤、验证、监控、限流等操作。

SpringCloud Stream

用于在微服务之间传递异步消息,其可以与Kafka、RabbitMQ等消息中间件进行集成,可以将SpringCloud Stream看作是一个消息中间件的抽象层

SpringCloud Sleuth

用于跟踪微服务之间的调用链路,即将独特的跟踪标识符添加到HTTP调用与消息(中间件)组件中.这样就可以获得一个事务在微服务架构中的完整视图。

但其作用是有限的,最终这些信息的表现形式为在不同的微服务中的日志中添加了相同的跟踪标识符. 一个完成的做法是与日志聚合工具进行集成,例如Zipkin、ELK等.这样就可以做到可视化地查看调用链路

ELK是Elasticsearch、Logstash、Kibana三个开源项目组成。简单来说三者分别用于:搜索引擎(搜索日志信息)、日志处理与分析、查询与可视化

SpringCloud Security

SpringCloud的安全即基于令牌的,其使用了OAuth2协议来进行认证与授权.其让服务可以通过认证服务器发送的令牌进行访问,而不是传递用户凭证信息。

12factor app - 微服务架构的最佳实践宣言

前置补充 - Cloud ready 与 Cloud native

Cloud ready 是一个针对已存在的项目的状态评估,其指的是一个项目是否可以在云环境中运行。(那么对应的就有Cloud not ready的状态) 这里的云环境指的是IaaS(基础设施即服务)或PaaS(平台即服务)环境。 要做到这一点,最关键的步骤就是做到应用程序配置的外部化(配置与代码分离),这样便确保应用程序可以在不更改源代码的情况下在不同的环境中进行构建与运行。

Cloud native 指的是哪些在设计之初便考虑要部署在云环境中的应用程序。这些应用程序的设计与实现都是为了在云环境中运行而进行的. 这意味着在设计之初就要考虑到应用程序的可伸缩性、弹性、可观测性、可维护性等特性。 并且这些应用程序的设计与实现都是基于微服务架构的,使用持续交付与部署的方式进行开发与运维,通过容器化技术进行部署。

12factor app 即是Cloud native的最佳实践宣言,其提出了12个原则,这些原则都是为了让应用程序可以在云环境中运行而进行的设计与实现。

12因素最佳实践

12factor app在本博文中的表述是较高抽象层次的,因为具体的实现将会在后续实践的过程中进行详细的阐述

同时也推荐直接去看原文,12factor app的官网也有中文版的, 由于其本身就是宣言性质的,所以一共也就十几页的内容

CodeBase

其实一些宣言,已经潜移默化到了我们的开发中,例如代码库的唯一性,这也是12factor app的第一条原则 这条最佳实践宣言也是具有普遍性的,不只是微服务架构开发,基本上所有的软件开发都可以使用该原则进行指导。

  • 每一个微服务的代码库应当是唯一的,且可以通过版本控制工具进行管理。
  • 代码库的在不同的部署环境中可以有多个(不同版本的)实体 (Dev, Test, Staging, production),但不可以将这些实体同其他微服务共享。

即一个微服务在不同的部署环境中可以有不同的版本代码,这并不违背唯一代码库的模式
但一个微服务不可以在代码上直接依赖另一个微服务的CodeBase,因为这将违背唯一代码库的模式( 如果直接依赖了另一个微服务的代码,那么这个微服务的代码库就不是唯一的了
好的解决方案应该是将共享代码打包为类库,然后使用依赖管理策略去加载这些依赖。 )

Dependencies

使用依赖管理工具去管理依赖,例如java中的Maven、Gradle等

程序不应该隐式依赖任何东西,即所有的依赖应该显式声明在依赖管理工具中

Config

不要将配置信息硬编码到代码中,而是将其外部化,这样可以使得应用程序在不同的部署环境中运行而不需要更改源代码

Backing services

后端服务指的是程序通过网络调用的外部服务,可以是 数据库、缓存、消息中间件、SMTP服务等

本宣言的要点就是将后端服务视作程序的附加资源,进行松耦合的设计与实现。

这意味着程序不区别后端服务的来源,例如不论一个数据库是在本地的还是云端服务商提供的,其对程序来说都是一样的。程序对其的处理是一样的。 而后,当一个后端服务出现故障时,程序应该可以(在不改变代码的情况下)通过更换另一个后端服务来进行恢复,而不是对后端服务的故障进行修复。

backing-service

Build, release, run

Build 、 Configuration 、 Release 三者完全分离(严格区分)

程序的构建环节是完全独立的,不需要考虑与依赖其他因素

配置环节是在程序构建之后,发布之前进行的,其目的是为了将程序与环境进行解耦,使得程序可以在不同的环境中运行

发布环节是将程序部署到目标环境中(根据环境不同,选择特定配置),这个环节是可以重复的,即可以多次发布同一个程序到不同的环境中

如果不把这三个环节进行严格区分,那么就会出现很多问题,这些问题往往是 未追踪的运行时配置修改导致的。 且就算没有出现问题,那么最终这些无法被追踪和配置也会被遗弃或是无法同步到其他环境中,这样就会导致环境之间的差异性,从而导致程序无法在不同的环境中运行。

Processes

程序应该以一种无状态与无共享的方式运行,即程序不应该依赖本地的存储,而是应该依赖外部的存储服务,例如数据库、缓存等

这样可以使得微服务节点可以随时被销毁与重启,而不会丢失任何数据

Port binding

微服务节点应该不依赖外部网络服务器,而是完全自我加载,创建一个面向网络的服务,并通过端口绑定来提供服务并监听来自该端口的请求。

这一点已经是Spring的缺省实现了,没有什么要注意的。

Concurrency

一台独立的虚拟机的扩展有瓶颈(垂直扩展),所以应用程序必须可以在多台物理机器间跨进程工作。即进行水平扩展

12factor app 不需要守护进程,应该借助操作系统的进程管理器(例如 systemd ,分布式的进程管理云平台,或是类似 Foreman 的工具) ,来管理 输出流 ,响应崩溃的进程,以及处理用户触发的重启和关闭超级进程的请求。

Disposability

即微服务可以被随时销毁与快速启动。

Dev/prod parity

尽可能保持 开发环境 与生产环境的一致性,这样可以减少在生产环境中出现的问题,也可以减少在开发环境中出现的问题。

具体来说,在开发时就应该使用生产环境中的配置,例如数据库、缓存等,而不是使用本地的配置。 而在生产环境中,应该使用与开发环境相同的构建工具,例如Maven、Gradle等,而不是使用其他的构建工具。

Logs

日志应该被视作事件流,微服务节点自身不考虑日志的存储,而是将日志输出到标准输出流中,由日志管理工具进行收集与存储。

Admin processes

存在一些不同于普通业务流程的管理操作,例如数据库迁移,数据导入等。
这些任务虽然往往只是临时性的操作,但是也应该被视作程序的一部分,需要创建最终可部署到生产环境的脚本来完成这一点。 且应该创建独立的管理进程来完成这些任务,而不是将其与微服务节点混合在一起。 (可用SpringCloudGateWay完成这方面的工作)

This post is licensed under CC BY 4.0 by the author.