虽然ribbon默认为我们提供了多钟负载均衡策略,但有时候我们仍然需要自定义符合自身业务逻辑的规则
使用配置文件的方式:我们只需要在配置文件中添加配置
serviceId.ribbon.NFLoadBalancerRuleClassName=自定义的负载均衡策略类
其中 serviceId 为具体服务名
这样在调用对应服务时候,就会使用我们自定义的负载策略,很方便
对于该配置文件springcloud是如何解析的呢,接下来我们就分析该配置为何生效
引入关键类 RibbonClientConfiguration
1
2
3
4
5
6
7
8
9
10
|
@Bean
@ConditionalOnMissingBean
public
IRule ribbonRule(IClientConfig config) {
if
(
this
.propertiesFactory.isSet(IRule.
class
, name)) {
return
this
.propertiesFactory.get(IRule.
class
, config, name);
}
ZoneAvoidanceRule rule =
new
ZoneAvoidanceRule();
rule.initWithNiwsConfig(config);
return
rule;
}
|
第一行:判断当前环境是否设置了IRule类
1
2
3
|
public
boolean
isSet(Class clazz, String name) {
return
StringUtils.hasText(getClassName(clazz, name));
}
|
getClassName 具体实现如下:
1
2
3
4
5
6
7
8
|
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
;
}
|
而classToProperty是啥呢
1
2
3
4
5
6
7
|
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"
);
}
|
可以看到 其中有IRule.class对应 NFLoadBalancerRuleClassName
回头看getClassName类
1
|
String className = environment.getProperty(name +
"."
+ NAMESPACE +
"."
+ classNameProperty);
|
其中 name 为ribbon.client.name 也就是我们服务名,
NAMESPACE 为 ribbon
classNameProperty 为 NFLoadBalancerRuleClassName
所以通过getClassName 方法 最终返回的是 我们系统中设置的 serviceId.ribbon.NFLoadBalancerRuleClassName 属性值
接着看ribbonRule 第二行代码
1
|
<strong>
return
this
</strong>.propertiesFactory.get(IRule.<strong>
class
</strong>, config, name);
|
具体get实现:
1
2
3
4
5
6
7
8
9
10
11
12
|
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) instantiateWithConfig(toInstantiate, config);
}
catch
(ClassNotFoundException e) {
throw
new
IllegalArgumentException(
"Unknown class to load "
+className+
" for class "
+ clazz +
" named "
+ name);
}
}
return
null
;
}</c></c>
|
这样就很清晰了,最终会根据我们配置的负载策略类全路径 生成对应的实例
而我们如果一个服务需要依赖调用N多服务的时候 采用这样的配置方式,显得有点繁琐,我们的负载配置不能全局化
怎么处理呢?
当然我们可以拓展org.springframework.cloud.netflix.ribbon.PropertiesFactory类 使其支持全局配置,但springcloud官方不推荐这样处理
结合我们目前项目的处理方式,这里我给出另一条思路
在配置文件中,我们添加一个属性
loadbalanced.services = service-A,service-B
另外添加一个RibbonLoadBalancerRuleConfiguration类
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
|
@Configuration
@ConditionalOnClass
(com.netflix.loadbalancer.ZoneAvoidanceRule.
class
)
public
class
RibbonLoadbalancerRuleConfiguration
implements
InitializingBean {
private
final
static
Logger log = LoggerFactory.getLogger(RibbonLoadbalancerRuleConfiguration.
class
);
@Value
(
"#{'${loadbalanced.services}'.split(',')}"
)
private
List<string> loadbalancedServices;
/**
* 默认使用切流量的负载均衡策略
*/
@Value
(
"${ribbon.NFLoadBalancerRuleClassName}"
)
private
String ribbonLoadBancerRule;
@Override
public
void
afterPropertiesSet()
throws
Exception {
if
(
null
!= loadbalancedServices)) {
for
(String service : loadbalancedServices)) {
String key = service +
".ribbon.NFLoadBalancerRuleClassName"
;
System.setProperty(key, ribbonLoadBancerRule);
}
}
}
}</string>
|
这样在配置文件中我们只需要配置
1
2
|
ribbon.NFLoadBalancerRuleClassName=自定义负载均衡策略
loadBalancedService=需要使用自定义负载均衡策略的服务
|
有时间会整理一篇更完整的ribbon负载均衡原理分析,敬请期待