What · 是什么
以一个或多个无状态进程执行应用
应用在运行环境中作为一个或多个进程执行;最简情形是独立脚本 + 本地笔记本运行时 + 命令行启动,如 python my_script.py。
复杂生产部署可含多种进程类型,实例化为零或多个运行进程,形成完整的运行拓扑,这使进程可以任意复制、迁移与水平扩展。
十二要素进程是无状态、share-nothing 的:任何需持久化的数据存入有状态 backing service,通常是数据库。
进程的内存空间或文件系统可作短暂的单事务缓存,如下载大文件、处理并把结果存入数据库,是应用横向扩展与弹性伸缩的前提条件。
十二要素应用绝不假设缓存于内存或磁盘的东西在未来请求/任务中仍可用——多进程运行时,未来请求很可能由另一个进程处理。
即使单进程,重启(代码部署、config 变更或运行环境把进程搬迁到不同物理位置)通常也会抹掉全部本地状态。
django-assetpackager 等资源打包器把文件系统当编译资源的缓存,十二要素更倾向在 build 阶段完成编译。
Jammit、Rails asset pipeline 可配置为在 build 阶段打包资源,避免运行期编译。
sticky sessions(把用户会话数据缓存于进程内存并期待同访客被路由到同进程)是十二要素违规,绝不应使用或依赖。
会话状态数据适合存入带时间过期的 datastore,如 Memcached 或 Redis,而非进程内存。
Why · 为什么
原理 · Principle
share-nothing 架构使进程可随意水平扩展:多进程下未来请求大概率由不同进程处理,无状态依赖。
重启会抹掉本地状态,故不得假设任何进程本地缓存对未来请求可用,设计必须外置,这使进程可以任意复制、迁移与水平扩展。
持久数据外置保证状态一致性、可恢复性与横向弹性,可靠性与扩展性兼得,是应用横向扩展与弹性伸缩的前提条件。
How · 怎么做
设计无状态进程,持久数据全部外置于有状态 backing service,进程只处理请求,这使进程可以任意复制、迁移与水平扩展。
会话数据存入带时间过期的存储(Memcached、Redis),而非进程本地内存,是应用横向扩展与弹性伸缩的前提条件。
资源编译在 build 阶段完成,而非运行期用文件系统缓存,避免缓存失效,保证任何进程随时被替换都不会丢失业务状态。
不用 sticky sessions;进程本地状态仅作单事务缓存,用完即弃,运行期的弹性与健壮性因此得到根本保障。
大文件下载→处理→结果入库的流程把最终结果写入数据库而非进程本地,保证可恢复,让多进程拓扑下行为保持一致、可预期。
案例 · Cases
| 代表性案例 |
|---|
| python my_script.py:独立脚本以进程方式在本地运行时直接启动,这使进程可以任意复制、迁移与水平扩展。 |
| django-assetpackager:用文件系统缓存编译后的资源,作为反例说明问题,是应用横向扩展与弹性伸缩的前提条件。 |
| Jammit、Rails asset pipeline:可配置为在 build 阶段打包资源,符合十二要素。 |
| Memcached、Redis:提供时间过期能力的会话/缓存 datastore,会话数据理想归宿。 |
反模式 · Anti-patterns
- sticky sessions——把会话状态绑到进程内存并依赖负载均衡路由黏性,违反无状态原则,这使进程可以任意复制、迁移与水平扩展。
- 把文件系统当编译资源缓存,推迟到运行期才编译,缓存失效即崩,是应用横向扩展与弹性伸缩的前提条件,保证任何进程随时被替换都不会丢失业务状态。
- 假设进程内存/磁盘缓存在未来请求中可用,多进程下必踩坑,运行期的弹性与健壮性因此得到根本保障,让多进程拓扑下行为保持一致、可预期。
- 进程内保存需要持久化的数据,重启即丢失,数据安全无法保证,避免进程本地状态成为扩展与迁移的绊脚石,持久数据的外置是十二要素无状态设计的核心。
相关边
实践资源 · 源码引用
| 路径 | 说明 |
|---|---|
| 08-source/app-content-en/processes.md | VI. Processes 原文:无状态/share-nothing/单事务缓存/sticky sessions 违规/会话进过期存储 |
| 08-source/app-content-en/backing-services.md | 持久数据存入 backing service 的交叉引用 |
| 08-source/app-content-en/build-release-run.md | 资源在 build 阶段编译的交叉引用 |
| 08-source/app-content-en/concurrency.md | 进程类型与零或多个运行实例化的交叉引用 |