阅读 121

Ribbon核心源码解析(1)- 调用流程与负载均衡

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方法的过程中,会调用LoadBalancerContextreconstructURIWithServer方法重构将要访问的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"; } 复制代码

调用接口测试结果,可以看出是通过LoadBalancerClientchoose方法,选择调用了不同端口上的服务实例,体现了负载均衡:

图片

对代码进行调试,发现注入的LoadBalancerClient的实现类正是之前看见过的RibbonLoadBalancerClient,进入其choos方法中,先后调用两次getServer方法:

图片

此时loadBalancer实例对象为ZoneAwareLoadBalancer,并且里面的allServerList列表已经缓存了所有的服务列表。调用chooseServer方法,由于此时我们只有一个zone,所以默认调用父类BaseLoadBalancerchooseServer方法:

图片

在父类的方法中,根据IRule实例定义的规则来确定返回哪一个具体的Server:

图片

这里的IRule实现使用了默认的ZoneAvoidanceRule,为区域内亲和选择算法。关于IRule负载均衡算法在后面再做介绍。由于ZoneAvoidanceRule中没有实现choose方法,直接调用其父类PredicateBasedRulechoose方法:

图片

调用AbstractServerPredicatechooseRoundRobinAfterFiltering方法:

图片

实现非常简单,通过轮询的方式选择下标:

图片

返回choose方法中,可以看到已经获得了一个server实例:

图片

总结

到这里,Ribbon的大体调用流程和负载均衡过程就简单的说明白了,下一篇,我们再来详细看看Ribbon中的核心组件ILoadBalancer


作者:Hydra
链接:https://juejin.cn/post/7020585268109377550


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐