从上线前准备到上线后监控,系统化掌握服务发布的完整生命周期
服务发布不是"部署代码"这一个动作,而是一个完整的生命周期。理解生命周期,才能体系化地选择和使用发布技术。
| 阶段 | 核心问题 | 不处理的后果 | 对应技术 |
|---|---|---|---|
| 发布前 | 代码是否稳定?配置是否正确? | 带着Bug上线,直接故障 | CI/CD、自动化测试、代码审查 |
| 上线时 | 如何让用户无感知?如何快速回滚? | 服务中断、用户投诉 | 蓝绿发布、金丝雀、滚动发布 |
| 上线中 | 新旧版本共存时流量怎么分配? | 数据不一致、请求错乱 | 流量镜像、Header路由、权重分配 |
| 上线后 | 如何发现异常?如何自动保护? | 故障扩大、雪崩效应 | 监控告警、熔断、限流、降级 |
无论选择哪种发布策略,"平滑上线"和"优雅下线"都是必须解决的基础问题。如果实例上下线时流量处理不当,再好的发布策略也会出问题。
新实例启动后,不能立即接收全量流量。JVM需要JIT编译、连接池需要建立、缓存需要加载。直接打满流量会导致响应延迟飙升甚至OOM。
负载均衡器/注册中心通过健康检查判断实例是否可用,只有健康后才放入流量池。
新实例启动后,负载均衡器逐渐增加分配给它的权重,让系统慢慢"热起来"。
数据库连接池、Redis连接池启动时提前建立连接,避免首次请求时创建连接的延迟。
Java应用启动后JIT编译器需要时间将热点代码编译为本地代码,预热期性能较差。
实例停止前,必须先停止接收新请求,处理完已有请求后再关闭。直接Kill会导致正在处理的请求失败。
先告诉注册中心"我要下线了",让负载均衡器不再把新请求发过来。
使用计数器跟踪活跃请求,确保所有请求处理完毕后再关闭。
1. Pod 被标记为 Terminating → 2. 执行 preStop Hook → 3. 发送 SIGTERM → 4. 等待 terminationGracePeriodSeconds → 5. 发送 SIGKILL
| 检查项 | 上线时 | 下线时 |
|---|---|---|
| 健康检查 | Readiness 通过后再接收流量 | 先设置 Readiness 为 false |
| 注册中心 | 注册后等待心跳确认 | 注销后等待 LB 刷新(通常 5-10s) |
| 连接池 | 提前预热连接 | 等待连接归还后关闭 |
| 活跃请求 | — | 等待所有请求处理完成 |
| 消息队列 | — | 停止消费,处理完已拉取的消息 |
发布策略解决的核心问题是:"如何将新版本安全地交付给用户"。不同策略在资源成本、风险控制、回滚速度之间做不同权衡。
一次性将所有流量切换到新版本。最简单,但风险最高。
个人项目、内部测试环境、低风险微小程序、非核心服务
维护两套完全相同的生产环境,通过负载均衡器瞬间切换流量。新版本在"暗处"验证,出问题秒级回滚。
在蓝区部署新版本 v2.0,进行内部测试和冒烟验证
通过负载均衡器将流量从绿区瞬间切换到蓝区(DNS/配置中心修改)
观察错误率、延迟、业务指标,确认新版本稳定运行
稳定则绿区保留待下次升级;出问题则瞬间切回绿区
蓝绿发布最大的挑战是数据库变更。解决方案:先部署数据库变更(向后兼容)→ 蓝绿切换 → 一段时间后清理旧字段。
将新版本先暴露给少量用户(如 5%),验证稳定后逐步扩大比例。像矿工用金丝雀探测毒气一样,用小流量探测风险。
部署新版本,仅对 5% 用户可见(内部员工/白名单/随机)
观察 5-30 分钟:错误率、P99 延迟、业务转化率
指标正常则扩大:5% → 20% → 50% → 100%
任何阶段发现异常,立即停止扩大,回退到上一比例或 0%
| 方式 | 原理 | 适用场景 | 优点 |
|---|---|---|---|
| 随机分流 | 按百分比随机分配 | 通用场景 | 简单直接 |
| Cookie 分流 | 基于用户 Cookie hash | 保证同一用户始终访问同一版本 | 体验一致 |
| Header 分流 | 基于请求头(X-User-ID) | 定向灰度特定用户 | 可追溯 |
| 权重分流 | 网关/Service Mesh 权重配置 | K8s/Istio 环境 | 动态调整 |
逐步替换旧版本实例,每次只更新一部分,保持服务持续可用。Kubernetes Deployment 的默认策略。
| 参数 | 说明 | 推荐值 |
|---|---|---|
| maxSurge | 更新过程中可超出的 Pod 数量/比例 | 1 或 25% |
| maxUnavailable | 更新过程中最大不可用的 Pod 数量/比例 | 0 或 25% |
同时运行两个版本,根据用户行为数据(转化率、点击率等)决定哪个版本更好。核心目标是"验证效果"而非"验证稳定性"。
| 维度 | 金丝雀发布 | A/B Testing |
|---|---|---|
| 核心目标 | 验证新版本是否稳定 | 验证哪个方案效果更好 |
| 决策依据 | 错误率、P99 延迟、CPU | 转化率、留存率、GMV |
| 持续时间 | 短则几小时,长则1天 | 通常数天到数周 |
| 流量分配 | 渐进式扩大(5%→100%) | 固定比例长期运行 |
| 结束条件 | 全量发布或回滚 | 统计显著性达标后选择优胜 |
通过配置开关控制功能开启/关闭,无需重新部署即可切换功能状态。是发布策略的"遥控器"。
| 开关类型 | 用途 | 示例 |
|---|---|---|
| 发布开关 | 控制功能对外可见 | 新功能灰度 |
| 实验开关 | A/B 测试分组 | 对比不同 UI 方案 |
| 运维开关 | 紧急降级/熔断 | 大促时关闭非核心功能 |
| 授权开关 | 灰度特定用户 | VIP 用户优先体验新功能 |
特性开关是发布策略的"最后一公里"。代码部署后,通过开关控制功能是否生效,实现"部署"和"发布"解耦。
发布过程中和发布后,需要一系列流量控制技术来保护系统稳定性。这些技术构成系统的"自我保护"能力。
将生产流量同时复制一份发送到新版本,但只使用旧版本的响应。新版本只"看"不"回",零风险验证。
新版本需要验证但不想影响用户;数据库查询类服务验证;读多写少的场景。注意:写操作镜像需要处理幂等性。
控制单位时间内的请求量,防止系统被突发流量打垮。
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 令牌桶 | 以固定速率放入令牌,请求消耗令牌 | 允许突发流量,平滑 | 实现稍复杂 |
| 漏桶 | 请求进入桶,以固定速率流出 | 流量绝对平滑 | 无法应对突发 |
| 滑动窗口 | 记录每个请求的时间戳,统计窗口内数量 | 精确,无边界问题 | 内存消耗大 |
当下游服务故障率过高时,自动切断调用,防止故障蔓延。保护调用方不被拖垮。
| 状态 | 行为 | 触发条件 |
|---|---|---|
| Closed | 正常调用,统计失败率 | 初始状态 / Half-Open 试探成功 |
| Open | 直接拒绝,快速失败 | 失败率超过阈值(如 50%) |
| Half-Open | 放行少量请求试探 | Open 状态持续超时后 |
调用失败时自动重试,提高成功率。但必须配合幂等性和熔断器使用。
当系统压力过大或部分服务不可用时,主动降低功能质量,保证核心业务可用。
| 降级策略 | 说明 | 示例 |
|---|---|---|
| 页面降级 | 展示缓存/静态页面 | 商品详情页静态化 |
| 功能降级 | 关闭非核心功能 | 关闭评论、推荐、排行榜 |
| 数据降级 | 返回默认/兜底数据 | 返回热门商品列表 |
| 缓存降级 | 使用本地缓存替代远程 | 读取本地缓存配置 |
| 同步转异步 | 非关键操作改为异步 | 发送通知改为MQ |
不同场景选择不同策略。以下从多个维度对比,并提供决策方法。
| 策略 | 资源成本 | 风险等级 | 回滚速度 | 复杂度 | 最佳场景 |
|---|---|---|---|---|---|
| 直接发布 | 无额外 | 极高 | 慢(重新部署) | 低 | 内部/测试/低风险 |
| 蓝绿发布 | 100% | 低 | 秒级 | 中 | 关键业务/支付 |
| 金丝雀 | 20-50% | 中 | 分钟级 | 高 | 高风险变更/大版本 |
| 滚动发布 | 10-30% | 中 | 分钟级 | 中 | 常规迭代/K8s |
| A/B Testing | 50% | 中 | 分钟级 | 高 | 产品优化/效果验证 |
| 特性开关 | 无额外 | 低 | 即时 | 中 | 快速迭代/功能灰度 |
| 指标类型 | 关注指标 | 告警阈值 | 说明 |
|---|---|---|---|
| 可用性 | 错误率、5xx 率 | 错误率 > 1% | 最直接的质量指标 |
| 性能 | P99 延迟、QPS | P99 > 500ms | 用户体验核心指标 |
| 资源 | CPU、内存、连接数 | CPU > 80% | 容量规划依据 |
| 业务 | 转化率、下单率 | 下降 > 10% | 业务影响指标 |
| 发布 | 金丝雀比例、实例状态 | 异常实例 > 0 | 发布过程指标 |
越靠近生产环境,发布策略越保守。生产环境必须至少使用金丝雀或蓝绿发布,严禁直接发布。