负载均衡各个算法 JAVA 诠释版

2024-02-04 09:38
文章标签 java 算法 负载 均衡 诠释

本文主要是介绍负载均衡各个算法 JAVA 诠释版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先给大家介绍下什么是负载均衡(来自百科)

负载均衡 建立在现有网络结构之上,它提供了一种廉价有效透明的方法扩展 网络设备和 服务器的带宽、增加 吞吐量、加强网络数据处理能力、提高网络的灵活性和可用性。

负载均衡,英文名称为Load Balance,其意思就是分摊到多个操作单元上进行执行,例如Web 服务器、 FTP服务器、 企业关键应用服务器和其它关键任务服务器等,从而共同完成工作任务。

多种负载均衡算法及其Java代码实现

本文讲述的是”将外部发送来的请求均匀分配到对称结构中的某一台服务器上”的各种算法,并以Java代码演示每种算法的具体实现,OK,下面进入正题,在进入正题前,先写一个类来模拟Ip列表:

import java.util.HashMap;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */public class IpMap   {// 待路由的Ip列表,Key代表Ip,Value代表该Ip的权重public static HashMap<String, Integer> serverWeightMap =new HashMap<String, Integer>();static{serverWeightMap.put("192.168.1.100", 1);serverWeightMap.put("192.168.1.101", 1);// 权重为4serverWeightMap.put("192.168.1.102", 4);serverWeightMap.put("192.168.1.103", 1);serverWeightMap.put("192.168.1.104", 1);// 权重为3serverWeightMap.put("192.168.1.105", 3);serverWeightMap.put("192.168.1.106", 1);// 权重为2serverWeightMap.put("192.168.1.107", 2);serverWeightMap.put("192.168.1.108", 1);serverWeightMap.put("192.168.1.109", 1);serverWeightMap.put("192.168.1.110", 1);}
}

 

轮询(Round Robin)法

轮询调度算法的原理是每一次把来自用户的请求轮流分配给内部中的服务器,从1开始,直到N(内部服务器个数),然后重新开始循环。算法的优点是其简洁性,它无需记录当前所有连接的状态,所以它是一种无状态调度。

其代码实现大致如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class RoundRobin   {private static Integer pos = 0;public static String getServer(){// 重建一个Map,避免服务器的上下线导致的并发问题Map<String, Integer> serverMap =new HashMap<String, Integer>();serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();ArrayList<String> keyList = new ArrayList<String>();keyList.addAll(keySet);String server = null;synchronized (pos){if (pos > keySet.size())pos = 0;server = keyList.get(pos);pos ++;}return server;}
}

由于serverWeightMap中的地址列表是动态的,随时可能有机器上线、下线或者宕机,因此为了避免可能出现的并发问题,方法内部要新建局部变量serverMap,现将serverMap中的内容复制到线程本地,以避免被多个线程修改。这样可能会引入新的问题,复制以后serverWeightMap的修改无法反映给serverMap,也就是说这一轮选择服务器的过程中,新增服务器或者下线服务器,负载均衡算法将无法获知。新增无所谓,如果有服务器下线或者宕机,那么可能会访问到不存在的地址。因此,服务调用端需要有相应的容错处理,比如重新发起一次server选择并调用。

对于当前轮询的位置变量pos,为了保证服务器选择的顺序性,需要在操作时对其加锁,使得同一时刻只能有一个线程可以修改pos的值,否则当pos变量被并发修改,则无法保证服务器选择的顺序性,甚至有可能导致keyList数组越界。

轮询法的优点在于:试图做到请求转移的绝对均衡。

轮询法的缺点在于:为了做到请求转移的绝对均衡,必须付出相当大的代价,因为为了保证pos变量修改的互斥性,需要引入重量级的悲观锁synchronized,这将会导致该段轮询代码的并发吞吐量发生明显的下降。
 

随机(Random)法

通过系统的随机算法,根据后端服务器的列表大小值来随机选取其中的一台服务器进行访问。由概率统计理论可以得知,随着客户端调用服务端的次数增多,

其实际效果越来越接近于平均分配调用量到后端的每一台服务器,也就是轮询的结果。

随机法的代码实现大致如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class Random   {public static String getServer(){// 重建一个Map,避免服务器的上下线导致的并发问题   Map<String, Integer> serverMap =new HashMap<String, Integer>();serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址List   Set<String> keySet = serverMap.keySet();ArrayList<String> keyList = new ArrayList<String>();keyList.addAll(keySet);java.util.Random random = new java.util.Random();int randomPos = random.nextInt(keyList.size());return keyList.get(randomPos);}
}

整体代码思路和轮询法一致,先重建serverMap,再获取到server列表。在选取server的时候,通过Random的nextInt方法取0~keyList.size()区间的一个随机值,从而从服务器列表中随机获取到一台服务器地址进行返回。基于概率统计的理论,吞吐量越大,随机算法的效果越接近于轮询算法的效果。
 

源地址哈希(Hash)法

源地址哈希的思想是根据获取客户端的IP地址,通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算,得到的结果便是客服端要访问服务器的序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会映射到同一台后端服务器进行访问。

源地址哈希算法的代码实现大致如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class Hash      {public static String getServer(){// 重建一个Map,避免服务器的上下线导致的并发问题Map<String, Integer> serverMap =new HashMap<String, Integer>();serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();ArrayList<String> keyList = new ArrayList<String>();keyList.addAll(keySet);// 在Web应用中可通过HttpServlet的getRemoteIp方法获取String remoteIp = "127.0.0.1";int hashCode = remoteIp.hashCode();int serverListSize = keyList.size();int serverPos = hashCode % serverListSize;return keyList.get(serverPos);}
}

前两部分和轮询法、随机法一样就不说了,差别在于路由选择部分。通过客户端的ip也就是remoteIp,取得它的Hash值,对服务器列表的大小取模,结果便是选用的服务器在服务器列表中的索引值。

源地址哈希法的优点在于:保证了相同客户端IP地址将会被哈希到同一台后端服务器,直到后端服务器列表变更。根据此特性可以在服务消费者与服务提供者之间建立有状态的session会话。

源地址哈希算法的缺点在于:除非集群中服务器的非常稳定,基本不会上下线,否则一旦有服务器上线、下线,那么通过源地址哈希算法路由到的服务器是服务器上线、下线前路由到的服务器的概率非常低,如果是session则取不到session,如果是缓存则可能引发”雪崩”。如果这么解释不适合明白,可以看我之前的一篇文章MemCache超详细解读,一致性Hash算法部分。
 

加权轮询(Weight Round Robin)法

不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。加权轮询法的代码实现大致如下:

import java.util.*;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */
class WeightRoundRobin   {private static Integer pos;public static String getServer(){// 重建一个Map,避免服务器的上下线导致的并发问题Map<String, Integer> serverMap =new HashMap<String, Integer>();serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();Iterator<String> iterator = keySet.iterator();List<String> serverList = new ArrayList<String>();while (iterator.hasNext()){String server = iterator.next();int weight = serverMap.get(server);for (int i = 0; i < weight; i++)serverList.add(server);}String server = null;synchronized (pos){if (pos > keySet.size())pos = 0;server = serverList.get(pos);pos ++;}return server;}
}

与轮询法类似,只是在获取服务器地址之前增加了一段权重计算的代码,根据权重的大小,将地址重复地增加到服务器地址列表中,权重越大,该服务器每轮所获得的请求数量越多。
 

加权随机(Weight Random)法

与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。

import java.util.*;/**  * @author ashang.peng@aliyun.com  * @date 二月 07, 2017  */class WeightRandom   {public static String getServer(){// 重建一个Map,避免服务器的上下线导致的并发问题Map<String, Integer> serverMap =new HashMap<String, Integer>();serverMap.putAll(IpMap.serverWeightMap);// 取得Ip地址ListSet<String> keySet = serverMap.keySet();Iterator<String> iterator = keySet.iterator();List<String> serverList = new ArrayList<String>();while (iterator.hasNext()){String server = iterator.next();int weight = serverMap.get(server);for (int i = 0; i < weight; i++)serverList.add(server);}java.util.Random random = new java.util.Random();int randomPos = random.nextInt(serverList.size());return serverList.get(randomPos);}
}

这段代码相当于是随机法和加权轮询法的结合,比较好理解,就不解释了。
 

最小连接数(Least Connections)法

最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前

积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。

前面几种方法费尽心思来实现服务消费者请求次数分配的均衡,当然这么做是没错的,可以为后端的多台服务器平均分配工作量,最大程度地提高服务器的利用率,但是实际情况是否真的如此?实际情况中,请求次数的均衡真的能代表负载的均衡吗?这是一个值得思考的问题。

上面的问题,再换一个角度来说就是:以后端服务器的视角来观察系统的负载,而非请求发起方来观察。最小连接数法便属于此类。

最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它正是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能地提高后端服务器的利用效率,将负载合理地分流到每一台机器。由于最小连接数设计服务器连接数的汇总和感知,设计与实现较为繁琐,此处就不说它的实现了。

这篇关于负载均衡各个算法 JAVA 诠释版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677044

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Hadoop集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x新特性) plan后面带的节点的名字必须是已经存在的,并且是需要均衡的节点。 如果节点不存在,会报如下错误: 如果节点只有一个硬盘的话,不会创建均衡计划: (1)生成均衡计划 hdfs diskbalancer -plan hadoop102 (2)执行均衡计划 hd

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory