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();
}
看一下有哪些實現類
這裏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
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