后端接口的 “流量整形”:请求合并与批处理优化

130 阅读4分钟

在高并发场景中,频繁的小请求可能成为系统瓶颈 —— 比如用户详情页需要调用用户服务、订单服务、优惠券服务等多个接口,每个接口单独请求会导致 “网络往返次数过多”“连接数耗尽” 等问题。请求合并与批处理技术,通过将多个小请求合并为一个批量请求,减少网络开销和资源消耗,提升系统整体吞吐量。

请求合并的核心价值

请求合并的核心是 “减少交互次数”,带来的优势:

  • 降低网络延迟:一次网络往返处理多个请求,减少 RTT(往返时间)累积
  • 减少资源占用:合并后的请求只需建立一次连接,降低服务器的连接数和线程消耗
  • 提升下游服务性能:下游服务处理批量请求的效率通常高于多次单条处理(如数据库批量查询比多次单查更快)

主流实现方案

1. 本地请求合并:同一服务内的批量处理

对于同一服务内的多次调用(如循环查询用户信息),通过批量接口替代单条查询。

反例(低效)

// 循环调用单条查询,产生N次数据库访问
List<Long> userIds = Arrays.asList(1L, 2L, 3L);
List<User> users = new ArrayList<>();
for (Long userId : userIds) {
    User user = userMapper.getById(userId); // 单条查询
    users.add(user);
}

正例(优化)

// 批量查询,1次数据库访问
List<Long> userIds = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.batchGetByIds(userIds); // 批量查询SQL:SELECT * FROM user WHERE id IN (1,2,3)

接口设计:提供批量查询接口,如POST /users/batch-get,接收 ID 列表,返回用户列表:

// 请求
{ "ids": [1,2,3] }

// 响应
{
  "code": 200,
  "data": [
    {"id":1, "name":"张三"},
    {"id":2, "name":"李四"}
  ]
}

2. 分布式请求合并:基于缓存的延迟合并

在分布式系统中,多个请求同时查询同一批资源(如商品详情),可通过缓存 + 延迟合并减少下游服务压力。

实现逻辑(以商品服务为例)

  1. 前端同时请求商品 1001、1002、1003 的详情

  2. 网关或 API 层收集一定时间内(如 10ms)的相同类型请求

  3. 将多个单 ID 请求合并为一个批量请求,调用商品服务的/products/batch-get接口

  4. 接收批量响应后,拆分结果返回给各请求方

代码简化示例

// 基于Guava的LoadingCache实现本地缓存+合并查询
private LoadingCache<Long, Product> productCache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .build(new CacheLoader<Long, Product>() {
        @Override
        public Product load(Long productId) {
            return productService.getById(productId); // 单条查询( fallback )
        }

        // 重写批量加载方法
        @Override
        public Map<Long, Product> loadAll(Iterable<? extends Long> productIds) {
            return productService.batchGetByIds(productIds); // 批量查询
        }
    });

// 获取商品详情(自动触发批量合并)
public Product getProduct(Long productId) {
    try {
        return productCache.get(productId);
    } catch (ExecutionException e) {
        throw new RuntimeException("获取商品失败", e);
    }
}

适用场景:高频、低延迟要求的查询接口(如商品详情、用户信息),且 ID 分散度不高。

批处理的注意事项

1. 批量大小控制

  • 单次批量请求的 ID 数量不宜过多(如控制在 100 以内),避免下游服务处理超时
  • 超过上限时自动分片(如 200 个 ID 分 2 次请求)

2. 超时与重试策略

  • 批量接口需设置合理的超时时间(比单条接口稍长,但不宜过长)
  • 重试需谨慎,避免批量失败后重试导致更大压力,建议部分失败时只重试失败的 ID

3. 一致性保证

  • 批量查询应保证与单条查询结果一致(如过滤条件、权限校验)
  • 批量更新 / 删除需考虑事务一致性,建议通过分布式事务或补偿机制确保数据正确

避坑指南

  • 避免 “为合并而合并”:低频接口或单次请求只查询 1 条数据的场景,合并反而增加复杂度

  • 注意缓存一致性:分布式合并查询依赖缓存时,需确保缓存更新机制可靠,避免返回旧数据

  • 监控批量接口性能:重点监控批量接口的响应时间、错误率,与单条接口对比,确保优化效果

请求合并与批处理不是 “银弹”,但在高频小请求场景中,它能像 “流量整形” 一样,将零散的请求汇聚成有序的 “数据流”,大幅降低系统的资源消耗 —— 这正是后端优化 “四两拨千斤” 的智慧所在。