`
lxlong
  • 浏览: 81773 次
社区版块
存档分类
最新评论

Netflix学习笔记:Ribbon

 
阅读更多

Ribbon是什么?

Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。

Ribbon提供的主要负载均衡策略介绍

简单轮询负载均衡(RoundRobin)

以轮询的方式依次将请求调度不同的服务器,即每次调度执行i = (i + 1) mod n,并选出第i台服务器。

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            List<Server> 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();
            int next = (current + 1) % modulo;
            if (nextServerCyclicCounter.compareAndSet(current, next))
                return next;
        }
    }

 

加权响应时间负载均衡 (WeightedResponseTime)

The basic idea for weighted round robin has been obtained from JCS
  The implementation for choosing the endpoint from the list of endpoints
  is as follows:Let's assume 4 endpoints:A(wt=10), B(wt=30), C(wt=40), 
  D(wt=20). 
  
  Using the Random API, generate a random number between 1 and10+30+40+20.
  Let's assume that the above list is randomized. Based on the weights, we
  have intervals as follows:
  
  1-----10 (A's weight)
  
  11----40 (A's weight + B's weight)
  
  41----80 (A's weight + B's weight + C's weight)
  
  81----100(A's weight + B's weight + C's weight + D's weight)
  
  Here's the psuedo code for deciding where to send the request:
  
  if (random_number between 1 &amp; 10) {send request to A;}
  
  else if (random_number between 11 &amp; 40) {send request to B;}
  
  else if (random_number between 41 &amp; 80) {send request to C;}
  
  else if (random_number between 81 &amp; 100) {send request to D;}
  

 

随机负载均衡 (Random)

随机选择状态为UP的Server

int index = rand.nextInt(serverCount);
server = upList.get(index);

 

区域感知轮询负载均衡(ZoneAware)

区域感知负载均衡内置电路跳闸逻辑,可被配置基于区域同源关系(Zone Affinity,也就是更倾向于选择发出调用的服务所在的托管区域内,这样可以降低延迟,节省成本)选择目标服务实例。它监控每个区域中运行实例的行为,而且能够实时的快速丢弃一整个区域。这样在面对整个区域故障时,帮我们提升了弹性。

The key metric used to measure the zone condition is Average Active Requests,
which is aggregated per rest client per zone. It is the
total outstanding requests in a zone divided by number of available targeted instances (excluding circuit breaker tripped instances).
This metric is very effective when timeout occurs slowly on a bad zone.

The  LoadBalancer will calculate and examine zone stats of all available zones. If the Average Active Requests for any zone has reached a configured threshold, this zone will be dropped from the active server list. In case more than one zone has reached the threshold, the zone with the most active requests per server will be dropped.
Once the the worst zone is dropped, a zone will be chosen among the rest with the probability proportional to its number of instances.
A server will be returned from the chosen zone with a given Rule (A Rule is a load balancing strategy, for example {@link AvailabilityFilteringRule})
For each request, the steps above will be repeated. That is to say, each zone related load balancing decisions are made at real time with the up-to-date statistics aiding the choice.

 

具体实现:

    @Override
    protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
        super.setServerListForZones(zoneServersMap);
        if (balancers == null) {
            balancers = new ConcurrentHashMap<String, BaseLoadBalancer>();
        }
        for (Map.Entry<String, List<Server>> entry: zoneServersMap.entrySet()) {
        	String zone = entry.getKey().toLowerCase();
            getLoadBalancer(zone).setServersList(entry.getValue());
        }
        // check if there is any zone that no longer has a server
        // and set the list to empty so that the zone related metrics does not
        // contain stale data
        for (Map.Entry<String, BaseLoadBalancer> existingLBEntry: balancers.entrySet()) {
            if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) {
                existingLBEntry.getValue().setServersList(Collections.emptyList());
            }
        }
    }    
        
    @Override
    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
        Server server = null;
        try {
            LoadBalancerStats lbStats = getLoadBalancerStats();
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            logger.debug("Zone snapshots: {}", zoneSnapshot);
            if (triggeringLoad == null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
            }

            if (triggeringBlackoutPercentage == null) {
                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            logger.debug("Available zones: {}", availableZones);
            if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                logger.debug("Zone chosen: {}", zone);
                if (zone != null) {
                    BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                    server = zoneLoadBalancer.chooseServer(key);
                }
            }
        } catch (Throwable e) {
            logger.error("Unexpected exception when choosing server using zone aware logic", e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            return super.chooseServer(key);
        }
    }
     
    @VisibleForTesting
    BaseLoadBalancer getLoadBalancer(String zone) {
        zone = zone.toLowerCase();
        BaseLoadBalancer loadBalancer = balancers.get(zone);
        if (loadBalancer == null) {
        	// We need to create rule object for load balancer for each zone
        	IRule rule = cloneRule(this.getRule());
            loadBalancer = new BaseLoadBalancer(this.getName() + "_" + zone, rule, this.getLoadBalancerStats());
            BaseLoadBalancer prev = balancers.putIfAbsent(zone, loadBalancer);
            if (prev != null) {
            	loadBalancer = prev;
            }
        } 
        return loadBalancer;        
    }

    private IRule cloneRule(IRule toClone) {
    	IRule rule;
    	if (toClone == null) {
    		rule = new AvailabilityFilteringRule();
    	} else {
    		String ruleClass = toClone.getClass().getName();        		
    		try {
				rule = (IRule) ClientFactory.instantiateInstanceWithClientConfig(ruleClass, this.getClientConfig());
			} catch (Exception e) {
				throw new RuntimeException("Unexpected exception creating rule for ZoneAwareLoadBalancer", e);
			}
    	}
    	return rule;
    }
    
       
    @Override
    public void setRule(IRule rule) {
        super.setRule(rule);
        if (balancers != null) {
            for (String zone: balancers.keySet()) {
                balancers.get(zone).setRule(cloneRule(rule));
            }
        }
    }

 

 

Ribbon自带负载均衡策略比较(转)

策略名 策略声明 策略描述 实现说明
BestAvailableRule public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule 选择一个最小的并发请求的server 逐个考察Server,如果Server被tripped了,则忽略,在选择其中ActiveRequestsCount最小的server
AvailabilityFilteringRule public class AvailabilityFilteringRule extends PredicateBasedRule 过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值) 使用一个AvailabilityPredicate来包含过滤server的逻辑,其实就就是检查status里记录的各个server的运行状态
WeightedResponseTimeRule public class WeightedResponseTimeRule extends RoundRobinRule 根据相应时间分配一个weight,相应时间越长,weight越小,被选中的可能性越低。 一个后台线程定期的从status里面读取评价响应时间,为每个server计算一个weight。Weight的计算也比较简单responsetime 减去每个server自己平均的responsetime是server的权重。当刚开始运行,没有形成statas时,使用roubine策略选择server。
RetryRule public class RetryRule extends AbstractLoadBalancerRule 对选定的负载均衡策略机上重试机制。 在一个配置时间段内当选择server不成功,则一直尝试使用subRule的方式选择一个可用的server
RoundRobinRule public class RoundRobinRule extends AbstractLoadBalancerRule roundRobin方式轮询选择server 轮询index,选择index对应位置的server
RandomRule public class RandomRule extends AbstractLoadBalancerRule 随机选择一个server 在index上随机,选择index对应位置的server
ZoneAvoidanceRule public class ZoneAvoidanceRule extends PredicateBasedRule 复合判断server所在区域的性能和server的可用性选择server 使用ZoneAvoidancePredicate和AvailabilityPredicate来判断是否选择某个server,前一个判断判定一个zone的运行性能是否可用,剔除不可用的zone(的所有server),AvailabilityPredicate用于过滤掉连接数过多的Server。

 

Ribbon架构图

 Ribbon使用举例:

1. 创建Maven项目:

		<dependency>
			<groupId>com.netflix.ribbon</groupId>
			<artifactId>ribbon-core</artifactId>
			<version>2.2.0</version>
		</dependency>
		<dependency>
			<groupId>com.netflix.ribbon</groupId>
			<artifactId>ribbon-httpclient</artifactId>
			<version>2.2.0</version>
		</dependency>

 

2. 配置properties file (sample-client.properties)

# Max number of retries 
sample-client.ribbon.MaxAutoRetries=1

# Max number of next servers to retry (excluding the first server)
sample-client.ribbon.MaxAutoRetriesNextServer=1

# Whether all operations can be retried for this client
sample-client.ribbon.OkToRetryOnAllOperations=true

# Interval to refresh the server list from the source
sample-client.ribbon.ServerListRefreshInterval=2000

# Connect timeout used by Apache HttpClient
sample-client.ribbon.ConnectTimeout=3000

# Read timeout used by Apache HttpClient
sample-client.ribbon.ReadTimeout=3000

# Initial list of servers, can be changed via Archaius dynamic property at runtime
sample-client.ribbon.listOfServers=www.sohu.com:80,www.163.com:80,www.sina.com.cn:80

sample-client.ribbon.EnablePrimeConnections=true

 

3. 代码:

    public static void main( String[] args ) throws Exception {
        ConfigurationManager.loadPropertiesFromResources("sample-client.properties");
        System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
        
        RestClient client = (RestClient)ClientFactory.getNamedClient("sample-client");
        HttpRequest request = HttpRequest.newBuilder().uri(new URI("/")).build();
        
        for(int i = 0; i < 20; i ++) {
        	HttpResponse response = client.executeWithLoadBalancer(request);
        	System.out.println("Status for URI:" + response.getRequestedURI() + " is :" + response.getStatus());
        }
        
        ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer) client.getLoadBalancer();
        System.out.println(lb.getLoadBalancerStats());
        
        ConfigurationManager.getConfigInstance().setProperty("sample-client.ribbon.listOfServers", "www.baidu.com:80,www.linkedin.com:80");
        
        System.out.println("changing servers ...");
        Thread.sleep(3000);
        
        for(int i = 0; i < 20; i ++) {
        	HttpResponse response = client.executeWithLoadBalancer(request);
        	System.out.println("Status for URI:" + response.getRequestedURI() + " is :" + response.getStatus());
        }
        System.out.println(lb.getLoadBalancerStats());
    }

 

 

  • 大小: 87.4 KB
分享到:
评论

相关推荐

    SpringCloud学习笔记SpringCloud学习笔记

    Ribbon:客户端负载均衡,用于访问注册到Eureka的服务。 Feign:简单易用的API客户端生成器。 ** Zuul**:一个基于Spring MVC的API网关,支持路由、过滤等功能。 Spring Cloud也支持其他功能模块,比如Spring Cloud ...

    springcloud学习笔记.docx

    2. **Spring Cloud Netflix**:这是 SpringCloud 的核心组件,它整合了 Netflix 的多个开源组件,如: - **Eureka**:作为服务治理组件,Eureka 提供服务注册与发现功能,可以让服务提供者自动注册到 Eureka 服务器...

    springCloud学习笔记.zip

    这个学习笔记将深入探讨 Spring Cloud 的核心组件和它们的应用场景。 首先,我们要了解 Spring Cloud 的核心组件: 1. **Eureka**:服务注册与发现。Eureka 是 Netflix 提供的服务发现组件,每个服务启动时会在 ...

    《深入理解Spring Cloud与微服务构建》学习笔记(十三)~在RestTemplat和Ribbon上使用熔断器

    本篇学习笔记将聚焦于在使用RestTemplate和Ribbon时如何集成和应用熔断器机制,以提高微服务的容错性和稳定性。 首先,Spring Cloud为微服务提供了强大的支持,其中Hystrix是Netflix开源的一个用于处理服务间的延迟...

    《深入理解Spring Cloud与微服务构建》学习笔记(十一)~使用RestTemplate和Ribbo消费服务

    在本篇学习笔记中,我们将深入探讨如何在Spring Cloud框架下使用RestTemplate和Ribbon来消费服务。Spring Cloud是基于Spring Boot实现的云应用开发工具集,它为开发者提供了在分布式系统(如配置管理、服务发现、...

    《深入理解Spring Cloud与微服务构建》学习笔记(十二)~写一个Feign客户端

    在本篇学习笔记中,我们将深入探讨如何在Spring Cloud框架下构建一个Feign客户端,用于在微服务架构中实现服务间的远程调用。Feign是一个声明式的Web服务客户端,它使得编写HTTP客户端变得简单,而Spring Cloud对...

    Eureka服务注册中心学习笔记

    Eureka是Netflix公司开源的一款基于Java的微服务发现组件,它是Spring Cloud生态体系中的关键一环,用于实现服务的注册与发现。Eureka通过提供一个中心化的服务注册表,使得微服务实例可以在启动时向Eureka注册自己...

    springcloudtext (2)_cloud学习笔记3_

    【SpringCloud学习笔记3:构建微服务架构的关键组件】 在本次学习笔记中,我们将深入探讨SpringCloud生态中的几个核心组件,这些组件对于构建一个完整的微服务架构至关重要。首先,我们从"cloud-gateway-gateway...

    spring cloudnetfix笔记.zip

    通过学习和理解 Spring Cloud Netflix 中的这些组件,你可以提升你的微服务架构设计和实施能力,为构建可扩展、高可用和容错的分布式系统打下坚实基础。在实际开发中,这些笔记将提供宝贵的指导,帮助你更好地运用 ...

    尚硅谷周阳SpringCloud第一季笔记(超详细非官方手工笔记)

    以下将详细解析SpringCloud的核心技术,并结合学习笔记的内容进行阐述。 1. **Eureka服务注册与发现**:Eureka是SpringCloud中的核心组件,用于服务注册和发现。每个微服务启动时都会向Eureka Server注册自身的信息...

    springcloud笔记.pdf

    【SpringCloud笔记】是关于微服务架构的学习资料,主要涵盖了SpringCloud的基本概念、核心组件以及实战技巧。在学习SpringCloud之前,需要具备一定的基础知识,包括IDEA、JDK8、Maven、SpringBoot以及Linux环境的...

    SpringCloud笔记+思维导图

    本资料包含SpringCloud的学习笔记和思维导图,旨在帮助开发者深入理解并掌握SpringCloud的核心概念和技术。 1. **服务发现**:SpringCloud采用了Eureka作为服务注册与发现的工具。Eureka服务器作为服务注册中心,每...

    123ddddggrew.zip

    在这个案例中,它包含了关于SpringCloud的学习笔记。SpringCloud是基于Spring Boot的一个微服务框架,它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线等)中快速构建一些...

    尚硅谷周阳SpringCloud第一季笔记

    3. **Ribbon客户端负载均衡器**:Ribbon是Netflix开发的一个客户端负载均衡器,它与Eureka结合,可以自动从服务注册中心获取服务列表并进行负载均衡。 4. **Hystrix容错管理工具**:Hystrix提供断路器模式,当服务...

    SpringCloud笔记.rar

    2. **Ribbon客户端负载均衡**:Ribbon是Netflix提供的一个客户端负载均衡器,集成在Spring Cloud中,可以配合Eureka进行服务的消费,支持多种负载策略。 3. **Hystrix熔断器**:Hystrix用于实现服务的容错管理,当...

    尚硅谷周阳老师SpringCloud笔记

    本资源“尚硅谷周阳老师SpringCloud笔记”涵盖了SpringCloud的核心概念和技术,是学习这一框架的理想材料。周阳老师是业界知名的IT教育专家,他的讲解深入浅出,有助于初学者快速理解和掌握SpringCloud的精髓。 ...

    SpringCloud学习笔记(十)声明式客户端Feign的简单调用 源码包

    - **负载均衡**: Feign内置了Ribbon,可以自动进行客户端负载均衡,选择合适的服务器进行请求。 - **断路器**: 结合Hystrix,Feign可以实现熔断和降级策略,提高系统的容错性。 2. **Feign使用步骤** - **创建...

    sprinCloud笔记总结实习

    【Spring Cloud笔记总结实习】 Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具集,它为开发者提供了在分布式系统(如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、...

Global site tag (gtag.js) - Google Analytics