On this page
16. Client Side Load Balancer:Ribbon
Ribbon 是一个 client 端负载均衡器,它可以让您对 HTTP 和 TCP 客户端的行为进行大量控制。 Feign 已使用 Ribbon,因此如果您使用@FeignClient
,则此部分也适用。
Ribbon 中的一个核心概念是名为 client 的概念。每个负载均衡器都是一组组件的一部分,这些组件一起工作以按需联系 remote 服务器,并且该集合具有 name,您可以将其作为 application 开发人员(e.g. 使用@FeignClient
annotation)。 Spring Cloud 使用RibbonClientConfiguration
为每个命名的 client 创建一个新的集合作为ApplicationContext
。这包含(除其他外)ILoadBalancer
,RestClient
和ServerListFilter
。
16.1 如何包含 Ribbon
要在项目中包含 Ribbon,请使用带有 group org.springframework.cloud
和 artifact id spring-cloud-starter-netflix-ribbon
的 starter。有关使用当前 Spring Cloud Release Train 设置 build 系统的详细信息,请参阅Spring Cloud 项目页面。
16.2 自定义 Ribbon Client
您可以使用<client>.ribbon.*
中的外部 properties 配置 Ribbon client 的某些位,这与本机使用 Netflix API 没有什么不同,除了您可以使用 Spring Boot configuration files。可以在CommonClientConfigKey(ribbon-core 的一部分)中将本机选项作为静态字段进行检查。
Spring Cloud 还允许您通过使用@RibbonClient
声明其他 configuration(在RibbonClientConfiguration
之上)来完全控制 client。 例:
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}
在这种情况下,client 由RibbonClientConfiguration
中的组件和FooConfiguration
中的任何组件组成(后者通常会覆盖前者)。
FooConfiguration
必须是@Configuration
,但要注意它不在主 application context 的@ComponentScan
中,否则它将被所有@RibbonClients
共享。如果使用@ComponentScan
(或@SpringBootApplication
),则需要采取措施以避免包含它(例如将其放在单独的 non-overlapping 包中,或指定要在@ComponentScan
中显式扫描的包)。
Spring Cloud Netflix 默认为 ribbon(BeanType
beanName:ClassName
)提供以下 beans:
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
configuration(例如上面的FooConfiguration
)中,可以覆盖所描述的每个 beans。 例:
@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();
}
}
这将NoOpPing
替换为PingUrl
并提供自定义serverListFilter
16.3 自定义所有 Ribbon Clients 的默认值
可以使用@RibbonClients
annotation 为所有 Ribbon Clients 提供默认的 configuration,并注册默认的 configuration,如下面的示例所示:
@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;
}
}
16.4 使用 properties 自定义 Ribbon Client
从 version 1.2.0 开始,Spring Cloud Netflix 现在支持使用 properties 自定义 Ribbon clients 以与Ribbon 文档兼容。
这允许您在不同环境中的启动 time 时更改行为。
支持的 properties 如下所示,应以<clientName>.ribbon.
为前缀:
NFLoadBalancerClassName
:应该实现ILoadBalancer
NFLoadBalancerRuleClassName
:应该实现IRule
NFLoadBalancerPingClassName
:应该实现IPing
NIWSServerListClassName
:应该实现ServerList
NIWSServerListFilterClassName
应该实现ServerListFilter
这些 properties 中定义的 Classes 优先于使用
@RibbonClient(configuration=MyRibbonConfig.class)
定义的 beans 和 Spring Cloud Netflix 提供的默认值。
要为服务 name users
设置IRule
,您可以设置以下内容:
application.yml.
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
有关 Ribbon 提供的 implementations,请参阅Ribbon 文档。
16.5 在 Eureka 中使用 Ribbon
当 Eureka 与 Ribbon(i.e.,都在 classpath 上)一起使用时,ribbonServerList
被覆盖,扩展名为DiscoveryEnabledNIWSServerList
,填充 Eureka 中的服务器列表。它还将IPing
接口替换为NIWSDiscoveryPing
,它委托给 Eureka 以确定服务器是否已启动。默认情况下安装的ServerList
是DomainExtractingServerList
,其目的是在不使用 AWS AMI 元数据(这是 Netflix 所依赖的内容)的情况下使负载均衡器可以使用物理元数据。默认情况下,服务器列表将使用实例元数据中提供的“zone”信息构建(如此 remote clients set eureka.instance.metadataMap.zone
),如果缺少,则可以使用服务器主机名中的域 name 作为 zone 的代理(如果 flag approximateZoneFromHostname
已设置)。一旦 zone 信息可用,它就可以在ServerListFilter
中使用。默认情况下,它将用于在与 client 相同的 zone 中查找服务器,因为默认值为ZonePreferenceServerListFilter
。 client 的 zone 的确定方式与 remote 实例的默认方式相同,i.e。通过eureka.instance.metadataMap.zone
。
设置 client zone 的正统“archaius”方法是通过名为“@zone”的 configuration property,如果可用,Spring Cloud 将优先于所有其他设置使用它(注意 key 必须在 YAML configuration 中引用) )。
如果没有其他 zone 数据源,则根据 client configuration(而不是实例 configuration)进行猜测。我们将
eureka.client.availabilityZones
,这是一个来自 region name 的 map 到 zones 列表,然后拉出实例自己区域的第一个 zone(i.e.eureka.client.region
,默认为“us-east-1”,以便与本机 Netflix 保持一致)。
16.6 示例:如何在没有 Eureka 的情况下使用 Ribbon
Eureka 是一种抽象 remote 服务器发现的便捷方式,因此您不必在 clients 中对其 URL 进行硬编码,但如果您不想使用它,Ribbon 和 Feign 仍然非常适合。假设您已为“stores”声明了@RibbonClient
,并且 Eureka 未被使用(甚至在 classpath 上也没有)。 Ribbon client 默认为已配置的服务器列表,您可以像这样提供 configuration
application.yml.
stores:
ribbon:
listOfServers: example.com,google.com
16.7 示例:在 Ribbon 中禁用 Eureka 使用
设置 property ribbon.eureka.enabled = false
将在 Ribbon 中明确禁用 Eureka。
application.yml.
ribbon:
eureka:
enabled: false
16.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
}
}
16.9 缓存 Ribbon Configuration
每个名为 client 的 Ribbon 都有 Spring Cloud 维护的 child Application Context,这个 application context 在第一次请求命名的 client 时被懒惰地加载。通过指定 Ribbon clients 的名称,可以将此惰性 loading 行为更改为在启动时急切地加载这些 child Application 上下文。
application.yml.
ribbon:
eager-load:
enabled: true
clients: client1, client2, client3
16.10 如何配置 Hystrix 线程池
如果将zuul.ribbonIsolationStrategy
更改为 THREAD,则 Hystrix 的线程隔离策略将用于所有 routes。在这种情况下,HystrixThreadPoolKey 默认设置为“RibbonCommand”。这意味着所有 routes 的 HystrixCommands 将在同一个 Hystrix 线程池中执行。可以使用以下 configuration 更改此行为,这将导致在 Hystrix 线程池中为每个 route 执行 HystrixCommands。
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
在这种情况下,默认的 HystrixThreadPoolKey 与每个 route 的服务 ID 相同。要为 HystrixThreadPoolKey 添加前缀,请将zuul.threadPool.threadPoolKeyPrefix
设置为要添加的 value。例如:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgw
16.11 如何为 Ribbon 的 IRule 提供 Key
如果您需要提供自己的IRule
implementation 来处理像金丝雀测试这样的特殊路由要求,您可能希望将一些信息传递给IRule
的choose
方法。
com.netflix.loadbalancer.IRule.java.
public interface IRule{
public Server choose(Object key);
:
您可以通过IRule
implementation 提供一些将用于选择目标服务器的信息,如下所示:
RequestContext.getCurrentContext()
.set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");
如果使用 key FilterConstants.LOAD_BALANCER_KEY
将 object 放入RequestContext
,它将被传递给IRule
implementation 的choose
方法。上面的 code 必须在RibbonRoutingFilter
执行之前执行,而 Zuul 的预过滤器是最好的选择。您可以通过预过滤器中的RequestContext
轻松访问 HTTP headers 和查询参数,因此可以用它来确定将传递给 Ribbon 的LOAD_BALANCER_KEY
。如果你没有在RequestContext
中放入任何带有LOAD_BALANCER_KEY
的 value,则 null 将作为choose
方法的参数传递。