Java 服务调用下游接口注意点

目录

  • [1. 必须设置超时](#1. 必须设置超时)
  • [2. 重试策略,不能无脑重试](#2. 重试策略,不能无脑重试)
  • [3. 熔断、降级、隔离](#3. 熔断、降级、隔离)
  • [4. 限流](#4. 限流)
  • [5. 异常处理,区分不同失败类型](#5. 异常处理,区分不同失败类型)
  • [6. 请求参数与响应处理](#6. 请求参数与响应处理)
  • [7. 线程池注意](#7. 线程池注意)
  • [8. 超时时间的设计,链路整体考虑](#8. 超时时间的设计,链路整体考虑)
  • [9. 资源与连接池(HTTP 客户端)](#9. 资源与连接池(HTTP 客户端))
  • [10. 业务层面:幂等、数据一致性](#10. 业务层面:幂等、数据一致性)
  • [11. 监控告警](#11. 监控告警)
  • 面试口述版本
  • 高频追问

1. 必须设置超时(重中之重,第一优先级)

问题

不设置超时,线程会无限阻塞等待下游返回,线程池线程被占住不释放,最终线程池打满、队列积压,整个服务雪崩。这是线上最常见故障!

实践

不管是 RestTemplate、OkHttp、HttpClient、Dubbo,必须配置连接超时、读取超时。

  • connectTimeout:建立网络连接超时
  • readTimeout:接口响应读取超时(业务接口 RT 上限)

示例:普通业务接口,connect 2s,read 3‑5s;大文件导出类接口 read 适当放大。

超时不是越大越好,下游慢,我们及时失败释放线程,保护自己。

面试一句话:调用下游一定要设置超时,防止线程无限阻塞,耗尽线程池。

2. 重试策略,不能无脑重试

什么场景可以重试:幂等接口;查询接口。

不能重试:非幂等写接口(下单、扣款),重试会重复创建订单、重复扣款。

重试注意点

  • 只对特定异常重试:网络异常、连接超时;业务返回业务异常不要重试。比如下游返回参数错误,再重试多少次都失败。
  • 一定要加退避策略(间隔时间),不要立刻疯狂重试,避免把下游打垮。
  • 设置最大重试次数,不能无限重试。
  • 重试带来的负载要算到下游 QPS 评估。

Spring Retry / Resilience4j / Dubbo 自带重试都可以实现。金融系统写接口优先关闭自动重试。

3. 熔断、降级、隔离(Resilience4j/Sentinel)

下游故障,不能把故障传导到本服务。

熔断 CircuitBreaker:下游大量失败,打开熔断,直接本地快速失败,不去调用下游,保护自己,也保护下游。

舱壁隔离 Bulkhead:调用下游的请求单独隔离线程池,下游慢,只占隔离池,不会占用业务核心线程池,不会影响别的业务。

很多人忽略:如果不做舱壁,下游全部卡死,自己业务线程池全部占满,其他无关接口全部挂掉。

汇丰金融项目很看重故障隔离,不能一个第三方挂掉把整个服务拖垮。

4. 限流

对下游做限流,控制发给下游最大 QPS,防止我方流量打垮下游。

自己服务入口也要限流,防止外部流量进来,大量调用下游把下游打崩。

5. 异常处理,区分不同失败类型

调用下游会出现多种失败,分开处理,不要一把全部 catch (Exception) 笼统处理:

  • 网络异常:连接失败、超时、socket 异常
  • HTTP 状态码:4xx(客户端错误,参数错)、5xx(下游服务报错)
  • 业务码:http200,但是 body 里面业务 code 代表业务失败
  • 返回体解析异常:返回 JSON 格式不对,解析报错

实践

  • 打印完整日志:请求参数、响应体、耗时、traceId,方便排查;
  • 不要吞异常,需要告警;
  • 区分是我方参数错误,还是下游故障。

:只判断 http 状态码 200 就认为成功,忽略业务 code。

6. 请求参数与响应处理

  • 请求体过大,注意序列化,避免大对象序列化耗 CPU;
  • 做好响应解析保护,不要直接强转,防止返回格式变更导致解析异常;
  • 大返回体,注意内存溢出风险;
  • 传递链路 traceId,方便全链路追踪。

7. 线程池注意(和前面线程池知识点联动)

高频考点:调用下游 IO 操作,会阻塞当前线程。

两种情况

  • 在同步接口里面调用下游:占用 web 容器线程(Tomcat 线程),下游慢会耗尽 tomcat 线程,接口全部卡死。
  • 在自定义异步线程池调用下游:占业务线程池线程,如果没有超时,线程池耗尽。

最佳实践

  • 调用下游必须设置超时;
  • 如果大量调用第三方,建议用单独隔离的线程池(舱壁),第三方故障不会污染核心业务线程池。

8. 超时时间的设计,链路整体考虑

链路总耗时 = 多个下游调用时间总和。

例如网关整体超时 10s,内部服务调用下游每个接口不能设置 8s,多个调用叠加直接超时。

每个下游接口超时要小于整体链路超时。

9. 资源与连接池(HTTP 客户端)

OkHttp、HttpClient 一定要配置连接池,复用 TCP 连接,不要每次请求新建连接。

设置最大连接数,避免创建大量 TCP 连接,出现 too many open files。

10. 业务层面:幂等、数据一致性

  • 调用写接口,下游要支持幂等,传入唯一业务 id,防止重试造成重复数据;
  • 调用失败,要考虑:回滚、补偿、本地记录调用日志,后续定时任务重试补偿;
  • 金融场景,不能简单调用失败就直接丢弃,要落地本地记录,异步补偿。

11. 监控告警

需要监控这些指标:

  • 下游调用 QPS
  • 调用耗时 p50/p95/p99
  • 失败率(超时、5xx、业务异常)
  • 熔断状态

失败率到达阈值要告警,不要等到业务报错才发现第三方挂了。

面试口述版本(B5 级别,可以直接复述)

调用下游接口,首先必须设置连接超时、读取超时,防止下游响应慢把我方线程无限阻塞,耗尽线程池或者 Tomcat 线程,造成服务雪崩。

然后是重试,查询类接口可以重试,写接口不能随便重试,需要下游支持幂等;重试要加退避,限制最大次数。

我们会使用 Sentinel 或者 Resilience4j 做熔断和舱壁隔离,把对下游的调用隔离在独立线程池,下游故障不会扩散到核心业务。

做好异常分类处理,区分网络异常、HTTP 状态码、业务错误码,打印完整请求响应日志,带上 traceId 方便排查。

HTTP 客户端要使用连接池复用 TCP 连接。同时评估整体链路超时,单个下游超时不能超过链路总时间。

金融写场景,调用失败不能直接丢弃,落库做补偿;对下游调用做监控,监控耗时、失败率,配置告警。

高频追问

Q:调用下游没设置超时,会发生什么?

线程会一直阻塞等待响应,线程不会释放。同步接口会耗尽 Tomcat 容器线程;异步任务会耗尽业务线程池。最终整个服务无法处理新请求,雪崩。

Q:熔断和舱壁的区别?

熔断:统计失败率,失败多了直接拒绝发起调用,保护下游和自己。

舱壁 Bulkhead:线程池隔离,限制调用该下游最多占用多少线程。哪怕下游全部卡死,最多耗尽隔离池的线程,不影响其他业务。两个组件配合使用。

Q:为什么写接口不能随便重试?

如果接口非幂等,重试会造成重复下单、重复扣款等脏数据;如果要重试,下游必须支持幂等,传入唯一业务标识。

相关推荐
许彰午1 小时前
08-条件拼接规则
java·低代码·架构
兔兔兔兔11 小时前
记录C++ 11
开发语言·c++
码匠许师傅1 小时前
【C++ 面试真题】32. 聊聊 C++ 的原子变量与无锁编程
java·c++·面试
撩得Android一次心动1 小时前
Kotlin 语言【知识点整理2】
android·开发语言·kotlin
不会就选b1 小时前
Linux之线程池(一)
java·开发语言
IT毕设实战小研1 小时前
基于大数据的二手车数据分析与预测
java·大数据·后端·爬虫·python·算法·课程设计
吠品1 小时前
OkHttp 多文件上传的一个实现
java·服务器·前端
牛油果子哥q2 小时前
C++序列式容器深度精讲:vector/list/deque底层实现、扩容原理、迭代器失效、性能对比、工程选型避坑
开发语言·c++·list
lingguangbuzhi2 小时前
【JAVA教程】java课程java学习编程
java·学习·教程