OpenFeign的使用
# OpenFeign的使用
# 服务间的调用方式
Spring Cloud中服务之间的调用方式主要有:
- Ribbon + RestTemplate:需要手动配置,负载均衡由Ribbon处理,比较灵活但代码较多。
- Feign:
声明式接口
,集成Ribbon和Hystrix,简化开发。 - OpenFeign:OpenFeign是Feign的一个升级版本,在Spring Cloud中被推荐使用以替代原Feign。继承了Feign的所有优点,并进行了增强。
- 异步消息(如RabbitMQ):通过消息队列解耦,适合异步处理。
- gRPC:Spring Cloud 并没有直接支持 gRPC,但可以通过社区维护的第三方库来集成,比如
grpc-spring-boot-starter
,属于扩展方式。gRPC是由Google开发的一种高性能、开源的远程过程调用(RPC)框架。 - Spring Cloud Gateway:作为API网关,进行路由和过滤,可能用于外部请求的路由,但内部服务间调用可能还是用前几种。
每种方式都有其适用场景,选择时需根据具体需求考虑性能、易用性、扩展性等因素。
例如,对于简单的服务调用,RestTemplate或OpenFeign可能是较好的选择;而对于需要高性能和跨语言支持的场景,gRPC可能更为合适。Spring Cloud Gateway则通常用于构建微服务架构中的网关层,处理外部请求的分发和安全控制等任务。
# 使用OpenFeign替代 RestTemplate
在Spring Cloud环境中推荐使用OpenFeign替代RestTemplate
的原因主要集中在以下几个方面:
- 声明式服务调用
- 简化编码:OpenFeign通过注解的方式定义HTTP请求,使得开发者只需声明接口及方法即可实现远程服务的调用,而不需要手动构建HTTP请求。这种方式大大简化了代码编写过程。
- 提高可读性:相比
RestTemplate
,OpenFeign的声明式编程模型
让服务调用逻辑更加直观、易于理解。
自动集成Spring MVC注解:OpenFeign支持Spring MVC注解(如
@RequestMapping
,@GetMapping
等),这使得从传统的Spring MVC控制器迁移到微服务客户端变得非常容易。内置负载均衡:当与Spring Cloud Netflix Ribbon结合使用时(尽管Ribbon已经进入维护模式,但OpenFeign默认集成了负载均衡功能),OpenFeign可以自动利用Eureka提供的服务注册发现机制进行客户端负载均衡,无需额外配置。
强大的扩展能力:OpenFeign允许通过自定义配置类和拦截器来增强其功能,例如添加请求头、处理异常等,提供了高度的灵活性。
支持多种序列化格式:除了JSON,OpenFeign还支持其他数据格式(如XML)作为请求和响应的内容类型,增加了其适用范围。
减少样板代码:使用
RestTemplate
时,每次发起HTTP请求都需要创建模板实例并设置相应的参数,而在OpenFeign中,只需要定义一次接口,就可以重复使用这些定义,减少了大量的样板代码。
# 硬编码问题
有人说弃用RestTemplate
的一个很大原因,是因为它的硬编码问题,其实不然,下面是它跟nacos进行服务间的调用。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
private final RestTemplate restTemplate;
@Autowired
public ConsumerController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/consumer")
public String consumerService() {
// 注意这里使用的是服务名,而非具体的IP和端口
String result = restTemplate.getForObject("http://provider-service/provider", String.class);
return "Consumer Service Call Result: " + result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
在这个例子中,“provider-service”是注册到Nacos中的服务提供者的名字。RestTemplate
会自动通过Nacos发现该服务的所有实例,并根据负载均衡策略选择一个实例进行调用。以上就一定程度上解决了该问题。
但为什么还要弃用它呢?
- 缺乏对复杂负载均衡策略的支持
- 尽管Ribbon提供了基本的负载均衡策略(如轮询、随机等),但对于更复杂的负载均衡需求(例如基于响应时间、流量权重等的自定义策略),可能需要额外的工作来实现或集成第三方库。
- 错误处理与重试机制不足
- 默认情况下,
RestTemplate
对于网络异常或服务不可达的情况处理较为简单。虽然可以通过添加拦截器等方式增强其错误处理能力和重试逻辑,但这增加了开发者的负担。 - 对比之下,OpenFeign提供了更加灵活的错误处理机制(如通过实现
ErrorDecoder
接口),以及内置的重试功能,使得处理这类问题更为简便。
- 维护性和扩展性
- 使用
RestTemplate
进行服务调用时,随着项目规模的增长,手动构建HTTP请求可能会变得冗长且难以维护。尤其是在需要处理多种不同的HTTP方法(GET、POST等)、不同格式的数据转换(JSON、XML等)时,代码量和复杂度都会显著增加。如果服务名很多且很长,那调用时,岂不是要传入很多,很长的参数。 - OpenFeign通过声明式的方式简化了这些操作,减少了模板代码的数量,提高了代码的可读性和维护性。
能不能像我们去调用以前的一个逻辑方法一样,调用我们之前的一个业务方法一样,去调用我们的远程方法呢?比方说我去要用一个stock service点点什么点这个reduct方法,这样直接来请求我的远程方法,我们就可以直接来接收这个返回参数了,这样多简便,对不对?
Feign就实现了这个功能。
注意:
OpenFeign是声明在我们的这个服务消费端的,也就是在我们的客户端的。
不要把它声明在我们的这个服务提供方了。
# 声明式的服务间调用
使用SpringCloud LoadBalancer+RestTemplate时,利用RestTemplate对http请求的封装处理形成了一套模版化的调用方法。
但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,但这样的调用通常需要编写大量的样板代码,如构建 HTTP 请求、处理响应等。例如,使用 RestTemplate
手动构建请求和处理响应的代码可能非常冗长,而使用 OpenFeign 只需几行注解即可完成相同的功能。
// 传统方式
ResponseEntity<String> response = restTemplate.getForEntity("http://your-service-name/endpoint", String.class);
String result = response.getBody();
2
3
这样的调用数量上来后,会特别繁琐与麻烦。
为了解决这个问题,OpenFeign
出现了, 它通过声明式接口和注解
,自动生成这些代码,大大减少了开发工作量。
在OpenFeign的实现下,我们只需创建一个接口并使用注解的方式来配置它(在一个微服务接口上面标注一个@FeignClient注解即可),即可完成对服务提供方的接口绑定,统一对外暴露可以被调用的接口方法,大大简化和降低了调用客户端的开发量,也即由服务提供者给出调用接口清单,消费者直接通过OpenFeign调用即可。
// OpenFeign 方式
@FeignClient(name = "your-service-name")
public interface YourServiceClient {
@GetMapping("/endpoint")
String callService();
}
2
3
4
5
6
# 它的特点
OpenFeign除了简化服务间的调用外,它还有以下几个优势:
集成服务发现和负载均衡:
- 与 Spring Cloud 的服务发现组件和负载均衡组件无缝集成,自动处理服务发现和负载均衡。
- 提高了系统的可维护性和灵活性。
断路器支持:
- 与 Hystrix 集成,提供断路器功能,增强了系统的稳定性和容错能力。
可扩展性:
- 支持自定义编码器、解码器、错误处理器等,满足不同业务场景的需求。
但,在使用OpenFeign的时候请注意——性能开销
,这个问题。
- 由于 OpenFeign 是基于注解和接口的动态代理实现,可能会引入一定的性能开销。
- 在高并发场景下,需要注意性能调优。
# 总结
Spring Cloud OpenFeign 是一个强大的声明式 HTTP 客户端,它简化了微服务之间的调用,提供了服务发现、负载均衡和断路器等功能。通过简单的接口定义和注解,开发者可以更加专注于业务逻辑,而无需关心底层的 HTTP 请求细节。
# 相关资料和文档
SpringCloud OpenFeign官网:
# 快速上手
SpringCloud OpenFeign官网特性与使用说明 (opens new window)
- https://docs.spring.io/spring-cloud-openfeign/reference/
SpringCloud OpenFeign官网首页介绍 (opens new window)
- https://spring.io/projects/spring-cloud-openfeign#learn
# 上手步骤
注意,OpenFeign,需要跟 服务注册组件一起使用,比如consul或者nacos等。
第一步:引入对应pom坐标,官网的对应说明如下:
To include Feign in your project use the starter with group
org.springframework.cloud
and artifact idspring-cloud-starter-openfeign
.
<!--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>
2
3
4
5
6
7
8
9
从Spring Cloud 2020.0.0版本开始,Ribbon(之前用于负载均衡的默认工具)被Spring Cloud LoadBalancer取代。
因此,如果你遇到了"No Feign Client for loadBalancing defined"这样的错误提示,很可能是由于缺少spring-cloud-starter-loadbalancer
依赖或未正确启用Spring Cloud LoadBalancer。
同时,你还要注意的是,这个OpenFeign,它是属于spring cloud,所以你要先确定一下有添加spring load的版本管理器。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.5</version> <!-- 根据需要选择合适的版本 -->
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2
3
4
5
6
7
8
9
10
11
注意:OpenFeign是在消费端进行使用。
第二步:修改配置文件,让服务注册中心
server:
port: 80
spring:
application:
name: cloud-consumer-openfeign-order
####Spring Cloud Consul for Service Discovery
cloud:
consul:
host: localhost
port: 8500
discovery:
prefer-ip-address: true #优先使用服务ip进行注册
service-name: ${spring.application.name}
2
3
4
5
6
7
8
9
10
11
12
13
14
第三步:在主类或配置类中添加注解@EnableFeignClients
(需要开放接口给其他服务调用的微服务),开启OpenFeign,启用服务间的发现。
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2
3
4
5
6
7
8
9
第四步:将之前的接口进行抽象添加注解,这些接口可以单独放在一个module中,也可以就在原有module中。这一步也成为定义Feign客户端。
// 定义 Feign 客户端
@FeignClient(name = "your-service-name")
//@FeignClient(name = "order" ,path = "/controller-path")
public interface YourServiceClient {
@GetMapping("/endpoint")
String callService();
}
2
3
4
5
6
7
your-service-name,就是你注册到服务中心的微服务名称,下方的接口callService()
,就是your-service-name-微服务中对应的接口,请注意接口对应的返回值也要一致。如果controller类上有mapping路径,则需要加上属性 path,值则为 controller-path。
到此,OpenFeign实现服务间的调用全部完成。
OpenFeign,它的这种远程调用的方式,它会自动的帮我们去集成ribbon,帮我们去集成负载均衡器.
同样的会去帮我们集成我们的nacos。根据我们在接口,注解上,写的这个服务名,去我们nacos当中获取对应的所有的服务实例。再结合我们的这个负载均衡器来进行调用,使用了动态代理。这些是基本的原理。
在需要的服务中注入并使用 Feign 客户端,即可调用其他微服务接口。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class YourService {
@Autowired
private UserServiceClient userServiceClient;
public User getUserById(Long id) {
return userServiceClient.getUser(id);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
最后提示,为了更好的使用,可以将以上第四步中的接口,放到一个公共module中,从而达到更高效的利用率。
# 原理探究
OpenFeign 的实现原理主要包括以下几个方面:
- 动态代理
- 注解解析
- HTTP 请求构建和发送
- 以及响应处理。
先大致说下流程:
首先是要通过咱们启动类上的@EnableFeignClients
这个注解,开启对Feign接口代理对象的构建以及装配。在注解源码中咱们可以看到它导入了一个叫做FeignClientsregister的注解。这个注解会扫描添加了`@FeignClientd注解的接口,并且创建远程调用的代理对象。而且还会将这个对象注入到咱们的spring容器中。这样我们就可以在需要的位置直接注入rpc远程调用的代理对象。
而在构建代理对象时,会为每一个远程调用的方法生成一个 requestTemplate 的请求模板实例。内部存储了整个请求的路径,请求方式以及请求参数等等。在发生请求调用时,会基于咱们的 requestTemplate 生成一个request的实例。在做具体请求发送前,需要基于FeignClient 的客户端的负载均衡实例去选择合适的服务。那一般会采用ribbon或者是咱们的load baLance,再基于成员变量client的对象的delegate去完成HTTP的请求的提交。而delay gate类型一般咱们会采用Apache 提供的HTTP client去完成远程调用。
# 1. 动态代理
OpenFeign 使用 Java 的动态代理机制来生成接口的实现类。当开发者定义了一个带有 @FeignClient
注解的接口时,OpenFeign 会为这个接口生成一个代理类。这个代理类负责处理实际的 HTTP 请求和响应。
# 2. 注解解析
OpenFeign 通过解析接口上的注解来确定 HTTP 请求的详细信息,包括请求方法、URL、请求参数等。常用的注解包括:
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
等,用于指定 HTTP 方法。@PathVariable
、@RequestParam
、@RequestBody
等,用于指定请求参数的来源和格式。
# 3. HTTP 请求构建和发送
OpenFeign 使用 HttpClient
、OkHttp
或 Ribbon
等 HTTP 客户端库来构建和发送 HTTP 请求。具体步骤如下:
- 解析注解:根据接口上的注解解析出请求方法、URL、请求参数等信息。
- 构建请求:使用解析出的信息构建 HTTP 请求。
- 发送请求:通过配置的 HTTP 客户端发送请求。
# 4. 响应处理
OpenFeign 处理 HTTP 响应并将结果转换为接口方法的返回类型。具体步骤如下:
- 接收响应:从 HTTP 客户端接收响应。
- 解析响应:将响应体解析为指定的返回类型。
- 返回结果:将解析后的结果返回给调用者。
# 总结
其他服务调用这些接口,先去OpenFeign中找,OpenFeign通过代理进行请求,得到结果后,返回给调用者。是不是有拦截器那味了。