本篇不纠结源码细节,源码走读可以参看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。