Spring Cloud 之 Ribbon

Spring Cloud 之 Ribbon

1.Ribbon 是客户端负载均衡
2.Ribbon会从Eureka Client 拿服务实例,放到Ribbon实例列表中
3.“IPing”会通过定时任务,剔除不健康的实例
4.Ribbon会在健康的Ribbon实例里表中,通过“IRule” ,挑选出一个实例地址 , 替换为远端服务多节点具体哪一个节点的实例地址

源码分析

初始化负载均衡拦截器

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {

	// 省略 ......
    @Configuration
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        //初始化负载均衡拦截器
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }
	// 省略 ......
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
	// 省略 ......
	//拦截服务端调用的请求
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //具体的方法
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
   		 //	获取负载均衡器
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        //通过Rule 规则,获取选举出来的server
        Server server = this.getServer(loadBalancer);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, ribbonServer, request);
        }
    }

获取负载均衡器

    public ILoadBalancer getLoadBalancer(String name) {
        return (ILoadBalancer)this.getInstance(name, ILoadBalancer.class);
    }

需要看一下ILoadBalancer的初始化,因为在这个地方用到了

@Configuration
@EnableConfigurationProperties
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
// 省略 ......
 @Bean
    @ConditionalOnMissingBean
    public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
        return (ILoadBalancer)
        (this.propertiesFactory.isSet(ILoadBalancer.class,this.name) ? 
		(ILoadBalancer)this.propertiesFactory.get(ILoadBalancer.class, config, this.name) 						
		:
 		new ZoneAwareLoadBalancer(config, rule, ping, serverList, serverListFilter,serverListUpdater));
    }
    // 省略 ......

在此处发现初始化了ZoneAwareLoadBalancer,实际上是初始化了其的父类DynamicServerListLoadBalancer

    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> filter,
                                         ServerListUpdater serverListUpdater) {
        //定时任务,IPing                                  
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        //从client 注册表中拉取数据到 Ribbon 注册表中
        restOfInit(clientConfig);
    }

先看一下Ping的逻辑

 void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping) {
    // 省略 ......
 	//继续跟进
  	setPingInterval(pingIntervalTime);
  	   // 省略 ......
}
    public void setPingInterval(int pingIntervalSeconds) {
       // 省略 ......
    	//ping的定时任务
        setupPingTask(); // since ping data changed
           // 省略 ......
    }
}
   void setupPingTask() {
        if (canSkipPing()) {
            return;
        }
        if (lbTimer != null) {
            lbTimer.cancel();
        }
        lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name,
                true);
                //默认10s执行一次
        lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000);
        forceQuickPing();
    }

  public void runPinger() throws Exception {
            if (!pingInProgress.compareAndSet(false, true)) { 
                return; // Ping in progress - nothing to do
            }
            
            // we are "in" - we get to Ping

            Server[] allServers = null;
            boolean[] results = null;

            Lock allLock = null;
            Lock upLock = null;

            try {
                /*
                 * The readLock should be free unless an addServer operation is
                 * going on...
                 */
                allLock = allServerLock.readLock();
                allLock.lock();
                allServers = allServerList.toArray(new Server[allServerList.size()]);
                allLock.unlock();

                int numCandidates = allServers.length;
                // results[i] = ping.isAlive(servers[i]);
                //获取健康的节点
                results = pingerStrategy.pingServers(ping, allServers);

                final List<Server> newUpList = new ArrayList<Server>();
                final List<Server> changedServers = new ArrayList<Server>();

                for (int i = 0; i < numCandidates; i++) {
                    boolean isAlive = results[i];
                    Server svr = allServers[i];
                    boolean oldIsAlive = svr.isAlive();

                    svr.setAlive(isAlive);

                    if (oldIsAlive != isAlive) {
                        changedServers.add(svr);
                        logger.debug("LoadBalancer [{}]:  Server [{}] status changed to {}", 
                    		name, svr.getId(), (isAlive ? "ALIVE" : "DEAD"));
                    }
					//将存活的数据更新到upServerList 中
                    if (isAlive) {
                        newUpList.add(svr);
                    }
                }
                upLock = upServerLock.writeLock();
                upLock.lock();
                upServerList = newUpList;
                upLock.unlock();

                notifyServerStatusChangeListener(changedServers);
            } finally {
                pingInProgress.set(false);
            }
        }
    }

从client 注册表中拉取数据到 Ribbon 注册表中

    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        //定时任务:定时从ping后健康的注册表中拉取数据,跟代码后发现,
        //其实定时任务还是调用的 updateListOfServers()
        enableAndInitLearnNewServersFeature();
		//拉取健康的实例列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }

接下来看一下通过IRule获取具体的server

   protected Server getServer(ILoadBalancer loadBalancer) {
        return loadBalancer == null ? null : loadBalancer.chooseServer("default");
    }
 public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
            	//获取具体的server
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }

从这里看到,最后是通过rule.choose(key);获取的Server,默认的ZoneAvoidanceRule实现

    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        Optional<Server> server = 
        //听说是轮询,后期更新
        getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值