Spring Cloud Ribbon源码详解

目录

代码示例

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

代码示例

Spring Cloud Ribbon源码详解_第1张图片

通过服务发现,客户端可以获取某个服务的所有可用服务地址,此时我们需要客户端负载均衡,以便将客户端请求按照特定方式分摊到各个服务地址上

稍微改造一下之前的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

LB实现原理

我们从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 Cloud Ribbon源码详解_第2张图片

spring.factories配置中指定了AutoConfiguration类 

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration

RibbonAutoConfiguration类十分关键,Ribbon自动创建对象以及自动加载配置的逻辑都在这个类中

在上面的示例中我们使用了两种调用方式,分别是LoadBalancerClient+普通RestTemplate或者负载均衡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的构造

Spring Cloud Ribbon源码详解_第3张图片

从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();
    }
	......

Spring Cloud Ribbon源码详解_第4张图片

通过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请求
			}
		}
	}
	......

LoadBalancerClient实现原理

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");
	}
	......

Spring Cloud Ribbon源码详解_第5张图片

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)));
		......
	}
	......

Spring Cloud Ribbon源码详解_第6张图片

获取到LoadBalancer对象后,下一步是选择一个服务节点发送请求

Spring Cloud Ribbon源码详解_第7张图片

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中获取的

Ribbon修改LB策略

从上面的源码分析,我们有两种方式修改默认的路由规则,一个是显示定义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

Ribbon如何获取服务地址

我们已经知道了

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配置

Ribbon如何通过Eureka获取服务地址

项目引入了spring-cloud-starter-netflix-eureka-client,其代码结构如下

Spring Cloud Ribbon源码详解_第8张图片

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配置,服务恢复

你可能感兴趣的:(Spring,Cloud,Spring,Cloud,负载均衡,Load,Balance,Ribbon)