1. LB的基本功能要求
想想一个LB是怎么工作的,不管是硬件的还是软件的,我们要求它有哪些能力,提个最精简的需求清单出来:好像能想到的也就两个吧:
- ?设置添加和读取后端的服务(器)列表
- 能从中选择一个服务(器)
Netflix的LoadBalancer也是这样被要求的。通过ILoadBalancer来要求的。
可以看到主要有:对应配置后端服务;读取后端服务;标记一个服务不可用;当然最主要的是选择一个后端服务来提供服务。
这是2.1.0版本的方法列表,2.1.3开始版本也就是把getServerList (boolean availableOnly)分成了两个getReachableServers和getAllServers(解释是in favor of the cleaner),一个分成俩,clean了吗,好像一点点。一个是得到所有的后端server,一个是只返回可用的后端server。
2 主要实现
观察ILoadBalancer最基础的实现类com.netflix.loadbalancer.BaseLoadBalancer可以了解主要行为。LB的主要实现都在里面,DynamicServerListLoadBalancer和ZoneAwareLoadBalancer不过是扩展了他的功能而已。
虽然从ribbon默认提供的是一种ZoneAwareLoadBalancer的LB.
1
2
3
4
5
6
7
8
9
10
11
|
@Bean
@ConditionalOnMissingBean
public
ILoadBalancer
ribbonLoadBalancer
(
IClientConfig
config
,
ServerList
&
lt
;
Server
&
gt
;
serverList
,
ServerListFilter
&
lt
;
Server
&
gt
;
serverListFilter
,
IRule
rule
,
IPing
ping
)
{
ZoneAwareLoadBalancer
&
lt
;
Server
&
gt
;
balancer
=
LoadBalancerBuilder
.
newBuilder
(
)
.
withClientConfig
(
config
)
.
withRule
(
rule
)
.
withPing
(
ping
)
.
withServerListFilter
(
serverListFilter
)
.
withDynamicServerList
(
serverList
)
.
buildDynamicServerListLoadBalancer
(
)
;
return
balancer
;
}
|
3 主要组件
可以看到一个LB中包含的几个重要的属性(工具),正是这几个工具为LB的功能提供支持。 他们是:
IRule | 负载均衡策略,可以插件化的为LB提供各种适用的负载均衡算法 |
Iping | ?判断目标服务是否存活 对应不同的协议不同的方式去探测,得到后端服务是否存活。如有http的,还有对于微服务框架内的服务存活的NIWSDiscoveryPing是通过eureka client来获取的instanceinfo中的信息来获取。 |
LoadBalancerStats | LB运行信息 记录LB的实时运行信息,这些运行信息可以被用来作为LB策略的输入。 |
观察下几个主要方法。首先是最重要的的服务选择方法:
1
2
3
4
5
6
7
|
public
Server
chooseServer
(
Object
key
)
{
if
(
counter
==
null
)
{
counter
=
createCounter
(
)
;
}
counter
.
increment
(
)
;
return
rule
.
choose
(
key
)
;
}
|
有点失望,啥也没有,除了个计数,就一句,可以看到将该功能委托给包含的负载均衡策略rule来实现。 可以通过使用不同的负载均衡策略的choose方法来达到LB的不同行为,这个将在另一篇文章中介绍。
其他的也没有什么特别的,维护后端的服务列表实现了getServerList和addServers方法如下:
1
2
3
4
5
6
7
8
9
10
|
public
List
&
lt
;
Server
&
gt
;
getServerList
(
boolean
availableOnly
)
return
(
availableOnly
?
Collections
.
unmodifiableList
(
upServerList
)
:
Collections
.
unmodifiableList
(
allServerList
)
)
;
}
public
void
addServers
(
List
&
lt
;
Server
&
gt
;
newServers
)
{
ArrayList
&
lt
;
Server
&
gt
;
newList
=
new
ArrayList
&
lt
;
Server
&
gt
;
(
)
;
newList
.
addAll
(
allServerList
)
;
newList
.
addAll
(
newServers
)
;
setServersList
(
newList
)
;
}
|
关于IPing的使用,在初始化LB的时候会启动一个timer来根据配置的周期pingIntervalSeconds,使用配置的IPing方式类判断后端的server哪些是不可用的。
另外重要一点,IRule和IPing两个LB中使用的重要逻辑的东西都可以在配置中配置一个类名来动态生成。
1
2
3
4
5
6
7
8
|
public
void
initWithNiwsConfig
(
IClientConfig
clientConfig
)
{
String
ruleClassName
=
(
String
)
clientConfig
.
getProperty
(
CommonClientConfigKey
.
NFLoadBalancerRuleClassName
)
;
String
pingClassName
=
(
String
)
clientConfig
.
getProperty
(
CommonClientConfigKey
.
NFLoadBalancerPingClassName
)
;
IRule
rule
;
IPing
ping
;
rule
=
(
IRule
)
ClientFactory
.
instantiateInstanceWithClientConfig
(
ruleClassName
,
clientConfig
)
;
ping
=
(
IPing
)
ClientFactory
.
instantiateInstanceWithClientConfig
(
pingClassName
,
clientConfig
)
;
}
|
这也是LB最灵活的地方,用户可以根据自己的server的协议和判断标准写一个ping方法。 如ribbon提供了常用的http的ping,还提供了用于于微服务框架内的服务存活的NIWSDiscoveryPing,通过eureka client来获取的instanceinfo中的信息来获取服务的状态。 当然更常用的是用户自己要写适合应用场景的负载均衡策略。这将在另一篇文章详细的说明。
4 Server 后端服务
Ribbon的后端服务的描述封装在com.netflix.loadbalancer.Server中,主要信息也就两个host和port。但实现上比这个却看上去要复杂,看到里面定义了这样一个内部接口的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
private
MetaInfo
simpleMetaInfo
=
new
MetaInfo
(
)
{
@Override
public
String
getAppName
(
)
{
return
null
;
}
@Override
public
String
getServerGroup
(
)
{
return
null
;
}
@Override
public
String
getServiceIdForDiscovery
(
)
{
return
null
;
}
@Override
public
String
getInstanceId
(
)
{
return
id
;
}
}
;
|
怎么还有getServiceIdForDiscovery?看下Server的子类com.netflix.niws.loadbalancer.DiscoveryEnabledServer,应该就全明白了。
1
2
3
4
5
6
7
8
9
|
this
.
serviceInfo
=
new
MetaInfo
(
)
{
@Override
public
String
getAppName
(
)
{
return
instanceInfo
.
getAppName
(
)
;
}
@Override
public
String
getServerGroup
(
)
{
return
instanceInfo
.
getASGName
(
)
;
}
@Override
public
String
getServiceIdForDiscovery
(
)
{
return
instanceInfo
.
getVIPAddress
(
)
;
}
@Override
public
String
getInstanceId
(
)
{
return
instanceInfo
.
getId
(
)
;
}
}
;
|
看到引用到的com.netflix.appinfo.InstanceInfo就理解了,这里的服务正是eureka发现和注册的服务,ribbon作为一个通用的客户端负载均衡,只要把请求转发到一个host+port上,但在netflix的微服务框架里,ribbon常用的的后端也就是服务框架中注册的服务。 能猜到getASGName这个ASG是什么意思不?AWS Service Group? 怎么还说有aws的东西。
完。
原创文章。为了维护文章的版本一致、最新、可追溯,转载请注明: 转载自idouba