图解六种常见负载均衡算法,一看就懂!

文章正文
发布时间:2024-12-14 00:34

服务器 服务器产品

轮询算法是最简单和最常见的负载均衡算法之一,其实现思路也非常直接:按预定顺序将请求依次转发到后端服务器。通常要求服务实例是无状态的。

负载均衡是指将来自客户端的请求分配到多个服务器上进行处理,从而有效地提高系统性能、可用性和可扩展性。

常见的负载均衡算法包括轮询、加权轮询、随机、加权随机、源IP哈希和最少连接等。下面将逐一介绍它们。

轮询算法(Round Robin)

轮询算法是最简单和最常见的负载均衡算法之一,其实现思路也非常直接:按预定顺序将请求依次转发到后端服务器。通常要求服务实例是无状态的。

如下图所示:

图片

图片

第一个请求首先发送到第一个服务器A;

第二个请求发送到下一个服务器,即第二个服务器B;

第三个请求发送到再下一个服务器,即第三个服务器C;对于第四个请求,由于三个服务器都已经依次发送过一次请求,所以需要从头开始,先发送到第一个服务器A;

依此类推……

该算法的优点是实现简单且可靠性高。然而,它没有考虑服务器的实际负载情况,可能导致一些服务器承担过重的负载,而其他服务器则处于空闲状态。

下面是轮询算法的一个简单实现代码,让你对其有个大致了解:

public class RoundRobinDemo { // 定义一个全局计数器,每次调用时递增 private static AtomicInteger index = new AtomicInteger(-1); // 定义一个服务器列表 private static List<String> serverList = new ArrayList<>(); public static String roundRobin() { // 获取服务器数量 int serverCount = serverList.size(); // 确定当前请求应转发到哪个服务器 int currentServerIndex = index.incrementAndGet() % serverCount; // 返回相应的服务器地址 return serverList.get(currentServerIndex); } public static void main(String[] args) { serverList.add("Server A"); serverList.add("Server B"); serverList.add("Server C"); System.out.println(roundRobin()); System.out.println(roundRobin()); System.out.println(roundRobin()); System.out.println(roundRobin()); } }

输出结果:

Server A Server B Server C Server A

加权轮询算法(Weighted Round Robin)

加权轮询算法是在轮询算法的基础上进行改进。其思路是在服务器选择过程中,根据服务器的处理能力或负载情况为服务器分配不同的权重,以便处理能力更强或负载较轻的服务器能够接收更多请求。

如下图所示:

图片

图片

服务器A、B和C的权重分别为4、3和1。那么服务器A将接收并处理更多请求。可以看到,前三个请求被路由到服务器A,而第四个请求被路由到服务器B。

该算法的优点是可以根据服务器的实际负载情况分配请求。然而,仍然存在服务器负载不均衡的问题,因为它仅根据权重值进行分配,而没有考虑服务器的实际负载情况。

以下是加权轮询算法的示例:

public class WeightRoundRobinDemo { // 定义一个全局计数器,每次调用时递增 private static AtomicInteger atomicInteger = new AtomicInteger(0); // 定义一个服务器及其对应权重值的映射 private static Map<String, Integer> serverMap = new TreeMap<>(); // 记录所有服务器的总权重 private static int totalWeight = 0; public static void main(String[] args) { serverMap.put("Server A", 4); serverMap.put("Server B", 3); serverMap.put("Server C", 1); // 计算所有服务器的总权重 for (Map.Entry<String, Integer> entry : serverMap.entrySet()) { totalWeight += entry.getValue(); } // 模拟四个请求 System.out.println(weightRoundRobin()); System.out.println(weightRoundRobin()); System.out.println(weightRoundRobin()); System.out.println(weightRoundRobin()); } public static String weightRoundRobin() { // 获取服务器数量 int serverCount = serverMap.size(); // 如果没有可用服务器,返回null if (serverCount == 0) { return null; } // 为避免多线程环境中并发操作导致的错误,在方法内部执行锁操作 synchronized (serverMap) { // 确定当前请求应转发到哪个服务器 int currentServerIndex = atomicInteger.incrementAndGet() % totalWeight; // 遍历服务器列表并根据服务器权重值选择相应地址 for (Map.Entry<String, Integer> entry : serverMap.entrySet()) { String serverAddress = entry.getKey(); Integer weight = entry.getValue(); currentServerIndex -= weight; if (currentServerIndex < 0) { return serverAddress; } } } return null; } }

输出结果:

Server A Server A Server A Server B

随机算法(Random)

随机算法是一种将请求随机分配到后端服务器的负载均衡算法。该算法实现简单,但分配效果不可控,难以确保后端服务器的负载均衡。因此,随机算法通常在测试或压力测试等临时场景中用作负载均衡算法。

如下图所示:

图片

图片

第一个请求随机分配到服务器A;

第二个和第四个请求随机分配到服务器C;

第三个请求随机分配到服务器B。

实现随机化方法的代码可以如下:

// 定义一个服务器列表 private static List<String> serverList = new ArrayList<>(); public static String random() { // 获取服务器数量 int serverCount = serverList.size(); // 如果没有可用服务器,返回null if (serverCount == 0) { return null; } // 生成一个随机数 int randomIndex = new Random().nextInt(serverCount); // 返回相应的服务器地址 return serverList.get(randomIndex); }

加权随机算法(Weighted Random)

加权随机算法是在随机算法的基础上进行改进。其思路是在服务器选择过程中,根据服务器的处理能力或负载情况为服务器分配不同的权重,以便处理能力更强或负载较轻的服务器能够获得更多请求。

图片

图片

加权随机算法的实现代码如下:

// 定义一个服务器及其对应权重值的映射 private static Map<String, Integer> serverMap = new ConcurrentHashMap<>(); public static String weightRandom() { // 获取服务器数量 int serverCount = serverMap.size(); // 如果没有可用服务器,返回null if (serverCount == 0) { return null; } // 为避免多线程环境中并发操作导致的错误,在方法内部执行锁操作 synchronized (serverMap) { // 计算所有服务器的总权重 int totalWeight = 0; for (Map.Entry<String, Integer> entry : serverMap.entrySet()) { totalWeight += entry.getValue(); } } // 生成一个随机数 int randomWeight = new Random().nextInt(totalWeight); // 遍历服务器列表并根据服务器权重值选择相应地址 for (Map.Entry<String, Integer> entry : serverMap.entrySet()) { String serverAddress = entry.getKey(); Integer weight = entry.getValue(); randomWeight -= weight; if (randomWeight < 0) { return serverAddress; } } // 默认返回null return null; }

源IP哈希算法(Hash)

源IP哈希算法是一种基于请求源IP地址的负载均衡算法。其思路是通过哈希函数为每个请求的源IP地址计算一个值,然后根据该值与可用服务器总数取模的结果来确定请求应转发到哪个服务器。

换句话说,源IP哈希算法使用客户端IP地址作为哈希键。负载均衡器将哈希值映射到可用服务器之一,然后将请求发送到该服务器进行处理。如果客户端IP地址发生变化(例如,重启后重新分配了新的IP地址),那么它将被分配到另一个服务器。

如下图所示:

图片

图片

来自客户端A的所有请求都分配到服务器A;来自客户端B的所有请求都分配到服务器C。

该算法的优点是可以避免某些客户端被重定向到不同的服务器。来自同一IP地址的请求将始终分配到同一服务器,因此可以在一定程度上提高缓存命中率等性能指标。然而,它也有一些缺点。例如,如果来自同一IP地址的请求很多,可能会导致某个服务器负载过重。

此外,由于服务器数量的变化,哈希值映射也会改变,这可能导致缓存失效,需要重新分配所有请求。

源IP哈希算法的实现代码示例如下:

// 定义一个服务器列表 private static List<String> serverList = new ArrayList<>(); public static String hash(String clientIP) { // 获取服务器数量 int serverCount = serverList.size(); // 如果没有可用服务器,返回null if (serverCount == 0) { return null; } // 计算客户端IP地址的哈希码 int hashCode = clientIP.hashCode(); // 根据哈希码确定请求应转发到哪个服务器 int serverIndex = hashCode % serverCount; // 返回相应的服务器地址 return serverList.get(serverIndex); }

最少连接算法(Least Connections)

最少连接算法是一种动态调整的负载均衡算法。其思路是尽可能将请求分配到当前空闲连接数最少的后端服务器,以达到负载均衡的效果。在实现过程中,通常需要定期检测每个服务器的连接数并进行动态调整。

如下图所示:

图片

图片

由于服务器C当前的连接数最少,所有请求都将分配给它。

最少连接算法的实现代码示例如下:

// 定义一个服务器列表 private static List<String> serverList = new ArrayList<>(); // 记录每个服务器的连接数 private static Map<String, Integer> connectionsMap = new ConcurrentHashMap<>(); public static String leastConnections() { // 获取服务器数量 int serverCount = serverList.size(); // 如果没有可用服务器,返回null if (serverCount == 0) { return null; } // 默认选择第一个服务器 String selectedServerAddress = serverList.get(0); // 获取第一个服务器的连接数 int minConnections = connectionsMap.getOrDefault(selectedServerAddress, 0); // 遍历服务器列表以找到连接数最少的服务器 for (int i = 1; i < serverCount; i++) { String serverAddress = serverList.get(i); int connections = connectionsMap.getOrDefault(serverAddress, 0); if (connections < minConnections) { selectedServerAddress = serverAddress; minConnections = connections; } } // 返回连接数最少的服务器的地址 return selectedServerAddress; }

需要注意的是,如果服务器宕机或网络链路中断,负载均衡器将需要重新计算服务器的连接数,这将延长响应时间并影响性能。

本文转载自微信公众号「程序猿技术充电站」,可以通过以下二维码关注。转载本文请联系程序猿技术充电站公众号。

责任编辑:武晓燕 来源: 程序猿技术充电站

分享到微信

微信扫码分享

分享到微博

相关推荐

六种常见负载均衡算法

今天我们来聊聊常见的负载均衡算法。负载均衡将网络流量或一组任务以某种算法合理分配给各个处理节点,使得节点得到平等的使用,并及时可靠地返回结果给用户。

2023-11-28 15:32:30

负载均衡算法

算法一看就懂之「 选择排序 」

「选择排序」虽然在实际应用中没有「插入排序」广泛,但它也是我们学习排序算法中必不可少的一种。「冒泡排序」和「插入排序」都是在两层嵌套循环中慢慢比较元素,不停的调整元素的位置。

2020-03-27 09:06:54

算法一看就懂之「 数组与链表 」

大多数人在正儿八经学习数据结构的时候估计是在大学计算机课上,而在实际项目开发中,反而感觉到用得不多。

2019-08-14 10:20:32

一看就懂的TypeScript工具类型

Partial,将T类型中的所有属性变为可选属性;Required,将T类型中的所有属性变为必选属性;Readonly,将T类型中的所有属性变为只读属性。

2023-05-12 09:08:48

开发 | Netty快速入门,一看就懂!

很早以前就写过关于Netty的使用,最近发现还有网友在看之前写的那篇Netty文章,个人感觉那时候写的很粗糙,怕影响同行的阅读质量,所以决定重新写一些关于Netty的文章,补充以前的不足。

2020-04-15 08:33:43

新手一看就懂的线程池

那相信大家也能感受到,其实用多线程是很麻烦的,包括线程的创建、销毁和调度等等,而且我们平时工作时好像也并没有这样来new一个线程,其实是因为很多框架的底层都用到了线程池。

2020-09-21 08:33:12

方法调用:一看就懂,一问就懵?

我们之前说过在类加载的解析阶段,会将一部分的符号引用转化为直接引用,该解析成立的前提是:方法在程序真正运行之前就已经有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。

2021-05-14 07:11:49

一看就懂的 Systemd 进程管理教程

systemd是目前Linux系统上主要的系统守护进程管理工具,由于init一方面对于进程的管理是串行化的,容易出现阻塞情况,所以从CentOS7开始也由systemd取代了init作为默认的系统进程管理工具。

2021-07-15 09:55:47

一看就懂的MySQL存储过程详解

我们常用的操作数据库语言SQL语句在执行的时候需要要先编译,然后执行,而存储过程(StoredProcedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。

2018-09-28 14:28:28

图解“红黑树”原理,一看就明白!

学过数据结构都知道二叉树的概念,而又有多种比较常见的二叉树类型,比如完全二叉树、满二叉树、二叉搜索树、均衡二叉树、完美二叉树等。

2019-08-22 09:22:44

图解Kafka的核心概念,一看就明白!

Kafka是主流的消息流系统,其中的概念还是比较多的,下面通过图示的方式来梳理一下Kafka的核心概念,以便在我们的头脑中有一个清晰的认识。

2021-05-13 07:30:27

一看就懂!

在单机时代,采用单块磁盘进行数据存储和读写的方式,由于寻址和读写的时间消耗,导致IO性能非常低,且存储容量还会受到限制。另外,单块磁盘极其容易出现物理故障,经常导致数据的丢失。

2019-01-15 09:55:24

一看就懂的Consul架构设计原理

如果用Eureka作为其注册中心的话,很多同学都觉得心里没底,所以现在很多公司都开始使用Consul作为其注册中心。那么这篇文章我们就来给大家说说:Consul这种服务注册中心的架构是如何设计的?

2022-08-15 19:49:57

一看就懂的适配器设计模式

一般客户端通过目标类的接口访问它所提供的服务。有时,现有类可以满足客户端类的需要,但所提供接口不一定是客户端所期望的,可能因为现有类中方法名与目标类中定义的方法名不一致。

2022-05-29 22:55:00

一看就懂 - 从零开始的游戏开发

对于开发而言,了解一下如何从零开始做游戏是一个非常有趣且有益的过程(并不)。这里我先以大家对游戏开发一无所知作为前提,以一个简单的游戏开发作为.

2021-12-30 09:10:28

每个程序员都应该知道的六种负载均衡算法

一个大型网络平台能轻松面对数百万请求而不产生崩溃,负载均衡器(LoadBalancer)是绝对的关键组件。

2023-11-02 14:21:06

一看就懂!15个交互与UI必懂的技术用语

我们经常和开发们打交道,也会经常听到一些技术上的用语(像什么SDK、RT、结构化数据等等)。当时听到这些时,你心里肯定是黑人问号脸:他到底在讲什么

2020-05-09 14:40:29

Nginx服务器的六种负载均衡策略详解

在服务器集群中,Nginx起到一个代理服务器的角色(即反向代理),为了避免单独一个服务器压力过大,将来自用户的请求转发给不同的服务器。详情请查看我的另一篇博客。

2019-09-12 09:22:58

图解Dubbo,六种扩展机制详解

今天详细的分解一下Dubbo的扩展机制,实现快速入门,丰富个人简历,提高面试level,给自己增加一点谈资,秒变面试小达人,BAT不是梦。

2023-04-12 08:00:34

Dubbo分布式服务

一看就懂的ReactJs入门教程-精华版

现在最热门的前端框架有AngularJS、React、Bootstrap等。自从接触了ReactJS,ReactJs的虚拟DOM(VirtualDOM)和组件化的开发深深的吸引了我,下面来跟我一起领略ReactJs的风采吧文章有点长,耐心读完,你会有很大收获哦

首页
评论
分享
Top