服务网格 ASM 负载均衡算法全面解析

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
全局流量管理 GTM,标准版 1个月
传统型负载均衡 CLB,每月750个小时 15LCU
简介: 在本文中,笔者将解析服务网格的多种负载均衡算法的实现原理和使用场景,为服务网格负载均衡算法的选择提供参考。

【阅读原文】戳:服务网格 ASM 负载均衡算法全面解析


引言:当今,分布式微服务是毫无争议地主流架构模式,微服务将复杂大型系统拆分成多个松耦合的微型系统,以此使得大型系统的迭代更为灵活,容错率也变得更高。对于其中的单一微服务,支持横向扩容,以根据业务情况灵活地调整系统中每个服务所使用的资源。要正确地调用多副本服务,有两个基础的问题需要解决:服务发现和负载均衡。K8s作为当前业界广泛采用的部署平台,通过服务(Service)非入侵式地解决了服务发现和负载均衡的问题,但是,面对日益复杂的业务场景,K8s服务仍然存在一些不尽人意之处,例如,其负载均衡算法仅支持随机,而对于一些特定场景,随机算法的表现较差,且没有调优余地。因此,越来越多地微服务运维团队选择了服务网格作为网络基础设施,其在K8s服务的基础上,提供了比K8s更强大的负载均衡能力。在本文中,笔者将解析服务网格的多种负载均衡算法的实现原理和使用场景,为服务网格负载均衡算法的选择提供参考。

 

 

服务网格负载均衡的优势

 

K8s Service通过iptables规则,实现了连接级别的负载均衡,这种负载均衡不需要入侵应用,而是直接从网络层面将流量做DNAT,实现了负载均衡。但K8s的负载均衡存在一些显著的缺陷,首先,由于其使用iptables规则实现负载均衡,这使得K8s的负载均衡算法的时间复杂度是O(n),因此在集群规模逐渐膨胀时,负载均衡的性能可能受到显著影响。其次,其只支持随机负载均衡算法,这种算法并不能严格地将请求均匀地分布到后端实例上。由于其基于iptables规则实现的缘故,因为其工作在3层(IP层),因此只能做到4层,即TCP连接级别的负载均衡,而当今大多数主流应用使用HTTP/GRPC协议进行通信,基于iptables规则实现的K8s服务负载均衡注定无法实现请求级别的路由。而服务网格控制平面从K8s集群ApiServer获得工作负载和服务信息,并将这些信息下发至SIdecar,借助其7层能力,支持对HTTP/GRPC请求实现请求级负载均衡,同时支持了适用于多种场景的负载均衡算法,用户可以根据业务特性,为特定服务选择适用的负载均衡算法,以更高效地利用系统资源。


 


 

 

负载均衡算法

 

服务网格ASM提供了多种负载均衡算法,其中包括与社区Istio兼容的RANDOM、ROUND_ROBIN,LEAST_REQUEST以及ASM特有的PEAK_EWMA。在本文接下来的内容中,笔者将逐一分析每种负载均衡算法的特点及其适用场景。

 

 

RANDOM和ROUND_ROBIN

 

服务网格ASM提供了经典的随机(RANDOM)和轮询(ROUND_ROBIN)算法,对于随机负载均衡,与K8s服务不同的是,由于ASM Sidecar工作在7层,因此对于HTTP、GRPC协议的通信可以提供请求级别的随机负载均衡。随机负载均衡由于随机计算本身的不稳定性,不能保证请求完全均匀地分布到后端。此时若工作负载的性能和请求产生的计算量都比较平均,则可以考虑使用轮询算法进行负载均衡,轮询算法可以完全平均地将请求分布到后端工作负载。这两种算法被广泛使用在各种分布式系统的负载均衡中,它们足够简单直观,且被几乎所有负载均衡软硬件支持,但是,在实践中却存在一些场景,使得这两种算法的表现不尽人意。

 

 

RANDOM和ROUND_ROBIN的不足之处

 

由于RANDOM和ROUND_ROBIN两种负载均衡算法均是完全不考虑后端服务状态,仅使用本地状态计算得出负载均衡结果的算法,因此,后端服务状态变化完全不会影响到负载均衡计算的结果,从而导致做出不够理想的负载均衡决策,例如以下这些场景:

 

1.后端处理能力不均衡

 

在后端处理能力不均衡时,完全均衡地分布请求反而会影响服务质量,由于后端负载能力不平均,处理能力弱的后端会更早过载,而负载均衡算法仍然以平均分配的方式将请求分配至后端,这些被分配至已过载的后端的请求会加剧已过载后端的压力,进一步恶化服务的延迟,影响服务的整体表现,例如下图例子中,Server-1的处理能力更强,还能够接受新的请求,而Server-2已经满载,但随机或者轮询负载均衡算法仍然将新请求打到Server-2,而不是尚有余力的Server-1,这将导致这个请求的返回实践因Server-2的满载而显著延长。


 


 

2.请求计算量不平均

 

即使后端的处理能力类似,同类请求对后端造成的压力也可能存在显著的差异,一个典型的例子是LLM应用,同样是几个单词的prompt,“write me a novel”和“what is the time”两个prompt在后端计算量上的差异可谓天壤之别,因此返回时间可能也存在显著差异,例如下图的例子中,Server-1和Server-2正在处理的请求虽然都是2个,但是,Server-2处理了一个大计算量的请求,导致其已经满载,然而随机或者轮询负载均衡算法仍然将新请求打到Server-2,而不是尚有余力的Server-1,这将导致这个请求的返回实践因Server-2的满载而显著延长。

 

即使是在请求计算量大致均等的场景下,错误的请求也可能导致类似的问题,例如客户端发送了错误的请求,服务端可能在校验失败后不做任何处理就立即返回,就会间接导致请求计算量不平均的问题。

 


 


 

如何避免RANDOM和ROUND_ROBIN的不足

 

RANDOM和ROUND_ROBIN存在的缺陷本质上还是因为负载均衡无法感知后端的状态所致,理论上来说,最优的负载均衡决策应该结合后端实时状态和请求的潜在计算量做出,然而,这两者在真正的负载均衡实现中都存在很大的挑战,那么是否存在对后端状态间接感知的方式呢?笔者将在下面的篇幅中介绍ASM支持的另外两种负载均衡算法。

 

 

LEAST_REQUEST

 

LEAST_REQUEST算法在用户未显式指定时被作为ASM默认提供的负载均衡算法,该负载均衡器会记录当前每个后端上未完成的请求数量,处理能力更强的后端往往会因请求更快完成而更快地消耗这个计数,负载均衡器借助这个现象来间接感知后端的压力状态,并总是将请求路由到未完成请求数最少的后端。

 

下面,我们来通过例子直观地感受一下LEAST_REQUEST的优势,首先以前面提到过的后端处理能力不均衡场景为例,在T1时刻,负载均衡向Server-1和Server-2分别分配了3个请求,由于Server-1的并发处理能力更大,到T2时刻时,Server-1已经完成了对全部3个请求的处理,而Server-2仍然有一个请求未完成处理,由于LEAST_REQUEST总是会向进行中请求最少的后端分配请求,因此,新到达的请求被分配至负载更低的Server-1。


 


 

我们再来看看请求计算量不均的场景,这个场景下同样的请求对后端的计算复杂度差异显著,下图例子中,在T1时刻,Server-1有2个进行中的请求,Server-2只有一个,但Server-2处理的是一个大计算量请求(例如一个复杂的Prompt)。到T2时刻,Server-1的2个请求都已经返回,而Server-2尚未完成一个复杂请求的计算和返回。此时,如果负载均衡器收到了新的请求,由于Server-1的进行中请求数量少于Server-2,新请求会被分配至Server-1。


 

 


结合上述两个例子,我们可以直观地看到LEAST_REQUEST负载均衡算法在应对更加复杂场景时相对于RANDOM和ROUND_ROBIN算法表现出了更强的自适应能力,通过进行中请求数的变化实时地感知后端地状态以做出更优的负载均衡决策,降低请求平均延迟,提升整个系统的利用率。这样的特性非常适合对于同类请求计算量差异的AI场景。

 

LEAST_REQUEST能在绝大多数情况下提供较好的负载均衡表现,但是LESAT_REQUEST是任何情况下的最优解吗?我们不妨思考这样一种情况,当后端因为异常出现报错时,由于错误请求没有被实际处理,往往会立即返回,LEAST_REQUEST负载均衡器中记录的报错节点的进行中的请求数量会快速降低,该后端在该评价体系下被误认为是“高性能”的,因此更多的请求被分配到发生错误的后端,进而造成系统的整体错误率提升。显然,这个缺陷是由于LEAST_REQUEST评估体系无法感知错误率的存在导致的,那么是否有更好的选择呢,接下来,笔者将介绍ASM提供的PEAK_EWMA负载均衡器。

 

 

PEAK_EWMA

 

 

ASM在1.21版本提供了PEAK_EWMA负载均衡器,该负载均衡器会ASM结合端点在过去一段时间内的响应时间、错误率,静态权重等因子计算出端点的负载均衡权重计算节点的分值,在一段时间内响应时间越短、错误率越低,静态权重越高,则该节点的分值越高,更容易被负载均衡选中。相比于LEAST_REQUEST,PEAK_EWMA的感知范围更全面,延迟升高、错误率上升都会造成该后端的权重降低,更少的请求被路由到这个后端,从而尽可能地提升服务整体地延迟和成功率表现。

 

这里我们提供了一个PEAK_EWMA负载均衡器的端到端实例,该示例使用simple-server应用作为服务端,该应用可通过启动参数指定,模拟不同状态码返回、随机延迟范围等多种服务端响应行为。在本例中,我们将部署两个Deployment,其中名为simple-server-503的deployment使用--mode=503启动参数设置为固定返回503,模拟一个收到请求立即返回503状态码的后端;名为simple-server-normal的deployment则使用--mode=normal--delayMin=50--delayMax=200模拟一个正常返回200的后端,其处理延迟在50-200之间波动。


 

 

 


前提条件

 

已创建ASM 1.21或以上版本的实例

已创建K8s 1.21或以上版本的集群,并已将K8s集群添加至ASM实例

 

 

部署示例

 

在K8s集群中通过以下yaml部署simple-server应用和服务,以及用于发起测试流量的sleep应用:


apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: simple-server
  name: simple-server-normal
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-server
  template:
    metadata:
      labels:
        app: simple-server
    spec:
      containers:
      - args:
        - --mode
        - normal
        image: registry-cn-hangzhou.ack.aliyuncs.com/test-public/simple-server:v1.0.0.2-gae1f6f9-aliyun
        imagePullPolicy: IfNotPresent
        name: simple-server
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: simple-server
  name: simple-server-503
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: simple-server
  template:
    metadata:
      labels:
        app: simple-server
    spec:
      containers:
      - args:
        - --mode
        - "503"
        image: registry-cn-hangzhou.ack.aliyuncs.com/test-public/simple-server:v1.0.0.2-gae1f6f9-aliyun
        imagePullPolicy: IfNotPresent
        name: simple-server
        env:
        - name: POD_NAME
          valueFrom:
            fieldRef:
              apiVersion: v1
              fieldPath: metadata.name
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: simple-server
  name: simple-server
  namespace: default
spec:
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: simple-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sleep
  template:
    metadata:
      labels:
        app: sleep
    spec:
      terminationGracePeriodSeconds: 0
      serviceAccountName: sleep
      containers:
      - name: sleep
        image: registry-cn-hongkong.ack.aliyuncs.com/test/curl:asm-sleep
        command: ["/bin/sleep", "infinity"]
        imagePullPolicy: IfNotPresent

 


为了避免ASM默认存在的重试机制(默认重试2次)对验证结果造成影响,我们通过为simple-server应用VirtualService来显式地关闭重试。使用ASM实例的kubeconfig,应用以下VirtualService:

 

apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: simple-server
  namespace: default
spec:
  hosts:
    - simple-server.default.svc.cluster.local
  http:
    - name: default
      retries:
        attempts: 0 # 关闭重试
      route:
        - destination:
            host: simple-server.default.svc.cluster.local

 


使用K8s集群kubeconfig,执行以下命令来发起测试:

 

$ kubectl exec -it deploy/sleep -c sleep -- sh -c 'for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" simple-server:8080/hello; done'
200
200
200
503
503
503
503
503
200
503

 


可以看到,由于simple-server服务下有一个后端是固定返回503的,默认的LEAST_REQUEST并不能很好地在负载均衡阶段避开返回错误的节点。接下来我们来看看ASM的PEAK_EWMA负载均衡器的表现,在ASM实例中应用以下DestinationRule,为simple-server服务启用PEAK_EWMA负载均衡器:

 

apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: simple-server
  namespace: default
spec:
  host: simple-server.default.svc.cluster.local
  trafficPolicy:
    loadBalancer:
      simple: PEAK_EWMA
---

 


使用同样的命令再次执行测试:

 

$ kubectl exec -it deploy/sleep -c sleep -- sh -c 'for i in $(seq 1 10); do curl -s -o /dev/null -w "%{http_code}\n" simple-server:8080/hello; done'
200
503
200
200
200
200
200
200
200
200

 


可以看到,在返回了一个503后,后续全部都是200,这是因为PEAK_EWMA负载均衡器在后端返回503后,导致错误率上升,从而降低了该断点的分值,因此在之后的一段时间内,该后端将在分值比较时低于正常的后端,因此不会被选择到。但是,过一段时间后,这种影响因为移动平均的缘故又被消除,从而使得负载均衡器可以重新选择到该后端。我们在前文提到过,PEAK_EWMA算法不仅可以感知后端的错误率,同时还可以感知后端延迟变化,尽可能将请求路由给延迟更低的端点,关于这一点ASM官方文档提供了一个例子,欢迎各位读者前往体验。

 

 

总结

 

负载均衡是一个现代分布式应用绕不开的一个重要话题,每一种负载均衡都有其适用的场景,根据业务场景正确地选择负载均衡算法能为应用带来更好的表现。阿里云服务网格ASM团队在结合了大量客户实际场景后,于1.21版本推出了PEAK_EWMA负载均衡算法,该负载均衡算法在传统的负载均衡算法上更进一步,提供了综合后端延迟、错误率、静态权重的负载均衡选择能力,让负载均衡能够动态适应应用状态的变化,做出更优的负载均衡决策,从而提升应用的全局表现。



我们是阿里巴巴云计算和大数据技术幕后的核心技术输出者。

欢迎关注 “阿里云基础设施”同名微博知乎

获取关于我们的更多信息~

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
21天前
|
弹性计算 负载均衡 网络协议
阿里云SLB深度解析:从流量分发到架构优化的技术实践
本文深入探讨了阿里云负载均衡服务(SLB)的核心技术与应用场景,从流量分配到架构创新全面解析其价值。SLB不仅是简单的流量分发工具,更是支撑高并发、保障系统稳定性的智能中枢。文章涵盖四层与七层负载均衡原理、弹性伸缩引擎、智能DNS解析等核心技术,并结合电商大促、微服务灰度发布等实战场景提供实施指南。同时,针对性能调优与安全防护,分享连接复用优化、DDoS防御及零信任架构集成的实践经验,助力企业构建面向未来的弹性架构。
165 76
|
2天前
|
机器学习/深度学习 监控 算法
员工上网行为监控软件中基于滑动窗口的C#流量统计算法解析​
在数字化办公环境中,员工上网行为监控软件需要高效处理海量网络请求数据,同时实时识别异常行为(如高频访问非工作网站)。传统的时间序列统计方法因计算复杂度过高,难以满足低延迟需求。本文将介绍一种基于滑动窗口的C#统计算法,通过动态时间窗口管理,实现高效的行为模式分析与流量计数。
17 2
|
20天前
|
存储 监控 算法
基于 C++ 哈希表算法的局域网如何监控电脑技术解析
当代数字化办公与生活环境中,局域网的广泛应用极大地提升了信息交互的效率与便捷性。然而,出于网络安全管理、资源合理分配以及合规性要求等多方面的考量,对局域网内计算机进行有效监控成为一项至关重要的任务。实现局域网内计算机监控,涉及多种数据结构与算法的运用。本文聚焦于 C++ 编程语言中的哈希表算法,深入探讨其在局域网计算机监控场景中的应用,并通过详尽的代码示例进行阐释。
41 4
|
23天前
|
存储 监控 算法
员工电脑监控场景下 Python 红黑树算法的深度解析
在当代企业管理范式中,员工电脑监控业已成为一种广泛采用的策略性手段,其核心目标在于维护企业信息安全、提升工作效能并确保合规性。借助对员工电脑操作的实时监测机制,企业能够敏锐洞察潜在风险,诸如数据泄露、恶意软件侵袭等威胁。而员工电脑监控系统的高效运作,高度依赖于底层的数据结构与算法架构。本文旨在深入探究红黑树(Red - Black Tree)这一数据结构在员工电脑监控领域的应用,并通过 Python 代码实例详尽阐释其实现机制。
40 6
|
9天前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
25 0
|
1月前
|
监控 算法 安全
基于 C# 的内网行为管理软件入侵检测算法解析
当下数字化办公环境中,内网行为管理软件已成为企业维护网络安全、提高办公效率的关键工具。它宛如一位恪尽职守的网络守护者,持续监控内网中的各类活动,以确保数据安全及网络稳定。在其诸多功能实现的背后,先进的数据结构与算法发挥着至关重要的作用。本文将深入探究一种应用于内网行为管理软件的 C# 算法 —— 基于二叉搜索树的入侵检测算法,并借助具体代码例程予以解析。
47 4
|
1月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
1月前
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
40 7
|
2月前
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
239 0
|
2月前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
43 0

推荐镜像

更多
下一篇
oss创建bucket