从零搭建 Spring Cloud 微服务:一个 Vim 党的踩坑实录

接口调不通
2025-12-28 08:26
阅读 2031

上周五晚上十一点半,我还在公司对着屏幕敲 :wq,窗外陆家嘴的霓虹灯早就熄了一半。刚上线的新功能在压测时直接崩了,监控面板上一堆红色告警,运维兄弟在钉钉群里疯狂 at 我:“你们这服务注册没做熔断?雪崩了知道吗?”
我当时真的想砸键盘——不是砸电脑,毕竟 MacBook 太贵,砸不起。

我是某上市公司技术中台团队的一名后端工程师,坐标上海张江,租的房子离公司步行十分钟(主要是为了省打车钱,以及方便深夜加班)。平时写代码基本只用 Vim,IntelliJ IDEA 对我来说就像外星科技——太重,启动慢,还老弹“智能提示”打断思路。不过最近半年,团队全面转向微服务架构,Spring Cloud 成了绕不开的坎。这篇文章,就是我在被产品经理催、被运维骂、被线上事故教育之后,总结出的一套“从零开始搞 Spring Cloud”的实战经验。


为啥非得搞微服务?

别信那些 PPT 架构师说的“高内聚低耦合”。我们搞微服务,纯粹是因为业务膨胀太快——去年双11期间,单体应用数据库连接池直接打满,订单服务拖垮了整个系统。领导拍板:“拆!不拆明年 KPI 别想要了。”

于是我们把原来的单体 Spring Boot 应用,按业务域拆成了用户中心、订单服务、库存服务、支付网关……每个服务独立部署、独立数据库。但问题来了:服务之间怎么通信?怎么知道对方在哪?挂了怎么办?这时候,Spring Cloud 就登场了。


Spring Cloud 不是“框架”,是一堆工具箱

很多人一上来就学 Eureka、Ribbon、Feign,结果连 CAP 理论都没搞懂。其实 Spring Cloud 本质是 Netflix OSS + Spring Boot 的整合包,它本身不做实现,只是提供统一的编程模型。

我们最终选型如下(结合公司实际情况):

组件 作用 替代方案 为什么选它
Nacos 服务注册与发现 + 配置中心 Eureka + Config 国产、支持 AP/CP 切换、配置热更新
OpenFeign 声明式 HTTP 客户端 RestTemplate + Ribbon 代码更简洁,支持 fallback
Sentinel 流量控制与熔断 Hystrix 阿里开源,实时监控更友好
Gateway API 网关 Zuul 性能更高,支持异步非阻塞

注:我们没用 Consul 或 ZooKeeper,一是运维成本高,二是公司已有 Nacos 集群。


从零创建第一个服务:别被“Hello World”骗了

很多教程教你写个 @EnableEurekaClient 就完事,但在生产环境,光注册成功远远不够。

第一步:服务注册(用 Nacos)

# application.yml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: nacos.prod.internal:8848
        namespace: prod-ns  # 隔离环境
        group: DEFAULT_GROUP

关键点:

  • namespace 必须配:不然测试环境的服务会注册到生产集群,我们吃过亏。
  • 健康检查要自定义:Nacos 默认用 TCP 探活,但我们的服务依赖 MySQL 和 Redis,必须等 DB 连通才算“健康”。
@Component
public class CustomHealthIndicator implements HealthIndicator {
    @Autowired
    private DataSource dataSource;

    @Override
    public Health health() {
        try (Connection conn = dataSource.getConnection()) {
            if (conn.isValid(2)) {
                return Health.up().build();
            }
        } catch (SQLException e) {
            // log error
        }
        return Health.down().withDetail("reason", "DB unreachable").build();
    }
}

第二步:服务调用(OpenFeign + Sentinel)

假设订单服务要调用户信息:

@FeignClient(name = "user-service", fallback = UserClientFallback.class)
public interface UserClient {
    @GetMapping("/api/users/{id}")
    UserDTO getUserById(@PathVariable("id") Long id);
}

@Component
public class UserClientFallback implements UserClient {
    @Override
    public UserDTO getUserById(Long id) {
        // 返回兜底数据 or 抛异常
        throw new ServiceUnavailableException("User service unavailable");
    }
}

但光有 fallback 不够!我们在线上遇到过:用户服务响应变慢(比如慢查询),导致订单服务线程池被占满,进而拖垮整个链路。所以必须加熔断规则

// Sentinel 规则配置(通过 Dashboard 或代码)
RuleManager.loadRules(Arrays.asList(
    new DegradeRule("user-service")
        .setGrade(RuleConstant.DEGRADE_GRADE_RT) // 按 RT 熔断
        .setCount(200) // 超过 200ms
        .setTimeWindow(10) // 熔断 10 秒
));

配置中心:别再把密码写死在 Git 里了!

曾经有个实习生把数据库密码 commit 到 GitHub 公共仓库,被安全扫描抓到,全组扣了绩效。从此我们强制使用 Nacos 配置中心。

# bootstrap.yml(优先级高于 application.yml)
spring:
  cloud:
    nacos:
      config:
        server-addr: nacos.prod.internal:8848
        file-extension: yaml
        namespace: prod-ns
        group: ORDER_GROUP

在 Nacos 控制台新建 order-service.yaml

db:
  url: jdbc:mysql://prod-db:3306/order?useSSL=false
  username: ${DB_USER}  # 从环境变量注入
  password: ${DB_PASS}

安全提示:敏感信息(如密码)绝不进配置文件,用 K8s Secret 或 Vault 注入环境变量。


网关:你的第一道防线

我们用 Spring Cloud Gateway 做统一入口,主要干三件事:

  1. 路由转发(/api/user/** → user-service)
  2. 认证鉴权(JWT 校验)
  3. 限流(防止恶意刷接口)
spring:
  cloud:
    gateway:
      routes:
        - id: user_route
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          filters:
            - StripPrefix=1
            - RequestRateLimiter=redis-rate-limiter, 10, 20 # 令牌桶

限流配置(基于 Redis):

@Bean
public RedisRateLimiter redisRateLimiter() {
    return new RedisRateLimiter(10, 20); // replenishRate=10, burstCapacity=20
}

有一次大促,某个爬虫疯狂请求商品详情,网关限流直接把它挡在外面,保护了下游服务。运维兄弟终于没在群里 at 我了,感动。


跨语言调用?别慌,HTTP 是万能胶

你可能会问:“我们前端用 Javascript,算法团队用 Python,怎么和 Java 微服务交互?”

答案很简单:所有服务对外只暴露 RESTful API。内部用 Feign,外部用 HTTP Client。

  • 前端(Javascript):fetch('/api/user/123')
  • Python 脚本:requests.get('http://gateway/api/user/123', headers={'Authorization': 'Bearer xxx'})

我们甚至给算法团队写了 Swagger 文档,他们用 Python 自动生成 client:

# 自动生成的 Python SDK(基于 OpenAPI spec)
from user_client import UserApi
api = UserApi()
user = api.get_user_by_id(user_id=123)

所以别被“微服务必须全家桶”忽悠了。只要协议统一(HTTP + JSON),语言不是问题。


面试题高频考点:别只会背八股文

最近帮 HR 筛简历,看到一堆“精通 Spring Cloud”的候选人,一问细节就露馅。以下是我们面试常问的几个真问题:

  1. Eureka 和 Nacos 在 CAP 上有什么区别?
    → Eureka 保证 AP(可用性+分区容错),Nacos 支持切换(AP 用于服务发现,CP 用于配置)。

  2. Feign 调用超时怎么设置?
    → 不是只设 feign.client.config.default.connectTimeout,还要看 Ribbon 和 Hystrix(如果用了)的超时。

  3. Sentinel 和 Hystrix 的核心差异?
    → Hystrix 基于线程池隔离,Sentinel 基于信号量 + 实时指标统计,更适合高并发场景。

  4. 配置中心如何做到“配置变更实时生效”?
    → Nacos 通过长轮询 + WebSocket 推送,配合 @RefreshScope 注解。

如果你正在求职,建议动手搭一套完整链路,比背一百道题都有用。


生产环境血泪教训

  1. 日志必须带 traceId
    用 Sleuth + Zipkin 实现全链路追踪。否则排查“为什么订单没生成”时,你要在十个服务的日志里 grep 同一个用户 ID。

  2. 不要忽略 JVM 参数
    微服务实例多,每个服务内存别设太大。我们默认 -Xmx512m -Xms512m,避免 K8s OOMKill。

  3. 健康检查路径要独立
    /actuator/health 不能依赖数据库,否则服务启动时因 DB 未就绪被误判为“宕机”。

  4. 灰度发布靠不住?自己写路由规则
    Nacos 支持元数据标签,我们在 Gateway 里根据 header env=gray 路由到灰度实例。


写在最后

搞微服务半年,头发少了,但对系统稳定性的理解深了。Spring Cloud 不是银弹,它解决的是“分布式复杂性”,但引入了“运维复杂性”。我们团队现在每天花 30% 时间在监控、告警、日志分析上——这才是真实世界。

如果你也在上海卷微服务,欢迎交流(最好别在周五晚上十一点找我)。Vim 用户不多,但我们可以一起吐槽 IDEA 启动慢。

P.S. 下次想跳槽的兄弟,别只刷 LeetCode 了。把这套 Spring Cloud 链路跑通,面试官眼睛会亮。毕竟,能搞定线上事故的人,才是团队最需要的。

评论 0

最热最新
暂无评论
接口调不通Lv.1
0
影响力
0
文章
0
粉丝