Ribbon 负载均衡原理

工程版本

<spring-boot.version>2.2.6.RELEASE</spring-boot.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<spring-cloud.version>Hoxton.SR4</spring-cloud.version>

Ribbon是SpringCloud下的客户端负载均衡器,消费者在通过服务名调用服务时,需要通过Ribbon做负载均衡获取实际的服务调用地址。

Ribbon核心接口

IRule

IRule是负载均衡策略的抽象,如果需要自定义负载均衡策略就需要实现这个接口。

// com.netflix.loadbalancer.IRule
public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */
    // 从 ILoadBalancer 获取一个可用的服务实例
    // 这里的key一般是指服务名
    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

IRule接口中有ServerILoadBalancerServer封装了注册到注册中心(如Nacos)的微服务信息。ILoadBalancer是一个接口,用来获取注册到注册中心的某个微服务信息。

Ribbon中共提供了很多负载均衡的策略实现,如com.netflix.loadbalancer.RoundRobinRulecom.netflix.loadbalancer.RandomRule

ServerList

ServerList 用 于 获 取 服 务 实 例 列 表 和 更 新 的 服 务 实 例 列 表 。

// com.netflix.loadbalancer.ServerList
public interface ServerList<T extends Server> {
    // 用于获取初始化的服务实例清单
    public List<T> getInitialListOfServers();
    
    // 用于获取更新的服务实例清单
    public List<T> getUpdatedListOfServers();   
}

不 同 的 注 册 中 心 有 不 同 的 实 现 , 如 Nacos的com.alibaba.cloud.nacos.ribbon.NacosServerList

public class NacosServerList extends AbstractServerList<NacosServer> {
    
    private NacosDiscoveryProperties discoveryProperties;
    // 服务名
	private String serviceId;

	public NacosServerList(NacosDiscoveryProperties discoveryProperties) {
		this.discoveryProperties = discoveryProperties;
	}

	@Override
	public List<NacosServer> getInitialListOfServers() {
		return getServers();
	}

	@Override
	public List<NacosServer> getUpdatedListOfServers() {
		return getServers();
	}

	private List<NacosServer> getServers() {
		try {
			String group = discoveryProperties.getGroup();
            // 从 Nacos 注册中心获取服务列表
			List<Instance> instances = discoveryProperties.namingServiceInstance()
					.selectInstances(serviceId, group, true);
			return instancesToServerList(instances);
		}
		catch (Exception e) {
			throw new IllegalStateException(
					"Can not get service instances from nacos, serviceId=" + serviceId,
					e);
		}
	}
    
    // 将从Nacos获取出来出来的Instance转换为NacosServer
	private List<NacosServer> instancesToServerList(List<Instance> instances) {
		List<NacosServer> result = new ArrayList<>();
		if (CollectionUtils.isEmpty(instances)) {
			return result;
		}
		for (Instance instance : instances) {
			result.add(new NacosServer(instance));
		}

		return result;
	}

	public String getServiceId() {
		return serviceId;
	}
}

ServerListUpdater

ServerListUpdater 是服 务 刷 新 更 新 器 、 启 动 定 时 任 务 、 负 责 定 时 从 注 册 中 心 获 取 更 新 的 服 务 列 表 , 调 用com.netflix.loadbalancer.ServerList 实现更新。 具体实现类有 com.netflix.loadbalancer.PollingServerListUpdater,查看PollingServerListUpdater构造方法可以知道,默认情况下,每隔30 秒拉取一次。

public interface ServerListUpdater {
    // 对ServerList的具体更新操作
    public interface UpdateAction {
        void doUpdate();
    }
    
    // 启动服务更新器,传入的UpdateAction对象为更新操作的具体实现。
    void start(UpdateAction updateAction);
    
    // 停止服务更新器
    void stop();
    
    // 获取最近的更新时间戳
    String getLastUpdate();
    
    // 获取上一次更新到现在的时间间隔,单位为毫秒
    long getDurationSinceLastUpdateMs();
    
    // 获取错过的更新周期数
    int getNumberMissedCycles();
    
    // 获取核心线程数
    int getCoreThreads();
}

查看PollingServerListUpdater源码

public class PollingServerListUpdater implements ServerListUpdater {
    // 默认值为false
    private final AtomicBoolean isActive = new AtomicBoolean(false);
    
    // 初始化获取服务列表的延时时间
    private final long initialDelayMs;
    
    // 刷新服务列表的周期时间
    private final long refreshIntervalMs;
    
    private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000; // msecs;
    private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // msecs;
    
    // 定时任务
    private volatile ScheduledFuture<?> scheduledFuture;
    
    // 构造函数
    public PollingServerListUpdater() {
        this(LISTOFSERVERS_CACHE_UPDATE_DELAY, LISTOFSERVERS_CACHE_REPEAT_INTERVAL);
    }

    public PollingServerListUpdater(IClientConfig clientConfig) {
        this(LISTOFSERVERS_CACHE_UPDATE_DELAY, getRefreshIntervalMs(clientConfig));
    }

    public PollingServerListUpdater(final long initialDelayMs, final long refreshIntervalMs) {
        this.initialDelayMs = initialDelayMs;
        this.refreshIntervalMs = refreshIntervalMs;
    }
    
    private static long getRefreshIntervalMs(IClientConfig clientConfig) {
        // IClientConfig 中配置了 ServerListRefreshInterval ,则返回 IClientConfig 中的值
        // 否则返回 LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // msecs;
        return clientConfig.get(CommonClientConfigKey.ServerListRefreshInterval, LISTOFSERVERS_CACHE_REPEAT_INTERVAL);
    }
    
    // 启动定时任务会传入一个 UpdateAction 
    // UpdateAction 执行具体的任务
    @Override
    public synchronized void start(final UpdateAction updateAction) {
        // 启动时将 isActive 由 false 修改为true
        // 如果CAS失败,则说明定时任务已经启动了
        if (isActive.compareAndSet(false, true)) {
            // 创建一个线程
            final Runnable wrapperRunnable = new Runnable() {
                @Override
                public void run() {
                    if (!isActive.get()) {
                        if (scheduledFuture != null) {
                            scheduledFuture.cancel(true);
                        }
                        return;
                    }
                    try {
                        updateAction.doUpdate();
                        lastUpdated = System.currentTimeMillis();
                    } catch (Exception e) {
                        logger.warn("Failed one update cycle", e);
                    }
                }
            };
            // 创建定时任务
            // 初始化延迟时间initialDelayMs,默认值为1000,即1s
            // 刷新周期时间refreshIntervalMs,默认为30*1000,即30s
            scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                    wrapperRunnable,
                    initialDelayMs,
                    refreshIntervalMs,
                    TimeUnit.MILLISECONDS
            );
        } else {
            logger.info("Already active, no-op");
        }
    }

    // 暂停定时任务
    @Override
    public synchronized void stop() {
        // 启动时将 isActive 由 true 修改为 false
        // 如果CAS失败,则说明定时任务已经暂停了
        if (isActive.compareAndSet(true, false)) {
            if (scheduledFuture != null) {
                scheduledFuture.cancel(true);
            }
        } else {
            logger.info("Not active, no-op");
        }
    }
    
}

ServerListFilter

这是一个服务列表过滤器,传入服务列表,根据一些规则返回过滤后的服务列表实例。

// com.netflix.loadbalancer.ServerListFilter
public interface ServerListFilter<T extends Server> {

    public List<T> getFilteredListOfServers(List<T> servers);

}

IPing

// com.netflix.loadbalancer.IPing
public interface IPing {
    /**
     * Checks whether the given <code>Server</code> is "alive" i.e. should be
     * considered a candidate while loadbalancing
     * 
     */
    // 检查服务是否还存活
    public boolean isAlive(Server server);
}

IPingStrategy

// com.netflix.loadbalancer.IPingStrategy
public interface IPingStrategy {
    // 根据 IPing,检测服务列表是否还存活
    boolean[] pingServers(IPing ping, Server[] servers);
}

BaseLoadBalancer的内部类SerialPingStrategy实现了该接口

private static class SerialPingStrategy implements IPingStrategy {
    @Override
    public boolean[] pingServers(IPing ping, Server[] servers) {
        int numCandidates = servers.length;
        boolean[] results = new boolean[numCandidates];

        logger.debug("LoadBalancer:  PingTask executing [{}] servers configured", numCandidates);

        for (int i = 0; i < numCandidates; i++) {
            results[i] = false; /* Default answer is DEAD. */
            try {
                if (ping != null) {
                    results[i] = ping.isAlive(servers[i]);
                }
            } catch (Exception e) {
                logger.error("Exception while pinging Server: '{}'", servers[i], e);
            }
        }
        return results;
    }
}

ILoadBalancer

ILoadBalancer是实现负载均衡策略的抽象接口

// com.netflix.loadbalancer.ILoadBalancer
public interface ILoadBalancer {
    
    // 向负载均衡器中增加新的服务实例列表
	public void addServers(List<Server> newServers);
    
	// 获取一个合适的实例
	public Server chooseServer(Object key);
	
    // 标记某个服务实例暂停服务
	public void markServerDown(Server server);
	
    // 获取可用的服务实例
    // 该方法已经过时被废弃了
	@Deprecated
	public List<Server> getServerList(boolean availableOnly);

    // 获取可用的服务实例列表。
    public List<Server> getReachableServers();

    // 获取所有的服务实例列表。
	public List<Server> getAllServers();
}

AbstractLoadBalancer

AbstractLoadBalancerILoadBalancer接口的抽象实现。

// com.netflix.loadbalancer.AbstractLoadBalancer
public abstract class AbstractLoadBalancer implements ILoadBalancer {
    // 定义了一个关于服务实例的分组枚举类ServerGroup
    // ALL-所有服务实例、STATUS_UP-正常服务的实例、STATUS_NOT_UP-停止服务的实例;
    public enum ServerGroup{
        ALL,
        STATUS_UP,
        STATUS_NOT_UP        
    }
   
    public Server chooseServer() {
    	return chooseServer(null);
    }
    
    // 根据分组类型获取服务列表
    public abstract List<Server> getServerList(ServerGroup serverGroup);
    
    // LoadBalancerStats对象被用来存储负载均衡器中各个服务实例当前的属性和统计信息,
    // 这些信息非常有用,我们可以利用这些信息来观察负载均衡器的运行情况,
    // 同时这些信息也是用来制定负载均衡策略的重要依据。
    public abstract LoadBalancerStats getLoadBalancerStats();    
}

BaseLoadBalancer

BaseLoadBalancer类是Ribbon负载均衡器的基础实现类,在该类中定义了很多关于负载均衡器相关的基础内容

// com.netflix.loadbalancer.BaseLoadBalancer
public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware{
    // 默认负载均衡规则
    private final static IRule DEFAULT_RULE = new RoundRobinRule();
    
    // 默认的IPingStrategy策略,用来检测服务列表的每一个实例是否存活
    private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
    
    // 使用默认的负载均衡规则
    protected IRule rule = DEFAULT_RULE;
    
    // 使用默认的IPingStrategy
    protected IPingStrategy pingStrategy = DEFAULT_PING_STRATEGY;

    // 默认为null,需要在构造时注入它的具体实现
    // 用来检查服务是否还存活的规则
    protected IPing ping = null;
    
    // 存储所有服务实例的列表
    @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> allServerList = Collections
            .synchronizedList(new ArrayList<Server>());
    
    // 存储正常服务的实例列表
    @Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> upServerList = Collections
            .synchronizedList(new ArrayList<Server>());
}

查看BaseLoadBalancer默认的构造函数

public BaseLoadBalancer() {
    this.name = DEFAULT_NAME;
    // 默认为null
    // 可以在有参构造函数注入它的具体实现
    this.ping = null;
    setRule(DEFAULT_RULE);
    // 启动ping任务,检测服务列表的服务是否还存活
    setupPingTask();
    lbStats = new LoadBalancerStats(DEFAULT_NAME);
}

setupPingTask() 用来启动ping任务,用来检测服务列表的服务是否还存活

void setupPingTask() {
    // 这里是判断是否跳过ping任务
    // 如果 ping 为 null 或者 ping 的类型为 DummyPing 则需要跳过ping任务
    if (canSkipPing()) {
        return;
    }
    if (lbTimer != null) {
        lbTimer.cancel();
    }
    lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
                                       true);
    // pingIntervalSeconds = 10
    // 这里是创建定时任务,每隔10秒执行一次
    lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
    // 这里是强制快速执行ping任务
    forceQuickPing();
}

PingTaskBaseLoadBalancer的内部类,用来执行ping任务

class PingTask extends TimerTask {
    public void run() {
        try {
            new Pinger(pingStrategy).runPinger();
        } catch (Exception e) {
            logger.error("LoadBalancer [{}]: Error pinging", name, e);
        }
    }
}

Pinger也是BaseLoadBalancer的内部类,这里是ping任务的具体实现

class Pinger {

    private final IPingStrategy pingerStrategy;

    public Pinger(IPingStrategy pingerStrategy) {
        this.pingerStrategy = pingerStrategy;
    }

    public void runPinger() throws Exception {
        // 这里判断是否存在ping任务,如果已经存在则不需要执行
        // 相当于尝试加锁如果加锁成功则继续执行
        if (!pingInProgress.compareAndSet(false, true)) { 
            return; // Ping in progress - nothing to do
        }

        // 开始执行ping任务
       
        Server[] allServers = null;
        boolean[] results = null;

        Lock allLock = null;
        Lock upLock = null;

        try {
            allLock = allServerLock.readLock();
            // 对 allServerList 加读锁
            allLock.lock();
            // 将 allServerList 复制到 allServers
            allServers = allServerList.toArray(new Server[allServerList.size()]);
            // 解锁
            allLock.unlock();
            
            int numCandidates = allServers.length;
            // 
            results = pingerStrategy.pingServers(ping, allServers);
            
            // 用来保存新的可用服务列表
            final List<Server> newUpList = new ArrayList<Server>();
            // 用来保存服务状态发生改变的服务
            final List<Server> changedServers = new ArrayList<Server>();

            for (int i = 0; i < numCandidates; i++) {
                // 服务的当前状态
                boolean isAlive = results[i];
                Server svr = allServers[i];
                // 服务的旧状态
                boolean oldIsAlive = svr.isAlive();
                // 设置服务的当前状态
                svr.setAlive(isAlive);
                
                // 如果服务状态发生改变,则添加到 changedServers 中
                if (oldIsAlive != isAlive) {
                    changedServers.add(svr);
                    logger.debug("LoadBalancer [{}]:  Server [{}] status changed to {}", 
                                 name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
                }
                
                // 如果服务还存活,则保存到 newUpList 
                if (isAlive) {
                    newUpList.add(svr);
                }
            }
            upLock = upServerLock.writeLock();
            upLock.lock();
            // 更新  upServerList
            upServerList = newUpList;
            upLock.unlock();
            // 将服务状态发生变化的服务列表
            notifyServerStatusChangeListener(changedServers);
        } finally {
            // 前面设置成了true,因此这里需要设置回false
            pingInProgress.set(false);
        }
    }
}

下面具体看看BaseLoadBalancerILoadBalancer接口的实现

addServers
@Override
public void addServers(List<Server> newServers) {
    if (newServers != null && newServers.size() > 0) {
        try {
            ArrayList<Server> newList = new ArrayList<Server>();
            newList.addAll(allServerList);
            newList.addAll(newServers);
            setServersList(newList);
        } catch (Exception e) {
            logger.error("LoadBalancer [{}]: Exception while adding Servers", name, e);
        }
    }
}

将原本已经维护的所有服务列表allServerList和新传入的服务实例清单newServers都加入到newList中,然后通过调用setServersList函数对newList进行处理。

chooseServer

获取一个合适的服务实例,根据IRule来获取合适的服务。

@Override
public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}
markServerDown

暂停某个服务实例

@Override
public void markServerDown(Server server) {
    if (server == null || !server.isAlive()) {
        return;
    }

    logger.error("LoadBalancer [{}]:  markServerDown called on [{}]", name, server.getId());
    server.setAlive(false);
    // forceQuickPing();

    notifyServerStatusChangeListener(singleton(server));
}
getReachableServers

获取可用的服务实例列表

@Override
public List<Server> getReachableServers() {
    return Collections.unmodifiableList(upServerList);
}
getAllServers

获取所有的服务实例列表

@Override
public List<Server> getAllServers() {
    return Collections.unmodifiableList(allServerList);
}

DynamicServerListLoadBalancer

DynamicServerListLoadBalancer类继承BaseLoadBalancer类,它是对BaseLoadBalancer的扩展。服务列表的获取与刷新就是通过这个类实现的。

// com.netflix.loadbalancer.DynamicServerListLoadBalancer
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    
    // 用于获取初始化服务实例列表和更新的服务实例列表
    // 不同的注册中心有不同的实现
    // 如 Nacos 的 com.alibaba.cloud.nacos.ribbon.NacosServerList
    // 前面已经分析过这个接口
    volatile ServerList<T> serverListImpl;
    
    // 根据一些规则返回过滤后的服务列表实例
    volatile ServerListFilter<T> filter;
    
    // 这是一个服务列表更新器
    // 执行ServerListUpdater.start(UpdateAction updateAction)时将这个属性作为参数传进去
    protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
        @Override
        public void doUpdate() {
            // 从注册中心获取服务列表
            // 内部调用的是serverListImpl.getUpdatedListOfServers()
            updateListOfServers();
        }
    };
    
    // 是服务刷新更新器、负责定时从注册中心获取更新的服务列表 
    // 调用 com.netflix.loadbalancer.ServerList 实现更新
    // 该接口的默认实现是 com.netflix.loadbalancer.PollingServerListUpdater
    // 前面已经分析过这个接口
    protected volatile ServerListUpdater serverListUpdater;
    
    // 构造函数
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> 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);
    }
    
    void restOfInit(IClientConfig clientConfig) {
        //省略部分代码......
        // 这里面会开启刷新服务列表的定时任务
        enableAndInitLearnNewServersFeature();
        
        // 从注册中心获取服务列表
        updateListOfServers();
        
        //省略部分代码......
    }
    
    // 从注册中心获取服务列表并更新本地服务列表
    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            // 获取所有的服务列表
            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);
    }
}

ZoneAwareLoadBalancer

ZoneAwareLoadBalancer是对DynamicServerListLoadBalancer的扩展。ZoneAwareLoadBalancer重写了BaseLoadBalancer.chooseServer(Object key)的方法,主要是根据Zone(区域)来实现负载均衡,这个方法会根绝一些条件来判断是否根据Zone来实现负载均衡,如果不根据Zone实现负载则会调用父类BaseLoadBalancer.chooseServer(Object key)方法来实现。

多区域部署的情况下会有一定的性能问题,而该负载均衡器则可以避免这样的问题。

Spring-Cloud接口

LoadBalancerClient

这是一个负载均衡客户端

// org.springframework.cloud.client.loadbalancer
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);

}

public interface ServiceInstanceChooser {
    // 根据传入的serviceId,从负载均衡器中挑选一个实例
	ServiceInstance choose(String serviceId);
}

RibbonLoadBalancerClient

RibbonLoadBalancerClient实现了LoadBalancerClient接口

主要分析choose(String serviceId)方法

public class RibbonLoadBalancerClient implements LoadBalancerClient {

	private SpringClientFactory clientFactory;

	public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
		this.clientFactory = clientFactory;
	}
    
    @Override
	public ServiceInstance choose(String serviceId) {
		return choose(serviceId, null);
	}
    
    public ServiceInstance choose(String serviceId, Object hint) {
		Server server = getServer(getLoadBalancer(serviceId), hint);
		if (server == null) {
			return null;
		}
		return new RibbonServer(serviceId, server, isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
	}
    
    // 根据 serviceId 获取 ILoadBalancer
    protected ILoadBalancer getLoadBalancer(String serviceId) {
		return this.clientFactory.getLoadBalancer(serviceId);
	}
    
    // 通过ILoadBalancer.chooseServer(Object key)来获取服务实例
    protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}
}

可以发现RibbonLoadBalancerClient是通过serviceId来获取对应的ILoadBalancer,再根据ILoadBalancer来获取服务实例。ILoadBalancer是一个负载均衡器,这个接口是Ribbon的。下面来分析如果获取ILoadBalancer

SpringClientFactory

用来创建客户端负载均衡器的工厂类,该工厂会为每一个不同名的Ribbon客户端生成不同的Spring应用上下文。

public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {
	public SpringClientFactory() {
        // 这里传入了 RibbonClientConfiguration
        // SpringClientFactory 为每个客户端创建Spring上下文时会注入这个配置类
        // 主要注意的是,这个类是在最后注入的。
		super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
	}
}

SpringClientFactory继承了抽象类NamedContextFactoryNamedContextFactory抽象类里面有一个contexts属性,这个属性用来存放不同服务的Ribbon客户端的Spring应用上下文。

private Map<String, AnnotationConfigApplicationContext> contexts = new ConcurrentHashMap();

每一个AnnotationConfigApplicationContext都有自己的ILoadBalancer实例,这是因为不同的客户端可以设置不同的负载均衡策略。

查看SpringClientFactory.getLoadBalancer(String name)源码

public ILoadBalancer getLoadBalancer(String name) {
    return getInstance(name, ILoadBalancer.class);
}

@Override
public <C> C getInstance(String name, Class<C> type) {
    C instance = super.getInstance(name, type);
    if (instance != null) {
        return instance;
    }
    IClientConfig config = getInstance(name, IClientConfig.class);
    return instantiateWithConfig(getContext(name), type, config);
}

继续查看super.getInstance(name, type)可以发现,里面会先判断contexts里面是否存在这个客户端的Spring上下文?

如果不存在,则会创建一个AnnotationConfigApplicationContext这个应用上下文的parent为当前启动类的应用上下文

如果存在,则从上下文中获取ILoadBalancer类型的bean实例。

当我们需要修改负载均衡策略时,需要使用@RibbonClient注解来配置,如下所示。需要注意的是,MyRibbonRule这个类不能放在启动类所有包及其子包下面,否则就会使得所有的客户端都采用这里面的负载均衡策略。

@RibbonClient(name = "app-provider", configuration = {MyRibbonRule.class})

那么它在创建服务名为app-provider的应用上下文时,也会注入MyRibbonRule.class这个配置类,根据前面的分析可以知道,RibbonClientConfiguration这个配置类是在MyRibbonRule之后注入的。

RibbonAutoConfiguration

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

    // 这里是获取所有 @RibbonClient 注解信息
    // SpringClientFactory 为客户端创建Spring上下文是会根据这些配置信息注入Bena实例
	@Autowired(required = false)
	private List<RibbonClientSpecification> configurations = new ArrayList<>();

	@Autowired
	private RibbonEagerLoadProperties ribbonEagerLoadProperties;

	@Bean
	public SpringClientFactory springClientFactory() {
		SpringClientFactory factory = new SpringClientFactory();
        // 将所有的 @RibbonClient 注解信息保存到 SpringClientFactory 
        // SpringClientFactory 为客户端创建Spring应用上下文是会根据这些配置信息创建Bean实例
		factory.setConfigurations(this.configurations);
		return factory;
	}

	@Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}
    // 省略......
}

RibbonClientConfiguration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
		RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {

	/**
	 * Ribbon client default connect timeout.
	 */
	public static final int DEFAULT_CONNECT_TIMEOUT = 1000;

	/**
	 * Ribbon client default read timeout.
	 */
	public static final int DEFAULT_READ_TIMEOUT = 1000;

	/**
	 * Ribbon client default Gzip Payload flag.
	 */
	public static final boolean DEFAULT_GZIP_PAYLOAD = true;

	@RibbonClientName
	private String name = "client";

	// TODO: maybe re-instate autowired load balancers: identified by name they could be
	// associated with ribbon clients

	@Autowired
	private PropertiesFactory propertiesFactory;

    // 如果不存在 IClientConfig,则创建默认的 IClientConfig
	@Bean
	@ConditionalOnMissingBean
	public IClientConfig ribbonClientConfig() {
		DefaultClientConfigImpl config = new DefaultClientConfigImpl();
		config.loadProperties(this.name);
		config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
		config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
		config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD);
		return config;
	}

    // 如果不存在 IRule,则创建默认的 IRule
	@Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) {
		if (this.propertiesFactory.isSet(IRule.class, name)) {
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}

    // 如果不存在 IPing,则创建默认的 IPing
	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) {
		if (this.propertiesFactory.isSet(IPing.class, name)) {
			return this.propertiesFactory.get(IPing.class, config, name);
		}
		return new DummyPing();
	}

    // 如果不存在 ServerList,则创建默认的 ServerList
	@Bean
	@ConditionalOnMissingBean
	@SuppressWarnings("unchecked")
	public ServerList<Server> ribbonServerList(IClientConfig config) {
		if (this.propertiesFactory.isSet(ServerList.class, name)) {
			return this.propertiesFactory.get(ServerList.class, config, name);
		}
		ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
		serverList.initWithNiwsConfig(config);
		return serverList;
	}

    // 如果不存在 ServerListUpdater,则创建默认的 ServerListUpdater
	@Bean
	@ConditionalOnMissingBean
	public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
		return new PollingServerListUpdater(config);
	}

    // 如果不存在 ILoadBalancer,则创建默认的 ILoadBalancer
	@Bean
	@ConditionalOnMissingBean
	public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
			ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
			IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}

    // 如果不存在 ServerListFilter,则创建默认的 ServerListFilter
	@Bean
	@ConditionalOnMissingBean
	@SuppressWarnings("unchecked")
	public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
		if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
			return this.propertiesFactory.get(ServerListFilter.class, config, name);
		}
		ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
		filter.initWithNiwsConfig(config);
		return filter;
	}
	// 省略一部分......
}

Nacos相关配置

我使用的是Nacos作为注册中心,所以查看Nacos相关配置

RibbonNacosAutoConfiguration

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnBean(SpringClientFactory.class)
@ConditionalOnRibbonNacos
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureAfter(RibbonAutoConfiguration.class)
// SpringClientFactory 为客户端创建Spring上下文时会注入这个配置类
// 注入时间要早于 RibbonClientConfiguration 
@RibbonClients(defaultConfiguration = NacosRibbonClientConfiguration.class)
public class RibbonNacosAutoConfiguration {

}

NacosRibbonClientConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {

	@Autowired
	private PropertiesFactory propertiesFactory;
    
    // 网容器中注入 NacosServerList
	@Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config,
			NacosDiscoveryProperties nacosDiscoveryProperties) {
		if (this.propertiesFactory.isSet(ServerList.class, config.getClientName())) {
			ServerList serverList = this.propertiesFactory.get(ServerList.class, config,
					config.getClientName());
			return serverList;
		}
		NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties);
		serverList.initWithNiwsConfig(config);
		return serverList;
	}

	@Bean
	@ConditionalOnMissingBean
	public NacosServerIntrospector nacosServerIntrospector() {
		return new NacosServerIntrospector();
	}

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值