16. Client Side Load Balancer:Ribbon
Ribbon 是一个 client-side 负载均衡器,可以让您对 HTTP 和 TCP 客户端的行为进行大量控制。 Feign 已使用 Ribbon,因此,如果您使用@FeignClient
,则此部分也适用。
Ribbon 中的一个核心概念是名为 client 的概念。每个负载均衡器都是一组组件的一部分,这些组件一起工作以按需联系 remote 服务器,并且整体具有 name,您可以将其作为 application 开发人员提供(对于 example,使用@FeignClient
annotation)。根据需要,Spring Cloud 通过使用RibbonClientConfiguration
为每个命名的 client 创建一个新的集合ApplicationContext
。这包含(除其他外)ILoadBalancer
,RestClient
和ServerListFilter
。
16.1 如何包含 Ribbon
要在项目中包含 Ribbon,请使用 group ID 为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 = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}
在这种情况下,client 由RibbonClientConfiguration
中的组件和CustomConfiguration
中的任何组件组成(后者通常会覆盖前者)。
CustomConfiguration
clas 必须是@Configuration
class,但要注意它不在主 application context 的@ComponentScan
中。否则,所有@RibbonClients
共享它。如果使用@ComponentScan
(或@SpringBootApplication
),则需要采取措施以避免包含它(例如,您可以将其放在单独的 non-overlapping 包中,或指定要在@ComponentScan
中显式扫描的包)。
以下 table 显示 Spring Cloud Netflix 默认为 Ribbon 提供的 beans:
Bean 类型 | Bean Name | 班级名称 |
---|---|---|
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,如下面的 example 所示:
@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();
}
}
前面的 example 中的 include 语句将NoOpPing
替换为PingUrl
并提供自定义serverListFilter
。
16.3 自定义所有 Ribbon Clients 的默认值
可以使用@RibbonClients
annotation 并注册默认 configuration 为所有 Ribbon Clients 提供默认的 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 文档兼容来自定义 Ribbon clients。
这使您可以在不同环境中的启动 time 时更改行为。
以下列表显示了受支持的 properties>:
-
<clientName>.ribbon.NFLoadBalancerClassName
:应该实现ILoadBalancer
-
<clientName>.ribbon.NFLoadBalancerRuleClassName
:应该实现IRule
-
<clientName>.ribbon.NFLoadBalancerPingClassName
:应该实现IPing
-
<clientName>.ribbon.NIWSServerListClassName
:应该实现ServerList
-
<clientName>.ribbon.NIWSServerListFilterClassName
:应该实现ServerListFilter
这些 properties 中定义的类优先于使用
@RibbonClient(configuration=MyRibbonConfig.class)
定义的 beans 和 Spring Cloud Netflix 提供的默认值。
要为名为users
的服务 name 设置IRule
,可以设置以下 properties:
application.yml.
users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
有关 Ribbon 提供的 implementations,请参阅Ribbon 文档。
16.5 在 Eureka 中使用 Ribbon
当 Eureka 与 Ribbon 一起使用时(也就是两者都在 classpath 上)时,ribbonServerList
被覆盖,扩展名为DiscoveryEnabledNIWSServerList
,从 Eureka 填充服务器列表。它还将IPing
接口替换为NIWSDiscoveryPing
,它委托 Eureka 来确定服务器是否已启动。默认情况下安装的ServerList
是DomainExtractingServerList
。其目的是在不使用 AWS AMI 元数据的情况下使负载均衡器可以使用元数据(这是 Netflix 所依赖的)。默认情况下,服务器列表使用“zone”信息构造,如实例元数据中所提供的(因此,在 remote clients 上设置eureka.instance.metadataMap.zone
)。如果缺少,并且设置了approximateZoneFromHostname
flag,则可以使用服务器主机名中的域 name 作为 zone 的代理。一旦 zone 信息可用,它就可以在ServerListFilter
中使用。默认情况下,它用于在与 client 相同的 zone 中定位服务器,因为默认值为ZonePreferenceServerListFilter
。默认情况下,client 的 zone 的确定方式与 remote 实例相同(即通过eureka.instance.metadataMap.zone
)。
设置 client zone 的正统“archaius”方法是通过名为“@zone”的 configuration property。如果可用,Spring Cloud 优先于所有其他设置使用它(请注意,必须在 YAML configuration 中引用 key)。
如果没有其他 zone 数据源,则根据 client configuration(而不是实例 configuration)进行猜测。我们将
eureka.client.availabilityZones
(区域 name 中的 map)带到区域列表中,然后拉出实例自己区域的第一个 zone(即eureka.client.region
,默认为“us-east-1”,以便与本机 Netflix 兼容)。
16.6 示例:如何在没有 Eureka 的情况下使用 Ribbon
Eureka 是一种抽象 remote 服务器发现的便捷方式,因此您无需在 clients 中对其 URL 进行硬编码。但是,如果您不想使用 Eureka,Ribbon 和 Feign 也可以使用。假设您已为“stores”声明了@RibbonClient
,并且 Eureka 未被使用(甚至在 classpath 上也没有)。 Ribbon client 默认为已配置的服务器列表。您可以按如下方式提供 configuration:
application.yml.
stores:
ribbon:
listOfServers: example.com,google.com
16.7 示例:在 Ribbon 中禁用 Eureka 使用
将ribbon.eureka.enabled
property 设置为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 维护的相应的子 application Context。此 application context 在对指定 client 的第一个请求上被延迟加载。通过指定 Ribbon clients 的名称,可以将此惰性 loading 行为更改为在启动时急切加载这些子 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 更改此行为:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
前面的 example 导致 HystrixCommands 在 Hystrix 线程池中为每个 route 执行。
在这种情况下,默认HystrixThreadPoolKey
与每个 route 的服务 ID 相同。要为HystrixThreadPoolKey
添加前缀,请将zuul.threadPool.threadPoolKeyPrefix
设置为要添加的 value,如下面的 example 所示:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgw
16.11 如何为 Ribbon 的 IRule 提供 Key
如果您需要提供自己的IRule
implementation 来处理像“canary”测试这样的特殊路由要求,请将一些信息传递给IRule
的choose
方法。
com.netflix.loadbalancer.IRule.java.
public interface IRule{
public Server choose(Object key);
:
您可以提供IRule
implementation 用于选择目标服务器的一些信息,如以下 example 所示:
RequestContext.getCurrentContext()
.set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");
如果使用的 key 将任何 object 放入RequestContext
,它将被传递给IRule
implementation 的choose
方法。必须在执行RibbonRoutingFilter
之前执行上一个 example 中显示的 code。 Zuul 的预过滤器是最好的选择。您可以通过预过滤器中的RequestContext
访问 HTTP headers 和查询参数,因此可以用它来确定传递给 Ribbon 的LOAD_BALANCER_KEY
。如果在RequestContext
中没有将_val与LOAD_BALANCER_KEY
放在一起,则将 null 作为choose
方法的参数传递。