微服务框架理解

前段时间,年级群里发了一份实习介绍,提到优先考虑了解微服务架构的同学。平时也经常听到“项目要上微服务”,所以我想先弄清楚:把一个应用拆成多个服务,到底解决了什么问题,又会多出哪些事情?

一、微服务的介绍及由来

单体应用通常把多个业务模块放在同一个部署单元里。比如用户、商品、订单和支付都在一个后端程序中,模块之间通过进程内调用协作,发布时一起构建、一起部署。

微服务则按业务能力划分服务,每个服务负责自己的职责,通过 HTTP、RPC 或消息队列协作,并尽量做到独立部署和演进。关键在于业务边界和交付边界是否清楚,而不只是进程数量变多。

微服务的由来

单体结构在项目较小时很直接:调用方便,本地调试和事务处理也容易。但当业务和团队变大,不同模块的发布节奏、资源需求可能出现差异。只改订单模块,也需要发布整个应用;某个模块流量上涨,也往往要连同其他模块一起扩容。

拆分服务给了团队分别发布和扩容的机会。不过,原本一次函数调用就能完成的协作,现在要经过网络;原本可以放在一个数据库事务里的修改,也可能跨越多个服务。这些成本会伴随拆分一起出现。

二、微服务与单体架构的对比

两种结构都可以做好模块划分,也都需要处理测试、部署和故障。区别主要在边界落在哪里:单体的模块共享一个运行与部署环境,微服务把一部分边界移到了网络和独立部署单元之间。

比较项 单体应用 微服务
部署 多个模块随应用一起发布 服务可以分别发布,但要维持接口兼容
扩容 通常扩容整个应用 可以针对热点服务扩容
调用 进程内调用,链路较短 网络调用,需要处理超时和失败
数据 单库事务较容易组织 数据边界分散后,要处理跨服务一致性
测试 单进程联调较直接 除服务内测试,还要验证接口和端到端链路
运维 运行单元少,部署链路较集中 需要管理更多实例、配置、日志和调用关系

单体也能通过多个实例水平扩展。微服务的扩容优势在于可以只扩某个服务,而不是单体完全无法扩容。代码是否容易维护,同样取决于模块边界和依赖关系,拆成多个仓库并不会自动降低耦合。

三、微服务架构的收益与代价

独立发布和按需扩容

如果用户服务与订单服务的边界清楚,两边可以按各自的节奏开发和发布。订单流量增加时,也可以优先给订单服务增加资源。团队不必为了每一次局部改动都协调整个应用的发布。

服务之间还可以使用不同技术栈,例如一个服务使用 Java,另一个使用 Go。不过,技术选择越多,团队需要维护的构建、监控和部署方式也越多。

故障隔离需要具体设计

拆开进程能够缩小部分故障的影响范围,但调用依赖仍然存在。支付服务不可用时,用户浏览商品可能继续正常,依赖支付结果的订单流程却仍会受影响。

要让故障影响可控,还需要超时、限流、熔断、降级等处理。Martin Fowler 和 James Lewis 在 Microservices 中把“为失败而设计”单独列为一项特征:服务调用随时可能失败,调用方需要处理这个结果。

数据一致性与运维成本

服务分别管理数据后,一笔业务可能涉及多个存储。例如创建订单和扣库存分属不同服务,就需要考虑其中一步失败、消息重复或处理延迟时如何恢复一致性。

与此同时,请求会穿过更多进程。排查问题时,需要知道请求经过哪些服务、在哪一步变慢或失败;部署时则要处理接口兼容、服务发现、配置和监控。自动化测试与 CI/CD 能减少重复操作,但这些能力本身也需要建设和维护。

四、什么时候值得拆分

判断是否适合微服务,可以先看项目有没有明确的拆分理由:

  • 某些业务模块需要独立发布,经常被整个应用的发布节奏拖住。
  • 不同模块的流量和资源需求差异明显,整体扩容浪费较大。
  • 多个团队已经围绕不同业务能力工作,需要更清楚的交付边界。
  • 某个业务确实需要不同的技术栈,并且团队能承担额外维护成本。

“业务复杂”“并发高”或“要求高可用”本身,还不足以决定拆分。需要继续说明:准备拆哪一块,拆完能解决什么,服务之间如何调用,失败以后怎样处理。

如果团队规模不大,业务边界还经常变化,先把单体内部的模块职责整理清楚,通常更容易验证想法。等到独立发布或扩容的需求真正出现,再沿着已有边界拆分,理由也会更充分。

五、参考资料

下面几篇是我整理这份笔记时看的资料,可以结合其中的案例继续理解服务拆分、组件选型和运维成本:

延伸阅读


微服务框架理解
https://blog.phlin.cn/2024/10/03/microservices/
作者
phlin
发布于
2024年10月3日
许可协议