[深入浅出LB]手把手带你实现一个负载均衡器

2021-12-08 16:39:50 浏览数 (1)

写作不易,未经作者允许禁止以任何形式转载!

简介

负载均衡,含义就是根据一定算法将负载(工作任务)进行平衡,分摊到多个操作单元上运行、执行,常见的为Web服务器、企业核心应用服务器和其他主要任务服务器等,从而协同完成工作任务。负载均衡在原有的网络结构上提供了一种透明且有效的的方法扩展服务器和网络设备的带宽、加强网络数据处理能力、增加吞吐量、提高网络的可用性和灵活性,同时承受住更大的并发量级。

简单来说就是将大量的并发请求处理转发给多个后端节点处理,减少工作响应时间。

  • 避免资源浪费
  • 避免服务不可用

一、分类

四层(传输层)

四层即OSI七层模型中的传输层,有TCP、UDP协议,这两种协议中包含源IP、目标IP以外,还包含源端口号及目标端口号。四层负载均衡在接收到客户端请求后,通过修改报文的地址信息(IP PORT)将流量转发到应用服务器。

七层(应用层)

代理负载均衡

七层即OSI七层模型中的应用层,应用层协议较多,常用的为HTTP/HTTPS。七层负载均衡可以给予这些协议来负载。这些应用层协议中会包含很多有意义的内容。比如同一个Web服务器的负载均衡,除了根据IP PORT进行负载均衡,还可以根据七层的URL、Cookie、浏览器类别、语言、请求类型来决定。

四层负载均衡的本质是转发,七层负载均衡的本质是内容交换和代理。

「四层负载均衡」

「七层负载均衡」

「基于」

IP PORT

URL 或 主机IP

「类似」

路由器

代理服务器

「复杂度」

「性能」

高,无需解析内容

中,需算法识别URL Header、Cookie等

「安全性」

低,无法识别DDoS攻击

高,可防御SYN Flood攻击

「扩展功能」

内容缓存、图片防盗链等

二、常见算法

前置数据结构

代码语言:javascript复制
interface urlObj{
  url:string,
  weight:number // 仅在权重轮询时生效
}
urlDesc: urlObj[]

interface urlCollectObj{
  count: number, // 连接数
  costTime: number, // 响应时间
  connection: number, // 实时连接数
}
urlCollect: urlCollectObj[]

Random

随机

image-20210622002949591

代码语言:javascript复制
const Random = (urlDesc) => {
  let urlCollect = [];

  //  收集url
  urlDesc.forEach((val) => {
    urlCollect.push(val.url);
  });

  
  return () => {
    //  生成随机数下标返回相应URL
    const pos = parseInt(Math.random() * urlCollect.length);
    return urlCollect[pos];
  };
};

module.exports = Random;

Weighted Round Robin

权重轮询算法

image-20210622003000250

代码语言:javascript复制
const WeiRoundRobin = (urlDesc) => {
  let pos = 0,
    urlCollect = [],
    copyUrlDesc = JSON.parse(JSON.stringify(urlDesc));

  // 根据权重收集url
  while (copyUrlDesc.length > 0) {
    for (let i = 0; i < copyUrlDesc.length; i  ) {
      urlCollect.push(copyUrlDesc[i].url);
      copyUrlDesc[i].weight--;
      if (copyUrlDesc[i].weight === 0) {
        copyUrlDesc.splice(i, 1);
        i--;
      }
    }
  }
  // 轮询获取URL函数
  return () => {
    const res = urlCollect[pos  ];
    if (pos === urlCollect.length) {
      pos = 0;
    }
    return res;
  };
};

module.exports = WeiRoundRobin;

IP Hash & URL Hash

源IP / URL Hash

image-20210622003030857

代码语言:javascript复制
const { Hash } = require("../util");

const IpHash = (urlDesc) => {
  let urlCollect = [];

  for (const key in urlDesc) {
    // 收集url
    urlCollect.push(urlDesc[key].url);
  }

  return (sourceInfo) => {
    // 生成Hash十进制数值
    const hashInfo = Hash(sourceInfo);
    // 取余为下标
    const urlPos = Math.abs(hashInfo) % urlCollect.length;
    // 返回
    return urlCollect[urlPos];
  };
};

module.exports = IpHash;

Consistent Hash

一致性Hash

image-20210622003051914

代码语言:javascript复制
const { Hash } = require("../util");

const ConsistentHash = (urlDesc) => {
  let urlHashMap = {},
    hashCollect = [];

  for (const key in urlDesc) {
    // 收集urlHash进数组和生成HashMap
    const { url } = urlDesc[key];
    const hash = Hash(url);
    urlHashMap[hash] = url;
    hashCollect.push(hash);
  }
  // 将hash数组从小到大排序
  hashCollect = hashCollect.sort((a, b) => a - b);

  return (sourceInfo) => {
    // 生成Hash十进制数值
    const hashInfo = Hash(sourceInfo);
    // 遍历hash数组找到第一个比源信息hash值大的,并通过hashMap返回url
    hashCollect.forEach((val) => {
      if (val >= hashInfo) {
        return urlHashMap[val];
      }
    });
    // 没找大则返回最大的
    return urlHashMap[hashCollect[hashCollect.length - 1]];
  };
};

module.exports = ConsistentHash;

Least Connections

最小连接数

image-20210622003136462

代码语言:javascript复制
const leastConnections = () => {
  return (urlCollect) => {
    let min = Number.POSITIVE_INFINITY,
      url = "";

    // 遍历对象找到最少连接数的地址
    for (let key in urlCollect) {
      const val = urlCollect[key].connection;
      if (val < min) {
        min = val;
        url = key;
      }
    }
    // 返回
    return url;
  };
};

module.exports = leastConnections;

注:urlCollect为负载均属数据统计对象,有以下属性

  • connection实时连接数
  • count处理请求次数
  • costTime响应时间。

FAIR

最小响应时间

image-20210622003155965

代码语言:javascript复制
const Fair = () => {
  return (urlCollect) => {
    let min = Number.POSITIVE_INFINITY,
      url = "";

     // 找到耗时最少的url 
    for (const key in urlCollect) {
      const urlObj = urlCollect[key];
      if (urlObj.costTime < min) {
        min = urlObj.costTime;
        url = key;
      }
    }
    // 返回
    return url;
  };
};

module.exports = Fair;

看到这里是不是感觉算法都挺简单的

0 人点赞