深入源码理解Ribbon的运行机制

一、什么是Ribbon?
Ribbon是Netflix公司开源的一个负载均衡的项目,负载均衡分为两种,一种是服务端的负载均衡,比如nginx,还有一种是客户端负载均衡,而ribbon就是一个基于客户端负载均衡器,运行在客户端,一般在微服务中结合注册中心和Feign使用,今天我们来了解一下Ribbon底层源码是如何实现负载均衡的,这里我们使用SpringBoot项目来学习ribbon的源码分析
二、源码入口
这里我们使用Spring提供的RestTemplate工具类来发起微服务调用,使用过ribbon的程序员都知道,如果直接使用RestTemplate发起服务调用是不会有负载均衡的效果,我们需要加上@LoadBalanced注解,如下图
深入源码理解Ribbon的运行机制_第1张图片
这样的话我们微服务调用,就会有负载均衡的效果,那么这里面底层是如何实现的呢,其实ribbon的底层还是基于Springboot的自动装配的功能,至于自动装配这里就不在赘述了,
深入源码理解Ribbon的运行机制_第2张图片

springboot启动的时候会加载在org.springframework.cloud.netflix.ribbon这个jar包里面spring.factories里面RibbonAutoConfiguration类我们来看看这个类里面做了什么

/*

  • Copyright 2013-2020 the original author or authors.
  • Licensed under the Apache License, Version 2.0 (the “License”);
  • you may not use this file except in compliance with the License.
  • You may obtain a copy of the License at
  •  https://www.apache.org/licenses/LICENSE-2.0
    
  • Unless required by applicable law or agreed to in writing, software
  • distributed under the License is distributed on an “AS IS” BASIS,
  • WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  • See the License for the specific language governing permissions and
  • limitations under the License.
    */

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类

深入源码理解Ribbon的运行机制_第3张图片
深入源码理解Ribbon的运行机制_第4张图片

这里会通过spring的依赖注入,注入所有的加了@LoadBanlance的RequestTemplate类,然后通过RestTemplateCustomizer将所有LoadBalancerInterceptor设置到restemplate的的Interceptors成员变量里面,然后我们来看控制层是如何调用服务的

深入源码理解Ribbon的运行机制_第5张图片
我们来看看这个restTemplate.getForObject()这个方法,
深入源码理解Ribbon的运行机制_第6张图片
深入源码理解Ribbon的运行机制_第7张图片

我们在看看这个doExecute方法
深入源码理解Ribbon的运行机制_第8张图片

在这里面有一个关键的代码,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方法
深入源码理解Ribbon的运行机制_第9张图片
最终会调用到里面的execute方法

深入源码理解Ribbon的运行机制_第10张图片
在这里面会拿到所有的iterator里面的元素遍历,这里面就是一堆的拦截器,也会包括之前通过SpringBoot启动的时候加载的LoadBalancerInterceptor,然后执行他的intercept方法

深入源码理解Ribbon的运行机制_第11张图片
而这个LoadBalancerClient有一个唯一的实现类RibbonLoadBalancerClient,继续看调用链

深入源码理解Ribbon的运行机制_第12张图片
深入源码理解Ribbon的运行机制_第13张图片
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
很明显这两行代码首先拿到ribbon的负载均衡算法实现类,默认的话是轮询,然后对从注册中心获取的服务进行负载调用,最终通过http方式发起服务的调用,到这里ribbon主线的流程已经完成

你可能感兴趣的:(源码,spring,boot,spring)