从资源约束出发做架构决定
独立开发时,技术方案的成本不仅是云服务账单,还包括部署、排查问题、处理用户反馈和恢复数据需要的时间。本文保留我从本地 Docker 开发到云端部署的思考,重点讨论每个阶段应该优先解决什么问题。
适用范围:需要 API、数据库和文件存储的早期产品。下面是选型与演进参考,不是性能基准,也不代表所有产品都需要微服务。
本地环境先保证可重复
我在项目初期采用本地 Docker 部署服务,便于调试和修改组件。这降低了早期迭代成本,但本地能运行,只验证了开发环境中的一部分条件。
准备上线时,还需要明确配置如何注入、数据保存在哪里、进程重启后能否恢复,以及数据库结构如何随代码升级。将这些问题记录下来,比先增加部署组件更有价值。
MVP 阶段的服务组合
原有实践中使用 Cloudflare R2 存储文件、Render 托管服务端和数据库,并通过 Cloudflare 管理 DNS。这样的组合减少了早期维护基础设施的工作。
需要区分“有免费额度”和“可以无限期承载生产数据”。截至本文修订时,Render 的免费 PostgreSQL 实例存在到期限制;R2 的免费额度也有适用条件。应在选型时核对 Render 免费服务限制 和 R2 官方说明,不把免费套餐作为数据持久性的保证。
图中 DNS 表示域名解析关系,不表示数据库或对象存储请求必须经过 DNS 服务转发。
什么时候值得增加复杂度
| 观察到的问题 | 先尝试的改进 | 再考虑的演进 |
|---|---|---|
| 发布手工步骤容易出错 | 自动化构建、部署与回滚 | 增加环境隔离及发布审批 |
| 长任务拖慢 API 响应 | 测量耗时,明确同步与异步边界 | 将任务移到独立工作进程 |
| 数据重要性提高 | 备份、权限和恢复演练 | 托管数据库与更完善的可用性配置 |
| 单实例容量成为瓶颈 | 找到具体资源瓶颈 | 无状态 API 与多实例部署 |
| 不同业务独立演进 | 先整理代码与数据边界 | 有证据支持时再拆分服务 |
“用户增长”本身不足以决定采用哪种架构。应说明是数据库连接、计算任务、部署频率,还是维护职责产生了压力,再针对它采取措施。
逐步拆分,而不是一次迁移
原文涉及的演进方向包括数据层独立、无状态 API、自动化部署,以及将视频处理、文本分析等任务拆成独立服务。它们可以分阶段实施,并不要求同时完成。
拆分前先检查边界:输入输出是否清楚、失败如何反馈、任务重试是否产生重复结果、部署是否真的能独立进行。若两个服务需要频繁同步修改或共享内部数据结构,物理拆开未必减少维护成本。
无状态 API 指的是请求处理不依赖某一实例独有的会话状态,并不意味着整个系统没有状态。业务数据、任务进度和文件都需要明确的持久化位置。
数据库升级与恢复能力
我使用 PostgreSQL 与 Flyway 管理数据库及迁移。对独立开发者来说,数据库变更至少需要三层验证:
- 迁移可追溯:版本化迁移进入代码管理,明确每个版本改变了什么。
- 旧数据可升级:在接近真实结构的数据副本上检查迁移,验证新旧应用之间的兼容窗口。
- 数据可恢复:有备份,并实际验证恢复过程;代码回滚不等于数据库已经回滚。
Flyway 使用校验信息识别已经执行的迁移是否发生变化,因此不要为了让校验通过而随意修改已应用脚本或执行修复。先查明差异,再决定新增迁移还是纠正环境。参见 Flyway Validate。
日志应该帮助定位问题
日志需要能关联一次请求或任务的执行路径,例如请求标识、阶段、耗时、结果和必要的错误信息。记录所有数据库操作或完整请求体,可能增加成本并暴露不必要的数据,应根据排查需求选择字段。
初期先关注错误率、延迟、任务失败和数据库连接等直接影响使用体验的信号。告警应指向可以采取的行动,避免增加一个只有通知、没有处理路径的系统。
上线前的最小检查
- 重启应用后,用户数据和文件是否仍然存在?
- 数据库升级失败时,能否识别停在哪一步?
- 外部服务超时后,客户端得到的反馈是否明确?
- 最近一次备份是否可以恢复,恢复需要多长时间?
- 新版本有问题时,是否知道应用和数据分别如何处理?
先把这些日常操作做可靠,再根据真实瓶颈演进架构。对独立开发而言,能解释、能维护、能恢复的方案,往往更适合持续交付。