- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Spring Cloud Ribbon负载均衡器处理方法由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
接下来撸一撸负载均衡器的内部,看看是如何获取服务实例,获取以后做了哪些处理,处理后又是如何选取服务实例的.
分成三个部分来撸:
配置 。
在上一篇《撸一撸Spring Cloud Ribbon的原理》的配置部分可以看到默认的负载均衡器是ZoneAwareLoadBalancer.
看一看配置类.
位置:
1
2
3
|
spring-cloud-netflix-core-
1.3
.
5
.RELEASE.jar
org.springframework.cloud.netflix.ribbon
RibbonClientConfiguration.
class
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
@SuppressWarnings
(
"deprecation"
)
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import
({OkHttpRibbonConfiguration.
class
, RestClientRibbonConfiguration.
class
, HttpClientRibbonConfiguration.
class
})
public
class
RibbonClientConfiguration {
// 略
@Bean
@ConditionalOnMissingBean
public
ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if
(
this
.propertiesFactory.isSet(ILoadBalancer.
class
, name)) {
return
this
.propertiesFactory.get(ILoadBalancer.
class
, config, name);
}
return
new
ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
// 略
}
|
在实例化ZoneAwareLoadBalancer的时候注入了,config、rule、ping、serverList、serverListFilter、serverListUpdater实例.
config:配置实例.
rule:负载均衡策略实例.
ping:ping实例.
serverList:获取和更新服务的实例.
serverListFilter:服务过滤实例.
serverListUpdater:服务列表信息更新实例.
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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
@SuppressWarnings
(
"deprecation"
)
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import
({OkHttpRibbonConfiguration.
class
, RestClientRibbonConfiguration.
class
, HttpClientRibbonConfiguration.
class
})
public
class
RibbonClientConfiguration {
// 略
@Bean
@ConditionalOnMissingBean
public
IClientConfig ribbonClientConfig() {
DefaultClientConfigImpl config =
new
DefaultClientConfigImpl();
config.loadProperties(
this
.name);
return
config;
}
@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;
}
@Bean
@ConditionalOnMissingBean
public
IPing ribbonPing(IClientConfig config) {
if
(
this
.propertiesFactory.isSet(IPing.
class
, name)) {
return
this
.propertiesFactory.get(IPing.
class
, config, name);
}
return
new
DummyPing();
}
@Bean
@ConditionalOnMissingBean
@SuppressWarnings
(
"unchecked"
)
public
ServerList<Server> ribbonServerList(IClientConfig config) {
if
(
this
.propertiesFactory.isSet(ServerList.
class
, name)) {
return
this
.propertiesFactory.get(ServerList.
class
, config, name);
}
ConfigurationBasedServerList serverList =
new
ConfigurationBasedServerList();
serverList.initWithNiwsConfig(config);
return
serverList;
}
@Bean
@ConditionalOnMissingBean
public
ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
return
new
PollingServerListUpdater(config);
}
@Bean
@ConditionalOnMissingBean
public
ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
if
(
this
.propertiesFactory.isSet(ILoadBalancer.
class
, name)) {
return
this
.propertiesFactory.get(ILoadBalancer.
class
, config, name);
}
return
new
ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
serverListFilter, serverListUpdater);
}
@Bean
@ConditionalOnMissingBean
@SuppressWarnings
(
"unchecked"
)
public
ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
if
(
this
.propertiesFactory.isSet(ServerListFilter.
class
, name)) {
return
this
.propertiesFactory.get(ServerListFilter.
class
, config, name);
}
ZonePreferenceServerListFilter filter =
new
ZonePreferenceServerListFilter();
filter.initWithNiwsConfig(config);
return
filter;
}
@Bean
@ConditionalOnMissingBean
public
RibbonLoadBalancerContext ribbonLoadBalancerContext(
ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
return
new
RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
}
// 略
}
|
在这里配置相关的实例 。
config:DefaultClientConfigImpl.
rule:ZoneAvoidanceRule.
ping:DummyPing.
serverList:ConfigurationBasedServerList,基于配置的服务列表实例.
serverListFilter:ZonePreferenceServerListFilter.
serverListUpdater:PollingServerListUpdater.
要注意的是,在这里serverList的实例是ConfigurationBasedServerList,这是在未使用Eureka时获取服务信息的实例,是从配置文件中获取.
那么在和Eureka配合使用时,需要从 Eureka Server获取服务信息,那该是哪个实例来做这件事情呢.
在启用Eureka服务发现时,会首先会采用EurekaRibbonClientConfiguration配置类.
位置:
1
2
3
|
spring-cloud-netflix-eureka-client-
1.3
.
5
.RELEASE.jar
org.springframework.cloud.netflix.ribbon.eureka
EurekaRibbonClientConfiguration.
class
|
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
|
@Configuration
@CommonsLog
public
class
EurekaRibbonClientConfiguration {
// 略
@Bean
@ConditionalOnMissingBean
public
IPing ribbonPing(IClientConfig config) {
if
(
this
.propertiesFactory.isSet(IPing.
class
, serviceId)) {
return
this
.propertiesFactory.get(IPing.
class
, config, serviceId);
}
NIWSDiscoveryPing ping =
new
NIWSDiscoveryPing();
ping.initWithNiwsConfig(config);
return
ping;
}
@Bean
@ConditionalOnMissingBean
public
ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
if
(
this
.propertiesFactory.isSet(ServerList.
class
, serviceId)) {
return
this
.propertiesFactory.get(ServerList.
class
, config, serviceId);
}
DiscoveryEnabledNIWSServerList discoveryServerList =
new
DiscoveryEnabledNIWSServerList(
config, eurekaClientProvider);
DomainExtractingServerList serverList =
new
DomainExtractingServerList(
discoveryServerList, config,
this
.approximateZoneFromHostname);
return
serverList;
}
// 略
}
|
在首先采用了EurekaRibbonClientConfiguration配置后,实际上各实例变成了 。
config:DefaultClientConfigImpl.
rule:ZoneAvoidanceRule.
ping:NIWSDiscoveryPing.
serverList:DomainExtractingServerList,内部是DiscoveryEnabledNIWSServerList,实际上是通过服务发现获取服务信息列表.
serverListFilter:ZonePreferenceServerListFilter.
serverListUpdater:PollingServerListUpdater.
获取服务 。
在找到获取服务信息入口前,先把负载均衡器的类继承关系撸一下.
在ZoneAwareLoadBalancer的构造中调用了父类DynamicServerListLoadBalancer构造.
位置:
ribbon-loadbalancer-2.2.2.jar 。
com.netflix.loadbalancer 。
ZoneAwareLoadBalancer.class 。
在DynamicServerListLoadBalancer的构造中,调用了restOfInit函数.
ribbon-loadbalancer-2.2.2.jar 。
com.netflix.loadbalancer 。
DynamicServerListLoadBalancer.class 。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
void
restOfInit(IClientConfig clientConfig) {
boolean
primeConnection =
this
.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this
.setEnablePrimingConnections(
false
);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if
(primeConnection &&
this
.getPrimeConnections() !=
null
) {
this
.getPrimeConnections()
.primeConnections(getReachableServers());
}
this
.setEnablePrimingConnections(primeConnection);
LOGGER.info(
"DynamicServerListLoadBalancer for client {} initialized: {}"
, clientConfig.getClientName(),
this
.toString());
}
|
先是通过调用enableAndInitLearnNewServersFeature方法启动定时更新服务列表,然后立即调用updateListOfServers函数马上获取并更新服务列表信息.
先看下enableAndInitLearnNewServersFeature方法,实际上是调用了服务列表信息更新实例的start方法启动定时更新功能.
1
2
3
4
5
6
7
8
9
|
/**
* Feature that lets us add new instances (from AMIs) to the list of
* existing servers that the LB will use Call this method if you want this
* feature enabled
*/
public
void
enableAndInitLearnNewServersFeature() {
LOGGER.info(
"Using serverListUpdater {}"
, serverListUpdater.getClass().getSimpleName());
serverListUpdater.start(updateAction);
}
|
这里的服务列表信息更新实例就是配置阶段配置的PollingServerListUpdater实例,看一下这个类的构造和start方法.
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
48
49
|
public
class
PollingServerListUpdater
implements
ServerListUpdater {
// 略
private
static
long
LISTOFSERVERS_CACHE_UPDATE_DELAY =
1000
;
// msecs;
private
static
int
LISTOFSERVERS_CACHE_REPEAT_INTERVAL =
30
*
1000
;
// msecs;
// 略
private
final
AtomicBoolean isActive =
new
AtomicBoolean(
false
);
private
volatile
long
lastUpdated = System.currentTimeMillis();
private
final
long
initialDelayMs;
private
final
long
refreshIntervalMs;
// 略
public
PollingServerListUpdater(IClientConfig clientConfig) {
this
(LISTOFSERVERS_CACHE_UPDATE_DELAY, getRefreshIntervalMs(clientConfig));
}
public
PollingServerListUpdater(
final
long
initialDelayMs,
final
long
refreshIntervalMs) {
this
.initialDelayMs = initialDelayMs;
this
.refreshIntervalMs = refreshIntervalMs;
}
@Override
public
synchronized
void
start(
final
UpdateAction updateAction) {
if
(isActive.compareAndSet(
false
,
true
)) {
final
Runnable wrapperRunnable =
new
Runnable() {
@Override
public
void
run() {
if
(!isActive.get()) {
if
(scheduledFuture !=
null
) {
scheduledFuture.cancel(
true
);
}
return
;
}
try
{
updateAction.doUpdate();
lastUpdated = System.currentTimeMillis();
}
catch
(Exception e) {
logger.warn(
"Failed one update cycle"
, e);
}
}
};
scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
wrapperRunnable,
initialDelayMs,
refreshIntervalMs,
TimeUnit.MILLISECONDS
);
}
else
{
logger.info(
"Already active, no-op"
);
}
}
// 略
}
|
从构造和常量定义看出来,延迟一秒执行,默认每隔30秒执行更新,可以通过配置修改间隔更新的时间.
从start方法看,就是开了一个定时执行的schedule,定时执行 updateAction.doUpdate().
回到start方法调用方DynamicServerListLoadBalancer类中看一下UpdateAction实例的定义.
1
2
3
4
5
6
|
protected
final
ServerListUpdater.UpdateAction updateAction =
new
ServerListUpdater.UpdateAction() {
@Override
public
void
doUpdate() {
updateListOfServers();
}
};
|
实际上就是调用了DynamicServerListLoadBalancer类的updateListOfServers方法,这跟启动完定时更新后立即更新服务信息列表的路径是一致的.
继续看updateListOfServers方法。 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
void
updateListOfServers() {
List<T> servers =
new
ArrayList<T>();
if
(serverListImpl !=
null
) {
servers = serverListImpl.getUpdatedListOfServers();
LOGGER.debug(
"List of Servers for {} obtained from Discovery client: {}"
,
getIdentifier(), servers);
if
(filter !=
null
) {
servers = filter.getFilteredListOfServers(servers);
LOGGER.debug(
"Filtered List of Servers for {} obtained from Discovery client: {}"
,
getIdentifier(), servers);
}
}
updateAllServerList(servers);
}
|
1.通过ServerList实例获取服务信息列表.
2.通过ServerListFilter 实例对获取到的服务信息列表进行过滤.
3.将过滤后的服务信息列表保存到LoadBalancerStats中作为状态保持.
接下分别看一下.
1.通过ServerList实例获取服务信息列表.
ServerList实例就是配置阶段生成的DomainExtractingServerList,获取服务信息都是委托给DiscoveryEnabledNIWSServerList.
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
48
49
50
51
|
public
class
DiscoveryEnabledNIWSServerList
extends
AbstractServerList<DiscoveryEnabledServer>{
// 略
@Override
public
List<DiscoveryEnabledServer> getInitialListOfServers(){
return
obtainServersViaDiscovery();
}
@Override
public
List<DiscoveryEnabledServer> getUpdatedListOfServers(){
return
obtainServersViaDiscovery();
}
private
List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
List<DiscoveryEnabledServer> serverList =
new
ArrayList<DiscoveryEnabledServer>();
if
(eurekaClientProvider ==
null
|| eurekaClientProvider.get() ==
null
) {
logger.warn(
"EurekaClient has not been initialized yet, returning an empty list"
);
return
new
ArrayList<DiscoveryEnabledServer>();
}
EurekaClient eurekaClient = eurekaClientProvider.get();
if
(vipAddresses!=
null
){
for
(String vipAddress : vipAddresses.split(
","
)) {
// if targetRegion is null, it will be interpreted as the same region of client
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
for
(InstanceInfo ii : listOfInstanceInfo) {
if
(ii.getStatus().equals(InstanceStatus.UP)) {
if
(shouldUseOverridePort){
if
(logger.isDebugEnabled()){
logger.debug(
"Overriding port on client name: "
+ clientName +
" to "
+ overridePort);
}
// copy is necessary since the InstanceInfo builder just uses the original reference,
// and we don't want to corrupt the global eureka copy of the object which may be
// used by other clients in our system
InstanceInfo copy =
new
InstanceInfo(ii);
if
(isSecure){
ii =
new
InstanceInfo.Builder(copy).setSecurePort(overridePort).build();
}
else
{
ii =
new
InstanceInfo.Builder(copy).setPort(overridePort).build();
}
}
DiscoveryEnabledServer des =
new
DiscoveryEnabledServer(ii, isSecure, shouldUseIpAddr);
des.setZone(DiscoveryClient.getZone(ii));
serverList.add(des);
}
}
if
(serverList.size()>
0
&& prioritizeVipAddressBasedServers){
break
;
// if the current vipAddress has servers, we dont use subsequent vipAddress based servers
}
}
}
return
serverList;
}
// 略
}
|
可以看到其实就是通过Eureka客户端从Eureka服务端获取所有服务实例信息并把上线的包装成DiscoveryEnabledServer实例,带有zone信息,做到服务列表中.
2.通过ServerListFilter 实例对获取到的服务信息列表进行过滤.
serverListFilte实例就是配置阶段生成的ZonePreferenceServerListFilter,通过调用该实例的getFilteredListOfServers方法进行过滤.
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
|
@Data
@EqualsAndHashCode
(callSuper =
false
)
public
class
ZonePreferenceServerListFilter
extends
ZoneAffinityServerListFilter<Server> {
private
String zone;
@Override
public
void
initWithNiwsConfig(IClientConfig niwsClientConfig) {
super
.initWithNiwsConfig(niwsClientConfig);
if
(ConfigurationManager.getDeploymentContext() !=
null
) {
this
.zone = ConfigurationManager.getDeploymentContext().getValue(
ContextKey.zone);
}
}
@Override
public
List<Server> getFilteredListOfServers(List<Server> servers) {
List<Server> output =
super
.getFilteredListOfServers(servers);
if
(
this
.zone !=
null
&& output.size() == servers.size()) {
List<Server> local =
new
ArrayList<Server>();
for
(Server server : output) {
if
(
this
.zone.equalsIgnoreCase(server.getZone())) {
local.add(server);
}
}
if
(!local.isEmpty()) {
return
local;
}
}
return
output;
}
}
|
在getFilteredListOfServers方法里面,一上来是调用父类的同名方法先过滤,其实父类也是把和消费端同区域的服务给过滤出来使用,不仅如此,增加了些智能的判定,保证在故障/负载较高时或者可用实例较少时不进行同区域的过滤.
但是在ZonePreferenceServerListFilter.getFilteredListOfServers这里,就算父类没做过过滤,这里依然要把同zone的服务给滤出来使用,谁叫这里的类是ZonePreference的呢.
这是比较怪异的地方,感觉父类的智能判定没什么作用.
还是看看ZoneAffinityServerListFilter.getFilteredListOfServers做的辛苦工作吧.
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
|
public
class
ZoneAffinityServerListFilter<T
extends
Server>
extends
AbstractServerListFilter<T>
implements
IClientConfigAware {
// 略
private
boolean
shouldEnableZoneAffinity(List<T> filtered) {
if
(!zoneAffinity && !zoneExclusive) {
return
false
;
}
if
(zoneExclusive) {
return
true
;
}
LoadBalancerStats stats = getLoadBalancerStats();
if
(stats ==
null
) {
return
zoneAffinity;
}
else
{
logger.debug(
"Determining if zone affinity should be enabled with given server list: {}"
, filtered);
ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered);
double
loadPerServer = snapshot.getLoadPerServer();
int
instanceCount = snapshot.getInstanceCount();
int
circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount();
if
(((
double
) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get()
|| loadPerServer >= activeReqeustsPerServerThreshold.get()
|| (instanceCount - circuitBreakerTrippedCount) < availableServersThreshold.get()) {
logger.debug(
"zoneAffinity is overriden. blackOutServerPercentage: {}, activeReqeustsPerServer: {}, availableServers: {}"
,
new
Object[] {(
double
) circuitBreakerTrippedCount / instanceCount, loadPerServer, instanceCount - circuitBreakerTrippedCount});
return
false
;
}
else
{
return
true
;
}
}
}
@Override
public
List<T> getFilteredListOfServers(List<T> servers) {
if
(zone !=
null
&& (zoneAffinity || zoneExclusive) && servers !=
null
&& servers.size() >
0
){
List<T> filteredServers = Lists.newArrayList(Iterables.filter(
servers,
this
.zoneAffinityPredicate.getServerOnlyPredicate()));
if
(shouldEnableZoneAffinity(filteredServers)) {
return
filteredServers;
}
else
if
(zoneAffinity) {
overrideCounter.increment();
}
}
return
servers;
}
// 略
}
|
首先会将与消费端相同的zone的服务过滤出来,然后通过shouldEnableZoneAffinity(filteredServers)来判定是否可以采纳同zone的服务,还是采用所有的服务.
在shouldEnableZoneAffinity方法内,对相同zone的服务做了一次snapshot,获取这些服务的实例数量,平均负载,断路的实例数进行计算判定.
可以看一下initWithNiwsConfig方法中关键指标的值.
判定条件:
断路实例百分比>=0.8(断路的实例数/服务的实例数量) 。
平均负载>=0.6 。
可用实例数<2(实例数量-断路的实例数) 。
如果达到判定条件,那么就使用全部的服务,保证可用性.
但,上面也说了,因为ZonePreferenceServerListFilter本身总是会选用和消费端zone一致的服务,所以ZoneAffinityServerListFilter.getFilteredListOfServers中做的智能操作并没什么用.
不过,当然可以通过自定义配置来采用ZoneAffinityServerListFilter实例.
3.将过滤后的服务信息列表保存到LoadBalancerStats中作为状态保持.
跟进updateAllServerList(servers);去,一步步深入,会发现,实际上是保存到LoadBalancerStats中,并且这时候的服务是按照zone分组以HashMap<String, List<Server>>结构保存的,key是zone.
选择服务 。
实现了ILoadBalancer接口的负载均衡器,是通过实现chooseServer方法来进行服务的选择,选择后的服务做为目标请求服务.
看一下ZoneAwareLoadBalancer.chooseServer方法.
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
|
@Override
public
Server chooseServer(Object key) {
if
(!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <=
1
) {
logger.debug(
"Zone aware logic disabled or there is only one zone"
);
return
super
.chooseServer(key);
}
Server server =
null
;
try
{
LoadBalancerStats lbStats = getLoadBalancerStats();
Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
logger.debug(
"Zone snapshots: {}"
, zoneSnapshot);
if
(triggeringLoad ==
null
) {
triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer."
+
this
.getName() +
".triggeringLoadPerServerThreshold"
,
0
.2d);
}
if
(triggeringBlackoutPercentage ==
null
) {
triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer."
+
this
.getName() +
".avoidZoneWithBlackoutPercetage"
,
0
.99999d);
}
Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
logger.debug(
"Available zones: {}"
, availableZones);
if
(availableZones !=
null
&& availableZones.size() < zoneSnapshot.keySet().size()) {
String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
logger.debug(
"Zone chosen: {}"
, zone);
if
(zone !=
null
) {
BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key);
}
}
}
catch
(Exception e) {
logger.error(
"Error choosing server using zone aware logic for load balancer={}"
, name, e);
}
if
(server !=
null
) {
return
server;
}
else
{
logger.debug(
"Zone avoidance logic is not invoked."
);
return
super
.chooseServer(key);
}
}
|
注意这里有两种用法:
1.通过配置ZoneAwareNIWSDiscoveryLoadBalancer.enabled=false关闭区域感知负载均衡,或者zone的个数<=1个.
2.采用区域感知,或者zone的个数>1.
一个个来看一下 。
1.通过配置ZoneAwareNIWSDiscoveryLoadBalancer.enabled=false关闭区域感知负载均衡,或者zone的个数<=1个.
这种情况下,调用了父类BaseLoadBalancer.chooseServer方法.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
Server chooseServer(Object key) {
if
(counter ==
null
) {
counter = createCounter();
}
counter.increment();
if
(rule ==
null
) {
return
null
;
}
else
{
try
{
return
rule.choose(key);
}
catch
(Exception e) {
logger.warn(
"LoadBalancer [{}]: Error choosing server for key {}"
, name, key, e);
return
null
;
}
}
}
|
这里使用的负载均衡策略rule实际上就是构造ZoneAwareLoadBalancer时传进来的,在配置阶段生成的ZoneAvoidanceRule策略实例。 。
1
2
3
4
5
6
7
8
9
10
11
|
public
void
setRule(IRule rule) {
if
(rule !=
null
) {
this
.rule = rule;
}
else
{
/* default rule */
this
.rule =
new
RoundRobinRule();
}
if
(
this
.rule.getLoadBalancer() !=
this
) {
this
.rule.setLoadBalancer(
this
);
}
}
|
假设,如果没有配置,默认用的是RoundRobinRule策略实例.
2.采用区域感知,或者zone的个数>1.
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
|
public
Server chooseServer(Object key) {
if
(!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <=
1
) {
logger.debug(
"Zone aware logic disabled or there is only one zone"
);
return
super
.chooseServer(key);
}
Server server =
null
;
try
{
LoadBalancerStats lbStats = getLoadBalancerStats();
Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
logger.debug(
"Zone snapshots: {}"
, zoneSnapshot);
if
(triggeringLoad ==
null
) {
triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer."
+
this
.getName() +
".triggeringLoadPerServerThreshold"
,
0
.2d);
}
if
(triggeringBlackoutPercentage ==
null
) {
triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
"ZoneAwareNIWSDiscoveryLoadBalancer."
+
this
.getName() +
".avoidZoneWithBlackoutPercetage"
,
0
.99999d);
}
Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
logger.debug(
"Available zones: {}"
, availableZones);
if
(availableZones !=
null
&& availableZones.size() < zoneSnapshot.keySet().size()) {
String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
logger.debug(
"Zone chosen: {}"
, zone);
if
(zone !=
null
) {
BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key);
}
}
}
catch
(Exception e) {
logger.error(
"Error choosing server using zone aware logic for load balancer={}"
, name, e);
}
if
(server !=
null
) {
return
server;
}
else
{
logger.debug(
"Zone avoidance logic is not invoked."
);
return
super
.chooseServer(key);
}
}
|
在这种情况下默认使用ZoneAvoidanceRule负载均衡策略.
获取zone的snapshot信息.
获取可用的zone,通过观察ZoneAvoidanceRule.getAvailableZones定义,不是可用zone的条件是:
可用zone都获取后,随机选一个.
并从该zone中,通过ZoneAwareLoadBalancer的父类BaseLoadBalancer.chooseServer选取服务,上面整理过,BaseLoadBalancer里如果没有传入rule,那么默认使用RoundRobinRule策略轮寻一个服务.
其实,还是上面获取服务中ZonePreferenceServerListFilter过滤器的问题,实际上过滤出来的只有一个和消费端相同的一个zone的服务,所以第2.部分的从可用zone中选取服务的功能是走不到,要走到就得把过滤器给换掉.
总结:
配置的负载均衡器会启动schedule获取服务信息,在使用了Eureka客户端时,会从Eureka服务获取所有服务实例信息,通过过滤器过滤出可以使用的服务,过滤器默认只过滤出与消费端相同zone的服务,如果要保证高可用可配置ZoneAffinityServerListFilter过滤器,过滤后的服务列表,通过实现了IRule接口的负载均衡策略选取对应的服务,如果是使用zone感知的策略,可以从负载情况良好的zone中选取合适的服务.
原文链接:https://www.cnblogs.com/kongxianghai/p/8456576.html 。
最后此篇关于Spring Cloud Ribbon负载均衡器处理方法的文章就讲到这里了,如果你想了解更多关于Spring Cloud Ribbon负载均衡器处理方法的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有一个 Cloud Run 服务,它通过 SQLAlchemy 访问 Cloud SQL 实例.但是,在 Cloud Run 的日志中,我看到 CloudSQL connection failed.
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 4年前关闭。 Improve t
在将 docker 容器镜像部署到 Cloud Run 时,我可以选择一个区域,这很好。 Cloud Run 将构建委托(delegate)给 Cloud Build,后者显然会创建两个存储桶来实现这
我正在尝试将 Cloud Functions 用作由 PubSub 触发的异步后台工作程序,并进行更长时间的工作(以分钟为单位)。完整代码在这里https://github.com/zdenulo/c
这是/etc/cloud/cloud.cfg的内容Ubuntu云16.04镜像: # The top level settings are used as module # and system co
如何从 Google Cloud Function 启动 Cloud Dataflow 作业?我想使用 Google Cloud Functions 作为启用跨服务组合的机制。 最佳答案 我已经包含了
我想使用 Cloud Shell 在我的第二代 Cloud Sql 实例上运行数据库迁移。 我找到了一个 example in the docs关于如何使用 gcloud 进行连接.但是当我运行命令时
我正在尝试使用 Google Cloud PubSub和我的 Google Cloud Dataproc群集,我收到如下身份验证范围错误: { "code" : 403, "errors" :
这是我的用例。 我已经有一个以私有(private)模式部署的 Cloud Run 服务。 (与云功能相同的问题) 我正在开发使用此 Cloud Run 的新服务。我在应用程序中使用默认凭据进行身份验
如何连接到 Cloud SQL 上的数据库,而无需在容器中添加我的凭据文件? 最佳答案 使用 UNIX 域套接字 (Java) 从云运行(完全托管)连接到云 SQL At this time Clou
我有一个google-cloud-ml作业,需要从gs存储桶加载numpy .npz文件。我遵循了this example上关于如何从gs加载.npy文件的操作,但是由于.npz文件已压缩,因此它对我
我想创建链接到另一个项目中的 Cloud Source Repository 的 Cloud Build 触发器。但是当我在应该选择存储库的步骤中时,列表是空的。我尝试了不同的许可,但没有运气。谁能告
向 Twilio 发送 SMS 时,Twilio 会向指定的 URL 发送多个请求,以通过 Webhook 提供该 SMS 传送的状态。我想让这个回调异步,所以我开发了一个 Cloud Functio
我需要更改我的项目 ID,因为要验证的 Firebase 身份验证链接在链接上显示了项目 ID,并且由于品牌 reshape ,项目名称已更改。根据我发现的信息,更改项目 ID 似乎不太可能。我正在考
用于部署我的 Angular 应用程序的 CI/CD 管道已关闭,但我看到 Google Cloud Run 在容器镜像更新后没有部署新修订版。 我已将 Cloud Build 设置为在 GitHub
报价https://cloud.google.com/load-balancing/docs/https/setting-up-https-serverless#enabling While Goog
Cloud Spanner 提供了两种不同的 API。 Cloud Spanner 读取与 Cloud Spanner SQL API 之间有什么区别? 最佳答案 在幕后,它们都使用相同的执行机制,因
我是 GCP 堆栈的新手,所以我对用于存储数据的 GCP 技术数量感到非常困惑: https://cloud.google.com/products/storage 虽然上面的文章中没有提到googl
我发现 Google Cloud Functions 的网络出站费用令人惊讶,我正在尝试了解发生这种情况的原因以及如何避免这种情况。 Stackdriver 监控表明有问题的函数是我的 ingest
我使用 Prisma使用 Cloud Run 和 Cloud SQL。在向 prisma.schema 提供 DATABASE_URL 后,它会在运行时抛出一个错误。 Can't reach data
我是一名优秀的程序员,十分优秀!