SpringCloud Ribbon 配置详解

前言

在前面的文章中我们分析了SpringCloud Ribbon的源码,了解了Ribbon的特点及一些重要接口。这篇文章我们来看下Ribbon在使用时的各种配置。

正文

Ribbon的自动化配置

由于Ribbon中定义的每个接口都有多种不同的策略实现,同时这些接口之间又有一定的依赖关系,某些开发者开始时很难上手,不知道如何选择具体实现策略等。

Spring Cloud Ribbon的自动化配置恰恰能够解决这种问题,当我们引入Spring Cloud Ribbon依赖之后,就能自动化构建下面这些接口的实现。

  • IClientConfig:Ribbon的客户端配置,默认采用com.netflix.client.config.DefaultClientConfigImpl实现。
  • IRule:Ribbon的负载均衡策略,默认采用com.netflix.loadbalancer.ZoneAvoidanceRule实现,该策略能够在多区域环境下选出最佳区域的实例进行访问。
  • IPing:Ribbon的实例检查策略,默认采用com.netflix.loadbalancer.NoOpPing实现,该策略不会检查实例是否可用,直接返回true,默认所有实例都是可用的。
  • ServerList<Server>:服务实例清单维护机制,默认采用com.netflix.loadbalancer.ConfigurationBasedServerList实现。
  • ServerListFilter<Server>:服务实例清单过滤机制,默认采用org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter实现,该策略能够优先过滤出与请求调用方处于同区域的服务实例。
  • ILoadBalancer:负载均衡器,默认采用com.netflix.loadbalancer.ZoneAwareLoadBalancer实现,它具备了区域感知的能力。

通过自动化配置的实现,我们可以轻松地实现客户端负载均衡。如果我们想实现一些个性化需求,也可以替换掉这些默认实现。

代码配置

如果我们想实现个性化配置,可以通过实现自定义的配置来完成,如下:

我们制定配置为CustomLoadBalancer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@EnableDiscoveryClient
@SpringBootApplication
@RibbonClient(name = "sakura",configuration = CustomLoadBalancer.class)
public class SakuraConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(SakuraConsumerApplication.class, args);
}

@Bean
@LoadBalanced
RestTemplate restTemplate(){
return new RestTemplate();
}
}

其实现如下:

使用了NIWSDiscoveryPingRandomRule和我们前面文章自写的ParallelPingStrategy策略。

1
2
3
4
5
6
7
@Configuration
public class CustomLoadBalancer {
@Bean
ILoadBalancer loadBalancer(){
return new BaseLoadBalancer(new NIWSDiscoveryPing(),new RandomRule(),new ParallelPingStrategy());
}
}

当然,如果我们不需这么多改动,只想改变负载均衡策略。则如下配置即可:

1
2
3
4
5
6
7
8
@Configuration
public class CustomerRule {

@Bean
public IRule rule(){
return new RandomRule();
}
}

其他也与之类似。

这种配置方式唯一一个缺点是对于集群部署,如果我们想让每个节点的策略不同,需要变更相关代码,当然一般情况下每个节点的策略应是一致的。

配置文件配置

相比代码配置,使用配置文件配置相关参数更加灵活。

我们在org.springframework.cloud.netflix.ribbon.PropertiesFactory这个类里可以看到如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class PropertiesFactory {

@Autowired
private Environment environment;

private Map<Class, String> classToProperty = new HashMap<>();

public PropertiesFactory() {
classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
classToProperty.put(ServerList.class, "NIWSServerListClassName");
classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
}

public boolean isSet(Class clazz, String name) {
return StringUtils.hasText(getClassName(clazz, name));
}

public String getClassName(Class clazz, String name) {
if (this.classToProperty.containsKey(clazz)) {
String classNameProperty = this.classToProperty.get(clazz);
String className = environment
.getProperty(name + "." + NAMESPACE + "." + classNameProperty);
return className;
}
return null;
}

@SuppressWarnings("unchecked")
public <C> C get(Class<C> clazz, IClientConfig config, String name) {
String className = getClassName(clazz, name);
if (StringUtils.hasText(className)) {
try {
Class<?> toInstantiate = Class.forName(className);
return (C) SpringClientFactory.instantiateWithConfig(toInstantiate,
config);
}
catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unknown class to load " + className
+ " for class " + clazz + " named " + name);
}
}
return null;
}

}

该类可以动态地为RibbonClient创建接口实现。使用时,我们只需要<clientName>.ribbon.<key>=<value>的形式进行配置即可。

比如上面我们的代码配置,则可以用配置文件进行如下配置:

1
2
3
sakura.ribbon.NFLoadBalancerPingClassName = com.netflix.niws.loadbalancer.NIWSDiscoveryPing
sakura.ribbon.NFLoadBalancerRuleClassName = com.netflix.loadbalancer.RandomRule
sakura.ribbon.NFLoadBalancerClassName = com.zwt.sakuraconsumer.loadbalancer.CustomLoadBalancer

关于参数配置

对于Ribbon的参数配置通常有两种方式:全局配置及指定客户端配置。

  • 全局配置:全局配置很简单,类如如下形式即可,ribbon.<key>=<value>格式配置即可。

    比如,我们全局配置Ribbon创建连接的超时时间。

    1
    ribbon.ConnectTimeout=300

    全局配置可以作为默认值使用,当指定客户端配置了相应的key的值时,将覆盖全局配置的内容。

  • 指定客户端配置:采用<client>.ribbon.<key>=<value>的格式进行配置。其中的<client>代表客户端名称,我们可以通过在@RibbonClient进行指定。

    比如,我们可以为客户端指定具体的实例清单,如下:

    1
    sakura.ribbon.listOfServers=localhost:8001,localhost:8002

com.netflix.client.config.CommonClientConfigKey类中,我们可以找到Ribbon更为详细的配置参数内容。

我把参数详细信息整理如下,大家可以参考下:

配置项类型默认值说明
AppNameString应用名称
VersionString应用版本号
PortInteger7001端口号
SecurePortInteger443安全端口号
VipAddressString虚拟IP地址
ForceClientPortConfigurationBooleanfalse是否强制使用客户端口号配置
DeploymentContextBasedVipAddressesString
MaxAutoRetriesInteger0当前实例连接最大重试次数
MaxAutoRetriesNextServerInteger1实例连接最大重试次数,超过会换其他实例重试
OkToRetryOnAllOperationsBooleanfalse是否对所有请求操作进行重试
RequestSpecificRetryOnBooleanfalse是否对特殊请求进行重试
ReceiveBufferSizeInteger接收到的数据流长度限制
EnablePrimeConnectionsBooleanfalse启用预连接 (关于预连接请参考下文)
PrimeConnectionsClassNameStringcom.netflix.niws.client.http.HttpPrimeConnection预连接类名
MaxRetriesPerServerPrimeConnectionInteger9每个server的预连接最大重试次数
MaxTotalTimeToPrimeConnectionsInteger30000预连接超时时间
MinPrimeConnectionsRatioFloat1.0f预连接最小时间间隔
PrimeConnectionsURIString/预连接URI
PoolMaxThreadsInteger200连接池内最大可用线程数
PoolMinThreadsInteger1连接池内最小可用线程数
PoolKeepAliveTimeInteger15 * 60L连接在池内存活时间,默认15min
PoolKeepAliveTimeUnitsStringSECONDS连接在池内存活时间单位
EnableConnectionPoolBooleantrue是否启用连接池
MaxHttpConnectionsPerHostInteger50已过时,详见MaxConnectionsPerHost
MaxTotalHttpConnectionsInteger200已过时,详见MaxTotalConnections
MaxConnectionsPerHostInteger50每个主机的最大连接数
MaxTotalConnectionsInteger200最大连接总数
IsSecureBooleanfalse是否安全连接
GZipPayloadBooleantrue是否启用GZip传输
ConnectTimeoutInteger2000请求连接超时时间
BackoffTimeoutInteger
ReadTimeoutInteger5000请求处理超时时间
SendBufferSizeInteger发送的数据最大长度
StaleCheckingEnabledBoolean
LingerInteger
ConnectionManagerTimeoutInteger2000连接管理器超时时间
FollowRedirectsBooleanfalse连接是否自动处理重定向
ConnectionPoolCleanerTaskEnabledBooleantrue是否启用连接池自动清理任务
ConnIdleEvictTimeMilliSecondsInteger30000空闲连接存活时间
ConnectionCleanerRepeatIntervalInteger30000连接清理时间间隔
EnableGZIPContentEncodingFilterBooleanfalse是否启用GZIP编码过滤
ProxyHostString服务代理地址
ProxyPortIntegerInteger.MIN_VALUE + 1代理服务端口号,默认值没有实际用途,如果使用需要用户自行设置
KeyStoreString
KeyStorePasswordString
TrustStoreString
TrustStorePasswordString
IsClientAuthRequiredBooleanfalse是否需要客户端安全认证(如果请求需要的话)
CustomSSLSocketFactoryClassNameString用户自定义的SSL连接类
IsHostnameValidationRequiredBoolean是否需要校验
IgnoreUserTokenInConnectionPoolForSecureClientBoolean
ClientClassNameStringcom.netflix.niws.client.http.RestClient请求客户端的实现类
InitializeNFLoadBalancerBooleantrue是否初始化Ribbon负载均衡器
NFLoadBalancerClassNameStringcom.netflix.loadbalancer.ZoneAwareLoadBalancer使用的负载均衡器类名
NFLoadBalancerRuleClassNameStringcom.netflix.loadbalancer.AvailabilityFilteringRule负载均衡器的过滤规则
NFLoadBalancerPingClassNameStringcom.netflix.loadbalancer.DummyPing负载均衡器的ping规则
NFLoadBalancerPingIntervalInteger30ping间隔时间
NFLoadBalancerMaxTotalPingTimeInteger2ping的最大次数
NFLoadBalancerStatsClassNameStringcom.netflix.loadbalancer.LoadBalancerStats负载均衡状态统计类
NIWSServerListClassNameStringcom.netflix.loadbalancer.ConfigurationBasedServerList获取服务列表所使用的类
ServerListUpdaterClassNameStringcom.netflix.loadbalancer.PollingServerListUpdater服务列表更新所使用的类
NIWSServerListFilterClassNameStringcom.netflix.loadbalancer.ZoneAffinityServerListFilter区域甄别服务列表过滤类名
ServerListRefreshIntervalInteger30 * 1000 ms服务列表刷新间隔,单位毫秒
EnableMarkingServerDownOnReachingFailureLimitBoolean
ServerDownFailureLimitInteger
ServerDownStatWindowInMillisInteger
EnableZoneAffinityBooleanfalse是否开启区域甄别
EnableZoneExclusivityBooleanfalse是否开启ZoneAffinity
PrioritizeVipAddressBasedServersBooleantrue
VipAddressResolverClassNameStringcom.netflix.client.SimpleVipAddressResolver
TargetRegionString
RulePredicateClassesString
RequestIdHeaderNameString
UseIPAddrForServerBooleanfalse是否使用IP地址请求
listOfServersString“”为客户端指定具体的实例清单

预连接

对于那些拥有客户端负载均衡,并且知道要链接的服务器集群的客户端,我们可以预先与服务端建立连接,进行“预热”,这样做的好处是对于一些有防火墙的服务应用,请求时可以快速与之建立连接,提高应用体验。

详细信息可以查看 com.netflix.niws.client.http.HttpPrimeConnection 预连接处理类,这儿就不过多介绍了。

其他

关于上面的参数,很多是关于对Ribbon使用的HttpClient的配置,不太了解的可以先了解下HttpClient的一些参数配置等。

参数都遵循<client>.ribbon.<key>=<value>的配置。

比如我们以Ribbon的重试机制来进行举例,Spring Cloud整合了 Spring Retry来增强RestTemplate的重试能力,对于我们只需简单的配置,便可以实现重试功能。

我们在配置文件中添加如下配置信息,其配置信息含义已经说明。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 该参数用来开启重试机制
spring.cloud.loadbalancer.retry.enabled=true

//断路器超时时间(断路器超时时间需要大于Ribbon超时时间,不然不会触发重试,有关断路器的内容,我们后面在了解)
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000

//请求连接的超时时间
sakura.ribbon.ConnectTimeout = 250

//请求处理的超时时间
sakura.ribbon.ReadTimeout= 1000

//对所有请求操作进行重试
sakura.ribbon.OkToRetryOnAllOperations = true

//切换实例的重试次数
sakura.ribbon.MaxAutoRetriesNextServer = 2

//对当前实例的重试次数
sakura.ribbon.MaxAutoRetries = 1

根据如上配置,当访问到故障请求时,Ribbon会再尝试访问一次当前实例(访问次数取决于MaxAutoRetries),如果不行,就换一个实例进行访问,如果还是不行,再换一次实例访问(更换次数取决于MaxAutoRetriesNextServer),如果依然不行,返回失败。

与Eureka结合

当我们在 Spring Cloud 的应用中同时引入 Spring Cloud Ribbon 和 Spring Cloud Eureka 依赖时,会触发 Eureka 中实现的对 Ribbon 的自动化配置。这时ServerList的维护机制实现将被com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList的实例所覆盖,该实现会将服务列表交给 Eureka 的服务治理机制来进行维护;IPing的实现将被com.netflix.niws.loadbalancer.NIWSDiscoveryPing的实例所覆盖,该实例也将实例检查的任务交给了服务治理框架来进行维护。默认情况下,用于获取实例请求的ServerList接口实现将采用 Spring Cloud Eureka 中封装的 org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList,其目的是为了让实例维护策略更加通用。

由于 Eureka 会为我们维护所有服务实例的清单,因此结合使用的时候,我们无需再配置类似sakura.ribbon.listOfServers的参数来指定服务实例清单。

此外,由于 Spring Cloud Ribbon 默认实现了区域亲和策略,所以我们可以通过 Eureka 实例的元数据配置来实现区域化的实例配置方案。比如,可以将处于不同机房的实例配置成不同的区域值,以作为跨区域的容错机制实现。而实现方式非常简单,只需在服务实例的元数据中增加 zone参数来指定自己所在的区域,比如:

1
eureka.instance.metadataMap.zone=beijing

在 Spring Cloud Ribbon 和 Spring Cloud Eureka 结合的工程中,我们也可以通过参数配置来禁用 Eureka 对 Ribbon 服务实例的维护实现。只需在配置文件加入如下参数:

1
ribbon.eureka.enabled=false

禁用后,我们服务实例的维护需要手动指定listOfServers等参数。

总结

以上就是关于 Spring Cloud Ribbon 的一些配置及介绍,实际应用中,我们大多数与 Eureka 结合使用,很多遵循默认配置,真正的开箱即用,但我们也应对它们的配置及原理有所了解,方便我们更好的使用及解决可能出现的一系列问题。




-------------文章结束啦 ~\(≧▽≦)/~ 感谢您的阅读-------------

您的支持就是我创作的动力!

欢迎关注我的其它发布渠道