Ribbon负载均衡算法
负载均衡算法原理
-
算法原理为:通过rest接口第几次请求数 % 服务器集群总数量 获取实际调用服务器位置的下标,每次服务重启后,rest接口第几次请求计数从1开始。
-
可以想象一下,你要对一个服务器进行请求,然后负载均衡的默认算法是轮询算法,即你要保证你的请求对所有服务器集群是一个轮换的机制。
又因为取余数便总能够得到比集群总数量小的数,联想数组下标,我们这个取的余数即可作为数组下标,当然这边理解为服务器位置的下标也是同种性质的。
那么通过每次下标自增一,满了之后归0,即可实现轮询所有的服务器。
负载均衡算法源码解读
IRule接口:根据特定算法从服务列表中选取一个要访问的服务
IRule接口中choose方法的作用是从存活的服务中选择一个服务来使用
public interface IRule{
/*
* choose one alive server from lb.allServers or
* lb.upServers according to key
*
* @return choosen Server object. NULL is returned if none
* server is available
*/
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
RoundRobinRule源码解读
默认负载均衡算法:轮询算法
spring容器能够使用这个RoundRobinRule类,所以我们理应查看它的构造方法。构造方法中将原始整形类赋初始值为0
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
public RoundRobinRule() {
nextServerCyclicCounter = new AtomicInteger(0);
}
}
我们轮询算法的重点肯定是在于如何从存活的服务中选出一个服务,那我们RoundRobinRule重写了IRule的choose方法
@Override
public Server choose(Object key) {
return choose(getLoadBalancer(), key);
}
内部又调用choose(getLoadBalancer(), key)方法,步骤解析如下代码
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter;
public Server choose(ILoadBalancer lb, Object key) {
// 1. 如果没有负载均衡,即报错
if (lb == null) {
log.warn("no load balancer");
return null;
}
// 2. 进行服务器的选择
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
// 2.1 获取所有可达的服务器(即存活的服务器)
List<Server> reachableServers = lb.getReachableServers();
// 2.2 获取所有服务器
List<Server> allServers = lb.getAllServers();
// 2.3 获取存活服务器的总数量,即上诉中的服务器集群总数量
int upCount = reachableServers.size();
// 2.4 获取所有服务器的总数量
int serverCount = allServers.size();
// 2.5 如果不存在存活服务器或者不存在服务器则报错
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
// 3. 获取下一个指定服务器的下标,即上述:取余后获取服务器下标
// 这里的下一个,不要理解为第一次进来的下一次第二次
// 应该理解为,你第一次请求进来的时候,这些代码是在你请求进来后去推算我们的服务器的位置,那这个next对于我们来说,即是第一次请求所对应的服务器
// 所以我们这里读下标的时候是从1-2-3-...-0-1-2-3-...-0...
int nextServerIndex = incrementAndGetModulo(serverCount);
// 4. 根据下标获取对应服务器
server = allServers.get(nextServerIndex);
// 5. 判断一下所获取到的服务器是否为空,如果为空则结束本次循环继续开始下次循环
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
// 6. 判断该服务器是否存活是否准备好去做负载均衡的服务器,否则将server置null继续后续循环
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo 所有服务器的总数量
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
// .get()即返回它自身的值,该值在创建对象放入spring容器时,即上面调用的构造方法,值为0
// 即当前下标为0 ,下一个下标为 (0+1)%总数
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
// cas比较,即如果current与nextServerCyclicCounter的值相等,则将nextServerCyclicCounter的值更新为next
if (nextServerCyclicCounter.compareAndSet(current, next))
// 即这里实现了我们上述所说的取余数,那这个余数,就可以作为我们的服务器下标去获取服务器
return next;
}
}
}
负载均衡接口
public interface ILoadBalancer {
/**
* @return Only the servers that are up and reachable.
*/
public List<Server> getReachableServers();
/**
* @return All known servers, both reachable and unreachable.
*/
public List<Server> getAllServers();
}
这里指:如果expect值与this值相等,那就想this值更新为update,并返回true
public class AtomicInteger extends Number implements java.io.Serializable {
private static final long serialVersionUID = 6214790243416807050L;
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
// valueOffset理解为这个value的内存地址值
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
}