Spring特定方法调用外部API报429 Too Many Requests排查
问题根因
从代码和报错表现看,问题由三个因素共同导致:
- Controller层HTTP方法与参数注解不匹配,触发隐式多发请求
你给/employees/list接口标注了@GetMapping,却用@RequestBody接收参数。HTTP 规范本身不建议GET请求携带请求体,不少HTTP客户端、Web代理、甚至Spring的默认请求处理链路遇到这类写法时,会自动触发多次预检请求、请求重发逻辑——表面上你只发了一次接口调用,实际可能已经向外部API发送了2-3次请求,直接撞上限流阈值。这也是为什么其他接口正常、唯独这个接口报429的核心原因:其他接口要么是POST请求搭配@RequestBody,要么是GET请求用查询参数/路径变量传参,不存在这个兼容性问题。 - 公开测试API本身限流阈值极低
你调用的测试用途员工API本身限流规则非常严格,单IP短时间内请求数达到2-3次就会返回429,连续触发还会临时封禁IP几分钟,本身就不适合高频实时调用。 - 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
相关产品推荐
相关产品推荐

