一 DynamicServerListLoadBalancer在类图中的位置
二 DynamicServerListLoadBalancer源码解读
1 关键代码请见注释
2 源码位置:ribbon-master\ribbon-loadbalancer\src\main\java\com\netflix\loadbalancer\DynamicServerListLoadBalancer.java
//继承于BaseLoadBalancer类,它是对基础负载均衡器的扩展。实现了下面两个功能
//服务实例在运行期间的动态更新
//对服务器实例清单的过滤功能,可以通过过滤器来选择地获取一批服务实例清单
public class DynamicServerListLoadBalancer extends BaseLoadBalancer {
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);
boolean isSecure = false;
boolean useTunnel = false;
protected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);
//服务列表操作对象
//这里泛型T是一个Server的子类,代表了一个具体的服务实例扩展类
volatile ServerList serverListImpl;
//对应过滤器
volatile ServerListFilter filter;
//实际实现委托给updateListOfServers函数
protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
@Override
public void doUpdate() {
updateListOfServers();
}
};
//用于触发向Eureka Server去获取服务实例清单以及如何在获取到服务实例清单后更新本地的服务实例清单
//这个对象实现的是对Server的更新,又被称为服务更新器
protected volatile ServerListUpdater serverListUpdater;
public DynamicServerListLoadBalancer() {
super();
}
@Deprecated
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList serverList, ServerListFilter filter) {
this(
clientConfig,
rule,
ping,
serverList,
filter,
new PollingServerListUpdater()
);
}
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList serverList, ServerListFilter filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping);
this.serverListImpl = serverList;
this.filter = filter;
this.serverListUpdater = serverListUpdater;
if (filter instanceof AbstractServerListFilter) {
((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
}
restOfInit(clientConfig);
}
public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
initWithNiwsConfig(clientConfig);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
try {
super.initWithNiwsConfig(clientConfig);
String niwsServerListClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.NIWSServerListClassName,
DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);
ServerList niwsServerListImpl = (ServerList) ClientFactory
.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
this.serverListImpl = niwsServerListImpl;
if (niwsServerListImpl instanceof AbstractServerList) {
AbstractServerListFilter niwsFilter = ((AbstractServerList) niwsServerListImpl)
.getFilterImpl(clientConfig);
niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
this.filter = niwsFilter;
}
String serverListUpdaterClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.ServerListUpdaterClassName,
DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
);
this.serverListUpdater = (ServerListUpdater) ClientFactory
.instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
restOfInit(clientConfig);
} catch (Exception e) {
throw new RuntimeException(
"Exception while initializing NIWSDiscoveryLoadBalancer:"
+ clientConfig.getClientName()
+ ", niwsClientConfig:" + clientConfig, e);
}
}
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());
}
@Override
public void setServersList(List lsrv) {
super.setServersList(lsrv);
List serverList = (List) lsrv;
Map> serversInZones = new HashMap>();
for (Server server : serverList) {
// make sure ServerStats is created to avoid creating them on hot
// path
getLoadBalancerStats().getSingleServerStat(server);
String zone = server.getZone();
if (zone != null) {
zone = zone.toLowerCase();
List servers = serversInZones.get(zone);
if (servers == null) {
servers = new ArrayList();
serversInZones.put(zone, servers);
}
servers.add(server);
}
}
setServerListForZones(serversInZones);
}
protected void setServerListForZones(
Map> zoneServersMap) {
LOGGER.debug("Setting server list for zones: {}", zoneServersMap);
getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
}
public ServerList getServerListImpl() {
return serverListImpl;
}
public void setServerListImpl(ServerList niwsServerList) {
this.serverListImpl = niwsServerList;
}
public ServerListFilter getFilter() {
return filter;
}
public void setFilter(ServerListFilter filter) {
this.filter = filter;
}
public ServerListUpdater getServerListUpdater() {
return serverListUpdater;
}
public void setServerListUpdater(ServerListUpdater serverListUpdater) {
this.serverListUpdater = serverListUpdater;
}
@Override
public void forceQuickPing() {
// no-op
}
public void enableAndInitLearnNewServersFeature() {
LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
serverListUpdater.start(updateAction);
}
private String getIdentifier() {
return this.getClientConfig().getClientName();
}
public void stopServerListRefreshing() {
if (serverListUpdater != null) {
serverListUpdater.stop();
}
}
//
@VisibleForTesting
public void updateListOfServers() {
List servers = new ArrayList();
if (serverListImpl != null) {
//实现从Eureka Server中获取服务可用实例列表
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);
}
/**
* Update the AllServer list in the LoadBalancer if necessary and enabled
*
* @param ls
*/
protected void updateAllServerList(List ls) {
// other threads might be doing this - in which case, we pass
if (serverListUpdateInProgress.compareAndSet(false, true)) {
try {
for (T s : ls) {
s.setAlive(true); // set so that clients can start using these
// servers right away instead
// of having to wait out the ping cycle.
}
setServersList(ls);
super.forceQuickPing();
} finally {
serverListUpdateInProgress.set(false);
}
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("DynamicServerListLoadBalancer:");
sb.append(super.toString());
sb.append("ServerList:" + String.valueOf(serverListImpl));
return sb.toString();
}
@Override
public void shutdown() {
super.shutdown();
stopServerListRefreshing();
}
@Monitor(name="LastUpdated", type=DataSourceType.INFORMATIONAL)
public String getLastUpdate() {
return serverListUpdater.getLastUpdate();
}
@Monitor(name="DurationSinceLastUpdateMs", type= DataSourceType.GAUGE)
public long getDurationSinceLastUpdateMs() {
return serverListUpdater.getDurationSinceLastUpdateMs();
}
@Monitor(name="NumUpdateCyclesMissed", type=DataSourceType.GAUGE)
public int getNumberMissedCycles() {
return serverListUpdater.getNumberMissedCycles();
}
@Monitor(name="NumThreads", type=DataSourceType.GAUGE)
public int getCoreThreads() {
return serverListUpdater.getCoreThreads();
}
}
三 ServerListFilter源码解读
//主要用于实现服务实例列表的过滤,通过传入的服务实例清单,根据一些规则返回过滤后的服务实例清单
public interface ServerListFilter {
public List getFilteredListOfServers(List servers);
}
四 过滤器类图
五 AbstractServerListFilter源码解读
//抽象过滤器
AbstractServerListFilterpublic abstract class AbstractServerListFilter implements ServerListFilter {
//存储了关于负载均衡器的一些属性和统计信息
private volatile LoadBalancerStats stats;
public void setLoadBalancerStats(LoadBalancerStats stats) {
this.stats = stats;
}
public LoadBalancerStats getLoadBalancerStats() {
return stats;
}
}
六 ZoneAffinityServerListFilter源码解读
//基于“区域感知(Zone Affinity)”的方式实现服务实例的过滤
//根据提供服务的实例所处的区域(Zone)与消费者自身所处的区域(Zone)进行比较,过滤掉那些不是同一个区域的实例
public class ZoneAffinityServerListFilter extends
AbstractServerListFilter implements IClientConfigAware {
private volatile boolean zoneAffinity = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_AFFINITY;
private volatile boolean zoneExclusive = DefaultClientConfigImpl.DEFAULT_ENABLE_ZONE_EXCLUSIVITY;
private DynamicDoubleProperty activeReqeustsPerServerThreshold;
private DynamicDoubleProperty blackOutServerPercentageThreshold;
private DynamicIntProperty availableServersThreshold;
private Counter overrideCounter;
private ZoneAffinityPredicate zoneAffinityPredicate = new ZoneAffinityPredicate();
private static Logger logger = LoggerFactory.getLogger(ZoneAffinityServerListFilter.class);
String zone;
public ZoneAffinityServerListFilter() {
}
public ZoneAffinityServerListFilter(IClientConfig niwsClientConfig) {
initWithNiwsConfig(niwsClientConfig);
}
@Override
public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
String sZoneAffinity = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneAffinity, false);
if (sZoneAffinity != null){
zoneAffinity = Boolean.parseBoolean(sZoneAffinity);
logger.debug("ZoneAffinity is set to {}", zoneAffinity);
}
String sZoneExclusive = "" + niwsClientConfig.getProperty(CommonClientConfigKey.EnableZoneExclusivity, false);
if (sZoneExclusive != null){
zoneExclusive = Boolean.parseBoolean(sZoneExclusive);
}
if (ConfigurationManager.getDeploymentContext() != null) {
zone = ConfigurationManager.getDeploymentContext().getValue(ContextKey.zone);
}
activeReqeustsPerServerThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxLoadPerServer", 0.6d);
logger.debug("activeReqeustsPerServerThreshold: {}", activeReqeustsPerServerThreshold.get());
blackOutServerPercentageThreshold = DynamicPropertyFactory.getInstance().getDoubleProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.maxBlackOutServesrPercentage", 0.8d);
logger.debug("blackOutServerPercentageThreshold: {}", blackOutServerPercentageThreshold.get());
availableServersThreshold = DynamicPropertyFactory.getInstance().getIntProperty(niwsClientConfig.getClientName() + "." + niwsClientConfig.getNameSpace() + ".zoneAffinity.minAvailableServers", 2);
logger.debug("availableServersThreshold: {}", availableServersThreshold.get());
overrideCounter = Monitors.newCounter("ZoneAffinity_OverrideCounter");
Monitors.registerObject("NIWSServerListFilter_" + niwsClientConfig.getClientName());
}
private boolean shouldEnableZoneAffinity(List 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();
//任何一个条件符合,就不启用“区域感知”过滤的服务实例清单
//三个条件是
//故障百分比(断路器断开数/实例数量)>=0.8
//实例平均负载>=0.6
//可用实例数(实例数量-断路器断开数)<2
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 getFilteredListOfServers(List servers) {
if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
//服务实例列表过滤结果
List filteredServers = Lists.newArrayList(Iterables.filter(
servers, this.zoneAffinityPredicate.getServerOnlyPredicate())); //zoneAffinityPredicate实现服务实例与消费者的Zone比较
//根据shouldEnableZoneAffinity结果判定是否启动区域感知
if (shouldEnableZoneAffinity(filteredServers)) {
return filteredServers;
} else if (zoneAffinity) {
overrideCounter.increment();
}
}
return servers;
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder("ZoneAffinityServerListFilter:");
sb.append(", zone: ").append(zone).append(", zoneAffinity:").append(zoneAffinity);
sb.append(", zoneExclusivity:").append(zoneExclusive);
return sb.toString();
}
}
七 DefaultNIWSServerListFilter源码解读
//完全继承自ZoneAffinityServerListFilter,是默认的NIWS(Netflix Internal Web Service)过滤器
public class DefaultNIWSServerListFilter extends ZoneAffinityServerListFilter {
}
八 ServerListSubsetFilter源码解读
//继承自ZoneAffinityServerListFilter,非常适用于大规模附录器集群(上百或更多)的系统。因为
//它可以产生一个“区域感知”结果的子集列表,同时它还能通过比较服务实例的通信
//失败数量和并发连接来判定该服务是否健康来选择性地从服务实例列表中剔除那些相
//对不健康的实例。
public class ServerListSubsetFilter extends ZoneAffinityServerListFilter implements IClientConfigAware, Comparator{
private Random random = new Random();
private volatile Set currentSubset = Sets.newHashSet();
private DynamicIntProperty sizeProp = new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.size", 20);
private DynamicFloatProperty eliminationPercent =
new DynamicFloatProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.forceEliminatePercent", 0.1f);
private DynamicIntProperty eliminationFailureCountThreshold =
new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.eliminationFailureThresold", 0);
private DynamicIntProperty eliminationConnectionCountThreshold =
new DynamicIntProperty(DefaultClientConfigImpl.DEFAULT_PROPERTY_NAME_SPACE + ".ServerListSubsetFilter.eliminationConnectionThresold", 0);
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
super.initWithNiwsConfig(clientConfig);
sizeProp = new DynamicIntProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace() + ".ServerListSubsetFilter.size", 20);
eliminationPercent =
new DynamicFloatProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace() + ".ServerListSubsetFilter.forceEliminatePercent", 0.1f);
eliminationFailureCountThreshold = new DynamicIntProperty( clientConfig.getClientName() + "." + clientConfig.getNameSpace()
+ ".ServerListSubsetFilter.eliminationFailureThresold", 0);
eliminationConnectionCountThreshold = new DynamicIntProperty(clientConfig.getClientName() + "." + clientConfig.getNameSpace()
+ ".ServerListSubsetFilter.eliminationConnectionThresold", 0);
}
@Override
public List getFilteredListOfServers(List servers) {
List zoneAffinityFiltered = super.getFilteredListOfServers(servers);
//获取“区域感知”过滤结果,作为候选的服务实例清单
Set candidates = Sets.newHashSet(zoneAffinityFiltered); //例如1,2,3
Set newSubSet = Sets.newHashSet(currentSubset); //例如1,2,4
LoadBalancerStats lbStats = getLoadBalancerStats();
for (T server: currentSubset) { //例如1,2,4
//剔除有问题的服务实例
if (!candidates.contains(server)) { //4不在candidates中
newSubSet.remove(server); //最终结果是1,2
} else {
ServerStats stats = lbStats.getSingleServerStat(server);
// 不健康指标如下,剔除不健康的实例
//服务实例的并连接数超过客户端的配置值
//服务实例的失败数超过客户端的配置值
if (stats.getActiveRequestsCount() > eliminationConnectionCountThreshold.get()
|| stats.getFailureCount() > eliminationFailureCountThreshold.get()) {
newSubSet.remove(server);
// also remove from the general pool to avoid selecting them again
candidates.remove(server);
}
}
}
int targetedListSize = sizeProp.get(); //客户端默认实例子集数量,默认为20
int numEliminated = currentSubset.size() - newSubSet.size(); //已被剔除的数量
int minElimination = (int) (targetedListSize * eliminationPercent.get()); //20*10%=2
int numToForceEliminate = 0;
if (targetedListSize < newSubSet.size()) {
// 还需要缩减的数量数
numToForceEliminate = newSubSet.size() - targetedListSize;
} else if (minElimination > numEliminated) {
// 还需要缩减的数量数
numToForceEliminate = minElimination - numEliminated;
}
if (numToForceEliminate > newSubSet.size()) {
numToForceEliminate = newSubSet.size();
}
if (numToForceEliminate > 0) {
List sortedSubSet = Lists.newArrayList(newSubSet);
// 排序,最不健康的排在前面
Collections.sort(sortedSubSet, this);
//找到最不健康的
List forceEliminated = sortedSubSet.subList(0, numToForceEliminate);
//删除最不健康的
newSubSet.removeAll(forceEliminated);
candidates.removeAll(forceEliminated);
}
//完成剔除后,清单已经少了至少10%(默认值)的服务实例,
//最后通过随机的方式从候选清单中选出一批实例加入清单,
//以保持服务实例子集和原来一致,默认值为20
if (newSubSet.size() < targetedListSize) {
int numToChoose = targetedListSize - newSubSet.size();
candidates.removeAll(newSubSet); //删除重合的
if (numToChoose > candidates.size()) {
//如果候选者不够数量,需要将候选者全部加入
candidates = Sets.newHashSet(zoneAffinityFiltered);
candidates.removeAll(newSubSet); //删除重合的
}
//选出一定数量的候选者
List chosen = randomChoose(Lists.newArrayList(candidates), numToChoose);
for (T server: chosen) {
newSubSet.add(server); //加入候选者
}
}
currentSubset = newSubSet;
return Lists.newArrayList(newSubSet);
}
private List randomChoose(List servers, int toChoose) {
int size = servers.size();
if (toChoose >= size || toChoose < 0) {
return servers;
}
for (int i = 0; i < toChoose; i++) {
int index = random.nextInt(size);
T tmp = servers.get(index);
servers.set(index, servers.get(i));
servers.set(i, tmp);
}
return servers.subList(0, toChoose);
}
@Override
public int compare(T server1, T server2) {
LoadBalancerStats lbStats = getLoadBalancerStats();
ServerStats stats1 = lbStats.getSingleServerStat(server1);
ServerStats stats2 = lbStats.getSingleServerStat(server2);
int failuresDiff = (int) (stats2.getFailureCount() - stats1.getFailureCount());
if (failuresDiff != 0) {
return failuresDiff;
} else {
return (stats2.getActiveRequestsCount() - stats1.getActiveRequestsCount());
}
}
}
九 ZonePreferenceServerListFilter源码解读
//Spring Cloud整合时新增的过滤器。若使用Spring Cloud整合 Eureka和Ribbon时会默认使用该过滤器。
//它通过配置或者Eureka实例元数据所属区域(Zone)来过滤出同区域的服务实例。
@Override
public List getFilteredListOfServers(List servers) {
//获得区域感知的服务实例列表
List output = super.getFilteredListOfServers(servers);
if (this.zone != null && output.size() == servers.size()) {
List local = new ArrayList<>();
//遍历服务列表,取出根据消费者预设的区域Zone来进行过滤
for (Server server : output) {
if (this.zone.equalsIgnoreCase(server.getZone())) {
local.add(server);
}
}
//返回通过消费者配置的Zone过滤后的结果
if (!local.isEmpty()) {
return local;
}
}
//返回父类获取的结果
return output;
}