负载均衡

负载均衡

关于负载均衡相关的一些介绍

要点

  1. 服务发现 - 如何识别或路由到最终的业务应用
  2. 产品选型 - 采用什么产品、硬件、架构
  3. 负载算法

一、服务发现

即关于负载均衡的路径信息该在哪个节点维护

一般来说数据流有 3 个区域: 客户端 -> 负载均衡器(lb) -> N 个上游服务器(service)

方案一: 在客户端配置

即最终访问哪个节点是由客户端决定的,有多种细分方案

  1. 客户端硬编码; 客户端有一个服务列表,每次请求按设定规则随机或轮询选择一个地址进行访问;

  2. 客户端指定服务器转发; 客户端在http请求 的 header 头增减一个决定路由的字段, 服务器端LB再根据这个字段进行转发到指定服务器;

  3. 服务接口列表:客户先请求一个服务器接口, 获取一个可用地址列表, 然后选一个再发起业务请求, 类似 DNS

优点: 简单, 可控, 客户端调试时很容易定位到这个请求是哪个服务器处理的;
缺点: 极端情况下变更服务节点可能需要同时改这三个区域的配置;

方案二: 在 LB 上配置

很好理解, 传统的 nginx, haprox 等都是这样做的;

即 LB 根据自身独立的负载规则转发到后端应用;

优点: 好理解, 可控程度高

缺点: 后端变更后(如新增节点), 需要再改一次 lb 的配置

方案三: 在后端服务上配置

核心是 lb 动态获取后端服务的变化, 有多种细分实现方法;

  1. 方法一: lb 提供一个接口, 后端应用服务上线时自动去 lb 上注册, 下线时主动注销;

  2. 方法二: 有一个公共注册中心或配置中心(etcd, consul, redis, eureka, mysql 等), service 去注册和注销, lb 动态去拉取更新, 或者是 lb 进行 watch 监听;

    这两个方法都存在的问题: 程序异常停止没主动下线时的问题, 需要配合健康检查;

  3. 方法三: 和方法二差不多, 但 lb 不是监听注册中心, 而是监听 docker, kubernetes 等组件的 api, 服务一有变动立即可知;
    程序内部不需要引入注册类的库, 历史老旧应用无需改造即可适配, 特别适合云原生环境;

优点: 一次变更只需要后端的服务改一次
缺点: 假设某个节点服务异常, 希望隔离流量待排查, 而不是直接下线服务时, 比较麻烦;

方案选择

根据实际情况选

  1. 绝对性能型 - 优先客户端负载
  2. 方便可控型 - 优先 lb 配置
  3. 自动化程度要求高 - 就由后端配置
  4. 如果是云原生或微服务场景也更推荐由后端应用控制

负载均衡算法

说明事项

不同模型下对于代理后面的的服务器叫法不一样, 有时候叫 上游(nginx), 有时候叫 后端,有的又叫 RS(lvs 的 RealServer),还有直接叫 server 的,大家知道是同一个东西就行。

算法简单分为两类: 静态算法 和 动态算法(根据状态的不同分配方式由所变化)

1. 静态算法

  1. RR (Round-Robin) 轮询
    优点:简单,性能高 特点: 不记录当前连接的状态,是一种无状态调度 缺点: 如果后端资源配置不一样, 容易造成负载不平衡

  2. WRR (Weighted Round-Robin) 加权轮询
    在 RR 的基础上,可以增加或减少分配给某个后端应用的流量比例;

    例如: A=1,B=2; 则请求 1 给 A, 请求 2,3 给 B, 请求 4 给 A, 请求 5,6 给 B

  3. random 随机算法

  4. ip_hash (Source Hashing) 源地址 HASH
    根据这个请求的源地址进行 hash, 然后从地址列表中找出一个服务器来; 类似与 取模 运算;

    优点: 同一个客户端IP的请求始终会落到一个后端节点,便于 cookie 与 session 进行会话绑定; 缺点: 后端新增或减少一个节点时,将有 1/n 流量始终不可用, 如果重启或重载配置, 则原来映射关系就会全部重新排。

  5. 一致性哈希
    简单理解: 划一个圈, 每个节点占一个范围;

    如果有新增服务器, 就圈内找最合适的一个节点, 将它的一部分范围转交给新节点;
    也可以是两个相邻节点各出让一部分 如果是某各节点故障, 就把它原来的这个范围, 给它相邻的两个节点进行瓜分;

    优点: 可以在客户端实现 缺点: 节点变化时需要操作的内容有些多, 适合在客户端实现, 而不是 LB

  6. 序列分配
    根据键的序列进行分配; 如 ID 为 1-1000 分到 A 节点, 1001-2000 分配到 B 节点

  7. 取模分配
    对 key 和节点数进行取模计算后分配到相应节点

2. 动态算法

  1. 最小连接数(Least-Connection ) 简称 LC

    对到每个后端服务器的连接进行计数, 谁少就把新连接给谁; 适合场景: 下载类的服务,小文件下载连接释放得快

  2. 加权最少链接(Weighted Least-Connection) 简称 WLC 在 LC 的基础上增加了权重, 权重高的可以承受更多连接

  3. 最快响应模式
    传递连接给那些响应最快的服务器

  4. 还有一些 lvs 的不常用算法, 如 LBLC(局部性的最少链接), LBLCR(带复制的基于局部性最少链接), SED(最短期望延迟), NQ(Never Queue) 等。比较复杂,我却从来没看谁用过,始终不理解场景, 就不抄了;

  5. 自定义控制
    如根据监控系统内的 CPU 使用率和连接数,当前大事务量等, 来改变注册中心内配置的权重 劣势: 实现复杂 优点: 非常灵活

LB 的一次连接和二次连接模式

一次连接: 负载均衡器对数据包仅做转发, 而不对后端重新发起三次握手, 类似交换机, 不记录状态;

二次连接: 和一次连接相对应, 在 tcp 转发时候, 对后端重新进行了三次握手,代理模式,有状态记录;

不严谨的话可以通过简单对比源端口是否有改变来简单判断是一次连接还是二次连接, 端口没改变, 可以理解为一次连接, 有改变就是二次连接

最后更新于