You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring特定方法调用外部API报429 Too Many Requests排查

问题根因

从代码和报错表现看,问题由三个因素共同导致:

  1. Controller层HTTP方法与参数注解不匹配,触发隐式多发请求
    你给/employees/list接口标注了@GetMapping,却用@RequestBody接收参数。HTTP 规范本身不建议GET请求携带请求体,不少HTTP客户端、Web代理、甚至Spring的默认请求处理链路遇到这类写法时,会自动触发多次预检请求、请求重发逻辑——表面上你只发了一次接口调用,实际可能已经向外部API发送了2-3次请求,直接撞上限流阈值。这也是为什么其他接口正常、唯独这个接口报429的核心原因:其他接口要么是POST请求搭配@RequestBody,要么是GET请求用查询参数/路径变量传参,不存在这个兼容性问题。
  2. 公开测试API本身限流阈值极低
    你调用的测试用途员工API本身限流规则非常严格,单IP短时间内请求数达到2-3次就会返回429,连续触发还会临时封禁IP几分钟,本身就不适合高频实时调用。
  3. Service层无缓存设计,请求完全穿透到外部API
    当前逻辑下,每收到一次用户请求,都会实时调用一次外部API拉取全量员工列表,没有任何缓存层拦截,调用频率完全和用户请求频率绑定,极容易触发限流。
修复方案
  • 第一步先修正Controller的注解错误:如果需要用@RequestBody接收JSON格式的ID列表,将接口的@GetMapping改为@PostMapping,从根源避免GET请求带请求体导致的隐式重复请求问题。修正后代码:
@PostMapping(value = "/employees/list")
public ResponseEntity<List<EmployeeDTO>> getEmployeesByIdList(@RequestBody IDS idList) {
    return new ResponseEntity<>(this.employeeService.getListEmployeesByIds(idList.getIds()), HttpStatus.OK);
}
  • 第二步给外部API返回结果增加本地缓存:员工全量列表属于低频变动数据,没必要每次用户请求都实时拉取。可以根据业务场景设置5-10分钟的缓存过期时间,缓存有效期内直接使用本地存储的数据做过滤,不再请求外部API,从根源减少外部接口调用量。简单的本地缓存实现示例:
// 简易本地缓存,生产环境建议替换为Spring Cache + Caffeine
private List<Employee> employeeCache;
private LocalDateTime cacheExpireTime;
private static final long CACHE_TTL = 10; // 缓存有效期10分钟

public List<EmployeeDTO> getListEmployeesByIds(int[] ids) {
    List<Employee> employees;
    // 缓存未命中或已过期时才调用外部API
    if (employeeCache == null || LocalDateTime.now().isAfter(cacheExpireTime)) {
        EmployeeJSON empJSON = restTemplate.getForObject(EndpointConfig.employeeUrl, EmployeeJSON.class);
        employees = Arrays.asList(empJSON.getData());
        employeeCache = employees;
        cacheExpireTime = LocalDateTime.now().plusMinutes(CACHE_TTL);
    } else {
        employees = employeeCache;
    }
    
    List<EmployeeDTO> responseList = new ArrayList<>();
    for (int id : ids) {
        employees.stream()
                .filter(emp -> emp.getId() == id)
                .findFirst()
                .ifPresent(emp -> responseList.add(new EmployeeDTO(emp)));
    }
    return responseList;
}
  • 可选优化:给RestTemplate增加容错机制,遇到429报错时读取响应头的Retry-After字段,等待对应时间后重试,不要直接将异常抛给前端;同时增加并发控制,避免同一时刻多个请求同时穿透到外部API。
验证提示

如果调整代码后仍然报429,可以先用curl/Postman直接调用外部员工API,确认当前IP是否处于临时封禁期——这类测试API的临时封禁一般10-15分钟会自动解除,封禁期间哪怕只发一次请求也会返回429。

内容的提问来源于stack exchange,提问作者hapless_automaton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:06:48