定风波

莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生。

SpringCloud Feign 生成流程

SpringCloud Feign 生成流程

Feign Bean 的生成

在使用Feign时,需要在启动类中声明@EnableFeignClient,在这个注解中会使用@Import 注册FeignClientsRegistrar.class

@Enable** 注解的说明:

enable注解通常都会附带一个@Import(XXXX.class) ,这个xxx.class通常有三个作用

  1. 直接导入配置类,这个类通常也会被注册成一个bean,添加@Configuration并初始化一些bean 例如:@EnableScheduling
  2. 依据条件选择配置类,这个需要继承AdviceModeImportSelector,并实现selectImports 方法来选择使用的配置。例如:@EnableSync
  3. 动态注册bean,实现ImportBeanDefinitionRegistrar 接口,在registerBeanDefinitions中动态的注册bean

FeignClientsRegistrar 这个类主要的作用是用来生成FeignClientFactoryBean 这个FactoryBean 。

这个类中扫描指定目录或者EnableFeignClient的基目录中,所有添加了FeignClient注解类,读取FeignClient的注解,并作为BeanDefinition的属性,这个里生成的是FeignClientFactoryBean bean

FactoryBean的作用呢,就是用来生成这一类Bean 的bean (这里指生成添加了@FeignClient注解 的bean)

FeignClientFactory中getObject生成的feignClientBean 是生成的一个代理,通过Bean(feign.ReflectiveFeign#newInstance )。(org.springframework.cloud.netflix.feign.Targeter 默认目标或者是熔断目标)

Feign调用流程

在调用felignClient方法时通过拦截方法 生成requestTemplate(feign.SynchronousMethodHandler#invoke) 来实际发起请求调用。

Feign在发起请求时,通过feign.SynchronousMethodHandler#executeAndDecode 来发起,这里会选择不同的服务器(负载均衡或者指定服务器)来执行

参考:http://www.h3399.cn/201706/96663.html

解决的问题

在开发时,我们使用本机进行开发,调试工作。服务的依赖服务是放在了docker 容器里面 。docker 集群中各个子服务注册到注册中心上去的时候,注册的地址是daocker 内部的地址。

这时就存在了问题,本机在启动后往注册中心注册自己使用的本机的ip ,使用eureka 时,获取的服务列表是docker 内部ip,本机是无法连接到内部的。那怎么能够在本机进行开发测试工作呢。

首先,我们在开发环境机器上增加了一个zuul 的网关,由网关负责转发到docker内部ip ,那么本机进行开发工作时就需要访问能够被网关识别到的一个特定url(如:http:/geteway//xx-service/a xx-service 是服务名称a是xx-service 提供的一个接口)。

我们想到了3中办法

  1. 在@FeignClient注解中增加url 属性,这个属性使用通配符的形式“${xx-service.url}”,这个属性值只在dev profile 中存在。这样,在本机开发时使用dev 就可以调用网关。这种方式,对开发的代码倾入比较大,在有更好的方式后已经放弃使用。

    ps: spring在生成FeignClient后优先使用url 属性,如果url 属性不存在才会使用负载均衡,从负载均衡中找一个地址进行调用

  2. 修改源码,修改@EnableFeignClient 注解源码,在扫描@FeignClient时,根据环境来判断是否生成特定的url 。但是,由于@EnableFeignClient 中import 的类,是一个非public ,无法继承,所以只能够使用一个新的注解。但是这样修改后,对以后的升级就不能够很好的过度。所以这个放弃了

  3. 最终使用的方法,我们对LoadBalancerFeignClient 这个类进行了扩展,重写了execute 方法 。然后我们加入了启动参数,当启动参数是指定的值时,我们就没用通过负载均衡拿到的地址进行请求,而是生成一个固定的gatewayurl 来请求。这个自定义的LoadBalancerFeignClient 是在启动容器时,根据这个启动参数来判断,是初始化默认的LoadBalancerFeignClient 还是自定义的LoadBalancerFeignClient 。

我们最终采用第三种方式来修改,这样对开发的代码倾入最小,spring-cloud 的升级也能够相对平滑。

ps:

为什么没有通过xx-service.ribbon.serverList 这个参数来指定具体的服务?

由于我们的url格式是 http://geteway/xx-service/a 当发出的具体请求时,ribbon 会截取host+port ,不会携带path 参数也就是会丢失xx-service ,所以最后的请求url 就会变成了http://geteway/a ,而这个网关是识别不到。

点赞

发表评论

电子邮件地址不会被公开。