On this page
16. Client 端负载均衡器:功能区
Ribbon 是 Client 端负载平衡器,可让您对 HTTP 和 TCPClient 端的行为进行大量控制。 Feign 已经使用了 Ribbon,因此,如果您使用@FeignClient
,则此部分也适用。
Ribbon 中的中心概念是指定 Client 端的概念。每个负载均衡器都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件具有您作为应用程序开发人员提供的名称(例如,使用@FeignClient
注解)。根据需要,Spring Cloud 通过使用RibbonClientConfiguration
为每个命名的 Client 端创建一个新的集合作为ApplicationContext
。其中包含ILoadBalancer
,RestClient
和ServerListFilter
。
16.1 如何包括功能区
要将 Ribbon 包含在您的项目中,请使用组 ID 为org.springframework.cloud
且工件 ID 为spring-cloud-starter-netflix-ribbon
的启动器。有关使用当前 Spring Cloud Release Train 设置构建系统的详细信息,请参见Spring Cloud Project 页面。
16.2 自定义功能区 Client 端
您可以使用<client>.ribbon.*
中的外部属性来配置 RibbonClient 端的某些位,这与本机使用 Netflix API 相似,不同之处在于可以使用 Spring Boot 配置文件。可以将本地选项作为CommonClientConfigKey(功能区核心的一部分)中的静态字段进行检查。
Spring Cloud 还允许您通过使用@RibbonClient
声明其他配置(在RibbonClientConfiguration
之上)来完全控制 Client 端,如以下示例所示:
@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}
在这种情况下,Client 端由RibbonClientConfiguration
中已经存在的组件以及CustomConfiguration
中的任何组件组成(其中后者通常会覆盖前者)。
Warning
CustomConfiguration
clas 必须是@Configuration
类,但请注意,对于主应用程序上下文,它不在@ComponentScan
中。否则,它由所有@RibbonClients
共享。如果使用@ComponentScan
(或@SpringBootApplication
),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan
中显式扫描的程序包)。
下表显示了 Spring Cloud Netflix 默认为 Ribbon 提供的 bean:
Bean Type | Bean Name | Class 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
配置(例如上面的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();
}
}
前面示例中的 include 语句将NoOpPing
替换为PingUrl
并提供了自定义serverListFilter
。
16.3 自定义所有功能区 Client 端的默认设置
通过使用@RibbonClients
Comments 并注册默认配置,可以为所有功能区 Client 端提供默认配置,如以下示例所示:
@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 通过设置属性来自定义功能区 Client 端
从 1.2.0 版开始,Spring Cloud Netflix 现在支持通过将属性设置为与Ribbon documentation兼容来自定义 RibbonClient 程序。
这使您可以在启动时在不同环境中更改行为。
以下列表显示了受支持的属性>:
<clientName>.ribbon.NFLoadBalancerClassName
:应实施ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName
:应实施IRule
<clientName>.ribbon.NFLoadBalancerPingClassName
:应实施IPing
<clientName>.ribbon.NIWSServerListClassName
:应实施ServerList
<clientName>.ribbon.NIWSServerListFilterClassName
:应实施ServerListFilter
Note
这些属性中定义的类优先于使用@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 documentation。
16.5 将功能区与 Eureka 一起使用
当 Eureka 与 Ribbon 结合使用时(也就是说,两者都在 Classpath 上),则ribbonServerList
被 extensionsDiscoveryEnabledNIWSServerList
覆盖,该 extensions 填充了 Eureka 中的服务器列表。它还用NIWSDiscoveryPing
替换了IPing
接口,该接口委托 Eureka 确定服务器是否启动。默认情况下安装的ServerList
是DomainExtractingServerList
。其目的是在不使用 AWS AMI 元数据(Netflix 依靠它)的情况下使元数据可用于负载平衡器。默认情况下,服务器列表是使用实例元数据中提供的“区域”信息构造的(因此,在远程 Client 端上,设置eureka.instance.metadataMap.zone
)。如果缺少该字段,并且设置了approximateZoneFromHostname
标志,则它可以使用服务器主机名中的域名作为该区域的代理。一旦区域信息可用,就可以在ServerListFilter
中使用它。默认情况下,它用于将服务器定位在与 Client 端相同的区域中,因为默认值为ZonePreferenceServerListFilter
。默认情况下,以与远程实例相同的方式(即,通过eureka.instance.metadataMap.zone
)确定 Client 端的区域。
Note
设置 Client 端区域的传统“ archaius”方法是通过名为“ @zone”的配置属性。如果可用,Spring Cloud 会优先使用所有其他设置(请注意,密钥必须在 YAML 配置中用引号引起来)。
Note
如果没有其他区域数据源,则根据 Client 端配置(而不是实例配置)进行猜测。我们采用eureka.client.availabilityZones
(这是从区域名称到区域列表的 Map),然后为实例自己的区域拉出第一个区域(即eureka.client.region
(默认为“ us-east-1”,以与本机兼容) Netflix)。
16.6 示例:如何在没有 Eureka 的情况下使用 Ribbon
Eureka 是一种抽象发现远程服务器的便捷方法,因此您不必在 Client 端中对它们的 URL 进行硬编码。但是,如果您不想使用 Eureka,Ribbon 和 Feign 也可以使用。假设您已经为“Store”声明了@RibbonClient
,并且 Eureka 未被使用(甚至不在 Classpath 上)。功能区 Client 端默认为配置的服务器列表。您可以提供以下配置:
application.yml.
stores:
ribbon:
listOfServers: example.com,google.com
16.7 示例:在功能区中禁用 Eureka 使用
将ribbon.eureka.enabled
属性设置为false
会显式禁用功能区中的 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 的 Client 端都有一个 Spring Cloud 维护的对应子应用程序上下文。该应用程序上下文在对命名 Client 端的第一个请求上延迟加载。通过指定功能区 Client 端的名称,可以更改此延迟加载行为,以代替在启动时急于加载这些子应用程序上下文,如以下示例所示:
application.yml.
ribbon:
eager-load:
enabled: true
clients: client1, client2, client3
16.10 如何配置 Hystrix 线程池
如果将zuul.ribbonIsolationStrategy
更改为THREAD
,则 Hystrix 的线程隔离策略将用于所有路由。在这种情况下,HystrixThreadPoolKey
默认设置为RibbonCommand
。这意味着所有路由的 HystrixCommands 在同一个 Hystrix 线程池中执行。可以使用以下配置更改此行为:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
前面的示例导致在 Hystrix 线程池中为每个路由执行 HystrixCommands。
在这种情况下,默认的HystrixThreadPoolKey
与每个路由的服务 ID 相同。要将前缀添加到HystrixThreadPoolKey
,请将zuul.threadPool.threadPoolKeyPrefix
设置为要添加的值,如以下示例所示:
application.yml.
zuul:
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgw
16.11 如何为功能区的 IRule 提供密钥
如果您需要提供自己的IRule
实现来处理特殊的路由要求(例如“ canary”测试),请将一些信息传递给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 的前置filter是执行此操作的最佳位置。您可以通过Pre filter中的RequestContext
访问 HTTPHeaders 和查询参数,因此可以用来确定传递到功能区的LOAD_BALANCER_KEY
。如果您没有在RequestContext
中使用LOAD_BALANCER_KEY
放置任何值,则将 NULL 作为choose
方法的参数传递。