Spring Cloud从零开始:微服务入门指南

南城开发者
2025-12-15 04:58
阅读 341

去年冬天,我还在成都某个县城的出租屋里裹着毯子敲代码。窗外是淅淅沥沥的小雨,B站上放着Lo-fi beats,手边泡着一壶茉莉花茶——典型的小镇做题家日常。

彼时我在一家小公司远程打工,项目组就三个人:一个产品经理(兼老板)、一个测试(也是老板)、还有一个运维(还是老板)。我的任务?把一套老旧的单体系统拆成微服务,理由是“隔壁老王说现在面试都问这个”。

于是,在Deadline压顶、KPI催命的背景下,我硬着头皮踏上了Spring Cloud之路。今天这篇笔记,既是给想入坑微服务的朋友一点参考,也算是自己踩坑半年后的血泪总结。


为什么非得搞微服务?

说实话,刚开始我是抗拒的。原来的Spring Boot单体应用跑得好好的,数据库连着MySQL,Redis缓存扛住大部分压力,日活也就几万,真没必要上微服务。但老板说了:“我们要为未来百万用户做准备!”(后来才知道他刚看了某大厂架构分享PPT)

不过回头想想,微服务确实有它的道理:

  • 独立部署:某个模块改了不用全量发布
  • 技术栈灵活:比如新功能用Go写个轻量服务
  • 容错隔离:订单崩了不影响用户登录

当然,代价也很明显:复杂度爆炸。注册中心、配置中心、网关、熔断、链路追踪……光是这些名词就能劝退一批人。更别说本地调试时N个服务同时启动,内存直接飙到16G。

面试题预警:“微服务解决了什么问题?带来了什么问题?” 这题我面了三家都遇到,答不全基本凉一半。


架构设计:别一上来就上全套

很多教程一上来就给你整Eureka + Zuul + Hystrix + Sleuth全家桶,搞得像在搭乐高。但实际项目中,最小可行架构才是王道。

我第一版只做了三件事:

  1. 服务拆分:按业务域切,比如 user-service、order-service、product-service
  2. 服务注册与发现:用 Nacos(比 Eureka 更现代,支持配置中心)
  3. 服务间调用:Feign + Ribbon,简单好用

其他高级功能(比如熔断、限流)先不上,等线上出问题再补——典型的“先跑起来,再优化”思路。

# bootstrap.yml 示例(user-service)
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848  # 本地起个 Nacos 就行
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

注意:bootstrap.yml 要放在 classpath 根目录,否则 Nacos 配置拉不下来,我当时卡了整整一个下午,最后发现是文件放错位置了……


服务通信:REST 还是 RPC?

我们内部争论过要不要上 gRPC。毕竟 Go 团队那边已经在用,性能好、协议清晰。但考虑到团队全是 Java 背景,最终还是选了 Feign + JSON

// order-service 调用 user-service
@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

虽然性能不如 gRPC,但胜在开发快、调试方便。而且加上 OpenFeign 的重试和超时配置,基本够用:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 10000
        retryer: com.example.config.CustomRetryer

吐槽一句:产品经理总以为“服务拆了就能并行开发”,结果两个服务接口对不上,联调三天没进展。最后我写了个 Swagger + Postman 共享文档,才勉强稳住局面。


数据库设计:别让事务变成噩梦

微服务最头疼的不是调用,而是分布式事务

比如下单流程:扣库存 → 创建订单 → 扣用户余额。这三个操作分布在三个服务,传统 @Transactional 直接失效。

我们没上 Seata(太重),也没搞 TCC(太复杂),而是用了最终一致性 + 补偿机制

  1. 订单服务发消息到 RocketMQ
  2. 库存服务消费消息,扣库存
  3. 如果失败,发“回滚”消息,订单状态变“取消”
// 伪代码:下单
@Transactional
public void createOrder(Order order) {
    orderRepository.save(order);
    mqProducer.send("ORDER_CREATED", order.getId());
    // 不直接调用库存服务!
}

这种方式虽然有延迟,但系统简单、可追溯。线上跑了几个月,补偿成功率 99.9%,剩下的 0.1% 人工处理——反正用户量不大,老板也能接受。


配置管理:别再 hardcode 了!

以前单体时代,配置全写在 application.yml 里,改个数据库密码就得重新打包。现在用 Nacos 做配置中心,动态刷新简直爽飞:

@RestController
@RefreshScope // 关键注解!
public class ConfigController {
    @Value("${app.feature.new-ui:false}")
    private boolean newUiEnabled;
}

在 Nacos 控制台改个开关,服务自动生效,不用重启!上周五晚上产品经理突然说“双11要上线新皮肤”,我改个配置就搞定了,十点准时下班——这种成就感,谁懂?


性能与监控:别等线上炸了才看

微服务调用链长,一个请求可能经过 5 个服务。不出问题还好,一出问题就是“到底哪一环慢?

我们加了 Spring Cloud Sleuth + Zipkin,链路追踪一目了然:

服务 平均耗时 错误率
gateway 12ms 0%
user-service 45ms 0.1%
order-service 210ms 2% ← 重点优化!

果然,订单服务查数据库没加索引,慢查询拖垮整个链路。加个联合索引后,TP99 从 800ms 降到 120ms。

另外,健康检查也别忘:

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always

运维(也就是老板)终于能在 Grafana 上看到服务状态了,再也不用半夜打电话问我“系统是不是挂了”。


面试题高频考点整理

学完这套,我面了两家,以下问题几乎必问:

问题 我的回答要点
Spring Cloud 和 Dubbo 区别? SC 基于 HTTP/REST,生态全;Dubbo 是 RPC,性能高但绑定 Java
Feign 和 RestTemplate 区别? Feign 声明式,集成 Ribbon/Hystrix;RestTemplate 更底层
服务雪崩怎么防? 熔断(Hystrix/Sentinel)+ 降级 + 限流
配置中心怎么保证高可用? Nacos 集群部署,本地缓存兜底
微服务下如何做权限控制? 网关统一鉴权(JWT),服务间用 Token 传递

血泪教训:别死记概念,一定要结合你自己的项目讲。比如我说“我们用 Nacos 做配置中心,有一次网络抖动导致配置拉取失败,后来加了本地 fallback 文件”,面试官眼睛都亮了。


最后:微服务不是银弹

折腾半年下来,我最大的感悟是:微服务不是技术升级,而是组织升级

如果你团队就几个人,业务也不复杂,硬上微服务只会增加维护成本。我们现在的架构其实还是“伪微服务”——服务拆了,但数据库还是共用一个 MySQL 实例(只是不同 schema),CI/CD 也还是手动打包上传。

但没关系,适合当前阶段的架构才是好架构

最近听说老板又想搞 Service Mesh,还买了本《云原生实战》……算了,等他看完再说吧。我先去听听歌,写写 Go —— 毕竟隔壁 Go 服务才 20MB 内存,而我的 Spring Boot 动不动 500MB,属实有点惭愧。


写在结尾:技术没有高低贵贱,只有合适不合适。在县城远程办公的日子,让我更明白:稳定、可维护、能按时下班的系统,才是好系统。至于那些 fancy 的架构图?留着面试吹牛用就好 😄

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝