从零搭建 Spring Cloud 微服务:一个 Vim 党的踩坑实录
上周五晚上十一点半,我还在公司对着屏幕敲 :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 做统一入口,主要干三件事:
- 路由转发(/api/user/** → user-service)
- 认证鉴权(JWT 校验)
- 限流(防止恶意刷接口)
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”的候选人,一问细节就露馅。以下是我们面试常问的几个真问题:
Eureka 和 Nacos 在 CAP 上有什么区别?
→ Eureka 保证 AP(可用性+分区容错),Nacos 支持切换(AP 用于服务发现,CP 用于配置)。Feign 调用超时怎么设置?
→ 不是只设feign.client.config.default.connectTimeout,还要看 Ribbon 和 Hystrix(如果用了)的超时。Sentinel 和 Hystrix 的核心差异?
→ Hystrix 基于线程池隔离,Sentinel 基于信号量 + 实时指标统计,更适合高并发场景。配置中心如何做到“配置变更实时生效”?
→ Nacos 通过长轮询 + WebSocket 推送,配合@RefreshScope注解。
如果你正在求职,建议动手搭一套完整链路,比背一百道题都有用。
生产环境血泪教训
日志必须带 traceId
用 Sleuth + Zipkin 实现全链路追踪。否则排查“为什么订单没生成”时,你要在十个服务的日志里 grep 同一个用户 ID。不要忽略 JVM 参数
微服务实例多,每个服务内存别设太大。我们默认-Xmx512m -Xms512m,避免 K8s OOMKill。健康检查路径要独立
/actuator/health不能依赖数据库,否则服务启动时因 DB 未就绪被误判为“宕机”。灰度发布靠不住?自己写路由规则
Nacos 支持元数据标签,我们在 Gateway 里根据 headerenv=gray路由到灰度实例。
写在最后
搞微服务半年,头发少了,但对系统稳定性的理解深了。Spring Cloud 不是银弹,它解决的是“分布式复杂性”,但引入了“运维复杂性”。我们团队现在每天花 30% 时间在监控、告警、日志分析上——这才是真实世界。
如果你也在上海卷微服务,欢迎交流(最好别在周五晚上十一点找我)。Vim 用户不多,但我们可以一起吐槽 IDEA 启动慢。
P.S. 下次想跳槽的兄弟,别只刷 LeetCode 了。把这套 Spring Cloud 链路跑通,面试官眼睛会亮。毕竟,能搞定线上事故的人,才是团队最需要的。

评论 0