前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring Cloud netflix ribbon源码分析

Spring Cloud netflix ribbon源码分析

作者头像
用户1215919
发布2020-10-26 17:41:09
4190
发布2020-10-26 17:41:09
举报
文章被收录于专栏:大大的微笑大大的微笑
  1. 依赖
代码语言:javascript
复制
 <dependency>
	 <groupId>org.springframework.cloud</groupId>
	 <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
	 <version>2.2.3.RELEASE</version>
</dependency>
  1. 配置

假设订单服务有两台,分别分8060、8061的两个服务

代码语言:javascript
复制
micro-order-service.ribbon.listOfServers=\
  http://localhost:8060/orders,\
  http://localhost:8061/orders
  1. 客户端负载均衡处理(user-service代码片段)
代码语言:javascript
复制
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping(value = "/orders")
public String orders(){
	// 通过LoadBalancerClient获取一个ServiceInstance实例
	// 其中包含了host、port等信息
       ServiceInstance serviceInstance = loadBalancerClient.choose("micro-order-service");
	return restTemplate.getForObject(String.format("http://%s:%s/orders",
	 serviceInstance.getHost(), serviceInstance.getPort()), String.class);
}
  1. order-service
代码语言:javascript
复制
   @Value("${server.port}")
    private Integer port;

    @GetMapping(value = "/orders")
    public String getOrders() {
        System.out.printf("curr service port: %s \n", port);
        System.out.println();
        // 伪代码
        return "{" +
                "订单编号: 12345678900000," +
                "订单编号: 12345678900001," +
                "}";
    }

查看控制台会发现轮询打印了端口的信息

@LoadBalanced

修改user-service服务的代码如下,会发现带来同样的效果

代码语言:javascript
复制
 @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Bean
 // 这里加上@LoadBalanced
    @LoadBalanced
    public RestTemplate newRestTemplate(RestTemplateBuilder builder){
        return builder.build();
    }

    @GetMapping(value = "/orders")
    public String orders(){
		// 直接将host和port换成micro-order-service
        return restTemplate.getForObject("http://micro-order-service/orders", String.class);
    }
@LoadBalanced做了什么
  1. @Qualifier

进入@LoadBalanced会发现当前类被@Qualifier标注;那么@Qualifier的作用是什么

1.1. 小测试

代码语言:javascript
复制
    @Qualifier
    @Bean("user1")
    public User newUser1(){
        return new User("张三");
    }

    @Bean("user2")
    @Qualifier
    public User newUser2(){
        return new User("李四");
    }

    @Autowired
    @Qualifier
    private List<User> users = Collections.emptyList();

 @GetMapping(value = "/users")
    public List users(){
        return users;
    }

当我们访问的 /users的时候会获取张三、李四两个对象;当我们将张三的@Qualifier注解去掉之后,只会获得李四一个对象;由此可以知道@Qualifier,可以作为一个标识,并且只能拿到同样标识了@Qualifier注解的对象。同时@Qualifier还具有分组的作用

代码语言:javascript
复制
     @Qualifier("g1")
    @Bean
    public User newTest(){
        return new User("111");
    }

    @Qualifier("g1")
    @Bean
    public User newTest2(){
        return new User("222");
    }

    @Qualifier("g2")
    @Bean
    public User newTest3(){
        return new User("333");
    }

    @Qualifier("g2")
    @Autowired
    List<User> u1;

    @Qualifier("g1")
    @Autowired
    List<User> u2;

 @GetMapping(value = "/test")
    public Map test(){
        Map<String, List<User>> map = new HashMap<>();
        map.put("g2",u1);
        map.put("g1",u2);
        return map;
    }
LoadBalancerAutoConfiguration

按照正常的逻辑来讲,一个http请求肯定需要host,port才可以发送请求,那么很显然我们通过serviceId的方式也可以发送,说明在发送之前肯定是做了拦截操作,LoadBalancerAutoConfiguration在程序启动的时候会被加载。

  1. 通过调试不难发现这里会加载LoadBalancerInterceptor拦截
  1. 然后会将拦截器置入到RestTemplate
发送请求

发送请求的时候我们继续调试会进入到RestTemplatedoExecute方法中,大致如下所示:

  1. 这里有个重要的方法createRequest

继续跟踪会发现这里会去选择一个RequestFactory去创建请求, 有两个实现如下: org.springframework.http.client.support.HttpAccessor, org.springframework.http.client.support.InterceptingHttpAccessor

  1. InterceptingHttpAccessor

InterceptingHttpAccessor 是HttpAccessor的子类,InterceptingHttpAccessor一定会覆盖HttpAccessor的同名方法

  1. getRequestFactory

很显然这里的getInterceptors就是前面LoadBalancerAutoConfiguration中设置的。所以最后这里返回的一定是InterceptingClientHttpRequestFactory

  1. 返回到createReuquest的地方

这里发现并没有我们要找的InterceptingClientHttpRequestFactory类,因此我们进入到抽象类中

  1. InterceptingClientHttpRequestFactory的createRequest

createRequest方法在当前抽象类中没有做处理,是在子类中处理的,其中有两个子类 org.springframework.http.client.InterceptingClientHttpRequestFactory org.springframework.http.client.BufferingClientHttpRequestFactory

  1. 创建InterceptingClientHttpRequest

逐步跟进最后会进入到InterceptingClientHttpRequestFactory类中然后创建的request对象显然是InterceptingClientHttpRequest

  1. 返回到RestTemplate的doExecute方法

这里会有个execute方法,很显然没有找到InterceptingClientHttpRequest类,所以我们继续进入到抽象类:org.springframework.http.client.AbstractClientHttpRequest

  1. AbstractBufferingClientHttpRequest#executeInternal

依然没有找到InterceptingClientHttpRequest,所以需要继续跟进到 org.springframework.http.client.AbstractBufferingClientHttpRequest#executeInternal类中,继续跟踪

  1. 千呼万唤使出来

突破层层包装以后终于找到了我们需要找到的InterceptingClientHttpRequest

  1. InterceptingClientHttpRequest内部类InterceptingRequestExecution#execute

继续跟进,最后会进入到内部类InterceptingRequestExecution的execute方法.然后会判断是否有拦截器,答案是肯定的;而且通过前面LoadBalancerAutoConfiguration的介绍,最终一定会进入到: org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor#intercept

RibbonLoadBalancerClient

这里会进入到RibbonLoadBalancerClient#execute, 这里的serviceId即前面传入的。然后会选择一个负载均衡器。如下图示:

  1. 进入到getLoadBalancer

进入到getLoadBalancer方法逐步跟进最后会进入到 org.springframework.cloud.netflix.ribbon.SpringClientFactory#getInstance方法拿到负载均衡器及所有的实例并返回,这里会根据我们在配置文件中配置的内容: app-service: ribbon: listOfServers: http://localhost:8060,http://localhost:8061

getServer

进入到getServer这里会选择一个具体的实例。: ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子类所以会进入到子类方法中: com.netflix.loadbalancer.ZoneAwareLoadBalancer#chooseServer

ZoneAwareLoadBalancer

DynamicServerListLoadBalancer中因为没有chooseServer方法,所以会继续往上找到: com.netflix.loadbalancer.BaseLoadBalancer

注意这里的判断条件会判断 ZoneAwareNIWSDiscoveryLoadBalancer.enabled这个配置是否是false(默认为true),或者AvailableZones=1(考虑到跨机房部署),所以这里会执行if条件中的内容

  1. BaseLoadBalancer#chooseServer

这里会根据负债均衡规则获取实例

  1. ZoneAvoidanceRule

RibbonClientConfiguration中默认配置的路由规则为ZoneAvoidanceRule,因为不存在choose方法所以找到他的父类PredicateBasedRule

3. chooseRoundRobinAfterFiltering方法

根据方法名,或者方法上的注释不难发现最后还是采用的轮询算法。

  1. incrementAndGetModulo方法
关于IRule.

IRule是制定负载均衡规则的接口,提供了诸如: 随机、轮询、最可用、响应时间等不同的规则,这里默认采用的是轮询。

关于IPing.

如上图所示如何知道节点是否可用就需要有一个健康检查机制,那么IPing提供了一系列的健康检查机制,甚至我们自己也可以实现自己的健康检查规则:

  1. RibbonClientConfiguration中定义了默认的规则

进入到com.netflix.loadbalancer.DummyPing内部会发现isAlive方法返回true,即为不做检查,默认都是存活的状态

  1. 自定义规则

官方文档指出通过如下方式配置即可实现自定义规则,从源码中也可以诡探一二

  1. RibbonClientConfiguration#ribbonPing

进入到:org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration#ribbonPing,

服务发现和更新
  • DynamicServerListLoadBalancer的构造方法 在调用的过程中,会进入到DynamicServerListLoadBalancer的构造方法中DynamicServerListLoadBalancer(IClientConfig, IRule , IPing , ServerList<T> , ServerListFilter<T>, ServerListUpdater)
  • restOfInit方法

在restOfInit方法中执行服务列表的更新动作,enableAndInitLearnNewServersFeature 方法会去执行PollingServerListUpdater的start方法,如下图所示:

  • com.netflix.loadbalancer.PollingServerListUpdater

最终会进入到com.netflix.loadbalancer.PollingServerListUpdater类中,他这边有个start方法用来检查服务是否需要更新,每隔30s执行一次,并且回调DynamicServerListLoadBalancer#updateListOfServers方法,如下图所示:

  • com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers

紧接着会回到DynamicServerListLoadBalancer并且调用updateListOfServers,这里有调用更新服务列表的时候有两个实现,最终会进入到ConfigurationBasedServerList:

  • com.netflix.loadbalancer.ConfigurationBasedServerList#derive
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • @LoadBalanced
  • @LoadBalanced做了什么
  • LoadBalancerAutoConfiguration
  • 发送请求
  • RibbonLoadBalancerClient
  • getServer
  • ZoneAwareLoadBalancer
  • 关于IRule.
  • 关于IPing.
  • 服务发现和更新
相关产品与服务
负载均衡
负载均衡(Cloud Load Balancer,CLB)提供安全快捷的流量分发服务,访问流量经由 CLB 可以自动分配到云中的多台后端服务器上,扩展系统的服务能力并消除单点故障。负载均衡支持亿级连接和千万级并发,可轻松应对大流量访问,满足业务需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档