springCloud Ribbon源碼淺析

springboot 版本2.1.3.RELEASE
springCloud版本Greenwich.RELEASE

LoadBalancerAutoConfiguration

截取重要部分

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

    //這裏的LoadBalanced整合了@Qualifier
    //加載所有帶@LoadBalanced標示的RestTemplate集合
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

    
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
	}

    @Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
	}

	@Configuration
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
		//創建LoadBalancerInterceptor攔截器
        @Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

        //將攔截器加入到restTemplate中
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}
}

接上面LoadBalancerInterceptor攔截器中

    public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

    //這裏的LoadBalancerClient默認是引用RibbonLoadBalancerClient
	private LoadBalancerClient loadBalancer;
	private LoadBalancerRequestFactory requestFactory;

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
		this.loadBalancer = loadBalancer;
		this.requestFactory = requestFactory;
	}

	public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
		// for backwards compatibility
		this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
	}
    
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
        //重點在這裏RibbonLoadBalancerClient.execute
		return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
	}
}


//在這裏引入了RibbonLoadBalancerClient
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
    @Bean
	@ConditionalOnMissingBean(LoadBalancerClient.class)
	public LoadBalancerClient loadBalancerClient() {
		return new RibbonLoadBalancerClient(springClientFactory());
	}
}

繼續看RibbonLoadBalancerClient.execute

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
		//springCloud使用了ZoneAwareLoadBalancer實現類
        ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
        //這裏根據負載規則獲取具體的server信息
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
				serviceId), serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
    
    
    @Override
	public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
		Server server = null;
		if(serviceInstance instanceof RibbonServer) {
			server = ((RibbonServer)serviceInstance).getServer();
		}
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}

		RibbonLoadBalancerContext context = this.clientFactory
				.getLoadBalancerContext(serviceId);
		RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

		try {
           //傳入需要請求的服務實例定義serviceInstance,裏面包括了實例id、端口、地址等信息
           //到這一步Ribbon攔截結束,調用ClientHttpRequestExecution.execute
			T returnVal = request.apply(serviceInstance);
            //Ribbon對服務請求進行了跟蹤將調用的情況存入ServerStats中
			//後續負載均衡器進行choose計算時會使用到
			statsRecorder.recordStats(returnVal);
			return returnVal;
		}
		// catch IOException and rethrow so RestTemplate behaves correctly
		catch (IOException ex) {
			statsRecorder.recordStats(ex);
			throw ex;
		}
		catch (Exception ex) {
			statsRecorder.recordStats(ex);
			ReflectionUtils.rethrowRuntimeException(ex);
		}
		return null;
	}

在這裏重點是ILoadBalancer接口

public interface ILoadBalancer {
    //添加新的服務實例
    void addServers(List<Server> var1);
    
    //根據一個key選擇服務實例
    Server chooseServer(Object var1);

    //標記一個服務實例down
    void markServerDown(Server var1);

    /** @deprecated */
    @Deprecated
    List<Server> getServerList(boolean var1);

    //獲取所有可用的服務實例(UP)
    List<Server> getReachableServers();

    //獲取所有的服務實例
    List<Server> getAllServers();
}

看一下有哪些實現類
fcc717f487cc0980636365001e6ba28a.png

這裏springCloud使用了ZoneAwareLoadBalancer

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@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) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}
}

接下來看下ILoadBalancer的各個具體實現

AbstractLoadBalancer
public abstract class AbstractLoadBalancer implements ILoadBalancer {
    
    //定義了服務的分組
    public enum ServerGroup{
        ALL,
        STATUS_UP,
        STATUS_NOT_UP        
    }
    
    public Server chooseServer() {
    	return chooseServer(null);
    }

   
    public abstract List<Server> getServerList(ServerGroup serverGroup);
    
    //LoadBalancerStats被用來存儲各個服務實例的當前屬性和統計信息,是制定負載均衡策略的重要依據
    public abstract LoadBalancerStats getLoadBalancerStats();    
}
BaseLoadBalancer

截取部分重要代碼

//Ribbon負載均衡器的基本實現
public class BaseLoadBalancer extends AbstractLoadBalancer implements
        PrimeConnections.PrimeConnectionListener, IClientConfigAware {
    
    //默認的負載規則,輪詢
    private final static IRule DEFAULT_RULE = new RoundRobinRule();
    //默認的ping策略,線性檢查服務狀態
    private final static SerialPingStrategy DEFAULT_PING_STRATEGY = new SerialPingStrategy();
    
    //檢查服務是否存活,實現boolean isAlive(Server server)方法
    protected IPing ping = null;
    
    @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> allServerList = Collections
            .synchronizedList(new ArrayList<Server>());
    @Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> upServerList = Collections
            .synchronizedList(new ArrayList<Server>());
            
   public BaseLoadBalancer() {
        this.name = DEFAULT_NAME;
        this.ping = null;
        setRule(DEFAULT_RULE);
        //啓動一個健康檢查任務 10秒檢查一次
        setupPingTask();
        lbStats = new LoadBalancerStats(DEFAULT_NAME);
    }
    
    //選擇一個具體服務實例,最終是交給IRule接口處理
    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
}
DynamicServerListLoadBalancer

ServerList


public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    
    //ServerList接口封裝了對serverList的處理,定義了倆個方法 分別處理serverList的初始化和更新
    volatile ServerList<T> serverListImpl;
    
    protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
        @Override
        public void doUpdate() {
            //這個方法最終調用了serverListImpl.getUpdatedListOfServers()更新serverList列表
            updateListOfServers();
        }
    };
    
    
    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            //調用ServerList接口更新serverList列表
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);

            if (filter != null) {
                //這裏調用了過濾器
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                        getIdentifier(), servers);
            }
        }
        updateAllServerList(servers);
    }
    
    //服務更新器,默認實現類是PollingServerListUpdater
    //實現了synchronized void start(final UpdateAction updateAction),開啓定時器30秒調用一次doUpdate()
    protected volatile ServerListUpdater serverListUpdater;
    //
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> filter,
                                         ServerListUpdater serverListUpdater) {
        super(clientConfig, rule, ping);
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
            ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
        }
        //這裏最終會調用serverListUpdater.start(final UpdateAction updateAction)
        restOfInit(clientConfig);
    }
}

ServerList接口封裝了對serverList的處理,springCloud的實現類DomainExtractingServerList,最終調用了com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList,從eureka獲取serverList

public interface ServerList<T extends Server> {

    public List<T> getInitialListOfServers();
    
  
    public List<T> getUpdatedListOfServers();   

}

//在這裏springCloud創建了DomainExtractingServerList實現ServerList接口,最終委派給DiscoveryEnabledNIWSServerList處理
@Configuration
public class EurekaRibbonClientConfiguration {
    @Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
		if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
			return this.propertiesFactory.get(ServerList.class, config, serviceId);
		}
		DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
				config, eurekaClientProvider);
		DomainExtractingServerList serverList = new DomainExtractingServerList(
				discoveryServerList, config, this.approximateZoneFromHostname);
		return serverList;
	}
}

服務更新器ServerListUpdater
4803617076f36f0d469cb1fa6b061b6a.png

public interface ServerListUpdater {

    /**
     * 實現更新的具體操作
     */
    public interface UpdateAction {
        void doUpdate();
    }

    /**
     * 開啓服務更新器,調用updateAction
     */
    void start(UpdateAction updateAction);

    /**
     * 停止服務更新器
     */
    void stop();

    /**
     * 獲取最近的更新時間戳
     */
    String getLastUpdate();

    /**
     * 獲取上一次更新到現在的間隔 毫秒
     */
    long getDurationSinceLastUpdateMs();

    /**
     * 獲取錯過的更新週期數
     */
    int getNumberMissedCycles();

    /**
     * 獲取核心線程數
     */
    int getCoreThreads();
}

ServerListFilter過濾器,在updateListOfServers方法中調用

 volatile ServerListFilter<T> filter;

@VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);

            //關鍵在這裏獲取到服務列表後,如果有過濾器則進行過濾
            if (filter != null) {
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                        getIdentifier(), servers);
            }
        }
        updateAllServerList(servers);
    }

ServerListFilter接口定義

public interface ServerListFilter<T extends Server> {

    public List<T> getFilteredListOfServers(List<T> servers);

}

看一下有哪些可用的過濾器
在這裏插入圖片描述
其中AbstractServerListFilter是抽象過濾器,定義了過濾時用到的重要指標LoadBalancerStats

public abstract class AbstractServerListFilter<T extends Server> implements ServerListFilter<T> {

    private volatile LoadBalancerStats stats;
    
    public void setLoadBalancerStats(LoadBalancerStats stats) {
        this.stats = stats;
    }
    
    public LoadBalancerStats getLoadBalancerStats() {
        return stats;
    }

}

ZoneAffinityServerListFilter
基於區域感知的方式實現服務實例過濾

public List<T> getFilteredListOfServers(List<T> servers) {
        //根據自身的zone和服務實例配置的zone進行匹配
        if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
            List<T> filteredServers = Lists.newArrayList(Iterables.filter(
                    servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));
            //這裏對篩選後的結果進行一次分析,用到了LoadBalancerStats和serverStats
            //分析內容 故障實例百分比、實例平均負載、可用實例數
            //如果發現上面的內容有一項不符合要求則放棄此次過濾,保證跨區域高可用
            if (shouldEnableZoneAffinity(filteredServers)) {
                return filteredServers;
            } else if (zoneAffinity) {
                overrideCounter.increment();
            }
        }
        return servers;
    }

DefaultNIWSServerListFilter完全繼承ZoneAffinityServerListFilter
netfix默認使用該過濾器

ServerListSubsetFilter,繼承於ZoneAffinityServerListFilter,通常用在大規模服務器集羣
他除了區域感知之外,還能對剩下的服務實例進行篩選,剔除相對不夠健康的實例

ZonePreferenceServerListFilter繼承於ZoneAffinityServerListFilter,springCloud默認使用的過濾器
對原server進行包裝,將eureka實例元數據中的zone設置到server中

@Override
	public List<Server> getFilteredListOfServers(List<Server> servers) {
        //先使用父類ZoneAffinityServerListFilter進行過濾
		List<Server> output = super.getFilteredListOfServers(servers);
        //將結果再按照zone進行過濾,只保留zone相同的實例
		if (this.zone != null && output.size() == servers.size()) {
			List<Server> local = new ArrayList<>();
			for (Server server : output) {
				if (this.zone.equalsIgnoreCase(server.getZone())) {
					local.add(server);
				}
			}
			if (!local.isEmpty()) {
				return local;
			}
		}
		return output;
	}
    
    //擴展netflix原有的server
    class DomainExtractingServer extends DiscoveryEnabledServer {
        public DomainExtractingServer(DiscoveryEnabledServer server, boolean useSecurePort,
			boolean useIpAddr, boolean approximateZoneFromHostname) {
		// host and port are set in super()
		super(server.getInstanceInfo(), useSecurePort, useIpAddr);
		//從eureka服務實例的元對象中獲取zone值放入server中
        if (server.getInstanceInfo().getMetadata().containsKey("zone")) {
			setZone(server.getInstanceInfo().getMetadata().get("zone"));
		}
		else if (approximateZoneFromHostname) {
			setZone(ZoneUtils.extractApproximateZone(server.getHost()));
		}
		else {
			setZone(server.getZone());
		}
		setId(extractId(server));
		setAlive(server.isAlive());
		setReadyToServe(server.isReadyToServe());
    }

配置寫法

##設定自己是北京的服務器
eureka.instance.metadata-map.zone=beijing
ZoneAwareLoadBalancer

ZoneAwareLoadBalancer繼承於DynamicServerListLoadBalancer
BaseLoadBalancer中的chooseServer使用的規則是RoundRobinRule輪詢,這樣可能會產生週期性的跨區域訪問,ZoneAwareLoadBalancer就是解決這個問題,加入了區域感知的規則

@Override
    //DynamicServerListLoadBalancer.java
    //更新serverList後將所有的server按照key=zone value=List<Server>組裝成map
    protected void setServerListForZones(
            Map<String, List<Server>> zoneServersMap) {
        LOGGER.debug("Setting server list for zones: {}", zoneServersMap);
        //將這個組裝好的map跟新到LoadBalancerStats中
        getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
    }


        private ConcurrentHashMap<String, BaseLoadBalancer> balancers = new ConcurrentHashMap<String, BaseLoadBalancer>();


    //ZoneAwareLoadBalancer.java
    //重寫了這個方法,在調用父類方法之後自己組裝了一個balancers,每個zone單獨創建一個BaseLoadBalancer,並且將該zone的serverList集合放入到相對應的BaseLoadBalancer中
    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());
        }
        
        //這裏做一個校驗,去除那些失效的zone
        for (Map.Entry<String, BaseLoadBalancer> existingLBEntry: balancers.entrySet()) {
            if (!zoneServersMap.keySet().contains(existingLBEntry.getKey())) {
                existingLBEntry.getValue().setServersList(Collections.emptyList());
            }
        }
    }
    
    //這裏重寫了 BaseLoadBalancer的chooseServer方法
    @Override
    public Server chooseServer(Object key) {
        //如果可用的zone不大於1,則直接調用默認的chooseServer
        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 {
            //這一段的邏輯是通過ZoneAvoidanceRule來進行zone可用性的判斷
            //判斷的依據是根據負載的闕值默認爲小於0.2、斷路器開關次數/實例總數小於0.99999
            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);
            //成功篩選出一個zone,通過balancers獲取相應的BaseLoadBalancer進行chooseServer
            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 (Exception e) {
            logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            //否則調用父類默認的chooseServer
            return super.chooseServer(key);
        }
    }
    
IRule

在這裏插入圖片描述
AbstractLoadBalancerRule
抽象類,定義了獲取ILoadBalancer

RandomRule
隨機獲取一個服務實例

RoundRobinRule
輪詢

RetryRule
本質上還是RandomRule,內部維護了一個RoundRobinRule,默認在500M內進行循環嘗試,最終獲得Server或者返回null

WeightedResponseTimeRule
根據服務實例的響應時間計算權重區間,區間越大選取的概率就越大

區間寬度 = 所有實例的總響應時間 - 該實例的平均響應時間

ClientConfigEnabledRoundRobinRule
一個基類,內部定義了RoundRobinRule,方便子類在備選方案上可以調用

BestAvailableRule
繼承ClientConfigEnabledRoundRobinRule,注入LoadBalancerStats
通過LoadBalancerStats過濾故障的實例,然後選擇最小併發量的實例

PredicateBasedRule
抽象類,基於google的Predicate類實現了一個過濾器,方便子類制定過濾規則,過濾後的服務實例再通過輪詢獲取

AvailabilityFilteringRule
繼承PredicateBasedRule,實現了一個過濾規則
過濾斷路器已生效的實例,過濾併發請求數大於闕值的實例 默認是Integer.MAX

ZoneAvoidanceRule
繼承PredicateBasedRule 先進行可用zone的過濾再執行availabilityPredicate的過濾

ribbon配置項
<clientName>.ribbon.NFLoadBalancerClassName: Should implement ILoadBalancer
<clientName>.ribbon.NFLoadBalancerRuleClassName: Should implement IRule
<clientName>.ribbon.NFLoadBalancerPingClassName: Should implement IPing
<clientName>.ribbon.NIWSServerListClassName: Should implement ServerList
<clientName>.ribbon.NIWSServerListFilterClassName: Should implement ServerListFilter

實例

users:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule

在使用了eureka的前提下 springCloud 已經幫我們默認配置好了合適的接口實現類

@Configuration
public class EurekaRibbonClientConfiguration {
    
    //com.netflix.niws.loadbalancer.NIWSDiscoveryPing
    @Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) {
		if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
			return this.propertiesFactory.get(IPing.class, config, serviceId);
		}
		NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
		ping.initWithNiwsConfig(config);
		return ping;
	}
    
    //org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList
    @Bean
	@ConditionalOnMissingBean
	public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
		if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
			return this.propertiesFactory.get(ServerList.class, config, serviceId);
		}
		DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
				config, eurekaClientProvider);
		DomainExtractingServerList serverList = new DomainExtractingServerList(
				discoveryServerList, config, this.approximateZoneFromHostname);
		return serverList;
	}
}

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {

    //com.netflix.loadbalancer.ZoneAvoidanceRule
    @Bean
	@ConditionalOnMissingBean
	public IRule ribbonRule(IClientConfig config) {
		if (this.propertiesFactory.isSet(IRule.class, name)) {
			return this.propertiesFactory.get(IRule.class, config, name);
		}
		ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
		rule.initWithNiwsConfig(config);
		return rule;
	}
    
    //com.netflix.loadbalancer.ZoneAwareLoadBalancer
    @Bean
	@ConditionalOnMissingBean
	public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
			ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
			IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}

    //org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter
	@Bean
	@ConditionalOnMissingBean
	@SuppressWarnings("unchecked")
	public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
		if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
			return this.propertiesFactory.get(ServerListFilter.class, config, name);
		}
		ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
		filter.initWithNiwsConfig(config);
		return filter;
	}
}
  

如果需要替換這些默認的實現類 只要在application.properties定義即可 或者直接定義自己的bean

springCloud 重試機制配置

ribbon 所有的配置在 CommonClientConfigKey類

##開啓重試機制,默認是開啓的
spring.cloud.loadbalancer.retry.enabled=true

# 對同樣的server最多進行一次重試,除掉最開始的訪問
sample-client.ribbon.MaxAutoRetries=1

# 最多切換一次服務重試
sample-client.ribbon.MaxAutoRetriesNextServer=1

# 默認情況只對get資源進行重試,如果配置了該項,則對post等請求也會重試
sample-client.ribbon.OkToRetryOnAllOperations=true


# 請求連接超時,默認2秒
sample-client.ribbon.ConnectTimeout=3000

# 請求處理超時,默認5秒
sample-client.ribbon.ReadTimeout=3000

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章