最近公司上线的新版本订单服务,刚一发布就出了问题。用户提交订单时卡顿严重,后台日志里全是超时告警。排查一圈才发现,不是数据库扛不住,也不是网络有问题,而是微服务之间的调用链太臃肿,一个小请求绕了五个服务才完成。这种情况其实很常见,微服务拆得细了,性能反而可能下降。
别让服务“对话”拖垮系统
微服务之间频繁通信就像同事之间反复确认细节——效率低还容易出错。比如一个查询用户信息的请求,先找认证服务验权限,再调用户中心拿基础资料,接着去积分系统查等级,最后还得问消息服务有没有未读通知。每个环节都走远程调用,延迟叠加起来,响应时间自然上去了。
这时候可以考虑聚合接口。在网关层或专门的编排服务里,把多个并行请求合并处理。比如用 CompletableFuture 实现异步并发调用:
CompletableFuture<UserInfo> userFuture = userService.getUser(id);
CompletableFuture<PointInfo> pointFuture = pointService.getPoints(id);
CompletableFuture<MessageInfo> msgFuture = messageService.getUnread(id);
CompletableFuture.allOf(userFuture, pointFuture, msgFuture).join();
UserInfo user = userFuture.get();
PointInfo points = pointFuture.get();
MessageInfo msgs = msgFuture.get();
这样原本串行 600ms 的操作,能压到 200ms 左右。
缓存不是万能药,但用对地方真香
有个项目里,商品详情页每次打开都要重新计算库存可用量,调三次规则引擎、两次价格服务。后来我们在 Redis 加了一层本地缓存(Caffeine),设置 30 秒过期,同一商品短时间内被频繁访问时,直接从内存返回结果。QPS 从 800 直接干到 4500,服务器资源占用反而降了。
不过要注意缓存一致性。比如订单状态更新后,得通过消息队列及时清理相关缓存,不然用户看到的还是旧数据。
限流降级,保护自己也保护别人
去年双十一前压测,发现支付回调服务一旦被打满,整个交易链路都会堵住。后来上了 Sentinel 做熔断降级,当异常比例超过 50% 时,自动切换到备用逻辑:先记录原始回调数据到 Kafka,等系统恢复后再补单。虽然实时性差一点,但至少不会雪崩。
配置也很简单:
FlowRule rule = new FlowRule();
rule.setResource("payCallback");
rule.setCount(100); // 每秒最多100次
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
FlowRuleManager.loadRules(Collections.singletonList(rule));
这招在高并发场景下特别管用,相当于给服务加了个“安全阀”。
日志和监控得跟上
光优化不观测等于盲人摸象。我们给每个关键服务都加上了 Micrometer + Prometheus 监控,接口耗时、线程池状态、GC 频率一目了然。有一次发现某个服务老是 Full GC,查下来是 JSON 反序列化时生成了大量临时对象,改用流式解析后内存平稳多了。
调优这事没有银弹,得根据实际流量特征、业务场景一步步试。有时候改一行代码提升 30%,有时候折腾半天只快了几毫秒。但只要持续盯着数据,总能找到突破口。