文章目录
- Ribbon核心接口
- IRule
- ServerList
- ServerListUpdater
- ServerListFilter
- IPing
- IPingStrategy
- ILoadBalancer
- AbstractLoadBalancer
- BaseLoadBalancer
- addServers
- chooseServer
- markServerDown
- getReachableServers
- getAllServers
- DynamicServerListLoadBalancer
- ZoneAwareLoadBalancer
- Spring-Cloud接口
- LoadBalancerClient
- RibbonLoadBalancerClient
- SpringClientFactory
- RibbonAutoConfiguration
- RibbonClientConfiguration
- Nacos相关配置
- RibbonNacosAutoConfiguration
- NacosRibbonClientConfiguration
工程版本
<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
接口中有Server
和ILoadBalancer
。Server
封装了注册到注册中心(如Nacos)的微服务信息。ILoadBalancer
是一个接口,用来获取注册到注册中心的某个微服务信息。
Ribbon
中共提供了很多负载均衡的策略实现,如com.netflix.loadbalancer.RoundRobinRule
、com.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
AbstractLoadBalancer
是ILoadBalancer
接口的抽象实现。
// 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();
}
PingTask
是BaseLoadBalancer
的内部类,用来执行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);
}
}
}
下面具体看看BaseLoadBalancer
对ILoadBalancer
接口的实现
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
继承了抽象类NamedContextFactory
,NamedContextFactory
抽象类里面有一个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();
}
}