Spring Cloud Ribbon负载均衡策略学习

简介

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可用性过滤

过滤规则

  1. 连续失败导致服务器断闸
  2. 活跃连接数小于最小要求阀值

详细参考: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为服务名称

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值