ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Nacos下 Ribbon 原理分析

2021-10-24 22:03:14  阅读:266  来源: 互联网

标签:负载 return restTemplate Nacos nacos 均衡器 原理 执行 Ribbon


Ribbon 原理解析

1 初始化 Ribbon 配置信息

  1. 在 nacos 作为注册中心的项目中,需要引入服务发现的依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

其中会引入 spring-cloud-commons 依赖,而该依赖又会引入 loadbancer 的东西,而这个就是 ribbon 的核心。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-commons</artifactId>
</dependency>
  1. 通过案例分析,restTemplate 需要使用 @LoadBalanced 实现负载均衡,那么首先看下 @LoadBalanced,注释中翻译下就是这个注解用来标记 restTemplate 和 webClient 的 bean作为配置使用 LoadBalancerClient, 而 LoadBalancerClient 则是一个接口,定义了执行请求的方法。
@LoadBalanced
@Bean("xxxTemplate")// 自定义 bean name,方便后续区分。
public RestTemplate restTemplate() {
    return new RestTemplate();
}
/**
 * Annotation to mark a RestTemplate or WebClient 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 {
}
  1. 在 loaderbanlancer 包下,查看下所有类,发现 LoadBalancerAutoConfiguration 自动配置类,那么很显然这个类就是实现负载均衡的配置加载类了,其中该类注解上添加了 LoadBalancerClient.class 和 RestTemplate.class,表示需要这两个 bean 都存在才会执行使用该类,而 LoadBalancerClient 该接口,只有一个实现,那么就是 RibbonLoadBalancerClient,也就是负载均衡客户端,如下图。
  2. 然后继续分析,看到了 restTemplates 将所有的 restTemplate 进行自动注入,这里可以看出是要将每一个 restTemplate 装配功能,实现负载均衡。这里可以尝试运行 springboot 项目,看下要进行什么操作。
  3. 初始化 LoadBalancerInterceptor, 然后将 LoadBalancerInterceptor 拦截器配置到 restTemplate 中。
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {

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

    // 将拦截器封装到 restTemplate
    @Bean
    @ConditionalOnMissingBean
    public RestTemplateCustomizer restTemplateCustomizer(
            final LoadBalancerInterceptor loadBalancerInterceptor) {
        return restTemplate -> {
            List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                    restTemplate.getInterceptors());
            list.add(loadBalancerInterceptor);
            restTemplate.setInterceptors(list);
        };
    }
}
  1. LoadBalancerInterceptor 作用
    在拦截器中获取到 originalUri,serviceName,后续猜测就是通过 serviceName,然后向注册中心中,获取到对应的注册的服务ip,最后将最开始的 url,进行替换。测试代码如下:已经在 nacos 上注册了两个 nacos-component-provider 服务,端口号分别是 8080 和 8081,url=http://nacos-component-provider/hystrixI/1,然后一步一步进行 debug,观察 restTemplate 的执行情况。
@GetMapping(value = "/hystrixII/{id}")
public String testRestTemplate(@PathVariable Integer id){
    return restTemplate.getForObject("http://nacos-component-provider/hystrixI/" + id, String.class);
}

2 restTemplate 负载均衡执行过程

  1. restTemplate 执行,首先设置返回的信息格式,因为还没有具体的执行。
  2. 添加 URI 的扩展属性,例如参数等,然后调用RestTemplate#execute(String, HttpMethod, RequestCallback, ResponseExtractor, java.lang.Object…)执行请求
  3. 创建 request 请求,然后执行请求方法
  4. 判断该请求是否已经执行过,如果是,那么就会抛出异常,否则继续执行。
  5. 接着执行 InterceptingClientHttpRequest#executeInternal,主要是执行拦截器中的方法,执行完后,最后才会执行最终发送请求url的方法。
  6. 由于这里 restTemplate 请求中的拦截器列表只有一个,那么就直接就可以走到 LoadBalancerInterceptor#intercept 的方法,这里就是发送请求的核心方法。
  7. 这个 loadBalancer 就是 RibbonLoadBalancerClient,执行负载均衡的功能
  8. 接着调用 RibbonLoadBalancerClient#execute(String, LoadBalancerRequest, Object) 方法,getLoadBalancer(serviceId) 方法,这里使用 serviceId,这里主要是为了做一个缓存,当再次访问该负载均衡器时,不用再去创建一个。
  9. 调用 RibbonLoadBalancerClient#getLoadBalancer 方法,获取该服务的负载均衡器,如果这里第一次获取时会使用IOC的原理,由于 ILoadBalancer.class 类型的实例没有,那么就会去创建一个ZoneAwareLoadBalancer 对象,代码如下,ZoneAwareLoadBalancer 负载均衡器:具备区域意识、动态服务列表的负载均衡器,同时它继承 DynamicServerListLoadBalancer,也就是说会同步 nacos上注册的服务信息节点。
public ILoadBalancer getLoadBalancer(String name) {
    return getInstance(name, ILoadBalancer.class);
}
// 当没有 ILoadBalancer 类型的对象时,则会去创建一个,执行下面的方法
@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);
    }
    // 该ZoneAwareLoadBalancer负载均衡器:具备区域意识、动态服务列表的负载均衡器
    return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
            serverListFilter, serverListUpdater);
}
  1. 负载均衡器就是 ZoneAwareLoadBalancer(动态服务列表负载均衡器),主要负责获取从 nacos 上已经注册好的服务,或者是移除已经下线的服务。每一个服务都有对应的负载均衡器。
  2. getServer(loadBalancer, hint) 方法,这里就可以猜想出来,对应的负载均衡器,就会将我们请求的 url 根据某种规则,轮询、随机等规则,选择一个服务进行使用,默认是采用轮询的方式去获取实例。
  3. 执行 PredicateBasedRule#choose 方法中的chooseRoundRobinAfterFiltering 方法:从nacos上读取到的该 serviceName 对应的所有 serverList,过滤出可用的服务,然后进行轮询。下面是轮询的规则的具体实现
// 轮询的规则的具体实现
//modulo:可用服务列表的大小
private int incrementAndGetModulo(int modulo) {
    for (;;) {
        // 该类维护一个自增器,然后每次访问都+1,对可用列表进行取模,实现轮询的效果
        int current = nextIndex.get();
        int next = (current + 1) % modulo;
        if (nextIndex.compareAndSet(current, next) && current < modulo)
            return current;
    }
}
  1. 根据服务名和负载均衡器,获取到一个服务信息,包括 ip:port 等信息,用于后续替换 url 请求。
  2. request.apply(serviceInstance) 这是一个函数接口,具体的执行的方法是 this.requestFactory.createRequest(request, body, execution),这个方法就是原来httpRequest 进行封装为 ServiceRequestWrapper serviceRequest,然后使用该 serviceRequest 去执行请求。
  3. 接着又回到了 InterceptingClientHttpRequest#execute(HttpRequest, byte[])方法,由于目前已经执行将所有的拦截器都执行完了,那么现在会执行 else 中的部分,真正的去发送请求,下面代码就是构建后新的 URI 请求。
//这个就是 serviceRequest.getURI(),将原来的http://nacos-component-provider/hystrixI/1,替换为 ip:port形式的请求
public URI getURI() {
    // 执行替换的方法
    URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
    return uri;
}


16. 最后构造一个委托类 delegate, 执行修改后的 URI,后续的代码就不需要再 debug 了,因为都是 http 请求相关的,处理响应后的内容等。

标签:负载,return,restTemplate,Nacos,nacos,均衡器,原理,执行,Ribbon
来源: https://blog.csdn.net/LarrYFinal/article/details/120942122

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有