负载均衡分类及算法

概述

随着系统日益庞大、逻辑业务越来越复杂,系统架构由原来的单一系统到垂直系统,发展到现在的分布式系统。分布式系统中,可以做到公共业务模块的高可用,高容错性,高扩展性,然而,当系统越来越复杂时,需要考虑的东西自然也越来越多,要求也越来越高,比如服务路由、负载均衡等

目的

负载均衡的目的就是通过调度集群,达到最佳化资源使用,最大化吞吐率,最小化响应时间,避免单点过载的问题。

分类

主要分为 DNS负载均衡二、三、四、七层负载均衡

DNS负载均衡

这种是属于较早出现的技术,其利用域名解析实现负载均衡,在DNS服务器配置多个A记录,这些A记录对应的服务器构成集群互相减轻服务压力。大型网站总是部分使用DNS解析,作为第一级负载均衡

二、三、四、七层负载均衡

是以ISO模型为准,从下到上分为:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层,这里其实正好符合tcp/ip的分界。
* 二层负载均衡(MAC)
二层负债均衡是基于数据链路层的负债均衡,即让负债均衡服务器和业务服务器绑定同一个虚拟IP(即VIP),客户端直接通过这个VIP进行请求,那么如何区分相同IP下的不同机器呢?没错,通过MAC物理地址,每台机器的MAC物理地址都不一样,当负载均衡服务器接收到请求之后,通过改写HTTP报文中以太网首部的MAC地址,按照某种算法将请求转发到目标机器上,实现负载均衡。
这种方式负载方式虽然控制粒度比较粗,但是优点是负载均衡服务器的压力会比较小,负载均衡服务器只负责请求的进入,不负责请求的响应(响应是有后端业务服务器直接响应给客户端),吞吐量会比较高。
在这里插入图片描述
* 三层负载均衡(IP)
三层负载均衡是基于网络层的负载均衡,通俗的说就是按照不同机器不同IP地址进行转发请求到不同的机器上。
这种方式虽然比二层负载多了一层,但从控制的颗粒度上看,并没有比二层负载均衡更有优势,并且,由于请求的进出都要经过负载均衡服务器,会对其造成比较大的压力,性能也比二层负载均衡要差。
* 四层负载均衡(TCP)
四层负载均衡是基于传输层的负载均衡,传输层的代表协议就是TCP/UDP协议,除了包含IP之外,还有区分了端口号,通俗的说就是基于IP+端口号进行请求的转发。相对于上面两种,控制力度缩小到了端口,可以针对同一机器上的不用服务进行负载。
这一层以LVS为代表。
* 七层负载均衡(HTTP)
七层负载均衡是基于应用层的负载均衡,应用层的代表协议有HTTP,DNS等,可以根据请求的url进行转发负载,比起四层负载,会更加的灵活,所控制到的粒度也是最细的,使得整个网络更"智能化"。例如访问一个网站的用户流量,可以通过七层的方式,将对图片类的请求转发到特定的图片服务器并可以使用缓存技术;将对文字类的请求可以转发到特定的文字服务器并可以使用压缩技术。可以说功能是非常强大的负载。
这一层以Nginx为代表。
> 分类总结:
在普通的应用架构中,使用Nginx完全可以满足需求,对于一些大型应用,一般会采用DNS+LVS+Nginx的方式进行多层次负债均衡,以上这些说明都是基于软件层面的负载均衡,在一些超大型的应用中,还会在前面多加一层物理负载均衡,比如知名的F5

负载均衡算法

一般分为静态负载均衡动态负载均衡

静态负载均衡

  1. 轮询法
    将请求按顺序轮流地分配到每个节点上,不关心每个节点实际的连接数和当前的系统负载。

    优点:简单高效,易于水平扩展,每个节点满足字面意义上的均衡;

    缺点:没有考虑机器的性能问题,根据木桶最短木板理论,集群性能瓶颈更多的会受性能差的服务器影响。

public class RoundRobin {
  private static Map<String, Integer> serviceWeightMap = new ConcurrentHashMap<>();
 
  static {
    serviceWeightMap.put("192.168.1.100", 1);
    serviceWeightMap.put("192.168.1.101", 1);
    serviceWeightMap.put("192.168.1.102", 4);
    serviceWeightMap.put("192.168.1.103", 1);
    serviceWeightMap.put("192.168.1.104", 1);
    serviceWeightMap.put("192.168.1.105", 3);
    serviceWeightMap.put("192.168.1.106", 1);
    serviceWeightMap.put("192.168.1.107", 2);
    serviceWeightMap.put("192.168.1.108", 1);
    serviceWeightMap.put("192.168.1.109", 1);
    serviceWeightMap.put("192.168.1.110", 1);
  }
 
  private static Integer pos = 0;
 
  public static String testRoundRobin() {
    Set<String> keySet = serviceWeightMap.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;
  }
}
  1. 随机法
    将请求随机分配到各个节点。由概率统计理论得知,随着客户端调用服务端的次数增多,其实际效果越来越接近于平均分配,也就是轮询的结果。

    优缺点和轮询相似。

 public static String testRandom() {
    Set<String> keySet = serviceWeightMap.keySet();
    ArrayList<String> keyList = new ArrayList<String>();
    keyList.addAll(keySet);
 
    Random random = new Random();
    int randomPos = random.nextInt(keyList.size());
    String server = keyList.get(randomPos);
    return server;
  }
  1. 源地址哈希法
    源地址哈希的思想是根据客户端的IP地址,通过哈希函数计算得到一个数值,用该数值对服务器节点数进行取模,得到的结果便是要访问节点序号。采用源地址哈希法进行负载均衡,同一IP地址的客户端,当后端服务器列表不变时,它每次都会落到到同一台服务器进行访问。

    优点:相同的IP每次落在同一个节点,可以人为干预客户端请求方向,例如灰度发布;

    缺点:如果某个节点出现故障,会导致这个节点上的客户端无法使用,无法保证高可用。当某一用户成为热点用户,那么会有巨大的流量涌向这个节点,导致冷热分布不均衡,无法有效利用起集群的性能。所以当热点事件出现时,一般会将源地址哈希法切换成轮询法。

  public static String testConsumerHash(String remoteIp) {
    Set<String> keySet = serviceWeightMap.keySet();
    ArrayList<String> keyList = new ArrayList<String>();
    keyList.addAll(keySet);
 
    int hashCode = remoteIp.hashCode();
    int pos = hashCode % keyList.size();
 
    return keyList.get(pos);
  }
  1. 加权轮询法
    不同的后端服务器可能机器的配置和当前系统的负载并不相同,因此它们的抗压能力也不相同。给配置高、负载低的机器配置更高的权重,让其处理更多的请;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载,加权轮询能很好地处理这一问题,并将请求顺序且按照权重分配到后端。

    加权轮询算法要生成一个服务器序列,该序列中包含n个服务器。n是所有服务器的权重之和。在该序列中,每个服务器的出现的次数,等于其权重值。并且,生成的序列中,服务器的分布应该尽可能的均匀。比如序列{a, a, a, a, a, b, c}中,前五个请求都会分配给服务器a,这就是一种不均匀的分配方法,更好的序列应该是:{a, a, b, a, c, a, a}。

    优点:可以将不同机器的性能问题纳入到考量范围,集群性能最优最大化;

    缺点:生产环境复杂多变,服务器抗压能力也无法精确估算,静态算法导致无法实时动态调整节点权重,只能粗糙优化。

 public static String testWeightRoundRobin() {
    Set<String> keySet = serviceWeightMap.keySet();
    Iterator<String> it = keySet.iterator();
    List<String> serverList = new ArrayList<String>();
 
    while (it.hasNext()) {
      String server = it.next();
      Integer weight = serviceWeightMap.get(server);
      for (int i=0; i<weight; i++) {
        serverList.add(server);
      }
      Collections.shuffle(serverList);
    }
 
    String server = null;
    synchronized (pos) {
      if (pos > serverList.size()) {
        pos = 0;
      }
      server = serverList.get(pos);
      pos++;
    }
    return server;
  }
  1. 加权随机法
    与加权轮询法一样,加权随机法也根据后端机器的配置,系统的负载分配不同的权重。不同的是,它是按照权重随机请求后端服务器,而非顺序。
 public static String testWeightRandom() {
    Set<String> keySet = serviceWeightMap.keySet();
    List<String> serverList = new ArrayList<String>();
    Iterator<String> it = keySet.iterator();
 
    while (it.hasNext()) {
      String server = it.next();
      Integer weight = serviceWeightMap.get(server);
      for (int i=0; i<weight; i++) {
        serverList.add(server);
      }
    }
    Random random = new Random();
    int randomPos = random.nextInt(serverList.size());
    String server = serverList.get(randomPos);
    return server;
  }

动态负载均衡

  1. 最小连接数法
    根据每个节点当前的连接情况,动态地选取其中当前积压连接数最少的一个节点处理当前请求,尽可能地提高后端服务的利用效率,将请求合理地分流到每一台服务器。俗称闲的人不能闲着,大家一起动起来。

    优点:动态,根据节点状况实时变化;

    缺点:提高了复杂度,每次连接断开需要进行计数;

    实现:将连接数的倒数当权重值。

  2. 最快响应速度法
    根据请求的响应时间,来动态调整每个节点的权重,将响应速度快的服务节点分配更多的请求,响应 速度慢的服务节点分配更少的请求,俗称能者多劳,扶贫救弱。

    优点:动态,实时变化,控制的粒度更细,跟灵敏;

    缺点:复杂度更高,每次需要计算请求的响应速度;

    实现:可以根据响应时间进行打分,计算权重。

  3. 观察模式法
    观察者模式是综合了最小连接数和最快响应度,同时考量这两个指标数,进行一个权重的分配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值