县城码农的微服务突围战:Spring Cloud从零搭建实录

数据迁移苦工
2025-12-26 03:19
阅读 736

上周五晚上十一点,我戴着耳机听着周杰伦的老歌,盯着屏幕上疯狂报错的 Service Unavailable,差点把键盘砸了。
这不是第一次线上崩了——但这次不一样。因为明天就是跳槽面试的日子。

我是那种典型的“小镇做题家”:大学在二本院校啃《算法导论》,毕业后没去大厂,而是回了老家县城,在一家做电商数据爬虫的小公司远程办公。白天写 Java 爬虫调度系统,晚上刷 LeetCode 准备跳槽。我们老板总说“系统要高可用”,可代码还是单体架构,数据库一抖,全站瘫痪。上个月双11,我们的爬虫平台因为某个模块 OOM,直接宕机三小时,产品经理在群里@我时语气都带着哭腔:“兄弟,用户看不到竞品价格了!”

忍无可忍,我决定搞微服务。不为别的,就为了简历上能写一句“主导微服务架构迁移”——毕竟现在连县城小厂招聘都要求“熟悉 Spring Cloud”了。


为什么是 Spring Cloud?

其实我也纠结过要不要上 Kubernetes 或者直接玩 Service Mesh,但现实很骨感:

  • 公司只有两个后端(包括我)
  • 运维?不存在的,上线靠 shell 脚本 + 手动 scp
  • 预算?老板说“能跑就行,别买云服务”

所以,稳定、轻量、Java 生态友好成了硬指标。Spring Cloud 完美契合——它基于 Spring Boot,我们现有项目几乎不用大改;Nacos 做注册中心比 Eureka 轻;OpenFeign 写远程调用比手撸 HTTP 简单一百倍。

最关键的是:它能让我的爬虫系统真正“解耦”

以前,我们的架构是这样的:

[爬虫调度] → [数据解析] → [存储入库] → [API接口]

一个环节卡住,全线阻塞。更离谱的是,爬虫任务状态和商品数据居然共用一张 MySQL 表!某次我误删了个测试任务,结果把生产商品数据也干掉了……(别问,问就是血泪史)

微服务化之后,我拆成了四个独立服务:

  • crawler-service:负责分布式爬虫任务分发
  • parser-service:HTML 解析 & 结构化
  • storage-service:写入 MongoDB 和 ES
  • gateway-service:统一 API 入口 + 鉴权

每个服务独立部署、独立扩缩容。就算爬虫炸了,用户还能查历史数据!


搭建过程:踩坑比写代码多

第一步:注册中心选型

一开始我试了 Eureka,但发现它不支持权重路由,而且配置麻烦。后来换成 Nacos,真香!启动一个 standalone 模式,5 分钟搞定:

# 下载 nacos-server
wget https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.tar.gz
tar -xzf nacos-server-2.2.3.tar.gz
cd nacos/bin
sh startup.sh -m standalone

然后在每个服务的 application.yml 里加两行:

spring:
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848

服务启动后自动注册,健康检查也自带。再也不用手动维护 IP 列表了!

第二步:服务间调用 —— 别再自己写 HttpClient 了!

以前调用解析服务,我是这样写的:

// 反面教材!别学我!
RestTemplate restTemplate = new RestTemplate();
String result = restTemplate.getForObject("http://192.168.1.100:8081/parse?url=xxx", String.class);

IP 写死、超时没处理、重试机制为零。现在用 OpenFeign,优雅到哭:

@FeignClient(name = "parser-service")
public interface ParserClient {
    @GetMapping("/parse")
    ParsedData parse(@RequestParam("url") String url);
}

注入就能用,底层自动负载均衡(Ribbon 已整合),还支持熔断(后面说)。

第三步:性能优化 —— 微服务不是银弹!

拆完服务后,QPS 反而下降了?查了监控才发现:服务调用链路太长,每次请求都要跨 3 个服务

我做了三件事:

  1. 本地缓存热点数据:比如商品类目信息,用 Caffeine 缓存,减少对 storage-service 的调用。
  2. 异步化非关键路径:爬虫完成后的日志上报、埋点统计,扔到 RabbitMQ 异步处理。
  3. 调优 Feign 超时:默认超时太短,爬虫慢的时候直接熔断。改成:
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 15000

第四步:熔断与降级 —— 别让一个服务拖垮全家

有次 parser-service 因为正则爆炸 CPU 100%,结果 crawler-service 线程池被占满,整个系统雪崩。

Sentinel!比 Hystrix 更轻量,还有可视化控制台:

@SentinelResource(value = "parseFallback", fallback = "fallbackParse")
public ParsedData parse(String url) {
    return parserClient.parse(url);
}

public ParsedData fallbackParse(String url, Throwable t) {
    log.warn("解析服务不可用,返回空数据", t);
    return ParsedData.empty();
}

配合 Sentinel Dashboard,可以动态设置 QPS 阈值、熔断策略。现在就算解析服务挂了,爬虫还能继续跑,只是不存结构化数据而已——总比全线崩溃强。


成果:从“救火队员”到“架构师”(自封的)

上线一个月,效果立竿见影:

指标 单体架构 微服务架构
平均响应时间 1200ms 420ms
故障恢复时间 30+ 分钟 < 2 分钟
爬虫任务成功率 87% 99.2%
我的加班次数 每周3次 近两周0次

最爽的是,上周面试官问我“你们怎么保证高可用”,我直接掏出架构图讲了半小时,最后他笑着说:“你这经验,比我们很多 senior 还扎实。”


给同样在县城奋斗的你几点建议

  1. 别盲目拆服务:先识别核心域。我们的爬虫调度和数据存储天然隔离,适合拆;但用户登录这种简单功能,暂时没必要。
  2. 监控必须跟上:至少要有 Spring Boot Admin 或 Prometheus + Grafana,不然等于盲人开车。
  3. Java 爬虫注意资源隔离:每个爬虫任务用单独线程池,避免一个站点反爬导致整个 crawler-service 挂掉。
  4. 别怕“小题大做”:在小公司搞微服务,很多人觉得“杀鸡用牛刀”。但正是这些实践,让你在跳槽时有底气谈架构、谈性能。

现在,我又戴上耳机,打开 IDEA,准备给 gateway 加个限流规则。窗外是县城安静的夜,而我的服务正在云端平稳运行。

有时候我在想,所谓“小镇做题家”的出路,或许不是逃离小城,而是用技术把小城的工作,做到一线大厂的水准。

毕竟,代码不分城乡,bug 不看户口。只要服务稳、QPS 高、简历硬,下一份 offer,可能就在北上广深等着我。

(完)

注:本文所有配置均已在生产环境验证,GitHub 有简化版 demo(私信我发你链接)。如果你也在县城远程办公,欢迎交流——咱们抱团取暖,一起卷出县城!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝