SpringCloud-openFeign(服务调用)

张开发
2026/4/10 1:12:31 15 分钟阅读

分享文章

SpringCloud-openFeign(服务调用)
一、OpenFeign 是什么OpenFeign 是SpringCloud 生态中的一个声明式 HTTP 客户端工具它是基于 Netflix Feign 进行的二次封装整合了Spring MVC 的注解如 RequestMapping、GetMapping等和 Spring Cloud 的服务发现能力目的就是为了简化微服务架构中服务间的HTTP调用流程。本质上OpenFeign 通过接口 注解的形式定义 HTTP 请求开发者不用手动进行编写 HttpClient 的调用代码框架会自动根据注解生成动态代理实现类完成请求的发起、参数封装、结果解析等操作。其核心特性包括1.声明式 API通过接口和注册定义请求代码简洁容易维护2.自动服务发现结合服务注册中心NACOS自动从注册中心获取服务地址无需硬编码IP/端口3.负载均衡集成默认整合 Spring Cloud LoadBalance实现服务调用的负载均衡。4.熔断降级支持可与 Sentinel、Resilience4j 等组件集成应对服务不可用场景5.日志可配置支持多级别日志打印便于调试 HTTP 请求细节。二、OpenFeign 能干嘛在微服务架构中服务间的远程调用的核心场景之一。OpenFeign 通过封装 HTTP 调用细节代替了传统手动调用具体能力有1. 简化远程调用代码传统使用 RestTemplate 调用远程服务时需手动拼接 URL、设置请求参数、处理响应结果代码冗余且容易出错。// 传统RestTemplate调用 String url http://user-service/user/{id}; User user restTemplate.getForObject(url, User.class, 1L);使用 OpenFeign 后只需要定义接口并添加注解无需手动构建请求// OpenFeign声明式调用 FeignClient(user-service) // 服务名 public interface UserFeignClient { GetMapping(/user/{id}) // 对应服务的接口路径 User getUserById(PathVariable(id) Long id); } // 调用时直接注入接口使用 Autowired private UserFeignClient userFeignClient; User user userFeignClient.getUserById(1L);2. 自动集成服务发现与负载均衡在 Spring Cloud 生态中OpenFeign 会自动从服务注册中心如 Nacos获取目标服务的实例列表并结合 Spring Cloud LoadBalancer 实现负载均衡默认轮询策略。开发者无需关心服务地址的动态变化只需指定服务名即可完成调用。3. 支持请求参数与响应的灵活处理OpenFeign 完全兼容 Spring MVC 的注解支持PathVariable路径参数、RequestParam查询参数、RequestBody请求体等参数绑定方式同时支持 JSON、Form 等多种数据格式的响应解析满足绝大多数微服务调用场景。4. 可扩展的熔断降级与日志能力通过集成 Sentinel 或 Resilience4jOpenFeign 可实现服务调用的熔断降级即 “兜底” 逻辑避免因下游服务故障导致的级联失败同时OpenFeign 支持多级别日志打印可清晰查看 HTTP 请求的 URL、参数、响应状态等细节便于问题排查。三、OpenFeign 与 FeignFeign 和 OpenFeign 同属 Netflix 开源的 HTTP 客户端工具但 OpenFeign 是 Spring Cloud 对 Feign 的增强版两者在起源、功能、依赖等方面存在显著区别具体对比如下在SpringCloud中优先选择 OpenFeign因为更贴合 Spring 生态、配置更简单、功能更丰富能够兼容Spring Cloud组件。四、Spring Boot 整合 OpenFeign本节基于Spring Boot 3.2.x、MyBatis-Plus 3.5.x、Lombok 1.18.x、Nacos 2.3.x注册中心 配置中心完整实现 OpenFeign 的整合包含 “服务提供者” 与 “服务消费者” 两端代码并集成 Sentinel 实现兜底逻辑。1. 环境准备1.1 依赖pom.xml在父工程的pom.xml中添加对应的依赖。dependencyManagement dependencies !-- Spring Boot 3 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version3.2.5/version typepom/type scopeimport/scope /dependency !-- Spring Cloud -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-dependencies/artifactId version2023.0.1/version typepom/type scopeimport/scope /dependency !-- Spring Cloud Alibaba -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2023.0.0.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement1.2 服务提供者user-service依赖在 子模块 user-service 中的pom.xml中添加依赖。dependencies !-- Spring Web -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Nacos注册中心 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency !-- Nacos配置中心 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.6/version /dependency !-- MySQL驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Lombok -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies1.3 服务消费者order-service依赖在子模块order-service中的pom.xml中添加依赖dependencies !-- 基础依赖同服务提供者 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.6/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- OpenFeign -- dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-openfeign/artifactId /dependency !-- Sentinel用于熔断降级兜底 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependency !-- OpenFeign整合Sentinel -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-sentinel-feign/artifactId /dependency /dependencies2. 配置文件nacos配置中心在 Nacos 控制台创建两个配置集分别对应user-service和order-service配置格式为 YAML。2.1 服务提供者user-service-test# 服务端口 server: port: 8081 # 服务名Nacos注册名 spring: application: name: user-service # Nacos配置 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 # Nacos注册中心地址 config: server-addr: 127.0.0.1:8848 # Nacos配置中心地址 file-extension: yaml # 配置文件格式 # 数据库配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/user_db?useSSLfalseserverTimezoneUTC username: root password: 123456 # MyBatis-Plus配置 mybatis-plus: mapper-locations: classpath:mapper/**/*.xml type-aliases-package: com.example.user.entity configuration: map-underscore-to-camel-case: true # 下划线转驼峰2.2 服务消费者order-service-test# 服务端口 server: port: 8082 # 服务名 spring: application: name: order-service # Nacos配置 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: server-addr: 127.0.0.1:8848 file-extension: yaml # Sentinel配置 sentinel: transport: dashboard: 127.0.0.1:8080 # Sentinel控制台地址需本地启动Sentinel # 数据库配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/order_db?useSSLfalseserverTimezoneUTC username: root password: 123456 # MyBatis-Plus配置 mybatis-plus: mapper-locations: classpath:mapper/**/*.xml type-aliases-package: com.example.order.entity configuration: map-underscore-to-camel-case: true # 开启OpenFeign整合Sentinel feign: sentinel: enabled: true3. 服务提供者代码user-service3.1 实体类Userpackage com.example.user.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; Data TableName(t_user) // 对应数据库表名 public class User { TableId(type IdType.AUTO) private Long id; private String username; private Integer age; private String address; }3.2 Mapper 接口UserMapperpackage com.example.user.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.user.entity.User; import org.apache.ibatis.annotations.Mapper; Mapper public interface UserMapper extends BaseMapperUser { // 继承BaseMapper无需手动写CRUD方法 }3.3 服务层UserServicepackage com.example.user.service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.user.entity.User; import com.example.user.mapper.UserMapper; import org.springframework.stereotype.Service; Service public class UserService extends ServiceImplUserMapper, User { // 如需自定义方法可在此扩展 public User getUserById(Long id) { return getById(id); // 继承自ServiceImpl的方法 } }3.4 控制器UserControllerpackage com.example.user.controller; import com.example.user.entity.User; import com.example.user.service.UserService; import lombok.RequiredArgsConstructor; 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; RestController RequestMapping(/user) RequiredArgsConstructor // Lombok生成构造器注入 public class UserController { private final UserService userService; // 提供给OpenFeign调用的接口 GetMapping(/{id}) public User getUserById(PathVariable(id) Long id) { return userService.getUserById(id); } }3.5 启动类UserApplicationpackage com.example.user; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; SpringBootApplication EnableDiscoveryClient // 开启服务注册与发现Spring Boot 3可省略默认开启 public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }4. 服务消费者代码order-service4.1 实体类Order、User// Order.java package com.example.order.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; Data TableName(t_order) public class Order { TableId(type IdType.AUTO) private Long id; private Long userId; // 关联用户ID private String orderNo; private Double amount; } // User.java与服务提供者一致可通过API模块共享 package com.example.order.entity; import lombok.Data; Data public class User { private Long id; private String username; private Integer age; private String address; }4.2 OpenFeign 客户端接口UserFeignClient定义 Feign 客户端并配置 Sentinel 兜底类package com.example.order.feign; import com.example.order.entity.User; import com.example.order.feign.fallback.UserFeignFallback; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; // name目标服务名Nacos中注册的user-service // fallback兜底类服务不可用时执行 FeignClient(name user-service, fallback UserFeignFallback.class) public interface UserFeignClient { // 与服务提供者的接口路径、参数完全一致 GetMapping(/user/{id}) User getUserById(PathVariable(id) Long id); }4.3 兜底类UserFeignFallback实现 Feign 客户端接口定义服务熔断 / 降级时的兜底逻辑package com.example.order.feign.fallback; import com.example.order.entity.User; import com.example.order.feign.UserFeignClient; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; Slf4j Component // 必须注入Spring容器 public class UserFeignFallback implements UserFeignClient { Override public User getUserById(Long id) { // 兜底逻辑记录日志 返回默认数据 log.error(调用user-service获取用户失败用户ID{}, id); User defaultUser new User(); defaultUser.setId(id); defaultUser.setUsername(默认用户服务暂不可用); defaultUser.setAge(0); defaultUser.setAddress(未知地址); return defaultUser; } }4.4 服务层OrderService注入 Feign 客户端调用远程服务package com.example.order.service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.order.entity.Order; import com.example.order.entity.User; import com.example.order.feign.UserFeignClient; import com.example.order.mapper.OrderMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; Service RequiredArgsConstructor public class OrderService extends ServiceImplOrderMapper, Order { private final UserFeignClient userFeignClient; // 订单详情查询关联查询用户信息调用远程服务 public Order getOrderWithUser(Long orderId) { // 1. 查询订单基本信息 Order order getById(orderId); if (order null) { throw new RuntimeException(订单不存在); } // 2. 调用user-service获取用户信息OpenFeign User user userFeignClient.getUserById(order.getUserId()); // 3. 可选将用户信息封装到订单扩展类中返回 // 此处简化直接返回订单 return order; } }4.5 控制器OrderControllerpackage com.example.order.controller; import com.example.order.entity.Order; import com.example.order.service.OrderService; import lombok.RequiredArgsConstructor; 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; RestController RequestMapping(/order) RequiredArgsConstructor public class OrderController { private final OrderService orderService; GetMapping(/{id}) public Order getOrderWithUser(PathVariable(id) Long id) { return orderService.getOrderWithUser(id); } }4.6 启动类OrderApplicationpackage com.example.order; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; SpringBootApplication EnableFeignClients // 开启OpenFeign客户端扫描 public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }5. 测试验证启动依赖服务启动 Nacos Server默认端口 8848、Sentinel Dashboard默认端口 8080启动服务提供者运行UserApplication查看 Nacos 控制台是否有user-service注册启动服务消费者运行OrderApplication查看 Nacos 控制台是否有order-service注册正常调用测试访问http://localhost:8082/order/1假设订单 ID1 关联用户 ID1返回正常订单与用户信息兜底测试停止user-service再次访问上述地址返回兜底逻辑中的 “默认用户” 信息证明熔断降级生效。五、OpenFeign 默认超时时间和配置超时时间OpenFeign 的超时时间分为连接超时建立 HTTP 连接的超时时间和读取超时获取 HTTP 响应的超时时间默认配置由 Spring Cloud Feign 的默认参数决定可通过配置文件自定义调整。1. 默认超时时间在 Spring Cloud OpenFeign 中默认超时时间如下连接超时时间Connect Timeout10 秒10000 毫秒读取超时时间Read Timeout60 秒60000 毫秒默认超时时间的来源是FeignClientProperties类中的默认值代码如下:public class FeignClientProperties { private int connectTimeout 10000; // 连接超时默认10秒 private int readTimeout 60000; // 读取超时默认60秒 // 其他属性... }2. 配置超时时间OpenFeign 支持全局超时配置对所有 Feign 客户端生效和局部超时配置仅对指定 Feign 客户端生效配置方式均通过application.yml或 Nacos 配置中心实现。2.1 全局超时配置在服务消费者如 order-service的配置文件中添加以下配置对所有 Feign 客户端生效feign: client: config: default: # default表示全局配置 connect-timeout: 5000 # 连接超时5秒5000毫秒 read-timeout: 30000 # 读取超时30秒30000毫秒2.2 局部超时配置若需对某个 Feign 客户端如user-service单独配置超时时间只需将default替换为目标服务名feign: client: config: user-service: # 仅对user-service的Feign客户端生效 connect-timeout: 3000 # 连接超时3秒 read-timeout: 10000 # 读取超时10秒2.3 注意事项超时时间的单位是毫秒配置时需注意数值大小如 1 秒 1000 毫秒局部配置优先级高于全局配置若同时配置以局部配置为准若服务调用需要处理大量数据如文件下载需适当延长读取超时时间避免提前断开连接六、设置 OpenFeign 日志打印级别OpenFeign 支持打印 HTTP 请求的详细日志如 URL、参数、响应状态、响应体等便于调试和问题排查。日志级别分为 4 种默认级别为NONE不打印日志可通过配置文件或 Java 代码调整。1. OpenFeign 日志级别说明OpenFeign 定义了 4 种日志级别从低到高分别为2. 配置日志打印级别方式 1通过配置文件配置推荐在服务消费者的配置文件中可通过logging.level指定 Feign 客户端接口的日志级别需配合feign.client.config开启日志# 1. 开启Feign客户端的日志指定日志级别 feign: client: config: user-service: # 目标服务名default表示全局 logger-level: FULL # 日志级别NONE/BASIC/HEADERS/FULL # 2. 配置Spring日志让Feign客户端接口的日志生效必须配置 logging: level: com.example.order.feign.UserFeignClient: DEBUG # Feign客户端接口的全类名说明feign.client.config.user-service.logger-level指定user-service对应的 Feign 客户端的日志级别logging.level.com.example.order.feign.UserFeignClient将 Feign 客户端接口的日志级别设置为DEBUGSpring 日志默认级别为INFO若不设置DEBUG级别日志不会输出。方式 2通过 Java 代码配置通过Configuration注解创建配置类手动设置 Feign 日志级别package com.example.order.config; import feign.Logger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; Configuration public class FeignLogConfig { // 配置Feign日志级别 Bean public Logger.Level feignLoggerLevel() { return Logger.Level.FULL; // 返回需要的日志级别 } }应用到指定 Feign 客户端在FeignClient注解中通过configuration属性指定配置类仅对当前 Feign 客户端生效FeignClient( name user-service, fallback UserFeignFallback.class, configuration FeignLogConfig.class // 指定日志配置类 ) public interface UserFeignClient { // 接口方法... }全局生效若需让配置类对所有 Feign 客户端生效可在启动类上添加EnableFeignClients(defaultConfiguration FeignLogConfig.class)SpringBootApplication EnableFeignClients(defaultConfiguration FeignLogConfig.class) public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }

更多文章