一、编写示例

1.服务端

pom.xml

    <properties><java.version>1.8</java.version><spring-cloud.version>Finchley.SR2</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>0.2.1.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

2.提供者代码,就是一个普通的HTTP REST接口。

@SpringBootApplication
@EnableDiscoveryClient
@RestController
@Slf4j
public class ProviderApplication {public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}@GetMapping("provider")public String provider(){log.info("..................................");return "hello,provider";}
}

3.消费端

POM

<properties><java.version>1.8</java.version><spring-cloud.version>Finchley.SR2</spring-cloud.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId><exclusions><exclusion><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework.retry</groupId>-->
<!--            <artifactId>spring-retry</artifactId>-->
<!--        </dependency>--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--<dependency>--><!--<groupId>org.springframework.retry</groupId>--><!--<artifactId>spring-retry</artifactId>--><!--</dependency>--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>0.2.1.RELEASE</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

这里注意,如果不想要RetryTemplate,就去掉Retry的依赖,否则默认会引入的。

消费端代码

@SpringBootApplication
@Slf4j
@EnableDiscoveryClient
@RestController
public class ConsumeApplication {@AutowiredRestTemplate restTemplate;@Bean@LoadBalancedpublic RestTemplate getRestTemplate(){HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();clientHttpRequestFactory.setConnectTimeout(5 * 1000);clientHttpRequestFactory.setReadTimeout(5 * 1000);return new RestTemplate(clientHttpRequestFactory);}public static void main(String[] args) {SpringApplication.run(ConsumeApplication.class, args);}@GetMapping("test")public String getContent(){log.info("发起请求");return restTemplate.getForObject("http://provider/provider",String.class);}}

二、初始化自动配置代码分析

1.org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration,这个为RIBBON私有的
LoadBalancerClient,LoadBalancer,IRule对象的初始化。
@Configuration
@ConditionalOnClass({ IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {@Beanpublic SpringClientFactory springClientFactory() {SpringClientFactory factory = new SpringClientFactory();factory.setConfigurations(this.configurations);return factory;}@Bean@ConditionalOnMissingBean(LoadBalancerClient.class)public LoadBalancerClient loadBalancerClient() {return new RibbonLoadBalancerClient(springClientFactory());}@Bean@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {return new RibbonLoadBalancedRetryFactory(clientFactory);}

2.org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration为springcloud-common的对象自动初始化。

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {for (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, transformers);}@Configuration@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}

这里的 restTemplate只会取加了@LoadBalance注解的,因为@LoadBalance加上了@Qualifer,这个注解会有筛选过滤作用。

去除了RetryTemplate就会走默认,否则走重试balance.

1.首先创建LoadBalancerRequestFactory工厂对象。

2.创建LoadBalancerInterceptor对象

3.对RestTemplate列表对象,设置拦截器属性。

三、调用分析

1.RestTemplate.getObject方法开始。这个方法会调用doExecute

public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {   @Nullableprotected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {Assert.notNull(url, "URI is required");Assert.notNull(method, "HttpMethod is required");ClientHttpResponse response = null;try {ClientHttpRequest request = createRequest(url, method);if (requestCallback != null) {requestCallback.doWithRequest(request);}response = request.execute();handleResponse(url, method, response);return (responseExtractor != null ? responseExtractor.extractData(response) : null);}catch (IOException ex) {String resource = url.toString();String query = url.getRawQuery();resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource);throw new ResourceAccessException("I/O error on " + method.name() +" request for \"" + resource + "\": " + ex.getMessage(), ex);}finally {if (response != null) {response.close();}}}

2.createRequest方法,RestTemplate是继承于InterceptingHttpAccessor和HttpAccessor

 HttpAccessor
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {ClientHttpRequest request = getRequestFactory().createRequest(url, method);if (logger.isDebugEnabled()) {logger.debug("Created " + method.name() + " request for \"" + url + "\"");}return request;}

3.getRequestFactory方法又是来源于InterceptingHttpAccessor,这里会对原有httpRequestFactory工厂包装。

public ClientHttpRequestFactory getRequestFactory() {List<ClientHttpRequestInterceptor> 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();}}

public class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {private final List<ClientHttpRequestInterceptor> interceptors;/*** Create a new instance of the {@code InterceptingClientHttpRequestFactory} with the given parameters.* @param requestFactory the request factory to wrap* @param interceptors the interceptors that are to be applied (can be {@code null})*/public InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,@Nullable List<ClientHttpRequestInterceptor> interceptors) {super(requestFactory);this.interceptors = (interceptors != null ? interceptors : Collections.emptyList());}@Overrideprotected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);}}

所以最后的ClientHttpRequest为InterceptingClientHttpRequest

3.接着我们回到第一步,response = request.execute();因为已经创建好request,现在开始执行。

class InterceptingClientHttpRequest extends AbstractBufferingClientHttpRequest {private final ClientHttpRequestFactory requestFactory;private final List<ClientHttpRequestInterceptor> interceptors;private HttpMethod method;private URI uri;protected InterceptingClientHttpRequest(ClientHttpRequestFactory requestFactory,List<ClientHttpRequestInterceptor> interceptors, URI uri, HttpMethod method) {this.requestFactory = requestFactory;this.interceptors = interceptors;this.method = method;this.uri = uri;}@Overridepublic HttpMethod getMethod() {return this.method;}@Overridepublic String getMethodValue() {return this.method.name();}@Overridepublic URI getURI() {return this.uri;}@Overrideprotected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();return requestExecution.execute(this, bufferedOutput);}private class InterceptingRequestExecution implements ClientHttpRequestExecution {private final Iterator<ClientHttpRequestInterceptor> iterator;public InterceptingRequestExecution() {this.iterator = interceptors.iterator();}@Overridepublic ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {if (this.iterator.hasNext()) {ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();return nextInterceptor.intercept(request, body, this);}else {HttpMethod method = request.getMethod();Assert.state(method != null, "No standard HTTP method");ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));if (body.length > 0) {if (delegate instanceof StreamingHttpOutputMessage) {StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage) delegate;streamingOutputMessage.setBody(outputStream -> StreamUtils.copy(body, outputStream));}else {StreamUtils.copy(body, delegate.getBody());}}return delegate.execute();}}}}

InterceptingClientHttpRequest的execute会执行executeInternel方法,最后调用

InterceptingRequestExecution.execute

InterceptingRequestExecution.execute这个方法先会调用各种拦截器进行处理,处理完后,再会由拦截器重新调用这个对象的execute方法,做成责任链模式。最后调用缺省的HTTP代理请求工厂生成代理对象真正发起HTTP请求。

目前的拦截器就是从注册中心获取实例地址,替换URL.

4.接着进行LoadBalancerInterceptor.interropt方法。

public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {private LoadBalancerClient loadBalancer;private LoadBalancerRequestFactory requestFactory;public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {this.loadBalancer = loadBalancer;this.requestFactory = requestFactory;}public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {// for backwards compatibilitythis(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));}@Overridepublic ClientHttpResponse intercept(final HttpRequest request, final byte[] body,final ClientHttpRequestExecution execution) throws IOException {final URI originalUri = request.getURI();String serviceName = originalUri.getHost();return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));}
}

5.这里的loadBalancer就是RibbonLoadBalancerClient,所以调用这个进行URL替换。

public class RibbonLoadBalancerClient implements LoadBalancerClient {private SpringClientFactory clientFactory;public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {this.clientFactory = clientFactory;}@Overridepublic URI reconstructURI(ServiceInstance instance, URI original) {Assert.notNull(instance, "instance can not be null");String serviceId = instance.getServiceId();RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);URI uri;Server server;if (instance instanceof RibbonServer) {RibbonServer ribbonServer = (RibbonServer) instance;server = ribbonServer.getServer();uri = updateToSecureConnectionIfNeeded(original, ribbonServer);} else {server = new Server(instance.getScheme(), instance.getHost(), instance.getPort());IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);ServerIntrospector serverIntrospector = serverIntrospector(serviceId);uri = updateToSecureConnectionIfNeeded(original, clientConfig,serverIntrospector, server);}return context.reconstructURIWithServer(server, uri);}@Overridepublic ServiceInstance choose(String serviceId) {Server server = getServer(serviceId);if (server == null) {return null;}return new RibbonServer(serviceId, server, isSecure(server, serviceId),serverIntrospector(serviceId).getMetadata(server));}@Overridepublic <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {ILoadBalancer loadBalancer = getLoadBalancer(serviceId);Server server = getServer(loadBalancer);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);}

6.接着调用RibbonLoadBalancerClient.getLoadBalancer获取负载均衡器。

7.SpringClientFactory.getLoadBalancer

return getInstance(name, ILoadBalancer.class);这个会去容器工厂自动创建获取ILoadBalancer类。 

8.ribbonLoadBalancingHttpClient为实现类。

 package org.springframework.cloud.netflix.ribbon.apache;/*** @author Spencer Gibb*/
@Configuration
@ConditionalOnClass(name = "org.apache.http.client.HttpClient")
@ConditionalOnProperty(name = "ribbon.httpclient.enabled", matchIfMissing = true)
public class HttpClientRibbonConfiguration {
@Bean@ConditionalOnMissingBean(AbstractLoadBalancerAwareClient.class)@ConditionalOnMissingClass(value = "org.springframework.retry.support.RetryTemplate")public RibbonLoadBalancingHttpClient ribbonLoadBalancingHttpClient(IClientConfig config, ServerIntrospector serverIntrospector,ILoadBalancer loadBalancer, RetryHandler retryHandler, CloseableHttpClient httpClient) {RibbonLoadBalancingHttpClient client = new RibbonLoadBalancingHttpClient(httpClient, config, serverIntrospector);client.setLoadBalancer(loadBalancer);client.setRetryHandler(retryHandler);Monitors.registerObject("Client_" + this.name, client);return client;}

9.这个依赖于ribbonLoadBalancer,实际为这个类ZoneAwareLoadBalancer

package org.springframework.cloud.netflix.ribbon;/*** @author Dave Syer* @author Tim Ysewyn*/
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
@Bean@ConditionalOnMissingBeanpublic 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);}

10.上面那个类又依赖于IRULE,所以最后创建 ribbonRule,实现类为

ZoneAvoidanceRule

11.创建完RULE后,创建loadBalance

12.创建ribbonLoadBalancer

13.我们再回到第5步,    @Override
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        Server server = getServer(loadBalancer);,开始获取server

14.选择好server后开始执行。

15.回到LoadBalancerRequestFactory.apply

16.又回到InterceptingRequestExecution.execute

17.接下来开始替换URL

18。RibbonLoadBalancerClient.reconstructURI

19.

LoadBalancerContext.reconstructURIWithServer

20.url替换完毕,调用默认的HTTP代理对象发起请求,返回

21 请求结束,处理消息体转换,返回数据。

21.请求完毕。

四、服务实例获取和选择分析

1.我们先回到第三单,第9步,这个依赖于ribbonLoadBalancer,实际为这个类ZoneAwareLoadBalancer,这个为负载均衡器的选择服务实例逻辑。

2.ZoneAwareLoadBalancer主要为感知分区的实例选择,我们一般用不到。目前使用DynamicServerListLoadBalancer

AbstractLoadBalancer

AbstractLoadBalancer类的定义如下:

public abstract class AbstractLoadBalancer implements ILoadBalancer {public enum ServerGroup{ALL,STATUS_UP,STATUS_NOT_UP        }public Server chooseServer() {return chooseServer(null);}public abstract List<Server> getServerList(ServerGroup serverGroup);public abstract LoadBalancerStats getLoadBalancerStats();
}

关于这个类我说以下几点:
1. AbstractLoadBalancer实现了ILoadBalancer接口,但它是一个抽象类,它里边定义了一个关于服务实例的分组枚举类,包含了三种类型的服务:ALL表示所有服务,STATUS_UP表示正常运行的服务,STATUS_NOT_UP表示下线的服务。
2. chooseServer方法毫无疑问是用来选取一个服务实例,但是要怎么选这里并没有说,我们以后在它的实现类里边寻找选取策略。
3. getServerList方法用来获取某一个分组中所有的的服务实例。
4. getLoadBalancerStats方法用来获取LoadBalancerStats对象,LoadBalancerStats对象中保存了每一个服务的所有细节信息。

BaseLoadBalancer

BaseLoadBalancer是AbstractLoadBalancer的一个实现类,源码比较长我就不贴出来的,我们在这里主要来说说BaseLoadBalancer提供了哪些功能:

1. 首先这个类中有两个List集合中放的Server对象,一个List集合用来保存所有的服务实例,还有一个List集合用来保存当前有效的服务实例。
2. BaseLoadBalancer中定义了一个IPingStrategy,用来描述服务检查策略,IPingStrategy默认实现采用了SerialPingStrategy实现,SerialPingStrategy中的pingServers方法就是遍历所有的服务实例,一个一个发送请求,查看这些服务实例是否还有效,如果网络环境不好的话,这种检查策略效率会很低,如果我们想自定义检查策略的话,可以重写SerialPingStrategy的pingServers方法。
3. 在BaseLoadBalancer的chooseServer方法中(负载均衡的核心方法),我们发现最终调用了IRule中的choose方法来找到一个具体的服务实例,IRule是一个接口,在BaseLoadBalancer它的默认实现是RoundRobinRule类,RoundRobinRule类中采用了最常用的线性负载均衡规则,也就是所有有效的服务端轮流调用。
4. 在BaseLoadBalancer的构造方法中会启动一个PingTask,这个PingTask用来检查Server是否有效,PingTask的默认执行时间间隔为10秒。
5. markServerDown方法用来标记一个服务是否有效,标记方式为调用Server对象的setAlive方法设置isAliveFlag属性为false。
6. getReachableServers方法用来获取所有有效的服务实例列表。
7. getAllServers方法用来获取所有服务的实例列表。
8. addServers方法表示向负载均衡器中添加一个新的服务实例列表。

BaseLoadBalancer的功能大概就这么多。

DynamicServerListLoadBalancer

DynamicServerListLoadBalancer是BaseLoadBalancer的一个子类,在DynamicServerListLoadBalancer中对基础负载均衡器的功能做了进一步的扩展,我们来看看。

1. 首先DynamicServerListLoadBalancer类一开始就声明了一个变量serverListImpl,serverListImpl变量的类型是一个ServerList<T extends Server>,这里的泛型得是Server的子类,ServerList是一个接口,里边定义了两个方法:一个getInitialListOfServers用来获取初始化的服务实例清单;另一个getUpdatedListOfServers用于获取更新的服务实例清单。
2. ServerList接口有很多实现类,DynamicServerListLoadBalancer默认使用了DomainExtractingServerList类作为ServerList的实现,但是在DomainExtractingServerList的构造方法中又传入了DiscoveryEnabledNIWSServerList对象,查看源码发现最终两个清单的获取方式是由DiscoveryEnabledNIWSServerList类来提供的。
3. DomainExtractingServerList类中的obtainServersViaDiscovery方法是用来发现服务实例并获取的,obtainServersViaDiscovery方法的主要逻辑是这样:首先依靠EurekaClient从服务注册中心获取到具体的服务实例InstanceInfo列表,然后对这个列表进行遍历,将状态为UP的实例转换成DiscoveryEnabledServer对象并放到一个集合中,最后将这个集合返回。
4. DynamicServerListLoadBalancer中还定义了一个ServerListUpdater.UpdateAction类型的服务更新器,Spring Cloud提供了两种服务更新策略:一种是PollingServerListUpdater,表示定时更新;另一种是EurekaNotificationServerListUpdater表示由Eureka的事件监听来驱动服务列表的更新操作,默认的实现策略是第一种,即定时更新,定时的方式很简单,创建Runnable,调用DynamicServerListLoadBalancer中updateAction对象的doUpdate方法,Runnable延迟启动时间为1秒,重复周期为30秒。
5. 在更新服务清单的时候,调用了我们在第一点提到的getUpdatedListOfServers方法,拿到实例清单之后,又调用了一个过滤器中的方法进行过滤。过滤器的类型有好几种,默认是DefaultNIWSServerListFilter,这是一个继承自ZoneAffinityServerListFilter的过滤器,具有区域感知功能。即它会对服务提供者所处的Zone和服务消费者所处的Zone进行比较,过滤掉哪些不是同一个区域的实例。

综上,DynamicServerListLoadBalancer主要是实现了服务实例清单在运行期间的动态更新能力,同时提供了对服务实例清单的过滤功能。

ZoneAwareLoadBalancer

ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子类,ZoneAwareLoadBalancer的出现主要是为了弥补DynamicServerListLoadBalancer的不足。由于DynamicServerListLoadBalancer中并没有重写chooseServer方法,所以DynamicServerListLoadBalancer中负责均衡的策略依然是我们在BaseLoadBalancer中分析出来的线性轮询策略,这种策略不具备区域感知功能,这样当需要跨区域调用时,可能会产生高延迟。ZoneAwareLoadBalancer重写了setServerListForZones方法,该方法在其父类中的功能主要是根据区域Zone分组的实例列表,为负载均衡器中的LoadBalancerStats对象创建ZoneStats并存入集合中,ZoneStats是一个用来存储每个Zone的状态和统计信息。重写之后的setServerListForZones方法主要做了两件事:一件是调用getLoadBalancer方法来创建负载均衡器,同时创建服务选择策略;另一件是对Zone区域中的实例清单进行检查,如果对应的Zone下已经没有实例了,则将Zone区域的实例列表清空,防止节点选择时出现异常。

2.我们来看调用堆栈分析

首先回到第9步,自动配置生成

package org.springframework.cloud.netflix.ribbon;/*** @author Dave Syer* @author Tim Ysewyn*/
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
@Bean@ConditionalOnMissingBeanpublic 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);}

从这里可以看到,会依赖自动注入ServerList,IRule等。

package org.springframework.cloud.alibaba.nacos.ribbon;import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ServerList;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** integrated Ribbon by default* @author xiaojing*/
@Configuration
@ConditionalOnRibbonNacos
public class NacosRibbonClientConfiguration {@Bean@ConditionalOnMissingBeanpublic ServerList<?> ribbonServerList(IClientConfig config) {NacosServerList serverList = new NacosServerList();serverList.initWithNiwsConfig(config);return serverList;}
}

因为我们使用的是NACOS,所以会注入NacosServerList.

IRule使用的是RoundRobinRule.

最后生成ZoneAwareLoadBalancer

3.接着进入到ZoneAwareLoadBalancer的构造,这里主要看DynamicServerListLoadBalancer

重点在restOfInit方法。

package com.netflix.loadbalancer;/*** A LoadBalancer that has the capabilities to obtain the candidate list of* servers using a dynamic source. i.e. The list of servers can potentially be* changed at Runtime. It also contains facilities wherein the list of servers* can be passed through a Filter criteria to filter out servers that do not* meet the desired criteria.* * @author stonse* */
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);boolean isSecure = false;boolean useTunnel = false;// to keep track of modification of server listsprotected AtomicBoolean serverListUpdateInProgress = new AtomicBoolean(false);volatile ServerList<T> serverListImpl;volatile ServerListFilter<T> filter;protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {@Overridepublic void doUpdate() {updateListOfServers();}};protected volatile ServerListUpdater serverListUpdater;@Deprecatedpublic DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter) {this(clientConfig,rule,ping,serverList,filter,new PollingServerListUpdater());}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) {boolean primeConnection = this.isEnablePrimingConnections();// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()this.setEnablePrimingConnections(false);enableAndInitLearnNewServersFeature();updateListOfServers();if (primeConnection && this.getPrimeConnections() != null) {this.getPrimeConnections().primeConnections(getReachableServers());}this.setEnablePrimingConnections(primeConnection);LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());}

这个ServerListUpdater就是一个定时任务更新服务列表。

具体的动作为updateAction,即为DynamicServerListLoadBalancer的updateAction

protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {@Overridepublic void doUpdate() {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);
}

最后也是updateAllServerList去获取服务列表。

serverListImpl.getUpdatedListOfServers();就是从NacosServerList服务注册中心获取实例列表。

获取完后setServersList去设置服务列表。

至此,服务列表就获取到allServer,upServer了。

4.然后使用IRULE策略来选择服务,IRULE就会从绑定的LOADBALANCER取出所有的服务列表,然后去选择一个。

到这里选择完毕。

springcloud ribbon @LoadBalance负载均衡源码流程分析相关推荐

  1. springcloud ribbon 配置负载均衡策略以及自定义策略

    一.系统内置的策略有以下几种.  这个负载策略配置说白了就是让 Ribbon 这个客户端负载均衡器怎么进行访问服务提供者列表.是轮流访问?随机访问?权重?等. Ribbon 的负载均衡策略 策略类   ...

  2. springcloud ribbon实现负载均衡的时候,提示Request URI does not contain a valid hostname: htt...

    qq社区:541122375,群名springClould全家桶 问题描述:  org.springframework.web.util.NestedServletException: Request ...

  3. android 虚拟按键源码流程分析

    android 虚拟按键流程分析 今天来说说android 的虚拟按键的源码流程.大家都知道,android 系统的状态栏,虚拟按键,下拉菜单,以及通知显示,keyguard 锁屏都是在framewo ...

  4. CentOS 6.5 + Nginx 1.8.0 + PHP 5.6(with PHP-FPM) 负载均衡源码安装 之 (三)Nginx负载均衡配置...

    Nginx反向代理到单个PHP-FPM(PHP-FPM可位于不同机器) 0.首先,创建我们的网站根目录[注:须在PHP-FPM所在的那台机器创建](以后网站的代码放到此目录下): mkdir /opt ...

  5. eureka源码流程分析

    这是euraka官网的架构图 从上面图中可以看到eureka的功能 服务注册 服务续约 服务同步 服务下线 远程调用 一.服务注册 这个服务提供者需需要把自己的实例注册到注册中心中(就相当于相亲时把自 ...

  6. AQS 源码流程分析

    导读: 我们日常开发中,经常会碰到并发的场景,在 Java 中语言体系里,我们会想到 ReentrantLock.CountDownLatch.Semaphore 等工具,但你是否清楚它们内部的实现原 ...

  7. 技术宝典 | WebRTC ADM 源码流程分析

    导读: 本文主要基于 WebRTC release-72 源码及云信音视频团队积累的相关经验而成,主要分析以下问题: ADM(Audio Device Manager)的架构如何?ADM(Audio ...

  8. WebRTC ADM 源码流程分析

    导读: 本文主要基于 WebRTC release-72 源码及云信音视频团队积累的相关经验而成,主要分析以下问题: ADM(Audio Device Manager)的架构如何?ADM(Audio ...

  9. Android 9.0系统恢复出场设置源码流程分析

    前言 作为Framework层的开发人员,如果我们想让系统恢复出厂设置,一般有一下三种方式: 1.在[系统设置页面]进入[恢复出厂设置页面],点击[恢复出厂设置]按钮. 2.直接通过adb发送恢复出厂 ...

最新文章

  1. dede首页调用栏目内容{dedefield.content}的方法
  2. openstack对接华为存储
  3. RIA开发权威指南 基于JavaFX(赠品)
  4. SpringBoot集成Flowable_Jsite办理任务菜单报403
  5. 强烈推荐:给去美国的新生说几句(转载),超实用
  6. 阿里云搭建nacos
  7. 从Java中的串口读取文件
  8. python笔记记录神器 jupyter notebook
  9. python爬取豆瓣电影250_利用Python爬取豆瓣TOP250的电影
  10. 欢迎光临 Javen-Studio 新网址:http://javenstudio.org
  11. 群晖nas 文件服务器,群晖nas挂到云服务器上
  12. Linux中的进程、线程和文件描述符
  13. mybatisplus--getOne和逻辑删除问题详解
  14. 【Python】安装pip
  15. Linux 文本文件读取的七种方式
  16. 阿里的“传奇程序员”
  17. growup怎么读_grow up是什么意思_grow up怎么读_grow up翻译_用法_发音_词组_同反义词_向上生长-新东方在线英语词典...
  18. 宝塔部署Django
  19. VirtualBox虚拟机与主机互通,并且虚拟机又能上网配置
  20. Opus一款开源、免费、自由度高的有损音频编解码器

热门文章

  1. SAP系统上线后的变化
  2. SAP RFC-RFC概述
  3. 2021年有不加班的选择吗?哪些城市加班最严重?
  4. 双十一最新预测:这个连续多年夺第一的省份2020会被超越吗?
  5. python 执行shell命令行效率提升_在python脚本中执行shell命令的方法
  6. 全站仪数据导入电脑_三鼎762R系列全站仪的SD卡传输教程
  7. python hashlib模块_python3 hashlib模块
  8. 移动互联网时代的信息安全与防护_移动互联网时代,草根创业还有哪些机会?...
  9. c语言仿ce内存搜索工 源代码_C语言函数库:动态库和静态库优缺点比较
  10. python中的函数def和函数的参数