超详细OpenFeign服务接口调用(使用步骤,超时控制,日志打印功能)

概述

OpenFeign是什么

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡

Feign能干什么

Feign旨在使编写Java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。


Feign集成了Ribbon

利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用

Feign和OpenFeign两者区别

 OpenFeign使用步骤

其实就是接口加注解——微服务调用接口+@FeignClient

POM依赖

        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

yaml配置文件

server:
  port: 80

eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

在主启动类上添加上 @EnableFeignClients 注解开启Feign的服务

新建一个OpenFeign服务调用接口 添加上 @FeignClient 注解启用Feign

package com.atguigu.springcloud.service;

import com.atguigu.springcloud.entity.CommonResult;
import com.atguigu.springcloud.entity.Payment;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;

/**
 * <p>
 * OpenFeign服务调用接口
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 14:34
 */
//一定记得要添加@Component注解将该接口注册为spring组件
@Component
//该注解就是使用Feign服务,里面的值就是要调用的微服务在注册中心的名称
@FeignClient("CLOUD-PAYMENT-SERVICE") 
public interface PaymentFeignService {
    //被调用服务端的方法体,Mapping里面的路径需要写全
    @PostMapping("/payment/create")
    CommonResult<Integer> create(@RequestBody Payment payment);

    @GetMapping("/payment/lb")
    String getPaymentLB();

    @GetMapping("/payment/get/{id}")
    CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}

新建Controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.entity.CommonResult;
import com.atguigu.springcloud.entity.Payment;
import com.atguigu.springcloud.service.PaymentFeignService;
import com.netflix.discovery.converters.Auto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 *
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 14:43
 */
@Slf4j
@RestController
@RequestMapping("consumer")
public class OrderFeignController {
    //将OpenFeign服务调用接口注入进来
    @Autowired
    private PaymentFeignService paymentFeignService;

    @GetMapping("/lb")
    public String getPaymentLB() {
        //直接调用OpenFeign服务调用接口中的方法实现服务调用
        return paymentFeignService.getPaymentLB();
    }

    @GetMapping("get/{id}")
    public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) {
        return paymentFeignService.getPaymentById(id);
    }
}

测试

 远程服务调用成功,可以看出Feign自带负载均衡配置项

小总结

 OpenFeign超时控制

在实际的运用中,当consumer调用服务时会发生等待时间过长的情况,但是我们是不能让consumer等待这么长时间的。因此必须采用超时控制。

那么就来模拟一下超时设置,故意设置超时演示出错情况(OpenFeign默认一秒钟超时)

服务提供方8001,8002故意写暂停程序

    @GetMapping("timeOut")
    public String paymentFeignTimeOut() {
        try {
            TimeUnit.SECONDS.sleep(3); //睡眠3秒钟再返回端口号
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return serverPort;
    }

然后相同的在80端口的consumer模块中远程调用provider模块里的该超时方法

测试

直接报错 

那么该怎么处理这种情况呢

默认Feign客户端只等待一秒钟,但是服务端处理需要超过1秒钟,导致Feign客户端不想等待了,直接返回报错。
为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。

yml文件中开启配置

#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
  ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
  ConnectTimeout: 5000

重启80服务再测试

 此次等待时间较长,但是服务器响应成功

OpenFeign日志打印功能

Feign 提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解 Feign 中 Http 请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出

日志级别

NONE:默认的,不显示任何日志;
 
BASIC:仅记录请求方法、URL、响应状态码及执行时间;
 
HEADERS:除了 BASIC 中定义的信息之外,还有请求和响应的头信息;
 
FULL:除了 HEADERS 中定义的信息之外,还有请求和响应的正文及元数据。

配置日志bean

package com.atguigu.springcloud.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * <p>
 * Feign配置类
 * </P>
 *
 * @author Kk
 * @since 2022/4/9 15:54
 */
@Configuration
public class FeignConfig {
    @Bean
    Logger.Level feignLoggerLevel() {
        return Logger.Level.FULL; //设置日志级别为FULL
    }
}

YML文件里需要开启日志的Feign客户端

logging:
  level:
    # feign日志以什么级别监控哪个接口
    com.atguigu.springcloud.service.PaymentFeignService: debug

启动服务访问,后台日志查看

2022-04-09 16:01:16.008 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] ---> GET http://CLOUD-PAYMENT-SERVICE/payment/get/1 HTTP/1.1
2022-04-09 16:01:16.008 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] ---> END HTTP (0-byte body)
2022-04-09 16:01:16.118  INFO 29608 --- [p-nio-80-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2022-04-09 16:01:16.136  INFO 29608 --- [p-nio-80-exec-1] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-CLOUD-PAYMENT-SERVICE
2022-04-09 16:01:16.136  INFO 29608 --- [p-nio-80-exec-1] c.netflix.loadbalancer.BaseLoadBalancer  : Client: CLOUD-PAYMENT-SERVICE instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=CLOUD-PAYMENT-SERVICE,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2022-04-09 16:01:16.141  INFO 29608 --- [p-nio-80-exec-1] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2022-04-09 16:01:16.153  INFO 29608 --- [p-nio-80-exec-1] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2022-04-09 16:01:16.154  INFO 29608 --- [p-nio-80-exec-1] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client CLOUD-PAYMENT-SERVICE initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=CLOUD-PAYMENT-SERVICE,current list of Servers=[10.8.0.106:8002, 10.8.0.106:8001],Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;	Instance count:2;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:10.8.0.106:8002;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
, [Server:10.8.0.106:8001;	Zone:defaultZone;	Total Requests:0;	Successive connection failure:0;	Total blackout seconds:0;	Last connection made:Thu Jan 01 08:00:00 CST 1970;	First connection made: Thu Jan 01 08:00:00 CST 1970;	Active Connections:0;	total failure count in last (1000) msecs:0;	average resp time:0.0;	90 percentile resp time:0.0;	95 percentile resp time:0.0;	min resp time:0.0;	max resp time:0.0;	stddev resp time:0.0]
]}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@7e8e81f6
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] <--- HTTP/1.1 200 (192ms)
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] connection: keep-alive
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] content-type: application/json
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] date: Sat, 09 Apr 2022 08:01:16 GMT
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] keep-alive: timeout=60
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] transfer-encoding: chunked
2022-04-09 16:01:16.201 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] 
2022-04-09 16:01:16.203 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] {"code":200,"message":"查询成功,serverPort:8002","data":{"id":1,"serial":"张三"}}
2022-04-09 16:01:16.203 DEBUG 29608 --- [p-nio-80-exec-1] c.a.s.service.PaymentFeignService        : [PaymentFeignService#getPaymentById] <--- END HTTP (87-byte body)
2022-04-09 16:01:17.149  INFO 29608 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: CLOUD-PAYMENT-SERVICE.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

<think>首先,用户的问题是:“OpenFeign远程调用长时间不使用报超时,再次调用正常的原因及解决方案”。我需要参考提供的引用来回答这个问题。 关键引用: - 引用[1]:讨论了在OpenFeign中配置超时时间,使用Ribbon来设置。具体有`ReadTimeout`和`ConnectTimeout`。 - 引用[2]:提到了在微服务调用中设置超时时间来避免长时间等待,防止资源耗尽和雪崩效应。 - 引用[3]:描述了服务接口调用超时机制,服务消费者调用提供者时发生阻塞,导致线程堆积,可能造成雪崩。超时机制设置超时时间自动断开连接。 - 引用[4]:涉及OpenFeign集成Hystrix(熔断器)。 用户的问题焦点是:长时间不使用OpenFeign远程调用后,第一次调用报超时,但第二次调用就正常。需要解释原因并提供解决方案。 ### 分析原因: - **超时机制**:OpenFeign默认使用Ribbon来处理超时。配置了`ConnectTimeout`(建立连接的时间)和`ReadTimeout`(读取资源的时间)。 - **长时间不使用**:这可能涉及到连接池的管理。当服务长时间不调用时,连接可能被释放或超时。在第一次调用时,建立连接可能需要时间,如果超过了设置的`ConnectTimeout`,就会报超时错误。 - **第二次调用正常**:因为第一次调用后,连接可能已经建立并缓存,所以后续调用速度快,没有超时。 - **类似问题**:这在分布式系统中常见,比如TCP连接复用问题。如果连接池的空闲连接被废弃,需要重新建立连接。 参考引用: - 引用[1]提到配置超时时间。 - 引用[3]讨论超时机制解决阻塞问题。 - 引用[4]提到Hystrix熔断器,用于处理故障隔离。 可能原因: 1. **连接池空闲超时**:Ribbon或HTTP客户端可能有连接池设置。空闲连接在一段时间后被关闭。当长时间不使用后,第一次调用需要重新建立连接,如果延迟高或网络慢,可能超时。 2. **服务器端超时**:服务器可能在空闲后关闭连接,客户端需要重新握手。 3. **缓存问题**:第一次调用时,DNS解析或服务发现延迟。 4. **客户端配置**:超时时间设置不合理,可能在边缘条件下超时。 解决方案: 1. **调整超时时间**:增加`ConnectTimeout`和`ReadTimeout`,容忍更长的连接建立时间。 2. **优化连接池**:配置HTTP客户端的连接池参数,如最大空闲时间、大小等。 3. **使用熔断器**:集成Hystrix或Resilience4j来处理失败调用,防止级联错误。 4. **预热机制**:定期发送心跳请求保持连接活跃。 5. **服务发现优化**:确保服务注册中心(如Nacos)及时更新,减少初始调用延迟。 ### 结构化回答: - **原因**:解释为什么长时间不用后第一次调用超时。 - **解决方案**:提供具体步骤。 用户要求在回答中自然地添加引用标识,例如[^1]。所以,在相关部分添加引用。 回答结构: 1. **引言**:简要概述问题。 2. **原因分析**:详细解释。 3. **解决方案**:列举方法。 4. **总结**。 5. **相关问题**:生成相关问题。 ### 生成相关问题: 系统指令要求“生成相关问题”,所以我需要在回答末尾添加相关问题。示例中是在结尾部分。 相关问题示例: 1. 如何配置OpenFeign的超时时间? 2. 什么是熔断器模式在微服务中的应用? 3. 如何优化OpenFeign的连接池设置? 4. 为什么服务调用在闲置后需要重新建立连接? ### LaTeX格式: - 行内数学公式用`$...$`。 - 独立公式用`$$...$$`,但在这个问题中可能不需要数学公式,除非涉及算法。 - 确保中文回答。 最终回答框架: - 原因:连接池空闲超时导致第一次调用重新建立连接。 - 解决方案:配置超时时间;优化连接池;启用熔断器;等等。 </think>### OpenFeign远程调用长时间不使用报超时,再次调用正常的原因及解决方案 根据您的描述,OpenFeign远程调用在长时间闲置后首次调用报超时错误,但后续调用恢复正常,这是一个典型的分布式系统连接管理问题。下面我将逐步分析原因并提供解决方案,参考了相关引用内容。 #### 原因分析 1. **连接池空闲超时导致连接关闭**: OpenFeign默认集成Ribbon作为负载均衡器,并通过HTTP客户端(如Apache HttpClient或OKHttp)管理连接池。当服务长时间未被调用(例如超过连接池的空闲超时时间),连接池中的空闲连接会被自动释放以节省资源。首次重新调用时,需重新建立TCP连接、执行服务发现(如Nacos)和SSL握手等操作。如果这些步骤耗时超过配置的`ConnectTimeout`(建立连接的超时时间),就会触发超时错误[^1][^3]。 后续调用正常是因为连接已建立并被缓存到连接池中,无需重复初始化。 2. **服务端资源回收引发延迟**: 服务提供者(被调用方)也可能在闲置期关闭空闲连接或释放资源。首次调用时,服务端需要重新初始化上下文(如数据库连接、线程池等),增加了响应时间。如果客户端设置的`ReadTimeout`(读取响应的超时时间)不足,会误判为超时[^2][^3]。 3. **客户端超时配置不合理**: OpenFeign的默认超时时间较短(通常`ConnectTimeout`和`ReadTimeout`均为1秒)。在首次调用涉及的高延迟操作(如冷启动的服务发现或网络抖动)下,容易超时。引用[1]中的配置示例显示超时时间设为5000毫秒(5秒),但实际环境中可能需要根据网络状况调整[^1]。 4. **熔断器未启用或缺席处理**: 如果未集成熔断器(如Hystrix),超时错误可能直接抛出异常,缺乏自动重试或降级机制。引用[4]提到可通过Hystrix实现故障隔离,但默认未开启[^4]。 #### 解决方案 针对上述原因,提供以下解决方案(基于引用内容优化): 1. **调整OpenFeign超时配置**: 在消费者端的`application.yml`中增加`ConnectTimeout`和`ReadTimeout`值,容忍连接重建的延迟。建议初始值设为5000毫秒以上,并根据压测调整。示例配置: ```yaml ribbon: ReadTimeout: 8000 # 读取响应超时时间(毫秒) ConnectTimeout: 8000 # 建立连接超时时间(毫秒) ``` 引用[1]建议此方法可缓解建立连接时的超时问题[^1]。 2. **优化HTTP连接池参数**: 配置连接池以延长空闲连接的存活时间,减少首次调用的重建频率。例如,使用Apache HttpClient时: ```yaml # 在application.yml中添加 feign: client: config: default: httpclient: max-connections: 200 # 最大连接数 max-connections-per-route: 50 # 每路由最大连接数 connection-time-to-live: 120s # 空闲连接存活时间(秒) ``` 这确保连接在闲置120秒后才被释放,降低首次调用超时风险[^3]。 3. **启用熔断器实现自动重试**: 集成Hystrix或Resilience4j,为超时错误添加重试逻辑或降级策略。在`application.yml`中启用Hystrix: ```yaml feign: hystrix: enabled: true # 开启熔断器 ``` 并在代码中定义降级方法: ```java @FeignClient(name = "service-provider", fallback = FallbackService.class) public interface RemoteService { @GetMapping("/api") String callApi(); } @Component public class FallbackService implements RemoteService { @Override public String callApi() { return "Fallback response"; // 超时后返回默认值 } } ``` 引用[4]表明Hystrix能隔离故障服务,防止级联错误[^4]。 4. **添加心跳机制保持连接活跃**: 定期发送轻量级请求(如HTTP HEAD)到服务提供者,维持连接池活跃。可通过Spring Scheduler实现: ```java @Scheduled(fixedRate = 60000) // 每分钟执行一次 public void keepAlive() { remoteService.ping(); // 调用一个空接口 } ``` 这避免连接因长时间闲置而关闭[^2]。 5. **监控与日志分析**: 使用Micrometer或Sleuth监控调用链路,定位超时根源(如网络延迟或服务端瓶颈)。关键日志包括: - Ribbon的`LoadBalancerContext`日志(记录连接建立时间)。 - Feign的`FeignLogger`(输出详细请求/响应时间)。 #### 总结 该问题主要由连接池管理和超时配置不当引起。**推荐方案**:优先调整`ribbon.ConnectTimeout`和连接池参数(解决80%场景),并启用熔断器增强鲁棒性。实施后,首次调用超时率可显著降低。若问题持续,需检查服务提供者性能或网络基础设施[^1][^2][^3]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值