35岁老码农的Spring Cloud实战手记:从零搭起微服务
凌晨两点,窗外北京五环外的夜色沉得像我的黑眼圈。工位上那杯凉透的速溶咖啡还没收拾,IDEA里刚跑通的注册中心日志终于不再刷“Connection refused”。我揉了揉发酸的颈椎——没错,这就是一个奔四程序员的日常:白天在公司被产品经理追着改需求,晚上回家还得和Eureka死磕。
去年双11前,我们团队接了个新项目:重构老旧的单体电商系统。领导拍板要用微服务架构,理由很充分:“隔壁组用了Spring Cloud,扛住了百万级流量。” 但没人告诉我,隔壁组有三个资深架构师+两个运维专家,而我们只有三个后端(其中俩刚毕业)+一个兼职DevOps。更惨的是,Deadline就在三个月后。
没办法,硬着头皮上吧。这篇不是教科书式的理论堆砌,而是我踩坑、熬夜、查文档、翻GitHub Issues后总结的实战指南。如果你也正被微服务折磨,希望它能帮你少熬几个通宵。
为什么选Spring Cloud?(而不是Python?)
先说清楚立场:我在家折腾过FastAPI+Consul的组合,Python写起来确实爽,异步处理也优雅。但生产环境求稳啊兄弟们!我们系统要对接银行支付、库存同步、物流追踪,任何一个环节崩了都是P0事故。Spring Cloud生态成熟、社区庞大、监控工具链完整,加上团队Java基础扎实,选它是最稳妥的“不性感但可靠”的选择。
面试题挑战:
“微服务一定要用Java吗?”
我的回答:技术栈服务于业务。如果你团队90%是Python工程师,且业务对强事务要求不高(比如内容平台),那FastAPI+gRPC完全OK。但金融、电商这类强一致性场景,Java的生态护城河还是难以替代。
第一步:拆!但别乱拆
很多新手一上来就狂拆十几个服务,结果发现服务间调用比数据库JOIN还慢。我们吸取教训,按业务边界+数据一致性来划分:
| 服务名 | 职责 | 是否独立数据库 |
|---|---|---|
| user-service | 用户注册/登录/信息管理 | 是 |
| order-service | 订单创建/状态流转 | 是 |
| inventory-service | 库存扣减/回滚 | 是 |
| gateway | 统一入口、鉴权、限流 | 否 |
关键原则:一个服务只操作自己的数据库。跨服务事务用Saga模式(后面详述),绝不搞分布式事务(除非你想半夜被PagerDuty叫醒)。
搭建注册中心:Eureka vs Nacos?
最初我选了Eureka——毕竟Spring Cloud Netflix全家桶嘛。但配置完发现:服务下线延迟高达90秒!测试时kill掉一个实例,gateway还在往它发请求,直接502。查文档才知道Eureka默认心跳90秒超时。
后来换成Nacos,真香!秒级服务发现,控制台还能看服务拓扑图。配置也简单,application.yml 三行搞定:
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos地址
血泪教训:别迷信Netflix套件!2018年后很多组件已停止维护。Nacos + Sentinel + Seata 这套国产组合拳,在阿里系验证过,更适合国内网络环境。
服务调用:Feign比RestTemplate香多了
早期用RestTemplate手写URL调用,代码丑得像我的发际线:
// 别这么干!
String url = "http://inventory-service/api/stock?skuId=" + skuId;
ResponseEntity<Stock> response = restTemplate.getForEntity(url, Stock.class);
换成Feign后,接口声明式调用,清爽如德芙:
@FeignClient(name = "inventory-service")
public interface InventoryClient {
@GetMapping("/api/stock")
Stock getStock(@RequestParam("skuId") String skuId);
}
// 使用处
@Autowired
private InventoryClient inventoryClient;
public Order createOrder(String skuId) {
Stock stock = inventoryClient.getStock(skuId); // 像调本地方法一样
// ...
}
注意:Feign默认没超时控制!线上曾因库存服务卡住,导致订单服务线程池耗尽。务必加配置:
feign:
client:
config:
default:
connectTimeout: 2000 # 连接超时
readTimeout: 5000 # 读取超时
配置中心:告别“改配置重启服务”
以前改个数据库密码,得逐个服务重启。现在用Nacos Config,动态刷新:
@RestController
@RefreshScope // 关键注解!配置变更时自动刷新
public class UserController {
@Value("${user.max-login-attempts:5}")
private int maxLoginAttempts; // 从Nacos配置中心读取
}
在Nacos控制台修改user-service.properties,服务秒级生效。再也不用求运维大哥半夜上线了!
网关:Zuul太重,Gateway真轻量
我们试过Zuul,但内存占用高得离谱。换成Spring Cloud Gateway后,QPS提升3倍,配置也直观:
spring:
cloud:
gateway:
routes:
- id: user_route
uri: lb://user-service # lb表示负载均衡
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1 # 去掉/api前缀
配合Sentinel做限流,防止单个服务拖垮整个系统:
// 限流规则:user-service每秒最多100个请求
GatewayRuleManager.loadRules(Set.of(
new GatewayFlowRule("user_route")
.setCount(100)
.setIntervalSec(1)
));
数据一致性:Saga模式救我狗命
最头疼的是下单扣库存。传统方案用分布式事务(2PC),但性能差到哭。我们用Saga模式:
- 创建订单(状态=待支付)
- 调用库存服务扣减
- 若扣减失败,发送“取消订单”消息
- 订单服务收到后,把状态改为“已取消”
用RocketMQ保证消息可靠:
// order-service中
@Transactional
public void createOrder(Order order) {
orderRepository.save(order);
// 发送“扣库存”消息
rocketMQTemplate.convertAndSend("inventory-topic",
new InventoryDeductCmd(order.getSkuId(), order.getCount()));
}
// 监听库存扣减结果
@RocketMQMessageListener(topic = "inventory-result")
public class InventoryResultConsumer implements RocketMQListener<InventoryResult> {
public void onMessage(InventoryResult result) {
if (!result.isSuccess()) {
// 发送补偿消息:取消订单
rocketMQTemplate.convertAndSend("order-cancel-topic", result.getOrderId());
}
}
}
线上事故复盘:
有次MQ集群故障,补偿消息丢失,导致库存超卖。后来加了定时对账任务:每天凌晨扫描“待支付”订单,检查库存是否一致。技术没有银弹,兜底方案必须有!
Python能掺和进来吗?
当然可以!我们有个AI推荐模块用Python写的(TensorFlow模型),通过gRPC暴露服务:
# ai-recommendation/main.py
class RecommendationService(recommend_pb2_grpc.RecommendServiceServicer):
def GetRecommendations(self, request, context):
# 调用模型...
return recommend_pb2.RecommendResponse(items=items)
# 启动gRPC服务器
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
recommend_pb2_grpc.add_RecommendServiceServicer_to_server(RecommendationService(), server)
server.add_insecure_port('[::]:50051')
server.start()
Java服务用grpc-spring-boot-starter调用:
@GrpcClient("ai-recommendation")
private RecommendServiceGrpc.RecommendServiceBlockingStub aiStub;
public List<Item> getRecommendations(String userId) {
RecommendResponse response = aiStub.getRecommendations(
RecommendRequest.newBuilder().setUserId(userId).build()
);
return response.getItemsList();
}
关键点:gRPC比HTTP快3-5倍,特别适合内部高频调用。但调试麻烦,建议用BloomRPC这类GUI工具。
运维经验:监控不上,迟早火葬场
微服务不监控等于裸奔。我们搭了三件套:
- Prometheus + Grafana:监控JVM、HTTP状态码、接口延迟
- SkyWalking:分布式链路追踪,定位慢请求
- ELK:集中日志,关键词告警(比如"OutOfMemoryError")
Grafana看板截图(文字描述):
[服务健康度]
user-service: ✅ (99.95% uptime)
order-service: ⚠️ (5xx errors ↑ 2%)
[接口延迟 P99]
/createOrder: 120ms
/getUser: 45ms
上周五晚上,order-service延迟突然飙升到2s。通过SkyWalking发现是数据库连接池耗尽,根源竟是新同事写了段没关ResultSet的代码... 当时真的想砸键盘,但深吸一口气,默默加了HikariCP的泄漏检测:
spring:
datasource:
hikari:
leak-detection-threshold: 60000 # 60秒未归还连接就报警
最后几句人话
Spring Cloud不是银弹,微服务也不是。如果业务简单,单体应用+模块化可能更香。但一旦决定拆,记住:
- 先拆业务,再拆技术
- 监控和日志比代码更重要
- 永远给意外留后路(补偿、降级、熔断)
现在我的微服务集群跑在K8s上,每天处理20万订单。虽然头发又少了两撮,但看到系统稳稳扛住流量高峰,凌晨三点的泡面都格外香。
对了,文中的代码我都整理到GitHub了(搜“spring-cloud-practice-old-coder”),欢迎Star——毕竟35岁老码农也要靠Star续命啊!
彩蛋:高频面试题
Q:Eureka和Zookeeper区别?
A:Eureka AP(高可用优先),ZK CP(强一致优先)。微服务要的是可用性,挂一个实例不能让整个注册中心瘫痪。Q:Feign底层用什么?
A:默认是HttpURLConnection,但可替换为OkHttp或Apache HttpClient(记得加连接池!)Q:配置中心怎么保证安全?
A:Nacos支持命名空间隔离 + LDAP认证,敏感配置(如DB密码)用Jasypt加密。
写完这篇,天快亮了。关电脑前看了眼招聘APP——嗯,微服务经验果然还是香饽饽。各位,保重身体,代码无Bug!

评论 0