参考: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客户端
可以在外部属性配置中使用 **.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:
创建其中一种类型的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 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文档兼容。
这允许在不同环境中启动时更改行为。
支持的属性如下所示,并应以**.ribbon.**为前缀:
注:这些在属性中定义的类优先于使用@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。
注:
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方法的参数传递。