介紹
先看下spring cloud官方對Ribbon的描述:Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients.
大意就是說拔疚,Ribbon是一個客戶端負責均衡器,它賦予了應(yīng)用一些支配HTTP與TCP行為的能力。那么,我們常見的nginx,lvs可以理解為服務(wù)端負載均衡滩届。在spring cloud中果正,F(xiàn)eign和Zuul中已經(jīng)默認集成了Ribbon贸毕。
Ribbon的負載均衡策略
開啟負載均衡
- 直接在RestTemplate加上LoadBalance注解
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
- 第二種方式稍微麻煩一些
在配置文件中加配置件蚕,hello-server是用來測試的兩個服務(wù),并注冊到eureka上
hello-server.ribbon.listOfServers=http://192.168.1.6:8101, http://192.168.1.6:8102
在啟動類上加注解
@RibbonClients(
@RibbonClient(value = "hello-server")
)
@SpringBootApplication
@EnableCircuitBreaker
public class StudyApplication {
public static void main(String[] args) {
SpringApplication.run(StudyApplication.class, args);
}
源碼解析
/**
* Annotation to mark a RestTemplate 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配置使用LoadBalancerClient产禾,那么問題來了排作,LoadBalancerClient在哪,老套路亚情,我們還是去RibbonAutoConfiguration自動配置類看一下妄痪,在這個類里會看到這樣的代碼
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
這段代碼中會注入SpringClientFactory,可以猜測一下這個類作用楞件,用來獲取一個bean衫生,那么獲取什么樣的bean,我們往下看土浸。
在RibbonAutoConfiguration還有一個重要的bean罪针,LoadBalancerClient
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
ok,可以清晰的看到這個SpringClientFactory會注入到LoadBalancerClient的實現(xiàn)類RibbonLoadBalancerClient中黄伊。
后面我們斷點跟進一下
@GetMapping("/study2")
public Object test2() {
ServiceInstance serviceInstance = loadBalancerClient.choose("hello-server");
String host = serviceInstance.getHost();
int port = serviceInstance.getPort();
return restTemplate.getForObject("http://" + host + ":" + port, String.class, "");
}
從這里進入choose方法最終會調(diào)用到RibbonLoadBalancerClient的如下方法
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}
進入getLoadBalancer()方法
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
這個clientFactory就是上面的SpringClientFactory泪酱,這里就明確了這個factory就是獲取ILoadBalancer的,獲得ILoadBalancer后还最,會獲取Server墓阀,那么繼續(xù)進入getServer()方法
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
if (loadBalancer == null) {
return null;
}
// Use 'default' on a null hint, or just pass it on?
return loadBalancer.chooseServer(hint != null ? hint : "default");
}
負責均衡的目標就是幫我們選擇服務(wù),繼續(xù)進入chooseServer()方法拓轻,會進入到BaseLoadBalancer的chooseServer()方法
public Server chooseServer(Object key) {
if (counter == null) {
counter = createCounter();
}
counter.increment();
if (rule == null) {
return null;
} else {
try {
return rule.choose(key);
} catch (Exception e) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e);
return null;
}
}
}
在這我們會看到會用IRule選擇服務(wù)斯撮,所以IRule的實現(xiàn)類(默認是ZoneAvoidanceRule)會注入到ILoadBalancer中,分析到這里我們基本就明白了ribbon是如何選擇服務(wù)的扶叉。
總結(jié)
本文分析了如何選擇服務(wù)的源碼勿锅,實際上我們通過RestTemplate發(fā)起調(diào)用的時候,會通過攔截器攔截請求辜梳,選擇服務(wù)粱甫,再返回結(jié)果。