【SpringCloud学习】Ribbon源码分析(Greenwich版)

Ribbon源码分析

带着想法读源码

当不使用@LoadBalanced时,会发生什么样的后果。

java.net.UnknownHostException: eureka-client-a
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184) ~[na:1.8.0_151]

从报错可以看出直接把服务名当成ip去请求,显然是找不到的。所以加上注解应该有代码拦截了这个请求,并把服务名转换成ip

查看注解@LoadBalanced

/**
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

注释写的很明显,标记一个restTemplete去配置LoadBlancerClient,那我们看下LoadBlancerClient

public interface LoadBalancerClient extends ServiceInstanceChooser {

    /**
     * Executes request using a ServiceInstance from the LoadBalancer for the specified
     * service.
     */
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    /**
     * Executes request using a ServiceInstance from the LoadBalancer for the specified
     * service.
     */
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

    /**
     * Creates a proper URI with a real host and port for systems to utilize.
     * Some systems use a URI with the logical service name as the host,
     * such as http://myservice/path/to/service.  This will replace the
     * service name with the host:port from the ServiceInstance.
     */
    URI reconstructURI(ServiceInstance instance, URI original);
}

查看继承的接口

public interface ServiceInstanceChooser {

    /**
     * Chooses a ServiceInstance from the LoadBalancer for the specified service.
     * @param serviceId The service ID to look up the LoadBalancer.
     * @return A ServiceInstance that matches the serviceId.
     */
    ServiceInstance choose(String serviceId);
}
  • 从LoadBalancer中为指定的服务选择一个ServiceInstance。

LoadBalancerClient是一个接口,应该会有一个类配置它。在同包下有一个LoadBlancerAutoConfiguration。内容如下

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
    //...
}

虽然没有看到直接生成LoadBalancerClient的bean。但是由注解可以看出这个类和LoadBalancerClient有一定的关联。仔细查看后里面有一段代码值得思考。

@Bean
public LoadBalancerInterceptor ribbonInterceptor(
        LoadBalancerClient loadBalancerClient,
        LoadBalancerRequestFactory requestFactory) {
    return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}

发现把loadbalancerClient和请求放入一个拦截器中,联想到请可能被此处处理,查看拦截器代码

    @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, requestFactory.createRequest(request, body, execution));
    }

debug代码,继续往下走

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

发现ip的出现时RibbonLoadBalancerClient的execte下的getServer或的。
查看getServer

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

进入loadBalancer

    /**
     * Choose a server from load balancer.
     * 
     * @param key An object that the load balancer may use to determine which server to return. null if 
     *         the load balancer does not use this parameter.
     * @return server chosen
     */
    public Server chooseServer(Object key);

从注解可以看出是从负载均衡中挑出一个server,那负载均衡的具体实现就应该在实现类里面。而实现类调用的是ZoneAwareLoadBalancer
所以默认使用ZoneAwareLoadBalancer进行负载算法。
后续代码太过复杂,不做分析。


文章作者: Ciwei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Ciwei !
 上一篇
【SpringCloud学习】Bus消息总线(Greenwich版) 【SpringCloud学习】Bus消息总线(Greenwich版)
Bus消息总线Bus简介Spring Cloud Bus 将分布式的节点用轻量的消息代理连接起来。它可以用于广播配置文件的更改或者服务之间的通讯,也可以用于监控。本文要讲述的是用Spring Cloud Bus实现通知微服务架构的配置文件的
2019-07-20
下一篇 
【SpringCloud学习】Eureka注册中心(Greenwich版) 【SpringCloud学习】Eureka注册中心(Greenwich版)
Eureka注册中心eureka简介eureka是Netflix开发的服务发现组件,本身是一个基于REST的服务。Spring Cloud将它集成在其子项目spring-cloud-netflix中,以实现Spring Cloud的服务发现
2019-07-20
  目录