服务架构升级记录

引言

大家好,最近我们团队刚完成了一次比较大的服务架构升级,前后折腾了将近两个月,总算稳定下来了。今天正好有空,跟大家聊聊这次升级的一些经验和教训,希望能给正在考虑做架构升级的朋友们一点参考。

其实做这个决定并不容易,毕竟线上服务跑得好好的,谁也不想没事找事。但随着业务量增长,原来的架构确实开始显现出一些疲态,正好趁这次机会一次性解决掉这些隐患。

旧架构的问题与挑战

先说说为什么要升级。我们之前的系统是三年前设计的,那时候日活用户才几万请求量,现在已经翻了几十倍。刚开始还能勉强支撑,后来问题就慢慢暴露出来了。

首先最明显的就是扩展性问题。我们当时用的是单体应用加数据库读写分离的架构,所有的业务逻辑都堆在一个应用里。虽然做了主从分离,但随着请求量增加,数据库连接池经常打满,查询响应时间从原来的几十毫秒飙升到几百毫秒,用户体验明显变差。

其次是维护成本太高。因为所有代码都在一个项目里,团队十几个人同时开发,经常出现代码冲突。部署一次要整个系统一起发,任何一个模块有问题就得全量回滚,风险很大。

还有一个很头疼的问题是排查故障。因为所有服务混在一起,出了问题很难定位到底是哪个环节的锅,经常要翻半天的日志才能找到根因。

这些问题叠加在一起,让我们意识到是时候该动手了。

升级目标与技术选型

确定要升级后,我们先花了大概一周时间讨论升级目标。总结下来主要有这么几点:

1. 服务拆分:把大的单体应用拆成多个微服务,每个服务独立部署、独立扩展

2. 引入消息队列:实现服务间的异步解耦,减少同步调用带来的性能损耗

3. 数据库优化:引入分库分表,同时使用缓存层减轻数据库压力

4. 可观测性:建立完整的链路追踪和监控体系

技术选型方面,我们调研了业界主流的方案,最后决定用 Spring Cloud Gateway 做网关,Nacos 做服务注册与配置中心,RocketMQ 做消息队列,Redis 做缓存,MySQL 依然保留但做了分库分表改造。监控这块用了 SkyWalking 做链路追踪,Prometheus + Grafana 做指标监控。

这里有个小建议,技术选型的时候一定要结合团队的实际情况。我们团队对 Java 技术栈比较熟,所以优先考虑了 Spring Cloud 生态的方案。如果你团队擅长其他技术,选自己熟悉的就行,别盲目追新。

核心改造过程

改造过程是最耗时的,我们大概花了六周时间。这里重点聊聊几个关键的坑。

服务拆分策略

拆分是最核心的步骤。我们先把原来的单体应用按业务领域做了划分,拆成了用户服务、订单服务、商品服务、支付服务等七八个微服务。每个服务都有独立的数据库表,跨服务调用通过 HTTP 或 RPC 进行。

拆分过程中有个很重要的问题:原来很多业务逻辑是直接操作数据库的,拆分成多个服务后,数据一致性成了大问题。比如下单流程,涉及库存扣减、订单创建、支付处理等多个操作,原来在一个事务里能保证原子性,拆分后就麻烦多了。

我们的解决办法是引入分布式事务。不过说实话,分布式事务性能损耗比较大,对于一些非核心场景,我们改成了最终一致性方案,利用消息队列来做补偿。比如库存扣减成功后发送消息,订单服务消费消息后创建订单,如果订单创建失败就发消息触发库存回滚。

// 库存服务示例

@Transactional(rollbackFor = Exception.class)

public boolean deductStock(Long productId, Integer quantity) {

// 扣减库存

int result = stockMapper.deduct(productId, quantity);

if (result > 0) {

// 发送库存扣减成功消息

rocketMQTemplate.asyncSend("stock-deduct-topic",

new StockDeductMessage(productId, quantity),

(sendResult, e) -> {

if (e != null) {

log.error("消息发送失败,需要人工补偿");

}

});

return true;

}

return false;

}

缓存层设计

为了减轻数据库压力,我们引入了 Redis 做二级缓存。这里有个细节要特别注意:缓存和数据库的数据一致性。

我们采用了 Cache Aside 模式:读的时候先查缓存,缓存没有再查数据库,然后写入缓存;写的时候先更新数据库,再删除缓存(注意是删除而不是更新,避免并发时出现脏数据)。

不过实际生产中还是遇到了缓存穿透、击穿、雪崩的问题。我们加了布隆过滤器防止缓存穿透,对热点数据设置了永不过期防止击穿,Redis 集群做了哨兵模式防止雪崩。这些都是老生常谈的问题了,但真的遇到的时候才明白有多麻烦。

API 网关设计

微服务拆出来后,对外暴露的接口需要统一管理。我们用 Spring Cloud Gateway 做了网关层,主要实现了以下功能:

- 路由转发:根据请求路径转发到对应的微服务

- 认证授权:统一处理登录态校验

- 限流熔断:防止突发流量打垮下游服务

- 日志记录:统一记录访问日志

spring:

cloud:

gateway:

routes:

- id: user-service

uri: lb://user-service

predicates:

- Path=/api/user/**

filters:

- StripPrefix=1

- RequestRateLimiter=redis-rate-limiter

- id: order-service

uri: lb://order-service

predicates:

- Path=/api/order/**

filters:

- StripPrefix=1

性能优化与监控

服务拆分后,我们花了大约两周时间做性能优化和监控体系建设。

性能优化

主要做了几件事:

1. SQL 优化:通过慢查询日志分析,加索引、优化查询语句,有些复杂的联表查询改成了多次单表查询

2. 连接池调优:根据实际业务量调整了数据库连接池和 HTTP 连接池的大小

3. 异步处理:非核心流程改成异步执行,比如消息通知、报表生成等

4. 资源预热:服务启动后自动预热,加载热点数据到缓存

监控体系

监控这块我们下了不少功夫,毕竟微服务出问题定位起来比单体应用困难多了。

- 链路追踪:用 SkyWalking 实现了全链路追踪,任何一次请求都能看到完整的调用链

- 指标监控:用 Prometheus 采集业务指标,Grafana 做可视化展示

- 日志聚合:所有服务日志统一收集到 ELK,方便搜索和排查

- 告警配置:设置了多级别告警,短信、邮件、钉钉机器人都有

有次线上出了一个奇怪的 bug,用户下单成功后状态显示异常亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏亏