Ribbon核心组件IRule及负载均衡算法
Ribbon常用负载均衡算法:
IRule接口,Riboon使用该接口,根据特定算法从所有服务中,选择一个服务,
Rule接口有7个实现类,每个实现类代表一个负载均衡算法,默认使用轮询
如何替换掉轮询
我们需要新建一个规则类,然后在启动类中添加注解即可。
但是:
官方文档给出了警告:
这个自定义配置类不能放在 @CommpomentScan 所扫描的当前包下以及子包下,
(即不能放在SpringBoot启动类包下及其子包)
否则我们自定义的这个规则类会被所有的 Ribbon 客户端共享,达不到特殊定制化的目的。
下面我们来操作:
在已有的order80服务中新建一个package,(即服务提供者)
目录结构如下
package com.xn2001.myrule;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Created by 乐心湖 on 2020/5/2 22:14
*/
@Configuration
public class MyselfRule {
@Bean
public IRule myRule(){
// 定义为随机
return new RandomRule();
}
}
在主启动类添加注解
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MyselfRule.class)
注意:name填写的是application.yml中配置的spring.application.name的大写形式。
测试,访问查看调用消费者是否已经随机而不是轮询。
在之前轮询的情况下端口是8001与8002交替出现,而负载均衡规则变为随机后,端口是随机出现的
负载均衡算法
负载均衡算法:rest 接口第几次请求数 % 服务器集群总数量 = 时机调用服务器位置下标,每次服务重启后rest 接口技术求从1开始。
List<ServiceInstance> instances = discoverClient.getInstances("CLOUD-PROVIDER-SERVICE");
如: List[0] instances = 127.0.0.1:8002
List[1] instances = 127.0.0.1:8001
8001 + 8002 组合为集群,他们共计2台服务器,集群总数为2 , 按照轮询算法原理:
当请求总数为1 时:1%2 = 1, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8001 当请求总数为2 时:2%2 = 0, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8002 当请求总数为3 时:2%2 = 1, 对应下标位置为1, 则获得服务地址为 127.0.0.1:8001
例如我们现在有两台机子去负载均衡
请求次数 |
计算公式 |
取得下标 |
---|---|---|
1 |
1%2=1 |
对应127.0.0.1:8001 |
2 |
2%2=0 |
对应127.0.0.1:8002 |
3 |
3%2=1 |
对应127.0.0.1:8001 |
… |
… |
… |
手写负载均衡算法
首先你需要对CAS和自旋锁有一定的了解
下文仅仅是对过程的大致描述,不是实际的演示过程
在服务提供者中的的controller添加一个方法,这里我们直接返回是端口号serverPort
@GetMapping("/payment/lb")
public String getPaymentLb(){
return serverPort;
}
将restTemplate配置类中的@LoadBalanced
注解删除
开始撸自己的算法
先建一个lib包,这里需要放到SpringBoot启动类下可以让Spring扫描到的包下,然后写一个接口
package com.xn2001.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* Created by 乐心湖 on 2020/5/7 14:55
*/
public interface LoadBalancer {
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}
package com.xn2001.springcloud.lb;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by 乐心湖 on 2020/5/7 14:54
*/
@Component
public class MyLB implements LoadBalancer{
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));
System.out.println("第几次访问"+next);
return next;
}
@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}
current是初始值,next是访问次数。每次访问都会在自旋锁中把初始值+1,然后使用compareAndSet方法比较并交换。成功就跳出循环,失败则继续进入循环重新获取初始值+1…
这里保证了不用synchronized方法也能在高并发下实现线程安全的增加次数
instances()实现了访问次数%集群数量,使这个值永远不超过集群数量,然后得到这个值作为获取单个实例的下标,根据实例返回实例
在服务消费者中的controller层加入方法
先获取集群中的实例,然后判断是否为空,把获取到的list传给获取负载均衡算法中去,获取到实例地址(也就是分配了哪那一台服务器),restTemplate去调用服务。
@GetMapping("/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);
}
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 浅析Android Studio 3.0 升级各种坑(推荐)
- Android EasyPermissions官方库高效处理权限相关教程
- 关于Android 6.0权限的动态适配详解
- 详解android 人脸检测你一定会遇到的坑
- Android实战RecyclerView头部尾部添加方法示例
- android实现多线程断点续传功能
- Android 8.0 中如何实现视频通话的画中画模式的示例
- Android7.0开发实现Launcher3去掉应用抽屉的方法详解
- Android利用Paint自定义View实现进度条控件方法示例
- 前端科普系列(5):ESLint - 守住优雅的护城河
- 图的储存方式,链式前向星最简单实现方式 (边集数组)
- 技术前刊:PostgreSQL12 COPY和bulkloading提升
- 疯子的算法总结(八) 最短路算法+模板
- POJ - 2387 Til the Cows Come Home (最短路入门)
- POJ - 3074 Sudoku (搜索)剪枝+位运算优化