三. 微服务源码阅读-Ribbon 源码

2. Ribbon 源码

1. spring.Factories

image-20220308193010687

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.CommonsClientAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.httpclient.HttpClientConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration


# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor

org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
	@Autowired(required = false) // false
	private List<RestTemplate> restTemplates = Collections.emptyList(); // list, 0
}

使用代码

@Bean
@LoadBalanced
RestTemplate restTemplate() {
    return new RestTemplate();
}

@Qualifier // 里面有一个qualifier
public @interface LoadBalanced {

}

只是这里加上@LoadBalanced 注解才能依赖注入到源码中的,如果不加这个注解则没有 ribbon 功能,因为源码里面获取不到这个实例了

2. 把拦截器设置到restTemplate中

LoadBalancerAutoConfiguration.RetryInterceptorAutoConfiguration#restTemplateCustomizer

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
    final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
    return restTemplate -> {
        List<ClientHttpRequestInterceptor> list = new ArrayList<>(
            restTemplate.getInterceptors());
        list.add(loadBalancerInterceptor); // 添加自定义拦截器
        restTemplate.setInterceptors(list);
    };
}

3. 创建 Ribbon 客户端对象

1. spring.factories

image-20220308202231079

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients // configurations 引用的实例化对象过程
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
    @Bean // 客户端工厂
    public SpringClientFactory springClientFactory() {
       SpringClientFactory factory = new SpringClientFactory();
       factory.setConfigurations(this.configurations);
       return factory;
    }
}

@Import

@Import(RibbonClientConfigurationRegistrar.class) // 导入客户端配置
public @interface RibbonClients {
   RibbonClient[] value() default {};
   Class<?>[] defaultConfiguration() default {};
}

RibbonClientConfigurationRegistrar

public class RibbonClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {
	...
   private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
         Object configuration) {
      BeanDefinitionBuilder builder = BeanDefinitionBuilder
            .genericBeanDefinition(RibbonClientSpecification.class); // 注册特定类
      builder.addConstructorArgValue(name);
      builder.addConstructorArgValue(configuration);
      registry.registerBeanDefinition(name + ".RibbonClientSpecification",
            builder.getBeanDefinition());
   }
}
2. restTemplate 的方法调用源码

调用入口

@HystrixCommand(fallbackMethod = "queryAllFallback",
        commandKey = "queryAll",
        groupKey = "querygroup-one",
        commandProperties = {
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD")
        },
        threadPoolKey = "queryAllhystrixHgypool")
@Override
public String queryAll() {
    // getForObject入口方法
    String results = restTemplate.getForObject("http://"
            + SERVIER_NAME + "/goods/queryGoods", String.class);
    return results;
}

restTemplate 中设置的拦截器,拦截器先调用

// org.springframework.web.client.RestTemplate#doExecute
response = request.execute();
// org.springframework.http.client.AbstractClientHttpRequest#execute
ClientHttpResponse result = executeInternal(this.headers);
// AbstractBufferingClientHttpRequest#executeInternal(HttpHeaders)
ClientHttpResponse result = executeInternal(headers, bytes);
// org.springframework.http.client.InterceptingClientHttpRequest#executeInternal
return requestExecution.execute(this, bufferedOutput);
// org.springframework.http.client.InterceptingClientHttpRequest.InterceptingRequestExecution#execute
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
    if (this.iterator.hasNext()) {
        ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
        return nextInterceptor.intercept(request, body, this);
    } else {
        ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());
        delegate.getHeaders().putAll(request.getHeaders());
        if (body.length > 0) {
            StreamUtils.copy(body, delegate.getBody());
        }
        return delegate.execute();
    }
}
// org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
                                    final ClientHttpRequestExecution execution) throws IOException {
    final URI originalUri = request.getURI();
    String serviceName = originalUri.getHost();
    Assert.state(serviceName != null,
                 "Request URI does not contain a valid hostname: " + originalUri);
    // 核心代码
    return this.loadBalancer.execute(serviceName,
                                     this.requestFactory.createRequest(request, body, execution));
}

来到核心源码

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
	ILoadBalancer loadBalancer = getLoadBalancer(serviceId); // 看这里
	Server server = getServer(loadBalancer);
	if (server == null) {
		throw new IllegalStateException("No instances available for " + serviceId);
	}
	RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
			serviceId), serverIntrospector(serviceId).getMetadata(server));

	return execute(serviceId, ribbonServer, request);
}

// 获取 ILoadBalancer 类型的实例
protected ILoadBalancer getLoadBalancer(String serviceId) {
    return this.clientFactory.getLoadBalancer(serviceId);
}

根据服务名称创建一个容器,然后把容器根据服务名称缓存,这里在调用的时候获取容器的 目的是为了拿到最新的服务列表,所以 ribbon 在第一次服务名称调用的时候是比较慢的, 涉及到创建容器过程,第二次就直接从缓存里面拿容器对象了

// org.springframework.cloud.netflix.ribbon.SpringClientFactory#getInstance
@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);
}
// org.springframework.cloud.context.named.NamedContextFactory#getInstance
public <T> T getInstance(String name, Class<T> type) {
    AnnotationConfigApplicationContext context = getContext(name);
    if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
                                                            type).length > 0) {
        return context.getBean(type);
    }
    return null;
}

// org.springframework.cloud.context.named.NamedContextFactory#getContext
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);
}

这里创建容器对象,并且把两个比较重要的类 register 进去了,register 其实就是把类变成 beanDefinition 对象,其实就是加载这两个类到容器中,触发这两个类重新获取服务列表

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);
   }
   context.setDisplayName(generateDisplayName(name));
   context.refresh();
   return context;
}
3. 第一个注册的类:(注册发现)

org.springframework.cloud.netflix.ribbon.eureka.EurekaRibbonClientConfiguration

创建 serverList 对象

@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
    if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
        return this.propertiesFactory.get(ServerList.class, config, serviceId);
    }
    DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
        config, eurekaClientProvider);
    DomainExtractingServerList serverList = new DomainExtractingServerList(
        discoveryServerList, config, this.approximateZoneFromHostname);
    return serverList;
}

在 DiscoveryEnabledNIWSServerList 类中有一个特别重要的方法

com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList

@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
    return obtainServersViaDiscovery();
}

private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
    List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();

    if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {
        logger.warn("EurekaClient has not been initialized yet, returning an empty list");
        return new ArrayList<DiscoveryEnabledServer>();
    }

    EurekaClient eurekaClient = eurekaClientProvider.get();
    if (vipAddresses!=null){
        for (String vipAddress : vipAddresses.split(",")) {
            // if targetRegion is null, it will be interpreted as the same region of client
            List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion); // 拉取
            ...
    }
    return serverList;
}

com.netflix.discovery.DiscoveryClient#getInstancesByVipAddress(String, boolean, String)

@Override
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,
                                                   @Nullable String region) {
    if (vipAddress == null) {
        throw new IllegalArgumentException(
                "Supplied VIP Address cannot be null");
    }
    Applications applications;
    if (instanceRegionChecker.isLocalRegion(region)) {
        applications = this.localRegionApps.get(); // 从本地拉取
    } else {
        applications = remoteRegionVsApps.get(region); // 远程拉取
        if (null == applications) {
            logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "
                    + "address {}.", region, vipAddress);
            return Collections.emptyList();
        }
    }

    if (!secure) {
        return applications.getInstancesByVirtualHostName(vipAddress);
    } else {
        return applications.getInstancesBySecureVirtualHostName(vipAddress);
    }
}
4. 第二个注册的类:(负载均衡调用)

org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration

这个类中有一个非常重要的方法,serverList 是从第一个注册的类中实例化的,在这里依赖 注入进来了

@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
    if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
        return this.propertiesFactory.get(ILoadBalancer.class, config, name);
    }
    return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,serverListFilter, serverListUpdater);
}

返回的实例就是 ILoadBalancer 类型的,所以我们刚刚看到的

// 获取 ILoadBalancer 类型的实例
protected ILoadBalancer getLoadBalancer(String serviceId) {
    return this.clientFactory.getLoadBalancer(serviceId);
}

获取到的实例就是 ZoneAwareLoadBalancer 实例。在该类实例化的构造函数中,最终会调到 其父类的构造函数,里面有一个方法

com.netflix.loadbalancer.DynamicServerListLoadBalancer

public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                     ServerList<T> serverList, ServerListFilter<T> filter,
                                     ServerListUpdater serverListUpdater) {
    super(clientConfig, rule, ping);
    this.serverListImpl = serverList;
    this.filter = filter;
    this.serverListUpdater = serverListUpdater;
    if (filter instanceof AbstractServerListFilter) {
        ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
    }
    restOfInit(clientConfig); // 这个方法
}

// com.netflix.loadbalancer.DynamicServerListLoadBalancer#restOfInit
void restOfInit(IClientConfig clientConfig) {
    boolean primeConnection = this.isEnablePrimingConnections();
    // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
    this.setEnablePrimingConnections(false);
    enableAndInitLearnNewServersFeature();

    updateListOfServers(); // 这个
    if (primeConnection && this.getPrimeConnections() != null) {
        this.getPrimeConnections()
            .primeConnections(getReachableServers());
    }
    this.setEnablePrimingConnections(primeConnection);
    LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
// com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers
@VisibleForTesting
public void updateListOfServers() {
    List<T> servers = new ArrayList<T>();
    if (serverListImpl != null) {
        servers = serverListImpl.getUpdatedListOfServers(); // 这儿
        LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                     getIdentifier(), servers);

        if (filter != null) {
            servers = filter.getFilteredListOfServers(servers);
            LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                         getIdentifier(), servers);
        }
    }
    updateAllServerList(servers);
}

这里就掉到了这个核心方法,从本地服务列表中获取到了服务列表信息

// com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList#getUpdatedListOfServers
@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
    return obtainServersViaDiscovery();
}
5. 再看看获取到这个实例化的接下来的操作
// RibbonLoadBalancerClient#execute(String,LoadBalancerRequest<T>,Object)
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
    throws IOException {
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId); // 获取到均衡器实例
    Server server = getServer(loadBalancer, hint); // 获取服务
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                                                 isSecure(server, serviceId),
                                                 serverIntrospector(serviceId).getMetadata(server));

    return execute(serviceId, ribbonServer, request); // 具体的http调用
}

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
    if (loadBalancer == null) {
        return null;
    }
    // Use 'default' on a null hint, or just pass it on?
    return loadBalancer.chooseServer(hint != null ? hint : "default");
}

这里就是根据前面从本地服务列表中获取到的列表信息,根据负载均衡算法从中获取到一个

// com.netflix.loadbalancer.BaseLoadBalancer#chooseServer
public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
            return rule.choose(key); // 负载均衡的策略
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

接下来就是具体 http 调用了,又会走回去,进到没有拦截器的实例中

// RibbonLoadBalancerClient#execute(String,LoadBalancerRequest<T>,Object)
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
    throws IOException {
    ILoadBalancer loadBalancer = getLoadBalancer(serviceId); // 获取到均衡器实例
    Server server = getServer(loadBalancer, hint); // 获取服务
    if (server == null) {
        throw new IllegalStateException("No instances available for " + serviceId);
    }
    RibbonServer ribbonServer = new RibbonServer(serviceId, server,
                                                 isSecure(server, serviceId),
                                                 serverIntrospector(serviceId).getMetadata(server));

    return execute(serviceId, ribbonServer, request); // 具体的http调用
}
// RibbonLoadBalancerClient#execute(java.lang.String, ServiceInstance, LoadBalancerRequest<T>)
public <T> T execute(String serviceId, ServiceInstance serviceInstance,
      LoadBalancerRequest<T> request) throws IOException {
   Server server = null;
   if (serviceInstance instanceof RibbonServer) {
      server = ((RibbonServer) serviceInstance).getServer();
   }
   if (server == null) {
      throw new IllegalStateException("No instances available for " + serviceId);
   }

   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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

岁月玲珑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值