一、什么是Ribbon?
Ribbon是Netflix公司开源的一个负载均衡的项目,负载均衡分为两种,一种是服务端的负载均衡,比如nginx,还有一种是客户端负载均衡,而ribbon就是一个基于客户端负载均衡器,运行在客户端,一般在微服务中结合注册中心和Feign使用,今天我们来了解一下Ribbon底层源码是如何实现负载均衡的,这里我们使用SpringBoot项目来学习ribbon的源码分析
二、源码入口
这里我们使用Spring提供的RestTemplate工具类来发起微服务调用,使用过ribbon的程序员都知道,如果直接使用RestTemplate发起服务调用是不会有负载均衡的效果,我们需要加上@LoadBalanced注解,如下图
这样的话我们微服务调用,就会有负载均衡的效果,那么这里面底层是如何实现的呢,其实ribbon的底层还是基于Springboot的自动装配的功能,至于自动装配这里就不在赘述了,
springboot启动的时候会加载在org.springframework.cloud.netflix.ribbon这个jar包里面spring.factories里面RibbonAutoConfiguration类我们来看看这个类里面做了什么
/*
https://www.apache.org/licenses/LICENSE-2.0
package org.springframework.cloud.netflix.ribbon;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import com.netflix.client.IClient;
import com.netflix.client.http.HttpRequest;
import com.netflix.ribbon.Ribbon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
/**
Auto configuration for Ribbon (client side load balancing).
@author Spencer Gibb
@author Dave Syer
@author Biju Kunjummen
*/
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
name = “org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration”)
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
@Autowired(required = false)
private List configurations = new ArrayList<>();
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature(“Ribbon”, Ribbon.class);
}
@Bean
@ConditionalOnMissingBean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
@Bean
@ConditionalOnClass(name = “org.springframework.retry.support.RetryTemplate”)
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
}
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
@Bean
@ConditionalOnProperty(“ribbon.eager-load.enabled”)
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(springClientFactory(),
ribbonEagerLoadProperties.getClients());
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HttpRequest.class)
@ConditionalOnRibbonRestClient
protected static class RibbonClientHttpRequestFactoryConfiguration {
@Autowired
private SpringClientFactory springClientFactory;
@Bean
public RestTemplateCustomizer restTemplateCustomizer(
final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
return restTemplate -> restTemplate
.setRequestFactory(ribbonClientHttpRequestFactory);
}
@Bean
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
return new RibbonClientHttpRequestFactory(this.springClientFactory);
}
}
// TODO: support for autoconfiguring restemplate to use apache http client or okhttp
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnRibbonRestClientCondition.class)
@interface ConditionalOnRibbonRestClient {
}
private static class OnRibbonRestClientCondition extends AnyNestedCondition {
OnRibbonRestClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@Deprecated // remove in Edgware"
@ConditionalOnProperty("ribbon.http.client.enabled")
static class ZuulProperty {
}
@ConditionalOnProperty("ribbon.restclient.enabled")
static class RibbonProperty {
}
}
/**
{@link AllNestedConditions} that checks that either multiple classes are present.
*/
static class RibbonClassesConditions extends AllNestedConditions {
RibbonClassesConditions() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}
@ConditionalOnClass(IClient.class)
static class IClientPresent {
}
@ConditionalOnClass(RestTemplate.class)
static class RestTemplatePresent {
}
@SuppressWarnings(“deprecation”)
@ConditionalOnClass(AsyncRestTemplate.class)
static class AsyncRestTemplatePresent {
}
@ConditionalOnClass(Ribbon.class)
static class RibbonPresent {
}
}
}
在这个类上面有一@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,注解,我们来看看这个LoadBalancerAutoConfiguration类
这里会通过spring的依赖注入,注入所有的加了@LoadBanlance的RequestTemplate类,然后通过RestTemplateCustomizer将所有LoadBalancerInterceptor设置到restemplate的的Interceptors成员变量里面,然后我们来看控制层是如何调用服务的
我们来看看这个restTemplate.getForObject()这个方法,
在这里面有一个关键的代码,request.execute()方法,会调用到AbstractClientHttpRequest类里面的execute方法
@Override
public final ClientHttpResponse execute() throws IOException {
assertNotExecuted();
ClientHttpResponse result = executeInternal(this.headers);
this.executed = true;
return result;
}
在这个executeInternal方法里面又会调用到AbstractBufferingClientHttpRequest的executeInternal方法,又会调用到InterceptingClientHttpRequest这个类的executeInternald方法
最终会调用到里面的execute方法
在这里面会拿到所有的iterator里面的元素遍历,这里面就是一堆的拦截器,也会包括之前通过SpringBoot启动的时候加载的LoadBalancerInterceptor,然后执行他的intercept方法
而这个LoadBalancerClient有一个唯一的实现类RibbonLoadBalancerClient,继续看调用链
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
很明显这两行代码首先拿到ribbon的负载均衡算法实现类,默认的话是轮询,然后对从注册中心获取的服务进行负载调用,最终通过http方式发起服务的调用,到这里ribbon主线的流程已经完成