Spring Cloud Ribbon

× 文章目录
  1. 1. 客户端负载均衡Ribbon
    1. 1.1. 怎么引入Ribbon
    2. 1.2. 定制化Ribbon Client
    3. 1.3. 一起使用Ribbon和Eureka
    4. 1.4. Example: 没有Eureka,如何使用Ribbon
  2. 2. Example: 在Ribbon停止Eureka可用

客户端负载均衡Ribbon

Ribbon是一个客户端负载均衡器,能够给HTTP和TCP客户端带来灵活的控制。 Feign已经使用了Ribbon,因此如果你正在使用@FeignClient,那么本部分的说明同样试用。

Ribbon的核心概念是命名的客户端。每一个负载均衡器都是一系列工作在一起的组件的一部分,并用于按需联系远程服务。你可以给这一系列一个名字(例如使用@FeignClient注解)。Spring Cloud使用RibbonClientConfiguration为每一个命名的客户端建立一个新系列为满足ApplicationContext的需求。 这包括一个ILoadBalancer, 一个RestClient和一个ServerListFilter。

怎么引入Ribbon

为了引入Ribbon到你的工程,可以使用org.springframework.cloud组的starter.artifact id是spring-cloud-starter-ribbon。可以参照Spring Cloud Project的细节来设置你的构建系统。

定制化Ribbon Client

你可以使用外部的属性<client>.ribbon.*来配置一些Ribbon Client,除了能够使用Sprint Boot的配置文件以外,它和使用原生的Netflix APIS没什么不同。原生选项可以在CommonClientConfigKey中被检查到。

通过使用@RibbonClient声明额外的配置(在RibbonClientConfiguration上面),Spring Cloud也让你全面的控制Client。 例如:

1
2
3
4
5
@Configuration
@RibbonClient(name = "foo", configuration = FooConfiguration.class)
public class TestConfiguration {
}

这个例子中,Client除了包括了已经存在于RibbonClientConfiguration的组件,也包括了自定义的FooConfiguration(一般后者会重载前者)。

警告:FooConfiguration 不能被@ComponentScan 在main application context。这样的话,它将被所有@RibbonClients共享。如果你使用 @ComponentScan (or @SpringBootApplication) ,你需要避免它被包括其中。(例如:放它到一个独立的,无重叠的包里,或者指明不被@ComponentScan扫描)。

Spring Cloud Netflix为ribbon提供了如下的Beans(BeanType beanName: ClassName):

  • IClientConfig ribbonClientConfig: DefaultClientConfigImpl
  • IRule ribbonRule: ZoneAvoidanceRule
  • IPing ribbonPing: NoOpPing
  • ServerList ribbonServerList: ConfigurationBasedServerList
  • ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
  • ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer

建立一个如上类型的bean,放置到@RibbonClient配置(如上FooConfiguration),允许你重载其中的任意一个。例如:

1
2
3
4
5
6
7
@Configuration
public class FooConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
}

如上将用PingUrl代替NoOpPing。

##使用properties定制化Ribbon Client
从1.2.0开始,Spring Cloud Netflix支持使用properties来定制化Ribbon clients。
这就允许你在不同的环境,在启动时改变举止。

支持的属性如下(加上.ribbon.的前缀):

  • NFLoadBalancerClassName: 实现ILoadBalancer
  • NFLoadBalancerRuleClassName: 实现IRule
  • NFLoadBalancerPingClassName: 实现IPing
  • NIWSServerListClassName: 实现ServerList
  • NIWSServerListFilterClassName: 实现ServerListFilter

    在这些属性中定义的Classes优先于在@RibbonClient(configuration=MyRibbonConfig.class)定义的beans和由Spring Cloud Netflix提供的缺省值。

例如,为一个叫user的服务设置IRule,可以进行如下设置:

users:
  ribbon:
    NFLoadBalancerRuleClassName:     com.netflix.loadbalancer.WeightedResponseTimeRule

一起使用Ribbon和Eureka

当Eureka被和Ribbon一起联合使用时, ribbonServerList被一个叫做DiscoveryEnabledNIWSServerList的扩展重载。这个扩展汇聚了来自于Eureka的服务列表。
IPing的接口也被NIWSDiscoveryPing代替,用于委托Eureka来测定服务是否UP。 缺省的ServerListThe是一个DomainExtractingServerList,目的是使得物理上的元数据可以用于负载均衡器,而不必要使用AWA AMI的元数据。 缺省情况下,服务List将根据“zone”信息被创建。(远程客户端设置eureka.instance.metadataMap.zone)。如果这个没有设置,它可以使用d来自于服务hostname的domain name作为zone的代理(取决于approximateZoneFromHostname是否被设置)。一旦那个zone的信息可获得,他可以在ServerListFilter被使用。缺省情况下,由于使用ZonePreferenceServerListFilter,它被用于定位一个和Client相同zone的server。 默认情况下,client的zone是和远程实例一样的,通过eureka.instance.metadataMap.zone来设置。

注意:正统的”archaius”方式是通过配置属性”@zone”来设置Clinet zone。如果它是有效的,Spring Cloud使用这个优先于其他设置。(注意,这个key必须在YAML configuration被引)。

如果zone数据没有其他来源,基于Client的配置将会有一个猜测。(as opposed to the instance configuration).我们称为eureka.client.availabilityZones。它是一个从region名到zones列表的map,并且抽出第一个zone 作为实例自己的region(例如the eureka.client.region)

Example: 没有Eureka,如何使用Ribbon

Eureka是一个实用的方式,它抽象了远程服务的发现机制,可以使你不用在Clinet中硬编码URLS。但如果你不打算使用它,Ribbon和Feign仍然是相当可用的。假设你已经声明了一个@RibbonClient为一个“Stores”的服务。Eureka没有被使用。Ribbon Client会缺省到一个已配置的Server列表。你可以提供配置像:

1
2
3
stores:
ribbon:
listOfServers: example.com,google.com

Example: 在Ribbon停止Eureka可用

设置ribbon.eureka.enabled = false,可以在Ribbon中停止对Eureka的使用

ribbon:
  eureka:
   enabled: false

##直接使用Ribbon API
可以直接使用LoadBalancerClient。例如:

1
2
3
4
5
6
7
8
9
10
11
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
}
}
如果您觉得文章不错,可以打赏我喝一杯咖啡!