Day34-22|15.9.2 服务粒度的控制以及无状态服务的设计

对应教材:第15章 面向服务架构设计理论与实践——15.9.2 服务粒度的控制以及无状态服务的设计

学习目标:掌握服务粒度的判断因素、过粗与过细的表现及调整方法;理解无状态服务的含义和适用范围;掌握状态外置、幂等、并发、会话、长流程和故障恢复设计;能够在案例题中给出有取舍的架构方案。

一、服务粒度为什么决定 SOA 的成败

服务粒度是服务所承担业务能力的范围和规模。粒度过粗,服务内部包含多个变化原因,难以复用和独立演进;粒度过细,调用链过长,网络、监控、部署和一致性成本迅速上升。合理粒度不是固定的“几个类”或“几个接口”,而是由业务内聚性、数据一致性、变化频率、团队边界、负载特征和质量目标共同决定。

服务拆分应回答:哪些业务规则必须共同执行?哪些数据需要同一事务?哪些功能由同一团队负责?哪些功能经常一起变化?哪些能力需要独立扩展或隔离?如果把这些问题都留到实现阶段,系统往往会先按数据库表或组织部门拆分,后续再被调用链和共享数据绑住。

可以把粒度选择看成一个平衡:

业务内聚性、独立演进、独立扩展 ↑ │ 过粗服务 ─────── 合理粒度 ─────── 过细服务 │ 网络调用、一致性和治理成本 ↑

目标不是让服务尽可能小,而是在自治收益和分布式成本之间取得平衡。

二、过粗服务的表现与问题

过粗服务通常表现为一个“核心平台”承载客户、订单、支付、库存、营销和报表等多种能力;接口参数数量巨大,调用方通过开关控制不同流程;任何小改动都要整体测试和发布;不同团队通过共享模块和共享数据库修改内部数据。

过粗的主要问题是职责不清、变化相互影响、扩展无法针对性进行、故障隔离困难和复用质量低。一个服务虽然看起来部署简单,但内部耦合可能比多个服务更严重。案例题中若出现“所有业务集中在一个中心系统,任何变更都需要全量回归”,可以从边界、团队和数据职责角度分析其粒度问题。

调整方法不是直接把模块全部拆成网络服务,而是先进行模块化治理:按领域划分内部模块,减少跨模块直接访问,明确接口和数据所有权,再选择变化频繁、负载差异明显或需要独立可用性的模块逐步服务化。

三、过细服务的表现与问题

过细服务常见于把每张表、每个 CRUD 操作或每个技术组件都部署成独立服务。一次用户请求需要串联十几个服务,服务之间频繁同步调用;多个服务必须同时发布;一个业务规则分散在多个服务中;为了完成简单操作需要跨服务事务。

过细会产生网络延迟、连接和线程消耗、故障点增加、分布式事务复杂、版本管理困难和排障成本上升等问题。服务独立部署并不等于独立演进,如果它们总是一起修改和发布,拆分只增加了运维负担。

发现过细后,可以合并高度内聚的服务,减少同步跳转;把同一事务内的规则收回一个服务;使用聚合层或读模型减少客户端串联;将不需要实时完成的步骤改为事件;对稳定基础能力采用平台服务而不是重复拆分。

四、判断合理粒度的六个因素

1. 业务内聚

同一服务的功能是否围绕一个明确业务能力?订单创建、订单取消和订单状态查询通常有较强内聚;订单和广告投放的变化原因不同,不宜因为页面一起展示就放到一个服务。

2. 数据一致性

必须强一致维护的状态尽量放在同一服务内。若两个模块每次操作都需要同一事务,拆分后会引入复杂的分布式一致性。允许最终一致的协作更适合通过事件和补偿完成。

3. 变化频率

变化频繁的领域与稳定领域分开,可以减少发布影响。变化原因不同往往是边界的重要信号。

4. 负载和扩展

需要独立水平扩展的能力可以单独划分。例如商品搜索在促销期间压力远高于后台管理,独立服务可以按流量扩展。

5. 团队和责任

服务应由能够端到端负责的团队维护。若没有独立开发、测试和运维能力,不宜盲目增加服务数量。

6. 故障和安全边界

高风险、强隔离或不同安全等级的能力需要独立边界。例如支付凭据处理、后台管理和公共查询不应共享过大的权限和资源池。

五、什么是无状态服务

无状态服务是指服务实例不依赖本地内存中保存的会话或业务状态来处理后续请求。相同请求可以由任意健康实例处理,实例销毁或扩展不会导致业务状态丢失。无状态并不意味着系统没有状态,而是把必须长期保存的状态放到可共享、可持久化和可恢复的外部组件中。

无状态服务的典型结构是:

客户端 → 负载均衡 → 任意服务实例 ↓ 数据库/缓存/对象存储 ↓ 消息与状态存储

无状态带来水平扩展、故障转移、滚动发布和弹性伸缩的便利。但状态外置后,架构师必须重新设计一致性、并发、过期、备份、安全和访问性能,不能简单把状态丢进缓存。

六、会话状态的外置

传统应用常把登录会话、购物车和流程变量保存在单实例内存中,导致请求必须粘滞到同一实例。无状态设计可以使用短期令牌承载少量身份声明,将会话保存到集中式缓存或持久化存储;购物车可放入带过期时间的缓存,订单和支付状态必须放入可靠数据库。

外置会话要考虑令牌撤销、过期、并发更新和敏感信息保护。不能把完整个人资料和权限列表长期写入客户端令牌;权限变化后,旧令牌是否立即失效也需要明确。缓存不可用时,系统应选择降级、重新认证或只读,而不是默认把所有请求转到某个本地实例。

七、无状态服务中的幂等设计

网络重试、消息重复和客户端重复提交都会导致同一操作执行多次。无状态服务不能依赖本地变量记录“已经处理过”,而应使用业务幂等键、请求记录、唯一约束或状态机持久化处理结果。

例如创建订单时,服务以 idempotencyKey 和业务主体建立唯一记录;第一次请求保存处理中或成功结果,后续相同请求返回原结果;相同键但请求内容不同则拒绝。支付、退款、发货和积分等具有副作用的操作都应设计幂等策略。

幂等不等于所有请求都没有副作用,而是重复执行产生与执行一次相同的业务结果。对于追加消息、扣减库存等操作,需要通过唯一业务流水、版本号、条件更新和处理记录实现。

八、并发与状态一致性

状态外置后,多个实例可能同时更新同一业务对象。可采用乐观锁、版本号、条件更新、分布式协调或按业务键串行化处理。选择时要根据冲突概率、吞吐量和一致性要求判断。

订单状态可以使用状态机限制合法迁移,例如已支付不能回到待支付;库存扣减可以使用带条件的原子更新,避免超卖;缓存更新要明确先更新数据库还是先更新缓存,以及失败时如何修复。把状态放在外部存储并不会自动保证正确性,业务不变量仍需由服务负责。

九、长流程和工作流状态

跨多个服务的业务流程通常不能保持一个长连接或长事务。应把流程状态持久化,由流程协调器、状态机或事件处理器根据事件推进。每一步记录已完成、处理中、失败和待补偿状态,服务实例重启后可以恢复。

例如订单流程包含订单创建、库存预占、支付、履约和通知。流程服务记录当前状态和超时时间;库存失败触发取消;支付超时触发查询;通知失败进入重试。流程状态本身也应幂等,重复事件不能使状态倒退或重复执行副作用。

十、无状态服务的适用边界

在线查询、认证校验、API 网关、无状态计算和大多数业务接口适合无状态设计。需要本地高性能状态、设备驱动、实时连接或复杂内存计算的场景不一定完全无状态,可以采用状态分片、主从、专用状态服务或会话路由。

例如长连接推送服务需要维护连接状态,但可以通过连接网关与消息平台分离,业务状态仍由外部系统保存;实时计算可以保留窗口状态,但必须提供检查点和恢复机制。架构师应说明状态在哪里、谁拥有、如何复制、如何恢复,而不是简单贴“无状态”标签。

十一、案例:高并发秒杀服务

某平台秒杀服务原本把商品库存和用户资格保存在应用内存中,单实例能够快速响应,但扩容后各实例库存不一致,重启会丢失状态。改造后,资格和库存的权威状态放在受控存储中,入口使用限流和排队,服务实例保持无状态。

请求 → 网关限流 → 秒杀服务 → 原子库存扣减 ↓ ↓ 幂等记录 秒杀结果事件 ↓ 订单创建与通知

库存操作使用商品和活动版本控制,重复请求根据用户和活动幂等;订单创建异步处理,用户先获得受理结果;缓存用于热点读取,但最终库存以权威存储为准;服务实例可以水平扩展,发布和故障转移不影响活动状态。高峰结束后通过对账检查库存、订单和支付结果。

十二、考试答题模板

题目问“如何控制服务粒度”,可按业务内聚、数据事务、变化频率、负载扩展、团队责任、故障和安全边界展开,再说明过粗与过细的风险和调整方法。题目问“如何设计无状态服务”,应写状态识别、状态外置、共享存储、会话策略、幂等、并发控制、缓存、故障恢复和监控。

论文中可以写:我没有按数据库表机械拆分,而是根据业务内聚和一致性边界确定服务粒度;对查询和计算服务采用无状态实例,通过外部存储管理会话与业务状态;对写操作使用幂等键、唯一约束和状态机;对跨服务长流程持久化流程状态并通过事件和补偿推进,从而同时获得水平扩展能力和可恢复性。

十三、课后练习

练习1

一个请求需要同步调用十二个服务,如何判断是否粒度过细?

参考答案:分析调用链、延迟、失败点、是否必须实时、是否共同发布、是否共享事务和是否可以聚合;可通过合并内聚服务、建立聚合层、读模型或异步事件减少同步跳转。

练习2

为什么把会话放入本地内存会阻碍弹性扩展?

参考答案:请求依赖特定实例,扩容、故障转移和滚动发布时无法共享会话;应将会话放到集中式缓存或持久化存储,或使用可验证的短期令牌,并处理过期和撤销。

练习3

无状态服务如何处理重复支付请求?

参考答案:使用稳定业务流水号或幂等键持久化请求与结果,建立唯一约束;相同请求返回原结果,参数冲突则拒绝;外部渠道超时通过状态查询确认,并用对账和补偿处理极端差异。

十四、今日复盘

服务粒度的本质是边界决策,无状态设计的本质是状态责任和恢复决策。粒度不能过粗也不能过细;状态不能消失,只能被正确外置和治理。高级答题要把业务内聚、数据一致性、弹性扩展、幂等和故障恢复联系起来,形成完整的架构论证。