簡介
Ribbon is a Inter Process Communication (remote procedure calls) library with built in software load balancers . The primary usage model involves REST calls with various serialization scheme support.(客戶端軟負載)官方文檔 java
負載均衡
本身實現
...
private final DiscoveryClient discoveryClient;
...
List<ServiceInstance> instances = discoveryClient.getInstances("lock-center" );
String url = instances.stream()
.map(instance -> instance.getUri() + "/lock/test/{id}" )
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("沒有當前實例" ));
List<String> urls = instances.stream()
.map(instance -> instance.getUri() + "/lock/test/{id}" )
.collect(Collectors.toList());
int i = ThreadLocalRandom.current().nextInt(urls.size());
log.info("請求的URl:{}" ,urls.get(i));
TblCar tblCar = restTemplate.getForObject(urls.get(i), TblCar.class, 4000002L);
複製代碼
ribbon實現
spring-cloud-starter-alibaba-nacos-discovery帶了,不須要加
複製代碼
...
@Bean
@LoadBalanced
public RestTemplate restTemplate (){
return new RestTemplate();
}
...
複製代碼
lock-center:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
ribbon:
eager-load:
clients: '*'
enabled: true
複製代碼
TblCar tblCar = restTemplate.getForObject("http://lock-center/lock/test/{id}" , TblCar.class, 4000002L);
複製代碼
ribbon組成
接口
做用
默認值
IClientConfig
讀取配置
DefaultClientConfigImpl
IRule
負載均衡規則,選則實例
ZoneAvoidanceRule
IPing
篩選ping不通的實例
DummyPing
ServerList
交給ribbon的實例列表
ConfigurationBasedServerList(ribbon)/NacosServerList(springcloudalibaba)
ServerListFilter
過濾不合規實例
ZonePreferenceServerListFilter
ILoadBalancer
ribbon的入口
ZoneAwareLoadBalancer
ServerListUpdater
更新交給ribbon的List的策略
PollingServerListUpdater
內置負債均衡算法
名稱
特色
RoundRobinRule
輪詢
RandomRule
隨機
AvailabilityFilteringRule
會優先過濾因爲屢次訪問故障而處於斷路器跳閘狀態的服務,還有併發的鏈接數量超過閥值的服務,而後對剩餘的服務輪詢
WeigthtedResponseTimeRule
根據平均響應時間計算全部服務的權重,響應時間越快權重越大被訪問機率越高,剛開始統計信息不夠使用輪詢,以後切換
RetryRule
先按照輪詢獲取服務,若是失敗則在指定的時間內重試,獲取可用服務
BestAvailableRule
會過濾因爲屢次訪問故障而處於斷路器跳閘狀態的服務,而後選擇一個併發量最小的服務
ZoneAvoidanceRule
默認規則,複合判斷server所在區域的性能和server可用性選擇服務器,沒有Zone的環境下相似輪詢
配置負債均衡算法
package com.virgo.user.configuration;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Configuration;
/**
* @author zhaozha
* @date 2019/10/11 上午10:39
*/
@Configuration
@RibbonClient(name = "lock-center" , configuration = RibbonConfiguration.class)
public class LockCenterRibbonConfiguration {
}
package com.virgo.user.configuration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author zhaozha
* @date 2019/10/11 上午10:47
*/
@Configuration
@ExcludeComponent
public class RibbonConfiguration {
@Bean
public IRule ribbonRule () {
// 負載均衡規則改成隨機
return new NacosWeightedRule();
}
}
// @ExcludeComponent解決父子上下文衝突問題,不解決變成全局配置
// 啓動類@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
value = {ExcludeComponent.class}))
package com.virgo.user.configuration;
/**
* @author zhaozha
* @date 2019/10/11 上午10:49
*/
public @interface ExcludeComponent {
}
複製代碼
lock-center:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
複製代碼
package com.virgo.user.configuration;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.netflix.ribbon.RibbonClients;
import org.springframework.context.annotation.Configuration;
/**
* @author zhaozha
* @date 2019/10/11 上午10:39
*/
@Configuration
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class LockCenterRibbonConfiguration {
}
複製代碼
支持nacos權重
package com.virgo.user.configuration;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import java.util.List;
/**
* @author zhaozha
* @date 2019/10/11 上午11:26
*/
@Slf4j
public class NacosWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
// 讀取配置文件,並初始化NacosWeightedRule
}
@Override
public Server choose(Object o) {
try {
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 想要請求的微服務的名稱
String name = loadBalancer.getName();
// 拿到服務發現的相關API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
// nacos client自動經過基於權重的負載均衡算法,給咱們選擇一個實例。
Instance instance = namingService.selectOneHealthyInstance(name);
log.info("選擇的實例是:port = {}, instance = {}" , instance.getPort(), instance);
return new NacosServer(instance);
} catch (NacosException e) {
return null;
}
}
}
複製代碼
同一Cluster下優先調用
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
namespace: 14c78c8e-c962-4d56-b5f3-0748830adc07
cluster-name: BJ
metadata:
version: v1
複製代碼
package com.virgo.user.configuration;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.client.naming.core.Balancer;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.nacos.NacosDiscoveryProperties;
import org.springframework.cloud.alibaba.nacos.ribbon.NacosServer;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author zhaozha
* @date 2019/10/17 下午11:05
*/
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule{
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
try {
// 拿到配置文件中的集羣名稱
String clusterName = nacosDiscoveryProperties.getClusterName();
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 想要請求的微服務的名稱
String name = loadBalancer.getName();
// 拿到服務發現的相關API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
// 1. 找到指定服務的全部實例 A
List<Instance> instances = namingService.selectInstances(name, true );
// 2. 過濾出相同集羣下的全部實例 B
List<Instance> sameClusterInstances = instances.stream()
.filter(instance -> Objects.equals(instance.getClusterName(), clusterName))
.collect(Collectors.toList());
// 3. 若是B是空,就用A
List<Instance> instancesToBeChosen = new ArrayList<>();
if (CollectionUtils.isEmpty(sameClusterInstances)) {
instancesToBeChosen = instances;
log.warn("發生跨集羣的調用, name = {}, clusterName = {}, instances = {}" ,
name,
clusterName,
instances
);
} else {
instancesToBeChosen = sameClusterInstances;
}
// 4. 基於權重的負載均衡算法,返回1個實例
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChosen);
log.info("選擇的實例是 port = {}, instance = {}" , instance.getPort(), instance);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("發生異常了" , e);
return null;
}
}
}
class ExtendBalancer extends Balancer {
public static Instance getHostByRandomWeight2(List<Instance> hosts) {
return getHostByRandomWeight(hosts);
}
}
複製代碼