Spring Cloud从零开始:微服务入门指南
上周五晚上十点半,北京的晚高峰早就散了,我还在公司调一个诡异的超时问题。测试同学刚提了个P0级Bug:“用户搜索结果偶尔为空,刷新几次又好了”。这不就是典型的分布式系统间调用链路断裂嘛!我一边抓包一边骂自己:“早知道当初就该把那几个单体服务拆干净点。”
没错,我是百度干了两年搜索算法的工程师,日常写Python处理亿级query,但偏偏我们组有个“传统艺能”——后端服务全用Java写的。老板说:“算法也要懂工程闭环。”于是去年双11前,我被一脚踹进微服务改造项目,被迫重拾Java,从Spring Boot一路摸到Spring Cloud。今天这篇水文,既是复盘,也是给和我一样“半路出家”的兄弟们指个路。
为什么非得上微服务?
先别急着喷“微服务已死”。在百度这种量级的公司,哪怕是个内部工具,QPS上千都是常态。以前我们有个推荐配置管理后台,所有逻辑塞在一个Spring Boot应用里,每次改个字段都要全量发布,运维大哥看我的眼神都带着刀。更别说和其他团队联调时,接口一改,对方直接挂掉——典型的“你动我代码,我删你库”。
微服务的核心不是炫技,是解耦 + 弹性 + 可观测。尤其对我们做搜索的,召回、粗排、精排、打散……每个模块迭代节奏不同,拆开才能各自快跑。当然,代价也有:网络延迟、分布式事务、链路追踪……但比起半夜被PagerDuty叫醒修线上事故,这点痛算什么?
从单体到微服务:我的翻车实录
第一步永远是最痛的。我把那个臃肿的配置后台拆成三个服务:
user-service:管权限和登录config-service:存策略配置(比如某个频道要不要展示广告)search-proxy:对外API网关,聚合数据
理想很丰满,现实直接给我上了一课。第一次联调,config-service 返回500,日志里赫然一行:
java.net.UnknownHostException: config-service
笑死,服务发现没配! 在单体时代,所有Bean都在同一个JVM里,互相@Autowired就行。但拆成微服务后,服务A怎么找到服务B的IP和端口?这时候就得靠 Eureka 或 Nacos 这类注册中心。
关键配置:服务注册与发现
以Nacos为例(我们组选它是因为阿里系文档全,而且支持配置中心二合一),每个服务的bootstrap.yml要这么写:
spring:
application:
name: config-service # 服务名,其他服务靠这个找你
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos地址
config:
server-addr: 127.0.0.1:8848
file-extension: yaml
然后代码里调用其他服务,别再手写RestTemplate拼URL了!用 OpenFeign 声明式调用:
@FeignClient(name = "config-service") // 直接写服务名
public interface ConfigClient {
@GetMapping("/api/v1/config/{channelId}")
ConfigDTO getChannelConfig(@PathVariable("channelId") String channelId);
}
配上Ribbon负载均衡,连重试策略都能配置。省下的时间够我多喝两杯瑞幸续命。
性能优化:别让微服务变“龟速服务”
拆完服务,QPS从5000掉到2000,我差点被算法Leader提着领子问:“你是不是故意拖慢搜索响应?” 一查链路,好家伙,一次请求跨了6个服务,每个HTTP调用平均50ms,光网络开销就300ms!
微服务性能优化,核心就两点:减少调用次数 + 降低单次延迟。
异步化:非关键路径用
@Async或MQ解耦。比如用户修改配置后发个Kafka消息,让下游服务自己消费,主链路秒返回。缓存:高频读场景,Redis扛住90%流量。注意缓存穿透(用布隆过滤器)和雪崩(随机TTL)。
连接池:Feign底层用HttpClient,记得调大连接池:
feign: httpclient: max-connections: 200 max-connections-per-route: 50
附上我们压测后的对比数据(4核8G机器,模拟200并发):
| 方案 | 平均RT (ms) | 错误率 | CPU使用率 |
|---|---|---|---|
| 单体应用 | 45 | 0.1% | 65% |
| 初版微服务(同步调用) | 280 | 5.2% | 85% |
| 优化后(异步+缓存) | 68 | 0.3% | 70% |
看到没?微服务不是原罪,乱用才是。
区块链?别慌,这里只是彩蛋
标题里提到区块链,估计有人要骂我标题党。其实吧,在微服务里真用不上区块链(除非你搞Web3)。但有次产品非要加个“操作留痕不可篡改”功能,我灵机一动:用Merkle Tree记录配置变更哈希,虽然没上链,但原理类似——每次修改生成新哈希,旧记录无法抵赖。代码不过20行,却让产品觉得我们技术“高大上”。有时候,满足产品经理的虚荣心比优化TPS更重要(狗头保命)。
生产环境血泪教训
微服务上线后,我以为能躺平了。结果第二天凌晨三点,手机炸了:Hystrix熔断器触发,整个搜索接口挂了。原因?config-service 数据库慢查询拖垮线程池,导致调用方全部超时。
从此我牢记三条铁律:
必须设超时:Feign默认没超时,一旦下游卡住,你的线程池就爆了。
feign: client: config: default: connectTimeout: 1000 readTimeout: 3000熔断降级不能少:用Resilience4j(Hystrix已停更)配fallback,比如配置服务挂了就返回默认策略,总比白屏强。
日志+链路追踪是亲爹:集成SkyWalking或Zipkin,一个Trace ID串起所有服务日志。上次排查那个“偶发空结果”Bug,全靠它定位到是网关层漏传了用户ID。
代码人生:从CRUD到架构思维
写这篇文时,窗外中关村的夜还是亮的。回想一年前,我还在纠结Python装饰器怎么写,现在却在画微服务依赖图、调JVM参数。程序员的成长,往往始于一个不得不解决的线上Bug。
微服务不是银弹,但它是现代后端开发的必经之路。尤其在大厂,你躲得过K8s,躲不过服务治理。如果你也像我一样被“赶鸭子上架”,记住:别怕踩坑,每个报错都是经验值。实在搞不定?凌晨两点的Stack Overflow和一杯冰美式,永远是你最忠实的战友。
最后送大家一句我们组墙上贴的话:“Make it work, make it right, make it fast.” 先跑起来,再优化,别一上来就想造轮子。毕竟,Deadline才是第一生产力。
作者注:本文所有代码均在Spring Cloud Alibaba 2021.0.1.0 + Spring Boot 2.6.13验证。别问为什么不升级到最新版——上次升完版本,测试环境直接循环依赖启动失败,我可不想再熬通宵了。

评论 0