1.簡單使用
1.1.在啟動類中,聲明一個RestTemplate的bean,用@LoadBalanced注解修飾:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
1.2.在進行http調用的地方,通過@Autowired方式,注入RestTemplate,再通過restTemplate調用。調用時,寫服務名就可以,比如按照下面這種方式調用。
return restTemplate.getForObject("http://MICOSERICE-USER/user/getuser",ResponseDto.class);
通過這兩步操作,進行調用時,就可以通過服務名,得到多個服務實例的信息,通過負載均衡的方式進行調用。
從上面的步驟看來,使用負載均衡的開發量還是挺簡單的,不過為什么在restTemplate上加個注解,通過restTemplate進行調用,就可以做到負載均衡呢,這里就需要研究下源碼才能知道原因了。
2.源碼分析
進入到@LoadBalanced注解,如下:
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
這個也看不出啥,在源碼中LoadBalancerAutoConfiguration中使用了這個注解,如下:
@LoadBalanced
@Autowired(
required = false
)
private List<RestTemplate> restTemplates = Collections.emptyList(); //定義了一個restTemplates列表
在Spring注入當前類的loadBalancedRestTemplateInitializer的Bean時有如下方法
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
public void afterSingletonsInstantiated() {
Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator();
while(var1.hasNext()) {
RestTemplate restTemplate = (RestTemplate)var1.next();
Iterator var3 = customizers.iterator();
while(var3.hasNext()) {
RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next();
customizer.customize(restTemplate); //調用RestTemplateCustomizer的customize方法
}
}
}
};
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor); //添加了一個loadBalancerInterceptor攔截器
restTemplate.setInterceptors(list);
}
};
}
loadBalancerInterceptor攔截器定義的攔截方法如下:
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI(); //獲取url
String serviceName = originalUri.getHost(); //獲取host
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution)); //loadBalancer執行execute方法
}
loadBalancer執行execute方法如下:
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId); //根據serviceId獲取loadBalancer
Server server = this.getServer(loadBalancer); //獲取server
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, ribbonServer, request); //執行相應的請求
}
}
其中getServer方法如下:
protected Server getServer(ILoadBalancer loadBalancer) {
return loadBalancer == null ? null : loadBalancer.chooseServer("default"); //選擇server
}
這里默認執行的是ZoneAwareLoadBalancer下的chooseServer方法
public Server chooseServer(Object key) {
if (ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
Server server = null;
try {
LoadBalancerStats lbStats = this.getLoadBalancerStats();
Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
logger.debug("Zone snapshots: {}", zoneSnapshot);
if (this.triggeringLoad == null) {
this.triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2D);
}
if (this.triggeringBlackoutPercentage == null) {
this.triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty("ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999D);
}
Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, this.triggeringLoad.get(), this.triggeringBlackoutPercentage.get());
logger.debug("Available zones: {}", availableZones);
if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
logger.debug("Zone chosen: {}", zone);
if (zone != null) {
BaseLoadBalancer zoneLoadBalancer = this.getLoadBalancer(zone);
server = zoneLoadBalancer.chooseServer(key); //選擇server
}
}
} catch (Exception var8) {
logger.error("Error choosing server using zone aware logic for load balancer={}", this.name, var8);
}
if (server != null) {
return server;
} else {
logger.debug("Zone avoidance logic is not invoked.");
return super.chooseServer(key);
}
} else {
logger.debug("Zone aware logic disabled or there is only one zone");
return super.chooseServer(key);
}
}
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); //根據規則選擇Server,默認是RoundRobinRule,即輪詢規則
} catch (Exception var3) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
IRule的實現有以下:
AvailabilityFilteringRule過濾器規則
BestAvailableRule 選擇最小請求數
ClientConfigEnabledRoundRobinRule 輪詢
RandomRule 隨機選擇一個server
RoundRobinRule 輪詢選擇server
RetryRule 根據輪詢的方式重試
WeightedResponseTimeRule 根據響應時間去分配一個weight ,weight越低,被選擇的可能性就越低
ZoneAvoidanceRule 根據server的zone區域和可用性來輪詢選擇