简介
Ribbon负责客户端负载均衡,为了避免单独一个服务器压力过大,将来自用户的请求转发给不同的服务器,默认的负载策略是轮询;核心算法接口为:com.netflix.loadbalancer.IRule
方法public Server choose(Object key)
最少连接方式
找出最小的并发请求的 Server,并剔除有异常状态的Server,详细参考:com.netflix.loadbalancer.BestAvailableRule
public class BestAvailableRule extends ClientConfigEnabledRoundRobinRule {
@Override
public Server choose(Object key) {
if (loadBalancerStats == null) {
return super.choose(key);
}
// 获取所有服务器列表
List<Server> serverList = getLoadBalancer().getAllServers();
int minimalConcurrentConnections = Integer.MAX_VALUE;
long currentTime = System.currentTimeMillis();
Server chosen = null;
// 找出可用连接数最小的服务器
for (Server server: serverList) {
ServerStats serverStats = loadBalancerStats.getSingleServerStat(server);
// 连接失败超时,一定时间内(如禁止30内不能使用)不可用
if (!serverStats.isCircuitBreakerTripped(currentTime)) {
int concurrentConnections = serverStats.getActiveRequestsCount(currentTime);
if (concurrentConnections < minimalConcurrentConnections) {
minimalConcurrentConnections = concurrentConnections;
chosen = server;
}
}
}
if (chosen == null) {
return super.choose(key);
} else {
return chosen;
}
}
public boolean isCircuitBreakerTripped(long currentTime) {
long circuitBreakerTimeout = getCircuitBreakerTimeout();
if (circuitBreakerTimeout <= 0) {
return false;
}
return circuitBreakerTimeout > currentTime;
}
}
AvailabilityFilteringRule可用性过滤
过滤规则
- 连续失败导致服务器断闸
- 活跃连接数小于最小要求阀值
详细参考:com.netflix.loadbalancer.AvailabilityFilteringRule
public class AvailabilityFilteringRule extends PredicateBasedRule {
@Override
public Server choose(Object key) {
int count = 0;
Server server = roundRobinRule.choose(key);
while (count++ <= 10) {
if (predicate.apply(new PredicateKey(server))) {
return server;
}
server = roundRobinRule.choose(key);
}
return super.choose(key);
}
}
判断方法为predicate.apply(new PredicateKey(server))
@Override
public boolean apply(@Nullable PredicateKey input) {
LoadBalancerStats stats = getLBStats();
if (stats == null) {
return true;
}
return !shouldSkipServer(stats.getSingleServerStat(input.getServer()));
}
private boolean shouldSkipServer(ServerStats stats) {
if ((CIRCUIT_BREAKER_FILTERING.get() && stats.isCircuitBreakerTripped())
|| stats.getActiveRequestsCount() >= activeConnectionsLimit.get()) {
return true;
}
return false;
}
最终逻辑为stats.isCircuitBreakerTripped()
与stats.getActiveRequestsCount() >= activeConnectionsLimit.get()
RandomRule
随机分配,详细参考:com.netflix.loadbalancer.RandomRule
public class RandomRule extends AbstractLoadBalancerRule {
@Override
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
Server server = null;
while (server == null) {
if (Thread.interrupted()) {
return null;
}
List<Server> upList = lb.getReachableServers();
List<Server> allList = lb.getAllServers();
int serverCount = allList.size();
if (serverCount == 0) {
/*
* No servers. End regardless of pass, because subsequent passes
* only get more restrictive.
*/
return null;
}
int index = chooseRandomInt(serverCount);
server = upList.get(index);
if (server == null) {
/*
* The only time this should happen is if the server list were
* somehow trimmed. This is a transient condition. Retry after
* yielding.
*/
Thread.yield();
continue;
}
if (server.isAlive()) {
return (server);
}
// Shouldn't actually happen.. but must be transient or a bug.
server = null;
Thread.yield();
}
return server;
}
protected int chooseRandomInt(int serverCount) {
return ThreadLocalRandom.current().nextInt(serverCount);
}
}
随机一个服务器列表下标,做到随机返回一个服务器,逻辑比较简单,这里不做说明
RoundRobinRule
轮询分配,详细参考:com.netflix.loadbalancer.RoundRobinRule
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter = nextServerCyclicCounter = new AtomicInteger(0);
@Override
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
}
轮询一个服务器列表下标,定义一个全局的轮询标号,通过方法incrementAndGetModulo
获取到轮询的下标,逻辑比较简单,这里不做说明
RetryRule
重试机制,详细参考:com.netflix.loadbalancer.RetryRule
对选定的负载均衡策略机上重试机制,也就是说当选定了某个策略进行请求负载时在一个配置时间段内若选择 Server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 Server。
WeightedResponseTimeRule
详细参考:com.netflix.loadbalancer.根据响应时间分配一个 Weight(权重),响应时间越长,Weight 越小,被选中的可能性越低。
对选定的负载均衡策略机上重试机制,也就是说当选定了某个策略进行请求负载时在一个配置时间段内若选择 Server 不成功,则一直尝试使用 subRule 的方式选择一个可用的 Server。
自定义负载均衡策略
通过实现 IRule 接口可以自定义负载策略,主要的选择服务逻辑在 choose 方法中。我们这边只是演示怎么自定义负载策略,所以没写选择的逻辑,直接返回服务列表中第一个服务。具体代码如下所示。
public class MyRule implements IRule {
private ILoadBalancer lb;
@Override
public Server choose(Object key) {
List<Server> servers = lb.getAllServers();
for (Server server : servers) {
System.out.println(server.getHostPort());
}
return servers.get(0);
}
@Override
public void setLoadBalancer(ILoadBalancer lb){
this.lb = lb;
}
@Override
public ILoadBalancer getLoadBalancer(){
return lb;
}
}
添加配置规则
${服务名称}.ribbon.NFLoadBalancerRuleClassName=${实现类}
配置文件application.properties添加如下
eureka-provider-app.ribbon.NFLoadBalancerRuleClassName=com.example.ribbon.demo.rule.MyRule
编写测试
public Object getData() {
ServiceInstance instance = loadBalancer.choose("eureka-provider-app");
return instance;
}
测试
常用配置
本地配置ip端口
ribbon.eureka.enabled=false
app.ribbon.listOfServers=192.168.1.55:8081,192.168.1.189:8082
其中app为服务名称