Ribbon

2019-10-20  本文已影响0人  一生逍遥一生

Ribbon的负载均衡主要与集群中的各个服务器进行通信,负载均衡器需要提供一下基础功能:

  • 维护服务器的ip、DNS名称等信息
  • 根据特定的逻辑在服务列表中循环。

为了实现负载均衡的处理功能,Ribbon的负载均衡器有一下三大子模块:

  • Rule:决定从服务器列表中返回那个服务器实例。
  • Ping:使用定时器来确保服务器网络可以连接。
  • ServerList:通过静态的配置确定负载的服务器服务器,也可以动态指定服务器列表。
  • ServerListFilter: 可以做处理,决定优先调用哪些服务
  • LoadBalancer: 负载均衡器调度管理

Ribbon自带的负载规则

  • RoundRobinRule:系统默认的规则,通过简单轮询服务列表来选择服务器。
  • AvailabilityFilteringRule:该规则会忽略一下服务器。
    • 无法连接的服务器。默认情况下,3次连接失败,服务器会被置为短路的状态,状态持续为30秒;再次连接失败,短路的状态持续时间将会以几何数增加。可以通过修改niws.loadbalancer.<clientName>.connectionFailureCountThreshold属性,配置连接失败的次数。
    • 并发数过高的服务器。可以修改<clientName>.ribbon.ActiveConnectionsLimit属性来设置最高并发数。
  • WeightedResponseTimeRule: 为每个服务器赋予一个权重值,服务器的响应时间越长,权重就越小,随机选择服务器,权重值有可能会决定服务器的选择。
  • ZoneAvoidanceRule: 该规则以区域、可用服务器为基础进行服务器选择。使用Zone对服务器进行分类。
  • BestAvailableRule: 忽略短路的服务器,并选择并发数较低的服务器。
  • RandomeRule: 随机选择可用的服务器。
  • RetryRule: 含有重试的选择逻辑。

IClientConfig

IClientConfig的实现类为DefaultClientConfigImpl。
DefaultClientConfigImpl是默认的客户端配置,可以从Archaius ConfigurationManager加载属性。
配置格式为:<clientName>.<nameSpace>.<propertyName>=<value>

IPing

检查实例是否存活。如何ping。实现类:

  • NoOpPing: 不进行Ping。
  • DummyPing:默认实现,标记存活的服务器。
  • NIWSDiscoveryPing: 假设服务器存活。
  • PingUrl: 一种健康检查的ping。

配置如下:
<client>.ribbon.NFLoadBalancerPingClassName: 配置IPing的实现类。
<client>.ribbon.NFLoadBalancerPingInterval: 配置Ping操作的时间间隔。

ILoadBalancer

LoadBalancer的组成:
1.一个基于特定条件可能进行存储的服务器列表。
2.一个类:通过IRule实现并定义LoadBalancing策略。
3.该类定义并实现一种机制,用户确定列表中节点/服务器的实用性/可用性。
LoadBalancer的实现类:

  • NoOpLoadBalancer: 没有实现
  • BaseLoadBalancer: 基本的实现,ping确定存活的服务器列表。
  • DynamicServerListLoadBalancer: 动态获取的服务器列表。
  • ZoneAwareLoadBalancer: LoadBalancer将计算并检查所有可用区域的区域统计信息。如果任何区域的“平均活动请求数”已达到配置的阈值,
    则该区域将从活动服务器列表中删除。如果多个区域已达到阈值,则将删除每台服务器上最活跃请求的区域。一旦删除了最坏的区域,
    将在其余区域中选择一个区域,其概率与其实例数成正比。服务器将从具有指定规则的选定区域返回。
    每个区域相关的负载平衡决策都是在最新统计信息的帮助下实时做出的。

ServerList<Server>

获取服务器列表。

  • DiscoveryEnabledNIWSServerList: 从Eureka 客户端获取服务器列表。
  • DomainExtractingServerList: 基于domain获取服务列表。
  • ConfigurationBasedServerList: 从配置中获取服务器列表,例如:从Archaius中获取属性。
    <clientName>.<nameSpace>.listOfServer=hostname:port 用服务器分开
  • StaticServerList: 使用ribbon的服务器列表。

ServerListFilter

在获取的服务器列表中进行获取。

  • ZoneAffinityServerListFilter: 根据区域亲缘关系过滤服务器。在使用这个过滤器时,需要开启CommonClientConfig#EnableZoneAffinity或者
    CommonClientConfigKey#EnableZoneExclusivity=true。开启后,同一个区域之外的服务器将被过滤。默认情况下,区域亲和力和排他性是关闭的,并且
    不会过滤任何内容。
  • ZonePreferenceServerListFilter: 主动首选本地区域的过滤器。
  • ServerListSubSetFilter: 服务器列表过滤器,将负载均衡器使用的服务器数量限制为所有服务器的子集。

ServerListUpdater

更新服务器列表。

  • EurekaNotificationServerListUpdater: 利用Eureka的时间监听器出发LB缓存更新。
  • PollingServerListUpdater: 默认的策略动态更新服务器列表。

<clientName>.ribbon.NFLoadBalancerClassName: 指定负载均衡器的列表。
<clientName>.ribbon.NFWSServerListClassName: 服务器列表处理类,用来维护服务器列表,Ribbon已经实现动态服务器列表。
<clientName>.ribbon.NIWSServerListFilterClassName: 用于处理服务器列表拦截。

<clientName>.ribbon.NFLoadBalancerClassName: 需要实现ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName: 需要实现IRule
<clientName>.ribbon.NFLoadBalancerPingClassName: 需要实现IPing
<clientName>.ribbon.NIWSServerListClassName: 需要实现ServerList
<clientName>.ribbon.NIWSServerListFilterClassName: 需要实现ServerListFilter

默认的配置

ribbon默认的配置项

Bean Type BeanName ClassName
IClientConfig ribbonClientConfig DefaultClientConfigImpl
IRule ribbonRule ZoneAvoidanceRule
IPing ribbonPing DummyPing
ServerList<Server> ribbonServerList ConfigurationBasedServerList
ServerListFilter<Server> ribbonServerListFilter ZonedPreferenceServerListFilter
ILoadBalancer ribbonLoadBalancer ZonedAwareLoadBalancer
ServerListUpdater ribbonServerListUpdater PollingServerListerUpdater

自定义

package com.edu.helloserver;
import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface CustomLoadBalancer {
}
package com.edu.helloserver;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;

import java.net.URI;

public class CustomHttpRequest implements HttpRequest {
    private HttpRequest httpRequest;

    public CustomHttpRequest(HttpRequest httpRequest) {
        this.httpRequest = httpRequest;
    }

    @Override
    public String getMethodValue() {
        return httpRequest.getMethodValue();
    }

    @Override
    public URI getURI() {
        try {
            String oldUri = httpRequest.getMethodValue().toString();
            URI newUri = new URI("http://localhost:8080/hello");
            return newUri;
        }catch (Exception e){
            e.printStackTrace();
        }
        return httpRequest.getURI();
    }

    @Override
    public HttpHeaders getHeaders() {
        return httpRequest.getHeaders();
    }
}
package com.edu.helloserver;

import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;

import java.io.IOException;

public class CustomerInterceptor implements ClientHttpRequestInterceptor {
    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        System.out.println("===custom interceptor===");
        System.out.println("===Original URI:"+request.getURI()+" ===");
        CustomHttpRequest httpRequest = new CustomHttpRequest(request);
        System.out.println("===New URI:"+httpRequest.getURI()+" ===");
        return execution.execute(httpRequest,body);
    }
}
package com.edu.helloserver;

import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Configuration
public class CustomAutoConfiguration {
    @Autowired(required = false)
    @CustomLoadBalancer
    private List<RestTemplate> customeRestTemplate = Collections.emptyList();

    @Bean
    public SmartInitializingSingleton customLoadBalancedRestTemplateInitializer() {
        System.out.println("====  这个Bean将在容器初始化时创建    =====");
        return new SmartInitializingSingleton() {

            public void afterSingletonsInstantiated() {
                for(RestTemplate tpl : customeRestTemplate) {
                    // 创建一个自定义的拦截器实例
                    CustomerInterceptor mi = new CustomerInterceptor();
                    // 获取RestTemplate原来的拦截器
                    List list = new ArrayList(tpl.getInterceptors());
                    // 添加到拦截器集合
                    list.add(mi);
                    // 将新的拦截器集合设置到RestTemplate实例
                    tpl.setInterceptors(list);
                }
            }
        };
    }
}

Ribbon重试机制

参考文献:http://cxytiandi.com/blog/detail/12507

饥饿加载

参考文献:http://blog.didispace.com/spring-cloud-tips-ribbon-eager/

主机名是不能包含_等特殊字符。

参考文献

Client Side Load Balancer: Ribbon
Spring Cloud Ribbon 重试机制
Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式
Spring Cloud实战小贴士:Zuul的饥饿加载(eager-load)使用

上一篇下一篇

猜你喜欢

热点阅读