要理解Feign首次调用慢的原因,首先需要梳理清楚远程调用的完整流程,这涉及到注册中心、负载均衡组件与Feign客户端之间的协作关系。在微服务架构中,服务实例会注册到Eureka或Nacos等注册中心。Feign本身并不直接处理服务发现与负载,这部分功能由Ribbon承担。Ribbon会从注册中心获取服务提供者的列表,并将其缓存在本地。随后,FeignClient在进行调用时,实际上是通过Ribbon选择一个可用的服务实例发起HTTP请求。
Ribbon的负载均衡机制
核心在于理解Ribbon如何获取并维护服务列表。下图展示了其核心工作原理:

在 RibbonClientConfiguration 配置类中,核心是 ILoadBalancer 接口的实现。该接口定义了对服务实例进行操作的关键方法,包括:添加服务器、选择服务器、标记服务下线、获取所有服务列表、获取健康服务列表等。

默认使用的负载均衡器是 ZoneAwareLoadBalancer。在其初始化过程中,会调用父类 DynamicServerListLoadBalancer 的 restOfInit 方法。其中有两个非常关键的方法:enableAndInitLearnNewServersFeature 和 updateListOfServers。

在 enableAndInitLearnNewServersFeature 方法内部,会启动一个服务列表更新器(ServerListUpdater):
LOGGER.info(“Using serverListUpdater {}”, serverListUpdater.getClass().getSimpleName());
serverListUpdater.start(updateAction);
ServerListUpdater.start 方法的实现,本质上是通过一个后台线程定时去拉取最新的服务列表。这就是Ribbon维护本地服务缓存的方式。

Ribbon负载均衡策略简介
除了服务列表获取,负载均衡策略也是影响调用的因素。Ribbon内置了多种策略:
- RoundRobinRule:轮询策略。
- WeightedResponseTimeRule:权重响应时间策略,响应时间越短的服务实例权重越高。
- RandomRule:随机策略。
- BestAvailableRule:最小并发数策略。
- RetryRule:重试策略。
- AvailabilityFilteringRule:可用性敏感策略,过滤掉不健康的实例。
- ZoneAvoidanceRule:区域感知策略(默认)。
关于如何自定义负载均衡策略,本文不做展开。
Ribbon的延迟加载与“饥饿加载”模式
问题的根源在于,Ribbon对于Feign客户端的负载均衡器是在服务启动后,第一次发生实际调用时才进行创建的。 因此,第一次调用不仅包含HTTP请求的网络耗时,还需要加上LoadBalancer、Feign Client等组件的初始化时间,从而导致明显延迟。
以下是一个简单的服务间调用示例(System服务调用System2服务):
@GetMapping(“/requestSystem2Api”)
public String requestSystem2Api(){
long startTime = System.currentTimeMillis();
R<String> stringR = iTestServiceClient.testRequestMethod();
if (null !=stringR){
log.info(“接口返回:”+stringR.getMsg());
}
long needTime = System.currentTimeMillis() - startTime;
log.info(“接口调用需要的时间:”+needTime);
return “”;
}
从调用日志可以清晰看到,第一次调用时,DynamicServerListLoadBalancer 会进行Feign客户端的负载均衡初始化,耗时较长;第二次调用则直接使用已初始化的组件,速度很快。

开启Ribbon饥饿加载
为了解决首次调用慢的问题,Spring Cloud Ribbon 提供了 ribbon.eager-load 配置(俗称“饥饿加载”模式)。该模式会在应用启动时,预先初始化指定服务的Ribbon客户端,从而避免首次调用的初始化开销。
在 application.yml 中的配置示例如下:
ribbon:
nacos:
enabled: true
eager-load:
enabled: true # 开启饥饿加载模式
clients: Lxlxxx-system2 # 指定需要预加载的服务名(可配置多个)
ReadTimeout: 10000
ConnectTimeout: 10000
开启此配置后,在项目启动日志中可以看到,指定的服务(如Lxlxxx-system2)的Ribbon客户端已被加载,从而有效避免了首次请求的超时问题。

总结
Ribbon的饥饿加载模式,本质上是一种 “客户端负载预热” 操作。它在应用启动阶段提前完成负载均衡客户端的初始化,将初始化时间从首次调用的关键路径中移出。如果你的微服务之间存在调用逻辑复杂、处理数据量大或依赖组件多的情况,导致接口本身处理时间较长,再叠加上首次调用的初始化延迟,很容易触发超时。启用饥饿加载是解决此类问题的一个有效实践。