简介
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);
}
}
复制代码