Halo

A magic place for coding

0%

浅谈负载均衡及代理

Introduction

   有关网络层的博客发的比较少,最近抽点时间了解一下这块的细节,然后把学习的一些心得分享到这里。在这篇博客主要介绍的是网络层中的负载均衡,这是一块现代软件工程很重要的组成部分。


What is network load balancing?

   这部分我们来看看什么是网络负载均衡(Network Load Balancing)。

Network Load Balancing

   负载均衡是计算机领域中一个很重要且广泛的概念,并不只是特指网络层。例如,在 **OS 领域 ** 中,操作系统通过负载均衡策略去调度各种计算任务,合理分配到不同的 CPU;在 ** 分布式系统领域 ** 中,容器编排调度系统(如 k8s)使用负载均衡策略调度不同的实例到不同的计算集群;在 ** 网络 ** 的领域,网络层通过负载均衡策略调度网络任务到可用的后端服务器。所以负载均衡是一个很广泛的概念,但是在这篇博客,我们把重点放在网络的负载均衡。

Network load bbalancing overview

   上图展示了一个网络中负载均衡最简单的结构。用户发起网络请求,负载均衡器在用户和服务器之间,它有下面几个工作内容:

  • ** 服务发现(Service Discovery)**:负载均衡器需要知道哪些后端服务器是可用的,需要通过服务发现来得到它们的 ip 地址;
  • ** 健康检查(Health Checfking)**:负载均衡器需要通过某种手段去验证后端服务器是否健康,以及能否接受网络请求;
  • ** 负载均衡(Load Balancing)**:通过某种策略,合理地把用户的请求分配到不同的健康的后端服务器。

   使用负载均衡有什么好处呢?

  • ** 命名抽象(Naming Abstraction)**:对于用户来说,后端服务器的 ip 地址是抽象的,用户并不需要知道每一个可用的后端服务器地址,它只需要知道负载均衡器的地址即可;
  • ** 容错机制(Fault Tolerance)**:负载均衡器的服务发现和健康检查使得用户的请求能有效避开不可用的后端服务器,当后端服务器出现部分故障或负载过高的情况,其余的请求仍然能够被正常处理;
  • ** 成本和经济效益(Cost and Performance Benefits)**:大多数的分布式系统会分布在不同的网络分区,或者一个分布式系统会跨多个网络分区。对于网络分区来说,不同分区之间一般是 oversubscribed 的,对于同一个分区内,通常是 undersubscribed 的,负载均衡能够把流量尽可能保持在一个网络分区内,可以提升性能吧,减少网络分区之间的开销。

负载均衡的种类

   目前工业界使用最多的就是 L4 和 L7 两种负载均衡,这个 4 层和 7 层是对应 OSI 模型而言的。这里的层数,指的是负载均衡过程中,利用到了第几层的信息。如 L4 意思就是利用到了第四层的信息,即网络层。所以能够利用 TCP 中的 ip 和 port 去做负载均衡。L7 的意思就是利用到了第七层的信息,比如对于 http 请求来说,就可以获得用户的语言、区域甚至 cookie 信息用于负载均衡。当然,除了这两个之外,还有其他的负载均衡,但是用的不多。

L4 Load Balancing 四层负载均衡

L4 Load Balancing Exmaple

   这幅图展示了一个简单的 L4 负载均衡的工作过程,用户发起一个 TCP 请求到负载均衡器,负载均衡器替换到网络层的 IP 地址和 port 后,重新发起一个新的 TCP 请求到后端服务器。返回服务器返回信息给负载均衡器,最后负载均衡器再把 response 返回给用户。这其中用到的最主要的信息就是 ip 和 port,这是第四层中最为关键的信息。

L7 Load balancing 七层负载均衡

L7 Load Balancing Example

   看完 L4 负载均衡的简单介绍,我觉得这个策略挺合理的,毕竟我们大多数都是用 ip+port 的形式去定位某一个用户,但是按照用户的维度做负载均衡,这个粒度还是不够细致。原因是当前很多客户端都采用多路复用的模式,即用一条链接发送所有的应用请求(因为创建链接的开销很大,尤其链接需要 TLS 加密时,开销更大),所以某个用户的请求也应该能够被合理地分配到不同的服务器进行处理。

   先来看一个场景,有两个 HTTP/2 Client 发起通信请求,Client 1 每分钟发送 1 个请求,Client 2 每秒 20 个请求,这两个请求的频率是相差很远的。如果使用 L4 负载均衡,会按照这两个 client 的 ip+port 去负载均衡,Client1 的请求分配到 A 服务器,Client2 的请求分配到 B 服务器,从结果看,B 服务器的负载就是 A 的 1200 倍了,这好像并没有达到负载均衡的要求。原因是什么呢?仅通过 ip+port 去分发流量并不够细致,因为 4 层的信息太少了,这也是为什么要引入 7 层负载均衡的原因。7 层包括了应用层协议,比如 http、smtp 等,这里面能够利用的信息就多很多了,我们可以根据里面的信息再做更加合理的分发。

   所以上面的例子,放到 L7 负载均衡是这样的。两个请求的内容可能是不一样的,Client1 请求的可能是实时性不高的 meta 信息,而 Client2 请求的是实时性很高的用户数据,那么可以根据这两个请求实际需要的资源(这个信息在应用层中),去分配到不同的服务器。

Types of Load Balancer Topologies

   在这一部分我们来讨论 Load Balancer 的拓扑类型,所谓拓扑类型指的是负载均衡服务器是怎么部署的。

Middle Proxy 中间代理

midlle proxy

   这种类型叫 ** 中间代理 **,顾名思义就是负载均衡服务器处于 Client 和 Server 的中间。Client 通过 DNS 连接 Load Balancer。这种模型是最常用也最通用的模型,部署起来也比较简单,缺点是这里的 Load Balancer 也是一个故障单点和系统瓶颈。

Edge Proxy 边缘代理

edge proxy

   这种类型叫 ** 边缘代理 **,这里的边缘指的是网关的边缘。这种模式可以理解为是中间代理的一个变种,中间代理模式下,Client 在公网直接访问到 Load Balancer;但是在边缘代理模式下,Load Balancer 前面还包含了一个 gateway,提供了 TLS 中断、限速、认证、流量路由等功能。其优缺点和中间代理是一样的,这种模式现在在大厂比较常用。对于一个面向公网的一个大型分布式系统,如果 Client 直接通过 DNS 连接 Load Balancer 是不可控的,也是极其危险的,所以通常会部署一个 gateway 让所有公网的流量进入系统。

Embedded Client Library 嵌入 Client 库

embedded client library

   上述两种模型的缺点都是单点故障风险以及不便于横向扩容,为了克服这个问题,有一种解决方法是以库的形式嵌入到服务中。基于库的解决方案带来的好处就是把负载均衡的逻辑都分发到每一个 Client 中,因此单点故障和瓶颈的问题也就不复存在了。然而这个方法也是有很大限制的。首先是需要使用多种语言去实现负载均衡的逻辑,以适用于不同的 Client 设备,同时,当这个开发库需要升级时,也需要花费很多资源。

Sidecar Proxy

sidecar proxy

  Sidecar proxy 是 Emdedding Client Libray 方案的一个变种,是为了解决库多语言的问题。这个方案通过另外一个不同进程来进行网络访问,通过牺牲一点性能来解决多语言的问题。这种模式是比较新的,但是逐渐变得普遍。


Reference

  1. Introduction to modern network load balancing and proxying
  2. 现代负载均衡和代理简介
  3. 四层、七层负载均衡的区别

Welcome to my other publishing channels