Ribbon负载均衡@[Mr_Wzzzz]
记录周阳老师的Spring Cloud教程,Ribbon目前已经进入维护模式,未来可以用LoadBalancer来替换
目录
Ribbon简介
Ribbon是基于Netfix Ribbon实现的一套基于客户端的负载均衡工具,是Netfix 发布的开源项目,主要功能是为客户端提供软件负载均衡算法和服务调用,它提供了一系列完善的配置项如超时连接、重试等等。就是在配置文件中列出LoadBalance后面所有的机器,Ribbon会帮你基于某种规则(轮询,随机)去连接这些机器。
LB负载均衡(Load Balance)是什么
负载均衡就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。常用的负载均衡软件有Nginx,LVS,硬件F5等。
Ribbon本地负载均衡客户端和Nginx服务端负载均衡的区别
Nginx是服务端负载均衡,客户端的所有请求都会交给Nginx,然后由Nginx实现转发请求。即负载均衡是由服务端完成的(集中式LB)
Ribbon本地负载均衡,在调用微服务的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地从而在本地实现RPC远程服务调用技术(进程内LB)
Ribbon自带的负载均衡算法
RoundRobinRule(轮询算法)
遍历服务列表,轮询的选择Server
RandomRule(随机算法)
随机选择一个Server
RetryRule
对选定的负载均衡策略上重试机制。
WeightedResponseTimeRule
根据响应时间分配一个weight,响应时间越长,weight越小,被选中的可能性越低。
AvailabilityFilteringRule
过滤掉那些因为一直连接失败的被标记为circuit tripped的后端server,并过滤掉那些高并发的的后端server(active connections 超过配置的阈值)
BestAvailableRule
选择一个最小的并发请求的server
ZoneAvoidanceRule
复合判断server所在区域的性能和server的可用性选择server
负载均衡算法原理
负载均衡算法:rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务从重启后rest接口计数从1开始
List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”)
如:
List[0] instances = 172.0.0.1:8002
List[1] instances = 172.0.0.1:8001
8001+8002组合为集群,他们共计两台机器,集群总数为2,按照轮询算法:
请求总数为1时:1 % 2 = 1 对应下标为1,则获得服务地址:172.0.0.1.8001
请求总数为2时:2 % 2 = 0 对应下标为0,则获得服务地址:172.0.0.1.8002
请求总数为3时:3 % 2 = 1 对应下标为1,则获得服务地址:172.0.0.1.8001
请求总数为4时:4 % 2 = 1 对应下标为1,则获得服务地址:172.0.0.1.8002
以此类推…
轮询算法源码
package com.netflix.loadbalancer;
import com.netflix.client.config.IClientConfig;
………………………………
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The most well known and basic load balancing strategy, i.e. Round Robin Rule.
*/
public class RoundRobinRule extends AbstractLoadBalancerRule {
………………………………
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) {
//获得还活着的健康的服务实例(机器)即可达的,也就是Status为up的实例
List<Server> reachableServers = lb.getReachableServers();
//获取所有服务实例,无论是死是活,只要注册进服务中心即可
List<Server> allServers = lb.getAllServers();
//Status为up的服务实例数量
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;
}
//调用服务器位置下标 = incrementAndGetModulo(服务器集群总数)
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;
}
/**
* Inspired by the implementation of {@link AtomicInteger#incrementAndGet()}.
*
* @param modulo The modulo to bound the value of the counter.
* @return The next value.
*/
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next))
return next;
}
}
}