文章首发:Spring Cloud Ribbon 入门和实战
Ribbon 是什么
Ribbon 是一个客户端负载均衡器,它赋予了利用一些摆布 HTTP 与 TCP 行为的能力,这里的负载平衡是客户端的负载平衡,也有人称为后端负载平衡是过程内负载平衡的一种。Ribbon 是 SpringCloud 生态里的不可短少的组件,有了它,是个服务的横向扩大更加不便了。此外想 Feign 和 Zuul 默认是集成了 Ribbon。
Ribbon 是 Neflix 开源的一个组件,目前 Ribbon 早已进入保护状态,然而就目前的状况来看,Spring Cloud Netflix 的一些组件还是能够应用。
Spring Cloud Loadbalancer 是 Spring Cloud 社区开源的组件,目标是代替进入保护状态的 Ribbon,然而 Loadbalancer 还是有很长的一段路要走。
Ribbon 入门
因为客户端负载平衡须要从注册核心获取服务列表,所以须要集成注册核心。
创立父级工程 cloud-ribbon-practice
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring.cloud-version>Hoxton.SR3</spring.cloud-version>
</properties>
<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>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
创立注册核心 cloud-eureka-server
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
配置文件 application.xml
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);
}
}
创立源服务工程 cloud-ribbon-hello-b1、cloud-ribbon-hello-b2、cloud-ribbon-hello-b3
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置文件 application.yml
b1
spring:
application:
name: ribbon-service-b
server:
port: 7777
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
b2
spring:
application:
name: ribbon-service-b
server:
port: 7778
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
b3
spring:
application:
name: ribbon-service-b
server:
port: 7779
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
b1、b2、b3 的 controller
@RestController
public class OrderController {@Value("${server.port}")
private Integer port;
@Value("${spring.application.name}")
private String name;
@GetMapping("/test")
public String add() {return "this service name is" + name + "and port is" + port;}
}
b1、b2、b3 的启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceB1Application {public static void main(String[] args) {SpringApplication.run(ServiceB1Application.class, args);
}
}
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceB2Application {public static void main(String[] args) {SpringApplication.run(ServiceB2Application.class, args);
}
}
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceB3Application {public static void main(String[] args) {SpringApplication.run(ServiceB3Application.class, args);
}
}
创立服务调用者 cloud-ribbon-hello-a
spring-cloud-starter-netflix-eureka-client
曾经集成了 ribbon。不须要额定引入,间接应用即可。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
配置文件 application.yml
spring:
application:
name: ribbon-hello-a
server:
port: 7776
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
启动类,RestTemplate 应用了@LoadBalanced
,这样 RestTemplate 就开启了 ribbon 的负载平衡了。
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceAApplication {public static void main(String[] args) {SpringApplication.run(ServiceAApplication.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {return new RestTemplate();
}
}
controller
@RestController
@RequestMapping("ribbon")
public class TestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/test")
public String test(){String body = restTemplate.getForEntity("http://RIBBON-SERVICE-B/test", String.class).getBody();
return body;
}
}
测试,应用 postman 拜访几次:http://localhost:7776/ribbon/…
Ribbon 实战
从上一节能够看到,开启负载平衡只需通过 @LoadBalanced
注解即可。负载平衡中又很多的负载平衡策略,如轮询(Round Robin)、权重(Weight)、ip_hash 等。这些丰盛的策略让咱们在构建利用的时候,有很多抉择的余地,能够依据理论的业务场景抉择最合适的策略。
在 Ribbon 中一共提供了 7 中负载平衡策略:
策略类 | 命名 | 形容 |
---|---|---|
RandomRule | 随机策略 | 随机抉择 Server |
RoundRobinRule | 轮询策略 | 按程序抉择 Server |
RetryRule | 重试策略 | 在一个配置时间段内当抉择 Server 不胜利,则始终尝试抉择一个可用的 Server |
BestAvailableRule | 最低并发策略 | 卓哥考查 Server,如果 Server 断路器关上,则疏忽,再抉择其中并发连贯最低的 Server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤始终连贯失败并标记为 circuit tripped 的 Server,过滤掉那些高并发连贯的 Server(active connections 超过配置的阈值) |
响应工夫加权策略 | 曾经被弃用,作用同 WeightedResponseTimeRule | |
WeightedResponseTimeRule | 响应工夫加权策略 | 依据 Server 的响应工夫调配权重,响应工夫越长,权重越低,被选中的概率就越低。响应工夫越短,权重越高,被抉择到的概率越高 |
ZoneAvoidanceRule | 区域衡量策略 | 综合判断 Server 所在区域的性能和 Server 的可用性轮询抉择 Server,并且判断一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 Server |
在下面的入门案例中 Ribbon 的默认负载平衡策略是轮询策略,
Ribbon 自定义配置负载平衡策略
全局配置
应用 Ribbon 时配置全局的负载平衡策略,须要加一个配置类。改配置类须要被 @ComponentScan
扫描到能力全局失效。
@Configuration
public class GlobalRuleConfig {
@Bean
public IRule ribbonRule() {return new RandomRule();
}
}
下面配置了随机的策略,屡次拜访 http://localhost:7776/ribbon/…
基于 @RibbonClient
或@RibbonClients
注解的配置
配置类,留神:编写自定义配置类,须要特地留神的是官网文档明确给出了正告:这个自定义配置类不能放在 @ComponentScan 所扫描的包以及其子包下(即不能放在主启动类所在的包及其子包下,因而咱们须要新建一个包来放该配置类),否则咱们自定义的这个配置类就会被所有的 Ribbon 客户端所共享,也就达不到特殊化定制的目标了
@Configuration
public class AnnoRuleConfig {
@Bean
public IRule ribbonRule() {return new RandomRule();
}
}
启动类的配置
@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(name = "ribbon-service-b", configuration = AnnoRuleConfig.class)
public class ServiceAApplication {public static void main(String[] args) {SpringApplication.run(ServiceAApplication.class, args);
}
@LoadBalanced
@Bean
public RestTemplate restTemplate() {return new RestTemplate();
}
}
通过 @RibbonClient
指定某个服务的负载平衡策略,其余没有被指定的,就是用默认的负载平衡策略。该注解能够把其余的配置类作为另外一个 IOC 容器导入到利用中,相当于加载了两个齐全不相干的 Spring 的 beans 配置文件,此时利用中会有两个 IOC 容器。
@RibbonClient(name = "RIBBON-SERVICE-B", configuration = AnnoRuleConfig.class)
也能够应用一下的形式,指定多个服务的负载平衡策略
@RibbonClients(value = {@RibbonClient(name = "RIBBON-SERVICE-B", configuration = AnnoRuleConfig.class),
@RibbonClient(name = "RIBBON-SERVICE-C", configuration = AnnoRuleConfig.class)
})
基于配置文件
上面对服务 ribbon-service-b
的负载平衡策略应用
RIBBON-SERVICE-B:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Ribbon 超时与重试
应用 HTTP 发动申请难免会产生问题,在 F 版开始 Ribbon 的重试机制默认是开启的,须要增加对超时工夫与重试策略的配置。列入上面 ribbon-service-b
服务的配置
RIBBON-SERVICE-B:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ConnectTimeout: 3000
ReadTimeout: 60000
MaxAutoRetries: 3 #对第一次申请的服务的重试次数
MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包含第一个服务)OkToRetryOnAllOperations: true
也能够全局配置
ribbon:
ConnectTimeout: 3000
ReadTimeout: 60000
MaxAutoRetries: 3 #对第一次申请的服务的重试次数
MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包含第一个服务)OkToRetryOnAllOperations: true
个别 Ribbon 都是搭配 OpenFeign 这类 Http 客户端或者其余 RPC 应用。因为这样去调用近程服务会更加优雅和不便。而 OpenFeign 默认是继承了 Ribbon,对于 Ribbon 的超时工夫配置也是很简略。
对于网络抖动这些能够应用spring-retry
,spring-retry 是 spring 提供的一个基于 spring 的重试框架,十分好用。
Ribbon 饥饿加载
Ribbon 在进行客户端负载平衡的时候,并不是启动时就加载上下文,而是在理论申请的时候采取创立。因为要加载上下文的起因,在第一次调用时可能会很慢,甚至导致超时。所以咱们能够指定 Ribbon 客户端开启立刻加载(饥饿加载),在利用启动的时候就立刻加载所有配置项的应用程序上下文。
ribbon:
eager-load:
clients: ribbon-service-b, ribbon-service-order
enabled: true
自定义 Ribbon 客户端
在 Ribbon 的 1.2.0 版本之后,就能够应用配置文件来定制 Ribbon 客户端,其实本质就是应用配置文件来指定一些默认加载类,从而更改 Ribbon 客户端的行为,并且应用这种形式优先级最高,优先级高于应用注解 @RibbonClient
指定的配置和源码中加载的相干的 Bean。看下表:
配置项 | 阐明 |
---|---|
<clientName>.ribbon.NFLoadBalancerClassName | 指定 ILoadBalancer 的实现类 |
<clientName>.ribbon.NFLoadBalancerRuleClassName | 指定 IRule 的实现类 |
<clientName>.ribbon.NFLoadBalancerPingClassName | 指定 IPing 的实现类 |
<clientName>.ribbon.NiWSServerListClassName | 指定 ServerList 的实现类 |
<clientName>.ribbon.NIWSServerListFilterClassName | 指定 ServerListFilter 的实现类 |
例如:这里应用的实现的 RIbbon 提供的实现
RIBBON-SERVICE-B:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
NiWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
Ribbon 脱离 Eureka 应用
在默认的状况下,Ribbon 客户端须要从 Eureka 注册核心读取服务注册信息列表,来达到一种动静负载平衡的性能。当应用的注册核心是公共的注册核心,例如:社区公益 Eureka(http://eureka.springcloud.cn)…
ribbon:
eureka:
enabled: false
#RIBBON-SERVICE- B 服务的获取地址
RIBBON-SERVICE-B:
ribbon:
listOfServers: http://localhost:8088/
Ribbon 进阶
外围工作原理
Ribbon 的外围接口:
接口 | 形容 | 默认实现 |
---|---|---|
IClientConfig | 定义 Ribbon 中治理配置的接口 | DefaultClientConfigImpl |
IRule | 定义 Ribbon 中负载平衡策略的接口 | ZoneAdvoidanceRule |
IPing | 定义定期 Ping 服务查看可用性的接口 | DummyPing |
ServerList\<Server> | 定义获取服务列表办法的接口 | ConfigurationBasedServerList |
ServerListFilter\<Server> | 定义特定冀望获取服务列表办法的接口 | ZonePreferenceServerListFilter |
ILoadBalancer | 定义负载平衡抉择服务的外围办法的接口 | ZoneAwareLoadBalancer |
ServerListUpdater | 为 DynamicServerListLoadBalancer 定义动静更新服务列表的接口 | PollingServerListUpdater |
Ribbon 齐全是基于这些接口上建设起来的,是 Ribbon 的外围。理解这些外围的类的性能对于了解 Ribbon 的原理和扩大很无利。
在之前的例子中,应用 Ribbon 负载平衡都是通过在 RestTemplate 的 Bean 上增加注解@LoadBalanced
,使得 RestTemplate 领有了负载平衡的能力。
LoadBalanced 源码:
/**
* 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 或者其余的 WebClient 的 Bean 上,来应用 LoadBalancerClient。
LoadBalancerClient:该接口扩大自 ServiceInstanceChooser
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* Executes request using a ServiceInstance from the LoadBalancer for the specified
* service.
* @param serviceId The service ID to look up the LoadBalancer.
* @param request Allows implementations to execute pre and post actions, such as
* incrementing metrics.
* @param <T> type of the response
* @throws IOException in case of IO issues.
* @return The result of the LoadBalancerRequest callback on the selected
* ServiceInstance.
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
/**
* Executes request using a ServiceInstance from the LoadBalancer for the specified
* service.
* @param serviceId The service ID to look up the LoadBalancer.
* @param serviceInstance The service to execute the request to.
* @param request Allows implementations to execute pre and post actions, such as
* incrementing metrics.
* @param <T> type of the response
* @throws IOException in case of IO issues.
* @return The result of the LoadBalancerRequest callback on the selected
* ServiceInstance.
*/
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
/**
* Creates a proper URI with a real host and port for systems to utilize. Some systems
* use a URI with the logical service name as the host, such as
* http://myservice/path/to/service. This will replace the service name with the
* host:port from the ServiceInstance.
* @param instance service instance to reconstruct the URI
* @param original A URI with the host as a logical service name.
* @return A reconstructed URI.
*/
URI reconstructURI(ServiceInstance instance, URI original);
}
ServiceInstanceChooser:
public interface ServiceInstanceChooser {
/**
* Chooses a ServiceInstance from the LoadBalancer for the specified service.
* @param serviceId The service ID to look up the LoadBalancer.
* @return A ServiceInstance that matches the serviceId.
*/
ServiceInstance choose(String serviceId);
}
- ServiceInstance choose(String serviceId):依据 ServiceId,联合负载均衡器抉择一个服务实例
- <T> T execute(String serviceId, LoadBalancerRequest<T> request):应用来自 LoadBalancer 的 ServiceInstance 为指定的服务执行申请
-
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request):应用来自 LoadBalancer 的 ServiceInstance 为指定的服务执行申请,是上一个办法的重载,在实现类中能够看到它们的关系,就是前一个办法的细节实现、
- URI reconstructURI(ServiceInstance instance, URI original):应用注解 ip 和 port 构建特定的 URL 以供 Ribbon 外部应用。Ribbon 应用具备逻辑服务名称的 URL 作为 host,例如:http://service-b/order/add。
从这些办法的性能能够晓得这两个接口的重要性了。这两个接口的同一包下有一个类 LoadBalancerAutoConfiguration
。LoadBalancerAutoConfiguration
在org.springframework.cloud.client.loadbalancer
包下,在 spring-cloud-commons
外面。该主动配置类正式 Ribbon 的外围配置类。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> 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);
}
@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);
};
}
}
/**
* Auto configuration for retry mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {return new LoadBalancedRetryFactory() {};}
}
/**
* Auto configuration for retry intercepting mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRetryProperties properties,
LoadBalancerRequestFactory requestFactory,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
requestFactory, loadBalancedRetryFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
这就清晰起来了,他的配置加载机会是以后工程环境必须有 RestTemplate 的实例和必须初始化了 LoadBalancerClient 的实现类。
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
LoadBalancerRequestFactory:用于创立 LoadBalancerRequest 给 LoadBalancerInterceptor 应用。
LoadBalancerInterceptorConfig:保护了 LoadBalancerInterceptor 与 RestTemplateCustomizer 的实例。
- LoadBalancerInterceptor:拦挡每一次的 HTTP 申请,将申请绑定金 Ribbon 的负载平衡的生命周期。
- RestTemplateCustomizer:为每一个 Restemplate 绑定 LoadBalancerInterceptor 拦截器。
LoadBalancerInterceptor 的作用曾经和贴近答案了。
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 compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
@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));
}
}
在源码能够看到它是通过 ClientHttpRequestInterceptor 实现每次对 HTTP 申请的拦挡,ClientHttpRequestInterceptor 类是 Spring 中保护的申请拦截器,实现它的 intercept 办法就能够使得申请进入办法内,从而 Ribbon 就能够做一些本人的解决了。
在应用 RestTemplate 申请服务时应用的 URI:http://serviceName/path/to/se…
LoadBalancerInterceptor 中的 intercept 办法,最终调用的是 RibbonLoadBalancerClient 的 execute 办法:
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
// 拿到负载均衡器的实现
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
// 拿到具体的 Server
Server server = this.getServer(loadBalancer, hint);
if (server == null) {throw new IllegalStateException("No instances available for" + serviceId);
} else {RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, (ServiceInstance)ribbonServer, (LoadBalancerRequest)request);
}
}
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {return loadBalancer == null ? null : loadBalancer.chooseServer(hint != null ? hint : "default");
}
对于 chooseServer 是接口 ILoadBalancer 的办法,这里就先看一下其中的一个实现 BaseLoadBalancer
public Server chooseServer(Object key) {if (this.counter == null) {this.counter = this.createCounter();
}
this.counter.increment();
if (this.rule == null) {return null;} else {
try {return this.rule.choose(key);
} catch (Exception var3) {logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
最初是通过:rule.choose(key)
拿到 Server,而 rule 就是 IRule。
在 RibbonClientConfiguration
中初始化了下面表格提到几个外围类
- 初始化 ribbonRule: ZoneAvoidanceRule
- 初始化 ribbonPing:DummyPing
- 初始化 ribbonServerList:ConfigurationBasedServerList
- 初始化 ServerListUpdater:new PollingServerListUpdater(config)
- 初始化 ILoadBalancer:ZoneAwareLoadBalancer
- 初始化 ribbonServerListFilter:ZonePreferenceServerListFilter
- 初始化 ribbonLoadBalancerContext:RibbonLoadBalancerContext
- 初始化 serverIntrospector:DefaultServerIntrospector
对于 BlockingLoadBalancerClient:
Spring Cloud Hoxton.RELEASE 版本公布之后,新增了一个新的负载均衡器实现 BlockingLoadBalancerClient
。它是第一个蕴含 阻塞式
和非阻塞式
负载均衡器客户端实现的版本,作为已进入保护模式的 Netflix Ribbon 的代替计划。
如果想在 RestTemplate
应用新的 BlockingLoadBalancerClient
, 须要减少 spring-cloud-loadbalancer
的依赖,否则默认应用RibbonLoadBalancerClient
。