一、Ribbon概述

  Ribbon是 Netflixfa 发布的一个负载均衡器,有助于控制 HTTP 和 TCP客户端行为。在 SpringCloud 中,Eureka一般配合Ribbon进行使用,Ribbon提供了客户端负载均衡的功能,Ribbon利用从Eureka中读取到的服务信息,在调用服务节点提供的服务时,会合理的进行负载。在SpringCloud中可以将注册中心和Ribbon配合使用,Ribbon自动的从注册中心中获取服务提供者的列表信息,并基于内置的负载均衡算法,请求服务

作用:

  • 服务调用:基于Ribbon实现服务调用, 是通过拉取到的所有服务列表组成(服务名-请求路径的)映射关系。借助RestTemplate 最终进行调用
  • 负载均衡:当有多个服务提供者时,Ribbon可以根据负载均衡的算法自动的选择需要调用的服务地址

使用方法:客户端RestTemplate上标注@LoadBalanced

    @LoadBalanced
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }

 二、负载均衡

  在搭建网站时,如果单节点的 web服务性能和可靠性都无法达到要求;或者是在使用外网服务时,经常担心被人攻破,一不小心就会有打开外网端口的情况,通常这个时候加入负载均衡就能有效解决服务问题。
  负载均衡是一种基础的网络服务,其原理是通过运行在前面的负载均衡服务,按照指定的负载均衡算法,将流量分配到后端服务集群上,从而为系统提供并行扩展的能力。负载均衡的应用场景包括流量包、转发规则以及后端服务,由于该服务有内外网个例、健康检查等功能,能够有效提供系统的安全性和可用性。

 

 

客户端负载均衡与服务端负载均衡

服务端负载均衡:先发送请求到负载均衡服务器或者软件,然后通过负载均衡算法,在多个服务器之间选择一个进行访问;即在服务器端再进行负载均衡算法分配

客户端负载均衡:客户端会有一个服务器地址列表,在发送请求前通过负载均衡算法选择一个服务器,然后进行访问,这是客户端负载均衡;即在客户端就进行负载均衡算法分配

源码解析

 

 

 

ServerList:可以响应客户端的特定服务的服务器列表。
ServerListFilter:可以动态获得的具有所需特征的候选服务器列表的过滤器。
ServerListUpdater:用于执行动态服务器列表更新。
Rule:负载均衡策略,用于确定从服务器列表返回哪个服务器。
Ping:客户端用于快速检查服务器当时是否处于活动状态。
LoadBalancer:负载均衡器,负责负载均衡调度的管理。

@LoadBalanced注解
使用Ribbon完成客户端负载均衡往往是从一个注解开始的

@LoadBalanced
@Bean
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

这个注解的主要作用是什么呢,查看源码

/**
* 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 {
}

通过注释可以知道@LoadBalanced注解是用来给RestTemplate做标记,方便我们对RestTemplate添加一个LoadBalancerClient,以实现客户端负载均衡。

自动装配
根据SpringBoot中的自动装配规则可以在spring-cloud-netflix-ribbon-2.1.0.RELEASE.jar 中可以找到spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

找到自动装配的类RibbonAutoConfiguration

@Configuration
@Conditional({RibbonAutoConfiguration.RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter(name ={"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"})
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class,AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class,ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
    @Bean
    public SpringClientFactory springClientFactory() {
        SpringClientFactory factory = new SpringClientFactory();
        factory.setConfigurations(this.configurations);
        return factory;
    }
    @Bean
    @ConditionalOnMissingBean({LoadBalancerClient.class})
    public LoadBalancerClient loadBalancerClient() {
        return new RibbonLoadBalancerClient(this.springClientFactory());
    }
    //省略
}

通过RibbonAutoConfiguration 引入了LoadBalancerAutoConfiguration 配置类

负载均衡调用 

public class LoadBalancerAutoConfiguration {
    //...
}

在该自动化配置类中,主要做了下面三件事:

  • 创建了一个LoadBalancerInterceptor 的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
  • 创建了一个RestTemplateCustomizer 的Bean,用于给RestTemplate 增加LoadBalancerInterceptor 拦截器。
  • 维护了一个被@LoadBalanced 注解修饰的RestTemplate 对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer 的实例来给需要客户端负载均衡的RestTemplate 增加LoadBalancerInterceptor 拦截器
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
  //...
}

  通过源码以及之前的自动化配置类,我们可以看到在拦截器中注入了LoadBalancerClient 的实现。当一个被@LoadBalanced 注解修饰的RestTemplate 对象向外发起HTTP请求时,会被LoadBalancerInterceptor 类的intercept 函数所拦截。由于我们在使用RestTemplate时候采用了服务名作为host,所以直接从HttpRequest 的URI对象中通过getHost()就可以拿到服务名,然后调用execute 函数去根据服务名来选择实例并发起实际的请求。
  分析到这里, LoadBalancerClient 还只是一个抽象的负载均衡器接口,所以我们还需要找到它的具体实现类来进一步分析。通过查看ribbon的源码,我们可以很容易的在org.springframework.cloud.netflix.ribbon 包下找到对应的实现类:RibbonLoadBalancerClient 。

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    //...
}

ServiceInstance choose(String serviceId):根据传入的服务id,从负载均衡器中为指定的服务选择一个服务实例。
T execute(String serviceId, LoadBalancerRequest request):根据传入的服务id,指定的负载均衡器中的服务实例执行请求。
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request):根据传入的服务实例,执行请求。
从 RibbonLoadBalancerClient 代码可以看出,实际负载均衡的是通过 ILoadBalancer 来实现的