Talk about eager load of spring cloud netflix ribbon.

  spring-cloud

Order

This article mainly studies eager load of spring cloud netflix ribbon.

RibbonAutoConfiguration

spring-cloud-netflix-ribbon-2.1.1.RELEASE-sources.jar! /org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java

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

        }

    }

}
  • RibbonAutoConfiguration has enabled RibbonEagerLoadProperties and ServerIntrospectorProperties, where SpringClientFactory is mainly registered. Create LoadBalancerClient, LoadBalancedRetryFactory, Ribbon ApplicationContextInitializer, RestTemplateCustomizer, and RibbonClientHttpRequestFactory

RibbonApplicationContextInitializer

spring-cloud-netflix-ribbon-2.1.1.RELEASE-sources.jar! /org/springframework/cloud/netflix/ribbon/RibbonApplicationContextInitializer.java

public class RibbonApplicationContextInitializer
        implements ApplicationListener<ApplicationReadyEvent> {

    private final SpringClientFactory springClientFactory;

    // List of Ribbon client names
    private final List<String> clientNames;

    public RibbonApplicationContextInitializer(SpringClientFactory springClientFactory,
            List<String> clientNames) {
        this.springClientFactory = springClientFactory;
        this.clientNames = clientNames;
    }

    protected void initialize() {
        if (clientNames != null) {
            for (String clientName : clientNames) {
                this.springClientFactory.getContext(clientName);
            }
        }
    }

    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        initialize();
    }

}
  • The Ribbon ApplicationContextInitializer implements the ApplicationListener interface, which initialize in response to the ApplicationReadyEvent event. Here, the SpringClientFactory. GetContext (ClientName) operation is mainly performed on the configured clientNames one by one.

SpringClientFactory

spring-cloud-netflix-ribbon-2.1.1.RELEASE-sources.jar! /org/springframework/cloud/netflix/ribbon/SpringClientFactory.java

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
    //......

    protected AnnotationConfigApplicationContext getContext(String name) {
        return super.getContext(name);
    }

    //......
}
  • The getContext method of SpringClientFactory called the getContext of the parent class NamedContextFactory

NamedContextFactory

spring-cloud-context-2.2.0.M1-sources.jar! /org/springframework/cloud/context/named/NamedContextFactory.java

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

    //......

    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);
                }
            }
        }
        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;
    }

    //......
}        
  • The getContext of NamedContextFactory is mainly to return or create an annotationconfigurationcontext.

Summary

  • RibbonAutoConfiguration has enabled RibbonEagerLoadProperties and ServerIntrospectorProperties, where SpringClientFactory is mainly registered. Create LoadBalancerClient, LoadBalancedRetryFactory, Ribbon ApplicationContextInitializer, RestTemplateCustomizer, and RibbonClientHttpRequestFactory
  • The Ribbon ApplicationContextInitializer implements the ApplicationListener interface, which initialize in response to the ApplicationReadyEvent event. Here, the SpringClientFactory. GetContext (ClientName) operation is mainly performed on the configured clientNames one by one.
  • The getContext method of SpringClientFactory called the getcontext of the parent class NamedContextFactory; The getContext of NamedContextFactory is mainly to return or create an annotationconfigurationcontext.

doc