负载均衡:spring cloud ribbon

源码:

@LoadBalanced注解用来给RestTemplate做标记,以使用负载均很的客户端(loadBalancerClient)来配置他.


loadBalancerClient类:是个接口,定义了几个核心方法choose、execute、reconstructURI


loadBalancerAutoConfiguration:客户端负载均衡器的自动化配置类。

主要做了三件事:1、创建LoadBalancerInterceptor的bean,用来对客户端发起的请求进行拦截

2、创建了RestTemplateCustomizer的bea,用于给RestTemplate增加LoadBalancerInterceptor拦截器

3、维护一个被@Loadbalanced注解修饰的RestTemplate对象列表,并且初始化


LoadBalancerInterceptor:

    /***
     * 当被@LoadBalance注解修饰的RestTemplate发起请求会被拦截进入该方法
     * @param request
     * @param body
     * @param execution
     * @return
     * @throws IOException
     */
    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);
        //具体由LoadBalancerClient的实现类LoadBalancerClient执行
        return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
    }


RibbonLoadBalancerClient:

/***
     * 请求拦截入口
     * @param serviceId   实例名:比如请求总地址为:http://eds2/admin/api/entityOrganization/getOrgTree.json   则该参数为 eds2
     * @param request      请求信息
     * @param <T>
     * @return
     * @throws IOException
     */
    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        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);//执行远程请求
        }
    }

getServer:选择具体某台服务器。这个方法涉及到的类图:


上图介绍:AbstractLoadBalancer定义了两个方法以及一个枚举,BaseLoadBalancer负载均衡的基本实现,最后两个是负载均衡实现的扩展。    

默认采用ZoneAwareLoadbalancer,而如果没有开启zone或者只有一个默认zone的话,则会调用BaseLoadBalancer的chooseServer

    public Server chooseServer(Object key) {
        if(ENABLED.get() && this.getLoadBalancerStats().getAvailableZones().size() > 1) {
            Server server = null;
            //此分支实现的是根据zone选择服务器的逻辑。。。。代码略
                
        } else {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
    }


核心类解读:

BaseLoadBalancer:是负载均衡器的基础实现类,定义了很多相关基础内容

维护两个存储服务实例的Server对象list。

LoadBalancerStats:用来存储负载均衡器服务实例和统计信息对象。

IPing:检查服务是否正常

IPingStrategy:检查服务实例操作的执行策略对象,在该类的静态内部类有实现SerialPingStrategy,采用线性遍历方式做检查,server列表过大或者Iping慢的时候不理想,可以重新该方法优化。

private static class SerialPingStrategy implements IPingStrategy {
        private SerialPingStrategy() {
        }

        public boolean[] pingServers(IPing ping, Server[] servers) {
            int numCandidates = servers.length;
            boolean[] results = new boolean[numCandidates];
            BaseLoadBalancer.logger.debug("LoadBalancer:  PingTask executing [{}] servers configured", Integer.valueOf(numCandidates));

            for(int i = 0; i < numCandidates; ++i) {
                results[i] = false;

                try {
                    if(ping != null) {
                        results[i] = ping.isAlive(servers[i]);
                    }
                } catch (Exception var7) {
                    BaseLoadBalancer.logger.error("Exception while pinging Server: '{}'", servers[i], var7);
                }
            }

            return results;
        }
    }

IRule:处理规则,chooseServer函数由该属性实现,默认为RondRobinRule的线性负载均衡规则

启动IPing任务:定时任务检查serverlist中server健康,间隔10秒


DynamicServerListLoadBalancer:BaseLoadBalancer的扩展类,实现了实例服务清单在运行期的动态更新能力,同时还具备对服务实例清单的过滤功能。

ServerList属性:为接口,定义了两个抽象方法,获取初始化的服务实例和获取更新的服务实例。通过搜索源码发现该类的实现类如下图:


那么具体用的哪个实现类?

搜索EurekaribbonclientConfiguration类,发现DomainExtractingServerList为实现类。

获取初始化的服务实例和获取更新的服务实例的具体实现方法在类dicoveryEnabledNIWSServerList的obtainServersViaDiscovery方法,该方法直接调取注册中心获取服务实例。

更新服务列表定时任务启动,以构造器为入口追踪会调用到方法enableAndInitLearnNewServersFeature,该方法调用更新器的start方法。搜索源码发现有两个实现类


PollingServerListUpdater:动态服务列表更新,DynamicServerListLoadBalancer默认为此,通过定时任务方法更新,更新服务在实例初始化1秒后开发执行,30秒频率

EurekaNotificationServerListUpdater:也可服务DynamicServerListLoadBalancer,但触发机制不同,需要Eureka事件监听来驱动。


ServerListFilter:对服务实例清单进行过滤的filter。类图:


ZoneAffinityServerListFilter:实现区域感知的过滤器。

    public List<T> getFilteredListOfServers(List<T> servers) {
        if(this.zone != null && (this.zoneAffinity || this.zoneExclusive) && servers != null && servers.size() > 0) {
            //按照zone过滤后的list
            List<T> filteredServers = Lists.newArrayList(Iterables.filter(servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));
            if(this.shouldEnableZoneAffinity(filteredServers)) {//这个方法判断是否开启过滤器
                return filteredServers;
            }

            if(this.zoneAffinity) {
                this.overrideCounter.increment();
            }
        }

        return servers;
    }
 /***
     * 根据一系列的算法求出一些基础指标(包含实例数量、断路器断开数、活动请求数、实例平均负载等)
     * 与阈值比较,有一个条件符合,则不开启
     * 这一算法实现为集群出现故障时,依然可以跨区域
     * @param filtered
     * @return
     */
    private boolean shouldEnableZoneAffinity(List<T> filtered) {
        if(!this.zoneAffinity && !this.zoneExclusive) {
            return false;
        } else if(this.zoneExclusive) {
            return true;
        } else {
            LoadBalancerStats stats = this.getLoadBalancerStats();
            if(stats == null) {
                return this.zoneAffinity;
            } else {
                logger.debug("Determining if zone affinity should be enabled with given server list: {}", filtered);
                ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered);
                double loadPerServer = snapshot.getLoadPerServer();
                int instanceCount = snapshot.getInstanceCount();
                int circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount();
                if((double)circuitBreakerTrippedCount / (double)instanceCount < this.blackOutServerPercentageThreshold.get() && loadPerServer < this.activeReqeustsPerServerThreshold.get() && instanceCount - circuitBreakerTrippedCount >= this.availableServersThreshold.get()) {
                    return true;
                } else {
                    logger.debug("zoneAffinity is overriden. blackOutServerPercentage: {}, activeReqeustsPerServer: {}, availableServers: {}", new Object[]{Double.valueOf((double)circuitBreakerTrippedCount / (double)instanceCount), Double.valueOf(loadPerServer), Integer.valueOf(instanceCount - circuitBreakerTrippedCount)});
                    return false;
                }
            }
        }
    }
ServerListSubsetFilter:



ZonePreferenceServerListFilter:实现了通过配置Eureka的实例元数据的所属区域来过滤出同区域的服务实例



ZoneAwareLoadBalancer:


负载均衡策略:

    


RandomRule:随机选择

RoundRobinRule:线性轮询

RetryRule:实现了一个具备重试机制的实例选择功能

.

.

.









猜你喜欢

转载自blog.csdn.net/u011064905/article/details/80240130