当前位置: 首页 > news >正文

Spring Cloud 教程(四) | OpenFeign 的作用

Spring Cloud 教程(四) | OpenFeign 的作用

  • 前言:为什么要使用 OpenFeign?(核心解决的问题)
    • 不用Feign的痛点
    • Feign的核心价值
  • 一、Feign 核心使用规则(必记)
  • 二、业务场景说明
  • 三、被调用方 —— 用户服务代码
    • 3.1 用户实体类
    • 3.2 用户Controller(提供对外接口)
  • 四、调用方 —— 订单服务完整Feign配置
    • 4.1 订单服务标准包结构
    • 4.2 引入核心依赖(调用方必加)
    • 4.3 启动类开启Feign功能
    • 4.4 编写Feign接口(复制用户服务Controller)
    • 4.5 注入Feign接口,实现远程调用
    • 4.6 订单配套Controller与实体类
  • 五、本地Service VS Feign接口 核心对比
  • 六、全文总结

前言:为什么要使用 OpenFeign?(核心解决的问题)

微服务架构中,服务与服务是完全独立的,每个服务运行在不同端口、不同进程,甚至不同服务器上。
比如:订单服务(8082)想要获取用户服务(8081)的用户数据,本质上是发起一次HTTP网络请求

不用Feign的痛点

如果不使用Feign,我们只能手动用RestTemplate发起HTTP调用:

  • 手动拼接请求URL、处理请求参数
  • 手动处理序列化、异常、请求头
  • 代码冗余、可读性差、维护成本极高
  • 调用方式和本地Service调用完全割裂,不符合Java编码习惯

Feign的核心价值

OpenFeign是Spring Cloud提供的声明式远程调用组件,它的作用只有一个:
让跨服务的HTTP调用,变得和调用本地Service方法一模一样简单

  • 无需手写HTTP请求代码
  • 只需定义接口+注解,自动生成网络请求逻辑
  • 支持@Autowired注入,和本地方法调用无区别
  • 适配微服务注册中心(Nacos),自动负载均衡

一、Feign 核心使用规则(必记)

所有微服务跨服务调用,只需要调用方操作,被调用方无需任何修改,核心规则3条:

  1. 谁调用别人,谁引入Feign依赖
    发起调用的服务(如订单服务),添加OpenFeign相关依赖
  2. 谁调用别人,谁创建Feign包
    在调用方项目内新建feign包,统一管理所有远程服务调用接口
  3. 谁调用别人,谁复制对方Controller接口
    把被调用服务(如用户服务)的Controller接口原样复制到Feign接口中
    (请求方式、路径、参数、返回值完全一致,仅复制接口,不复制实现)

二、业务场景说明

我们以两个最常见的微服务为例,演示跨服务调用:

  1. 用户服务(user-service)
    • 端口:8081
    • 角色:被调用方,提供根据ID查询用户的接口
  2. 订单服务(order-service)
    • 端口:8082
    • 角色:调用方,需要调用用户服务获取订单对应用户信息

三、被调用方 —— 用户服务代码

被调用方只需要编写正常的Controller接口,无需任何Feign配置,完全无侵入。

3.1 用户实体类

importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;@Data@NoArgsConstructor@AllArgsConstructorpublicclassUser{privateLongid;privateStringname;privateIntegerage;}

3.2 用户Controller(提供对外接口)

importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.HashMap;importjava.util.Map;@RestController@RequestMapping("/users")publicclassUserController{// 模拟数据库数据privatefinalMap<Long,User>userMap=newHashMap<>();{userMap.put(1L,newUser(1L,"张三",25));userMap.put(2L,newUser(2L,"李四",30));}/** * 订单服务需要远程调用的核心接口 */@GetMapping("/{id}")publicUsergetUserById(@PathVariable("id")Longid){returnuserMap.get(id);}}

四、调用方 —— 订单服务完整Feign配置

订单服务作为调用方,严格按照核心规则完成配置,实现远程调用。

4.1 订单服务标准包结构

com.example.order ├── controller/ // 订单自身接口 ├── service/ // 订单自身业务逻辑 ├── entity/ // 订单实体、DTO └── feign/ // 🔥 统一存放所有远程调用Feign接口 └── UserFeignClient.java // 调用用户服务的接口

4.2 引入核心依赖(调用方必加)

在订单服务pom.xml中添加Nacos服务发现+OpenFeign依赖:

<!-- Nacos 服务发现 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- OpenFeign 声明式调用核心依赖 --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>

4.3 启动类开启Feign功能

importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;importorg.springframework.cloud.client.discovery.EnableDiscoveryClient;importorg.springframework.cloud.openfeign.EnableFeignClients;@SpringBootApplication@EnableDiscoveryClient// 开启微服务发现@EnableFeignClients// 🔥 开启Feign远程调用功能publicclassOrderServiceApplication{publicstaticvoidmain(String[]args){SpringApplication.run(OrderServiceApplication.class,args);}}

4.4 编写Feign接口(复制用户服务Controller)

feign包下创建接口,完全复制用户服务Controller的接口定义

importcom.example.order.entity.User;importorg.springframework.cloud.openfeign.FeignClient;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;/** * Feign客户端:调用用户服务 * 规则:请求方式、路径、参数、返回值 与对方Controller完全一致 */@FeignClient(name="user-service")// 指定被调用服务在Nacos的注册名称publicinterfaceUserFeignClient{// 👇 原样复制 UserController 的 getUserById 方法@GetMapping("/users/{id}")UsergetUserById(@PathVariable("id")Longid);}

4.5 注入Feign接口,实现远程调用

和注入本地Service完全一致,直接@Autowired注入使用:

importcom.example.order.entity.Order;importcom.example.order.entity.OrderDTO;importcom.example.order.entity.User;importcom.example.order.feign.UserFeignClient;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importjava.util.HashMap;importjava.util.Map;@ServicepublicclassOrderService{// 🔥 注入Feign接口,和本地Service调用无区别@AutowiredprivateUserFeignClientuserFeignClient;// 模拟订单数据privatefinalMap<Long,Order>orderMap=newHashMap<>();{orderMap.put(1001L,newOrder(1001L,"订单1",1L));orderMap.put(1002L,newOrder(1002L,"订单2",2L));}/** * 查询订单详情(包含用户信息,需跨服务调用) */publicOrderDTOgetOrderDetail(LongorderId){// 1. 查询本地订单信息Orderorder=orderMap.get(orderId);if(order==null){thrownewRuntimeException("订单不存在");}// 2. 🔥 跨服务调用用户服务,像本地方法一样使用Useruser=userFeignClient.getUserById(order.getUserId());// 3. 组装返回数据OrderDTOorderDTO=newOrderDTO();orderDTO.setOrderId(order.getOrderId());orderDTO.setOrderName(order.getOrderName());orderDTO.setUserName(user.getName());orderDTO.setUserAge(user.getAge());returnorderDTO;}}

4.6 订单配套Controller与实体类

// 订单Controller@RestController@RequestMapping("/orders")publicclassOrderController{@AutowiredprivateOrderServiceorderService;@GetMapping("/{id}")publicOrderDTOgetOrderDetail(@PathVariable("id")Longid){returnorderService.getOrderDetail(id);}}// 订单实体@Data@NoArgsConstructor@AllArgsConstructorclassOrder{privateLongorderId;privateStringorderName;privateLonguserId;}// 订单返回DTO@DataclassOrderDTO{privateLongorderId;privateStringorderName;privateStringuserName;privateIntegeruserAge;}

五、本地Service VS Feign接口 核心对比

对比维度本地Service接口Feign客户端接口
作用执行本地业务逻辑发起跨服务HTTP请求
实现类来源自己手动编写Feign框架自动生成(动态代理)
定义规则自定义即可必须复制被调用方Controller接口
调用方式@Autowired注入本地调用@Autowired注入远程调用
运行环境同一服务、同一进程跨服务、跨端口、网络请求

六、全文总结

  1. Feign解决的核心问题
    简化微服务跨服务HTTP调用,让远程调用和本地Service调用体验完全一致。
  2. Feign使用核心原则
    谁调用别人,谁加依赖;谁调用别人,谁建Feign包;谁调用别人,谁复制对方Controller。
  3. 无侵入特性
    被调用服务无需任何修改,仅调用方配置即可完成远程调用。
  4. 工程规范
    所有远程调用接口统一放在feign包下,项目结构清晰、易维护、易扩展。
http://www.jsqmd.com/news/626821/

相关文章:

  • 从逻辑门到运算核心:算术逻辑单元(ALU)的设计演进与标志位生成
  • AI Agent生成测试用例,真能覆盖100%?我们拿一个真实金融项目做了次压力测试
  • Arduino嵌入式移动平均滤波库:零开销泛型实现
  • 分布式配置中心实现原理
  • Visual Studio 2022 中的编译优化与 DLL 编写
  • 百度伐谋Agent 2.0登顶MLE,百度的板凳有多深?
  • 前端响应式设计原理
  • QNAP 紧急安全警示:NetBak PC Agent 受 ASP.NET Core 高危漏洞影响,建议立即修复
  • 用ROS话题连接Carla与罗技G29:一份给自动驾驶开发者的硬件在环(HIL)入门指南
  • Azure DevOps中动态获取仓库ID和设置分支策略
  • 避坑指南:YooAsset整合HybridCLR时,如何正确处理AOT与热更DLL的打包与加载?
  • Calico IPIP 使用指南仍
  • 健康追踪设备计步功能大比拼:Google Pixel Watch 4 拔得头筹
  • 【PTA题目解析】7-7 数组差值计算与格式化输出技巧
  • FunASR离线部署避坑指南:从Docker容器GPU驱动到模型热加载的实战踩坑记录
  • SAP会计凭证冲销踩坑记录:FB08报错OBJ_TYPE字段的3种处理方案
  • 软件实时计算中的低延迟保证
  • 基于PLC的霓虹灯控制系统
  • 嵌入式俄罗斯方块游戏内核:MCU级Tetris逻辑库设计与实现
  • CondConv动态卷积实战:如何在PyTorch中轻松实现自适应卷积核(附完整代码解析)
  • GreaterWMS:基于福特亚太售后物流实战经验的开源仓储管理系统架构解析
  • Nunchaku-flux-1-dev与.NET开发:跨平台AI应用构建指南
  • MuJoCo机械臂PID调参实战:如何避免超调让控制更丝滑(附完整代码)
  • 高通平台Android 10充电系统调试实战:从日志分析到参数调优
  • AT32F403A开发板实战:用PB14/PB15模拟IIC驱动AT24C04(附完整源码)
  • ASCIIGraph:嵌入式串口终端实时波形可视化库
  • 智能车实战指南——从PWM到编码器的模块驱动全解析
  • 别让AI代码,变成明天的技术债览
  • 软件服务管理化的流程执行与改进
  • 工作单元管理化技术事务管理与会话