vlambda博客
学习文章列表

Maglev -- 谷歌的负载均衡器(一)

Maglev是谷歌的软件负载均衡器(Software Load Balancer)。


Maglev从2008年以来就作为谷歌各项服务的负载均衡器部署在生产环境中,那些拥有全球级流量的服务(Search,YouTube,Gmail)的背后,都是由Maglev负责把全球数十亿用户每天发出的每一次http request转发到实际的服务后端上。


负载均衡器

负载均衡就是把任务能够尽可能地平均分配到每一个操作单元的这么一个过程。

打个比方就是:现在有多个技能一样出色的工人(server),然后把工作(workload)尽可能地平均分配到他们每个人的身上,保证整个项目的工作效率最高的这么一个过程就叫负载均衡。

那负载均衡器顾名思义就是完成负载均衡这个功能的机器。

负载均衡器分为硬件负载均衡器(以F5为代表)和软件负载均衡器(LVS, HA Proxy, Nginx, AVI LB, Maglev)。

硬件负载均衡是指用专门的硬件设备来完成负载均衡这样的一种技术,它的优点是性能非常强悍,吞吐能力强,而且很容易扩充(无脑堆叠硬件即可)。但是它的缺点也非常明显,首先是太贵了,具体价格可参见下图。其次是这样负载均衡对于用户来说就是完全黑盒的一个技术,如果出现了问题就需要提供负载均衡的厂家来维修。

硬件负载均衡器价格

软件负载均衡是指使用软件编写来实现负载均衡的技术,它的优点是相对于硬件负载均衡价格显著降低,而且可以针对自己的应用场景更灵活地调配负载均衡的资源和策略,还有就是整个系统完全在自己的掌控之内,不需要依赖于第三方来支持。但是,同样的,软件负载均衡也有自己的缺点,首先就是软件负载均衡的性能很难超越硬件,其次就是当需要扩容的时候,软件负载均衡不能够无脑堆叠,而需要自己实现scale策略才能保证负载均衡的性能能够得到提升。

总而言之,硬件策略的性能强悍而不需要操心,软件策略更加便宜而且灵活可控。


动机——为什么设计Maglev?

市面上已经有各种各样成熟的硬件和软件负载均衡器,那么为什么谷歌还要自己设计maglev作为自己的负载均衡设备呢?

简单来说,因为无论是软件还是硬件负载均衡器,都无法满足谷歌的需求。

如果采用硬件负载均衡器,面对谷歌搜索、YouTube和Gmail邮箱这样庞大的流量,需要的价格已经高到连谷歌都无法承受得住。而如果采用软件负载均衡器,它们的性能却又无法很好地支撑起上面所说的这样庞大的流量,于是就催化了谷歌的“磁悬浮” maglev load balancer的诞生。


Maglev Structure

Maglev -- 谷歌的负载均衡器(一)

一个Maglev的架构如上图所示,它由Controller和Forwarder两部分组成。Controller和Forwarder都由Config Objects负责提供具体的配置,尤其是VIP(virtual IP)的配置信息。

Controller 主要负责对Forwarder进行health check,如果Forwarder依然很健康就会通过BGP协议对外广播自己拥有的VIP,否则就对外广播撤回自己拥有的VIP。这样就保证了所有通过路由器流进来的流量都会被流入一个处于健康状态的maglev上,防止maglev宕机导致服务不可用。

Forwarder则负责具体地把发送到maglev上的所有网络包均匀地发送到对应的后端服务上。它具体的结构如下图:

当packets从网卡(NIC)流入Maglev时:

  1. Forwarder会直接从网卡处拿到数据包而不经过内核处理。(kernel bypassing)

  2. 然后进入steering阶段,Forwarder会通过一个哈希函数(hash function)使用网络包的7元组来计算这个包的一个哈希值并根据这个哈希值把它发送到一个专门处理包的队列(Queue)里面去。

  3. 每一个队列会绑定一个专门的线程,这样可以尽可能地减少Context-Switch浪费的时间。

  4. 在队列里时,Forwarder首先会看一下这个VIP在不在当前这个Maglev声明的VIP里面,如果不在的话就直接把这个网络包drop掉。

  5. 然后Forwarder会重新计算这个网络包的哈希值,再去connection tracking table中找相对应的哈希值。

    1. 如果找到,立刻使用这个已经找到的后端。

    2. 如果没找到就调用一致性哈希算法来给这个包找到一个后端,并且把这次的结果缓存在connection tracking table中。

  6. 然后进入多路复用(Muxing)模块,它会轮询所有的队列,并且把所用封装好的包裹传回网卡(NIC)。

为了能够高效地实现和网卡的交互,Google的程序员们设计了一个网络包池(Packet Pool)和环形链表(Ring LinkedList)的数据结构,如下图所示:

Maglev和网卡(NIC)共享这样的一个资源池(Packet Pool),并且通过链表指针(Pointer)操纵池里的包,这样保证了整个过程中不需要进行任何多于的复制(Copy)过程,极致地提升了效率。

在这个链表结构中,根据steering和muxing两个阶段,每个阶段各有三个指针,它们分别代表:

Steering:

  1. Received: NIC接受到的包裹

  2. Processed:已经分发给线程的包裹

  3. Reserved:还没有被使用的空间

Muxing:

  1. Sent:NIC已经发送的包裹

  2. Ready: 已经处理好等到发送的包裹

  3. Recycled:已经发送包裹的空间

这样,三个指针互相追逐(Chasing),环形前进,完美地和网卡协作在一起。


Maglev Advantages

Maglev作为一款软件负载均衡器,却能够达到媲美硬件负载均衡的效率,并且还兼具软件负载均衡器的便宜,灵活部署的特点,主要得益于:

  • 使用ECMP协议将路由器中的流量均匀地分发到每一个Maglev。

  • 完全绕开Linux内核的设计,maglev会从网卡处“截流”所有的网络包,而不需要经过Linux内核,从而避免了从内核态switch到用户态处理完成后再switch回到内核态这样一种资源和时间上的浪费,极大地提升了软件负载均衡的效率。

  • Maglev hashing,在一致性哈希算法的基础上,通过牺牲一点点resilience来更加均衡地选择后端。(下一章讲到)

  • 精妙的数据结构。通过packet pool和ring linkedlist完全避免了任何in-memory copy,将效率提升做到极致。


本文介绍了Maglev内部的具体组成部分,那么它到底是怎么实现能够把网络流量平均地分配到每一个后端服务上的呢?下周的“Maglev Hashing”将解答这个问题。


Reference:

Maglev: A Fast and Reliable Software Network Load Balancer:

https://storage.googleapis.com/pub-tools-public-publication-data/pdf/44824.pdf