Ribbon核心源码解析(一)- 调用流程与负载均衡
Spring cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡工具,简单的说,它能够使用负载均衡器基于某种规则或算法调用我们的微服务集群,并且我们也可以很容易地使用Ribbon实现自定义负载均衡算法。
在之前使用Eureka的过程中,需要导入对应的依赖,但是Ribbon有一点特殊,不需要引入依赖也可以使用。这是因为在Eureka-client中,已经默认为我们集成好了Ribbon,可以直接拿来使用。
根据Spring Boot自动配置原理,先从各个starter的spring.factories
中寻找可能存在的相关配置类:
- 在spring-cloud-common中,存在自动配置类
LoadBalancerAutoConfiguration
- 在eureka-client中,存在配置类
RibbonEurekaAutoConfiguration
- 在ribbon中,存在配置类
RibbonAutoConfiguration
需要注意,RibbonEurekaAutoConfiguration
中存在@AutoConfigureAfter
注解,说明需要在加载RibbonAutoConfiguration
配置类后再加载当前配置类。这三个类的配置将在后面结合具体代码调试中说明。
下面我们通过代码调试的方式来探究Ribbon的运行流程。
调用流程
Ribbon的调用过程非常简单,使用RestTemplate
加上@LoadBalanced
注解就可以开启客户端的负载均衡,写一个简单的测试用例进行测试:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
@GetMapping("/test")
public String test(String service){
String result=restTemplate.getForObject("http://eureka-hi/"+service,String.class);
System.out.println(result);
return result;
}
结果:
通过结果可以看出,RestTemplate
基于服务名称,即可实现访问Eureka-client集群下的不同服务实例,实现负载均衡的调用方式。看一下@LoadBalanced
注解的定义:
/**
* 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 {
}
注释说明了@LoadBalanced
用于注解在RestTemplate
上实现负载均衡,那么来看一下@LoadBalanced
注解是如何生效的呢?回到前面提到的配置类LoadBalancerAutoConfiguration
中:
在配置类中定义了一个LoadBalancerInterceptor
拦截器,并且为restTemplate
添加了这个拦截器。在restTemplate
每次执行方法请求时,都会调用intercept
方法执行拦截:
在上面的intercept
拦截方法中,首先获取本次访问的url地址,从中获取本次要访问的服务名,然后调用RibbonLoadBalancerClient
中的execute
方法。
在这里通过服务名获取了该服务对应的负载均衡器ILoadBalancer
的实例对象,然后调用该实例的chooseServer
方法获取一个可用服务实例,关于ILoadBalancer
会在后面具体介绍。
在execute
方法调用apply
方法的过程中,会调用LoadBalancerContext
的reconstructURIWithServer
方法重构将要访问的url
地址:
在拼接完成url
后,调用AbstractClientHttpRequest
类的execute
方法发送请求。
调用executeInternal
方法:
可以看到,最终RestTemplate
底层调用了HttpURLConnection
来发送请求。
总体的调用流程我们总结完了,那么负载均衡的过程究竟是如何实现的呢?我们来详细梳理一下。
负载均衡过程
在Ribbon中有个非常重要的组件LoadBalancerClient
,它是负载均衡的一个客户端,我们从这入手写一个测试接口:
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/choose")
public String loadBalance(String serviceId){
ServiceInstance instance = loadBalancerClient.choose(serviceId);
System.out.println(instance.getHost()+" "+instance.getPort());
return "ok";
}
调用接口测试结果,可以看出是通过LoadBalancerClient
的choose
方法,选择调用了不同端口上的服务实例,体现了负载均衡:
对代码进行调试,发现注入的LoadBalancerClient
的实现类正是之前看见过的RibbonLoadBalancerClient
,进入其choos
方法中,先后调用两次getServer
方法:
此时loadBalancer
实例对象为ZoneAwareLoadBalancer
,并且里面的allServerList
列表已经缓存了所有的服务列表。调用chooseServer
方法,由于此时我们只有一个zone
,所以默认调用父类BaseLoadBalancer
的chooseServer
方法:
在父类的方法中,根据IRule
实例定义的规则来确定返回哪一个具体的Server:
这里的IRule
实现使用了默认的ZoneAvoidanceRule
,为区域内亲和选择算法。关于IRule
负载均衡算法在后面再做介绍。由于ZoneAvoidanceRule
中没有实现choose
方法,直接调用其父类PredicateBasedRule
的choose
方法:
调用AbstractServerPredicate
的chooseRoundRobinAfterFiltering
方法:
实现非常简单,通过轮询的方式选择下标:
返回choose
方法中,可以看到已经获得了一个server实例:
总结
到这里,Ribbon的大体调用流程和负载均衡过程就简单的说明白了,下一篇,我们再来详细看看Ribbon中的核心组件ILoadBalancer。
最后
觉得对您有所帮助,小伙伴们可以点个赞啊,非常感谢~
公众号『码农参上』,一个热爱分享的公众号,有趣、深入、直接,与你聊聊技术。欢迎来加我好友 DrHydra9,围观朋友圈,做个点赞之交。
- 点赞
- 收藏
- 关注作者
评论(0)