如果你已经用 SpringBoot 开发过几个项目,一定会遇到这样的场景:项目越来越大,一个工程里有几十个模块,几百个接口,每次打包要花好几分钟,改一行代码需要重启整个应用。
这就是单体应用走到尽头时的典型症状。
微服务,就是解决这个问题的思路。它不是一门新技术,而是一套架构思想------把一个大而全的系统,拆分成多个小而独立的服务。
这篇文章用最通俗的方式,讲清楚微服务是什么、解决了什么问题、以及带来了哪些新挑战。
一、一个餐厅的比喻
想象你要开一家餐厅。
单体模式
一开始,你一个人包揽所有活:点菜、炒菜、收银、洗碗、端盘子。
这就是单体应用。一个人做所有事,简单直接。
生意越来越好,你忙不过来了。于是你招了厨师、收银员、服务员。但他们还是在一个小店里,共用同一个厨房、同一台收银机。炒菜和洗碗抢水池,点菜和收银挤在一起。
这就是单体应用变大后的样子。看似有了分工,实际上还是耦合在一起,一个人卡住,全店瘫痪。
微服务模式
你把餐厅拆成了独立的"部门":
-
点菜服务:专门负责接待、点菜
-
后厨服务:专门负责做菜
-
收银服务:专门负责结账
-
洗碗服务:专门负责洗碗
每个部门有自己的小厨房、自己的收银机、自己的服务员。部门之间通过"传菜窗口"和"对讲机"沟通。
这就是微服务。每个服务独立运行、独立部署、独立扩展。生意火爆时,后厨忙不过来就多开几个灶台,点菜慢了就多招几个迎宾,互不影响。
二、什么是微服务
微服务是一种架构风格,把一个大型应用拆分成一组小型的、独立的服务。
每个服务都有这样几个特征:
-
单一职责:一个服务只做一件事。订单服务只管订单,用户服务只管用户。
-
独立部署:改订单服务的代码,只需要重启订单服务,其他服务不受影响。
-
独立数据:每个服务有自己的数据库,不跟别人共用。
-
技术异构:订单服务用 Java,用户服务用 Go,完全可以。
三、从单体到微服务的演进
第一阶段:单体应用
所有功能写在一个项目里,一个数据库,一个部署包。
优点:开发简单、部署方便、调用快(都在内存里)。
缺点:改一行代码要测整个系统,一个小问题可能让整个应用崩溃,团队协作困难(十几个人改同一个仓库)。
第二阶段:集群单体
还是同一个应用,但部署了三份,前面加一个负载均衡。
优点:提升了并发能力,一台挂了还有别的。
缺点:数据库还是单点,代码还是耦合在一起,改一行代码还是要全部重新部署。
第三阶段:微服务
把大应用拆成小服务:用户服务、订单服务、商品服务、支付服务......
每个服务独立部署,独立数据库,服务之间通过网络调用。
优点:每个服务可以独立开发、独立部署、独立扩展。团队可以各自负责一个服务,互不干扰。
缺点:复杂度飙升。原来在一个进程里调个方法就行的事,现在变成了网络请求。
四、微服务带来的新问题
拆分容易,拆分后怎么管理才是难点。微服务带来了几个必须解决的问题:
4.1 服务发现
以前在单体里,A 调用 B 直接 new 一个对象就行。现在 B 在哪台机器上?IP 是多少?B 挂了吗?B 扩容了怎么办?
解决方案是引入注册中心。所有服务启动时都去注册中心"报到",告诉别人自己叫什么、在哪。调用方去注册中心问"谁叫订单服务",拿到地址后再去调用。Spring Cloud 中的 Eureka、Nacos、Consul 都是干这个活的。
4.2 配置管理
以前配置写在 application.yml 里,每个环境一份。现在几十个服务,每个服务都有配置文件,改一个配置要改几十处。
解决方案是配置中心。所有配置集中管理,服务启动时从配置中心拉取配置,配置变更时还能动态刷新,不用重启服务。
4.3 网关路由
以前前端请求直接打到单体应用。现在几十个服务,前端应该调用谁?总不能把几十个地址都写在前端吧。
解决方案是网关。所有请求先到网关,网关根据请求路径判断应该转发给哪个服务。网关还负责认证、限流、日志等公共功能。
4.4 服务间通信
服务 A 调用服务 B,怎么调?
同步调用常用 HTTP + JSON 或 gRPC。异步调用常用消息队列(RabbitMQ、Kafka)。
同步调用简单直观,但会形成调用链依赖。异步调用解耦更好,但复杂度更高。
4.5 分布式事务
单体里的事务很简单:一个方法里操作多张表,加个 @Transactional 就行。
微服务里,创建订单可能同时要调用订单服务、库存服务、优惠券服务、积分服务。如果库存扣减成功,但积分增加失败,怎么办?
这就是分布式事务问题。没有完美解,常见的方案有:最终一致性(先做完再说,后面慢慢对账)、TCC 事务(Try-Confirm-Cancel)、Saga 模式。
4.6 链路追踪
一个请求可能经过:网关 → 订单服务 → 库存服务 → 优惠券服务 → 用户服务。其中某一个慢了,怎么知道是哪个?
解决方案是链路追踪。每个请求经过每个服务时,都带上同一个 Trace ID,把经过的路径和耗时记录下来。这样就可以清晰看到请求在哪个环节慢了。常见的工具有 SkyWalking、Zipkin、Jaeger。
4.7 熔断与降级
服务 A 调用服务 B,服务 B 挂了怎么办?没有保护的话,服务 A 的线程全卡在等 B 响应,慢慢把 A 也拖垮,然后连锁反应把整个系统拖垮。
解决方案是熔断器。当 B 连续失败达到阈值,熔断器打开,后续请求直接失败,不再调用 B。过一段时间放行几个请求试探,如果 B 好了就关闭熔断器。
降级则是熔断后的兜底方案,比如返回缓存数据、返回默认值、提示稍后重试。
五、微服务与 SpringBoot 的关系
很多人搞不清 SpringBoot 和 Spring Cloud 的区别。
SpringBoot 是快速开发单体应用的框架。它让你能快速写出一个独立运行的 Java 服务。
Spring Cloud 是一套微服务解决方案。它提供了一系列工具,帮你解决上面提到的服务发现、配置管理、网关、熔断等问题。
两者的关系:SpringBoot 用来写每个微服务本身,Spring Cloud 用来把这些微服务组织成一个系统。
打个比方:SpringBoot 是造车的技术,Spring Cloud 是修高速公路、建交通信号灯、设立交桥的技术。你用车可以跑任何路,但有了高速公路和交通系统,多辆车才能高效协同。
六、微服务不是银弹
说了这么多微服务的好处,但它不是万能的。
什么时候不该用微服务:
-
团队只有三五个人,维护几十个服务会把人累死
-
业务逻辑简单,用户量不大
-
项目刚起步,业务方向还不确定
什么时候可以考虑微服务:
-
团队有几十人,改一个模块要等半天
-
不同模块的流量差异巨大(有的要扛高并发,有的几乎没流量)
-
需要多语言开发(不同服务用不同技术栈)
一个务实的建议:先做单体,等痛点出现了再拆分。不要为了微服务而微服务。一个结构清晰的单体,比一个拆得乱七八糟的微服务要好得多。
七、总结
微服务是一种把大系统拆成小服务的架构思想。
| 维度 | 单体应用 | 微服务 |
|---|---|---|
| 部署 | 一个包 | 几十个包 |
| 扩展 | 整体扩展 | 按需扩展 |
| 开发 | 一个仓库 | 多个仓库 |
| 故障影响 | 整个系统 | 单个服务 |
| 复杂度 | 低 | 高 |
微服务解决的问题是"大团队协作、高并发场景下的灵活扩展",但它带来的复杂度也不容忽视。
回到餐厅的比喻:开个煎饼摊子,一个人就够了,没必要搞微服务。开个五星级酒店,有中餐厅、西餐厅、宴会厅、客房服务,那每个部门独立运作、独立管理,就是自然而然的选择。
你的项目处在什么阶段,就选什么架构。没有最好的架构,只有最合适的架构。