客户端负载均衡器:Ribbon
Ribbon是一个客户端负载均衡器,可以让你对HTTP和TCP客户端的行为进行大量控制,Feign已经使用了Ribbon,因此,如果你使用@FeignClient
,此部分也适用。
Ribbon中的一个核心概念是命名客户端,每个负载均衡器都是一组组件的一部分,这些组件一起工作以按需联系远程服务器,并且该集合具有你作为应用程序开发人员提供的名称(例如,通过使用@FeignClient
注解)。根据需要,Spring Cloud通过使用RibbonClientConfiguration
为每个命名客户端创建一个新的集合作为ApplicationContext
,这包含(除其他外)ILoadBalancer
、RestClient
和ServerListFilter
。
如何包含Ribbon
要在项目中包含Ribbon,使用组ID为org.springframework.cloud
和工件ID为spring-cloud-starter-netflix-ribbon
。
自定义Ribbon客户端
你可以使用<client>.ribbon.*
中的外部属性配置Ribbon客户端的某些,这类似于使用原生Netflix API,但你可以使用Spring Boot配置文件,可以在CommonClientConfigKey(ribbon-core的一部分)中将原生选项作为静态字段进行检查。
Spring Cloud还允许你通过使用@RibbonClient
声明其他配置(在RibbonClientConfiguration
之上)来完全控制客户端,如以下示例所示:
@Configuration
@RibbonClient(name = "custom", configuration = CustomConfiguration.class)
public class TestConfiguration {
}
在这种情况下,客户端由RibbonClientConfiguration
中已有的组件以及CustomConfiguration
(后者通常覆盖前者)中的任何组件组成。
CustomConfiguration
类必须是
@Configuration
类,但要注意它不在
@ComponentScan
中用于主应用程序上下文,否则,它由所有
@RibbonClients
共享。如果使用
@ComponentScan
(或
@SpringBootApplication
),则需要采取措施以避免包含它(例如,你可以将其放在单独的非重叠包中,或指定要在
@ComponentScan
中显式扫描的包)。
下表显示了Spring Cloud Netflix默认为Ribbon提供的bean:
Bean类型 | 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();
}
}
前面示例中的语句将NoOpPing
替换为PingUrl
,并提供自定义serverListFilter
。
自定义所有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;
}
}
通过设置属性自定义Ribbon客户端
从版本1.2.0开始,Spring Cloud Netflix现在支持通过将属性设置为与Ribbon文档兼容来自定义Ribbon客户端。
这使你可以在不同环境中启动时更改行为。
以下列表显示了支持的属性:
-
<clientName>.ribbon.NFLoadBalancerClassName
:应该实现ILoadBalancer
-
<clientName>.ribbon.NFLoadBalancerRuleClassName
:应该实现IRule
-
<clientName>.ribbon.NFLoadBalancerPingClassName
:应该实现IPing
-
<clientName>.ribbon.NIWSServerListClassName
:应该实现ServerList
-
<clientName>.ribbon.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提供的实现,请参阅Ribbon文档。
使用Ribbon与Eureka
当Eureka与Ribbon一起使用时(即两者都在类路径上),ribbonServerList
被DiscoveryEnabledNIWSServerList
的扩展覆盖,该扩展填充Eureka的服务器列表,它还用NIWSDiscoveryPing
替换IPing
接口,它委托Eureka确定服务器是否启动,默认情况下安装的ServerList
是DomainExtractingServerList
,其目的是在不使用AWS AMI元数据的情况下使负载均衡器可以使用元数据(这是Netflix所依赖的)。默认情况下,服务器列表使用“zone”信息构建,如实例元数据中所提供的(因此,在远程客户端上,设置eureka.instance.metadataMap.zone
)。如果缺少该标志并且设置了approximateZoneFromHostname
标志,则可以使用服务器主机名中的域名作为区域的代理,区域信息可用后,可以在ServerListFilter
中使用。默认情况下,它用于在与客户端相同的区域中查找服务器,因为默认值为ZonePreferenceServerListFilter
,默认情况下,客户端区域的确定方式与远程实例相同(即通过eureka.instance.metadataMap.zone
)。
设置客户端区域的传统“archaius”方法是通过名为“@zone”的配置属性,如果可用,Spring Cloud优先于所有其他设置使用它(请注意,必须在YAML配置中引用该键)。
如果没有其他区域数据源,则根据客户端配置进行猜测(与实例配置相反),获取
eureka.client.availabilityZones
,它是从region名称到zone列表的映射,并为实例自己的region 拉出第一个zone(即
eureka.client.region
,默认为“us-east-1”,以便与原生Netflix兼容)。
示例:如何在没有Eureka的情况下使用Ribbon
Eureka是一种抽象远程服务器发现的便捷方式,因此你无需在客户端中对其URL进行硬编码,但是,如果你不想使用Eureka,Ribbon和Feign也可以使用。假设你已为“stores”声明了@RibbonClient
,并且未使用Eureka(甚至在类路径中也没有),Ribbon客户端默认为已配置的服务器列表,你可以按如下方式提供配置:
application.yml
stores:
ribbon:
listOfServers: example.com,google.com
示例:禁用在Ribbon中使用Eureka
将ribbon.eureka.enabled
属性设置为false
会显式禁用在Ribbon中使用Eureka,如以下示例所示:
application.yml
ribbon:
eureka:
enabled: false
直接使用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
}
}
Ribbon配置的缓存
每个Ribbon命名客户端都有一个Spring Cloud维护的相应子应用程序上下文,在对命名客户端的第一次请求上延迟加载此应用程序上下文,通过指定Ribbon客户端的名称,可以将此延迟加载行为更改为在启动时急切地加载这些子应用程序上下文,如以下示例所示:
application.yml
ribbon:
eager-load:
enabled: true
clients: client1, client2, client3
如何配置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
如何为Ribbon的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的预过滤器是最好的选择。你可以通过预过滤器中的RequestContext
访问HTTP headers和查询参数,因此可以用它来确定传递给Ribbon的LOAD_BALANCER_KEY
,如果未在RequestContext
中为LOAD_BALANCER_KEY
设置任何值,则将null
作为choose
方法的参数传递。