在上一期中,我们借助 Nacos 实现了服务的注册与发现,使得微服务之间可以通过服务名动态获取可用实例,从而以 HTTP 方式完成远程调用。这种方式虽然灵活,但在实际开发中,若每次调用都手动拼接 URL、构造请求头、处理响应等,不仅代码冗余,也违背了"DRY(Don't Repeat Yourself)"原则。
为了解决这一问题,Spring Cloud 提供了声明式的 HTTP 客户端------OpenFeign。通过 OpenFeign,我们只需定义一个接口并添加相应注解,即可实现对远程服务的调用,无需编写繁琐的模板代码。这不仅大幅简化了开发流程,还提升了代码的可维护性与可读性。
1. 快速实现
1. 引入依赖
XML
<!--openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--负载均衡器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
由于远程调用的目标服务可能存在多个运行实例,为了合理分配请求流量,我们需要借助负载均衡机制从可用实例中动态选择一个进行访问。因此,在使用 OpenFeign 时,通常还需引入负载均衡器(如 Spring Cloud LoadBalancer)的相关依赖,以实现自动化的实例选择与请求分发。
2. 启用 OpenFeign
使用 OpenFeign 需要在对应服务的启动类上添加 @EnableFeignClients 的注解
java
@EnableFeignClients // 启用 OpenFeign
@SpringBootApplication
public class ProjectApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
}
3. 编写OpenFeign客户端
创建 client 用来存放远程调用相关逻辑的代码
java
/**
* 声明式远程调用客户端,用于调用注册在 Nacos 中名为 "xxx-service" 的服务。
* OpenFeign 会根据此接口自动生成 HTTP 请求,无需手动编写实现类。
*/
@FeignClient("xxx-service")
public interface XxxClient {
/**
* 调用远程服务的 /text 接口,获取文本列表。
*
* @param keyword 查询关键字(示例参数)
* @return 返回文本列表
*/
@GetMapping("/text")
List<String> query(@RequestParam("keyword") String keyword);
}
4. 使用 FeignClient
java
@Service
public class YourService {
@Autowired
private XxxClient xxxClient;
public void someMethod(String keyword) {
List<String> key = xxxClient.query(keyword);
// 后续逻辑...
}
}
2. 连接池
Feign 本身并不直接发起 HTTP 请求,而是依赖于底层的 HTTP 客户端实现。其支持的 HTTP 客户端包括以下几种:
- HttpURLConnection:JDK 自带的默认实现,不支持连接池,性能较低;
- Apache HttpClient :功能强大,支持连接池,适用于高并发场景;
- OkHttp :轻量高效,同样支持连接池,且具有良好的默认配置和扩展性。
由于 HttpURLConnection 缺乏连接复用能力,在生产环境中通常会选用支持连接池的客户端(如 OkHttp 或 Apache HttpClient)来替代默认实现,以提升性能和稳定性。
1. 引入依赖
在调用方引入依赖
XML
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
2. 开启连接池
XML
feign:
okhttp:
enabled: true # 开启OKHttp功能
3. 抽取 Feign 客户端
在实际业务开发中,系统往往涉及大量的远程服务调用。如果将这些调用逻辑分散在各个模块中,不仅会导致代码重复,还会显著增加后期维护和管理的复杂度。
为了解耦和提升可维护性,建议在项目中单独创建一个 app-service(或类似命名,如 common-feign、client-sdk 等)模块,专门用于集中管理所有远程调用相关的 Feign Client 接口及其配置。这样既能实现逻辑复用,又能统一维护接口契约,便于团队协作和版本控制。
1. 引入依赖
将远程调用逻辑抽取到独立的 app-service 模块后,应将所有相关的依赖(如 Feign Client 所需的 OpenFeign、OkHttp、Jackson 等)统一声明在该模块中
其他需要使用远程调用的业务模块,只需引入 app-service 作为依赖,即可直接使用其中定义的 Feign 接口,无需重复配置或添加底层依赖
2. 扫描包
当我们将相关业务逻辑迁移到 app-service 模块后,即使在主启动类上添加了 @EnableFeignClients 注解,Spring 仍可能无法自动扫描到 Feign 客户端接口。这是因为 @EnableFeignClients 默认只会扫描主启动类所在包及其子包下的 Feign 接口。
假设我们的 Feign 客户端接口统一存放在 com.model.api.client 包下(例如 XxxClient.java),而该包不在 Spring Boot 主类的默认扫描路径中,此时就需要显式指定扫描路径:
java
@EnableFeignClients(basePackage = "com.model.api.client")
@SpringBootApplication
public class ProjectApplication {
public static void main(String[] args) {
SpringApplication.run(ProjectApplication.class, args);
}
}