Spring Cloud 优雅实现远程调用:OpenFeign
Spring Cloud OpenFeign 是声明式 Web Service 客户端,简化微服务间 HTTP 通信。相比 RestTemplate 需拼接 URL 且代码臃肿,OpenFeign 通过接口加注解方式调用远程服务,支持负载均衡。参数传递涵盖单个、多个、对象及 JSON 格式。最佳实践建议将 Feign 接口抽取为独立模块供消费方依赖,或采用继承方式复用公共接口定义,提升代码可维护性与复用性。

Spring Cloud OpenFeign 是声明式 Web Service 客户端,简化微服务间 HTTP 通信。相比 RestTemplate 需拼接 URL 且代码臃肿,OpenFeign 通过接口加注解方式调用远程服务,支持负载均衡。参数传递涵盖单个、多个、对象及 JSON 格式。最佳实践建议将 Feign 接口抽取为独立模块供消费方依赖,或采用继承方式复用公共接口定义,提升代码可维护性与复用性。

观察之前文章介绍的远程调用的代码:
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
String url = "http://product-service/product/" + orderInfo.getProductId();
ProductInfo productInfo = restTemplate.getForObject(url, ProductInfo.class);
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
虽说 RestTemplate 对 HTTP 封装后,已经比直接使用 HTTPClient 简单方便很多,但是还存在一些问题。
需要拼接 URL,灵活性高,但是封装臃肿,URL 复杂时,容易出错。代码可读性差,风格不统一。
微服务之间的通信方式,通常有两种:RPC 和 HTTP。 在 SpringCloud 中,默认是使用 HTTP 来进行微服务的通信,最常用的实现形式有两种: • RestTemplate • OpenFeign
RPC(Remote Procedure Call)远程过程调用,是一种通过网络从远程计算机上请求服务,而不需 要了解底层网络通信细节。RPC 可以使用多种网络协议进行通信,如 HTTP、TCP、UDP 等,并且在 TCP/IP 网络四层模型中跨越了传输层和应用层。简言之 RPC 就是像调用本地方法一样调用远程方法。 常见的 RPC 框架有:
OpenFeign 是一个声明式的 Web Service 客户端。它让微服务之间的调用变得更简单,类似 controller 调用 service,只需要创建一个接口,然后添加注解即可使用 OpenFeign。
OpenFeign 的前身 Feign 是 Netflix 公司开源的一个组件。 • 2013 年 6 月,Netflix 发布 Feign 的第一个版本 1.0.0 • 2016 年 7 月,Netflix 发布 Feign 的最后一个版本 8.18.0
2016 年,Netflix 将 Feign 捐献给社区 • 2016 年 7 月 OpenFeign 的首个版本 9.0.0 发布,之后一直持续发布到现在。
Spring Cloud Feign
Spring Cloud Feign 是 Spring 对 Feign 的封装,将 Feign 项目集成到 Spring Cloud 生态系统中。受 Feign 更名影响,Spring Cloud Feign 也有两个 starter
spring-cloud-starter-feign spring-cloud-starter-openfeign
由于 Feign 的停更维护,对应的,我们使用的依赖是 spring-cloud-starter-openfeign。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
在 order-service 的启动类添加注解 @EnableFeignClients,开启 OpenFeign 的功能。
@EnableFeignClients
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
基于 SpringMVC 的注解来声明远程调用的信息
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
@FeignClient 注解作用在接口上,参数说明:
• name/value:指定 FeignClient 的名称,也就是微服务的名称,用于服务发现,Feign 底层会使用 Spring Cloud LoadBalance 进行负载均衡。也可以使用 url 属性指定一个具体的 url. • path: 定义当前 FeignClient 的统一前缀。
修改远程调用的方法
@Autowired
private ProductApi productApi;
/**
* Feign 实现远程调用
* @param orderId
*/
public OrderInfo selectOrderById(Integer orderId) {
OrderInfo orderInfo = orderMapper.selectOrderById(orderId);
ProductInfo productInfo = productApi.getProductById(orderInfo.getProductId());
orderInfo.setProductInfo(productInfo);
return orderInfo;
}
不同方式之间代码对比:
通过观察,我们也可以发现,Feign 的客户端和服务提供者的接口声明非常相似 上面例子中,介绍了 Feign 从 URL 中获取参数,接下来介绍下 Feign 参数传递的其他方式。
服务提供方:product-service
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/p1")
public String p1(Integer id) {
return "p1 接收到参数:" + id;
}
}
Feign 客户端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
}
服务消费方 order-service:
@RequestMapping("/feign")
@RestController
public class TestFeignController {
@Autowired
private ProductApi productApi;
@RequestMapping("/o1")
public String o1(Integer id) {
return productApi.p1(id);
}
}
使用多个@RequestParam 进行参数绑定即可
服务提供方 product-service
@RequestMapping("/p2")
public String p2(Integer id, String name) {
return "p2 接收到参数,id:" + id + ",name:" + name;
}
Feign 客户端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
}
服务消费方 order-service:
@RequestMapping("/o2")
public String o2(@RequestParam("id") Integer id, @RequestParam("name") String name) {
return productApi.p2(id, name);
}
服务提供方 product-service
@RequestMapping("/p3")
public String p3(ProductInfo productInfo) {
return "接收到对象,productInfo:" + productInfo;
}
Feign 客户端:
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
服务消费方 order-service:
@RequestMapping("/o3")
public String o3(ProductInfo productInfo) {
return productApi.p3(productInfo);
}
服务提供方 product-service:
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo) {
return "接收到对象,productInfo:" + productInfo;
}
Feign 客户端
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
服务消费方 order-service:
@RequestMapping("/o4")
public String o4(@RequestBody ProductInfo productInfo) {
System.out.println(productInfo.toString());
return productApi.p4(productInfo);
}
最佳实践,其实也就是经过历史的迭代,在项目中的实践过程中,总结出来的最好的使用方式。 通过观察,我们也能看出来,Feign 的客户端与服务提供者的 controller 代码非常相似
Feign 客户端:
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
}
服务提供方 Controller:
@RequestMapping("/product")
@RestController
public class ProductController {
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
//...
}
}
有没有一种方法可以简化这种写法呢?
Feign 支持继承的方式,我们可以把一些常件的操作封装到接口里。 我们可以定义好一个接口,服务提供方实现这个接口,服务消费方编写 Feign 接口的时候,直接继承这个接口
接口可以放在一个公共的 Jar 包里,供服务提供方和服务消费方使用
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
复制 ProductApi, ProductInfo 到 product-api 模块中
public interface ProductInterface {
@RequestMapping("/{productId}")
ProductInfo getProductById(@PathVariable("productId") Integer productId);
@RequestMapping("/p1")
String p1(@RequestParam("id") Integer id);
@RequestMapping("/p2")
String p2(@RequestParam("id") Integer id, @RequestParam("name") String name);
@RequestMapping("/p3")
String p3(@SpringQueryMap ProductInfo productInfo);
@RequestMapping("/p4")
String p4(@RequestBody ProductInfo productInfo);
}
服务提供方实现接口 ProductInterface:
@RequestMapping("/product")
@RestController
public class ProductController implements ProductInterface {
@Autowired
private ProductService productService;
@RequestMapping("/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId) {
System.out.println("收到请求,Id:" + productId);
return productService.selectProductById(productId);
}
@RequestMapping("/p1")
public String p1(Integer id) {
return "p1 接收到参数:" + id;
}
@RequestMapping("/p2")
public String p2(Integer id, String name) {
return "p2 接收到参数,id:" + id + ",name:" + name;
}
@RequestMapping("/p3")
public String p3(ProductInfo productInfo) {
return "接收到对象,productInfo:" + productInfo;
}
@RequestMapping("/p4")
public String p4(@RequestBody ProductInfo productInfo) {
return "接收到对象,productInfo:" + productInfo;
}
}
服务消费方继承 ProductInterface
@FeignClient(value = "product-service", path = "/product")
public interface ProductApi extends ProductInterface {}
这样通过继承就可以实现远程调用
官方推荐 Feign 的使用方式为继承的方式,但是企业开发中,更多是把 Feign 接口抽取为一个独立的模块 (做法和继承相似,但理念不同)。 操作方法: 将 Feign 的 Client 抽取为一个独立的模块,并把涉及到的实体类等都放在这个模块中,打成一个 Jar。服务消费方只需要依赖该 Jar 包即可。这种方式在企业中比较常见,Jar 包通常由服务提供方来实现。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
复制 ProductApi, ProductInfo 到 product-api 模块中 之后通过 Maven 打包,可以观察 Maven 本地仓库检查是否打包成功
1 删除 ProductApi, ProductInfo. 2. 引入依赖:
<dependency>
<groupId>org.example</groupId>
<artifactId>product-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
修改项目中 ProductApi, ProductInfo 的路径为 product-api 中的路径
@EnableFeignClients(basePackages = {"com.bite.api"})
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
也可以指定需要加载的 Feign 客户端
@EnableFeignClients(clients = {ProductApi.class})
以上就是本文全部内容,本文主要为大家介绍了 Spring Cloud 中实现远程调用的另一个组件 -OpenFeign 的相关知识与操作。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online