外观
一、OpenFeign简介
什么是OpenFeign
OpenFeign目前是Spring Cloud 二级子项目。平时说的Feign指的是Netflix下的Feign,现在我们学习的是OpenFeign,是Spring提供的。
OpenFeign是一种声明式、模板化的HTTP客户端(仅在Application Client中使用)。
OpenFeign的作用:声明式的服务调用。声明式调用是指,就像调用本地方法一样调用远程方法,无需感知操作远程http请求。学习完OpenFeign后可以不使用RestTemplate进行调用。
使用OpenFeign时就好像在写控制器方法,OpenFeign都是写在接口中,在声明的方法上添加SpringMVC注解或声明的参数上添加SpringMVC注解就可以完成调用远程的控制器方法。
OpenFeign的执行流程

文字说明
整体流程说明:
Application Service向Eureka Server注册服务。Application Client从Eureka Server中发现服务。- 在
Application Client中调用OpenFeign接口中方法。 Application Client中OpenFeign通过应用程序名访问Application Service。- OpenFeign访问远程服务时,基于Ribbon实现负载均衡。
二、第一个OpenFeign项目
Application Service应用准备
创建Application service项目
创建项目application_service
POM依赖
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>编写配置文件
编写配置文件application.yml
yaml
server:
port: 8888
spring:
application:
name: application-service编写controller方法
java
package com.lhp.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* `@author` lhp
* `@create` 2025-08-14 21:17
*/
`@RestController`
public class UserController {
`@GetMapping`("/test1")
public String test1(){
return "我是test1方法被执行返回的内容!";
}
`@PostMapping`("/test2")
public String test2(){
return "我是test2方法被执行返回的内容!";
}
}编写启动类
编写启动类,然后启动项目。
Application Client应用开发
创建Application Client项目
创建项目application_client_openfeign
POM依赖
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>编写配置文件
编写配置文件application.yml
yaml
server:
port: 9999
spring:
application:
name: eureka-client-open-feign编写OpenFeign本地接口
编写接口 com.lhp.feign.AppServiceOpenfeignClient
java
/**
* 定义接口,基于注解,实现声明式远程服务调用。
* 技术是OpenFeign。
* 需要确定的事情:
* 1. 访问的远程服务名称是什么。
* 2. 访问的远程服务具体地址是什么。
* 3. 访问的远程服务请求方式是什么。
* 4. 访问的远程服务,参数是什么。
* 5. 访问的远程服务,返回结果类型是什么。
*
* FeignClient - 代表当前的接口是一个OpenFeign客户端,要访问远程的服务。
* 具体的实现类对象,由spring cloud动态生成代理对象来实现。
* 必要属性: value - 要访问的远程服务命名是什么。
*/
`@FeignClient`("application-service")
public interface UserFeignClient {
/**
* 定义方法。使用SpringMVC注解+方法定义,实现远程服务访问规则定义。
* 建议写法: 找到要访问的控制器。复制对应的方法签名即可。
*
* GetMapping - 约束了请求方式
* 注解属性value - 约束了请求的具体地址
* 方法返回值 - 约束了远程服务返回结果类型
* 方法参数表 - 约束了远程服务的请求参数
*/
`@GetMapping`("/test1")
public String test1();
/**
* post请求,无参数
* `@return`
*/
`@PostMapping`("/test2")
public String test2();
}编写服务接口即实现
也就是编写application client端的service层代码,编写service层的接口和实现类,在实现类中通过OpenFeign调用远程的接口。
编写服务接口
编写接口com.lhp.serivce.OpenfeignService
java
/**
* 本地服务接口。
*/
public interface OpenfeignService {
String test1();
String test2();
}编写服务实现
编写实现com.lhp.serivce.impl.OpenfeignServiceImpl
java
/**
* 本地服务实现类
*/
`@Service`
public class OpenFeignServiceImpl implements OpenFeignService {
`@Autowired`
private UserFeignClient feignClient;
`@Override`
public String test1() {
return feignClient.test1();
}
`@Override`
public String test2() {
return feignClient.test2();
}
}编写控制器
编写控制器com.lhp.controller.OpenfeignController
java
`@RestController`
public class OpenFeignController {
`@Autowired`
private OpenFeignService openFeignService;
`@GetMapping`("/test1")
public String test1(){
return openFeignService.test1();
}
`@PostMapping`("/test2")
public String test2(){
return openFeignService.test2();
}
}编写启动类型
java
/**
* EnableFeignClients - 开启Openfeign技术。让spring cloud扫描Openfeign相关注解,
* 生成动态代理实现对象。
* 可选属性 basePackages = {"feign接口所在包1", "feign接口所在包2"}
* 默认扫描当前类型所在包,及所有子孙包。
*/
`@SpringBootApplication`
`@EnableFeignClients`(basePackages = {"com.lhp.feign"})
public class OpenFeignAppClientApp {
public static void main(String[] args) {
SpringApplication.run(OpenFeignAppClientApp.class, args);
}
}测试
启动application_client_openfeign项目,访问 http://localhost:8888/test1 和 http://localhost:8888/test2。观察结果。
总结
核心实现原理
OpenFeign通过为@FeignClient注解的接口生成动态代理对象,在调用时根据注解信息构造请求地址,最终通过HTTP客户端(如JDK的HttpURLConnection或第三方库HttpClient/OkHttp)发起远程调用。这种设计使其能像调用本地方法一样进行服务间通信。
与RestTemplate的对比
- 调用方式:
RestTemplate需要手动拼接URL和参数,而OpenFeign通过接口声明自动完成请求封装。 - 负载均衡:两者均可整合Ribbon实现负载均衡,但OpenFeign的负载均衡通过动态代理自动处理,
RestTemplate需显式添加@LoadBalanced注解。 - 底层通信:
RestTemplate直接使用HTTP客户端,而OpenFeign在代理层封装了HTTP调用细节。
技术演进
OpenFeign最初由Netflix开发,后捐赠给Spring Cloud社区并整合了Ribbon等组件,其设计目标是简化声明式REST调用。虽然早期版本可能依赖RestTemplate,但现代实现已独立于具体HTTP客户端。
总结来说,OpenFeign和RestTemplate是两种独立的服务调用方案,前者通过动态代理提供更高级的抽象,后者则是更基础的HTTP工具类。
三、OpenFeign参数管理
传递简单类型参数
GET请求传递参数
修改OpenFeign接口,增加方法
java
/**
* 传递参数。
* 远程服务无论参数类型是什么(简单参数,自定义实体类型),都是模拟浏览器,传递多个简单参数。
*
* openfeign传递参数的时候,方法参数表要固定要求:
* 1. 只能有唯一一个参数不写任何注解。
* 2. 传递参数的时候,都要提供`@RequestParam注解`,代表这个方法参数,是一个表单或请求头参数。
* 注解的属性value,必须定义,代表具体的请求参数名称是什么。
* 3. 如果是Restful参数,必须提供`@PathVariable注解`,代表这个方法参数是一个restful参数。
* 且PathVariable注解的属性value,必须定义,对应路径地址中的占位变量名。
*/
`@GetMapping`("/getWithParams")
public String getWithParams(`@RequestParam`("name") String name, `@RequestParam`("age") int age);修改服务接口和实现,增加方法
java
String getWithParams(String name, int age);java
/**
* get请求,有参数
* `@param` name
* `@param` age
* `@return`
*/
`@Override`
public String getWithParams(String name, int age) {
String result = appServiceOpenfeignClient.getWithParams(name, age);
System.out.println("远程返回:" + result);
return result;
}修改控制器,增加方法
java
`@RequestMapping`("/getWithParams")
public String getWithParams(String name, int age){
System.out.println("控制器执行 - getWithParams()");
return openfeignService.getWithParams(name, age);
}POST请求传递参数
修改OpenFeign接口,增加方法
java
/**
* post请求,有参数
* `@return`
*/
`@PostMapping`("/postWithParams")
public String postWithParams(`@RequestParam`("name") String name, `@RequestParam`("age") int age);修改服务接口和实现,增加方法
java
String postWithParams(String name, int age);java
`@Override`
public String postWithParams(String name, int age) {
String result = appServiceOpenfeignClient.postWithParams(name, age);
System.out.println("远程返回: " + result);
return result;
}修改控制器,增加方法
java
`@RequestMapping`("/postWithParams")
public String postWithParams(String name, int age){
System.out.println("控制器执行 - postWithParams()");
return openfeignService.postWithParams(name, age);
}请求体传递参数
修改OpenFeign接口,增加方法
java
/**
* RequestBody参数 - 一个方法上只能定义唯一一个没有任何注解修饰的方法参数。就是RequestBody参数。
* 建议还是使用`@RequestBody注解修饰指定的方法参数`。明确这个参数是如何传递的。
* 一个方法,可以在传递RequestBody参数的同时,传递其他参数。
* `@return`
*/
`@PostMapping`("/postBodyParams")
public String postBodyParams(`@RequestBody` Map<String, String> params, `@RequestParam`("name") String name);修改服务接口和实现,增加方法
java
String postBodyParams(Map<String, String> params, String name);java
`@Override`
public String postBodyParams(Map<String, String> params, String name) {
String result = appServiceOpenfeignClient.postBodyParams(params, name);
System.out.println("远程返回:" + result);
return result;
}修改控制器,增加方法
java
`@RequestMapping`("/postBodyParams")
public String postBodyParams(String name){
// 把请求参数封装处理,并调用服务方法
Map<String, String> params = new HashMap<>();
params.put("name", name);
System.out.println("控制器执行 - postBodyParams()");
return openfeignService.postBodyParams(params, name);
}restful参数
修改OpenFeign接口,增加方法
java
/**
* restful参数。要求注解必须提供,属性value必须定义。一般我们开发的时候都会省略value属性的设置。
* 原因参考1.2.2章节。
* `@param` name
* `@param` age
* `@return`
*/
`@RequestMapping`("/restfulParams/{name}/{age}")
public String restfulParams(`@PathVariable`("name") String name,
`@PathVariable`("age") int age);restful参数注意事项
在spring-cloud-starter-openfeign中,对PathVariable有额外的约束。
要求必须为PathVariable注解的属性value赋值,如果未赋值属性value,则抛出异常:
java.lang.IllegalStateException: PathVariable annotation was empty on param 0.具体如下:

源码中的约束:

一般情况下,此异常不会出现。但并不代表此约束不存在,我们开发的时候,如果工程是继承spring-boot-starter-parent,则不会出现此问题,因为在spring-boot-starter-parent中,定义了注解参数自动补偿插件,插件是:
xml
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>如果工程不是继承spring-boot-starter-parent,则可以手工管理插件。
修改服务接口和实现,增加方法
java
String restfulParam(String name, int age);java
`@Override`
public String restfulParam(String name, int age) {
String result = appServiceOpenfeignClient.restfulParams(name, age);
System.out.println("远程返回:" + result);
return result;
}修改控制器,增加方法
java
`@RequestMapping`("/restfulParam/{name}/{age}")
public String restfulParam(`@PathVariable` String name, `@PathVariable`("age") int age){
System.out.println("控制器执行 - restfulParam()");
return openfeignService.restfulParam(name, age);
}四、OpenFeign通讯优化
GZIP简介
gzip介绍:gzip是一种数据格式,采用用deflate算法压缩数据;gzip是一种流行的数据压缩算法,应用十分广泛,尤其是在Linux平台。
gzip能力:当Gzip压缩到一个纯文本数据时,效果是非常明显的,大约可以减少70%以上的数据大小。
gzip作用:网络数据经过压缩后实际上降低了网络传输的字节数,最明显的好处就是可以加快网页加载的速度。网页加载速度加快的好处不言而喻,除了节省流量,改善用户的浏览体验外,另一个潜在的好处是Gzip与搜索引擎的抓取工具有着更好的关系。例如 Google就可以通过直接读取gzip文件来比普通手工抓取更快地检索网页。
HTTP协议中关于压缩传输的规定(原理)

第一:客户端向服务器请求头中带有:Accept-Encoding:gzip, deflate 字段,向服务器表示,客户端支持的压缩格式(gzip或者deflate),如果不发送该消息头,服务器是不会压缩的。
第二:服务端在收到请求之后,如果发现请求头中含有Accept-Encoding字段,并且支持该类型的压缩,就对响应报文压缩之后返回给客户端,并且携带Content-Encoding:gzip消息头,表示响应报文是根据该格式压缩过的。
第三:客户端接收到响应之后,先判断是否有Content-Encoding消息头,如果有,按该格式解压报文。否则按正常报文处理。
在OpenFeign技术中应用GZIP压缩
在Spring Cloud微服务体系中,一次请求的完整流程如下:

在整体流程中,如果使用GZIP压缩来传输数据,涉及到两次请求-应答。而这两次请求-应答的连接点是Application Client,那么我们需要在Application Client中配置开启GZIP压缩,来实现压缩数据传输。
配置OpenFeign请求-应答的GZIP压缩
在交互数据量级不够的时候,看不到压缩内容。
这里只开启Feign请求-应答过程中的GZIP,也就是浏览器-Application Client之间的请求应答不开启GZIP压缩。
在全局配置文件中,使用下述配置来实现OpenFeign请求-应答的GZIP压缩(在使用Feign的微服务配置即可)
yaml
# 配置openfeign请求和应答的gzip压缩处理
feign:
compression:
request:
enabled: true # 开启请求压缩处理。默认false
min-request-size: 128 # 请求容量多少,开始压缩。默认2048字节
mime-types: text/html, text/xml, text/plain, text/css, application/json # 请求头content type是什么,做压缩处理
response:
enabled: true # 开启响应压缩处理。默认falseTomcat服务器GZIP优化配置
在配置文件中提供下述配置(在浏览器访问的那个微服务中配置即可):
yaml
server:
compression:
enabled: true # 是否开启响应压缩处理。默认false
mime-types: text/html, text/xml, text/plain, text/css, text/javascript, application/javascript, application/json, application/xml # 响应content type什么类型,做压缩处理。
min-response-size: 128 # 响应容量多大,做压缩处理。 默认2048字节测试
最终我们可以在浏览器中观察:
在配置前的响应头中是没有 content-encoding** **的
在配置后的响应头中是有 content-encoding 的