Spring Cloud Netflix负载均衡组件Ribbon介绍

Netflix:[‘netfli?ks]

ribbon:英[?r?b?n]美[?r?b?n]
n. 带; 绶带; (打印机的) 色带; 带状物;
v. 把…撕成条带; 用缎带装饰; 形成带状;

   

LB方案分类

  目前主流的LB方案可分成两类:

  • 一种是集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
  • 另一种是进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。Ribbon就属于后者,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon介绍

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

Ribbon的主要组件与工作流程

Ribbon的核心组件(均为接口类型)有以下几个:

  • ServerList 
    用于获取地址列表。它既可以是静态的(提供一组固定的地址),也可以是动态的(从注册中心中定期查询地址列表)。
  • ServerListFilter 
    仅当使用动态ServerList时使用,用于在原始的服务列表中使用一定策略过虑掉一部分地址。
  • IRule 
    选择一个最终的服务地址作为LB结果。选择策略有轮询、根据响应时间加权、断路器(当Hystrix可用时)等。

Ribbon在工作时首选会通过ServerList来获取所有可用的服务列表,然后通过ServerListFilter过虑掉一部分地址,最后在剩下的地址中通过IRule选择出一台服务器作为最终结果。

与Eureka结合使用

当与Eureka组合使用Ribbon时,ServerList接口会使用DiscoveryEnabledNIWSServerList实现,该实现会通过 EurekaClient 向Eureka服务器获取可用的服务列表。ServerListFilter默认实现为ZonePreferenceServerListFilter,其作用是过虑掉不同zone下的服务(即优先选择同一个zone下的地址)。IRule使用ZoneAvoidanceRule实现,它是一种复合策略,同时使用ZoneAvoidancePredicateAvailabilityPredicate来判断是否选择某个server,前者以一个区域为单位判断可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的server;后者用于过滤掉连接数过多和断路器处于打开状态的server。该复合策略经过上述两次过虑后最终选择出一个地址作为LB结果。

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

简单轮询负载均衡(RoundRobin)

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

Java代码  

  1. Server server = null;
  2. int count = 0;
  3. while (server == null && count++ < 10) {
  4. List<Server> reachableServers = lb.getReachableServers();
  5. List<Server> allServers = lb.getAllServers();
  6. int upCount = reachableServers.size();
  7. int serverCount = allServers.size();
  8. if ((upCount == 0) || (serverCount == 0)) {
  9. log.warn("No up servers available from load balancer: " + lb);
  10. return null;
  11. }
  12. int nextServerIndex = incrementAndGetModulo(serverCount);
  13. server = allServers.get(nextServerIndex);
  14. if (server == null) {
  15. /* Transient. */
  16. Thread.yield();
  17. continue;
  18. }
  19. if (server.isAlive() && (server.isReadyToServe())) {
  20. return (server);
  21. }
  22. // Next.
  23. server = null;
  24. }
  25. if (count >= 10) {
  26. log.warn("No available alive servers after 10 tries from load balancer: "
  27. + lb);
  28. }
  29. return server;
  30. ///////////////////////////////////////////////////////////////////
  31. /**
  32. * Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
  33. *
  34. * @param modulo The modulo to bound the value of the counter.
  35. * @return The next value.
  36. */
  37. private int incrementAndGetModulo(int modulo) {
  38. for (;;) {
  39. int current = nextServerCyclicCounter.get();
  40. int next = (current + 1) % modulo;
  41. if (nextServerCyclicCounter.compareAndSet(current, next))
  42. return next;
  43. }
  44. }

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

Html代码  

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

随机负载均衡 (Random)

随机选择状态为UP的Server

Java代码  

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

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

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

Java代码  

  1. The key metric used to measure the zone condition is Average Active Requests,
  2. which is aggregated per rest client per zone. It is the
  3. total outstanding requests in a zone divided by number of available targeted instances (excluding circuit breaker tripped instances).
  4. This metric is very effective when timeout occurs slowly on a bad zone.
  5. 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.
  6. Once the the worst zone is dropped, a zone will be chosen among the rest with the probability proportional to its number of instances.
  7. A server will be returned from the chosen zone with a given Rule (A Rule is a load balancing strategy, for example {@link AvailabilityFilteringRule})
  8. 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.

具体实现:

Java代码  

  1. @Override
  2. protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
  3. super.setServerListForZones(zoneServersMap);
  4. if (balancers == null) {
  5. balancers = new ConcurrentHashMap<String, BaseLoadBalancer>();
  6. }
  7. for (Map.Entry<String, List<Server>> entry: zoneServersMap.entrySet()) {
  8. String zone = entry.getKey().toLowerCase();
  9. getLoadBalancer(zone).setServersList(entry.getValue());
  10. }
  11. // check if there is any zone that no longer has a server
  12. // and set the list to empty so that the zone related metrics does not
  13. // contain stale data
  14. for (Map.Entry<String, BaseLoadBalancer> existingLBEntry: balancers.entrySet()) {
  15. if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) {
  16. existingLBEntry.getValue().setServersList(Collections.emptyList());
  17. }
  18. }
  19. }
  20. @Override
  21. public Server chooseServer(Object key) {
  22. if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
  23. logger.debug("Zone aware logic disabled or there is only one zone");
  24. return super.chooseServer(key);
  25. }
  26. Server server = null;
  27. try {
  28. LoadBalancerStats lbStats = getLoadBalancerStats();
  29. Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
  30. logger.debug("Zone snapshots: {}", zoneSnapshot);
  31. if (triggeringLoad == null) {
  32. triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
  33. "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
  34. }
  35. if (triggeringBlackoutPercentage == null) {
  36. triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
  37. "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
  38. }
  39. Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
  40. logger.debug("Available zones: {}", availableZones);
  41. if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
  42. String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
  43. logger.debug("Zone chosen: {}", zone);
  44. if (zone != null) {
  45. BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
  46. server = zoneLoadBalancer.chooseServer(key);
  47. }
  48. }
  49. } catch (Throwable e) {
  50. logger.error("Unexpected exception when choosing server using zone aware logic", e);
  51. }
  52. if (server != null) {
  53. return server;
  54. } else {
  55. logger.debug("Zone avoidance logic is not invoked.");
  56. return super.chooseServer(key);
  57. }
  58. }
  59. @VisibleForTesting
  60. BaseLoadBalancer getLoadBalancer(String zone) {
  61. zone = zone.toLowerCase();
  62. BaseLoadBalancer loadBalancer = balancers.get(zone);
  63. if (loadBalancer == null) {
  64. // We need to create rule object for load balancer for each zone
  65. IRule rule = cloneRule(this.getRule());
  66. loadBalancer = new BaseLoadBalancer(this.getName() + "_" + zone, rule, this.getLoadBalancerStats());
  67. BaseLoadBalancer prev = balancers.putIfAbsent(zone, loadBalancer);
  68. if (prev != null) {
  69. loadBalancer = prev;
  70. }
  71. }
  72. return loadBalancer;
  73. }
  74. private IRule cloneRule(IRule toClone) {
  75. IRule rule;
  76. if (toClone == null) {
  77. rule = new AvailabilityFilteringRule();
  78. } else {
  79. String ruleClass = toClone.getClass().getName();
  80. try {
  81. rule = (IRule) ClientFactory.instantiateInstanceWithClientConfig(ruleClass, this.getClientConfig());
  82. catch (Exception e) {
  83. throw new RuntimeException("Unexpected exception creating rule for ZoneAwareLoadBalancer", e);
  84. }
  85. return rule;
  86. }
  87. @Override
  88. public void setRule(IRule rule) {
  89. super.setRule(rule);
  90. if (balancers != null) {
  91. for (String zone: balancers.keySet()) {
  92. balancers.get(zone).setRule(cloneRule(rule));
  93. }
  94. }
  95. }

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项目:

Xml代码  

  1. <dependency>
  2. <groupId>com.netflix.ribbon</groupId>
  3. <artifactId>ribbon-core</artifactId>
  4. <version>2.2.0</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.netflix.ribbon</groupId>
  8. <artifactId>ribbon-httpclient</artifactId>
  9. <version>2.2.0</version>
  10. </dependency>

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

Java代码  

  1. # Max number of retries
  2. sample-client.ribbon.MaxAutoRetries=1
  3. # Max number of next servers to retry (excluding the first server)
  4. sample-client.ribbon.MaxAutoRetriesNextServer=1
  5. # Whether all operations can be retried for this client
  6. sample-client.ribbon.OkToRetryOnAllOperations=true
  7. # Interval to refresh the server list from the source
  8. sample-client.ribbon.ServerListRefreshInterval=2000
  9. # Connect timeout used by Apache HttpClient
  10. sample-client.ribbon.ConnectTimeout=3000
  11. # Read timeout used by Apache HttpClient
  12. sample-client.ribbon.ReadTimeout=3000
  13. # Initial list of servers, can be changed via Archaius dynamic property at runtime
  14. sample-client.ribbon.listOfServers=www.sohu.com:80,www.163.com:80,www.sina.com.cn:80
  15. sample-client.ribbon.EnablePrimeConnections=true

3. 代码:

Java代码  

  1. public static void main( String[] args ) throws Exception {
  2. ConfigurationManager.loadPropertiesFromResources("sample-client.properties");
  3. System.out.println(ConfigurationManager.getConfigInstance().getProperty("sample-client.ribbon.listOfServers"));
  4. RestClient client = (RestClient)ClientFactory.getNamedClient("sample-client");
  5. HttpRequest request = HttpRequest.newBuilder().uri(new URI("/")).build();
  6. for(int i = 0; i < 20; i ++) {
  7. HttpResponse response = client.executeWithLoadBalancer(request);
  8. System.out.println("Status for URI:" + response.getRequestedURI() + " is :" + response.getStatus());
  9. }
  10. ZoneAwareLoadBalancer lb = (ZoneAwareLoadBalancer) client.getLoadBalancer();
  11. System.out.println(lb.getLoadBalancerStats());
  12. ConfigurationManager.getConfigInstance().setProperty("sample-client.ribbon.listOfServers", "www.baidu.com:80,www.linkedin.com:80");
  13. System.out.println("changing servers ...");
  14. Thread.sleep(3000);
  15. for(int i = 0; i < 20; i ++) {
  16. HttpResponse response = client.executeWithLoadBalancer(request);
  17. System.out.println("Status for URI:" + response.getRequestedURI() + " is :" + response.getStatus());
  18. }
  19. System.out.println(lb.getLoadBalancerStats());
  20. }
时间: 2024-10-07 10:24:33

Spring Cloud Netflix负载均衡组件Ribbon介绍的相关文章

客户端负载均衡Ribbon之一:Spring Cloud Netflix负载均衡组件Ribbon介绍

Netflix:['netfli?ks] ribbon:英[?r?b?n]美[?r?b?n]n. 带; 绶带; (打印机的) 色带; 带状物;v. 把…撕成条带; 用缎带装饰; 形成带状;     LB方案分类 目前主流的LB方案可分成两类: 一种是集中式LB, 即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方: 另一种是进程内LB,将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址

Spring Cloud Alibaba之负载均衡组件 - Ribbon

负载均衡 我们都知道在微服务架构中,微服务之间总是需要互相调用,以此来实现一些组合业务的需求.例如组装订单详情数据,由于订单详情里有用户信息,所以订单服务就得调用用户服务来获取用户信息.要实现远程调用就需要发送网络请求,而每个微服务都可能会存在有多个实例分布在不同的机器上,那么当一个微服务调用另一个微服务的时候就需要将请求均匀的分发到各个实例上,以此避免某些实例负载过高,某些实例又太空闲,所以在这种场景必须要有负载均衡器. 目前实现负载均衡主要的两种方式: 1.服务端负载均衡:例如最经典的使用N

Spring Cloud Netflix子模块综合整理-Ribbon

客户端负载均衡器:Rbbion Ribbon是一个客户端负载均衡器,可以让您对HTTP和TCP客户端的行为进行大量控制. Feign已使用Ribbon,因此如果您使用的是@FeignClient,则此部分也适用. Ribbon中的一个核心概念是指定客户端的概念.每个负载均衡器都是组件集成的一部分,这些组件协同工作以按需联系远程服务器,并且集成有共开发人员引用的名称(例如,使用@FeignClient注解).Spring Cloud使用RibbonClientConfiguration按需为每个命

spring cloud 客户端负载均衡 - Ribbon

Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现的,Ribbon不像注册中心.网关那样需要单独部署,它是作为一个工具直接集成到Service里.后面要讲到的Feign里面也集成了Ribbon. 1.手动搭建一个客户端负载均衡 准备工作: 准备一个由 peer1.peer2 构成的配置中心 准备一个由 service-1(8091).service-1(8092) 构成的服务端集群 准备一个Ribbon客户端 添加pom依赖

spring boot分布式技术,spring cloud,负载均衡,配置管理器

spring boot分布式的实现,使用spring cloud技术. 下边是我理解的spring cloud的核心技术: 1.配置服务器 2.注册发现服务器eureka(spring boot默认使用的服务器类别) 3.RestTemplate通过ribbon实现负载均衡技术 具体请看下列图: 参考:http://www.cnblogs.com/skyblog/p/5127690.html

Spring Cloud做负载均衡

1.新建maven工程,点击finish完成     2.在pom.xml文件中加入必要的依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLoca

Spring Cloud Netflix概览和架构设计

Spring Cloud简介 Spring Cloud是基于Spring Boot的一整套实现微服务的框架.他提供了微服务开发所需的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等组件.最重要的是,跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便. Spring Cloud包含了非常多的子框架,其中,Spring Cloud Netflix是其中一套框架,由Netflix开发后来又并入Spring Cloud大

Spring Cloud netflix 概览和架构设计

pring Cloud是基于Spring Boot的一整套实现微服务的框架.他提供了微服务开发所需的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布式会话和集群状态管理等组件.最重要的是,跟spring boot框架一起使用的话,会让你开发微服务架构的云服务非常好的方便.Spring Cloud包含了非常多的子框架,其中,Spring Cloud netflix是其中一套框架,由Netflix开发后来又并入Spring Cloud大家庭,它主要提供的模块包括:服务发

spring-cloud-feign负载均衡组件

Feign简介: Feign是一个声明式的Web服务客户端,使用Feign可使得Web服务客户端的写入更加方便.它具有可插拔注释支持,包括Feign注解和JAX-RS注解.Feign还支持可插拔编码器和解码器.Spring Cloud增加了对Spring MVC注释的支持,并HttpMessageConverters在Spring Web中使用了默认使用的相同方式.Spring Cloud集成了Ribbon和Eureka,在使用Feign时提供负载平衡的http客户端.Fegin对Robbin进