前两文写了ribbon的根据传参进行自定义负载均衡,今天要解释一下,为什么能够这样做。将这几天的源码查看,做一个记录,以备日后分享。
当我们使用了@LoadBalanced注解后,就具有了负载均衡的能力。现在开始解密之旅。
一,@LoadBalanced
@LoadBalanced
@bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
发生了一些神秘的变化,一切开始不同。
看一下这个注解
/**
* Annotation to mark a RestTemplate or WebClient bean to be configured to use a
* LoadBalancerClient.
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
注释说,使restTemplate 使用了LoadBalancerClient。重点说明一下已集成了@Qualifier注解,这说明LoadBalanced注解也具有了排重的能力。使用者可以通过LoadBalanced注解或@Qualifier,找到包含此注解的对象。
我们通过Alt+f7看下哪里使用了@LoadBalanced注解。
在loadBalancerAutoConfiguration类里找到了
二,loadBalancerAutoConfiguration
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
。。。
这个是指,将所有带有@LoadBalanced(内置@Qualifier)的bean 装配过来。
再看LoadBalancerAutoConfiguration的静态内部类LoadBalancerInterceptorConfig
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
可以看到restTemplateCustomizer方法,将上文中restTemplate增加一个拦截器loadBalancerInterceptor。
那我们看下loadBalancerInterceptor类具体做了什么。
三,loadBalancerInterceptor
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
。。。。
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null,
"Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
}
在此拦截器中,使用this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution))。可以看到是查出serviceName,如service1。交由loadBalancer.execute执行。
那么就要看LoadBalancerClient类了
四,LoadBalancerClient
LoadBalancerClient是个接口,那么具体是使用哪个类做的bean呢,我们在自动装配类xxAutoConfiguration找一下。
果然,在RibbonAutoConfiguration类中找到该bean
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
所以,在不存在其他LoadBalancerClient的情况下,使用的是RibbonLoadBalancerClient。
五,RibbonLoadBalancerClient
这是一个今天的重点类之一
public class RibbonLoadBalancerClient implements LoadBalancerClient {
private SpringClientFactory clientFactory;
。。。
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
//获取一个loadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
//获取真正的server
Server server = getServer(loadBalancer, hint);
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);
}
。。。
//从LoadBalancer里挑选server
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");
}
其中这行代码 ILoadBalancer loadBalancer = getLoadBalancer(serviceId);会通过一个工厂获取到一个ILoadBalancer,其中代码较多,不是本次重点。先知道可以获取一个已注册的ILoadBalancer即可。
那么究竟是哪个ILoadBalancer的实现呢?再次使用alt+f7,找xxautoConfiguration类。发现生成在RibbonClientConfiguration类。
六,RibbonClientConfiguration
public class RibbonClientConfiguration {
@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;
}
@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;
}
@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();
}
@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);
}
此类中提供了ILoadBalancer,IRule,IClientConfig几个bean的默认实现。
其中IRule采用了ZoneAvoidanceRule,
ILoadBalancer最终采用了 ZoneAwareLoadBalancer。
那么回到loadBalancer.chooseServer(hint != null ? hint : “default”),我们就要看一下,ZoneAwareLoadBalancer里的具体实现。
七,ZoneAwareLoadBalancer
public class ZoneAwareLoadBalancer<T extends Server> extends DynamicServerListLoadBalancer<T> {
@Override
public Server chooseServer(Object key) {
if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
。。。
看到默认只有一个zone时,转给了父类 super.chooseServer(key);
进入父类BaseLoadBalancer
八,BaseLoadBalancer
public class BaseLoadBalancer extends AbstractLoadBalancer implements
PrimeConnections.PrimeConnectionListener, IClientConfigAware {
protected IRule rule = DEFAULT_RULE;
。。。
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
//通过Irule和key选择server
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
....
如此便进入了Irule里。
上文我们说到,在RibbonClientConfiguration里,默认装配了一个Irule,即ZoneAvoidanceRule。所以,此时我们就要看ZoneAvoidanceRule里的实现了。
九,ZoneAvoidanceRule
public class ZoneAvoidanceRule extends PredicateBasedRule {
private static final Random random = new Random();
private CompositePredicate compositePredicate;
public ZoneAvoidanceRule() {
super();
ZoneAvoidancePredicate zonePredicate = new ZoneAvoidancePredicate(this);
AvailabilityPredicate availabilityPredicate = new AvailabilityPredicate(this);
compositePredicate = createCompositePredicate(zonePredicate, availabilityPredicate);
}
private CompositePredicate createCompositePredicate(ZoneAvoidancePredicate p1, AvailabilityPredicate p2) {
return CompositePredicate.withPredicates(p1, p2)
.addFallbackPredicate(p2)
.addFallbackPredicate(AbstractServerPredicate.alwaysTrue())
.build();
}
。。。
@Override
public AbstractServerPredicate getPredicate() {
return compositePredicate;
}
继承自PredicateBasedRule,目前我们仅需关注最后一个方法,getPredicate(),这是一个重写方法。
而ZoneAvoidanceRule中并没有实现choose()方法,所以我们需要往父类PredicateBasedRule 查找
十,PredicateBasedRule
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
public abstract AbstractServerPredicate getPredicate();
@Override
public Server choose(Object key) {
ILoadBalancer lb = getLoadBalancer();
Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
if (server.isPresent()) {
return server.get();
} else {
return null;
}
}
}
这是一个抽象类,确实有我们要找的choose(key)方法,可以注意到,getPredicate()是一个抽象方法,由子类提供实现。
Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
注意这句里面的lb.getAllServers(),这行代码很显然是要获取到所有server,然后再使用compositePredicate.chooseRoundRobinAfterFiltering选择一个server。
chooseRoundRobinAfterFiltering中实现了依次选取server的逻辑。
到这里,Irule路由选择server的事情已经说完了。
----------------中场休息分割线--------------
下一个问题就是server从哪来?
显然是这里,lb.getAllServers(),那么lb我们上文说到,默认使用的是ZoneAwareLoadBalancer,而ZoneAwareLoadBalancer的继承关系是:ZoneAwareLoadBalancer–>DynamicServerListLoadBalancer–>BaseLoadBalancer–>AbstractLoadBalancer.
getAllServers()方法定义在父类BaseLoadBalancer中,我们再来看BaseLoadBalancer
十一,BaseLoadBalancer
public class BaseLoadBalancer extends AbstractLoadBalancer implements
PrimeConnections.PrimeConnectionListener, IClientConfigAware {
@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> allServerList = Collections
.synchronizedList(new ArrayList<Server>());
@Override
public List<Server> getAllServers() {
return Collections.unmodifiableList(allServerList);
}
...
可以看到BaseLoadBalancer仅仅是将allServerList返回,那么什么时候初始化和更新的呢,我们看下子类DynamicServerListLoadBalancer
十二,DynamicServerListLoadBalancer
这又是今天的重点类。
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
volatile ServerList<T> serverListImpl;
//构造方法,注意最后调用了restOfInit(clientConfig)
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);
}
//同样在 initWithNiwsConfig中调用了restOfInit(clientConfig)
public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
initWithNiwsConfig(clientConfig);
}
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
try {
super.initWithNiwsConfig(clientConfig);
String niwsServerListClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.NIWSServerListClassName,
DefaultClientConfigImpl.DEFAULT_SEVER_LIST_CLASS);
ServerList<T> niwsServerListImpl = (ServerList<T>) ClientFactory
.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
this.serverListImpl = niwsServerListImpl;
if (niwsServerListImpl instanceof AbstractServerList) {
AbstractServerListFilter<T> niwsFilter = ((AbstractServerList) niwsServerListImpl)
.getFilterImpl(clientConfig);
niwsFilter.setLoadBalancerStats(getLoadBalancerStats());
this.filter = niwsFilter;
}
String serverListUpdaterClassName = clientConfig.getPropertyAsString(
CommonClientConfigKey.ServerListUpdaterClassName,
DefaultClientConfigImpl.DEFAULT_SERVER_LIST_UPDATER_CLASS
);
this.serverListUpdater = (ServerListUpdater) ClientFactory
.instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
restOfInit(clientConfig);
} catch (Exception e) {
throw new RuntimeException(
"Exception while initializing NIWSDiscoveryLoadBalancer:"
+ clientConfig.getClientName()
+ ", niwsClientConfig:" + clientConfig, e);
}
}
//重点方法,实现了多个重要功能
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
//启动定时器,默认30s执行依次updateListOfServers(),用来更新serverList。默认使用PollingServerListUpdater
enableAndInitLearnNewServersFeature();
//调用内部方法,更新serverList
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
//更新serverList
@VisibleForTesting
public void updateListOfServers() {
List<T> servers = new ArrayList<T>();
if (serverListImpl != null) {
//调用serverListImpl中的getUpdatedListOfServers实现更新
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);
}
@Override
public void setServersList(List lsrv) {
super.setServersList(lsrv);
List<T> serverList = (List<T>) lsrv;
Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
for (Server server : serverList) {
// make sure ServerStats is created to avoid creating them on hot
// path
getLoadBalancerStats().getSingleServerStat(server);
String zone = server.getZone();
if (zone != null) {
zone = zone.toLowerCase();
List<Server> servers = serversInZones.get(zone);
if (servers == null) {
servers = new ArrayList<Server>();
serversInZones.put(zone, servers);
}
servers.add(server);
}
}
setServerListForZones(serversInZones);
}
我们来看下流程,首先两个构造方法调用了restOfInit()方法,在其中执行了updateListOfServers()方法,在其中调用了一个serverListImpl获取了所有servers,然后又调用了updateAllServerList(servers),在updateAllServerList(servers)方法中调用setServersList(List lsrv)方法,最终在其中调用父类方法super.setServersList(lsrv),将获得到的servers,都传给了父类,由父类加载进allServerList等列表,等待开头getAllServers()的调用。
这样默认Loadbalancer即ZoneAwareLoadBalancer,就将自己获取到的serverList贡献给,ZoneAvoidanceRule中lb.getServers()使用了,再由ZoneAvoidanceRule提供的路由规则选取一个,返回给RibbonLoadBalancerClient,并在其中进行名称到ip+port的转换。这样一个完美的robbin负载均衡就走完了。
刚才为了梳理流程,我跳过的一个细节,DynamicServerListLoadBalancer中serverListImpl获取server的具体过程。
serverListImpl是通过构造函数注入进来的,那就要看下有没有相应的实现呢?
十三,serverListImpl的具体实现
在 RibbonClientConfiguration我找到了这样一个默认bean
@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;
}
可以看到,默认使用的ConfigurationBasedServerList,产生serverList,这个类允许使用配置文件产生serverList,如在yml中定义:
spring.application.name=shop-order
shop-product.ribbon.listOfServers=http://localhost:8001,http://localhost:8002
就会解析成具有8001,8002的两个server的serverList。
终于走完啦!
十四,最终流程梳理
首先,在loadBalancerAutoConfiguration中,所有被@LoadBalanced注释的restTemplate bean都会被加入loadBalancerInterceptor,并在loadBalancerInterceptor中对原请求做负载均衡转换。
其次,loadBalancerInterceptor使用默认提供的RibbonLoadBalancerClient执行serverList的获取。而在RibbonLoadBalancerClient中注入默认实现的IRule:ZoneAvoidanceRule进行负载均衡,使用ZoneAwareLoadBalancer作为默认的loadbalancer,获取所有的由ZoneAwareLoadBalancer从默认yml配置读过来的serverList。
最后由DynamicServerListLoadBalancer中引入的PollingServerListUpdater,负责对serverList的定时更新。
综上,由loadBalancerInterceptor插入拦截,由RibbonLoadBalancerClient封装调用IRule和loadBalancer,由loadBalancer的实现类ZoneAwareLoadBalancer负责从配置中读取的serverList,由IRule的实现类ZoneAvoidanceRule执行选择server。一个完整的robbin负载均衡流程就结束了。
十五,可扩展性
由于所有bean在xxAutoConfiguration中都是@ConditionalOnMissingBean类型的,所以可扩展性非常好。
比如引入NocosClient的时候,ServerList就被置换为Nocos的ServerList实现,从NacosServer获取列表。而引入Eureka client便会从Eureka获取服务列表。
再比如我们可以自定义路由方式,只需要添加我们自己的IRule类型的bean。正如我上篇文章ribbon按参数路由,实现灰度发布功能
可扩展性非常强。
码字不易,请给点赞哦!