抖音春晚活动背后的 Service Mesh 流量治理技术

本文整理自火山引擎开发者社区 Meetup 的同名演讲,主要介绍了抖音春晚红包大规模流量场景下的 Service Mesh 流量治理技术。编程

背景与挑战

2021 年的央视春晚红包项目留给业务研发同窗的时间很是少,他们须要在有限的时间内完成相关代码的开发测试以及上线。后端

整个项目涉及到不一样的技术团队,天然也会涉及众多的微服务。这些微服务有各自的语言技术栈,包括 Go,C++,Java,Python,Node 等,同时又运行在很是复杂的环境中,好比容器、虚拟机、物理机等。这些微服务在整个抖音春晚活动的不一样阶段,可能又须要使用不一样的流量治理策略来保证稳定性。缓存

所以基础架构就须要为这些来自不一样团队、用不一样语言编写的微服务提供统一的流量治理能力。安全

传统微服务架构的应对

说到微服务,咱们先来看一下传统的微服务架构是怎么解决这些问题的。随着企业组织的不断发展,产品的业务逻辑日渐复杂,为了提高产品的迭代效率,互联网软件的后端架构逐渐从单体的大服务演化成了分布式微服务。分布式架构相对于单体架构,其稳定性和可观测性要差一些。markdown

为了提高这些点,咱们就须要在微服务框架上实现不少功能。例如:网络

  • 微服务须要经过相互调用来完成原先单体大服务所实现的功能,这其中就涉及到相关的网络通讯,以及网络通讯带来的请求的序列化、响应的反序列化
  • 服务间的相互调用涉及服务发现
  • 分布式的架构可能须要不一样的流量治理策略来保证服务之间相互调用的稳定性。
  • 微服务架构下还须要提高可观测性能力,包括日志、监控、Tracing 等。

经过实现以上这些功能,微服务架构也能解决前面提到的一些问题。可是微服务自己又存在一些问题:架构

  • 在多语言的微服务框架上实现多种功能,涉及的开发和运维成本很是高
  • 微服务框架上一些新 Feature 的交付或者版本召回,须要业务研发同窗配合进行相关的改动和发布上线,会形成微服务框架的版本长期割裂不受控的现象。

那咱们怎么去解决这些问题呢?在软件工程的领域有这样一句话:任何问题均可以经过增长一个中间层去解决。而针对咱们前面的问题,业界已经给出了答案,这个中间层就是 Service Mesh(服务网格)。并发

自研 Service Mesh 实现

下面就给你们介绍一下火山引擎自研 Service Mesh 的实现。先看下面这张架构图。负载均衡

图中蓝色矩形的 Proxy 节点是 Service Mesh 的数据面,它是一个单独的进程,和运行着业务逻辑的 Service 进程部署在一样的运行环境(同一个容器或同一台机器)中。由这个 Proxy 进程来代理流经 Service 进程的全部流量,前面提到的须要在微服务框架上实现的服务发现、流量治理策略等功能就均可以由这个数据面进程完成。框架

图中的绿色矩形是 Service Mesh 的控制面。咱们须要执行的路由流量、治理策略是由这个控制面决定的。它是一个部署在远端的服务,由它和数据面进程下发一些流量治理的规则,而后由数据面进程去执行。

同时咱们也能够看到数据面和控制面是与业务无关的,其发布升级相对独立,不须要通知业务研发同窗。

基于这样的架构就能够解决前文提到的一些问题:

  • 咱们不须要把微服务框架众多的功能在每种语言上都实现一遍,只须要在 Service Mesh 的数据面进程中实现便可;
  • 同时由数据面进程屏蔽各类复杂的运行环境,Service 进程只须要和数据面进程通信便可;
  • 各类灵活多变的流量治理策略也均可以由 Service Mesh 的进程控制面服务进行定制。

Service Mesh 流量治理技术

接下来给你们介绍咱们的 Service Mesh 实现具体提供了哪些流量治理技术来保障微服务在面对抖音春晚活动的流量洪峰时可以有一个比较稳定的表现。

首先介绍一下流量治理的核心:

  • 路由:流量从一个微服务实体出发,可能须要进行一些服务发现或者经过一些规则流到下一个微服务。这个过程能够衍生出不少流量治理能力。
  • 安全:流量在不一样的微服务之间流转时,须要经过身份认证、受权、加密等方式来保障流量内容是安全、真实、可信的。
  • 控制:在面对不一样的场景时,用动态调整治理策略来保障微服务的稳定性。
  • 可观测性:这是比较重要的一点,咱们须要对流量的状态加以记录、追踪,并配合预警系统及时发现并解决问题。

以上的四个核心方面配合具体的流量治理策略,能够提高微服务的稳定性,保障流量内容的安全,提高业务同窗的研发效率,同时在面对黑天鹅事件的时候也能够提高总体的容灾能力。

下面咱们继续来看一下 Service Mesh 技术具体都提供了哪些流量治理策略来保障微服务的稳定性。

稳定性策略——熔断

首先是熔断。在微服务架构中,单点故障是一种常态。当出现单点故障的时候,如何保障总体的成功率是熔断须要解决的问题。

熔断能够从客户端的视角出发,记录从服务发出的流量请求到达下游中每个节点的成功率。当请求达到下游的成功率低于某一阈值,咱们就会对这个节点进行熔断处理,使得流量请求再也不打到故障节点上。

当故障节点恢复的时候,咱们也须要必定的策略去进行熔断后的恢复。好比能够尝试在一个时间周期内发送一些流量打到这个故障节点,若是该节点仍然不能提供服务,就继续熔断;若是可以提供服务了,就逐渐加大流量,直到恢复正常水平。经过熔断策略,能够容忍微服务架构中个别节点的不可用,并防止进一步恶化带来的雪崩效应。

稳定性策略——限流

另一个治理策略是限流。限流是基于这样的一个事实:Server 在过载状态下,其请求处理的成功率会下降。好比一个 Server 节点正常状况下可以处理 2000 QPS,在过载状况下(假设达到 3000 QPS),这个 Server 就只能处理 1000 QPS 甚至更低。限流能够主动 drop 一些流量,使得 Server 自己不会过载,防止雪崩效应。

稳定性策略——降级

当 Server 节点进一步过载,就须要使用降级策略。降级通常有两种场景:

  • 一种是按照比例丢弃流量。好比从 A 服务发出到 B 服务的流量,能够按照必定的比例(20% 甚至更高)丢弃。
  • 另一种是旁路依赖的降级。假设 A 服务须要依赖 B、C、D 3 个服务,D 是旁路,能够把旁路依赖 D 的流量掐掉,使得释放的资源能够用于核心路径的计算,防止进一步过载。

稳定性策略——动态过载保护

熔断、限流、降级都是针对错误发生时的治理策略,其实最好的策略是防患于未然,也就是接下来要介绍的动态过载保护。

前面提到了限流策略很难肯定阈值,通常是经过压测去观测一个节点可以承载的 QPS,可是这个上限量级可能会因为运行环境的不一样,在不一样节点上的表现也不一样。动态过载保护就是基于这样一个事实:资源规格相同的服务节点,处理能力不必定相同。

如何实现动态过载保护?它分为三个部分:过载检测,过载处理,过载恢复。其中最关键的是如何判断一个 Server 节点是否过载。

上图中的 Ingress Proxy 是 Service Mesh 的数据面进程,它会代理流量并发往 Server 进程。图中的 T3 能够理解为从 Proxy 进程收到请求到 Server 处理完请求后返回的时间。这个时间是否能够用来判断过载?答案是不能,由于 Server 有可能依赖于其余节点。有多是其余节点的处理时间变长了,致使 Server 的处理时间变长,这时 T3 并不能反映 Server 是处于过载的状态。

图中 T2 表明的是数据面进程把请求转发到 Server 后,Server 真正处理到它的时间间隔。T2 可否反映过载的状态?答案是能够的。为何能够?举一个例子,假设 Server 的运行环境是一个 4 核 8g 的实例,这就决定了该 Server 最多只能同时处理 4 个请求。若是把 100 个请求打到该 Server,剩余的 96 个请求就会处于 pending 的状态。当 pending 的时间过长,咱们就能够认为是过载了。

检测到 Server 过载以后应当如何进行处理?针对过载处理也有不少策略,咱们采用的策略是根据请求的优先级主动 drop 低优的请求,以此来缓解 Server 过载的状况。当 drop 了一些流量后 Server 恢复了正常水平,咱们就须要进行相应的过载恢复,使得 QPS 可以达到正常状态。

这个过程是如何体现动态性的?过载检测是一个实时的过程,它有必定的时间周期。在每个周期内,当检测到 Server 是过载的状态,就能够慢慢根据必定比例 drop 一些低优请求。在下一个时间周期,若是检测到 Server 已经恢复了,又会慢慢调小 drop 的比例,使 Server 逐渐恢复。

动态过载保护的效果是很是明显的:它能够保证服务在大流量高压的状况下不会崩溃,该策略也普遍地应用于抖音春晚红包项目中的一些大服务。

稳定性策略——负载均衡

接下来咱们看一下负载均衡策略。假设有一个服务 A 发出的流量要达到下游服务 B,A 和 B 都有一万个节点,咱们如何保障从 A 出发的流量达到 B 中都是均衡的?作法其实有不少,比较经常使用的是随机轮询、加权虚机、加权轮询,这些策略其实看名字就能知道是什么意思了。

另外一种比较常见的策略是一致性哈希。哈希是指根据请求的一些特征使得请求必定会路由到下游中的相同节点,将请求和节点创建起映射关系。一致性哈希策略主要应用于缓存敏感型服务,能够大大提高缓存的命中率,同时提高 Server 性能,下降超时的错误率。当服务中有一些新加入的节点,或者有一些节点不可用了,哈希的一致性能够尽量少地影响已经创建起的映射关系。

还有不少其余的负载均衡策略,在生产场景中的应用范围并非很普遍,这里再也不赘述。

稳定性策略——节点分片

面对抖音春晚红包这种超大流量规模的场景,还有一个比较有用的策略是节点分片。节点分片基于这样一个事实:节点多的微服务,其长链接的复用率是很是低的。由于微服务通常是经过 TCP 协议进行通讯,须要先创建起 TCP 链接,流量流转在 TCP 链接上。咱们会尽量地复用一个链接去发请求搜响应,以免因频繁地进行链接、关闭链接形成的额外开销。

当节点规模很是大的时候,好比说 Service A 和 Service B 都有 1 万个节点,它们就须要维持很是多的长链接。为避免维持这么多长链接,一般会设置一个 idle timeout 的时间,当一个链接在必定的间隔内没有流量通过的时候,这个链接就会被关掉。在服务节点规模很是大的场景下,长链接退化成的短链接,会使得每个请求都须要创建链接才能进行通信。它带来的影响是:

  • 链接超时带来的错误。
  • 性能会有所下降。

解决这个问题可使用节点分片的策略。实际上咱们在抖音春晚红包的场景中也是很是普遍地使用了这个策略。这个策略对节点数较多的服务进行节点分片,而后创建起一种映射关系,使得以下图中所示的 A 服务的分片 0 发出的流量必定能到达 service B 的分片 0。

这样就能够大大提高长链接的复用率。对于原先 1000010000 的对应关系,如今就变成了一个常态的关系,好比 100100。咱们经过节点分片的策略大大提高了长链接的复用率,下降了链接超时带来的错误,而且提高了微服务的性能。

效率策略

前面提到的限流、熔断、降级、动态过载保护、节点分片都是提高微服务稳定性相关的策略,还会有一些与效率相关的策略。

咱们先介绍一下泳道和染色分流的概念。

上图中所示的某个功能可能涉及到 a、b、c、d、e、f 六个微服务。泳道能够对这些流量进行隔离,每个泳道内完整地拥有这六个微服务,它们能够完整的完成一个功能。

染色分流是指根据某些规则使得流量打到不一样的泳道,而后借此来完成一些功能,这些功能主要包括:

  • Feature 调试:在线上的开发测试过程当中,能够把我的发出的一些请求打到本身设置的泳道并进行 Feature 调试。
  • 故障演练:在抖音春晚活动的一些服务开发完成以后,须要进行演练以对应对不一样的故障。这时咱们就能够把压测流量经过一些规则引流到故障演练的泳道上。
  • 流量录制回放:把某种规则下的流量录制下来,而后进行相关回放,主要用于 bug 调试或在某些黑产场景下发现问题。

安全策略

安全策略也是流量治理的重要环节。咱们主要提供三种安全策略:

  • 受权:受权是指限定某一个服务可以被哪些服务调用。
  • 鉴权:当一个服务接收到流量时,须要鉴定流量来源的真实性。
  • 双向加密(mTLS) :为了防止流量内容被窥探、篡改或被攻击,须要使用双向加密。

经过以上的这些策略,咱们提供了可靠的身份认证,安全地传输加密,还能够防止传输的流量内容被篡改或攻击。

春晚红包场景落地

经过前面提到的各类策略,咱们能够大大提高微服务的稳定性以及业务研发的效率。可是当咱们落地这一套架构的时候也会遇到一些挑战,最主要的挑战是性能问题。咱们知道,经过增长一个中间层,虽然提高了扩展性和灵活性,但同时也必然有一些额外的开销,这个开销就是性能。在没有 Service Mesh 时,微服务框架的主要开销来自于序列化与反序列化、网络通信、服务发现以及流量治理策略。使用了 Service Mesh 以后,会多出两种开销:

协议解析

对于数据面进程代理的流量,须要对流量的协议进行必定的解析才能知道它从哪来到哪去。可是协议解析自己的开销很是高,因此咱们经过增长一个 header (key 和 value 的集合) 能够把流量的来源等服务元信息放到这个 header 里,这样只须要解析一两百字节的内容就能够完成相关的路由。

进程间通信

数据面进程会代理业务进程的流量,一般是经过 iptables 的方式进行。这种方案的 overhead 很是高,因此咱们采用了进程间通信的方式,经过和微服务框架约定一个 unix domain socket 地址或者一个本地的端口,而后进行相关的流量劫持。虽然这种方式相对于 iptables 会有一些性能提高,它自己也存在的额外的一些开销。

咱们是如何下降进程间通信开销的呢?在传统的进程间通信里,好比像 unix domain socket 或者本地的端口,会涉及到传输的内容在用户态到内核态的拷贝。好比请求转发给数据面进程会涉及到请求在用户态和内核态之间拷贝,数据面进程读出来的时候又会涉及内核态到用户态的拷贝,那么一来一回就会涉及到多达 4 次的内存拷贝。

咱们的解决方案是经过共享内存来完成的。共享内存是 Linux 下最高性能的一种进程间通信方式,可是它没有相关的通知机制。当咱们把请求放到共享内存以后,另一个进程并不知道有请求放了进来。因此咱们须要引入一些事件通知的机制,让数据面进程知道。咱们经过 unix domain socket 完成了这样一个过程,它的效果是能够减小内存的拷贝开销。同时咱们在共享内存中引用了一个队列,这个队列能够批量收割 IO,从而减小了系统的调用。它起到的效果也是很是明显的,在抖音春晚活动的一些风控场景下,性能能够提升 24%。

完成这些优化以后,要去落地的阻力就没那么大了。

总结

本次分享主要为你们介绍了 Service Mesh 技术可以提供哪些流量治理能力来保证微服务的稳定和安全。主要包括三个核心点:

  • 稳定:面对瞬时亿级 QPS 的流量洪峰, 经过 Service Mesh 提供的流量治理技术,保证微服务的稳定性。
  • 安全:经过 Service Mesh 提供的安全策略,保证服务之间的流量是安全可信的。
  • 高效:春晚活动涉及众多不一样编程语言编写的微服务,Service Mesh 自然为这些微服务提供了统一的流量治理能力,提高了开发人员的研发效率。

Q&A

Q:共享内存中的 IPC 通讯为何可以减小系统调用?

A:当客户端进程把一个请求放到共享内存中以后,咱们须要通知 Server 进程进行处理,会有一个唤醒的操做,每次唤醒意味着一个系统调用。当 Server 尚未被唤醒的时候,或者它正在处理请求时,下一个请求到来了,就不须要再执行相同的唤醒操做,这样就使得在请求密集型的场景下咱们不须要去频繁的唤醒,从而起到下降系统调用的效果。

Q:自研 Service Mesh 实现是纯自研仍是基于 Istio 等社区产品?若是是自研使用的是 Go 仍是 Java 语言?数据面用的是 Envoy 么?流量劫持用的 iptables 么?

A

  1. 数据面是基于 Envoy 进行二次开发的,语言使用 C++。
  2. 流量劫持用与微服务框架约定好的的 uds 或者本地端口,不用 iptables。
  3. Ingess Proxy 和业务进程部署在一样的运行环境里,发布升级不须要重启容器。
相关文章
相关标签/搜索