项目-网盘-远程调用-Feign

远程调用理论

一、远程调用方客户端

1
2
3
4
5
6
7
8
9
@FeignClient(name = "some-service")// 这个指定了远程调用的服务名,一般是在服务注册中心中注册的服务吗
// 这个注释说明spring cloud feign要创建一个这个接口的代理实例,并将其用于名"some-service"发送远程服务的请求
public interface SomeServiceClient {
@GetMapping("/some-endpoint")//定义了一个get请求,目标是远程服务some-service的/some-endpoint 端点
ResponseEntity<String> getSomeData();
// 这个是feign客户端接口的方法的定义,也就是说,如果这个方法被调用了,那么就会把相应的请求转发到这个远程服务的端点上,、
// 此外还定义了这个方法的响应,也就是ResponseEntity<String>,接受响应的结果
}

二、使用这个feign客户端的服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class SomeOtherServiceController {

@Autowired
private SomeServiceClient someServiceClient;

@GetMapping("/use-some-service")
public ResponseEntity<String> useSomeService() {
// 调用远程服务的端点
//这里就调用了一里面的客户端
return someServiceClient.getSomeData();
}
}

三、被远程调用的服务名

按照上述的,远程调用的服务名“远程服务 some-service:”

  • 这是注册在服务发现系统(如 Eureka)中的服务,name = "some-service" 对应它的服务名。
  • 它有一个暴露的 RESTful 端点 /some-endpoint,这个端点被 Feign 客户端所调用。
1
2
3
4
5
6
7
8
9
@RestController
public class SomeServiceController {

@GetMapping("/some-endpoint")
public ResponseEntity<String> getSomeData() {
// 服务逻辑处理
return ResponseEntity.ok("Some data from some-service");
}
}

四、总结

  • SomeServiceClient 是远程调用的客户端接口,通过 Feign 创建。
  • SomeServiceController 中的 /some-endpoint 是被远程调用的端点。
  • useSomeService 方法使用 Feign 客户端 someServiceClient 调用远程服务 some-service/some-endpoint

在调用方法具体流程,浏览器在调用/use-some-service的时候,被这个服务的消费者,也就是这个SomeOtherServiceController捕捉到,

在这个方法中,注入了

1
2
3
@Autowired
private SomeServiceClient someServiceClient;

在具体这个@GetMappping方法中,会调用这个远程调用的客户端接口的对象,使用里面的

1
return someServiceClient.getSomeData();

这个客户端方法里,会把这个方法的请求,转移发送到远程调用的服务端点那边去,

1
2
@GetMapping("/some-endpoint")//定义了一个get请求,目标是远程服务some-service的/some-endpoint 端点
ResponseEntity<String> getSomeData();

发送到服务的some-endpoint端点上去

然后远程调用的服务提供者提供服务,返回数据

消费者得到返回的响应,形成一个对象

openfeign的具体实现和理论

一、实现

1. pom.xml中添加依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.启动feign客户端,在启动类上使用@EnableFeignClients注释

1
2
3
4
5
6
7
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

3.定义Feign客户端接口

使用 @FeignClient 注解定义一个接口来描述远程服务的 API。注解的 name 属性指定服务的名称,这个名称应该和服务注册中心(例如 Eureka)中的服务名一致。

1
2
3
4
5
6
7
8
9
@FeignClient(name = "some-service")
public interface SomeServiceClient {

@GetMapping("/some-endpoint")
ResponseEntity<String> getSomeData();

@GetMapping("/another-endpoint")
ResponseEntity<String> getAnotherData(@RequestParam("param") String param);
}

4.使用这个feign客户端接口

就是在controller中调用这个feign客户端中的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@RestController
public class SomeOtherServiceController {

@Autowired
private SomeServiceClient someServiceClient;

@GetMapping("/use-some-service")
public ResponseEntity<String> useSomeService() {
// 调用远程服务的端点
return someServiceClient.getSomeData();
}

@GetMapping("/use-another-service")
public ResponseEntity<String> useAnotherService(@RequestParam("param") String param) {
// 调用另一个远程服务的端点
return someServiceClient.getAnotherData(param);
}
}

5.配置feign客户端

1
2
3
4
5
6
7
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: full

假设有一个名为 some-service 的微服务,它有两个端点 /some-endpoint/another-endpoint,定义如下

image-20240519175609772

二、原理

  • Feign Client@FeignClient 注解用于定义 Feign 客户端,Spring Cloud Feign 会自动创建该接口的实现。
  • 负载均衡:如果启用了 Ribbon(默认情况),Feign 客户端将通过 Ribbon 实现负载均衡,选择合适的实例进行调用。
  • Hystrix:Feign 还可以与 Hystrix 配合使用,实现熔断和回退机制。
  • 日志:通过配置 Feign 日志级别,可以记录详细的 HTTP 请求和响应。

openfeign是一个声明式的HTTP客户端,简化了REST API的调用,底层涉及了非常多的组件,包括:

  • 动态代理
  • 负载均衡
  • 拦截器
  • ……

1. 动态代理

按照之前介绍的,可以看到,声明的feign客户端其实是一个接口类,并不是一个实现类,

所以,当定义一个feign客户端接口的时候,openfeign会创建这个接口的代理对象,在调用方法的时候,代理对象会拦截方法调用,然后将这个转化为HTTP请求

a. 创建这个接口的代理对象,应该是cglib,

b.在调用方法的时候,这个代理对象会把这个方法的调用给拦截下来,

c.然后将这个方法转化为http的请求发送出去,到远程调用的远程服务提供者暴露的服务端点处

2.核心组件

(1)Feign.Builder// 后续还需要学

这个是创建feign客户端的核心类,是负责构造feign客户端实例,可以配置各种的选项,包括编码器,解码器,日志记录器

1
2
3
4
Feign.Builder builder = Feign.builder()
.decoder(new JacksonDecoder())//将 HTTP 响应解码为响应对象
.encoder(new JacksonEncoder());//将请求对象编码为 HTTP 请求
SomeServiceClient client = builder.target(SomeServiceClient.class, "http://some-service");

spring cloud feign默认使用spring的消息转换器(jackson)来处理编解码

(2)Contract

1
Contract 负责解析 Feign 客户端接口的注解。Spring Cloud Feign 使用 SpringMvcContract 来解析 Spring MVC 的注解,如 @RequestMapping、@GetMapping 等。

(3)Client//是一个接口,负责执行HTTP请求.

就是一个HTTP的客户端,feign在builder的时候,可以选择使用Apache HttpClient 或 OkHttp\或者其他,然后利用这个客户端来实现执行HTTP的请求.

1
2
Client client = new OkHttpClient();
Feign.Builder builder = Feign.builder().client(client);

(4)Logger,用于记录请求和响应的日志

image-20240519182131701

三springcloud的组件

1. @EnableFeignClients

1
2
@EnableFeignClients
这个注解启用了 Feign 客户端的自动配置。它会扫描指定包下的所有接口,并为每个接口创建一个 Feign 客户端代理。

这个注释启用了feign的自动化配置,它会扫描指定包下面的所有的接口,为每个接口都创建一个feign的客户端代理,

2.自动配置类

spring cloud feign提供了一些自动配置类,如 FeignAutoConfigurationFeignClientsConfiguration.

这些配置类会自动的配置feign客户端所需要的各项组件.

3.load balance

这个feign是默认集成了ribbon,用于客户端的负载均衡

image-20240519183020401

4.Hystrix 整合

Feign 与 Hystrix 整合,可以实现熔断和回退机制。通过在配置中启用 Hystrix,Feign 客户端在调用失败时可以自动进行回退。

image-20240519183310894

5.扩展

image-20240519183321748

@EnableFeignClients

1.注释的声明

@EnableFeignClients 注解定义在 org.springframework.cloud.openfeign 包中。它的主要功能是通过 FeignClientsRegistrar 来注册 Feign 客户端。

1
2
3
4
5
6
7
8
9
10
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(FeignClientsRegistrar.class)
public @interface EnableFeignClients {
String[] value() default {};
String[] basePackages() default {};//指定要扫描的包。
Class<?>[] basePackageClasses() default {};
Class<?>[] defaultConfiguration() default {};//指定默认的配置类。
Class<?>[] clients() default {};//指定具体的 Feign 客户端类。
}

2.FeignClientsRegistrar 的工作原理

1
FeignClientsRegistrar` 是一个 `ImportBeanDefinitionRegistrar

作用是在spring的bean的定义注册阶段注册Feign客户端的相关Bean

注册过程

a. 扫描Feign客户端接口:FeignClientsRegistrar会根据@EnableFeignClients 注释的属性,比如basePackages,扫描指定包中的所有接口

‘’这个basePackages中的接口是怎么定义的?

b.注册Feign客户端Bean:对于每一个扫描到的接口,FeignClientsRegistrar 会注册一个 FeignClientFactoryBean,它是一个工厂 Bean,负责创建 Feign 客户端的代理对象。

自动配置类

FeignAutoConfigurationFeignClientsConfiguration

1.FeignAutoConfiguration

FeignAutoConfiguration 负责配置 Feign 的核心组件,如 Feign.BuilderDecoderEncoderContract 等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Configuration
@ConditionalOnClass(Feign.class)
@Import(FeignClientsConfiguration.class)
public class FeignAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public Feign.Builder feignBuilder() {
return Feign.builder();
}

@Bean
@ConditionalOnMissingBean
public Decoder feignDecoder() {
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
}

@Bean
@ConditionalOnMissingBean
public Encoder feignEncoder() {
return new SpringEncoder(this.messageConverters);
}

@Bean
@ConditionalOnMissingBean
public Contract feignContract() {
return new SpringMvcContract();
}

@Bean
@ConditionalOnMissingBean
public Logger feignLogger() {
return new Slf4jLogger();
}
}

2.FeignClientsConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
public class FeignClientsConfiguration {
@Bean
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(messageConverters));
}

@Bean
public Encoder feignEncoder() {
return new SpringEncoder(messageConverters);
}

@Bean
public Contract feignContract() {
return new SpringMvcContract();
}

@Bean
public Logger feignLogger() {
return new Slf4jLogger();
}
}

3.Feign客户端的创建

当Feign的客户端创建后,

  1. Spring会使用FeignClientFacatoryBean来创建代理对象.
  2. 把接口的方法调用,转发到feign生成的HTTP请求

FeignClientFactoryBean 是一个工厂 Bean,用于创建 Feign 客户端代理对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean {
@Override
public Object getObject() throws Exception {
return feignContext.getInstance(this.name, Feign.Builder.class)
.target(this.targetType, this.url);
}

@Override
public void afterPropertiesSet() throws Exception {
// 初始化配置
}
}