spring boot 2.x spring cloud Greenwich.SR1 负载均衡ribbon自动装配,负载均衡部分源码解析

spring boot 2.x spring cloud Greenwich.SR1 负载均衡ribbon自动装配,负载均衡部分源码解析

1、找到自动装配的主入口;

ribbon的构建,是基于eureka上的,使用ribbon,只需要引入spring-cloud-starter-netflix-ribbon的包就可以了;

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

首先,在项目找到spring-cloud-starter-netflix-ribbon的jar包,看到里面并没有代码,而是引入了spring-cloud-netflix-ribbon的jar包,如图所示:

另外还有一个jar包,也很重要,就是cloud的基础包spring-cloud-starter,后面会提到。

找到spring-cloud-starter-netflix-ribbon包的spring.factories文件;

查看文件内容

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

可以看到,默认ribbon自动装配的配置类是org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;在对应包下,找到这个类文件,内容如下所示:

@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<RibbonClientSpecification> configurations = new ArrayList<>();

	@Autowired
	private RibbonEagerLoadProperties ribbonEagerLoadProperties;

	@Bean
	public HasFeatures ribbonFeature() {
		return HasFeatures.namedFeature("Ribbon", Ribbon.class);
	}

	@Bean
	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
	@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 {

		}

		@ConditionalOnClass(AsyncRestTemplate.class)
		static class AsyncRestTemplatePresent {

		}

		@ConditionalOnClass(Ribbon.class)
		static class RibbonPresent {

		}
	}
}

2、对自动装配类源码分析

2.1、类上注解分析
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })

@Configuration:表示一个配置类

@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class):本类装配成功的先决条件

查看这个内部类,实例化的条件,必须要有IClient.classRestTemplate.classAsyncRestTemplate.classRibbon.class的类;

/**
	 * {@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 {
		}
		@ConditionalOnClass(AsyncRestTemplate.class)
		static class AsyncRestTemplatePresent {
		}
		@ConditionalOnClass(Ribbon.class)
		static class RibbonPresent {
		}
	}

@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"):eureka这个自动装配类再本类装配完后,再执行eureka的装配;就是ribbon实例启动成功后,才会向eureka中注入本服务。

@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,AsyncLoadBalancerAutoConfiguration.class}):本类实例化之前,要先执行LoadBalancerAutoConfiguration和AsyncLoadBalancerAutoConfiguration的自动装配;这里很重要,注入了同步负载均衡器和异步的负载均衡器,后面的代码中会用到。

@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,ServerIntrospectorProperties.class}):使这两个配置类,实例化(生效)。

2.2、类中装配的实例
  @Bean
	public HasFeatures ribbonFeature() {
		return HasFeatures.namedFeature("Ribbon", Ribbon.class);
	}

	@Bean
	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());
	}

从上往下看

2.2.1、HasFeatures:不知道暂时不知道什么作用,过滤掉;
2.2.2、SpringClientFactory:这个工厂类,为容器中注入了比较多组建,我们详细看一下
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
  public SpringClientFactory() {
		super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
	}
}

SpringClientFactory的类,构造方法,为父级,准备了一个RibbonClientConfiguration的类主要的一些方法,就是获取容器中的实例,只拿了一个例子,如下:

 public ILoadBalancer getLoadBalancer(String name) {
		return getInstance(name, ILoadBalancer.class);
	}

SpringClientFactory继承自NamedContextFactory,我们看NamedContextFactory的代码,如下:

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
		implements DisposableBean, ApplicationContextAware {

DisposableBean接口:销毁方法

ApplicationContextAware:这个接口,可以拿到ApplicationContext对象

主要的方法就一个getInstance,以及getInstance的重载方法,就是通过ApplicationContext对象,获取对象的实例;

		@SuppressWarnings("unchecked")
    public <T> T getInstance(String name, ResolvableType type) {
        AnnotationConfigApplicationContext context = getContext(name);
        String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, type);
        if (beanNames.length > 0) {
            for (String beanName : beanNames) {
                if (context.isTypeMatch(beanName, type)) {
                    return (T) context.getBean(beanName);
                }
            }
        }
        return null;
    }

2.2.3、LoadBalancerClient:负载均衡器,主要的操作就是这个;我们看这个的源码,为spring容器中注入了哪些可以用的业务或者组件

主要方法

choose:选择器;

execute:执行器;、

getServer:获取服务;

我们主要看下execute执行器的方法,ribbon的最终执行,就是调用execute方法来执行的;

@Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException {
    //获取Server的实例
		Server server = null;
		if (serviceInstance instanceof RibbonServer) {
			server = ((RibbonServer) serviceInstance).getServer();
		}
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
	//获取ribbon的负载均衡器配置
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);
    //状态记录器
		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

		try {
      //执行,并且设置状态
			T returnVal = request.apply(serviceInstance);
			statsRecorder.recordStats(returnVal);
			return returnVal;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}

步骤,我在代码中,写了注释,主要看这一步

//获取ribbon的负载均衡器配置
		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);

调用最上面实例化的SpringClientFactory中的getLoadBalancerContext方法,再调用getInstance,在getInstance里,再调用父级的super.getContext(name);

 public RibbonLoadBalancerContext getLoadBalancerContext(String serviceId) {
		return getInstance(serviceId, RibbonLoadBalancerContext.class);
	}

 @Override
	public <C> C getInstance(String name, Class<C> type) {
		C instance = super.getInstance(name, type);
		if (instance != null) {
			return instance;
		}
		IClientConfig config = getInstance(name, IClientConfig.class);
		return instantiateWithConfig(getContext(name), type, config);
	}
 @Override
	protected AnnotationConfigApplicationContext getContext(String name) {
		return super.getContext(name);
	}

我们再看父级NamedContextFactory.class的getContext方法,调用了下面的createContext方法。

protected AnnotationConfigApplicationContext getContext(String name) {
		if (!this.contexts.containsKey(name)) {
			synchronized (this.contexts) {
				if (!this.contexts.containsKey(name)) {
					this.contexts.put(name, createContext(name));
				}
			}
		}
		return this.contexts.get(name);
	}

protected AnnotationConfigApplicationContext createContext(String name) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	//为容器中注入实例	
  if (this.configurations.containsKey(name)) {
			for (Class<?> configuration : this.configurations.get(name)
					.getConfiguration()) {
				context.register(configuration);
			}
		}
		for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
			if (entry.getKey().startsWith("default.")) {
				for (Class<?> configuration : entry.getValue().getConfiguration()) {
					context.register(configuration);
				}
			}
		}
  //为容器中,注入this.defaultConfigType
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
				this.propertySourceName,
				Collections.<String, Object>singletonMap(this.propertyName, name)));
		if (this.parent != null) {
			// Uses Environment from parent as well as beans
			context.setParent(this.parent);
			// jdk11 issue
			// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
			context.setClassLoader(this.parent.getClassLoader());
		}
		context.setDisplayName(generateDisplayName(name));
		context.refresh();
		return context;
	}

主要看这一步,为容器中注入的this.defaultConfigType

 //为容器中,注入this.defaultConfigType
		context.register(PropertyPlaceholderAutoConfiguration.class,
				this.defaultConfigType);

this.defaultConfigType 就是SpringClientFactory构造方法中的RibbonClientConfiguration.class,上述有提到过;

剩下的,我们看下RibbonClientConfiguration.class这个类中,主要配置了ribbon的负载均衡算法

	@Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) {
		if (this.propertiesFactory.isSet(IRule.class, name)) {
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}

这个就说明,我们在使用ribbon的时候,切换负载均衡算法,主需要为容器中配置一个IRule接口的实现的实例就可以了;

BestAvailableRule:表示请求数最少策略;

PredicateBaseRule:表示过滤掉一些一直连接失败的服务,或者并发高的服务;先过滤再轮询的策略

RandomRule:表示随机策略;

RetryRule:当前请求超时,那么就再次轮询调用下一个请求,直到成功;

RoundRobinRule:轮询策略;轮询策略下有个加权策略:WeightedResponseTimeRule;另外一个(ResponseTimeWeightedRule)不建议使用了,名字跟WeightedResponseTimeRule差不多,只是单次排列不一样;

ribbon切换默认的负载均衡算法

@Configuration
public class RestConfig {
    @Bean
    @LoadBalanced // Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

    @Bean
    public IRule myRule() {
        // return new RoundRobinRule();
        return new RandomRule();// 达到的目的,用我们重新选择的随机算法替代默认的轮询。
        // return new RetryRule();
    }
}

向spring容器中,注入一个IRule的实例就可以了;

2.2.4、后面的,就不说了,结束了,主要也就是知道ribbon装配了什么,怎么切换负载均衡算法,就可以了,大家有兴趣的,可以再继续看下去,再远程调用的时候,debug模式,会看到更详细。

具体代码信息,可以查看《码云》

原创文章 83 获赞 155 访问量 36万+

猜你喜欢

转载自blog.csdn.net/qq_28410283/article/details/102689483