SpringCloud - Spring Cloud Netflix - 客户端负载均衡:Ribbon

参考:https://cloud.spring.io/spring-cloud-static/Edgware.SR5/multi/multi_spring-cloud-ribbon.html

Ribbon是一个客户端负载均衡器,可以对HTTP和TCP客户端的行为进行控制。 Feign已使用Ribbon,因此如果使用的是@FeignClient,则此部分也适用。

Ribbon中一个核心是指定客户端。 每个负载均衡器都是一组组件的一部分,这些组件一起工作以按需联系远程服务器,并且都能够指定名称(例如,使用@FeignClient注解)。 Spring Cloud使用RibbonClientConfiguration按需为每个命名的客户端创建一个新的集合作为ApplicationContext。 这包含(除其他外)ILoadBalancer、RestClient和ServerListFilter。

1. 如何包含Ribbon
要在项目中包含Ribbon,请使用group为 org.springframework.cloud和artifact id为 spring-cloud-starter-netflix-ribbon的starter。 

2. 自定义Ribbon客户端
可以在外部属性配置中使用 <client>.ribbon.* 配置Ribbon客户端,这与使用原生的Netflix API没有什么不同,只是可以使用Spring Boot配置文件。 可以在CommonClientConfigKey(ribbon-core的一部分)中将本地选项作为静态字段进行检查。

Spring Cloud还允许通过使用@RibbonClient声明其他配置(在RibbonClientConfiguration之上)来完全控制客户端。 例:

@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}

在这种情况下,客户端由RibbonClientConfiguration中已有的组件以及FooConfiguration中的任何组件组成(后者通常会覆盖前者)。

注:
FooConfiguration必须是@Configuration,但要注意它不在主应用程序上下文的@ComponentScan中,否则它将由所有@RibbonClients共享。如果使用@ComponentScan(或@SpringBootApplication),则需要采取措施以避免包含它(例如将其放在单独的非重叠包中,或在@ComponentScan中指定显式扫描的包)。

Spring Cloud Netflix默认为ribbon(格式: BeanType beanName:ClassName)提供以下bean:

  • IClientConfig ribbonClientConfig:DefaultClientConfigImpl
  • IRule ribbonRule:ZoneAvoidanceRule
  • IPing ribbonPing:DummyPing
  • ServerList<Server> ribbonServerList:ConfigurationBasedServerList
  • ServerListFilter<Server> ribbonServerListFilter:ZonePreferenceServerListFilter
  • ILoadBalancer ribbonLoadBalancer:ZoneAwareLoadBalancer
  • ServerListUpdater ribbonServerListUpdater:PollingServerListUpdater

创建其中一种类型的bean并将其放在@RibbonClient配置中(例如上面的FooConfiguration)允许覆盖所描述的每个bean。例:

@Configuration
protected static class FooConfiguration {
    @Bean
    public ZonePreferenceServerListFilter serverListFilter() {
        ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
        filter.setZone("myTestZone");
        return filter;
    }

    @Bean
    public IPing ribbonPing() {
        return new PingUrl();
    }
}

这将使用PingUrl替换NoOpPing并提供自定义serverListFilter

3. 自定义所有Ribbon客户端的默认值
可以使用@RibbonClients注解为所有Ribbon客户端提供默认配置并注册,如以下示例所示:

@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
public class RibbonClientDefaultConfigurationTestsConfig {

    public static class BazServiceList extends ConfigurationBasedServerList {
        public BazServiceList(IClientConfig config) {
            super.initWithNiwsConfig(config);
        }
    }
}

@Configuration
class DefaultRibbonConfig {

    @Bean
    public IRule ribbonRule() {
        return new BestAvailableRule();
    }

    @Bean
    public IPing ribbonPing() {
        return new PingUrl();
    }

    @Bean
    public ServerList<Server> ribbonServerList(IClientConfig config) {
        return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
    }

    @Bean
    public ServerListSubsetFilter serverListFilter() {
        ServerListSubsetFilter filter = new ServerListSubsetFilter();
        return filter;
    }
}

4. 使用属性自定义Ribbon客户端
从版本1.2.0开始,Spring Cloud Netflix支持使用属性自定义Ribbon客户端以与Ribbon文档兼容。

这允许在不同环境中启动时更改行为。

支持的属性如下所示,并应以 <clientName>.ribbon. 为前缀:

  • NFLoadBalancerClassName:应该实现ILoadBalancer
  • NFLoadBalancerRuleClassName:应该实现IRule
  • NFLoadBalancerPingClassName:应该实现IPing
  • NIWSServerListClassName:应该实现ServerList
  • NIWSServerListFilterClassName:应该实现ServerListFilter

注:这些在属性中定义的类优先于使用@RibbonClient(configuration = MyRibbonConfig.class)定义的bean以及Spring Cloud Netflix提供的默认值。

要为名称为users的服务设置IRule,可以设置以下内容:

application.yml

users:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

有关Ribbon提供的实现,参考文档:https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers

5. 在Eureka上使用Ribbon
当Eureka与Ribbon一起使用时(即两者都在类路径上),ribbonServerList被覆盖,其扩展名为DiscoveryEnabledNIWSServerList,它填充了Eureka的服务器列表。它还用NIWSDiscoveryPing替换IPing接口,NIWSDiscoveryPing委托Eureka来确定服务器是否启动。默认情况下安装的ServerList是DomainExtractingServerList,其目的是在不使用AWS AMI元数据的情况下使负载均衡器可以使用物理元数据(这是Netflix所依赖的)。默认情况下,服务器列表将使用实例元数据中提供的“区域”信息构建(因此在远程客户端上设置eureka.instance.metadataMap.zone),如果缺少它,则可以使用服务器主机名中的域名作为区域的代理(如果设置了flagsZoneFromHostname标志)。一旦区域信息可用,它就可以在ServerListFilter中使用。默认情况下,它将用于在与客户端相同的区域中查找服务器,因为默认值为ZonePreferenceServerListFilter。默认情况下,客户端区域的确定方式与远程实例相同,即通过eureka.instance.metadataMap.zone。

注:

  • 设置客户区的“archaius”方式是通过名为“@zone”的配置属性,如果可用,Spring Cloud将优先于所有其他设置(密钥必须在YAML中引用)。
  • 如果没有其他区域数据源,则根据客户端配置进行猜测(与实例配置相反)。我们使用eureka.client.availabilityZones,它是从区域名称到区域列表的映射,实例使用第一个区域(即eureka.client.region,为了与原生Netflix兼容,默认为"us-east-1")。

6. 示例:如何在没有Eureka的情况下使用Ribbon
Eureka是一种抽象远程服务发现的便捷方式,因此无需在客户端对其URL进行硬编码,但如果不想使用它,则Ribbon和Feign仍然非常适合。假设已为“stores”声明了@RibbonClient,并且Eureka未被使用(甚至在类路径中也没有)。Ribbon客户端默认为已配置的服务器列表,可以提供此类配置

application.yml。

stores:
  ribbon:
    listOfServers: example.com,google.com

7. 示例:禁用Ribbon中的Eureka
设置属性ribbon.eureka.enabled=false将明确禁用在Ribbon中使用Eureka。

application.yml

ribbon:
  eureka:
   enabled: false

8. 直接使用Ribbon API
也可以直接使用LoadBalancerClient。 例:

public class MyClass {
    @Autowired
    private LoadBalancerClient loadBalancer;

    public void doStuff() {
        ServiceInstance instance = loadBalancer.choose("stores");
        URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
        // ... do something with the URI
    }
}

9. Ribbon配置的缓存
每个命名的客户端的Ribbon都有一个Spring Cloud维护的相应子应用程序上下文,这个应用程序上下文在第一次请求指向客户端时被延迟加载。 通过指定Ribbon客户端的名称,可以将此延迟加载行为更改为在启动时加载这些子应用程序上下文。

application.yml

ribbon:
  eager-load:
    enabled: true
    clients: client1, client2, client3

10. 如何配置Hystrix线程池
如果将zuul.ribbonIsolationStrategy更改为THREAD,则Hystrix的线程隔离策略将用于所有路由。 在这种情况下,HystrixThreadPoolKey默认设置为“RibbonCommand”。 这意味着所有路由的HystrixCommands将在同一个Hystrix线程池中执行。 可以使用以下配置更改此行为,这将导致在Hystrix线程池中为每个路由执行HystrixCommands。

application.yml

zuul:
  threadPool:
    useSeparateThreadPools: true

在这种情况下,默认的HystrixThreadPoolKey与每个路由的服务ID相同。 要向HystrixThreadPoolKey添加前缀,将zuul.threadPool.threadPoolKeyPrefix设置为要添加的值。 例如:

application.yml

zuul:
  threadPool:
    useSeparateThreadPools: true
    threadPoolKeyPrefix: zuulgw

11. 如何为Ribbon的IRule提供密钥
如果需要提供自己的IRule实现来处理像canary test(金丝雀测试)这样的特殊路由要求,可能希望将一些信息传递给IRule的choose方法。

com.netflix.loadbalancer.IRule.java

public interface IRule{
    public Server choose(Object key);
         :

可以通过IRule实现提供一些用于选择目标服务器的信息,如下所示:

RequestContext.getCurrentContext()
              .set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");

如果使用密钥FilterConstants.LOAD_BALANCER_KEY将任何对象放入RequestContext,它将被传递给IRule实现的choose方法。 必须在执行RibbonRoutingFilter之前执行上面的代码,Zuul的pre 过滤器是最佳选择。 可以通过pre 过滤器中的RequestContext轻松访问HTTP头和查询参数,因此可以使用它来确定将传递给Ribbon的LOAD_BALANCER_KEY。 如果在RequestContext中没有为LOAD_BALANCER_KEY设置任何值,则null将作为choose方法的参数传递。

猜你喜欢

转载自blog.csdn.net/mytt_10566/article/details/84504336