轮询算法
轮询算法按顺序把每个新的连接请求分配给下一个服务器,最终把所有请求平分给所有的服务器。
优点
绝对公平
缺点
无法根据服务器性能去分配,无法合理利用服务器资源。
import com.google.common.collect.Lists;
import java.util.List;
public static void main(String[] args) {
List<String> ips = Lists.newArrayList("192.168.1.1", "192.168.1.2", "192.168.1.3");
Round<String> testRoundRobin = new Round<String>();
for (int i = 0; i < 10; i++) {
String s = testRoundRobin.roundRobin(ips);
System.out.println(s);
}
}
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class Round<T> {
private AtomicInteger index = new AtomicInteger(0);
public <T> T roundRobin(List<T> data) {
T t;
if (index.get() >= data.size()) {
index = new AtomicInteger(0);
}
t = data.get(index.get());
//轮询+1
index.getAndIncrement();
return t;
}
}
加权轮询法
该算法中,每个机器接受的连接数量是按权重比例分配的。这是对普通轮询算法的改进,
比如可以设定:第三台机器的处理能力是第一台机器的两倍,那么负载均衡器会把两倍的连接数量分配给第三台机器,轮询可以将请求顺序按照权重分配到后端。
优点
相较于轮询算法灵活度更高,并且相较于复杂的动态负载均衡算法,加权轮询实现起来相对简单,且具有很好的可预测性和稳定性。
缺点
相比于简单的轮询算法,加权轮询需要额外维护和计算节点权重,对系统开销有一定增加,尤其是在节点数量较多或权重频繁调整的情况下。
public void weightedPolling() throws NoSuchFieldException, IllegalAccessException {
Weight testWeightRobin = new Weight();
List<DbConfig> dbConfigList = new ArrayList<>();
dbConfigList.add(new DbConfig("192.168.1.1", 1));
dbConfigList.add(new DbConfig("192.168.1.2", 2));
dbConfigList.add(new DbConfig("192.168.1.3", 4));
for (int i = 0; i < 10; i++) {
DbConfig server = (DbConfig) testWeightRobin.weightRobin(dbConfigList);
System.out.println(server);
}
}
import org.springframework.util.CollectionUtils;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author juiwi
*/
public class Weight<T> {
private AtomicInteger index = new AtomicInteger(0);
public List<T> getDataByWeight(List<T> data) throws NoSuchFieldException, IllegalAccessException {
List<T> ips = new CopyOnWriteArrayList<T>();
if (CollectionUtils.isEmpty(data)) {
return data;
}
for (T t : data) {
Field nameField = t.getClass().getDeclaredField("name");
// setAccessible 实体中的属性是用private定义的,需要设置setAccessible 为true,才可以访问到对象
nameField.setAccessible(true);
// 获取属性值
String name = (String) nameField.get(t);
Field weightField = t.getClass().getDeclaredField("weight");
// setAccessible 实体中的属性是用private定义的,需要设置setAccessible 为true,才可以访问到对象
weightField.setAccessible(true);
// 获取属性值
Integer weight = (Integer) weightField.get(t);
for (int ipCount = 0; ipCount < weight; ipCount++) {
ips.add(t);
}
}
return ips;
}
public T weightRobin(List<T> data) throws NoSuchFieldException, IllegalAccessException {
List<T> list = this.getDataByWeight(data);
if (index.get() >= list.size()) {
index = new AtomicInteger(0);
}
T t = list.get(index.get());
index.getAndIncrement();
return t;
}
}
import lombok.*;
/**
* @author :juiwi
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class DbConfig {
private String name;
private int weight;
}
加权随机法
加权随机法(Weighted Random Selection)是一种随机选择方法,它允许每个选项有不同的概率被选中,其中每个选项的概率与其所对应的权重成正比。这种随机选择的过程可以用于多种场合,比如在游戏中角色技能发动的概率分配、广告展示的频率分配、推荐系统的物品推荐等。
优点
- 灵活性:加权随机法可以根据实际情况灵活地调整各个选项被选中的概率,有助于实现个性化和定制化的服务。
- 公平性:在考虑个体特征或重要程度差异时,可以通过权重合理分配资源或机会,更符合实际需求。
- 高效性:在预先计算好累计权重的情况下,可以在常数时间内完成随机选择,效率较高。
缺点
- 权重设定主观性:权重的选择往往依赖于主观判断或历史数据分析,若权重设置不合理,可能导致最终结果偏离预期目标。
- 忽视动态变化:如果权重是固定的,当选项的实际重要性或可用性发生变化时,需要重新调整权重才能保证选择的有效性。
- 复杂度增加:在大规模数据集上,特别是当选项数目巨大且权重分布复杂时,维护和更新权重列表会增加一定的系统复杂性。
public void randomWeight() throws NoSuchFieldException, IllegalAccessException {
RandomWeight<DbConfig> randomWeightRobin =new RandomWeight<DbConfig>();
List<DbConfig> dbConfigList= new ArrayList<>();
dbConfigList.add(new DbConfig("192.168.1.1", 1));
dbConfigList.add(new DbConfig("192.168.1.2", 2));
dbConfigList.add(new DbConfig("192.168.1.3", 4));
for (int i =0;i< 10 ;i++){
DbConfig server = randomWeightRobin.randomWeightRobin(dbConfigList);
System.out.println(server);
}
}
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadLocalRandom;
public class RandomWeight<T> {
public <T> List<T> getDataByWeight(List<T> data) throws NoSuchFieldException, IllegalAccessException {
List<T> ips = new CopyOnWriteArrayList<T>();
for (T t : data) {
Field nameField = t.getClass().getDeclaredField("name");
// setAccessible 实体中的属性是用private定义的,需要设置setAccessible 为true,才可以访问到对象
nameField.setAccessible(true);
// 获取属性值
String name = (String) nameField.get(t);
Field weightField = t.getClass().getDeclaredField("weight");
// setAccessible 实体中的属性是用private定义的,需要设置setAccessible 为true,才可以访问到对象
weightField.setAccessible(true);
// 获取属性值
Integer weight = (Integer) weightField.get(t);
// 根据权重不同,放入list 中的数量等同于权重,轮询出的的次数等同于权重
for (int ipCount = 0; ipCount < weight; ipCount++) {
ips.add(t);
}
}
return ips;
}
public <T> T randomWeightRobin(List<T> data) throws NoSuchFieldException, IllegalAccessException {
List<T> ips = this.getDataByWeight(data);
// //循环随机数
// Random random=new Random();
// int index =random.nextInt();
int index = ThreadLocalRandom.current().nextInt(ips.size());
T t = ips.get(index);
return t;
}
}
随机法
负载均衡中的随机法是一种简单的分配策略,它适用于将网络请求或其他负载均匀地分散到一组服务器上的场景。随机法的基本思想是,每当有新的请求到达时,负载均衡器根据一组服务器列表随机选择一个服务器来处理请求。选择哪个服务器是基于纯粹的随机概率,每个服务器被选中的概率理论上是相等的。
优点:
- 实现简单:随机算法实现起来较为直观和简洁,不需要额外的信息收集和复杂计算。
- 在长时间运行下,理论上可以达到平均意义上的负载均衡:由于随机性,随着请求次数的增加,每个服务器处理的请求数量趋于平衡。
缺点:
- 微观上的负载不均衡:在短期内,由于随机性,可能出现某些服务器连续被选中而暂时负载过高,其他服务器却闲置的情况。
- 不考虑服务器当前状态:随机法并未根据服务器的实时负载状况进行选择,因此无法有效处理服务器之间性能差异或瞬时负载不均的问题。
public void random() {
Random<DbConfig> randomdRobin = new Random<DbConfig>();
List<DbConfig> dbConfigList = new ArrayList<>();
dbConfigList.add(new DbConfig("192.168.1.1", 1));
dbConfigList.add(new DbConfig("192.168.1.2", 2));
dbConfigList.add(new DbConfig("192.168.1.3", 4));
for (int i = 0; i < 10; i++) {
DbConfig dbConfig = randomdRobin.randomRobin(dbConfigList);
System.out.println(dbConfig);
}
}
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class Random<T> {
public <T> T randomRobin(List<T> data) {
int index = ThreadLocalRandom.current().nextInt(data.size());
T t = data.get(index);
return t;
}
}
IP_Hash 法
负载均衡中的IP_Hash(也称为ip_hash)算法是一种特殊的负载均衡策略,它主要用于保持客户端与服务器之间的会话亲和性(Session Affinity),确保来自相同客户端IP地址的请求总是被转发到同一台后端服务器上处理。
工作原理: 当负载均衡器(如Nginx、HAProxy等)接收到一个客户端请求时,它首先提取客户端的IP地址,然后使用某种哈希函数对该IP地址进行计算,得到一个哈希值。接着,将这个哈希值与后端服务器池中的服务器数量进行取模运算,得出一个索引,指向后端服务器列表中的一个具体服务器。这样一来,相同IP地址的请求将始终被映射到同一台服务器上,从而维持了会话状态的一致性,这对于那些需要维持用户会话状态的应用程序非常重要,比如Web应用中需要保持用户登录状态或购物车信息的情况。
特点:
- 会话保持:ip_hash算法可以有效保持客户端会话在同一服务器上处理,避免了会话状态在多服务器间共享的问题,减少了对分布式会话存储的需求。
- 适用场景:特别适用于那些不支持分布式会话管理的应用,或者是由于性能、安全性或其他原因不适合在集群环境中共享会话的应用。
- 局限性:
- 如果某台服务器宕机,原来被映射到该服务器的客户端IP将不能再被正常处理,除非有备用服务器机制或重新计算哈希映射。
- 客户端IP地址改变(如用户切换网络环境或经过代理服务器)会导致请求被转发到不同的服务器,影响会话连续性。
- 在大型集群中,由于哈希碰撞,可能存在少量IP被映射到同一服务器的风险,造成负载不均衡。
public void ipHash() {
IpHash<DbConfig> ipHash =new IpHash<DbConfig>();
List<DbConfig> dbConfigList= new ArrayList<>();
dbConfigList.add(new DbConfig("192.168.1.1", 1));
dbConfigList.add(new DbConfig("192.168.1.2", 2));
dbConfigList.add(new DbConfig("192.168.1.3", 4));
DbConfig dbConfig = ipHash.ipHashRobin(dbConfigList, "192.168.90.2");
System.out.println(dbConfig.toString());
}
import java.util.List;
/**
* @author juiwi
*/
public class IpHash<T> {
/**
* @param data
* @param hashData
* @param <T>
* @return
*/
public <T> T ipHashRobin(List<T> data, String hashData) {
// int hashCode = hashData.hashCode();
// int listSize = data.size();
// int index = hashCode % listSize;
int index = hashData.hashCode() % data.size();
T t = data.get(index);
return t;
}
}