声明式远程调用:OpenFeign 基础教程

远程调用(OpenFeign)

我没专门讲编程式的远程调用,可以自己看一下 RestTemplate ,这里就不多做赘述了

什么是OpenFeign?

  • 声明式HTTP客户端:通过接口+注解方式实现服务调用
  • 基于Ribbon的负载均衡:内置客户端负载均衡能力
  • 与Spring Cloud整合:无缝对接Eureka/Nacos等注册中心
  • 优势: ✅ 简化HTTP API调用 ✅ 支持多种编码器/解码器 ✅ 可插拔的注解支持

核心优势

特性 传统 RestTemplate OpenFeign
代码复杂度 需手动拼接 URL 和参数 接口声明直接映射远程 API
维护成本 修改 URL 需多处调整 集中管理接口定义
扩展性 需自定义拦截器 支持全局/局部拦截器配置

快速入门

  1. 添加依赖
xml 复制代码
<!-- OpenFeign 核心依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>4.1.2</version>
        </dependency>
  1. 启用OpenFeign
css 复制代码
在启动类上加`@EnableFeignClients`注解开启 Feign 远程调用功能
less 复制代码
@EnableFeignClients  // 关键注解:开启 Feign 客户端扫描
@EnableDiscoveryClient // 开启服务发现功能
@SpringBootApplication
public class ProductApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProductApplication.class, args);
    }
    
}
  1. 声明式接口定义

向第三方发送也是一样的,只要声明正确了(@FeignClient的url属性),feign 会帮忙发送

kotlin 复制代码
@FeignClient("small-order") // feign 客户端, 指定服务名(需注册到注册中心)
public interface OrderFeignClient {
​
    @GetMapping("/order/test") // 与远程接口完全一致
    String test();
​
}

我这里提前写好了一个测试用的服务接口,和 feign 不是同一个服务

其他类型的参数:

和springMVC使用相同的注解即可

less 复制代码
@GetMapping("/user/{id}")  
User getUserById(@PathVariable("id") Long userId);  // 必须指定参数名  
​
@PostMapping("/user/create")  
User createUser(@RequestBody UserDTO userDTO);  // 对象自动序列化为 JSON  
  1. 使用

    在我们的业务中注入刚才声明好的 feign 接口,注入到属性中即可使用,和 mybatis 类似,后续在访问此接口的时候,会使用feign完成远程调用

less 复制代码
@Slf4j
@RestController
public class TestController {
    @Autowired
    private OrderFeignClient orderFeignClient;
​
    @GetMapping("/test")
    public String test() {
        String test = orderFeignClient.test();
        log.info("使用 feign 远程调用 order 服务的test接口,返回数据:{}",test);
        return "test";
    }
}

可以在控制台看到日志记录,feign帮我们完成了远程调用

常见问题排查:

问题现象 解决方案
404 接口不存在 检查 @FeignClient 服务名是否与注册中心一致
参数传递失败 确保 @PathVariable 指定参数名
无法注入 Feign 客户端 确认主类添加了 @EnableFeignClients

进阶配置

  1. 日志追踪

springboot的配置文件

yaml 复制代码
logging:
  level:
    com.huang.small.feign: debug # 注意这是自己的包名
    

配置类:

kotlin 复制代码
@Configuration  
public class FeignConfig {  
    @Bean  
    Logger.Level feignLoggerLevel() {  
        return Logger.Level.FULL;  // 输出完整请求日志  正式环境不推荐使用,可以用来排错
    }  
}  
  1. 超时控制

我们这里通过一个场景来讲解一下为什么需要这个功能。

我们现在有两个服务,分别是订单服务和商品服务,我们生成一个商品的订单时,需要去向商品服务发送请求,获取到具体的商品数据如果商品服务的服务器宕机了,订单服务就一直无法连接到商品服务,或者说连接上了但是业务逻辑处理的太慢了,很久都无法活动响应数据,就会导致 openfeign 一致获取不到结果,就可能导致雪崩问题(这里不详细说明,简单理解一下就可以)。

简单来说,就是 openfeign 在发起远程调用的时候,设置一个超时时间,在超时时间内,就正常返回结果,如果超过了这个时间,就不等了,中断这次远程调用,这个时候 openfeign 有两种形式的返回:

  • 返回错误信息,提示我们远程服务超时
  • 返回兜底数据,比如默认为 0,没有库存(需要配合熔断框架)

对于超时控制,我们还需要了解两个配置:

  • connectTimeout: 连接超时(默认10s)
  • readTimeout: 读取超时 (默认60s)

可以通过配置来修改这两个配置:

yaml 复制代码
spring:
  application:
    name: small-product # 应用名称,会在 nacos 中显示
  cloud:
    openfeign:
      client:
        config:
          default: # 这个是默认配置,也可以像下面一样进行精确配置
            connect-timeout: 1000 # 连接超时时间 毫秒
            read-timeout: 3000 # 读取超时时间 毫秒
          small-product: # 你的feign客户端名字,可以在@FeignClient("small-order")这个注解中配置
            connect-timeout: 1000 # 连接超时时间 毫秒
            read-timeout: 3000 # 读取超时时间 毫秒
  1. 重试机制

远程调用失败后,还可以进行多次尝试,如果某次成功则返回ok,如果多次依然失败则结束调用,返回错误(默认不重试 )

在配置类中加入重试器的 bean :

typescript 复制代码
@Bean
Retryer retryer() {
    return new Retryer.Default(1000, 1000, 3);
}
  • period: 间隔多少毫秒(第一次间隔,后面每次的间隔是前一次的1.5倍)
  • maxPeriod: 最多间隔多少(每次超时重试的等待时间为 间隔 * 1.5 最多不超过这个参数的时长)
  • maxAttempts: 最多尝试几次
  1. 拦截器

请求拦截器: openfeign 在远程服务发送请求之前,由请求拦截器对请求进行一些定制化的修改,比如向请求头里面加入一些字段

响应拦截器: 远程服务处理完请求以后,把响应数据交给响应拦截器,对响应进行一个预处理,再把真正的结果发给 openfeign

因为响应拦截器用的比较少,所以我这里只给出请求拦截器的实例代码,响应拦截器也差不多,不过是换一个接口实现即可

定义一个类:实现 RequestInterceptor ,重写apply方法,需要使用@Component注解注入到容器中

typescript 复制代码
@Component
public class TestRequestInterceptor implements RequestInterceptor {
    /**
     * 请求拦截器
     * @param requestTemplate 这次请求的详细信息
     */
    @Override
    public void apply(RequestTemplate requestTemplate) {
        requestTemplate.header("test", "test");
    }
}

可以自己测试一下响应拦截器的用法

  1. Fallback(需要配合熔断框架才能实现)

返回兜底数据:

  • 创建一个类实现我们的 feign 客户端接口,并加上@Component注解,注册到容器中:
less 复制代码
@Slf4j
@Component
public class OrderFeignClientFallback implements OrderFeignClient {
    @Override
    public String test() {
        log.info("这是兜底回调.......");
        return "兜底数据";
    }
}

实现接口中的方法,这个方法将会作为兜底的执行方案

  • 在 feign 接口的注解写明对应的兜底的类

@FeignClient注解中的 fallback 就是用来兜底的类,请求失败之后会从容器中拿到这个类型的bean执行兜底回调

kotlin 复制代码
@FeignClient(value = "small-order",fallback = OrderFeignClientFallback.class) // feign 客户端
public interface OrderFeignClient {
​
    @GetMapping("/order/test")
    String test();
​
}
  • 整合熔断框架

只是为了可以看到效果,这里不会具体介绍熔断相关的知识

导入sentinel的依赖

xml 复制代码
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
      <version>2023.0.1.0</version>
</dependency>

添加配置

yaml 复制代码
feign:
  sentinel:
    enabled: true 
相关推荐
你的人类朋友2 分钟前
【Docker】说说卷挂载与绑定挂载
后端·docker·容器
间彧26 分钟前
在高并发场景下,如何平衡QPS和TPS的监控资源消耗?
后端
间彧27 分钟前
QPS和TPS的区别,在实际项目中,如何准确测量和监控QPS和TPS?
后端
间彧1 小时前
消息队列(RocketMQ、RabbitMQ、Kafka、ActiveMQ)对比与选型指南
后端·消息队列
brzhang2 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang2 小时前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
Roye_ack2 小时前
【项目实战 Day9】springboot + vue 苍穹外卖系统(用户端订单模块 + 商家端订单管理模块 完结)
java·vue.js·spring boot·后端·mybatis
AAA修煤气灶刘哥4 小时前
面试必问的CAS和ConcurrentHashMap,你搞懂了吗?
后端·面试
SirLancelot15 小时前
MinIO-基本介绍(一)基本概念、特点、适用场景
后端·云原生·中间件·容器·aws·对象存储·minio
golang学习记5 小时前
Go 1.25 新特性:正式支持 Git 仓库子目录作为 Go 模块
后端