目录
代码示例
LB实现原理
负载均衡RestTemplate实现原理
LoadBalancerClient实现原理
Ribbon修改LB策略
Ribbon如何获取服务地址
Ribbon如何通过Eureka获取服务地址
Java版本:1.8
Spring版本:5.1.8.RELEASE
Spring Boot版本:2.1.6.RELEASE
Spring Cloud版本:Greenwich.SR1
通过服务发现,客户端可以获取某个服务的所有可用服务地址,此时我们需要客户端负载均衡,以便将客户端请求按照特定方式分摊到各个服务地址上
稍微改造一下之前的Test-Client(代码参考https://blog.csdn.net/a19881029/article/details/100585120),由于spring-cloud-starter-netflix-eureka-client中包含Ribbon相关依赖(见如下pom依赖)
org.springframework.cloud
spring-cloud-starter-netflix-ribbon
com.netflix.ribbon
ribbon-eureka
因此不需要修改pom文件
修改RestTemplateConfig在普通RestTemplate的基础上增加负载均衡RestTemplate
package com.sean;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* Created by seanzou on 2019/9/10.
*/
@Configuration
public class RestTemplateConfig {
@Bean(name = "normalTemplate")
public RestTemplate normalTemplate(ClientHttpRequestFactory clientHttpRequestFactory){
return new RestTemplate(clientHttpRequestFactory);
}
@Bean(name = "loadBalanceTemplate")
@LoadBalanced
public RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory){
return new RestTemplate(clientHttpRequestFactory);
}
@Bean
public ClientHttpRequestFactory clientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);//ms
factory.setConnectTimeout(5000);//ms
return factory;
}
}
修改TestService并使用LoadBalancerClient替换之前的DiscoveryClient,通过LoadBalancerClient+普通RestTemplate或者负载均衡RestTemplate两种方式请求服务
package com.sean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
/**
* Created by seanzou on 2019/9/24.
*/
public class TestLoadBalancerRequest implements LoadBalancerRequest {
private String path;
private RestTemplate restTemplate;
@Override
public String apply(ServiceInstance instance) throws Exception {
String host = instance.getHost();
Integer port = instance.getPort();
ResponseEntity responseEntity = restTemplate.getForEntity("http://" + host + ":" + port + path, String.class);
if(responseEntity != null && responseEntity.getStatusCode() != null &&
responseEntity.getStatusCode().is2xxSuccessful()){
String resp = (String)responseEntity.getBody();
return resp;
}
return null;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
public void setRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
}
package com.sean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
/**
* Created by seanzou on 2019/7/10.
*/
@Component
public class TestService {
@Autowired
private RestTemplate normalTemplate;
@Autowired
private RestTemplate loadBalanceTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
public void test() {
System.out.println("start loadBalancerClient querying");
TestLoadBalancerRequest req = new TestLoadBalancerRequest();
req.setPath("/name");
req.setRestTemplate(normalTemplate);
for(int i = 0 ; i < 10 ; i++){
try {
String resp = loadBalancerClient.execute("test-service", req);
System.out.println(resp);
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
System.out.println("start restTemplate querying");
for(int i = 0 ; i < 10 ; i++){
ResponseEntity responseEntity = loadBalanceTemplate.getForEntity("http://test-service/name", String.class);
if(responseEntity != null && responseEntity.getStatusCode() != null &&
responseEntity.getStatusCode().is2xxSuccessful()){
String resp = (String)responseEntity.getBody();
System.out.println(resp);
}
}
}
}
启动Test-Client后访问http://localhost:8090/test以调用测试代码,后端日志输出如下
start loadBalancerClient querying
sean
sean two
sean
sean two
sean
sean two
sean
sean two
sean
sean two
start restTemplate querying
sean
sean two
sean
sean two
sean
sean two
sean
sean two
sean
sean two
我们从pom文件开始
spring-cloud-starter-netflix-eureka-client依赖spring-cloud-starter-netflix-ribbon
spring-cloud-starter-netflix-ribbon依赖spring-cloud-netflix-ribbon
spring-cloud-netflix-ribbon的代码结构如下
spring.factories配置中指定了AutoConfiguration类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
RibbonAutoConfiguration类十分关键,Ribbon自动创建对象以及自动加载配置的逻辑都在这个类中
在上面的示例中我们使用了两种调用方式,分别是LoadBalancerClient+普通RestTemplate或者负载均衡RestTemplate两种方式请求服务,下面我们兵分两路
RibbonAutoConfiguration类中有如下注解
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
我们观察一下LoadBalancerAutoConfiguration类
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
//参考LoadBalanced注解定义
//@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
//@Retention(RetentionPolicy.RUNTIME)
//@Documented
//@Inherited
//@Qualifier 这个很关键,使用Qualifier注解定义的注解,可以当做Qualifier使用
//public @interface LoadBalanced {}
//这里相当于Autowired+Qualifier
//所有被LoadBalanced注解修饰的RestTemplate都会被自动装配到restTemplates中
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
//RetryTemplate在spring-retry包中,这个包需要单独引入
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
//LoadBalancerInterceptor的创建依赖于LoadBalancerClient
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
......
所有使用LoadBalanced注解修饰的RestTemplate,都会自动装配到LoadBalancerAutoConfiguration类的restTemplates属性中统一进行管理
SmartInitializingSingleton最先被构造,由于SmartInitializingSingleton的创建依赖于RestTemplateCustomizer,并且工程中没有引入spring-retry包,因此引发LoadBalancerInterceptorConfig静态内部类的解析,LoadBalancerInterceptorConfig中最先被构造的是LoadBalancerInterceptor,由于LoadBalancerInterceptorConfig的构造依赖LoadBalancerRequestFactory,因此会触发LoadBalancerRequestFactory的构造
从RestTemplateCustomizer类的构造过程来看,每个LoadBalanced注解修饰的RestTemplate都被设置了一个LoadBalancerInterceptor,我们看看LoadBalancerInterceptor的实现
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
@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);
//这里的serviceName是从uri中取的,也就是test-service
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
......
接下来看看RestTemplate是如何发送请求的
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
public ResponseEntity getForEntity(String url, Class responseType,
Object... uriVariables) throws RestClientException {
RequestCallback requestCallback = this.acceptHeaderRequestCallback(responseType);
ResponseExtractor> responseExtractor =
this.responseEntityExtractor(responseType);
return (ResponseEntity)nonNull(this.execute(url, HttpMethod.GET, requestCallback,
responseExtractor, uriVariables));
}
@Nullable
public T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor responseExtractor, Object... uriVariables)
throws RestClientException {
URI expanded = this.getUriTemplateHandler().expand(url, uriVariables);
return this.doExecute(expanded, method, requestCallback, responseExtractor);
}
@Nullable
protected T doExecute(URI url, @Nullable HttpMethod method,
@Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor responseExtractor) throws RestClientException {
......
ClientHttpRequest request = this.createRequest(url, method);
response = request.execute();
}
......
通过RequestFactory对象创建请求对象
public abstract class HttpAccessor {
private ClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
public ClientHttpRequestFactory getRequestFactory() {
return this.requestFactory;
}
protected ClientHttpRequest createRequest(URI url, HttpMethod method)
throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
......
return request;
}
......
RestTemplate中是否设置了拦截器,返回InterceptingClientHttpRequestFactory
否则返回ClientHttpRequestFactory
public abstract class InterceptingHttpAccessor extends HttpAccessor {
// 向RestTemplate中添加的拦截器都保存在这个属性中
private final List interceptors = new ArrayList<>();
public List getInterceptors() {
return this.interceptors;
}
// 由于interceptors不为空,这里返回的是InterceptingClientHttpRequestFactory
// 所有拦截器都由RestTemplate传递给了RequestFactory
@Override
public ClientHttpRequestFactory getRequestFactory() {
List interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
factory = new InterceptingClientHttpRequestFactory(
super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
......
通过RequestFactory创建请求
public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
......
@Override
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod,
ClientHttpRequestFactory requestFactory) {
// 所有拦截器又经由RequestFactory传递给Request
return new InterceptingClientHttpRequest(requestFactory, this.interceptors,
uri, httpMethod);
}
......
RestTemplate如果设置了拦截器,则使用拦截器处理请求
结合LoadBalancerInterceptor类的intercept方法,可以发现RestTemplate本质上也是使用LoadBalancerClient发送请求
class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {
......
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator iterator;
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
return nextInterceptor.intercept(request, body, this);
}
else {
//发送普通http请求
}
}
}
......
RibbonAutoConfiguration类中会检测是否显示创建了LoadBalancerClient,如果没有则自动创建
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
示例中我们没有定义自己的LoadBalancerClient,因此程序中使用的是RibbonLoadBalancerClient,创建RibbonLoadBalancerClient唯一需要指定的参数就是SpringClientFactory,我们来看看这个类的构建过程
public class SpringClientFactory extends NamedContextFactory {
public SpringClientFactory() {
super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
}
......
RibbonClientConfiguration类仅仅作为参数,并没有解析RibbonClientConfiguration中的配置信息 (调用时解析创建)
public abstract class NamedContextFactory
implements DisposableBean, ApplicationContextAware {
......
private Class> defaultConfigType;
public NamedContextFactory(Class> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;//RibbonClientConfiguration.class
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;//ribbon.client.name
}
......
再看下RibbonClientConfiguration类,这个类十分重要
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
@Import({ HttpClientConfiguration.class, OkHttpRibbonConfiguration.class,
RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class })
public class RibbonClientConfiguration {
......
@RibbonClientName
private String name = "client";
@Autowired
private PropertiesFactory propertiesFactory;
@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;
}
//ILoadBalancer的创建依赖于IRule
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList serverList, ServerListFilter 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);
}
......
}
如此多的ConditionalOnMissingBean注解,你一定明白了,所有默认对象及配置都是在这里定义的,包括IClientConfig、IRule、IPing、ServerList、ServerListUpdater、ILoadBalancer、ServerListFilter、RibbonLoadBalancerContext、RetryHandler、ServerIntrospector
其中有两个有意思的点
一,name属性由RibbonClientName注解修饰,RibbonClientName注解定义如下
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Value("${ribbon.client.name}")
public @interface RibbonClientName {}
name的值由配置文件中ribbon.client.name配置定义,如果没有ribbon.client.name配置,默认值为client
二,设置了一些配置参数,根据是否设置参数,决定构建自定义对象还是默认对象
这里className的值是test-service.ribbon.NFLoadBalancerRuleClassName,原因看下面分析
public class PropertiesFactory {
@Autowired
private Environment environment; //profile+property文件中的配置
private Map classToProperty = new HashMap<>();
public PropertiesFactory() {
classToProperty.put(ILoadBalancer.class, "NFLoadBalancerClassName");
classToProperty.put(IPing.class, "NFLoadBalancerPingClassName");
classToProperty.put(IRule.class, "NFLoadBalancerRuleClassName");
classToProperty.put(ServerList.class, "NIWSServerListClassName");
classToProperty.put(ServerListFilter.class, "NIWSServerListFilterClassName");
}
public boolean isSet(Class clazz, String name) {
return StringUtils.hasText(getClassName(clazz, name));
}
public String getClassName(Class clazz, String name) {
if (this.classToProperty.containsKey(clazz)) {
String classNameProperty = this.classToProperty.get(clazz);
//按照示例的代码,没有显示设置ribbon.client.name
//如果构建IRule对象
//这里取值看起来应该是client.ribbon.NFLoadBalancerRuleClassName
//实际上却是test-service.ribbon.NFLoadBalancerRuleClassName
String className = environment
.getProperty(name + "." + NAMESPACE + "." + classNameProperty);
return className;
}
return null;
}
......
示例没有显示定义IRule类型的bean,也没有设置NFLoadBalancerRuleClassName参数,因此系统自动创建ZoneAvoidanceRule,同理,系统以ZoneAvoidanceRule为参数自动创建了ZoneAwareLoadBalancer
我们使用loadBalancerClient.execute方法发送请求,我们看看RibbonLoadBalancerClient类中的实现方式
public class RibbonLoadBalancerClient implements LoadBalancerClient {
//根据示例代码,这里serviceId=test-service
@Override
public T execute(String serviceId, LoadBalancerRequest request)
throws IOException {
return execute(serviceId, request, null);
}
public T execute(String serviceId, LoadBalancerRequest request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
......
return execute(serviceId, ribbonServer, request);
}
@Override
public T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest request) throws IOException {
......
T returnVal = request.apply(serviceInstance);
......
}
//返回系统自动创建的ZoneAwareLoadBalancer
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
......
我们再回到SpringClientFactory类,可见调用时才会真正创建对象
public class SpringClientFactory extends NamedContextFactory {
//根据示例代码,这里name=test-service
public ILoadBalancer getLoadBalancer(String name) {
return getInstance(name, ILoadBalancer.class);
}
@Override
public C getInstance(String name, Class type) {
C instance = super.getInstance(name, type);
if (instance != null) {
return instance;
}
......
}
......
会为每一个服务构建单独的Context,并且将ribbon.client.name的值替换为服务名称
因此,当接下来判断是否为该服务配置了特定的LoadBalancer时,查看是否有test-service.ribbon.NFLoadBalancerRuleClassName配置项
public abstract class NamedContextFactory
implements DisposableBean, ApplicationContextAware {
private final String propertyName;
public NamedContextFactory(Class> defaultConfigType, String propertySourceName,
String propertyName) {
this.defaultConfigType = defaultConfigType;
this.propertySourceName = propertySourceName;
this.propertyName = propertyName;//ribbon.client.name
}
//name=test-service
public T getInstance(String name, Class type) {
AnnotationConfigApplicationContext context = getContext(name);
if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
type).length > 0) {
return context.getBean(type);
}
return null;
}
protected AnnotationConfigApplicationContext getContext(String name) {
if (!this.contexts.containsKey(name)) {
synchronized (this.contexts) {
if (!this.contexts.containsKey(name)) {
this.contexts.put(name, createContext(name));
}
}
}
return this.contexts.get(name);
}
//this.propertyName=ribbon.client.name
//name=test-service
protected AnnotationConfigApplicationContext createContext(String name) {
......
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
this.propertySourceName,
Collections.singletonMap(this.propertyName, name)));
......
}
......
获取到LoadBalancer对象后,下一步是选择一个服务节点发送请求
ZoneAwareLoadBalancer类中的chooseServer方法最终会调用ZoneAwareLoadBalancer类的父类BaseLoadBalancer中的chooseServer方法(BaseLoadBalancer类源码中设置了DEFAULT_RULE = new RoundRobinRule)
public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnectionListener, IClientConfigAware {
......
private static final IRule DEFAULT_RULE = new RoundRobinRule();
public Server chooseServer(Object key) {
......
return this.rule.choose(key); //系统自动创建的ZoneAvoidanceRule
......
}
......
ZoneAvoidanceRule类中没有实现自己的choose方法,因此这里调用的是ZoneAvoidanceRule类的父类PredicateBasedRule中的choose方法
public Server choose(Object key) {
ILoadBalancer lb = this.getLoadBalancer();
Optional server = this.getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
return server.isPresent()?(Server)server.get():null;
}
从代码中可以看出:
1,默认的负载均衡策略是轮询
2,服务列表是从ILoadBalancer中获取的
从上面的源码分析,我们有两种方式修改默认的路由规则,一个是显示定义IRule的实现类
@Bean
public IRule rule(){
return new RandomRule();
}
另一种是修改Spring Cloud配置,添加如下配置,注意test-service为服务名称
test-service.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
两种修改方式的直接结果如下
start loadBalancerClient querying
sean
sean two
sean
sean two
sean
sean two
sean two
sean
sean two
sean two
start restTemplate querying
sean two
sean
sean two
sean two
sean two
sean two
sean
sean two
sean
sean
我们已经知道了
1,ServerList和ILoadBalancer默认定义在RibbonClientConfiguration类中
2,并且ServerList是从ILoadBalancer中获取的
我们回到RibbonClientConfiguration类
@Bean
@ConditionalOnMissingBean
@SuppressWarnings("unchecked")
public ServerList 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;
}
@Bean
@ConditionalOnMissingBean
public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
ServerList serverList, ServerListFilter 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);
}
ServerList与IRule和ILoadBalancer的构建过程相同,优先使用test-service.ribbon.NIWSServerListClassName配置
如果该配置不存在,则创建ConfigurationBasedServerList类
public class ConfigurationBasedServerList extends AbstractServerList {
private IClientConfig clientConfig;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
this.clientConfig = clientConfig;
}
@Override
public List getUpdatedListOfServers() {
String listOfServers = clientConfig.get(CommonClientConfigKey.ListOfServers); // listOfServers
return derive(listOfServers);
}
protected List derive(String value) {
List list = Lists.newArrayList();
if (!Strings.isNullOrEmpty(value)) {
for (String s: value.split(",")) {
list.add(new Server(s.trim()));
}
}
return list;
}
}
ConfigurationBasedServerList将被用来构建ILoadBalancer,我们看下ZoneAwareLoadBalancer
// serverList = ConfigurationBasedServerList
public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
IPing ping, ServerList serverList, ServerListFilter filter,
ServerListUpdater serverListUpdater) {
super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}
再看下ZoneAwareLoadBalancer类的父类DynamicServerListLoadBalancer
// serverList = ConfigurationBasedServerList
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
ServerList serverList, ServerListFilter filter,
ServerListUpdater serverListUpdater) {
......
this.serverListImpl = serverList; // serverListImpl = ConfigurationBasedServerList
......
restOfInit(clientConfig);
}
void restOfInit(IClientConfig clientConfig) {
......
updateListOfServers();
......
}
public void updateListOfServers() {
......
// ConfigurationBasedServerList.getUpdatedListOfServers()
servers = serverListImpl.getUpdatedListOfServers();
......
}
可见如果没有配置test-service.ribbon.NIWSServerListClassName,则使用listOfServers配置
项目引入了spring-cloud-starter-netflix-eureka-client,其代码结构如下
spring.factories配置中指定了AutoConfiguration类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
RibbonEurekaAutoConfiguration类的定义,如果Ribbon和Eureka都是可用的,则Ribbon设置基于Eureka的配置
/**
* Spring configuration for configuring Ribbon defaults to be Eureka based if Eureka
* client is enabled.
*
* @author Dave Syer
* @author Biju Kunjummen
*/
@Configuration
@EnableConfigurationProperties
// Ribbon和Eureka都是可用的
@ConditionalOnRibbonAndEurekaEnabled
// 先配置Ribbon
@AutoConfigureAfter(RibbonAutoConfiguration.class)
// 基于Eureka的Ribbon默认配置
@RibbonClients(defaultConfiguration = EurekaRibbonClientConfiguration.class)
public class RibbonEurekaAutoConfiguration {}
优先使用test-service.ribbon.NIWSServerListClassName配置,如果没有该配置,通过Eureka获取服务地址
public class EurekaRibbonClientConfiguration {
......
@Bean
@ConditionalOnMissingBean
public ServerList> ribbonServerList(IClientConfig config,
Provider eurekaClientProvider) {
if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
return this.propertiesFactory.get(ServerList.class, config, serviceId);
}
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
config, eurekaClientProvider);
DomainExtractingServerList serverList = new DomainExtractingServerList(
discoveryServerList, config, this.approximateZoneFromHostname);
return serverList;
}
服务地址的整体优先级是:
1,test-service.ribbon.NIWSServerListClassName(该配置存在)
2,Eureka(引入Eureka相关依赖,并且Eureka可用)
3,listOfServers(该配置存在)
创建CustomerServerList
package com.sean;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractServerList;
import com.netflix.loadbalancer.Server;
import java.util.LinkedList;
import java.util.List;
/**
* Created by seanzou on 2019/11/1.
*/
public class CustomerServerList extends AbstractServerList {
private List servers = new LinkedList<>();
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
Server server = new Server("localhost", 8080);
servers.add(server);
}
@Override
public List getInitialListOfServers() {
return servers;
}
@Override
public List getUpdatedListOfServers() {
return servers;
}
}
并在配置文件中添加如下配置
test-service.ribbon.NIWSServerListClassName=com.sean.CustomerServerList
无论是否引用spring-cloud-starter-netflix-eureka-client,都会使用CustomerServerList,说明test-service.ribbon.NIWSServerListClassName配置的优先级是最高的
引入spring-cloud-starter-netflix-eureka-client,去掉test-service.ribbon.NIWSServerListClassName配置,程序可正常运行
关闭Eureka-Server,程序异常,提示无法找到test-service服务实例
删除spring-cloud-starter-netflix-eureka-client引用,去掉test-service.ribbon.NIWSServerListClassName配置,程序异常,提示无法找到test-service服务实例
test-service.ribbon.listOfServers=localhost:8080
添加listOfServers配置,服务恢复