1. 程式人生 > >簡析Ribbon原始碼

簡析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。