面试官:Feign第一次调用为什么会很慢?

本文探讨了Feign在首次调用时速度较慢的原因,主要涉及Ribbon的负载均衡和初始化过程。通过分析Ribbon的LoadBalancer、ServerListUpdater和各种负载策略,揭示了由于服务列表的获取和客户端创建导致的延迟。提出并解释了Ribbon-eager-load(饥饿加载)模式,即在项目启动时预先加载服务,以避免首次调用的延迟问题,提高了服务间调用的效率。

前言

首先要了解 Feign 是如何进行远程调用的,这里面包括,注册中心、负载均衡、FeignClient 之间的关系,微服务通过不论是 eureka、nacos 也好注册到服务端,Feign 是靠 Ribbon 做负载的,而 Ribbon 需要拿到注册中心的服务列表,将服务进行负载缓存到本地,然后 FeignClient 客户端在进行调用,大概就是这么一个过程。


Ribbon是如何进行负载的

首先我们要清楚 Ribbon 是如何进行负载的,也就是如何获取 nacos、eureka 的服务列表,这个很关键。

图片

RibbonClientConfiguration

RibbonClientConfiguration 类中通过 LoadBalancer,我们知道 ribbon 是靠LoadBalancer 做负载的 无非就是 ILoadBalancer 接口的方法,依次是添加新的服务、在负载均衡里选择一个服务、markServerDown 服务下线、获取服务列表、获取存活的服务器、获取所有服务器(包括健康和不健康的)

图片

ILoadBalancer 接口

ZoneAwareLoadBalancer

图片

restOfInit 方法

enableAndInitLearnNewServersFeature 方法里面。


LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());serverListUpdater.start(updateAction);

让我们看 ServerListUpdater.start 方法的实现,通过自定义线程去拿,这就是获取服务列表。

图片

ServerListUpdater.start

Ribbon负载均衡策略

服务列表获取说了,当然负载均衡的策略这块也有必要讲一下,主要有七种:

  • RoundRobinRule(轮询策略,按照服务顺序依次循环调用)

  • WeightedResponseTimeRule(权重比策略,优先选择权重比高的服务,也就是服务响应时间比较短的,响应时间越长权重比越低)

  • RandomRule(随机策略,服务提供者列表随机选择一个服务)

  • BestAvailableRule(最小连接数策略,获取服务列表中连接数最小的服务实例)

  • RetryRule(重试策略,重试获取已经失效的服务,指定时间没有获取到返回NULL)

  • AvailabilityFilteringRule(可用性敏感策略,过滤非健康服务实例,选择lianji)

  • ZoneAvoidanceRule(区域敏感策略)


Ribbon-eager-load(饥饿加载)模式

Ribbon 对于负载 Client 是在服务启动后,发生调用的时候才会去创建 Client,所以在第一次发生 http 请求调用的时候,不光要算上 http 的请求时间,还要算上 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 "";}

从调用日志可以看出,第一次调用 System2 服务,Ribbon 的 DynamicServerListLoadBalancer 会将 feign 客户端进行负载,然后进行调用,第一次调用的时间就是会长一些,第二次调用直接进行请求可以看到调用时间很快。

图片

第一次慢,第二次快

开启Ribbon饥饿加载

ribbon:

nacos: enabled: true # 开启naocos轮询eager-load: enabled: true # 开启Ribbon的饥饿加载模式(防止第一次请求超时的问题) clients: Lxlxxx-system2 # 指定需要开启的服务(需要开启Ribbon的饥饿加载模式) ReadTimeout: 10000 ConnectTimeout: 10000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 1 OkToRetryOnAllOperations: false

在项目启动的时候,可以从日志看到,已经把 Lxlxxx-system2 服务进行加载,从而避免了第一次请求超时的情况。

图片

开启Ribbon饥饿加载

总结

其实这种饥饿加载模式,类似于“客户端负载预热”的一个操作,项目启动的时候进行加载,防止服务之间调用可以因为数据量、业务逻辑处理复杂性导致接口超时,如果你的服务之间调用业务处理比较复杂、且慢,不妨可以试试这种解决方式。

### Feign底层原理解析 Feign 是一个声明式的 Web 服务客户端,主要用于简化 HTTP API 的调用过程。它通过定义接口的方式描述服务间通信的细节,从而减少手动构建 HTTP 请求的工作量。 #### 声明式接口的核心机制 Feign 使用 Java 接口来描述服务之间的交互协议[^2]。这些接口通常包含方法签名以及相关的注解(如 `@RequestLine` 或 Spring Cloud 提供的 `@GetMapping`),用于指定 URL 路径、请求方法和参数绑定等内容。在应用程序启动阶段,Feign 根据这些接口动态生成代理对象,并将其注入到依赖项中以便后续使用。 #### 动态代理与 MethodHandler 当开发调用了某个由 Feign 定义的服务端点时,实际上是在操作该接口的一个代理实现类实例。此过程中涉及的关键技术之一就是 **Java 反射** 和自定义的 **MethodHandler** 处理器[^3]: 1. **Dispatch 映射表**: 在初始化期间,Feign 将每个方法与其对应的处理逻辑关联起来形成 dispatch 表; 2. **Invoke 方法执行流程**: 当触发某次远程调用时,框架内部会依据当前被执行的方法名查找匹配的 MethodHandler 实例; 3. 构建并发出真正的 HTTP 请求前,还会经历一系列预处理步骤比如设置 headers, body 参数转换等等最终交给负载均衡组件决定具体连接哪个上游服务器节点完成整个数据交换周期。 #### 支持多种编组/反编组形式 除了基本的功能外,Fegin也提供了灵活的数据格式适配能力允许我们采用不同的方式进行消息体编码解码工作其中包括但不限于JSON/XML等形式的支持.[^4] ```java // Example of defining a Feign client with JSON serialization/deserialization. @FeignClient(name="example-service", url="http://localhost:8080") public interface ExampleService { @PostMapping("/data") ResponseData sendData(@RequestBody RequestData data); } ``` 以上代码片段展示了如何利用 Feign 创建针对 RESTful APIs 的轻量化访问入口同时自动完成了从 POJO 到 Json 字符串再回到另一个方向上的相互转变动作. ### 总结 综上所述可以看出尽管表面上看去简单易懂但实际上背后隐藏着相当复杂精妙的设计理念和技术手段共同作用才成就了如今如此强大好用特性的开源工具库产品即Fiegn本身.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值