04.SpringCloud Ribbon (负载均衡服务调用)
1.Ribbon的概述
是什么


未来替换方案 LoadBalancer 现在还是ribbon用的多

能干嘛
LB(负载均衡)

集中式LB

进程内LB

前面我们讲解过了80通过轮询负载访问8001/8002
一句话
ribbon = 负载均衡+RestTemplate调用
2.Ribbon负载均衡演示
架构说明

总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。
eureka的客户端集成了ribbon
pom说明


RestTemplate的使用
官网
- 返回的object
- 返回的entity
getForObject和getForEntity

postForObject和postForEntity

3.Ribbon核心组件IRule
IRule基本说明
IRule:根据特定算法从服务列表中选取一个要访问的服务

com.netflix.loadbalancer包下
- RoundRobinRule 轮询 默认轮询
- RandomRule 随机
- RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试
- WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
- BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
- AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例
- ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器
替换负载均衡组件步骤
修改cloud-consumer-order80
注意配置细节

新建package com.chl.myrule

上面包下新建MySelfRule规则类
/**
* 切换负载均衡的算法
*/
@Configuration
public class MyselfRule {
//切换为随机
@Bean
public IRule myRule(){
return new RandomRule();
}
}主启动类添加@RibbonClient
@EnableEurekaClient
@SpringBootApplication
//自定义CLOUD-PAYMENT-SERVICE 的ribbon负载均衡算法
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyselfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}测试
4.Ribbon负载均衡算法(轮询说明)
原理

源码
public class RoundRobinRule extends AbstractLoadBalancerRule {
private AtomicInteger nextServerCyclicCounter; //原子类
private static final boolean AVAILABLE_ONLY_SERVERS = true;
private static final boolean ALL_SERVERS = false;
private static Logger log = LoggerFactory.getLogger(RoundRobinRule.class);
public RoundRobinRule() {
this.nextServerCyclicCounter = new AtomicInteger(0);
}
public RoundRobinRule(ILoadBalancer lb) {
this();
this.setLoadBalancer(lb);
}
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) {
int current;
int next;
do {
current = this.nextServerCyclicCounter.get();
next = (current + 1) % modulo;
} while(!this.nextServerCyclicCounter.compareAndSet(current, next));
return next;
}
//
public Server choose(Object key) {
return this.choose(this.getLoadBalancer(), key);
}
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
}
手写轮询负载均衡
7001/7002集群启动
8001/8002微服务改造 PaymentController 添加测试方法
//获取端口 用于测试手写的ribbon轮训算法
@GetMapping(value = "/payment/lb")
public String getPaymentLB(){
return serverPort;
}
80订单微服务改造
1.ApplicationContextBean去掉@LoadBalanced (去掉ribbon负载均衡实现的注解)
2.新建立LoadBalancer接口
public interface LoadBalancer {
//收集服务器总共有多少台能够提供服务的机器,并放到list里面,实现类,实现负载均衡算法
//返回一个实例
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}3.MyLB
/**
* 手写的轮询算法
*/
@Component
public class MyLB implements LoadBalancer {
private AtomicInteger atomicInteger = new AtomicInteger(0);
//坐标 自旋锁实现
private final int getAndIncrement(){
int current;
int next;
do {
current = this.atomicInteger.get();
next = current >= 2147483647 ? 0 : current + 1; //访问一次,加1
}while (!this.atomicInteger.compareAndSet(current,next)); //第一个参数是期望值,第二个参数是修改值是
System.out.println("*******第几次访问,次数next: "+next);
return next;
}
//放全部实例列表,然后自旋锁获取下标,余数得到坐标
//根据坐标,从实例中获取实例返回去
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) { //得到机器的列表
int index = getAndIncrement() % serviceInstances.size(); //得到服务器的下标位置
return serviceInstances.get(index);
}
}
4.OrderController
/**
*
* 测试手写的轮训算法
* 就是根据服务名,获取某个服务的全部实例总数
* 传到手写的负载均衡算法里面,返回一个实例
* 获取返回实例的uri,访问服务提供方
* @return
*/
@GetMapping(value = "/consumer/payment/lb")
public String getPaymentLB(){
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
if (instances == null || instances.size() <= 0){
return null;
}
ServiceInstance serviceInstance = loadBalancer.instances(instances);
URI uri = serviceInstance.getUri();
return restTemplate.getForObject(uri+"/payment/lb",String.class);
}5.测试
http://localhost/consumer/payment/lb 8001 8002 端口交替出现

浙公网安备 33010602011771号