Spring Cloud 微服务入门,从被领导催到真香
上周五晚上十点,我正用 Vim 改着一个 Rust 的 toy 项目,想着“这 borrow checker 虽然烦人,但至少不会半夜报警”,突然钉钉弹出一条消息:“下周三前,把订单服务拆成微服务,不然双11崩了算你头上。”
发信人:我那个永远在写 OKR 的后端 TL。
我愣了两秒,手一抖,:wq 写成了 :WQ,Vim 报错:“E492: Not an editor command”。那一刻,我仿佛看到了自己在国考行测卷上涂错的答题卡——熟悉的无力感又来了。
说起来,我是个在职程序员,坐标北京,每天通勤一小时,白天写 Java 后端,晚上刷申论。最近一边啃《粉笔5000题》,一边研究 Rust,结果领导一句话,直接把我拽回了 Spring Cloud 的深水区。
为什么是现在?
其实我们团队一直用的是单体 Spring Boot 应用。代码量不大,但模块耦合得像我妈织的毛衣——扯一根线,整件都变形。去年双11,订单模块一崩,连用户登录都挂了,运维兄弟在群里咆哮:“你们后端能不能别把所有逻辑塞一个 jar 里?!”
产品经理倒是乐呵呵:“微服务嘛,高大上,赶紧上!”
测试小哥默默补了一句:“那自动化测试也得重写吧……”
我:???
但现实是,求职市场对“微服务经验”的要求越来越高。最近面了几家,HR 张口就问:“用过 Spring Cloud 吗?Eureka、Nacos、Sentinel 都玩过吧?” 我只能尬笑:“呃……用过 Spring Boot。”
所以,与其被动挨打,不如主动出击。正好这次“被逼重构”,就当给自己攒个跳槽筹码——毕竟,考公路上,多一份技术底气,少一分焦虑。
从零开始:别怕,微服务没那么玄
很多人一听“微服务”,脑子里立刻浮现出一堆中间件:注册中心、配置中心、网关、熔断、链路追踪……吓得直接放弃。但其实,微服务的核心思想很简单:拆!
把一个大应用,按业务边界拆成多个小服务,每个服务独立部署、独立伸缩、独立数据库。比如:
- 用户服务(user-service)
- 订单服务(order-service)
- 商品服务(product-service)
它们之间通过 HTTP 或 RPC 通信,而不是直接调用对方的类或方法。
而 Spring Cloud,就是帮你搞定这些“拆完之后怎么管”的全家桶。
第一步:搭个注册中心,让服务能找到彼此
微服务的第一步,不是写业务,而是让服务能互相发现。就像你搬家了,得先告诉快递公司新地址,不然包裹全寄错。
Spring Cloud 提供了两种主流选择:Eureka(Netflix) 和 Nacos(阿里开源)。我选了 Nacos,原因很现实:国产 + 控制台好看 + 支持配置中心,而且我们公司已经在用,运维不用重新学。
搭建 Nacos Server(本地开发)
# 下载 nacos-server
wget https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.3.0.tar.gz
tar -xzf nacos-server-2.3.0.tar.gz
cd nacos/bin
# 单机模式启动(开发用)
sh startup.sh -m standalone
访问 http://localhost:8848/nacos,默认账号密码都是 nacos。界面清爽,比 Eureka 那个简陋的 HTML 友好多了。
在 Spring Boot 项目中接入
假设你有一个 user-service,想注册到 Nacos:
- 加依赖(
pom.xml):
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2022.0.0.0</version>
</dependency>
- 写配置(
application.yml):
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
- 主启动类加注解:
@SpringBootApplication
@EnableDiscoveryClient // 关键!开启服务发现
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
启动后,刷新 Nacos 控制台,就能看到 user-service 上线了!同理,再起一个 order-service,它也能在列表里看到。
踩坑记录:第一次启动时,我忘了加
@EnableDiscoveryClient,服务死活不注册。查了半小时日志,最后发现是注解漏了。微服务里,细节决定生死。
第二步:服务间调用,别再硬编码 IP
现在两个服务都注册了,怎么让 order-service 调用 user-service 的接口?
最蠢的办法是写死 IP:http://192.168.1.100:8080/user/123。但 IP 会变,端口会冲突,线上环境更不可能这么干。
正确姿势是:通过服务名调用 + 负载均衡。
Spring Cloud 提供了 RestTemplate + @LoadBalanced 的组合拳:
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 这个注解让 restTemplate 支持服务名调用
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
然后在 order-service 里:
@Service
public class OrderService {
@Autowired
private RestTemplate restTemplate;
public User getUserById(Long userId) {
// 注意:这里用的是服务名 user-service,不是 IP!
return restTemplate.getForObject("http://user-service/user/" + userId, User.class);
}
}
底层,Spring Cloud 会自动从 Nacos 拉取 user-service 的所有实例,做负载均衡(默认轮询)。如果某个实例挂了,下次调用会自动跳过。
真实场景:上周压测时,我们故意 kill 了一个
user-service实例,订单服务居然毫无感知,继续跑。那一刻,我终于理解了什么叫“高可用”。
第三步:统一入口,上 API 网关
随着服务越来越多,前端不可能每个服务都记一个 URL。而且,鉴权、限流、日志这些横切关注点,总不能每个服务都写一遍。
这时候,API 网关就该出场了。Spring Cloud Gateway 是官方推荐的网关组件,基于 WebFlux,性能比 Zuul 高不少。
简单配置路由
spring:
cloud:
gateway:
routes:
- id: user_route
uri: lb://user-service # lb 表示负载均衡
predicates:
- Path=/api/user/**
- id: order_route
uri: lb://order-service
predicates:
- Path=/api/order/**
这样,前端所有请求都发到网关(比如 http://gateway:8080/api/user/123),网关根据路径转发到对应服务。
吐槽:产品经理看到这个配置后,一脸惊喜:“以后改接口不用找前端了?” 我:“理论上是的,但你敢改路径吗?”
第四步:别让雪崩发生,加个熔断器
微服务最大的风险是什么?级联故障。比如 user-service 响应慢,导致 order-service 线程池耗尽,接着 payment-service 也崩了……整个系统雪崩。
解决方案:熔断 + 降级。Spring Cloud Alibaba Sentinel 是个好选择。
引入依赖后,只需在调用处加个注解:
@SentinelResource(
value = "getUserFallback",
fallback = "getUserFallback" // 降级方法
)
public User getUserById(Long userId) {
return restTemplate.getForObject("http://user-service/user/" + userId, User.class);
}
// 降级逻辑
public User getUserFallback(Long userId, Throwable e) {
log.warn("用户服务不可用,返回空用户", e);
return new User(); // 返回兜底数据
}
Sentinel 还支持实时监控 QPS、响应时间,甚至可以动态调整规则。线上事故时,它就是你的“紧急刹车”。
数据库设计:每个服务独享数据库
微服务架构下,强烈建议每个服务拥有自己的数据库。不要共享同一张表!否则,拆分就失去了意义。
比如:
user-service用user_dborder-service用order_db
跨服务数据一致性怎么办?用 最终一致性 + 消息队列。比如下单成功后,发一条 MQ 消息,用户服务监听后更新积分。
血泪教训:早期我们图省事,订单和用户共用一个 DB,结果一次 DDL 操作锁表,两个服务全挂。运维大哥差点提刀来办公室。
性能与运维:上线不是终点
微服务拆完,性能反而可能变差——因为多了网络调用。所以必须关注:
| 优化点 | 建议方案 |
|---|---|
| 网络延迟 | 服务尽量部署在同一机房,减少跨机房调用 |
| 序列化 | 用 JSON 就够了,别折腾 Protobuf(除非 QPS 极高) |
| 日志追踪 | 接入 Sleuth + Zipkin,用 traceId 串起全链路 |
| 监控告警 | Prometheus + Grafana,监控每个服务的 RT、错误率 |
我们还搞了个“微服务健康检查”页面,TL 一眼就能看到哪个服务红了,再也不用半夜打电话问我:“是不是你又改了什么?”
考公程序员的真心话
写这篇文章的时候,窗外北京的晚高峰刚刚开始。我合上笔记本,心里有点复杂。
一方面,Spring Cloud 确实让系统更健壮、更易维护;另一方面,微服务带来的复杂度,也让加班成了常态。有时候真羡慕那些只写 CRUD 的同事,准点下班,周末还能去公园晨读。
但转念一想,技术债早晚要还,早拆早超生。而且,这段经历写进简历,面试时至少能吹十分钟。说不定哪天,我就带着“微服务架构经验”成功上岸,成为某局信息中心的公务员,白天写政务系统,晚上继续研究 Rust。
谁知道呢?
最后:给想入坑的朋友几点建议
- 别为了微服务而微服务:业务量小就别折腾,单体 Spring Boot 足够香。
- 先学好 Spring Boot:它是 Spring Cloud 的基础,不懂自动配置、Starter,后面全是雾里看花。
- 本地开发用 Docker Compose:一键启停 Nacos、Sentinel、Zipkin,比手动开一堆终端优雅多了。
- 求职时重点讲“为什么”:面试官不关心你用了多少组件,而关心你解决了什么问题。
附上我的 docker-compose.yml 片段,自取不谢:
version: '3'
services:
nacos:
image: nacos/nacos-server:v2.3.0
ports:
- "8848:8848"
environment:
MODE: standalone
微服务不是银弹,但它是一把好刀。关键看你怎么用。
而我,还得继续刷行测。毕竟,代码可以重构,人生不能重开。

评论 0