Ribbon核⼼源码剖析

目录

一. Ribbon⼯作原理

二. @LoadBalanced源码剖析

​ (一)研究LoadBalancerAutoConfifiguration

(二)分析拦截器LoadBalancerInterceptor

1. 关注点1:

2. 关注点2:选择服务 

 3 . 关注点3:

(三)serverList分析

三. RoundRobinRule轮询策略源码剖析

 四. RandomRule随机策略源码剖析


一. Ribbon⼯作原理

Ribbon核⼼源码剖析_第1张图片

Ribbon核⼼源码剖析_第2张图片 重点:RibbonrestTemplate添加了⼀个拦截器

思考: Ribbon 在做什么:
当我们访问 http://lagou-service-resume/resume/openstate/ 的时候, ribbon 应该
根据服务名 lagou-service-resume 获取到该服务的实例列表并按照⼀定的负载均衡
策略从实例列表中获取⼀个实例 Server ,并最终通过 RestTemplate 进⾏请求访问
Ribbon 细节结构图(涉及到底层的⼀些组件 / 类的描述)
1) 获取服务实例列表 2 )从列表中选择⼀个 server
Ribbon核⼼源码剖析_第3张图片

图中核⼼是 负载均衡管理器 LoadBalancer (总的协调者,相当于⼤脑,为了做事
情,协调四肢) ,围绕它周围的多有 IRule IPing
  • IRule:是在选择实例的时候的负载均衡策略对象
  • IPing:是⽤来向服务发起⼼跳检测的,通过⼼跳检测来判断该服务是否可⽤
  • ServerListFilter:根据⼀些规则过滤传⼊的服务实例列表
  • ServerListUpdater:定义了⼀系列的对服务列表的更新操作

二. @LoadBalanced源码剖析

我们在 RestTemplate 实例上添加了⼀个 @LoadBalanced 注解,就可以实现负载均
衡,很神奇,我们接下来分析这个注解背后的操作(负载均衡过程)
  • 查看@LoadBalanced注解,那这个注解是在哪⾥被识别到的呢?
Ribbon核⼼源码剖析_第4张图片

  • LoadBalancerClient类(实现类RibbonLoadBalancerClient待⽤
package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

import java.io.IOException;
import java.net.URI;

/**
 * Represents a client-side load balancer.
 * @author Spencer Gibb
 */
public interface LoadBalancerClient extends ServiceInstanceChooser {

    // 根据服务执⾏请求内容
	 T execute(String serviceId, LoadBalancerRequest request) throws IOException;

    // 根据服务执⾏请求内容
	 T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest request) throws IOException;

    // 拼接请求⽅式 传统中是ip:port 现在是服务名称:port 形式
	URI reconstructURI(ServiceInstance instance, URI original);
}
  • ⽼规矩:SpringCloud充分利⽤了SpringBoot的⾃动装配特点,找spring.factories配置⽂件

Ribbon核⼼源码剖析_第5张图片

Ribbon核⼼源码剖析_第6张图片 (一)研究LoadBalancerAutoConfifiguration

 Ribbon核⼼源码剖析_第7张图片

 =========》》》LoadBalancerAutoConfifiguration⾥⾯的内容剖析

第⼀处:注⼊ resttemplate 对象到集合待⽤

 第⼆处:注⼊resttemplate定制器

Ribbon核⼼源码剖析_第8张图片

 第三处:使⽤定制器给集合中的每⼀个resttemplate对象添加⼀个拦截器

Ribbon核⼼源码剖析_第9张图片

到这⾥,我们明⽩,添加了注解的 RestTemplate 对象会被添加⼀个拦截器
LoadBalancerInterceptor,该拦截器就是后续拦截请求进⾏负载处理的。
所以,下⼀步重点我们该分析拦截器 LoadBalancerInterceptor------>>>intercept()
⽅法。

(二)分析拦截器LoadBalancerInterceptor

 ==========》》》》分析LoadBalancerInterceptor.intercept()⽅法

Ribbon核⼼源码剖析_第10张图片

那么? RibbonLoadBalancerClient 对象是在哪⾥注⼊的 === 》》回到最初的⾃动配
置类 RibbonAutoConfifiguration
Ribbon核⼼源码剖析_第11张图片
负载均衡的事情执⾏原来交给了我们最初看到的LoadBalancerClient 接口的实现类:RibbonLoadBalancerClient 类的对象

⾮常核⼼的⼀个⽅法:RibbonLoadBalancerClient.execute()

Ribbon核⼼源码剖析_第12张图片

1. 关注点1:

 =====》》》回到主配置类RibbonAutoConfifiguration

Ribbon核⼼源码剖析_第13张图片

 Ribbon核⼼源码剖析_第14张图片

 RibbonClientConfifiguration中装配了⼤脑和肢⼲

Ribbon核⼼源码剖析_第15张图片

 Ribbon核⼼源码剖析_第16张图片

2. 关注点2:选择服务 

Ribbon核⼼源码剖析_第17张图片

 ZoneAwareLoadBalancer#chooseServer

Ribbon核⼼源码剖析_第18张图片

 ⽗类:com.netflflix.loadbalancer.BaseLoadBalancer#chooseServer

Ribbon核⼼源码剖析_第19张图片

来到区域隔离策略的⽗类 choose ⽅法中
com.netflflix.loadbalancer.PredicateBasedRule#choose
Ribbon核⼼源码剖析_第20张图片

Ribbon核⼼源码剖析_第21张图片

 Ribbon核⼼源码剖析_第22张图片

 3 . 关注点3:

Ribbon核⼼源码剖析_第23张图片

在下图所示的RibbonLoadBalancerClient类的execute方法的return代码行打上断点,消费端Debug运行后,浏览器请求消费端,进入该方法

Ribbon核⼼源码剖析_第24张图片

 Ribbon核⼼源码剖析_第25张图片

 Ribbon核⼼源码剖析_第26张图片

 AbstractClientHttpRequest#execute

此处,就已经到了 RestTemplate 底层执⾏的代码了,由此也将验证最终请求的调⽤
还是靠的 RestTemplate
Ribbon核⼼源码剖析_第27张图片

(三)serverList分析

接下来,在进⾏负载 chooseServer 的时候, LoadBalancer 负载均衡器中已经有了
serverList ,那么这个 serverList 是什么时候被注⼊到 LoadBalancer 中的,它的⼀个
机制⼤概是怎样的?
来到 RibbonClientConfifiguration

 Ribbon核⼼源码剖析_第28张图片

还是在RibbonClientConfiguration这个类中有一个ServerList类型的Bean 

Ribbon核⼼源码剖析_第29张图片

 把⽬光聚焦到使⽤这个空对象ServerList的地⽅

Ribbon核⼼源码剖析_第30张图片

 Ribbon核⼼源码剖析_第31张图片

Ribbon核⼼源码剖析_第32张图片 Ribbon核⼼源码剖析_第33张图片

 进⼊enableAndInitLearnNewServersFeature()⽅法

 

进入ServerListUpdater接口的实现类EurekaNotificationServerListUpdater,在该类中找到start(final UpdateAction updateAction)方法:

Ribbon核⼼源码剖析_第34张图片

三. RoundRobinRule轮询策略源码剖析

/*
 *
 * Copyright 2013 Netflix, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.netflix.loadbalancer;

import com.netflix.client.config.IClientConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * The most well known and basic load balancing strategy, i.e. Round Robin Rule.
 *
 * @author stonse
 * @author Nikos Michalakis 
 *
 */
public class RoundRobinRule extends AbstractLoadBalancerRule {

    private AtomicInteger nextServerCyclicCounter;
    private static final boolean AVAILABLE_ONLY_SERVERS = true;
    private static final boolean ALL_SERVERS = false;

    private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);

    public RoundRobinRule() {
        nextServerCyclicCounter = new AtomicInteger(0);
    }

    public RoundRobinRule(ILoadBalancer lb) {
        this();
        setLoadBalancer(lb);
    }

    // 负载均衡策略类核⼼⽅法
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            // 所有可⽤服务实例列表
            List reachableServers = lb.getReachableServers();
            // 所有服务实例列表
            List allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }
            // 获得⼀个轮询索引
            int nextServerIndex = incrementAndGetModulo(serverCount);
            // 根据索引取出服务实例对象
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }
            // 判断服务可⽤后返回

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }

    /**
     * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
     *
     * @param modulo The modulo to bound the value of the counter.
     * @return The next value.
     */
    private int incrementAndGetModulo(int modulo) {
        for (;;) {
            // 取出上次的计数
            int current = nextServerCyclicCounter.get();
            // 因为是轮询,计数+1之后对总数取模
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

    @Override
    public Server choose(Object key) {
        return choose(getLoadBalancer(), key);
    }

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
    }
}

 四. RandomRule随机策略源码剖析

Ribbon核⼼源码剖析_第35张图片

Ribbon核⼼源码剖析_第36张图片

你可能感兴趣的:(Java进阶代码示例,ribbon,spring,cloud,云原生)