Tout d'abord, permettez-moi de vous présenter ce qu'est l'équilibrage de charge (extrait de l'encyclopédie)
L'équilibrage de charge est construit sur structure de réseau existante, il fournit une méthode peu coûteuse, efficace et transparente pour étendre la bande passante des périphériques et des serveurs réseau, augmenter le débit, améliorer les capacités de traitement des données du réseau et améliorer la flexibilité et la disponibilité du réseau.
Équilibrage de charge, le nom anglais est Load Balance, ce qui signifie allouer l'exécution à plusieurs unités opérationnelles, telles que les serveurs Web, les serveurs FTP, les serveurs d'applications de clé d'entreprise et autres serveurs critiques, etc., afin que pour terminer le travail ensemble Tâche.
Cet article parle de divers algorithmes pour "répartir uniformément les requêtes envoyées de l'extérieur à un certain serveur dans une structure symétrique", et démontre l'implémentation spécifique de chaque algorithme dans le code Java. OK, ci-dessous Passons au. Avant d'entrer dans le sujet, écrivez d'abord une classe pour simuler la liste IP :
import java.util.HashMap; /** * @author [email protected] * @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); // 权重为4 serverWeightMap.put("192.168.1.102", 4); serverWeightMap.put("192.168.1.103", 1); serverWeightMap.put("192.168.1.104", 1); // 权重为3 serverWeightMap.put("192.168.1.105", 3); serverWeightMap.put("192.168.1.106", 1); // 权重为2 serverWeightMap.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); } }
Le principe de l'algorithme de planification round robin est d'envoyer des données depuis l'utilisateur. les requêtes sont attribuées tour à tour aux serveurs internes, en commençant de 1 jusqu'à N (le nombre de serveurs internes), puis le cycle recommence. L'avantage de l'algorithme est sa simplicité. Il n'est pas nécessaire d'enregistrer l'état de toutes les connexions en cours, il s'agit donc d'une planification sans état.
L'implémentation du code est à peu près la suivante :
import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author [email protected] * @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地址List Set keySet = serverMap.keySet(); ArrayList keyList = new ArrayList(); keyList.addAll(keySet); String server = null; synchronized (pos) { if (pos > keySet.size()) pos = 0; server = keyList.get(pos); pos ++; } return server; } }
Étant donné que la liste d'adresses dans serverWeightMap est dynamique, les machines peuvent se mettre en ligne, se déconnecter ou être en panne à tout moment, donc dans l'ordre pour éviter une éventuelle concurrence Le problème est qu'une nouvelle variable serverMap doit être créée à l'intérieur de la méthode. Copiez maintenant le contenu de serverMap dans le thread local pour éviter d'être modifié par plusieurs threads. Cela peut introduire de nouveaux problèmes. Les modifications apportées à serverWeightMap après la réplication ne peuvent pas être répercutées sur serverMap. Autrement dit, lors de ce cycle de sélection de serveur, l'algorithme d'équilibrage de charge ne sera pas en mesure de savoir si de nouveaux serveurs ou des serveurs hors ligne sont ajoutés. Peu importe si vous en ajoutez un nouveau. Si un serveur se déconnecte ou tombe en panne, vous pouvez accéder à une adresse inexistante. Par conséquent, l'appelant du service doit disposer d'un traitement de tolérance aux pannes correspondant, tel que la réinitialisation de la sélection et de l'appel du serveur.
Pour la variable de position pos actuellement interrogée, afin de garantir l'ordre de sélection du serveur, elle doit être verrouillée pendant le fonctionnement, afin qu'un seul thread puisse modifier la valeur de pos en même temps, sinon lorsque la variable pos Si elle est modifiée simultanément, l'ordre de sélection du serveur ne peut pas être garanti, et cela peut même faire sortir le tableau keyList des limites.
L'avantage de la méthode d'interrogation est qu'elle tente d'atteindre un équilibre absolu dans le transfert des demandes.
L'inconvénient de la méthode d'interrogation est que pour atteindre l'équilibre absolu du transfert des demandes, un prix considérable doit être payé, car afin d'assurer l'exclusion mutuelle des modifications de variables pos, un verrou pessimiste lourd synchronisé doit être introduit. Cela entraînera une baisse significative du débit simultané de ce code d'interrogation.
Grâce à l'algorithme aléatoire du système, l'un des serveurs est sélectionné au hasard pour l'accès en fonction de la valeur de taille de liste du serveur principal. On peut savoir d'après la théorie des probabilités et des statistiques qu'à mesure que le nombre de fois que le client appelle le serveur augmente, l'effet réel se rapproche de plus en plus d'une répartition uniforme des appels vers chaque serveur dans le backend, ce qui est le résultat d'un sondage. .
L'implémentation du code de la méthode aléatoire est à peu près la suivante :
L'idée globale du code est cohérente avec la méthode d'interrogation. Le serverMap est d'abord reconstruit, puis la liste des serveurs est reconstruite. obtenu. Lors de la sélection d'un serveur, utilisez la méthode nextInt de Random pour prendre une valeur aléatoire comprise entre 0 et keyList.size(), obtenant ainsi aléatoirement une adresse de serveur à partir de la liste de serveurs et la renvoyant. Selon la théorie des statistiques probabilistes, plus le débit est élevé, plus l'effet de l'algorithme aléatoire est proche de celui de l'algorithme d'interrogation.import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author [email protected] * @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 keySet = serverMap.keySet(); ArrayList keyList = new ArrayList(); keyList.addAll(keySet); java.util.Random random = new java.util.Random(); int randomPos = random.nextInt(keyList.size()); return keyList.get(randomPos); } }
Méthode de hachage d'adresse source (Hash)
L'implémentation du code de l'algorithme de hachage d'adresse source est à peu près la suivante :
Les deux premières parties sont les mêmes que la méthode d'interrogation et la méthode aléatoire. La différence réside dans le. partie de routage. Obtenez sa valeur de hachage via l'IP du client, c'est-à-dire remoteIp, et modulo la taille de la liste des serveurs. Le résultat est la valeurimport java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Set; /** * @author [email protected] * @date 二月 07, 2017 */ class Hash { public static String getServer() { // 重建一个Map,避免服务器的上下线导致的并发问题 Map<String, Integer> serverMap = new HashMap<String, Integer>(); serverMap.putAll(IpMap.serverWeightMap); // 取得Ip地址List Set keySet = serverMap.keySet(); ArrayList keyList = new ArrayList(); 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); } }
du serveur sélectionné dans la liste des serveurs. L'avantage du hachage d'adresse source est qu'il garantit que la même adresse IP client sera hachée sur le même serveur backend jusqu'à ce que la liste des serveurs backend change. Selon cette fonctionnalité, une session avec état peut être établie entre les consommateurs de services et les fournisseurs de services.
源地址哈希算法的缺点在于:除非集群中服务器的非常稳定,基本不会上下线,否则一旦有服务器上线、下线,那么通过源地址哈希算法路由到的服务器是服务器上线、下线前路由到的服务器的概率非常低,如果是session则取不到session,如果是缓存则可能引发"雪崩"。如果这么解释不适合明白,可以看我之前的一篇文章MemCache超详细解读,一致性Hash算法部分。
不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。加权轮询法的代码实现大致如下:
import java.util.*; /** * @author [email protected] * @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地址List Set keySet = serverMap.keySet(); Iterator iterator = keySet.iterator(); List serverList = new ArrayList(); 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; } }
与轮询法类似,只是在获取服务器地址之前增加了一段权重计算的代码,根据权重的大小,将地址重复地增加到服务器地址列表中,权重越大,该服务器每轮所获得的请求数量越多。
与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
import java.util.*; /** * @author [email protected] * @date 二月 07, 2017 */ class WeightRandom { public static String getServer() { // 重建一个Map,避免服务器的上下线导致的并发问题 Map<String, Integer> serverMap = new HashMap<String, Integer>(); serverMap.putAll(IpMap.serverWeightMap); // 取得Ip地址List Set keySet = serverMap.keySet(); Iterator iterator = keySet.iterator(); List serverList = new ArrayList(); 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); } }
这段代码相当于是随机法和加权轮询法的结合,比较好理解,就不解释了。
最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它是根据后端服务器当前的连接情况,动态地选取其中当前
积压连接数最少的一台服务器来处理当前的请求,尽可能地提高后端服务的利用效率,将负责合理地分流到每一台服务器。
前面几种方法费尽心思来实现服务消费者请求次数分配的均衡,当然这么做是没错的,可以为后端的多台服务器平均分配工作量,最大程度地提高服务器的利用率,但是实际情况是否真的如此?实际情况中,请求次数的均衡真的能代表负载的均衡吗?这是一个值得思考的问题。
上面的问题,再换一个角度来说就是:以后端服务器的视角来观察系统的负载,而非请求发起方来观察。最小连接数法便属于此类。
最小连接数算法比较灵活和智能,由于后端服务器的配置不尽相同,对于请求的处理有快有慢,它正是根据后端服务器当前的连接情况,动态地选取其中当前积压连接数最少的一台服务器来处理当前请求,尽可能地提高后端服务器的利用效率,将负载合理地分流到每一台机器。由于最小连接数设计服务器连接数的汇总和感知,设计与实现较为繁琐,此处就不说它的实现了。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!