国产精品国产三级国产av

发布日期:2022-06-18 17:03    点击次数:72

真切清楚 OpenFeign 的架构旨趣

 全球好,我是悟空呀。

前次咱们真切教师了 Ribbon 的架构旨趣,此次咱们再来看下 Feign 汉典调用的架构旨趣。

一、清楚汉典调用

汉典调用怎么清楚呢?

汉典调用和腹地调用是相对的,那咱们先说腹地调用更好清楚些,腹地调用便是统一个 Service 内部的要害 A 调用要害 B。

那汉典调用便是不同 Service 之间的要害调用。Service 级的要害调用,便是咱们我方构造申请 URL和申请参数,就不错发起汉典调用了。

在管事之间调用的话,咱们都是基于 HTTP 契约,一般用到的汉典管事框架有 OKHttp3,Netty, HttpURLConnection 等。其调用历程如下:

然而这种虚线方框中的构造申请的过程是很繁琐的,有莫得更方便的格式呢?

Feign 便是来简化咱们发起汉典调用的代码的,那简化到什么进度呢?简化成就像调用腹地要害那样浅易。

比如我的开源格式 PassJava 中的使用 Feign 履行汉典调用的代码:

//汉典调用拿到该用户的学习时长 R memberStudyTimeList = studyTimeFeignService.getMemberStudyTimeListTest(id); 

而 Feign 又是 Spring Cloud 微管事时刻栈中相称进军的一个组件,要是让你来狡计这个微管事组件,你会怎么来狡计呢?

咱们需要商量这几个身分:

如何使汉典调用像腹地要害调用浅易? Feign 如何找到汉典管事的地址的? Feign 是如何进行负载平衡的?

接下来咱们围绕这些中枢问题来一路看下 Feign 的狡计旨趣。

二、Feign 和 OpenFeign

OpenFeign 组件的前身是 Netflix Feign 格式,它最早是行动 Netflix OSS 格式标一部分,由 Netflix 公司树立。其后 Feign 格式被孝顺给了开源组织,于是才有了咱们今天神用的 Spring Cloud OpenFeign 组件。

Feign 和 OpenFeign 有许多大同小异之处,不同的是 OpenFeign 支援 MVC 注解。

不错觉得 OpenFeign 为 Feign 的增强版。

浅易归来下 OpenFeign 能用来做什么:

OpenFeign 是声明式的 HTTP 客户端,让汉典调用更浅易。 提供了HTTP申请的模板,编写浅易的接口和插入注解,就不错界说好HTTP申请的参数、体式、地址等信息 整合了Ribbon(负载平衡组件)和 Hystix(管事熔断组件),不需要表现使用这两个组件 Spring Cloud Feign 在 Netflix Feign的基础上扩张了对SpringMVC注解的支援 三、OpenFeign 如何用?

OpenFeign 的使用也很浅易,这里如故用我的开源 SpringCloud 格式 PassJava 行动示例。

开源地址: https://github.com/Jackson0714/PassJava-Platform

心爱的小伙伴来点个 Star 吧,冲 2K Star。

Member 管事汉典调用 Study 管事的要害 memberStudyTime(),如下图所示。

第一步:Member 管事需要界说一个 OpenFeign 接口:

@FeignClient("passjava-study") public interface StudyTimeFeignService {     @RequestMapping("study/studytime/member/list/test/{id}")     public R getMemberStudyTimeListTest(@PathVariable("id") Long id); } 

咱们不错看到这个 interface 上添加了注解@FeignClient,而且括号内部指定了管事名:passjava-study。表现声明这个接口用来汉典调用 passjava-study管事。

第二步:Member 启动类上添加 @EnableFeignClients注解开启汉典调用管事,且需要开启管事发现。如下所示:

@EnableFeignClients(basePackages = "com.jackson0714.passjava.member.feign") @EnableDiscoveryClient 

第三步:Study 管事界说一个要害,其要害旅途和 Member 管事中的接口 URL 地址一致即可。

URL 地址:"study/studytime/member/list/test/{id}"

@RestController @RequestMapping("study/studytime") public class StudyTimeController {     @RequestMapping("/member/list/test/{id}")     public R memberStudyTimeTest(@PathVariable("id") Long id) {        ...      } } 

第四步:Member 管事的 POM 文献中引入 OpenFeign 组件。

<dependency>     <groupId>org.springframework.cloud</groupId>     <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency

第五步:引入 studyTimeFeignService,Member 管事汉典调用 Study 管事即可。

Autowired private StudyTimeFeignService studyTimeFeignService;  studyTimeFeignService.getMemberStudyTimeListTest(id); 

通过上头的示例,咱们清亮,加了 @FeignClient 注解的接口后,咱们就不错调用它界说的接口,然后就不错调用到汉典管事了。

这里你是否有疑问:为什么接口都莫得完结,就不错调用了?

OpenFeign 使用起来倒是浅易,然而内部的旨趣可莫得那么浅易,OpenFeign 帮咱们做了许多事情,接下来咱们来看下 OpenFeign 的架构旨趣。

四、梳理 OpenFeign 的中枢历程

先看下 OpenFeign 的中枢历程图:

1、在 Spring 格式启动阶段,管事 A 的OpenFeign 框架会发起一个主动的扫包历程。 2、从指定的目次下扫描并加载所有被 @FeignClient 注解修饰的接口,然后将这些接口调遣成 Bean,搭伙交给 Spring 来措置。 3、左证这些接口会经过 MVC Contract 契约默契,将要害上的注解都默契出来,放到 MethodMetadata 元数据中。 4、基于上头加载的每一个 FeignClient 接口,会生成一个动态代理对象,指向了一个包含对应要害的 MethodHandler 的 HashMap。MethodHandler 对元数据有援用干系。生成的动态代理对象会被添加到 Spring 容器中,并注入到对应的管事里。 5、管事 A 调用接口,准备发起汉典调用。 6、从动态代理对象 Proxy 中找到一个 MethodHandler 实例,生成 Request,包含有管事的申请 URL(不包含管事的 IP)。 7、经过负载平衡算法找到一个管事的 IP 地址,拼接出申请的 URL 8、管事 B 处理管事 A 发起的汉典调用申请,履行业务逻辑后,复返反应给管事 A。

针对上头的历程,咱们再来看下每一步的狡计旨趣。领先主动扫包是如何扫的呢?

五、OpeFeign 包扫描旨趣

上头的 PassJava 示例代码中,波及到了一个 OpenFeign 的注解:@EnableFeignClients。左证字面酷好不错清亮,不错注解是开启 OpenFeign 功能的。

包扫描的基本历程如下:

(1)@EnableFeignClients 这个注解使用 Spring 框架的 Import 注解导入了 FeignClientsRegistrar 类,运转了 OpenFeign 组件的加载。PassJava 示例代码如下所示。

// 启动类加上这个注解 @EnableFeignClients(basePackages = "com.jackson0714.passjava.member.feign")  // EnableFeignClients 类还引入了 FeignClientsRegistrar 类 @Import(FeignClientsRegistrar.class) public @interface EnableFeignClients {   ... } 

(2)FeignClientsRegistrar 崇拜 Feign 接口的加载。

源码如下所示:

 

@Override public void registerBeanDefinitions(AnnotationMetadata metadata,       BeanDefinitionRegistry registry) {    // 注册成立    registerDefaultConfiguration(metadata, registry);    // 注册 FeignClient    registerFeignClients(metadata,
国模丰满少妇私拍 registry); } 

 

(3)registerFeignClients 会扫描指定包。

中枢源码如下,调用 find 要害来查找指定旅途 basePackage 的所有带有 @FeignClients 注解的带有 @FeignClient 注解的类、接口。

 

Set<BeanDefinition> candidateComponents = scanner       .findCandidateComponents(basePackage); 

 

(4)只保留带有 @FeignClient 的接口。

 

// 判断是否是带有注解的 Bean。 if (candidateComponent instanceof AnnotatedBeanDefinition) {   // 判断是否是接口    AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;    AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();   // @FeignClient 只可指定在接口上。    Assert.isTrue(annotationMetadata.isInterface(),          "@FeignClient can only be specified on an interface"); 

 

接下来咱们再来看这些扫描到的接口是如何注册到 Spring 中。

六、注册 FeignClient 到 Spring 的旨趣

如故在 registerFeignClients 要害中,当 FeignClient 扫描完后,就要为这些 FeignClient 接口生成一个动态代理对象。

追根求源,进到这个要害内部,不错看到这一段代码:

 

BeanDefinitionBuilder definition = BeanDefinitionBuilder       .genericBeanDefinition(FeignClientFactoryBean.class); 

 

中枢便是 FeignClientFactoryBean 类,左证类的名字咱们不错清亮这是一个工场类,用来创建 FeignClient Bean 的。

咱们最运转用的 @FeignClient,内部有个参数 "passjava-study",这个是注解的属性,当 OpenFeign 框架去创建 FeignClient Bean 的期间,就会使用这些参数去生成 Bean。历程图如下:

默契 @FeignClient 界说的属性。 将注解@FeignClient 的属性 + 接口 StudyTimeFeignService的信息构形成一个 StudyTimeFeignService 的 beanDefinition。 然后将 beanDefinition 调遣成一个 holder,这个 holder 便是包含了 beanDefinition, alias, beanName 信息。 终末将这个 holder 注册到 Spring 容器中。

源码如下:

 

// 生成 beanDefinition AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); // 调遣成 holder,包含了 beanDefinition, alias, beanName 信息 BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,     new String[] { alias }); // 注册到 Spring 荆棘文中。 BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry); 

 

上头咱们还是清亮 FeignClient 的接口是如何注册到 Spring 容器中了。背面管事要调用接口的期间,就不错平直用 FeignClient 的接口要害了,如下所示:

 

@Autowired private StudyTimeFeignService studyTimeFeignService;  // 不详部分代码 // 平直调用  studyTimeFeignService.getMemberStudyTimeListTest(id); 

 

然而咱们并莫得细讲这个 FeignClient 的创建细节,底下咱们看下 FeignClient 的创建细节,这个亦然 OpenFeign 中枢旨趣。

七、OpenFeign 动态代理旨趣

上头的源码默契中咱们也提到了是由这个工场类 FeignClientFactoryBean 来创建 FeignCient Bean,是以咱们有必要对这个类进行领悟。

在创建 FeignClient Bean 的过程中就会去生成动态代理对象。调用接口时,其实便是调用动态代理对象的要害来发起申请的。

分析动态代理的进口要害为 getObject()。源码如下所示:

 

Targeter targeter = get(context, Targeter.class); return (T) targeter.target(this,国产精品国产三级国产av builder, context,       new HardCodedTarget<>(this.type, this.name, url)); 

 

接着调用 target 要害这一块,内部的代码竟然许多很细,我把中枢的代码拿出来给全球讲下,这个 target 会有两种完结类:

DefaultTargeter 和 HystrixTargeter。而非论是哪种 target,都需要去调用 Feign.java 的 builder 要害去构造一个 feign client。

在构造的过程中,依赖 ReflectiveFeign 去构造。源码如下:

 

// 不详部分代码 public class ReflectiveFeign extends Feign {   // 为 feign client 接口中的每个接口要害创建一个 methodHandler  public <T> T newInstance(Target<T> target) {     for(...) {       methodToHandler.put(method, handler);     }     // 基于 JDK 动态代理的机制,创建了一个 passjava-study 接口的动态代理,所有对接口的调用都会被左右,然后转交给 handler 的要害。     InvocationHandler handler = factory.create(target, methodToHandler);     T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),           new Class<?>[] {target.type()}, handler); } 

 

ReflectiveFeign 做的使命便是为带有 @FeignClient 注解的接口,创建出接口要害的动态代理对象。

比如示例代码中的接口 StudyTimeFeignService,会给这个接口中的要害 getMemberStudyTimeList 创建一个动态代理对象。

 

@FeignClient("passjava-study") public interface StudyTimeFeignService {     @RequestMapping("study/studytime/member/list/test/{id}")     public R getMemberStudyTimeList(@PathVariable("id") Long id); } 

 

创建动态代理的旨趣图如下所示:

默契 FeignClient 接口上各个要害级别的注解,比如汉典接口的 URL、接口类型(Get、Post 等)、各个申请参数等。这里用到了 MVC Contract 契约默契,背面会讲到。

然后将默契到的数据封装成元数据,并为每一个要害生成一个对应的 MethodHandler 类行动要害级别的代理。相称于把管事的申请地址、接口类型等都帮咱们封装好了。这些 MethodHandler 要害会放到一个 HashMap 中。 然后会生成一个 InvocationHandler 用来措置这个 hashMap,其中 Dispatch 指向这个 HashMap。 然后使用 Java 的 JDK 原生的动态代理,完结了 FeignClient 接口的动态代理 Proxy 对象。这个 Proxy 会添加到 Spring 容器中。 当要调用接口要害时,其实会调用动态代理 Proxy 对象的 methodHandler 来发送申请。

这个动态代理对象的结构如下所示,它包含了所有接口要害的 MethodHandler。

八、默契 MVC 注解的旨趣

上头咱们讲到了接口上是有一些注解的,比如 @RequestMapping,@PathVariable,这些注解统称为 Spring MVC 注解。然而由于 OpenFeign 是不睬解这些注解的,是以需要进行一次默契。

默契的历程图如下:

而默契的类便是 SpringMvcContract 类,调用 parseAndValidateMetadata 进行默契。默契完之后,就会生成元数据列表。源码如下所示:

List<MethodMetadata> metadata = contract.parseAndValidateMetadata(target.type()); 

这个类在这个旅途下,全球不错自行翻阅下如何默契的,不在本篇的商榷边界内。

https://github.com/spring-cloud/spring-cloud-openfeign/blob/main/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java 

这个元数据 MethodMetadata 内部有什么东西呢?

要害的界说,如 StudyTimeFeignService 的 getMemberStudyTimeList 要害。 要害的参数类型,如 Long。 发送 HTTP 申请的地址,如 /study/studytime/member/list/test/{id}。

然后每个接口要害就会有对应的一个 MethodHandler,它内部就包含了元数据,当咱们调用接口要害时,其实是调用动态代理对象的 MethodHandler 来发送汉典调用申请的。

上头咱们针对 OpenFeign 框架如何为 FeignClient 接口生成动态代理还是讲完毕,底下咱们再来看下当咱们调用接口要害时,动态代理对象是如何发送汉典调用申请的。

九、OpenFeign 发送申请的旨趣

先精巧程图:

如故在 ReflectiveFeign 类中,有一个 invoke 要害,会履行以下代码:

dispatch.get(method).invoke(args); 

这个 dispatch 咱们之前还是教师过了,它指向了一个 HashMap,内部包含了 FeignClient 每个接口的 MethodHandler 类。

这行代码的酷好便是左证 method 找到 MethodHandler,调用它的 invoke 要害,况兼传的参数便是咱们接口中的界说的参数。

那咱们再跟进去看下这个 MethodHandler invoke 要害内部做了什么事情。源码如下所示:

public Object invoke(Object[] argv) throws Throwable {   RequestTemplate template = buildTemplateFromArgs.create(argv);   ... } 

咱们不错看到这个要害内部生成了 RequestTemplate,它的值肖似如下:

GET /study/list/test/1 HTTP/1.1 

RequestTemplate 调遣成 Request,它的值肖似如下:

GET http://passjava-study/study/list/test/1 HTTP/1.1 

这旅途未便是咱们要 study 管事的要害,这么就不错平直调用到 study 服了呀!

OpenFeign 帮咱们拼装好了发起汉典调用的一切,咱们只管调用就好了。

接着 MethodHandler 会履行以下要害,发起 HTTP 申请。

client.execute(request, options); 

从上头的咱们要调用的管事便是 passjava-study,然而这个管事的具体 IP 地址咱们是不清亮的,那 OpenFeign 是如何获得到 passjava-study 管事的 IP 地址的呢?

追念下最运转咱们提议的中枢问题:OpenFeign 是如何进行负载平衡的?

咱们是否不错逸猜测上一讲的 Ribbon 负载平衡,它未便是用来做 IP 地址聘用的么?

那咱们就来看下 OpenFeign 又是如何和 Ribbon 进行整合的。

十、OpenFeign 如何与 Ribbon 整合的旨趣

为了考证 Ribbon 的负载平衡,咱们需要启动两个 passjava-study 管事,这里我启动了两个管事,端标语分歧为 12100 和 12200,IP 地址都是本机 IP:192.168.10.197。

接着上头的源码络续看,client.execute() 要害其实会调用 LoadBalancerFeignClient 的 exceute 要害。

这个要害内部的履行历程如下图所示:

将管事称号 passjava-study 从 Request 的 URL 中删掉,剩下的如下所示:
GET http:///study/list/test/1 HTTP/1.1 
左证管事名从缓存中找 FeignLoadBalancer,要是缓存中莫得,则创建一个 FeignLoadBalancer。 FeignLoadBalancer 会创建出一个 command,这个 command 会履行一个 sumbit 要害。 submit 要害内部就会用 Ribbon 的负载平衡算法聘用一个 server。源码如下:
Server svc = lb.chooseServer(loadBalancerKey); 

通过 debug 调试,咱们不错看到两次申请的端标语不相同,一个是 12200,一个是 12100,诠释如实进行了负载平衡。

然后将 IP 地址和之前剔除去管事称号的 URL 进行拼接,生成终末的管事地址。 终末 FeignLoadBalancer 履行 execute 要害发送申请。

那全球有莫得疑问,Ribbon 是如何拿到管事地址列表的?这个便是上一讲 Ribbon 架构内部的践诺。

Ribbon 的中枢组件 ServerListUpdater,用来同步注册表的,它有一个完结类 PollingServerListUpdater ,挑升用来做定时同步的。默许1s 后履行一个 Runnable 线程,背面便是每隔 30s 履行 Runnable 线程。这个 Runnable 线程便是去获得注册中心的注册表的。

十一、OpenFeign 处理反应的旨趣

当汉典管事 passjava-study 处理完业务逻辑后,就会复返 reponse 给 passjava-member 管事了,这里还会对 reponse 进行一次解码操作。

Object result = decode(response); 

这个内部做的事情便是调用 ResponseEntityDecoder 的 decode 要害,将 Json 字符串升沉为 Bean 对象。

十二、归来

本文通过我的开源格式 PassJava 顶用到的 OpenFeign 行动示例代码行动进口进行教师。然后以图解+解读源码的格式真切领悟了 OpenFeign 的运行机制和架构狡计。

中枢绪想: OpenFeign 会扫描带有 @FeignClient 注解的接口,然后为其生成一个动态代理。 动态代理内部包含有接口要害的 MethodHandler,MethodHandler 内部又包含经过 MVC Contract 默契注解后的元数据。 发起申请时,MethodHandler 会生成一个 Request。 负载平衡器 Ribbon 会从管事列表中收用一个 Server,拿到对应的 IP 地址后,拼接成终末的 URL,就不错发起汉典管事调用了。

OpenFeign 的中枢历程图:

 





Powered by 东北女人毛多水多牲交视频 @2013-2022 RSS地图 HTML地图