Spring Cloud 常见面试题总结(二)

Part4

【Q-01】Eureka Client 提交的 CANCEL_OVERRIDE 状态修改请求,Eureka Server 是如何处理的,Server 端都做了哪些变更?请谈一下你的认识。

【RA】Eureka Client 提交的 CANCEL_OVERRIDE 状态修改请求,Eureka Server 在接收到后,首先根据该 Client 的微服务名称及 instanceId 在 Server 端注册表中进行了查找。若没有找到,则直接返回 失败;若找到了,其会执行两大任务:

  • 任务一:将 Server 端删除 overridden 状态。
  • 任务二:将此变更同步到其它 server。

删除 overridden 状态,从哪里删除呢?这个删除处理,在 Server 端主要完成了三样工作:

  • 将该 instanceInfo 在缓存集合 overriddenInstanceStatusMap 中的 Entry 删除。
  • 将 instanceInfo 中的 overridden 状态变更为 UNKNOWN。
  • 将 instanceInfo 中的 status 状态变更为 UNKNOWN。

overriddenInstanceStatusMap 中缓存的 instanceInfo的 overridden 状态,对于后续注册、续约等请求处理时,对instanceInfo的状态判断起很大作用。另外此时,该Client发送的心跳Server是不接收的。Server 会向该 Client 返回 404。


【Q-02】Eureka Client 的注册请求被 Server 接收到后,会首先判断该 Client 在注册表中是否存在。按理说,这里是注册处理,注册表中应该是不存在该 Client 的 lease 信息的,但的确会出现新注册的 Client 在注册表中存在的情况,这是为什么?请谈一下你的看法。

【RA】Eureka Client 的注册请求被 Server 接收到后,注册表中可能会出现新注册的 Client 在注册表中已经存在的情况。这是因为,当 Client 的 instanceInfo 的续约信息发生了变更,Client的“定时更新 InstanceInfo 信息给 Server”的定时任务发出的是 register()请求,但该客户端其实在之前已经注册过了。此时就会出现注册表中已经存在该 instanceId 的情况。


【Q-03】Eureka Server 在处理新注册的 Client 在注册表中已经存在的情况时会出现一种比较奇怪的情况:当前新注册的 InstanceInfo 中的 lastDirtyTimestamp,比注册表中缓存的当前这个 InstanceInfo 中的 lastDirtyTimestamp 小。即后注册的反而过时,这是为什么?请谈一下你的看法。

【RA】Eureka Server 在处理新注册的 Client 在注册表中已经存在的情况时出现,当前新注册的 InstanceInfo 中的 lastDirtyTimestamp,比注册表中缓存的当前这个 InstanceInfo 中的 lastDirtyTimestamp 小的情况,即后注册的反而过时。这是因为,若 Client 的 instanceInfo 的续约信息相继发生了两次变更,Client 就提交了两次 register()请求。但是由于网络原因,第二次注册请求先到达 Server。当第一次注册请求到达后就会出现“后到达的注册请求中携带的 instanceInfo 的最后修改时间反而过时”的情况。


【Q-04】Eureka Server 在处理 Client 的注册时请求时,是否可能出现新注册的 registrant 的 OverriddenStatus 状态不是 UNKNOWN 的场景呢?为什么?请谈一下你的看法。

【RA】这种场景是可能会出现的。且只有一种情况:当前注册的 registrant 是由于其续约信息发生了变更而引发的注册,且在续约信息变更之前用户通过 Actuator 修改过状态。
  当然,这种通过 Actuator 修改的状态仅仅修改的是 Server 端注册表中的状态,并没有修改客户端的任何状态。这个修改的结果实际是通过客户端定时更新客户端注册表时,将所有变更信息下载到的客户端,其中就包含它自己状态的修改信息。


【Q-05】Eureka Server 会根据 Client 请求中携带的状态信息、该 Client 在 Server 的注册表的 InstanceInfo 中的状态信息,及缓存 map 中的 OverriddenStatus 状态信息根据指定规则计算出当前 Client 的 status。请问,Eureka Server 中使用的是哪种状态计算规则?请谈一下你对这个规则的认识。

【RA】Eureka Server 中使用的状态计算规则为 FirstMatchWinsCompositeRule。该规则是一个复合规则,具有一个包含了三条子规则的有序列表:DownOrStartingRule、OverrideExistsRule 与 LeaseExistsRule。其会依次逐个尝试着去匹配其所包含的子规则,并返回第一个匹配上的结果。若都没有匹配上,则返回AlwaysMatchInstanceStatusRule 规则的结果。这个规则能够匹配上任意 InstanceInfo 的状态。


【Q-06】请谈一下你对 Eureka Server 中的 DownOrStartingRule 状态匹配规则的认识。

【RA】DownOrStartingRule 是计算 instanceInfo 状态的一种规则,该规则的执行原则是:只要参数的 instance 的 status 是 STARTING 或 DOWN 状态,那么这个状态是可信的,即可以直接将这个状态值作为计算出的 intance 的真正服务状态结果返回。若是参数的 instance 的服务状态为 UP 或 OUT_OF_SERVICE 状态,那么,这个状态是不可靠的。因为该 instance 在服务端的状态可能已经被 Actuator 给改了。所以需要再查看该 instance 的状态是否在服务端也被修改了。所以这个状态是不可靠的,需要对其进行再次确认。所以返回“不匹配”的结果,以进行下一个规则的匹配。从代码可以看出,只有 UP 与 OUT_OF_SERVICE 状态是匹配不上这个规则的。也就是说,能进入下一个规则判断,一定是 UP 或 OUT_OF_SERVICE 状态。


【Q-07】请谈一下你对 Eureka Server 中的 OverrideExistsRule 状态匹配规则的认识。

【RA】OverrideExistsRule 是计算 instanceInfo 状态的一种规则。在进行该规则之前是先进行了 DownOrStartingRule 规则匹配的,所以能走到这里,说明参数的 intance 的服务状态是 UP或 OUT_OF_SERVICE。该规则的执行原则是:查看服务端缓存 overriddenInstanceStatusMap中是否有其对应的可覆盖状态 overriddenStatus。若有,则直接将这个状态作为计算结果返回。若没有,则该规则未被匹配上。进入下一个规则进行匹配。


【Q-08】请谈一下你对 Eureka Server 中的 LeaseExistsRule 状态匹配规则的认识。

【RA】LeaseExistsRule 是计算 instanceInfo 状态的一种规则。在进行该规则之前是先进行了 OverrideExistsRule 规则匹配的,所以能走到这里,说明参数的 intance 的服务状态是 UP 或 OUT_OF_SERVICE。该规则的执行原则是:查看服务端注册表中该 instance 对应的服务状态是否也是 UP 或 OUT_OF_SERVICE。若是,则直接将这个状态作为计算出的 intance 的真正服务状态结果返回。若注册表中没有对应的 instance,或注册表中对应 instance 的状态不是 OUT_OF_SERVICE 与 UP,则返回“不匹配”结果,以进行下一个规则的匹配。


【Q-09】请谈一下你对 Eureka Server 中的 AlwaysMatchInstanceStatusRule 状态匹配规则的认识。

【RA】AlwaysMatchInstanceStatusRule 是计算 instanceInfo 状态的一种规则。首先,从这个类名可以看出,该规则可以匹配上任意状态,最终都会给出一个当前 instance 服务状态的计算结果的。只所以可以匹配任意状态,是因为其是直接返回参数给的 instance 的 status 状态,无论是什么状态。


Part5

【Q-01】Eureka Client 提交的续约请求,Eureka Server 是如何处理的?请谈一下你的认识。

【RA】客户端续约请求,即客户端向服务端发送心跳。客户端的心跳提交的是一个没有携带任何请求体的 PUT 请求,不过其在请求 URI 中携带了心跳的发出者的 InstanceId,及当前心跳发出者的状态。Server 端对于续约请求,主要完成了若项任务:

  • 当 Server 接收到 Client 发送的续约信息后,根据注册表中当前 InstanceInfo 的状态信息计算出其状态,然后更新为新的 status。
  • 更新续约时间
  • 将心跳同步到其它 server。
  • 服务端之间的续约数据同步(对方比我新,我比对方新 两种情况的处理)

【Q-02】Eureka Server间进行续约同步过程中可能会导致overridden状态的不一致,为什么?请谈一下你的看法。

【RA】首先要清楚一点,Eureka 是 AP 的,是允许出现 Server 间数据不一致的。例如,当前 Eureka 中由于客户端下架请求而从注册表中删除了某 Client,在进行 Server 间同步时,由于另一个 Server 处于自我保护模式,所以其是不能删除 Client 的。此时就出现了 Server 间数据的不一致。
  下面再来说续约。无论是直接处理Client的续约请求,还是处理Server间续约同步,Server 端对于续约的处理,根本不涉及 lastDirtyTimestamp 时间戳,及 overridden 状态。这一点从续约的源码中是可以看出来的。那么有可能会出现以下场景:Client 通过 Actuator 修改了状态,而这个状态修改操作在 Server 间同步时并没有同步成功,出现了 Server 间对于同一个 InstanceInfo 中 overridden 状态的不一致。
  虽然 Eureka 本身是 AP 的,但其仍是尽量想让 Eureka 间实现同步,所以在其发生最频繁的续约中解决了这个问题。只不过,由于续约本身根本不涉及 overridden 状态,仅靠续约是解决不了的。所以需要在 Eureka Server 的配置文件中添加专门的配置解决这个问题。不过,这个属性设置默认是开启的。


【Q-03】Eureka Server 在进行续约处理时,若发现其计算的当前 isntanceInfo 的 status 状态为 UNKNOWN,说明什么?请谈一下你的看法。

【RA】Eureka Server 在进行续约处理时,若发现其计算的当前 instanceInfo 的 status 状态为UNKNOWN,说明这个计算结果是在 OverrideExistsRule 规则中计算出的结果,即当前 instanceInfo 的 overridden 状态为 UNKNOWN。对于一个 InstanceInfo 来说,可以从缓存 map 中读取到其 overridden 状态为 UNKNOWN,只能有一种情况:这个 UNKNOWN 的 overridden 状态是通过 Actuator 的 CANCEL_OVERRIDE 修改的状态,即用户取消了该 InstanceInfo 的 overridden 状态。
  那么也就是说,Eureka Server 在进行续约处理时,若发现其计算的当前 instanceInfo 的 status 状态为 UNKNOWN,则说明该 InstanceInfo 已经不对外提供服务了。


【Q-04】Eureka Client 提交的下架请求,Eureka Server 是如何处理的?请谈一下你的认识。

【RA】Eureka Server 在接收到 Client 的下架请求后,主要完成了两项任务:

  • 从注册表中将该提交请求的 intanceInfo 删除,并将其 overridden 状态从缓存 map 中删除,记录删除时间戳,最后修改时间戳 lastUpdatedTimestamp 等。
  • 将下架请求同步到其它 server。

【Q-05】Eureka Server 在处理 Client 的全量下载注册信息请求时可以设置从自己的只读缓存 readOnlyCacheMap 中获取到所有注册到自己的注册信息,而 readOnlyCacheMap 中的数据是定时同步的读写缓存 readWriteCacheMap 的。这样的话就存在一个问题:这个定时更新无论更新频率多么高,一定存在用户从 readOnlyCacheMap 中读取的数据与 readWriteCacheMap 中不一致的情况,为什么不直接从 readWriteCacheMap 中读取?也就是说,这样的设计好处是什么?请谈一下你的认识。

【RA】这样设计的目的是为了保证在并发环境下“集合迭代的稳定性”。集合迭代的稳定性指的是,当一个共享变量是集合且存在并发读写操作时,要保证在对共享集合进行读操作时能够读取到稳定的数据,即在读取时不能对其执行写操作,但又不能妨碍了写操作的执行。此时就可以将集合的读写功能进行分离,创建出两个共享集合:一个专门用于处理写功能,外来的数据写入到这个集合;一个专门用于处理读功能,定时同步写集合的数据。这种方案存在的弊端是,无法保证只读集合中的数据与读写集合中数据的随时完全一致。当然,这种不一致在下一次定时同步时就会达到一致。所以这种方案的应用场景是对数据的实时性要求不是很高的情况。


【Q-06】Eureka Server 在处理 Client 的全量下载注册信息请求与处理增量下载请求有什么不同?请谈一下你的认识。

【RA】Eureka Server 在处理 Client 的全量下载注册信息请求时,其读取的是当前 Server 注册表 registry 中的注册信息,而处理增量下载请求时根本就没有操作注册表 registry,而是直接读取了最近更新队列 recentlyChangeQueue 中的信息。这两种请求的处理方式,操作了两上不同的共享集合。


【Q-07】Eureka Server 在处理 Client 的增量下载注册信息请求时是从 recentlyChangeQueue 最近更新队列中直接获取的数据,这个 recently 是多久?如果超过了这个 recently 时间又是如何处理的?请谈一下你的认识。

【RA】最近更新队列 recentlyChangeQueue 中的 recently 是可以通过配置文件属性指定的,默认为 3 分钟。当 recentlyChangeQueue 中的元素超过了 3 分钟,那么系统会自动将这些过期的元素删除。只不过这个删除操作是一个 repeated 定时任务,在 AbstractInstanceRegistry 类的构造器中被创建并启动,默认每 30 秒执行一次。


【Q-08】Eurek Server 对于像 Client 注册、状态修改等写操作添加的都是读锁,而对于像增量下载请求添加的是写锁,为什么?为什么对于续约请求这种写操作处理中没有添加读锁?为什么全量下载中没有添加写锁?对于 Eureka Server 中添加锁的方式,请谈一下你的认识。

【RA】总体来说,这种读/写锁的添加方式就是为了解决两个共享集合 recentlyChangedQueue 与注册表 registry 的“集合迭代稳定性”问题。即增加读锁是为了限制其增加写锁,而增加写锁也是为了限制其增加读/写锁。若仅是考虑到 recentlyChangedQueue 集合的迭代稳定性问题,完全可以在处理注册、状态修改、删除 overridden 状态、下架、续约等写操作请求时添加写锁,而在处理全量下载、增量下载请求时添加读锁。
  但这样做对于注册表 registry 集合来说就会出现问题。由于续约请求是一个发生频率非常高的写操作处理,若为其添加了写锁,则意味着在进行续约处理时,其它任何对 registry 的读/写操作均将阻塞。所以,续约处理是不能加写锁的。那为其添加读锁是否可以呢?也不行。因为对于这么一个发生频率很高的处理,若添加了读锁,那么,几乎这个 registry 就会被读锁给锁定,其它任何写操作均将被阻塞。所以,续约处理不能加锁。
  处理注册、状态修改、删除 overridden 状态、下架等写操作请求时,为什么要添加读锁呢?添加写锁不行吗?若添加写锁,则意味着,任意一个对 registry 的写操作请求处理,均将阻塞所有其它对 registry 的读/写操作,效率非常低。而若这些写操作添加的是共享锁读锁,则意味着,这些写操作可以同时进行。即使可能会出现对这些写操作同时操作同一个 registry中的相同 instanceInfo 的情况,也不会出现问题。因为 registry 及 recentlyChangedQueue 都是 JUC 的,是线程安全的。
  由于那些写操作添加了读锁,所以增量下载这种读操作添加了写锁,以保证对共享集合读/写操作的互斥。
  为什么增量下载添加了写锁,而全量下载没有添加呢?因为增量下载中没有涉及对共享集合注册表 registry 的操作,而全量下载读取了 registry。若为全量下载添加写锁,则必然会导致其在读取期间出现续约请求处理被阻塞的情况。对于这种频率非常高的续约处理是不能停止的。

Part6

【Q-01】Eurek Server 对于续约过期的 Client 会定期进行清除,这个定时任务是何时启动的?由谁来完成的?又做了些什么?请谈一下你的认识。

【RA】Eurek Server 对于续约过期的 Client 的定时清除任务是在 Eureka Server 启动时专门创建了一个新的线程来执行的。确切地说,是 EurekaServerAutoConfiguration 在做实例化过程中完成的该定时任务线程的创建、执行。
  这个定时任务首先查看了自我保护模型是否已经开启,若已经开启则不进行清除。若没有开启,则首先会将所有失联的 instance 集中到一个集合中,然后再计算“为了不启动自我保护模型,最多只能清除多少个 instance”,在进行清除时不能越过此最大值。


【Q-02】Eurek Server 对于续约过期的 Client 定期进行清除时有一个“补偿时间”概念。什么是补偿时间,请谈一下你的认识。

【RA】对于“补偿时间”的理解,首先要清楚 repeated 定时器的执行原理。本次任务的执行条件有两个,这两个条件必须同时满足才会执行任务。一是,上次任务执行完毕,二是,按照配置文件中设置的清除间隔,本次任务的执行时间点也已经到了或过了。
  那么什么是“补偿时间”呢?举例来说。
  例如,正常情况下,若每 5s 清除一次过期对象,而清除一次需要 2s,则在第 5s 时开始清除 0s-5s 期间的过期对象。第 10s 开始清除 5s-10s 期间的过期对象。
  若清除操作需要 6s,则在第 5s 时会开始清除,其清除的是 0s-5s 期间的过期对象。然而这次清除用时 6s,也就是说,在第 11s 时才清除完毕 0s-5s 期间的过期对象。按理说应该在第 10s 时开始清除 5s-10s 期间的过期对象,但由于上次的清除任务还未结束,所以在第10s 时不能开始清除,而在第 11s 时开始清除操作。因为已经过了 10s 这个时间点。此时要清除的对象就应该是 5s-11s 期间过期的,而多出的那 1s 就是需要补偿的时间。


【Q-03】Eurek Server 对于续约过期的 Client 会定期进行清除,而这个清除过程中,系统会从过期对象数量 expiredLeases.size,与开启自我保护的阈值数量 evictionLimit 两个数值中选择一个最小的数进行清除。根据这个最小值进行清除,是不是会导致自我保护模式永远无法启动的情况?若出现了很多的过期对象,即这个 expiredLeases.size 很大,而 evictionLimit 是固定的。那么其清除的一定是 evictionLimit 个过期对象。这样的话是否自我保护模型永远无法启动?

【RA】答案是否定的。自我保护模式的启动并不是由这个清除任务决定的,而是由其它线程决定。只要发现收到的续约数据低于阈值了,那么就会启动自我保护模式。这里选择最少的进行清除,是为了尽量少些清除,给那些没有被清除的对象以“改过自新,浪子回头”的机会。可能由于网络抖动导致的失联,网络现在好了,其还可以恢复。若直接清除掉,那么就没有这个恢复的机会了。


【Q-04】Spring Cloud 中 OpenFeign 的@EnableFeignClients 与@FeignClient 两个注解有怎样的区别与联系。请谈一下你的认识。

【RA】@EnableFeignClients 注解主要用于查找所有的@FeignClient 接口,并为这些 Feign Client设置一些默认的配置。而@FeignClient 则是就某一个特定的 Feign Client 进行配置。一个 Consumer 中只能有一个@EnableFeignClients,但可以有多个@FeignClient。


【Q-05】Spring Cloud 中 OpenFeign 中@FeignClient 注解中有一个 name 属性与 path 属性。这两个属性是什么意思?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 中@FeignClient注解中的name属性用于指定当前Feign Client 的名称,这个名称为其要消费的提供者微服务名称。消费者对提供者的负载均衡就是通过这个名称实现的。而 path 属性用于指定直连方式的连接的提供者的 ip 与 port。一旦指定了 path属性,就不再使用负载均衡方式来选择提供者了。


【Q-06】Spring Cloud 中 OpenFeign 中有一个 FeignClientSpecification 类,这个类是干嘛的?请谈一下你的认识。

【RA】FeignClientSpecification 是一个 Feign Client 的生成规范。所谓生成规范就是该实例中定义了生成 Feign Client 的各种配置信息,在动态生成 Feign Client 时需要按照这些配置信息来生成。具体的规范存放在 configuration 属性中。


【Q-07】Spring Cloud 中 OpenFeign 中有一个 FeignContext 类,这个类是干嘛的?请谈一下你的认识。

【RA】FeignContext 是一个为 Feign Client 创建所准备的上下文对象,从这个对象中可以获取到每一个要创建的 Feign Client 所需要的 Spring 子容器,而在这些子容器中存放着创建每一个不同的 Feign Client 所需要的“原材料”bean。一个应用只会有一个 FeignContext 实例。


Part 7

【Q-01】Spring Cloud 中 OpenFeign 中对于 Feign Client 的创建源码解析,从哪里下手开始呢?请谈一下你的思路。

【RA】对于 Feign Client 的创建源码解析从@EnableFeignClients 着手分析是比较好的。该注解中@Import 了一个 FeignClientsRegistrar 类。该类实现了 ImportBeanDefinitionRegistrar 接口,即实现了该接口的 registerBeanDefinitions()方法。这个接口就是专门处理配置类的。而 @EnableFeignClients 注解的的 defaultConfiguration 与@FeignClient 注解中的 configuration 属性都是配置类,就是由这个接口方法处理的。所以就从 FeignClientsRegistrar 类的方法registerBeanDefinitions()入手分析。


【Q-02】关于 volatile 与 synchronized,很多时候会一起使用,为什么?请谈一下你的认识。

【RA】首先要清楚,在系统中存在这样的两类内存。一类是为每个线程单独分配的工作内存,即高速缓存。一类是为当前应用(进程)分配的主内存。对于共享变量,其是同时存在于每个线程的工作内存与主内存中的。一个线程若要修改共享变量的值,其首先需要从主内存中将共享变量值读取到自己的工作内存,然后在自己的工作内存中进行修改。即对于共享变量的修改是在各个线程的工作内存中进行的。而对于共享变量的读取,则是从主内存中读取的。
  为了使共享变量在线程的工作内存中修改后的值能够立即更新到进程主内存,就需要为共享变量添加 volitile 修饰符,即 volitile 可以保证共享变量值对所有线程的“可见性”。
  但在并发环境下对于共享变量的修改“有序性”就需要使用 synchronized 了。将对共享变量的修改代码放入到 synchronized 语句块中,就可以保证了对共享变量修改的“有序性”。
  将 synchronized 与 volatile 联合使用就可以保证了“我在修改时,你们都不能改。但我改过了,你们都可以看到”。当然,若对主内存中的共享变量的某个时刻的值,同时有多个线程读取到了各自的工作内存,其是通过“乐观锁”机制解决版本冲突问题的。


【Q-03】什么是迭代稳定性问题?请谈一下你的认识。

【RA】迭代稳定性问题其实就是共享集合迭代稳定性问题。其描述的问题是,在并发处理的场景下,对某实例中共享集合的访问中,若在对该集合进行写操作的同时,又有线程对其执行迭代访问,此时迭代访问的结果可能会出现稳定性问题。
  为了解决这个问题,可以采用以下三种方案:

  • 读/写操作均添加读/写锁
  • 引入专门的只读集合
  • 集合替换

【Q-04】Spring Cloud 中 OpenFeign 的@EnableFeignClients 注解中 value、basePackages 与 basePackageClasses 属性均可以用于指定要扫描 Feign Client 的基本包。这三个属性值是什么关系?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 的@EnableFeignClients 注解中 value、basePackages 与 basePackageClasses 属性均可以用于指定要扫描 Feign Client 的基本包,只要这三个属性都指定了值,那么,这些指定的包均会扫描。


【Q-05】Spring Cloud 中 OpenFeign 的@EnableFeignClients 注解中 value、basePackages、basePackageClasses 属性与 clients 属性均可以用于指定要扫描 Feign Client 的位置。这五个属性值是什么关系?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 的@EnableFeignClients 注解中 value、basePackages、basePackageClasses属性与clients属性均可以用于指定要扫描Feign Client的位置。其中value、basePackages、basePackageClasses 属性用于指定要扫描的基本包,而 clients 属性用于指定与 Feign Client 同包的类或接口。若指定了 clients 属性,则用于指定基本包的属性将不起作用。


【Q-06】Spring Cloud 中 OpenFeign 的@FeignClient 注解中 value、contextId、name 与 serviceId 属性均可以用于指定要生成的 Feign Client 的名称。这四个属性值是什么关系?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 的@FeignClient 注解中 value、contextId、name 与 serviceId 属性均可以用于指定要生成的 Feign Client 的名称。这四个属性值若都指定了不同的值,那么只会有一个起作用,它们的优先级由高到低分别是 contextId、value、name、serviceId。


【Q-07】对于共享集合,为何有的需要考虑迭代稳定性而有的不需要?请谈一下你的看法。

【RA】对于共享集合,在并发环境下,若存在某些线程对其进行修改时,其它线程可能会对其进行遍历迭代访问的情况,那么就需要考虑迭代稳定性问题。若不存在遍历迭代的情况,就无需考虑。


【Q-08】Spring Cloud 中 OpenFeign 的 FeignContext 对于 Feign Client 的创建非常的重要。其中维护着一个重要集合 contexts,该集合中都保存了什么内容?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 的 FeignContext 中维护着一个重要集合 contexts,该集合是一个线程安全的 map。其 key 为每个@FeignClient 的 name 属性,即微服务名称,而 value 则为该@FeignClient 所对应的一个 Spring 子容器对象,该容器中存放着该 Feign Client 创建所需要的所有对象。


【Q-09】Spring Cloud 中 OpenFeign 的 FeignContext 对于 Feign Client 的创建非常的重要。其中维护着一个重要集合 configurations,该集合中都保存了什么内容?请谈一下你的认识。

【RA】Spring Cloud 中 OpenFeign 的 FeignContext 中维护着一个重要集合 configurations,存的是FeignClient规范,其是一个线程安全的 map。其 key-value 分为两类:

  • 第一类只有一个,key 是字符串“default + 当前启动类的全限定性类名”,而 value 是FeignClientSpecification 类型,其configuration属性就是 @EnableFeignClients 中 configuration 的值;
  • 第二类可以有多个,key 为当前@FeignClient 的 name 属性名,即微服务名称,value 同样是FeignClientSpecification类型,但其configuration属性 是每个@FeignClient 的 configuration 的属性值。这一类 key-value 对会有很多,有多少的@FeignClient 就会有多少的这类 key-value。

【Q-10】在一个类或方法上出现的 synthetic 是什么意思?请谈一下你的认识。

【RA】synthetic,合成的。当一个类或方法前出现了该关键字,则说明该类或方法是由编译器自动生成的。这个关键字一般不是我们人为写到代码中的,而是由编译器编译生成的字节码文件中。
  例如,当一个内部类中的方法要访问外部类的 private 成员时,编译器会为该内部类方法前自动添加上 synthetic 修饰符,表示该类中的方法可以访问到那个 private 成员。
  编译器为什么要为其添加这个修饰符呢?因为对于编译器来说,无论是内部类还是外部类,其编译出的字节码文件都是一个独立的文件。为了标识出这个内部类文件的方法与其它类文件的方法的不同,编译器会为该类自动添加上这个修饰符,以使其可以访问到另一个类的 private 成员。
  当然,若一个方法是由编译器内部生成的,那么这个编译器生成的这个方法前也会自动添加上 synthetic 关键字的。


【Q-11】Spring Cloud 中 OpenFeign 中创建的 Feign Client 是一个什么对象?请谈一下你的看法。

【RA】Spring Cloud 中 OpenFeign 中创建的 Feign Client 是一个 JDK 的 Proxy 动态代理对象。


【Q-12】Spring Cloud 中 OpenFeign 中 Feign Client 的一个方法调用是如何发出去的?如果查看这个源码?请谈一下你的思路。

【RA】Spring Cloud 中 OpenFeign 中创建的 Feign Client 是一个 JDK 的 Proxy 动态代理对象,所以若要解析 Feign 方法的调用,可以找到 JDK 的 Proxy 生成时的 InvocationHandler 实例的 invoke()方法,该方法是 Feign 方法执行后系统立即调用的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

犬豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值