Day31-15|10.6.4 大型网站系统架构演化实例(四):服务集群与并发处理能力
Day31-15|10.6.4 大型网站系统架构演化实例(四):服务集群与并发处理能力
一、教材定位
当大型网站完成垂直拆分和缓存优化后,单个应用实例的处理能力仍可能成为瓶颈。通过部署多个服务实例组成集群,可以利用多台机器共同处理请求,提高吞吐量、可用性和故障承受能力。服务集群是网站架构演化的重要阶段,但“增加几台服务器”并不自动等于集群架构成功。
集群化要求系统能够进行请求分发、健康检查、故障摘除、资源隔离和状态共享或外置。应用实例如果依赖本地会话、临时文件和内存状态,就难以自由扩展和切换;数据库、缓存、消息和配置中心如果仍是单点,应用集群的收益也会受到限制。考试答题应从无状态化、负载均衡、会话、数据、故障转移和并发控制等方面完整分析。
二、服务集群的基本结构
典型结构包括客户端、DNS或入口、负载均衡器、多个应用实例、缓存集群、数据库主从或集群、消息系统和监控平台。入口将请求按照策略分发到健康实例,实例尽量保持无状态,通过外部存储或令牌共享必要状态。
集群可以是同构集群,也可以是按功能、区域或版本划分的异构集群。同构集群简化扩缩容和故障转移;异构集群适合灰度、专用资源和不同质量要求,但路由和运维更复杂。
三、集群化解决的问题
1. 提升并发处理能力
请求可以分散到多个实例,突破单机 CPU、线程、连接和网络带宽限制。扩容可以按实例数进行,适合流量逐步增长和短期峰值。
2. 提高可用性
单个实例故障时,负载均衡器可以摘除故障实例,把新请求转发到健康实例。配合多可用区和故障域,可以降低单点故障影响。
3. 支持灰度和滚动发布
新旧版本可以在集群中并存,按流量、用户或区域逐步切换。出现问题时可快速减少新版本流量或回滚。
4. 提供弹性
根据请求量、延迟、队列和业务指标动态增加或减少实例。集群是弹性的基础,但弹性还需要自动化部署、配置、健康检查和容量策略。
四、无状态化设计
1. 为什么要无状态
无状态实例不依赖本地保存的用户会话、任务进度和业务状态,任何健康实例都可以处理请求。这样能够自由扩容、故障转移和滚动发布,降低节点亲和性。
2. 会话外置
会话可以保存在分布式缓存、数据库或由客户端携带签名令牌。外置缓存需要考虑过期、容量、故障和权限;令牌要考虑大小、撤销、密钥轮换和敏感信息。不能把完整权限和隐私数据无保护地放入客户端。
3. 本地状态处理
本地缓存、临时文件和计算中间结果可以存在实例内,但必须允许丢失或重建。需要持久化的状态应写入权威存储,临时文件应使用共享对象存储或可重试的上传机制。
4. 粘性会话
粘性会话让同一用户持续访问同一实例,改造成本较低,但会造成负载不均、故障迁移困难和扩缩容复杂。它可以作为过渡方案,不能掩盖本地状态设计问题。
五、负载均衡
1. 常见策略
轮询适合实例能力相近的无状态服务;加权轮询适合不同规格实例;最少连接适合请求耗时差异较大的场景;基于响应时间可以更快避开慢实例;按一致性哈希适合缓存和会话等需要稳定路由的场景。
策略应结合业务和实例特征,不能只追求分配均匀。长请求、批任务和大文件操作可能导致实例连接数差异,单一策略不一定有效。
2. 健康检查
健康检查应区分存活、就绪和业务健康。进程存活不代表能够访问数据库、缓存和关键依赖;如果把所有依赖故障都定义为实例不健康,可能导致集群同时摘除。检查要设置超时、失败阈值、恢复阈值和抖动保护。
3. 故障摘除与恢复
故障实例应及时停止接收新请求,但正在处理的请求需要优雅退出、超时和重试。实例恢复后要经过预热和小流量观察,不能启动即承受全部流量。回流时要避免缓存击穿和连接风暴。
六、并发控制与资源隔离
1. 线程和连接
每个实例要限制线程池、连接池、队列和并发请求,避免无限并发耗尽资源。不同业务可使用隔离线程池或舱壁,核心请求与非核心请求分开,防止后台任务拖垮交易。
2. 限流和背压
入口限流、服务限流和依赖限流要配合使用。队列和背压可以把突发流量转化为可处理的积压,但要设置最大长度、过期和拒绝策略。对实时业务应快速失败,对可异步业务可排队处理。
3. 并发安全
多实例会放大并发写入,需要乐观锁、唯一约束、分布式锁、幂等键和状态机保护。不能依赖单机内存锁保证全局一致。锁的范围和超时要合理,避免锁竞争和死锁。
七、集群与数据层
应用集群不能脱离数据层。数据库可能需要读写分离、主备、分片、连接池和故障切换;缓存需要集群、副本和热点保护;消息系统需要分区、消费者组、积压和重平衡。
数据层扩展会改变事务、一致性和路由。应用要明确读写源、复制延迟、失败重试和数据对账。不能因为应用已经集群化,就默认数据库也具备相同的高可用能力。
八、集群发布和版本兼容
滚动发布时,新旧实例会同时处理请求。接口、消息、缓存键和数据库结构必须兼容。数据库变更通常采用先增加兼容结构,再发布新代码,完成迁移后再删除旧结构。
灰度流量要能按实例、版本、租户、区域或用户路由,并设置停止条件。回滚时要考虑已经写入的新字段、发送的消息和缓存数据,必要时执行补偿和失效。
九、案例分析:高峰网站服务集群化
某网站节假日流量增长,单实例应用出现线程池耗尽和响应超时。团队增加了多台实例,但故障仍频繁发生:登录会话保存在本地,部分用户被反复要求登录;负载均衡只按轮询,慢实例仍持续接收请求;健康检查只访问端口,数据库不可用时仍被认为健康;报表任务与核心请求共享线程池。
改造首先外置会话并使用签名令牌或共享会话存储,清理本地临时状态;负载均衡增加就绪检查、失败摘除和预热,按请求耗时采用加权或最少连接策略;核心数据库和缓存依赖加入分层健康检查,依赖故障时采用降级而不是摘除全部实例;报表与后台任务使用独立队列和资源池,设置限流和背压;数据库连接、线程池和请求并发设置上限,关键写操作使用幂等和版本控制。
测试覆盖实例宕机、网络分区、数据库只读、缓存故障、滚动发布、突发流量和恢复回流。指标包括吞吐、P99、实例分布、故障摘除时间、登录成功率、核心业务成功率、队列积压和数据差异。只有应用、数据、入口和运维都具备相应能力,集群化才真正改善并发和可用性。
十、综合知识练习
题1
为什么服务集群通常要求应用尽量无状态?
参考答案:无状态实例可以自由扩容、缩容、故障转移和滚动发布,不需要把请求固定到某个节点。必要状态外置后,负载均衡更灵活,单实例故障不会丢失用户会话和业务进度。
题2
端口健康检查为什么不能代表服务健康?
参考答案:端口开放只说明进程可连接,不能说明线程池、数据库、缓存、消息和核心业务依赖正常。健康检查应区分存活、就绪和业务健康,并控制依赖故障导致的连锁摘除。
题3
集群化后如何避免后台任务拖垮核心请求?
参考答案:使用独立线程池、队列、实例或资源配额,设置限流、背压和最大并发;对非核心任务采用异步、降级和暂停策略,核心链路使用舱壁隔离并监控资源。
十一、论文写作素材
论文中可以这样描述集群演化:
“针对单实例并发能力不足和故障影响范围过大的问题,我将应用改造成可横向扩展的服务集群。通过外置会话和持久化状态实现无状态化,采用负载均衡、存活与就绪检查、故障摘除、预热和优雅下线分配请求;通过线程池、连接池、队列、限流和舱壁隔离保护核心资源。数据库、缓存和消息层同步建立副本、故障切换、积压和对账机制,滚动发布期间保证接口、消息和数据库结构兼容。通过吞吐、P99、摘除时间、核心成功率、积压和数据一致性指标验证集群效果。”
十二、今日复盘
- 集群化通过多个实例提高并发、可用性和弹性,但需要配套治理。
- 无状态化、外置会话、负载均衡和健康检查是服务集群基础。
- 线程池、连接池、限流、背压和舱壁用于控制并发和故障传播。
- 应用集群不能替代数据库、缓存和消息层的高可用设计。
- 滚动发布要处理新旧版本、数据结构、消息和缓存兼容。
- 案例答题要写清入口、实例、状态、数据、故障、资源和度量闭环。