簡析Ribbon原始碼
本篇不糾結原始碼細節,原始碼走讀可以參看Spring Cloud原始碼分析(二)Ribbon和深入理解Ribbon之原始碼解析。Ribbon這一塊原始碼的設計模式非常值得借鑑學習,符合開閉原則,對擴充套件開放,對修改封閉。所以大致看下原始碼這塊的程式設計的思路,看Ribbon怎麼把各個功能整合在一起的。 首先從Ribbon中一個非常重要的元件LoadBalancerClient開始:
public interface LoadBalancerClient extends ServiceInstanceChooser { <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException; <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException; URI reconstructURI(ServiceInstance instance, URI original); }
execute方法會使用從LoadBalancer獲取的ServiceInstance執行請求。LoadBalancerClient是一個介面,它繼承ServiceInstanceChooser:
public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}
ServiceInstanceChooser介面,只有一個方法,用來根據serviceId來獲取ServiceInstance。
來看LoadBalancerClient的實現類RibbonLoadBalancerClient,看重要的部分:
public class RibbonLoadBalancerClient implements LoadBalancerClient { //...略 @Override public ServiceInstance choose(String serviceId) { Server server = getServer(serviceId); if (server == null) { return null; } return new RibbonServer(serviceId, server, isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server)); } @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); } protected Server getServer(String serviceId) { return getServer(getLoadBalancer(serviceId)); } protected Server getServer(ILoadBalancer loadBalancer) { if (loadBalancer == null) { return null; } return loadBalancer.chooseServer("default"); // TODO: better handling of key } protected ILoadBalancer getLoadBalancer(String serviceId) { return this.clientFactory.getLoadBalancer(serviceId); } //...略 }
作為負載均衡的客戶端,RibbonLoadBalancerClient的execute()方法先找到ILoadBalancer,然後ILoadBalancer選擇出服務例項,最後用找到的例項去進行請求。
來看ILoadBalancer,ILoadBalancer是實現軟體負載均衡的一個介面:
public interface ILoadBalancer {
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
它提供可供選擇的服務註冊列表資訊。到這裡要思考下,既然有了ILoadBalancer,那麼還需要有負載均衡策略來決定某次請求的服務例項,還要檢查服務例項是否有效。因此ILoadBalancer的子類BaseLoadBalancer來實現這些,當然不止這些。
public class BaseLoadBalancer extends AbstractLoadBalancer implements
PrimeConnections.PrimeConnectionListener, IClientConfigAware {
//...略
protected IRule rule = DEFAULT_RULE;
protected IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY;
protected IPing ping = null;
// 例項化方式
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
String ruleClassName = (String) clientConfig
.getProperty(CommonClientConfigKey.NFLoadBalancerRuleClassName);
String pingClassName = (String) clientConfig
.getProperty(CommonClientConfigKey.NFLoadBalancerPingClassName);
IRule rule;
IPing ping;
try {
rule = (IRule) ClientFactory.instantiateInstanceWithClientConfig(
ruleClassName, clientConfig);
ping = (IPing) ClientFactory.instantiateInstanceWithClientConfig(
pingClassName, clientConfig);
} catch (Exception e) {
throw new RuntimeException("Error initializing load balancer", e);
}
initWithConfig(clientConfig, rule, ping);
}
/* Returns either null, or "server:port/servlet" */
public String choose(Object key) {
if (rule == null) {
return null;
} else {
try {
Server svr = rule.choose(key);
return ((svr == null) ? null : svr.getId());
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server", name, e);
return null;
}
}
}
}
可以看到類屬性IRule、IPing。IRule決定了按照什麼策略去選擇服務例項,IPing決定了怎麼判斷服務可用。看下IRule:
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {
private ILoadBalancer lb;
@Override
public void setLoadBalancer(ILoadBalancer lb){
this.lb = lb;
}
@Override
public ILoadBalancer getLoadBalancer(){
return lb;
}
}
IRule有很多實現類,預設是RoundRobinRule。IRule擁有ILoadBalancer屬性,以便IRule拿到所有的可用Server。 IPing是用來來判斷該server是否有響應,從而判斷該server是否可用。
在BaseLoadBalancer 的子類DynamicServerListLoadBalancer這個擴充套件類中添加了ServerList、ServerListFilter,以獲取所有的服務例項和新增過濾策略。 ServerList是獲取所有的server資訊的介面:
public interface ServerList<T extends Server> {
public List<T> getInitialListOfServers();
public List<T> getUpdatedListOfServers();
}
ServerListFilter介面用於過濾某些server列表:
public interface ServerListFilter<T extends Server> {
public List<T> getFilteredListOfServers(List<T> servers);
}
從Eureka獲取server時,ServerList的實現類為DiscoveryEnabledNIWSServerList。