RestTemplet+Ribbon实现负载均衡源码分析

本文涉及的产品
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 为什么加上@LoadBalanced,RestTemplate就有负载均衡的能力呢?源码分析:

为什么加上@LoadBalanced,RestTemplate就有负载均衡的能力呢?源码分析:

RestTemplate拦截器

首先看RestTemplate类,继承了InterceptingHttpAccessor,代码中有个类型为ClientHttpRequestInterceptor拦截器集合,Ribbon的功能就是在这扩展的。

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
   
  ......
}
public abstract class InterceptingHttpAccessor extends HttpAccessor {
   
    private final List<ClientHttpRequestInterceptor> interceptors = new ArrayList();
  ......
}

拦截器注入

Interceptor在哪里注入给RestTemplate的?看LoadBalancerAutoConfiguration这个类,该类重点关注这个静态内部类LoadBalancerInterceptorConfig,其往spring注入了LoadBalancerInterceptor这个Bean,实现了上面所讲的ClientHttpRequestInterceptor接口,并赋值给RestTemplate。

@Configuration(
    proxyBeanMethods = false
)
@ConditionalOnClass({
   RestTemplate.class})
@ConditionalOnBean({
   LoadBalancerClient.class})
@EnableConfigurationProperties({
   LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
   
  ......
    @ConditionalOnMissingClass({
   "org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
   
        LoadBalancerInterceptorConfig() {
   
        }

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

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
   
            return (restTemplate) -> {
   
                List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
        }
    }
}

RestTemplate执行过程

接着看RestTemplate请求执行的过程,在执行doExecute()时获取一个ClientHttpRequest->InterceptingClientHttpRequest,调用request.execute()时会做两件事

  1. 遍历intercept,对请求的URL进行处理,这里的intercept也就时步骤2的LoadBalancerInterceptor。
  2. 对处理后的URL进行真正的HTTP请求。
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
   
        Assert.notNull(url, "URI is required");
        Assert.notNull(method, "HttpMethod is required");
        ClientHttpResponse response = null;
        Object var14;
        try {
   
              //InterceptingClientHttpRequest
            ClientHttpRequest request = this.createRequest(url, method);
            ......
            response = request.execute();
              ......
        } catch (IOException var12) {
   
           ......
        } finally {
   
           ......
        }
        return var14;
    }
class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
   
    ......
    protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
   
        InterceptingClientHttpRequest.InterceptingRequestExecution requestExecution = new InterceptingClientHttpRequest.InterceptingRequestExecution();
        return requestExecution.execute(this, bufferedOutput);
    }
    private class InterceptingRequestExecution implements ClientHttpRequestExecution {
   
        ......
        public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
   
          //这里执行LoadBalancerInterceptor
            if (this.iterator.hasNext()) {
   
                ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();
                return nextInterceptor.intercept(request, body, this);
            } else {
   
              //拦截器处理完后执行真正的HTTP请求
                ......
                ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);
                ......
                return delegate.execute();
            }
        }
    }
}

Ribbon拦截器处理

4.intercept是如何处理URL的?调用intercept时有个loadBalancer,其实现为RibbonLoadBalancerClient,在执行loadBalancer的execute时将URL映射成对应的ip:port,期间包括负载策略、重试策略、服务列表的获取等功能。

//拦截器的处理
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
   
   ......
    public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
   
        ......
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
}
public class RibbonLoadBalancerClient implements LoadBalancerClient {
   
  ......
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
   
    //获取“service-provider”的服务列表
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
    //通过负载策略选择具体的服务
        Server server = this.getServer(loadBalancer, hint);
        if (server == null) {
   
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
   
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
        }
    }
      protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
   
     //这里是BaseLoadBalancer
        return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
    }
  ......
}
public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {
   
  //默认的负载策略
    private final static IRule DEFAULT_RULE = new RoundRobinRule();
    protected IRule rule = DEFAULT_RULE;

    public BaseLoadBalancer() {
   
        this.name = DEFAULT_NAME;
        this.ping = null;
        setRule(DEFAULT_RULE);
        setupPingTask();
        lbStats = new LoadBalancerStats(DEFAULT_NAME);
    }
        //添加服务
    public void addServer(Server newServer) {
   
         ...
    }

  //设置服务列表
    public void setServersList(List lsrv) {
   
       ......
    }

      //获取服务列表
    public List<Server> getServerList(boolean availableOnly) {
   
        return (availableOnly ? getReachableServers() : getAllServers());
    } 

      //根据负载策略选择具体的服务
    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;
            }
        }
    }
}

简单提一嘴,Ribbon在2019年停止维护,2020年cloud版本删除了Ribbon的依赖由Loadbalancer顶替负载均衡,个人感觉跟Ribbon一样。

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
14天前
|
负载均衡 算法 架构师
Ribbon负载均衡
上一节就已经实现的负载均衡笔者并未深入探讨,本节通过分析负载均衡算法、Ribbon实现负载均衡的底层原理和实现过程,让大家对负载均衡有了一个大体认识,同时针对Ribbon自定义负载均衡策略,饥饿加载让大家对于Ribbon的了解又多一些。Ribbon实现的负载均衡只是方案之一,我们可以尽量多了解但不要局限于此。
|
4天前
|
存储 设计模式 缓存
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
该文章主要介绍了如何在OpenFeign中集成Ribbon以实现负载均衡,并详细分析了Ribbon中服务选择和服务过滤的核心实现过程。文章还涉及了Ribbon中负载均衡器(ILoadBalancer)和负载均衡策略(IRule)的初始化方式。
OpenFeign集成Ribbon负载均衡-过滤和选择服务核心实现
|
4天前
|
缓存 负载均衡 Java
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
文章标题为“OpenFeign的Ribbon负载均衡详解”,是继OpenFeign十大可扩展组件讨论之后,深入探讨了Ribbon如何为OpenFeign提供负载均衡能力的详解。
OpenFeign最核心组件LoadBalancerFeignClient详解(集成Ribbon负载均衡能力)
|
22天前
|
负载均衡 算法 网络协议
Ribbon 负载均衡源码解读
Ribbon 负载均衡源码解读
41 15
Ribbon 负载均衡源码解读
|
22天前
|
负载均衡 Java API
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
Feign 进行rpc 调用时使用ribbon负载均衡源码解析
38 11
|
2月前
|
缓存 负载均衡 Java
Java一分钟之-Spring Cloud Netflix Ribbon:客户端负载均衡
【6月更文挑战第9天】Spring Cloud Netflix Ribbon是客户端负载均衡器,用于服务间的智能路由。本文介绍了Ribbon的基本概念、快速入门步骤,包括添加依赖、配置服务调用和使用RestTemplate。此外,还讨论了常见问题,如服务实例选择不均、超时和重试设置不当、服务列表更新不及时,并提供了相应的解决策略。最后,展示了如何自定义负载均衡策略。理解并正确使用Ribbon能提升微服务架构的稳定性和效率。
107 3
|
2月前
|
负载均衡 算法 Java
Ribbon怎么实现的负载均衡
Ribbon怎么实现的负载均衡
18 0
|
3月前
|
负载均衡 算法 Java
Ribbon自定义负载均衡算法
Ribbon自定义负载均衡算法
37 1
|
3月前
|
负载均衡
【SpringCloud】Ribbon负载均衡原理、负载均衡策略、饥饿加载
【SpringCloud】Ribbon负载均衡原理、负载均衡策略、饥饿加载
42 0
|
3月前
|
负载均衡
Ribbon负载均衡策略
Ribbon负载均衡策略