FeignClient用法笔记
1. FeignClient简介
在微服务架构中总需要各个服务相互调用,各个服务又是以接口方式暴露,所以需要Http远程调用;为了简化调用,Spring Cloud OpenFeign 库提供了org.springframework.cloud.openfeign.FeignClient 注解:
- 它用于客户端,是一种声明式、模板化的HTTP客户端。使用该注解声明一个接口为远程服务的客户端,通过简单的接口描述远程调用,而不必手动编写低级的 HTTP 客户端代码。
- 当你将 @FeignClient 注解添加到接口上时,Spring Cloud OpenFeign 将为你提供一个实现了该接口的代理实例,你可以直接通过这个代理实例调用远程服务。Feign 自动处理了请求的发送以及响应的接收和转换。
- 使用Feign, 我们可以做到使用HTTP请求远程服务时能与调用本地方法一样的编码体验,开发者完全感知不到这是远程方法,更感知不到这是个HTTP请求。
2. 使用Demo
2.1. 服务提供方
服务提供方是运行在服务端的应用程序,通常会暴露 REST API 等接口供其他服务调用。这个端点可以用 Spring MVC 注解来创建。比如服务提供方提供了如下接口:
- 假设服务方的域名为:https://employee.bear.com/
@RestController
public class EmployeeWebServiceController{@Autowiredprivate EmployeeService employeeService;//查询所有员工@RequestMapping(value = "api/employee/list", method = RequestMethod.GET)public List<EmployeeModel> list() {return JSON.parseArray(JSON.toJSONString(employeeService.list()), EmployeeMode.class);}
}
2.2. 服务消费者(客户端)
服务消费者是运行在客户端的程序,它使用 @FeignClient 声明的接口来调用远程服务提供者的端点,大致分两步:
step1.使用 @FeignClient 注解, 生成服务的消费者客户端
//name和url是【服务提供者】的地址(url)和 名称(name)
//name和url同时存在,优先使用url描述的服务端地址来访问服务端
@FeignClient(name="", url = "https://employee.bear.com")
public interface EmployeeFeignClient {//注意:这里的path和方法定义要和EmployeeWebServiceController里对应的path和方法保持一致@RequestMapping(value = "api/employee/list", method = RequestMethod.GET)public List<EmployeeModel> list();
}
这里需要注意:
- FeignClient接口中的方法&路径需要和服务提供者提供接口的方法&路径要保持一致,比如这里的GET方法,path=“api/employee/list”都要和EmployeeWebServiceController里保持一致。
- 服务消费者需要在Java的启动类上加上@EnableFeignClients, 用来启用Feign
step2.在客户端代码中,就像调用本地方法一样去调用定义的feign接口就可以了
// step2.在消费者服务中的使用客户端
@Service
public class EmployeeConsumerService {//使用了Feign,就像调用本地方法一样,调用远程HTTP请求@Autowiredprivate final EmployeeFeignClient employeeFeignClient;public List<EmployeeModel> listEmployee() {List<EmployeeModel> employeeModels = employeeFeignClient.list(); return employeeModels;}
}
在这个例子中,EmployeeFeignClient 接口用 @FeignClient 注解声明了要调用的 Employee 服务。name 属性定义服务名,url 为服务提供者的地址,EmployeeFeignClient 接口的方法对应服务提供者控制器中的端点。
EmployeeConsumerService 是在服务消费者端使用的一个服务,它注入了 EmployeeFeignClient 的实例并调用其方法来获取用户信息。当调用这些客户端方法时,OpenFeign 类库会自动构建 HTTP 请求,调用远程服务,并将响应反序列化为定义的 Java 对象。
3. 推荐用法
上述使用Demo里可以看到使用FeignClient有个约束:客户端在使用FeignClient时,要求FeignClient接口定义的方法&路径需要和服务提供者提供接口的方法&路径要保持一致。
在很多微服务架构中,为了减少代码重复和保持服务间接口的一致性,通常会将客户端接口以 SDK的形式提供给其他服务使用。也就是说,服务提供者会创建一个客户端库(SDK),这个库包含用于与服务交互的 FeignClient 客户端接口,其他服务可以引用这个库来作为它们的服务消费者。
3.1. 服务提供方
3.1.1. 定义FeignClient 接口
首先,服务提供者定义一个 EmployeeFeignClient 接口,并将其放在一个可共享的sdk库中。
// 注解里是【服务提供者】的地址(url)和 名称(name)
//name和url同时存在,优先使用url描述的服务端地址来访问服务端
@FeignClient(name="employee-service", url = "https://employee.bear.com")
public interface EmployeeFeignClient {@RequestMapping(value = "api/employee/list", method = RequestMethod.GET)public List<EmployeeModel> list();
}
EmployeeFeignClient定义完成后,服务提供者可以构建和发布 SDK 到 Maven 仓库:
<!-- employee-service-sdk/pom.xml -->
<groupId>com.example.employee-service</groupId>
<artifactId>employee-service-sdk</artifactId>
<version>1.0.0</version><build>
<!-- 配置构建过程 -->
</build>
3.1.2. 实现服务端点
服务提供者通过 实现 EmployeeFeignClient 接口,来确保其Controller与FeignClient接口的定义保持一致。这种方式有助于确保服务端点的实现与定义的Feign客户端接口同步,避免因接口更改而导致的不一致性。
EmployeeController 实现了 EmployeeFeignClient 接口,这样它就承担了两个角色:
- 作为服务提供者的控制器:EmployeeController直接利用了 EmployeeFeignClient 中定义的接口方法 list,并通过 EmployeeService 实例来调用服务层逻辑
- 作为 EmployeeFeignClient 接口的实现:服务提供者在定义 EmployeeFeignClient 接口时,也必须实现接口中定义的方法,这种实现方式从而提高了代码的一致性和可维护性。服务消费方可以通过FeignClient代理直接调用远程服务,而无需关心服务端的实现细节。
@RestController
public class EmployeeController implements EmployeeFeignClient {@Autowiredprivate final EmployeeService userService;@Overridepublic List<EmployeeModel> list() {return JSON.parseArray(JSON.toJSONString(employeeService.list()), EmployeeMode.class);}
}
3.2. 服务消费方(客户端)
服务消费者将 SDK 作为依赖引入到项目中后,可以像调用本地方法一样直接使用 EmployeeFeignClient,Feign 会处理背后的远程服务调用。
//step1.在服务消费者项目的pom.xml中添加对sdk的依赖
<dependency>
<groupId>com.example.employee-service</groupId>
<artifactId>employee-service-sdk</artifactId>
<version>1.0.0</version>
</dependency>// step2.在消费者服务中的使用客户端
@Service
public class EmployeeConsumerService {//使用了Feign,就像调用本地方法一样,调用远程HTTP请求@Autowiredprivate final EmployeeFeignClient employeeFeignClient;public List<EmployeeModel> listEmployee() {// 直接通过 FeignClient 调用服务提供者的方法List<EmployeeModel> employeeModels = employeeFeignClient.list(); return employeeModels;}
}
通过这种方式,服务提供者负责维护和发布服务客户端代码,而服务消费者可以专注于自身的业务逻辑的实现。
注意:服务消费者需要在Java的启动类上加上@EnableFeignClients, 用来启用Feign
4. 其他
在实际情况中,服务提供者的地址(url)通常通过服务发现机制来配置,例如使用 Eureka,此时你不需要在 @FeignClient 注解里硬编码服务的 URL,Spring Cloud 会利用服务名来自动查找和解析服务的实际地址。
参考文档:
- FeignClient地址:https://github.com/OpenFeign/feign
- 服务提供者RequestMapping一致形式:https://blog.csdn.net/shijiujiu33/article/details/94732895
- 服务提供者实现Feign接口的形式:https://blog.csdn.net/shijiujiu33/article/details/94735494
-
@FeignClient使用详解:https://blog.csdn.net/xhwangSGTL/article/details/111991017
-
如何优雅的使用Feign?https://juejin.cn/post/7031331378289442