聊
刚刚更换了新的工作环境,第一周就赶上公司的技术公开课《云时代技术人员的颠覆与突破》,课程提到了《云原生》,恰好自己看过一本云原生的书籍,由于一直没有时间对书中讲到的知识进行应用实践,渐渐的就放下了,这次借助技术公开课的学习,及新环境的平台,我也希望自己将一些书籍知识不仅仅放在书本和脑袋中,而是将它们以实践的方式写出来,并尽可能的应用于工作中,也希望能给其他开发者带来一些共鸣,本文介绍一下《云原生》的核心理念,十二要素原则,该原则不限于语言实现,但本文将其实现构建于SpringBoot框架中。
十二要素原则
十二要素原则是由Heroku云平台的创建者所编写的一套应用程序开发理论。Twelve-Factor App 是一个由Heroku联合创始人Adam Wiggins 创建的网站,用于描述利用现代云平台来实际构建Saas应用程序的一份宣言。
[网站地址]: https://www.12factor.net/zh_cn/
本篇我将用Java开发应用,套用十二要素原则的实践进行介绍。
十二要素程序的实践
| 十二要素 | 描述 |
|---|---|
| 代码库 | 一份版本控制下的基准代码库,多份部署 |
| 依赖 | 显式声明和隔离依赖关系 |
| 配置 | 在环境中存储配置 |
| 后端服务 | 把后端服务当作附加资源 |
| 构建,发布,运行 | 严格分离构建和运行阶段 |
| 进程 | 将应用程序作为一个活多个无状态进程执行 |
| 端口绑定 | 通过端口绑定暴露服务 |
| 并发 | 通过进程模型进行扩展 |
| 易处理 | 通过快速启动和正常关机来最大限度地提高健壮性 |
| 开发/生产环境一致 | 尽可能保持开发、预发布和生产环境的配置一致 |
| 日志 | 将日志视为事件流 |
| 管理进程 | 将管理任务作为一次性进程运行 |
代码库
一份版本控制下的基准代码库,多份部署
应用程序的源代码仓库应该只包含一个应用程序,并列出它所依赖的资源清单。对于不同的环境,我们应该不需要重新编译或打包应用程序。每个环境中特有的设置应该与代码无关。
SpringBoot 实现
反例
正例
依赖
显式声明和隔离依赖关系
应该显式地声明应用程序的依赖关系,并且可以通过依赖项管理软件(如Apache Maven)的仓库,下载任意或所有的依赖关系。
满足十二要素的程序,不应该依赖于底层系统级包才能够运行。应用程序的所有依赖关系都应该被明确地声明在清淡文件中,该文件清晰地列出了每个引用的详细信息。
SpringBoot 实现
反例
正例
配置
在环境中存储配置
应用程序的代码应与配置严格分开。应用程序的配置应由各自环境管理。
所依赖服务的连接字符串、密码或者主机名等,应该存储为环境变量,这样易于修改,无需部署配置文件。
应用程序在环境之间的任何差异,应该都认为是一种环境配置,并且应该存储在环境中,而不是应用程序中。
SpringBoot 实现
反例
正例
后端服务
把后端服务当作附加资源
后端服务是十二要素程序正常运行所需要依赖的任何服务。举例说明,后端服务包括数据库,API驱动的RESTfulWeb服务,SMTP服务器或FTP服务器。
后端服务被认为是应用程序的某种资源。这些资源在运行期间被绑定到应用程序上。十二要素程序应该在无须代码改动的前提下,可以从一个测试环境中的嵌入式SQL数据库,切换到预发布环境下的一个独立MySQL数据库。
SpringBoot 实现
反例
正例
构建、发布、运行
严格分离构建和运行阶段
十二要素程序严格分离了构建、发布、和运行几个阶段。
- 构建阶段
- 构建阶段将应用程序的源代码编译或打包到一个程序包中。创建的包被称为一次构建物。
- 发布阶段
- 发布阶段需要将某次构建与配置相结合。随后,创建出的发布文件应该可以在某个执行环境中运行。无论是使用版本号还是时间戳,每个版本应该有一个唯一的标识符。每个发布文件都应该被添加到一个目录中,可以通过发布管理工具回滚到之前的发布版本
- 运行阶段
- 运行阶段是指在可执行环境中运行一个指定的应用版本。
通过将这些阶段分成独立的流程,在运行时就完全不可能去更改任何程序代码。更改应用程序代码的唯一方法,就是通过构建阶段来创建一个新的版本,或者回滚到之前部署的某个版本。
SpringBoot 实现
反例
正例
进程
将应用程序作为一个或多个无状态进程执行
十二要素程序应该是一种无共享、无状态的架构。应用程序可以依赖的唯一持久化元素是后端服务。提供持久化的后端服务包括数据库或对象存储。应用程序运行时需要的所有资源都应该作为后端服务。无论应用程序是否是无状态的,都需要进行测试,保证应用程序的执行环节在关闭或启动时都不会有任何数据丢失发生。
十二要素程序不应该在执行环境中的本地文件系统上存储任何有关的状态信息。
SpringBoot 实现
反例
正例
端口绑定
通过端口绑定暴露服务
十二要素程序本身是完全独立的,这表示它们不需要在运行时引入一个web服务器,就可以提供对外的Web服务能力。每个应用程序通过将自身HTTP端口绑定到执行环境中,来对外公开自己的访问。在部署期间,对于来自公开主机的请求,路由层灰将请求路由到应用程序的执行环境和绑定的HTTP端口来进行处理。
SpringBoot 实现
反例
正例
并发
通过进程模型进行扩展
在需要时,应用程序应该能够通过增加进程或线程来并行执行工作。JVM应用程序能够使用多个线程自动处理进程中的并发情况。
根据类型不同,应用程序应该能够同时分配多个工作。目前,JVM的大多数应用程序框架都内置了这个能力。对于一些需要长时间运行的数据处理工作,你应该使用执行器,异步的将多个并发工作分配给可用的线程池。
十二要素程序还必须能够水平扩展,并且能够将多个并发工作分配给可用的线程池。
SpringBoot 实现
反例
正例
易处理
通过快速启动和正常关机来最大限度的提高健壮性
十二要素程序需要被设计成易处理的。应用程序应该可以在执行过程中的任意时刻停止,并且能够优雅地释放进程。
应用程序的进程应尽可能地减少启动时间。应用程序应该在几秒钟内启动,并且开始能够处理接收的请求。短暂的启动时间会降低应用程序进行水平扩展时花费的时间。
如果应用程序的进程启动花费太长时间,可能会在高峰时间导致所有的应用程序实例过载,从而降低系统整体的可用性。通过将应用程序的启动时间减少到几秒钟,新的实例能够更快速地响应不可预测的流量峰值,而不会降低系统的可用性或者性能。
SpringBoot 实现
反例
正例
开发/生产环境一致
尽可能保持开发、预发布和生产环境的配置一致
十二要素程序应防止开发和生产环境之间存在差异。有三种类型的差异需要注意。
时间差异
开发人员希望开发的变更代码能够快速部署到生产环境中。
人员差异
更改代码的开发人员应该密切关注生产环境的部署,并在部署后密切监控应用程序的行为。
工具差异
每个环境应该具备相同的技术和框架,避免因为一些细小的意外差异导致不一致的行为出现。
SpringBoot 实现
反例
正例
日志
将日志视为事件流
十二要素程序将写日志看作到标准输出的一个有序事件流。应用程序不应该负责管理自身日志文件的存储。应用程序日志输出的收集和归档工作应该由运行环境来完成。
SpringBoot 实现
反例
正例
管理进程
将管理任务作为一次性进程运行
有时,应用程序的开发人员需要运行一次性的管理任务。这类任务可能包括数据库迁移或运行已提交到源代码仓库中的一次性脚本。这些都被认为是管理进程。管理进程应该在应用程序的执行环境中运行,并将脚本提交到代码仓库中,以便保持各个环境之间的一致性。