3. 模型服务架构设计:单体架构 vs 微服务架构、服务发现与负载均衡、无状态设计原则
模型服务架构是决定推理系统可扩展性、运维复杂度与资源效率的核心决策层。本章从单体与微服务的架构对比出发,深入剖析服务发现与负载均衡的工程实现,并强调无状态设计在模型服务中的关键作用。
3.1 单体架构 vs 微服务架构
在模型服务早期阶段,单体架构因其简单性被广泛采用;随着业务规模增长,微服务架构逐渐成为主流选择。下表从多个维度对比两种架构的差异:
| 对比维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署单元 | 单个进程/容器,包含预处理、推理、后处理 | 每个功能模块独立部署为独立服务 |
| 扩展粒度 | 整体扩缩容,资源浪费明显 | 按模块独立扩缩容,资源利用率高 |
| 故障隔离 | 单点故障导致整个服务不可用 | 故障被限制在单个服务内,不影响全局 |
| 开发效率 | 初期开发快,但随代码膨胀协作成本剧增 | 团队可并行开发,技术栈灵活 |
| 运维复杂度 | 低,只需管理一个服务 | 高,需要服务发现、负载均衡、链路追踪等基础设施 |
| 适用场景 | 原型验证、低并发、小团队 | 高并发、多模型、多版本、大规模生产环境 |
架构选型建议: 对于日均请求量低于 10 万、模型数量少于 3 个的初期项目,单体架构可快速上线;当请求量突破 50 万或需要同时服务多个模型版本时,应尽早迁移至微服务架构。
3.2 服务发现与负载均衡
在微服务架构中,服务实例的动态注册与请求分发是保障高可用的核心机制。服务发现负责维护可用实例列表,负载均衡则决定请求如何分配到具体实例。
常见的服务发现模式包括:
- 客户端发现: 客户端直接从注册中心获取实例列表,自行选择目标实例(如 Netflix Eureka + Ribbon)。
- 服务端发现: 客户端请求通过负载均衡器(如 Nginx、Kong),由负载均衡器查询注册中心并转发请求。
- Kubernetes 原生: 利用 Service 对象与 kube-proxy 实现 DNS 或 IPVS 方式的自动发现与轮询。
负载均衡策略在模型服务场景中的选择:
| 策略 | 原理 | 模型服务适用性 |
|---|---|---|
| 轮询(Round Robin) | 依次将请求分配给每个实例 | 适用于各实例硬件配置一致的场景 |
| 最少连接(Least Connections) | 优先分配给当前活跃请求最少的实例 | 适用于推理时间波动较大的模型 |
| 一致性哈希(Consistent Hashing) | 根据请求特征(如模型 ID)映射到固定实例 | 适用于模型分片或需要本地缓存加速的场景 |
| 加权分配(Weighted) | 根据实例的 GPU 显存、CPU 核数等设置权重 | 适用于异构硬件集群 |
工程实践: 在 Kubernetes 环境中,推荐使用
Service + Ingress 实现服务端发现,配合 HorizontalPodAutoscaler 基于 GPU 利用率或请求 QPS 自动扩缩容。对于延迟敏感场景,可引入 gRPC 长连接与 Weighted Least Connections 策略。
3.3 无状态设计原则
无状态设计是模型服务能够水平扩展的前提。所谓无状态,是指服务实例不存储任何与当前请求相关的会话数据,所有请求所需的上下文信息均由请求本身携带或从外部存储获取。
在模型服务中,无状态设计的具体体现:
- 模型权重只读加载: 模型参数在服务启动时从共享存储(如 S3、NFS)加载到内存/显存,推理过程中不修改权重。
- 请求上下文外置: 用户会话信息、历史对话记录存储在 Redis 或数据库,请求时通过 token 或 session ID 获取。
- 本地缓存可选但非必需: 即使使用本地缓存(如特征哈希表),缓存丢失也不影响正确性,仅影响性能。
- 日志与监控异步输出: 推理日志通过消息队列发送到集中式日志系统,不依赖本地磁盘。
常见反模式: 将用户特征或模型中间结果存储在服务实例的本地内存中,且未做持久化。当实例被重新调度或扩缩容时,这些数据丢失会导致请求失败或结果不一致。正确的做法是将状态数据外移到 Redis、Memcached 或数据库。
3.4 架构设计决策清单
在实际项目中,架构选型需结合团队规模、请求量级、模型复杂度等因素综合判断。以下决策清单可作为参考:
- 评估当前与未来 6 个月的请求 QPS: QPS < 500 可考虑单体架构;QPS > 2000 建议微服务架构。
- 确定模型数量与版本管理需求: 多模型或多版本并行服务时,微服务架构更便于独立发布与回滚。
- 选择服务发现方案: Kubernetes 环境优先使用 Service + DNS;非 K8s 环境推荐 Consul 或 etcd + 客户端发现。
- 配置负载均衡策略: 通用场景使用轮询;推理时间方差大时使用最少连接;需要缓存亲和性时使用一致性哈希。
- 审查服务状态依赖: 确保所有会话数据、模型中间结果均外置,服务实例可随时销毁与重建。
推荐阅读: 本章后续内容将深入探讨模型服务的 弹性伸缩策略(第 4 章)与 服务网格集成(第 5 章),帮助你在微服务架构中实现更精细的流量管理与可观测性。