微服务架构设计实战:从单体到分布式
“简历上写着‘熟悉微服务’,结果连注册中心都配错了?”
——这是我入职新公司第二周在内部 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,而是看谁最能“苟住”。
我们定了几个原则:
- 渐进式迁移:不能停机,不能重写,边跑边拆。
- 团队熟悉度优先:后端都是 Java 老兵,别整 Rust 或 Go 把他们吓跑。
- 可观测性必须强:不能再靠 log 手动 grep 排查问题。
- 成本可控:老板明确说:“别搞太复杂,咱不是阿里。”
最终我们敲定了这套组合拳:
| 组件 | 选型 | 理由 |
|---|---|---|
| 服务治理 | 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