Ribbon

1.简介

Ribbon是一个客户端IPC库,在云中经过实战测试。它提供以下功能

  • 负载均衡
  • 容错
  • 异步和反应模型中的多协议(HTTP,TCP,UDP)支持
  • 缓存和批处理
1
2
3
4
5
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version>2.2.2</version>
</dependency>

ribbon:在其他功能区模块和Hystrix之上集成负载平衡,容错,缓存/批处理的API
ribbon-loadbalancer:负载均衡器API,可以单独使用,也可以与其他模块一起使用
ribbon-eureka:使用Eureka客户端为云提供动态服务器列表的API
ribbon-transport:使用具有负载平衡功能的RxNetty传输支持HTTP,TCP和UDP协议的客户端
ribbon-httpclient:构建在与负载均衡器集成的Apache HttpClient之上的REST客户端(不建议使用并由功能块模块替换)
ribbon-example:示例
ribbon-core:客户端配置API和其他共享API

2.入门

最简单的入门方法是使用属性驱动的工厂来创建具有负载均衡器的客户端实例,在如下所示的ribbon-httpclient 示例应用程序中基础用法,描述如下

2.1 属性文件(sample-client.properties)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 同一服务器上的最大重试次数(不包括第一次尝试)
sample-client.ribbon.MaxAutoRetries=1

# 要重试的下一个服务器的最大数量(不包括第一个服务器)
sample-client.ribbon.MaxAutoRetriesNextServer=1

# 是否可为改机器重试所有操作
sample-client.ribbon.OkToRetryOnAllOperations=true

# 刷新服务器列表时间间隔
sample-client.ribbon.ServerListRefreshInterval=2000

# Apache HttpClient 连接超时时间
sample-client.ribbon.ConnectTimeout=3000

# Apache HttpClient 读超时时间
sample-client.ribbon.ReadTimeout=3000

# 初始服务器列表,可以通过Archaius属性在运行期间动态更新
sample-client.ribbon.listOfServers=www.microsoft.com:80,www.yahoo.com:80,www.google.com:80

配置格式是:

1
<clientName>.<nameSpace>.<propertyName>=<value>

后面将在工厂中使用clientName创建客户端,nameSpace是可选的,默认情况下为ribbon。CommonClientConfigKey中提供了公共属性名称。

2.2 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws Exception {
ConfigurationManager.loadPropertiesFromResources("sample-client.properties"); // 1
System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
RestClient client = (RestClient) ClientFactory.getNamedClient("sample-client"); // 2
HttpClientRequest request = HttpClientRequest.newBuilder().setUri(new URI("/")).build(); // 3
for (int i = 0; i < 20; i++) {
HttpClientResponse response = client.executeWithLoadBalancer(request); // 4
System.out.println("Status code for " + response.getRequestedURI() + " :" + response.getStatus());
}
ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer) client.getLoadBalancer();
System.out.println(lb.getLoadBalancerStats());
ConfigurationManager.getConfigInstance().setProperty(
"sample-client.ribbon.listOfServers", "www.linkedin.com:80,www.google.com:80"); // 5
System.out.println("changing servers ...");
Thread.sleep(3000); // 6
for (int i = 0; i < 20; i++) {
HttpClientResponse response = client.executeWithLoadBalancer(request);
System.out.println("Status code for " + response.getRequestedURI() + " : " + response.getStatus());
response.releaseResources();
}
System.out.println(lb.getLoadBalancerStats()); // 7
}

笔记:

  • 1.使用Archaius ConfigurationManager加载属性文件。
  • 2.使用ClientFactory创建负载均衡器客户端。
  • 3.使用构建器构建http请求。请注意,我们只需提供URI的路径部分(“/”)。一旦负载均衡器选择了服务器,客户端就会计算完整的URI。
  • 4.调用client.executeWithLoadBalancer()API,而不是execute()API。
  • 5.从配置中动态更改服务器池。
  • 6.等到服务器列表刷新(属性文件中定义的2秒参考间隔)
  • 6.打印出负载均衡器记录的服务器统计信息。

负载均衡器统计信息包含大量用于监视的信息,并用作负载均衡器算法的输入。以下是输出中的示例:

1
2
3
4
5
6
Zone stats: {unknown=[Zone:unknown;	Instance count:2;	Active connections count: 0;	Circuit breaker tripped count: 0;	Active connections per server: 0.0;]
},Server stats: [[Server:www.microsoft.com:80; Zone:UNKNOWN; Total Requests:6; Successive connection failure:0; Total blackout seconds:0; Last connection made:Fri Jan 25 14:52:18 PST 2013; First connection made: Fri Jan 25 14:52:15 PST 2013; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:129.83333333333334; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:33.0; max resp time:530.0; stddev resp time:179.30180949697325]
, [Server:www.google.com:80; Zone:UNKNOWN; Total Requests:17; Successive connection failure:0; Total blackout seconds:0; Last connection made:Fri Jan 25 14:52:23 PST 2013; First connection made: Fri Jan 25 14:52:14 PST 2013; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:67.6470588235294; 90 percentile resp time:50.0; 95 percentile resp time:50.0; min resp time:45.0; max resp time:384.0; stddev resp time:79.12234842799629]
, [Server:www.yahoo.com:80; Zone:UNKNOWN; Total Requests:7; Successive connection failure:0; Total blackout seconds:0; Last connection made:Fri Jan 25 14:52:18 PST 2013; First connection made: Fri Jan 25 14:52:13 PST 2013; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:561.1428571428571; 90 percentile resp time:0.0; 95 percentile resp time:0.0; min resp time:445.0; max resp time:879.0; stddev resp time:132.42618274930024]
, [Server:www.linkedin.com:80; Zone:UNKNOWN; Total Requests:10; Successive connection failure:0; Total blackout seconds:0; Last connection made:Fri Jan 25 14:52:23 PST 2013; First connection made: Fri Jan 25 14:52:22 PST 2013; Active Connections:0; total failure count in last (1000) msecs:0; average resp time:94.6; 90 percentile resp time:404.0; 95 percentile resp time:404.0; min resp time:53.0; max resp time:404.0; stddev resp time:103.25037530198135]
]

3.开发指南

3.1 客户端配置选项

配置客户端和负载均衡器的最简单方法是将属性加载到符合特定格式的Archaius中:

1
<clientName>.<nameSpace>.<propertyName>=<value>

您可以在类路径上的文件中定义属性,也可以将其定义为系统属性。如果是使用前者,则应调用ConfigurationManager.loadPropertiesFromResources()API来加载文件。

默认情况下,“ribbon”应为nameSpace。

如果没有为命名客户端指定属性,ClientFactory将使用默认配置属性值创建负载均衡器客户端,默认值在DefaultClientConfigImpl中指定。

如果某个属性缺少clientName,则会将其解释为适用于所有客户端的属性。

例如

1
ribbon.ReadTimeout=1000

这将为所有客户端建立默认的ReadTimeout属性。

您还可以通过构造DefaultClientConfigImpl的实例以编程方式设置属性。
通过如下步骤:

  • 1.调用DefaultClientConfigImpl.getClientConfigWithDefaultValues(String clientName)以加载默认值,以及已在Archaius中使用Configuration定义的任何属性
  • 2.通过调用DefaultClientConfigImpl.setProperty()API设置所有所需的属性。
  • 3.将此实例与客户端名称一起传递给正确的ClientFactory API。

如果希望在不同的名称空间中定义属性,例如“foo”

1
myclient.foo.ReadTimeout=1000

您应该使用getClientConfigWithDefaultValues(String clientName,String nameSpace)构造函数 - 在本例中为getClientConfigWithDefaultValues(“myclient”,“foo”)- 在上面的第一步中。

3.2 .在ClientFactory中使用非默认属性名称空间

如果要使用“ribbon”以外的属性名称空间,并使用ClientFactory API创建客户端或负载均衡器,有以下几种方法:

3.2.1 方法1

扩展DefaultClientConfigImpl并重写方法getNameSpace(),例如,

1
2
3
4
5
6
public class MyClientConfig extends DefaultClientConfigImpl {
// ...
public String getNameSpace() {
return "foo";
}
}

假设您的所有属性都定义为myclient.foo。*,您可以使用以下ClientFactory API来创建客户端:

1
MyClient client = (MyClient) ClientFactory.createNamedClient("myclient", MyClientConfig.class);

3.2.2 方法2

使用DefaultClientConfigImpl将默认名称空间更新为自定义命名空间,例如,我们的客户端名为“myclient”,其属性在名称空间“foo”中定义:

1
2
3
DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl("foo");
clientConfig.loadProperites("myclient");
MyClient client = (MyClient) ClientFactory.registerClientFromProperties("myclient", clientConfig);

3.3.使用负载均衡器支持实现您自己的客户端

您需要扩展com.netflix.client.AbstractLoadBalancerAwareClient并实现一些方法。具体而言,应实现IClient.execute()方法以执行协议特定的操作。

AbstractLoadBalancerAwareClient负责负载均衡器集成,重试逻辑和统计信息收集,这些统计信息用作负载均衡器算法和监控的输入。

客户端应用程序需要为其配置定义此属性

1
<clientName>.<nameSpace>.ClientClassName=<Your implementation class name>

然后,客户端应用程序可以使用ClientFactory中的适当API获取客户端的实例

3.4 与Eureka整合

Eureka提供服务发现功能,可将ribbon于eureka集成以提供动态服务器列表。要使用Eureka提供的服务列表,请按照下列步骤操作:

  • 1.在您的依赖项中添加ribbon-eureka模块
  • 2.(默认情况下已完成)为客户端启用负载均衡器,并将负载均衡器配置为DynamicServerListLoadBalancer或其子类。默认情况下这是已启用。
  • 3.将ServerList配置为com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList
  • 4.配置服务器刷新率。(可选,默认为30秒)
  • 5.为客户端配置服务器的虚拟地址(“Vip Address”),并确保它与服务器的“Vip Address”匹配,用于向Eureka服务器注册。

demo 如下

1
2
3
4
5
6
7
myclient.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList

# refresh every minute
myclient.ribbon.ServerListRefreshInterval=60000

# movieservice 是一个注册在Eureka server注册中心的服务虚拟ip,
myclient.ribbon.DeploymentContextBasedVipAddresses=movieservice

此外,您需要为Eureka客户端提供适当的配置文件。看到这里

4.使用balancers

ribbon 提供软件与集群服务器的负载均衡通信,负载均衡提供如下基本功能

  • 1.向通信客户端提供单个服务器的公共DNS名称或IP
  • 2.根据具体的逻辑运行在服务器列表中

有些负载均衡器还提供了一下高级功能如下:

  • 1.通过将客户端和服务器划分为多个区域(如数据中心中的机架)来建立客户端和服务器之间的关联,并支持同一区域中的服务器以减少延迟
  • 2.保持服务器统计信息,避免服务器出现高延迟或频繁出现故障
  • 3.保持区域统计并避免可能中断的区域

4.1.负载均衡器组件

  • rule - 用于确定从列表返回哪个服务器的逻辑组件
  • Ping - 在后台运行的组件,用于确保服务器的活跃性
  • ServerList - 服务列表,可以是静态的或动态的,如果是使用动态的(使用DynamicServerListLoadBalancer),后台线程将刷新并以特定间隔过滤列表

这些组件可以通过编程方式设置,也可以通过客户端的一些配置属性,并通过反射创建。这些是有相关属性名称(.. 在属性文件中添加前缀):

1
2
3
4
5
NFLoadBalancerClassName
NFLoadBalancerRuleClassName
NFLoadBalancerPingClassName
NIWSServerListClassName
NIWSServerListFilterClassName

通过Archaius更改属性,这些组件的行为通常可以在运行时更改

4.2. common rule

4.2.1 RoundRobinRule

此规则只是通过循环选择服务器。它通常用作默认规则或更高级规则的备选。

4.2.2 AvailabilityFilteringRule

此规则将跳过被视为“电路跳闸”或具有高并发连接数的服务器。

默认情况下,如果RestClient最近三次无法与其建立连接,则实例会跳闸。一旦实例电路跳闸,它将在电路被认为再次关闭之前保持这种状态30秒。然而,如果它继续连接失败,它将再次“电路跳闸”并且它变为“电路闭合”的等待时间将指数地增加到连续故障的数量。

可以通过Archaius 设置以下属性ConfigurationManager:

1
2
3
4
5
6
7
8
# 连续连接失败阈值使服务器处于电路跳闸状态,默认为3
niws.loadbalancer.<clientName>.connectionFailureCountThreshold

# 实例不可用状态的最大周期,默认:30
niws.loadbalancer.<clientName>.circuitTripMaxTimeoutSeconds

# 并发连接的阈值计数跳过服务器,默认为Integer.MAX_INT
<clientName>.<clientConfigNameSpace>.ActiveConnectionsLimit

4.2.3 WeightedResponseTimeRule

对于此规则,每个服务器根据其平均响应时间给予权重。响应时间越长,重量就越小。该规则随机选择服务器,其中可能性由服务器的权重决定。

要启用WeightedResponseTimeRule,请通过API使用负载均衡器进行设置,或者设置以下属性

1
<clientName>.<clientConfigNameSpace>.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.WeightedResponseTimeRule

4.3.ServerList

4.3.1 Adhoc 静态服务列表

您始终可以使用BaseLoadBalancer或其子类与API一起设置静态服务器列表 BaseLoadBalancer.setServersList()

4.3.2 ConfigurationBasedServerList

这是负载均衡器的默认ServerList实现。
您可以使用Archaius ConfigurationManager将服务器列表设置为属性。例如

1
sample-client.ribbon.listOfServers=www.microsoft.com:80,www.yahoo.com:80,www.google.com:80

如果动态更改属性,则负载均衡器的服务器列表也将更改。

4.3.3 DiscoveryEnabledNIWSServerList

此ServerList实现从Eureka客户端获取服务器列表。必须通过属性中的VipAddress标识服务器群集。
例如

1
2
3
myClient.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList 
# the server must register itself with Eureka server with VipAddress "myservice"
myClient.ribbon.DeploymentContextBasedVipAddresses=myservice

4.4 ServerListFilter

ServerListFilter是用于DynamicServerListLoadBalancer过滤从ServerList实现返回的服务器的组件。功能区中有两个ServerListFilter实现:

4.4.1 ZoneAffinityServerListFilter

筛选出与客户端不在同一区域中的服务器,除非客户端区域中没有可用的服务器。可以通过指定以下属性来启用此过滤器(假设客户端名称为“myclient”,客户端属性名称空间为“ribbon”):

1
myclient.ribbon.EnableZoneAffinity=true

4.4.2 ServerListSubsetFilter

此筛选器确保客户端仅看到ServerList实现返回的整个服务器的固定子集。它还可以定期用新服务器替换可用性较差的子集中的服务器。要启用此过滤器,请指定以下属性

1
2
3
4
5
6
myClient.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList 
# the server must register itself with Eureka server with VipAddress "myservice"
myClient.ribbon.DeploymentContextBasedVipAddresses=myservice
myClient.ribbon.NIWSServerListFilterClassName=com.netflix.loadbalancer.ServerListSubsetFilter
# only show client 5 servers. default is 20.
myClient.ribbon.ServerListSubsetFilter.size=5

4.5 java doc