最近在帮一个创业团队做技术方案评审遇到了一个很典型的问题线上订单量涨起来之后单体应用越来越难发布每次发版都要十几个人同时守着改一个积分模块却要回归所有业务。数据库连接池被打满、应用启动越来越慢、需求排期越来越长团队开始认真讨论一个问题我们是不是该上微服务了这个问题非常普遍。很多团队在“要不要微服务”之间反复犹豫本质上是因为没有把“为什么需要微服务”想清楚。如果只为了追新架构而拆分拆分之后会发现分布式事务、链路追踪、运维复杂度等问题接踵而至反而得不偿失。这篇文章从单体架构的痛点出发拆解微服务真正要解决的问题并配合订单系统从单体到微服务的拆分案例、代码示例和常见误区帮你判断自己的项目到底需不需要微服务以及如果要落地应该从哪里入手。1. 从单体应用说起1.1 单体应用到底长什么样单体应用是指一个业务系统的所有功能模块都部署在同一个进程里使用同一套代码仓库和同一个数据库。用户管理、商品管理、订单管理、支付逻辑、消息通知全部打在一个 Jar 包或 War 包里。先看一个典型的单体订单模块代码// 单体订单模块OrderController.java RestController RequestMapping(/order) public class OrderController { /** * 下单接口 * 在单体应用中一个接口内部会直接编排多个模块的 Service */ PostMapping(/create) public Result createOrder(RequestBody OrderCreateRequest request) { // 第一步校验用户状态 User user userService.getById(request.getUserId()); if (user null || user.getStatus() ! 1) { return Result.fail(用户不存在或已禁用); } // 第二步扣减库存 Stock stock stockService.deduct(request.getSkuId(), request.getQuantity()); if (stock null) { return Result.fail(库存不足); } // 第三步创建订单 Order order orderService.create(user, stock, request); return Result.success(order); } }在单体应用里这段逻辑看起来非常自然。用户服务、库存服务、订单服务都只是同一个项目下的不同包方法之间直接调用不需要走网络不需要考虑超时也不需要处理各种远程异常。很多中小型项目甚至大型项目的前期阶段都是这样开发的。它简单、直接、易调试团队可以快速把业务跑通。1.2 单体应用的优势很多人一提起单体应用就想到“臃肿”“难维护”但这是不客观的。单体应用在很长一段时间里都是最正确的选择原因也很明确开发调试简单。本地启动一个服务打断点直接就能看到所有逻辑的调用过程。事务天然支持。一个下单操作涉及用户、库存、订单多个表的修改使用本地事务就可以保证强一致性不需要处理分布式事务。部署交付简单。打包一个 Jar 包或 War 包上传服务器就能运行运维成本极低。资源占用小。一个进程可以支撑很大的并发量不需要为了调用链路上多个服务付出额外的内存和网络开销。在实际业务中如果项目规模不大、团队人数不多、业务边界不清晰单体应用反而能更快地把业务做出来。1.3 单体应用在什么时候开始“疼”随着业务增长单体应用的痛点会逐渐显现而且通常不是某一个点疼而是多个点一起爆发。第一个信号是发布变慢。一个项目有几十个模块每次发版都要把所有模块一起回归。可能只是修改了一个短信通知的文案却要执行完整的全量回归用例发布窗口越来越长线上问题修复也要等排期业务响应速度变慢。第二个信号是资源无法隔离。某个模块出现内存泄漏或者死循环会把整个应用拖垮。在单体架构下故障范围是整个系统任何一个模块的异常都可能影响所有用户。第三个信号是团队协作成本上升。多个团队维护同一套代码仓库时代码冲突频繁需求相互等待。订单团队要上线用户团队也要上线最后只能大家约定同一个窗口互相妥协。第四个信号是局部扩展困难。双十一大促期间需要扩容的是订单和支付这类核心模块但单体应用无法只扩容某一个模块只能整应用一起扩容非常浪费资源。当这些信号出现时团队就会开始思考是不是应该换一种架构方式了2. 微服务是什么解决了什么2.1 一句话理解微服务微服务架构就是把一个大型业务系统按照业务边界拆分成多个独立的小服务每个服务独立开发、独立部署、独立扩展服务之间通过轻量级的通信机制协作。比如一个电商系统可以拆分成用户服务、商品服务、订单服务、支付服务、库存服务、消息服务等。每个服务都拥有独立的数据库表甚至独立的数据库实例服务之间通过 HTTP 接口或者消息队列通信。和单体应用相比微服务更像是把一个“全能型团队”拆成了多个“专项小组”。每个小组只负责自己的服务边界清晰职责单一。2.2 微服务的核心特征微服务架构并不是简单地把项目拆成多个子模块它有几个核心特征独立部署。每个微服务都是一个独立的部署单元修改订单服务只需要重新构建和发布订单服务不需要重新部署整个系统。独立扩展。大促期间订单服务压力大可以单独为订单服务增加实例而不必扩展用户服务和商品服务。故障隔离。一个服务出现故障时可以通过限流、熔断、降级等手段避免故障扩散到整个系统。技术异构。不同服务可以根据自身需求选择合适的技术栈。比如订单服务用 Java数据分析服务用 Python彼此互不影响。数据独立。每个服务拥有自己的数据存储服务之间不能直接访问对方的数据库只能通过接口访问。2.3 微服务与 SOA 的边界很多读者会问微服务和 SOA面向服务架构看起来很像区别在哪里SOA 出现的更早它强调企业级应用的集成核心是 ESB企业服务总线。所有服务都接入 ESB由 ESB 完成协议转换、消息路由、数据映射等操作。ESB 本身是一个重量级组件配置复杂运维成本高。微服务则更加轻量通常不使用 ESB服务之间直接通过 REST 接口、gRPC 或消息队列通信。微服务更强调去中心化治理每个服务都有自己的治理能力而不是依赖一个中心化的总线。简单理解SOA 是“服务通过总线互连”微服务是“服务直接互连治理能力下沉到每个服务”。3. 单体与微服务的核心对比为了更直观地看出两种架构的差异这里做一个横向对比维度单体架构微服务架构代码仓库一个仓库包含全部模块每个服务独立仓库部署方式一个应用包整体发布每个服务独立构建发布扩展方式整体水平扩容按服务独立伸缩故障范围一个进程崩溃影响全部业务故障被隔离在单个服务数据一致性本地事务天然强一致分布式事务一致性方案复杂开发调试本地断点调试方便需要跨服务联调调试成本高技术栈全项目统一不同服务可异构运维成本低高依赖注册中心、网关、监控等组件团队协作多个团队在一个仓库中冲突每个团队独立负责自己的服务3.1 为什么不能只看表面功能从表格上看微服务似乎全面优于单体架构。但要注意微服务是把“复杂度”从代码层转移到了基础设施层。单体应用的复杂度在于代码内部模块之间的边界需要人为约束数据库表之间的关联容易出现耦合。但这些复杂度是“静态”的开发人员可以借助 IDE、编译器和单元测试来管理。微服务则把这种复杂度变成了“运行时”的复杂度服务之间的远程调用要考虑超时、重试、熔断、负载均衡数据分散在多个数据库中查询和事务都需要专门方案线上问题排查需要链路追踪系统辅助定位。所以微服务不是降低了复杂度而是改变了复杂度的呈现方式。选择微服务本质上是选择用更复杂的架构来换取独立部署和独立扩展的能力。3.2 什么时候才真正需要微服务判断一个系统是否需要微服务可以参考以下几个条件团队人数达到一定规模通常至少三个以上的独立业务小组且各小组负责的业务边界足够清晰。单体应用的发布频率和回归成本已经严重影响业务交付速度。不同模块对资源的需求差异明显比如某个模块是 CPU 密集型另一个模块是 IO 密集型需要独立扩缩容。某个局部模块需要采用不同的技术栈或独立的性能优化方案。当前团队已经有足够的运维能力和微服务治理经验。如果以上条件一条都不满足那么单体架构仍然是更高效的选择。4. 一个订单系统的拆分演进这一节用一个电商订单系统“从单体到微服务”的演进过程演示拆分思路和落地方式。以下代码以 Spring Boot 2.7.x Spring Cloud 2021.x 为例新版本在不同组件上可能有差异但整体思路一致。4.1 单体状态在单体阶段订单、用户、商品、库存全部在一个工程里数据库也共用同一套表。一个下单请求会直接调用本进程内的 UserService、ProductService、StockService。这种模式很简单但发展到一定程度后就会出现前面说的各种问题。4.2 如何拆分边界拆分微服务的核心是识别业务边界。对于电商系统可以先根据业务域拆分成以下服务user-service用户注册、登录、用户信息查询。product-service商品信息、SKU、库存扣减。order-service订单创建、订单查询、订单状态流转。payment-service支付下单、回调处理、退款。api-gateway统一入口负责路由转发和简单鉴权。这里最关键的是数据拆分。比如订单服务不能直接访问用户表在需要用户信息时既可以在订单表中冗余部分用户快照信息也可以通过接口调用用户服务获取。4.3 拆分后的项目结构拆分后的工程目录可以这样组织spring-cloud-order-demo/ ├── api-gateway/ # 统一网关服务 │ ├── src/main/java/com/demo/gateway/ │ └── pom.xml ├── user-service/ # 用户服务 │ ├── src/main/java/com/demo/user/ │ └── pom.xml ├── product-service/ # 商品库存服务 │ ├── src/main/java/com/demo/product/ │ └── pom.xml ├── order-service/ # 订单服务 │ ├── src/main/java/com/demo/order/ │ └── pom.xml ├── payment-service/ # 支付服务 │ ├── src/main/java/com/demo/payment/ │ └── pom.xml ├── common/ # 公共模块统一返回、异常、工具类 └── pom.xml每个服务都是独立的 Maven 模块拥有自己的启动类、Controller、Service、Mapper 和数据库配置。4.4 微服务之间的调用方式订单服务在创建订单时需要调用用户服务和产品服务。在 Spring Cloud 体系中通常使用 OpenFeign 声明式 HTTP 客户端完成服务间调用。先引入依赖dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency在 order-service 启动类上开启 Feign// 文件路径order-service/src/main/java/com/demo/order/OrderApplication.java SpringBootApplication EnableFeignClients public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }定义用户服务客户端// 文件路径order-service/src/main/java/com/demo/order/feign/UserFeignClient.java FeignClient(name user-service, path /user) public interface UserFeignClient { GetMapping(/{id}) UserDTO getUser(PathVariable(id) Long id); }定义产品服务客户端// 文件路径order-service/src/main/java/com/demo/order/feign/ProductFeignClient.java FeignClient(name product-service, path /product) public interface ProductFeignClient { PostMapping(/deduct) Boolean deduct(RequestBody StockDeductRequest request); }订单服务中就可以像调用本地方法一样调用远程接口// 文件路径order-service/src/main/java/com/demo/order/service/OrderServiceImpl.java Service public class OrderServiceImpl implements OrderService { Resource private UserFeignClient userFeignClient; Resource private ProductFeignClient productFeignClient; Override public Result createOrder(OrderCreateRequest request) { // 远程调用用户服务 UserDTO user userFeignClient.getUser(request.getUserId()); if (user null || user.getStatus() ! 1) { return Result.fail(用户不存在或已禁用); } // 远程调用商品服务扣减库存 StockDeductRequest stockRequest new StockDeductRequest(); stockRequest.setSkuId(request.getSkuId()); stockRequest.setQuantity(request.getQuantity()); Boolean success productFeignClient.deduct(stockRequest); if (!Boolean.TRUE.equals(success)) { return Result.fail(库存不足); } // 本地创建订单 return Result.success(orderMapper.insert(request)); } }这里需要特别说明的是Feign 调用是远程调用不再是简单的本地方法调用这就带来两个问题网络是不可靠的调用可能超时需要考虑重试策略。下游服务可能不可用需要结合 sentinel 或 hystrix 做熔断降级避免雪崩。4.5 注册发现与配置中心在微服务架构中服务实例的数量是动态变化的因此需要注册中心。服务启动时把自己的 IP 和端口注册到注册中心调用方通过服务名从注册中心获取实例列表再进行负载均衡调用。常用的注册中心有 Nacos、Eureka、Consul 等。以 Nacos 为例服务提供方和消费方只需要配置# 文件路径order-service/src/main/resources/bootstrap.yml spring: application: name: order-service cloud: nacos: discovery: server-addr: 127.0.0.1:8848配置中心也推荐使用 Nacos把不同环境的配置放到 Nacos 中统一管理避免配置散落在各个服务里。这样做的好处是修改配置不需要重新打包配合 Nacos 的配置热更新能力可以动态调整日志级别、开关配置等。5. 微服务带来的新问题与解法微服务不是银弹它在解决单体问题的同时也引入了新的问题。这一节重点说几个最常见的。5.1 分布式事务在单体时代下单、扣库存、修改用户积分可以在一个本地事务里完成要么全部成功要么全部失败。拆分成微服务后这三个操作分布在三个服务中本地事务已经无法覆盖。分布式事务常用的方案有本地消息表。利用业务数据库和消息表保证最终一致性适合对实时性要求不高的场景。事务消息。通过 RocketMQ 等消息中间件的事务消息机制先发送半消息然后执行本地事务最后提交或回滚消息。TCC 模式。Try、Confirm、Cancel 三个阶段适合资金类等高一致性业务但实现复杂度很高。Seata 分布式事务框架。AT 模式通过代理数据源的方式对业务代码侵入较小适合中小型项目。需要强调的是不要为了强一致而强一致。在实际业务中很多场景只需要最终一致性。比如扣库存成功但下单失败可以通过消息队列发送补偿消息把库存加回来。如果所有操作都追求强一致系统的吞吐量会受到很大影响。5.2 数据一致性与查询单体应用中一个 JOIN 查询就可以把用户、商品、订单数据全部查出来。微服务拆分后数据分散在不同服务的数据库中跨服务 JOIN 查询基本不可行。常见的解决思路是接口聚合。订单服务需要展示用户信息时通过 Feign 调用用户服务获取服务端聚合后返回给前端。数据冗余。在订单表中冗余用户昵称、商品名称等字段查询时直接使用冗余字段避免频繁远程调用。CQRS 模式。将读操作和写操作分离通过监听业务事件构建独立的查询库或数据仓库满足复杂查询需求。对于 Activiti 这类工作流引擎在微服务架构中也需要特别注意。流程定义、待办任务、历史实例这些能力更适合独立收敛为一个工作流服务由上层业务通过接口调用而不是在每个微服务中都内嵌一套流程引擎否则会造成数据和引擎的重复建设。5.3 故障处理与链路追踪服务化之后一次用户请求可能会经过网关、订单服务、用户服务、商品服务、支付服务等多个节点。任何一个节点变慢整个链路都会受影响。因此需要引入熔断、限流、降级机制。Sentinel 是当前微服务中比较主流的流量治理组件支持按接口限流、按服务熔断、热点参数限流等功能。同时跨服务的请求追踪需要链路追踪系统。Spring Cloud Sleuth 配合 Zipkin 可以给每个请求生成全局 TraceId在调用链路上传递方便在界面上查看整个请求的调用耗时和服务依赖关系。生产环境排查问题时链路追踪系统是必备工具。5.4 运维复杂度一个单体应用运维只需要管理一台服务器和一个进程。一个微服务系统可能有几十个服务、几百个实例还包含注册中心、配置中心、网关、消息队列、监控系统等基础设施。如果没有自动化运维能力微服务带来的运维成本会远远高于单体。因此在微服务落地之前应该先建立好 CI/CD 流水线、容器化能力、日志集中收集和监控告警体系。通常的做法是使用 Docker Kubernetes 管理应用生命周期使用 GitLab CI 或 Jenkins 实现自动构建部署使用 Prometheus Grafana 做指标监控使用 ELK 或 Loki 做日志收集。5.5 第三方能力统一接入在微服务架构中类似微信服务号登录这种第三方能力最好统一收敛在认证服务或网关层而不是让每个业务服务都单独对接微信接口。这样一方面可以统一维护密钥和回调地址另一方面可以复用统一的会话校验逻辑避免重复开发和安全隐患。6. 常见疑问与微服务面试题“微服务”是面试高频话题这里整理了几道高频问题并给出分析思路。这些问题也是你在做技术选型时需要真正想明白的点。常见问题分析思路什么是微服务从独立部署、独立扩展、业务边界、轻量通信几个角度回答并对比单体架构微服务有哪些优缺点优点说独立部署、故障隔离、按需扩展缺点说调试困难、分布式事务、运维成本高如何拆分微服务基于领域驱动设计识别限界上下文先拆分业务边界清晰、变化频率高的核心业务分布式事务有哪些方案本地消息表、事务消息、TCC、Seata AT 模式明确不同方案的适用场景注册中心的作用是什么服务注册、服务发现、健康检查以 Nacos 为例讲解 AP 和 CP 的取舍熔断和降级有什么区别熔断是当下游异常比例达到阈值时主动切断调用降级是当服务压力过大时牺牲非核心功能你们项目为什么需要微服务从业务规模、团队组织、发布频率、扩展需求等方面回答强调不是为了技术而技术面试时最容易犯的错误是背概念。真正有经验的候选人会结合自己项目的拆分案例说明为什么当时决定引入微服务遇到了哪些问题又是如何解决的。这其实比背概念更能体现对微服务本质的理解。7. 落地微服务的最佳实践7.1 先分模块再拆服务很多团队犯的致命错误是在项目初期就直接上微服务结果业务边界没想清楚服务越拆越乱一个接口跨五六个服务调用性能和稳定性都急剧下降。更好的做法是先保持单体架构但把代码按照业务模块整理清楚模块之间只通过接口交互禁止跨模块直接访问对方的数据库表。当模块边界稳定之后再逐步把模块拆成独立的服务。这个过程相当于先把“逻辑边界”画清楚再实现“物理边界”风险要小得多。7.2 拆分粒度怎么判断微服务拆得太粗和单体没啥区别拆得太细服务数量爆炸运维成本承受不了。比较合理的判断标准是一个服务应该可以由一个小团队独立维护并且在修改这个服务时不需要协调其他团队。行业内有个通俗说法叫“两个披萨团队”意思是两个披萨够吃的团队规模大概六七个人适合负责一个微服务。从业务角度来说拆分应该以领域模型为依据而不是按技术层拆。把 Controller、Service、Mapper 拆到不同服务里是错误做法因为这是技术分层不是业务边界。7.3 团队组织与康威定律康威定律指出系统设计本质上是对组织沟通结构的复制。如果团队是多个小组按业务线划分的那么系统最终也会长成按业务线划分的微服务结构如果团队是按前端、后端、测试角色划分的系统很难真正服务化。想要落地微服务先要调整团队结构让每个服务有明确的负责人端到端地负责开发、测试、部署和运维。没有匹配的组织结构微服务架构很难持久运行。7.4 引入成熟框架还是自研微服务治理涉及注册发现、配置管理、网关、熔断限流、链路追踪等大量基础组件。推荐的路线是直接使用 Spring Cloud Alibaba 全家桶包括 Nacos、Sentinel、Gateway、Seata 等再结合开源脚手架快速搭建。国内很多团队会基于 RuoYi-Cloud 这类开源微服务脚手架起步虽然早期需要注意代码质量和二次开发成本但对于中小团队来说确实可以大大降低搭建注册中心、网关、权限认证等基础设施的时间。如果不是业务极其特殊不建议在起步阶段自研微服务框架。先站在成熟方案上跑通业务再根据痛点逐步替换或二次开发。7.5 渐进式演进策略不要梦想一次性把所有系统都拆成微服务更推荐渐进式演进。第一个拆分出来的服务应该是业务边界最清晰、独立部署价值最大、影响面可控的服务。比如一个重 CPU 计算模块拆出来以后可以单独扩容对系统整体稳定性影响也比较小。跑通第一个服务之后再逐步扩展。老系统改造时可以采用“绞杀者模式”在新需求上按微服务方式实现通过网关做路由切换老系统的请求还是走原来的单体应用新服务的请求走新的微服务集群逐步替换直到单体应用彻底下线。这种方式的优势是风险可控每一步都能独立上线。8. 微服务学习路线如果决定系统学习微服务建议按照下面的路线逐步深入第一阶段学好单体。先把 Spring Boot 用熟练理解自动装配原理掌握 Web 开发、数据访问、缓存、消息队列等基础能力。很多人一上来就学微服务反而忽略了单体基础后面很容易踩坑。第二阶段掌握核心组件。学习 Spring Cloud Alibaba 的 Nacos 注册与配置中心、OpenFeign 服务调用、Sentinel 限流熔断、Gateway 网关、Seata 分布式事务理解每个组件的定位和使用场景。第三阶段深入原理。阅读服务发现、负载均衡、熔断机制的源码理解 Feign 动态代理、Sentinel 滑动窗口算法、Nacos 一致性协议这个阶段才是真正拉开差距的地方。第四阶段走向云原生。学习 Docker 容器化、Kubernetes 编排、Service Mesh如 Istio、Serverless 等前沿方向。微服务的最终形态往往都是建立在容器和云原生基础设施之上的。第五阶段实践与复盘。选择一个真实业务动手拆分一个最小的微服务项目记录过程中遇到的问题接口超时、分布式事务、配置管理、日志排查等。实践中出现的问题才是最有价值的学习素材。回到最初的问题为什么需要微服务本质原因是当业务复杂度、团队规模、扩展需求超过单体架构承载能力时独立部署、独立扩展、故障隔离这些能力变成了刚需。微服务不是目的更快、更稳、更好地交付业务才是目的。如果你的系统还在单体阶段不要急着拆分先把模块边界、数据库边界、团队结构梳理清楚等单体的痛点真正显现出来再动手。如果系统已经因为耦合严重而拖慢业务那就从核心业务开始用渐进式的方式逐步服务化。记住适合自己的架构才是好架构。