# Spring Cloud Ribbon
SpringCloudRibbon
是基于Netflix Ribbon
实现的一套客户端负载均衡的工具,主要提供客户端的软件负载均衡算法和服务调用。Ribbon
客户端提供一系列完善的配置项如连接超时,重试等。
As simple
,Ribbon
就是再配置文件中列出Load Balancer
后面所有的机器,自动帮你基于某种规则(如简单轮询,随坤连接)去连接这些机器。
# LB负载均衡是什么
简单来说就是将用户的请求平摊到多个服务上,从而达到系统的HA
(高可用)。常用的负载均衡有软件Nginx,LVS
,硬件F5
等。
# Ribbon本地负载均衡客户端 VS Nginx服务端负载均衡的区别
Nginx
是服务器负载均衡,客户端所有请求都会交给Nginx
,然后由Nginx
实现转发请求,及负载均衡是由服务器实现的。
Ribbon
本地负载均衡,在调用服务接口时候,会在注册中心获取信息服务列表之后缓存到JVM
本地,从而在本地实现RPC
远程服务调用技术。
TIP
集中式LB
:即在服务的消费方和提供方之间使用独立的LB设施(硬件:F5
;软件:Nginx
),由该设施负责把访问请求通过某种策略转发至服务的提供方
TIP
进程内LB
:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选取一个合适的服务器。
Ribbon
就属于进程内LB
,他只是一个类库,集成与消费方进程,消费方通过它来获取到服务提供方的地址。
# Ribbon架构
![](/assets/img/Ribbon.f66e718b.png)
Ribbon
在工作时分成两步
第一步选择EurekaServer
,它优先选择在同一个区域内负载较少的~server~,
第二步再根据用户指定的策略,在从server
取到的服务注册列表中选择一个地址,其中Ribbon
提供了许多种策略,如轮询,随机和根据响应时间加权
# RestTemplate
![](/assets/img/Ribbon (2).03c6645c.png)
![](/assets/img/Ribbon (1).ffbdc885.png)
# Ribbon接口
![](/assets/img/Ribbon (4).b2bc3413.png)
IRule
:根据特定算法从服务列表中选取一个要访问的服务
# Ribbon配置
![](/assets/img/Ribbon (3).8604b68f.png)
所以在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();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
主启动类添加@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);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Ribbon负载均衡算法
com.netflix.loadbalance.RoundRobbinRule
// 轮询
com.netflix.loadbalance.RandomRule
// 随机
com.netflix.loadbalance.RetryRule
// 先按照RoundRobbinRule的策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务
WeightedResponseTimeRule
// 对RoundRobinRule的扩展,响应速度越快的实例选择权重越多大,越容易被选择
BestAvailableRule
// 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
AvailabilityFilteringRule
// 先过滤掉故障实例,再选择并发较小的实例
ZoneAvoidanceRule
// 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
2
3
4
5
6
7
8
9
10
11
12
13
14
负载均衡算法: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
。。。。。。
2
3
4
5
6
7
源码:
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;
}
//自旋锁
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
简单自定义实现:
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());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17