美文网首页
Ribbon源码分析

Ribbon源码分析

作者: 昨日已逝去 | 来源:发表于2019-02-27 09:36 被阅读0次

    Ribbon源码分析

    原文在github,有些相对路径连接不能跳转,如想看原文项目地址 spingboot2.1.3加springcloud G版本,如果觉的不错给个star 谢谢!

    带着想法读源码

    当不使用@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);
    }
    
    • 使用LoadBalancer中的ServiceInstance执行指定的服务。
    • 创建一个带有真实主机和端口的URI,供系统使用。一些系统使用带有逻辑服务名称的URI作为主机,例如http://myservice/path/to/service。这将取代主机的服务名称:来自ServiceInstance的端口。

    查看继承的接口

    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进行负载算法。
    后续代码太过复杂,不做分析。

    相关文章

      网友评论

          本文标题:Ribbon源码分析

          本文链接:https://www.haomeiwen.com/subject/vfhxuqtx.html