SpringCloud-源码分析 Ribbon

本文作者:陈刚,叩丁狼高级讲师。原创文章,转载请注明出处。

本文章会通过断点跟踪的方式来解读 Ribbon 源码 ,希望同学们把文章看完之后不仅能够了解 Ribbon的实现原理,还能掌握源码解读的方式和技巧(重要)。

回顾

回顾一下我们的 Ribbon部分内容我们当时使用TestTemplate + LoadBalanced 做了这样的一个案例

 

叩丁狼教育.png

叩丁狼教育.png

当时我们在配置类中做了如下Bean的定义去开启了RestTemplate的负载均衡功能

//通过@LoadBalanced注解表明这个restRemplate开启负载均衡的功能。
    //RestTemplate是spring内置的http请求封装
    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

然后在Consumer中去请求Producer服务(当然会同时开启多个Producer服务)

//访问 PRODUCER 服务
String result = 
restTemplate.getForObject("http://PRODUCER/provide?name="+name,String.class);

而我们需要达到的效果是该请求多次调用会从不同的Producer服务获取到结果(根据负载均衡规则),然而我们发的请求始终只会有一个呀,那么它要如何才能实现服务之间的切换调用呢?那么猜想一下,Ribbon是不是需要需要先拦截到我们的请求,然后根据我们定义的负载均衡算法,然后从服务清单中去选择合适的服务实例,然后完成调用呢???(思考一下)

那么接下来我们就来对这样的一个请求进行源码追踪分析。

@LoadBalanced

我们先来研究一下@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 {
}

注释 “Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient” 告诉我们:@LoadBalanced标签是用来给RestTemplate标记,以使用LoadBalancerClient(负载均衡的客户端)来配置它。
我们继续追踪 LoadBalancerClient的源码

/**
 * Represents a client side load balancer
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {

    /**
     * execute request using a ServiceInstance from the LoadBalancer for the specified
     * service
     * @param serviceId the service id to look up the LoadBalancer
     * @param request allows implementations to execute pre and post actions such as
     * incrementing metrics
     * @return the result of the LoadBalancerRequest callback on the selected
     * ServiceInstance
     */
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    /**
     * execute request using a ServiceInstance from the LoadBalancer for the specified
     * service
     * @param serviceId the service id to look up the LoadBalancer
     * @param serviceInstance the service to execute the request to
     * @param request allows implementations to execute pre and post actions such as
     * incrementing metrics
     * @return the result of the LoadBalancerRequest callback on the selected
     * ServiceInstance
     */
    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;

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

注释“Represents a client side load balancer”表示该接口它是一个客户端负载均衡器 ,提供了几个方法,翻译方法上的注释得知他们的作用大致如下
execute : 使用LoadBalancer中的ServiceInstance为指定的服务执行请求,说白了就是通过它来实现服务的请求调用。
reconstructURI:使用真实主机和端口创建适当的URI以供系统使用,获取要调用的服务的主机和端口

并且该接口它继承ServiceInstanceChooser接口

/**
 * Implemented by classes which use a load balancer to choose a server to
 * send a request to.
 *
 * @author Ryan Baxter
 */
public interface ServiceInstanceChooser {

    /**
     * Choose 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);
}

翻译接口上的注释“ Implemented by classes which use a load balancer to choose a server to send a request to.”大致意思为: 使用负载均衡器选择一个服务,然后去发起请求,而 choose方法的大致作用为:从LoadBalancer负载均衡器中为指定的服务(serviceId)选择一个服务实例(ServiceInstance) ,其实到这里我们大致已经明白了LoadBalancerClient的目的,就是通过choose方法选择要调用的服务实例,通过reconstructURI获取服务和主机和端口,然后通过execute执行服务的调用,而 RibbonLoadBalancerClient是对 LoadBalancerClient 的实现 ,他们的层级关系如下(idea中按ctrl+alt+u查看):

 

image.png

image.png

 

那么LoadBalancer到底是怎么让RestTtemplate 实现负载均衡的呢?要揭露这个答案我们必须得跟踪RestTemplate的的请求,断点一下RestTemplate服务调用的代码,然后去浏览器请求该方法触发断点,看看底层是如何实现调用的

  @RequestMapping("/consumer")
    public String consumer(@RequestParam("name") String name){
        //访问 PRODUCER 服务
        String result = restTemplate.getForObject("http://PRODUCER/provide?name="+name,String.class);
        return result;
    }

跟踪 getForObject 方法进入

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
      RequestCallback requestCallback = acceptHeaderRequestCallback(responseType);
      HttpMessageConverterExtractor<T> responseExtractor =
                new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
      return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables);
    }

这里继续调用了RestTemplate.execute 方法,并且把调用的服务地址传入进去,然后使用HttpMethod.GET方式进行调用,继续跟踪下去

protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
            @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {

        Assert.notNul
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值