微服务架构设计实战:从单体到分布式

CtrlV艺术家
2025-12-18 18:46
阅读 2027

“简历上写着‘熟悉微服务’,结果连注册中心都配错了?”
——这是我入职新公司第二周在内部 Slack 里被@时的内心 OS。

大家好,我是小 Z,阿里前 P7 前端工程师(对,你没看错,前端写微服务文章),刚跳槽到一家中型电商公司两个月。虽然 title 是前端,但在这儿我被迫成了“全栈胶水工程师”——后端缺人、运维躺平、产品经理天天催上线,最后锅全甩给我:“你不是大厂来的吗?这点事搞不定?”

说实话,去年双11我在阿里主要负责的是前端性能优化和 SSR 架构,微服务?那是后端大佬们的事。但现实很骨感:新公司还在用一个十年前的老 Spring Boot 单体应用,数据库锁表、发布要停机、改个文案都要 QA 跑全量回归……上周五晚上 10 点,测试群里又炸了:“订单创建失败!错误码 500!” 我点开日志一看:

Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.

好家伙,数据库连接池直接干爆了。那一刻我真想砸电脑——不是因为 Bug,是因为这系统根本扛不住流量稍微一涨。领导拍我肩膀:“小 Z 啊,你不是写过简历说‘有高并发系统经验’吗?能不能把架构重构一下?”

得,又被简历坑了。

为什么非拆不可?

其实我不是没劝过团队保持单体。毕竟微服务不是银弹,拆不好就是“分布式单体”+“运维地狱”。但现实是:

  • 业务耦合严重:用户、商品、订单、支付全在一个 jar 包里,改个优惠券逻辑要测整个下单链路。
  • 部署效率低:一个小功能上线要全员回归,QA 已经对我拉黑三天了。
  • 技术栈冻结:因为怕兼容问题,Java 版本卡死在 8,Spring Boot 还是 2.1.x。
  • 扩展性为零:大促时只能整体扩容,但 80% 的流量只集中在订单模块。

于是,我拉着后端老哥、DBA 和运维开了个“深夜烧烤局”(其实是线上会议,但点了外卖),决定:拆!必须拆!

技术选型:别被 hype 带跑偏

市面上微服务框架一堆:Spring Cloud Alibaba、Dubbo、gRPC、Kubernetes + Istio……但我深知,选型不是比谁更 fancy,而是看谁最能“苟住”。

我们定了几个原则:

  1. 渐进式迁移:不能停机,不能重写,边跑边拆。
  2. 团队熟悉度优先:后端都是 Java 老兵,别整 Rust 或 Go 把他们吓跑。
  3. 可观测性必须强:不能再靠 log 手动 grep 排查问题。
  4. 成本可控:老板明确说:“别搞太复杂,咱不是阿里。”

最终我们敲定了这套组合拳:

组件 选型 理由
服务治理 Nacos 阿里开源,集成简单,支持 AP/CP 切换
RPC 框架 Spring Cloud OpenFeign + Ribbon 团队熟悉,注解式调用,调试方便
网关 Spring Cloud Gateway 非阻塞、性能好,比 Zuul 2 强太多
链路追踪 SkyWalking 自动埋点,UI 直观,比 Zipkin 更友好
配置中心 Nacos Config 和注册中心一体化,减少运维负担
消息队列 RocketMQ 阿里系,事务消息支持完善

吐槽一句:本来想上 Consul,结果运维说“没文档看不懂”,行吧,Nacos 就 Nacos,至少我熟。

拆分策略:从“最痛”的地方下手

我们没搞什么“按业务域一刀切”,而是采用 绞杀者模式(Strangler Fig Pattern) ——新功能走微服务,旧功能逐步替换。

第一步,先拆出 用户服务商品服务。为啥?因为这两个模块改动少、接口清晰,且不涉及核心交易流程,试错成本低。

数据库怎么拆?

这是最头疼的。原单体用一个 MySQL 库,所有表都在一起。直接物理拆库?外键关联、事务一致性全崩。

我们的方案:

  • 读写分离先行:先用 ShardingSphere 做逻辑分片,不改代码。
  • 领域数据自治:每个微服务拥有自己的数据库,禁止跨库 JOIN。
  • 最终一致性靠 MQ:比如用户改昵称,发消息给商品服务更新缓存。

举个例子,原来查商品详情要 join user 表拿卖家信息,现在改成:

// 商品服务
public ProductDetailDTO getProductDetail(Long productId) {
    Product product = productMapper.selectById(productId);
    // 调用用户服务获取卖家信息(异步缓存兜底)
    SellerInfo seller = userClient.getSellerInfo(product.getSellerId());
    return buildDetail(product, seller);
}

这里有个坑:Feign 调用超时。初期设的默认 1s,结果用户服务慢一点就熔断。后来加了 Hystrix 降级 + 缓存兜底:

# application.yml
feign:
  client:
    config:
      default:
        connectTimeout: 2000
        readTimeout: 5000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 6000

网关与鉴权:别让安全成短板

单体时代,鉴权在 Controller 层写个 @PreAuthorize 就完事。微服务后,每个服务都要验 token?那不得累死。

我们把鉴权前置到 Gateway 层:

  • JWT 解析 + 权限校验统一在网关做
  • 透传用户 ID 到下游服务(通过 Header)
  • 敏感操作(如支付)再做二次校验

配置示例:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("order-service", r -> r.path("/api/order/**")
            .filters(f -> f.stripPrefix(1)
                .requestHeaderToRequestUri("X-User-Id", "/user/{userId}"))
            .uri("lb://order-service"))
        .build();
}

这样下游服务就不用关心“你是谁”,只处理“你要干啥”。运维还夸我:“终于不用每个服务配 OAuth2 了!”

运维视角:没有可观测性,微服务就是灾难

上线第一周,订单服务突然延迟飙升。单体时代看一个日监就行,现在 10 个服务互相调用,根本不知道瓶颈在哪。

还好提前上了 SkyWalking

  • 自动采集服务拓扑
  • 慢 SQL 自动标记
  • 异常堆栈直接关联 Trace ID

那天我点开 UI,一眼看到:商品服务 → 用户服务 调用耗时 2.3s。点进去发现是 Redis 缓存穿透,立刻加布隆过滤器搞定。要是没链路追踪,估计得通宵查日志。

顺便安利一句:日志一定要带 Trace ID!我们在 MDC 里注入:

@Slf4j
@Component
public class TraceFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
        String traceId = ((HttpServletRequest) request).getHeader("sw8");
        if (traceId != null) {
            MDC.put("traceId", traceId.substring(0, Math.min(16, traceId.length())));
        }
        chain.doFilter(request, response);
        MDC.clear();
    }
}

现在查日志只要 grep "traceId=abc123",全链路一目了然。

真实效果:不只是技术爽,更是业务爽

重构花了 6 周(期间熬了 3 个通宵,感谢 Red Bull),成果如下:

指标 拆分前 拆分后 提升
部署频率 1次/周 10+次/天 70x
平均响应时间 850ms 210ms 75%↓
故障隔离 全站挂 单服务降级 可用性↑
新人上手 2周 2天 效率↑

最爽的是上周大促预演:订单服务 CPU 打满,我们直接单独扩容它,其他服务纹丝不动。产品经理都惊了:“你们这次居然没崩?”

写在最后:微服务不是终点,而是起点

回头看,这次重构最大的收获不是技术本身,而是团队协作方式的改变

  • 后端开始写 OpenAPI 文档(以前靠口头传参)
  • 前端用 Mock Server 自测,不再等后端联调
  • 运维主动问:“要不要上 Kubernetes?”

至于我?简历上终于可以写“主导微服务架构落地”了(笑)。不过说真的,如果你现在还在单体,别盲目跟风拆。微服务解决的是组织问题,不是技术问题。我们能成功,是因为团队愿意改、老板敢投入、业务允许试错。

最后送大家一句我在阿里学到的话:“架构不是设计出来的,是演化出来的。”

所以,别怕脏,别怕乱,先跑起来,再优化。毕竟——
Deadline 才是第一生产力,不是吗?


P.S. 如果你也正在经历“被简历反噬”的痛苦,欢迎留言交流。顺便,我们公司在招后端,要求不高:会写 Java,不怕加班,能接受我这个“前端架构师”指手画脚就行 😅

评论 0

最热最新
暂无评论
CtrlV艺术家Lv.1
0
影响力
0
文章
0
粉丝