Ribbon源码解析
使用ribbon实现服务消费者的负载均衡。中途出现了一点点错误和一点点疑惑就去底层看了部分功能的实现原理。
错误出现的是 No instances available for localhost] with root cause。
首先这个错误是在在实现负载均衡功能时,访问其他服务的时候出现的。
return restTemplate.getForObject("http://localhost/hi?name="+name,String.class);
RibbonLoadBalancerCIient这个类用来执行最终的的负载均衡的请求处理。看一下结构
出错的点在这个excute()方法中
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
这个错说明server=null.那继续追踪getServer()方法
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
继续看发现在chooseServer的ZoneAwareLoadBalancer实现类方法。看到zone猜测是该区域没有东西的
this.getLoadBalancerStats().getAvailableZones().size()=0 导致后面的解析都没有进行,直接最后一步了,重写该方法,应该也类似于一个初始化,就返回空。
public Server chooseServer(Object key) {
if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
Server server = null;
try {
LoadBalancerStats lbStats = this.getLoadBalancerStats();
Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
logger.debug("Zone snapshots: {}", zoneSnapshot);
if (this.triggeringLoad == null) {
this.triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2D);
}
if (this.triggeringBlackoutPercentage == null) {
this.triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999D);
}
Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, this.triggeringLoad.get(), this.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 = this.getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key);
}
}
} catch (Exception var8) {
logger.error("Error choosing server using zone aware logic for load balancer={}", this.name, var8);
}
if (server != null) {
return server;
} else {
logger.debug("Zone avoidance logic is not invoked.");
return super.chooseServer(key);
}
} else {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
}
最后发现是
应该调用的是yml里的服务名字。对应的defaltZone。而不是用IP来访问。因为我们负载均衡本来就是一个IP下的多个服务。
所以用服务名来区分就可以了。