欢迎访问Spring Cloud中国社区

《重新定义Spring Cloud实战》由Spring Cloud中国社区倾力打造,基于Spring Cloud的Finchley.RELEASE版本,本书内容宽度足够广、深度足够深,而且立足于生产实践,直接从生产实践出发,包含大量生产实践的配置。欢迎加微信Software_King进群答疑,国内谁在使用Spring Cloud?欢迎登记

Spring Cloud Bamboo源码分析

三分之一程序员 · 1年前 · 1906 ·

一、项目简介

Spring cloud bamboo是spring cloud中国社区推出的一个多版本控制插件,它通过扩展spring-cloud-ribbon实现了多版本调用,地址为https://github.com/SpringCloud/spring-cloud-gray/tree/master/spring-cloud-bamboo。

二、思路分析

在spring cloud微服务体系中,服务的请求主要为通过网关和内部服务之间调用,通过向项目开发者了解到目前spring-cloud-bamboo主要支持的是spring cloud Dalston.SR5版本以及spring boot1.5.4-1.5.7,也就是说网关是zuul,而内部调用在spring cloud下则为spring cloud-feign以及Ribbon+RestTemplate的方式,其中发现核心都是通过spring cloud ribbon去负载均衡的,所以说要实现多版本控制必须先要修改默认的Ribbon负载均衡规则,其次在zuul、feign、restTemplate做衔接。

三、核心实现Ribbon源码分析

首先通过思路分析,我们去找到项目的配置类BambooAutoConfiguration.java发现有@RibbonClients(defaultConfiguration = BambooRibbonClientsConfiguration.class),这个指明了Ribbon的配置类,打开配置来发现在这里配置了Bamboo的负载均衡规则

这里分析一下此配置的作用,通过@ConditionalOnMissingBean(value = {BambooAutoConfiguration.UnUseBambooIRule.class})配置了多版本负载均衡失效策略,这是为了其他扩展要修改负载均衡而做出的配置,其中BambooZoneAvoidanceRule类是负载均衡的规则。代码如下。

其中重写了Ribbon默认的ZoneAvoidanceRule的getPredicate(),返回了封装了原先默认的规则和先有的版本验证的Predicate,版本验证规则实现在BambooApiVersionPredicate类实现的,其中BambooZoneAvoidanceRule会被ILoadBalancer执行时调用choose方法,代码如下

choose方法调用了gerPredicate()中的带过滤的负载均衡算法,最终在getEligibleServers()方法里调用了在BambooApiVersionPredicate类重写的apply()方法进行了版本过滤,代码如下

四、zuul、feign、restTemplate与ribbon的衔接

zuul和feign都是默认启用了Ribbon,restTemplate则是通过拦截器的方式注入了ribbon的逻辑,而通过上面分析bamboo已经做了新的负载均衡规则,而bamboo只需在这zuul、feign、restTemplate拦截器中做参数处理即可。

4.1 zuul

首先在zuul里通过增加filter的方式注入了bamboo的逻辑,路由前filter代码如下:

首先是封装了Bamboo所需的request已经需要的一些参数处理,而这些最终都是交给DefaultRibbonConnectionPoint类去处理,DefaultRibbonConnectionPoint类实现了BambooRibbonConnectionPoint接口,实现了executeConnectPoint()方法和shutdownconnectPoint()方法,其中executeConnectPoint()主要为ribbon做参数处理,DefaultRibbonConnectionPoint()则是做一些清理,其中executeConnectPoint()实现如下

其中在executeConnectPoint()方法中可以看到一个List<LoadBalanceRequestTrigger>,spring-cloud-bamboo提供一个LoadBalanceRequestTrigger接口,用户可以实现这个接口,这个接口的实现将会在ribbon请求发起时被执行。
BambooPostZuulFilter主要是在zuul在路由完请求执行的,bamboo主要在这个类中调用了DefaultRibbonConnectionPoint的shutdownconnectPoint()方法去清理了Threadlocal里的参数清理,此处不再赘述。

4.2 Feign

在bamboo.feign.config目录可以找到bamboo关于feign做的衔接配置,代码如下

首先用@EnableFeignClients指明了Feign的配置类来代替默认的配置,在BambooFeignClientsConfiguration中bamboo注入了默认的FeignClient,通过注入的FeignClient去构造了BambooFeignClient,启用了BambooFeignClient去执行请求,源码如下

通过源码我们可以发现,bamboo未对原来的feign做任何的修改,只是加入了ribbon版本过滤所需的参数构造逻辑,最终还是delegate.execute(request, options)去执行,有关参数构造的问题此处不再赘述,有兴趣的话可以去阅读源码。

4.3 restTemplate

默认ribbon的对restTemplate的处理为,构造了一个LoadBalancerInterceptor类去执行了ribbon逻辑,而bamboo已经对ribbon的负载均衡规则做了扩展实现了多版本控制,所以此处只需要构造一个ClientHttpRequestInterceptor的实现去构造ribbon多版本过滤的参数就行,bamboo实现的代码如下

四、关于version的设置和获取

bamboo对version的设置为通过eureka的eureka.instance.metadata-map设置,多版本用逗号分割,这是利用了eureka的元数据特性,服务消费者可以从eureka上获取到version来进行过滤。bamboo对获取用户请求的version提供了一个接口,源码如下图

bamboo默认是通过queryString获取的,用户可根据需求来实现这个接口去做扩展或者修改默认内部类的实现。

个人博客地址:http://xuyangyang.club
微信公众号:三分之一程序员