Spring Cloud Ribbon
SpringCloudRibbon
是基于Netflix Ribbon
实现的一套客户端负载均衡的工具,主要提供客户端的软件负载均衡算法和服务调用。Ribbon
客户端提供一系列完善的配置项如连接超时,重试等。
In simple terms
,Ribbon
就是再配置文件中列出Load Balancer
后面所有的机器,自动帮你基于某种规则(如简单轮询,随坤连接)去连接这些机器。
LB负载均衡是什么
简单来说就是将用户的请求平摊到多个服务上,从而达到系统的HA
(高可用)。常用的负载均衡有软件Nginx,LVS
,硬件F5
等。
Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡的区别
Nginx
是服务器负载均衡,客户端所有请求都会交给Nginx
,然后由Nginx
实现转发请求,及负载均衡是由服务器实现的。
Ribbon
本地负载均衡,在调用服务接口时候,会在注册中心获取信息服务列表之后缓存到JVM
本地,从而在本地实现RPC
远程服务调用技术。
集中式LB
:即在服务的消费方和提供方之间使用独立的LB设施(硬件:F5
;软件:Nginx
),由该设施负责把访问请求通过某种策略转发至服务的提供方
进程内LB
:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选取一个合适的服务器。
Ribbon
就属于进程内LB
,他只是一个类库,集成与消费方进程,消费方通过它来获取到服务提供方的地址。
Ribbon架构
Ribbon
在工作时分成两步
第一步选择EurekaServer
,它优先选择在同一个区域内负载较少的server,
第二步再根据用户指定的策略,在从server
取到的服务注册列表中选择一个地址,其中Ribbon
提供了许多种策略,如轮询,随机和根据响应时间加权
RestTemplate
Ribbon接口
IRule
:根据特定算法从服务列表中选取一个要访问的服务
Ribbon配置
所以在main/java
目录下新建一个package myrule
package com.swh.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MySelfRule {
@Bean
public IRule myRule() {
// 定义为随机
return new RoundRobinRule();
}
}
主启动类添加@RibbonClient
package com.swh.springcloud;swh
import com.atguigu.myrule.MySelfRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE", configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class, args);
}
}
Ribbon负载均衡算法
com.netflix.loadbalance.RoundRobbinRule
// 轮询
com.netflix.loadbalance.RandomRule
// 随机
com.netflix.loadbalance.RetryRule
// 先按照RoundRobbinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
WeightedResponseTimeRule
// 对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
BestAvailableRule
// 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule
// 先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule
// 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
负载均衡算法:rest
接口第几次请求%服务器集群总数量=实际调用服务器位置下标,每次服务启动后rest接口计数从1开始。
List<ServiceInstance> instance = discoveryClient.getinstances("CLOUD-PAYMENT-SERVICE");
如:
List[0] instance=127.0.0.1:8001
List[1] instance=127.0.0.1:8002
当请求数为1时,1%2=1,对应下标位置为1,则获得服务地址为127.0.0.1:8001
当请求数为2时,2%2=2,对应下标位置为0,则获得服务地址为127.0.0.1:8002
当请求数为3时,3%2=1,对应下标位置为1,则获得服务地址为127.0.0.1:8001
当请求数为4时,4%2=1,对应下标位置为0,则获得服务地址为127.0.0.1:8002
。。。。。。
源码:
private AtomicInteger nextServerCyclicCounter;
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
} else {
Server server = null;
int count = 0;
while(true) {
if (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) {
int nextServerIndex = this.incrementAndGetModulo(serverCount);
server = (Server)allServers.get(nextServerIndex);
if (server == null) {
Thread.yield();
} else {
if (server.isAlive() && server.isReadyToServe()) {
return server;
}
server = null;
}
continue;
}
log.warn("No up servers available from load balancer: " + lb);
return 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;
}
//自旋锁
}
简单自定义实现:
private AtomicInteger atomicInteger=new AtomicInteger(0);
public final int getAndIncrement(){
int current;
int next;
do{
current=this.atomicInteger.get();
next=current>=2147483647?0:current+1;
}while (!this.atomicInteger.compareAndSet(current,next));
//自旋锁,防止高并发场景下这个数字已经被其他进程占用过。
// 2147483647Integer最大值Max_Value
System.out.println(next);
return next;
}
@Override
public ServiceInstance instance(List<ServiceInstance> serviceInstances){
return serviceInstances.get(getAndIncrement()%serviceInstances.size());
}