1. 程式人生 > >給高併發降溫,美團高效能、高可靠四層負載均衡MGW優化實踐

給高併發降溫,美團高效能、高可靠四層負載均衡MGW優化實踐

負載均衡的作用及分類

網際網路初期階段,業務邏輯簡單、流量不大,單臺伺服器就可滿足日常需求。隨著網際網路的發展,業務不僅會流量爆發、邏輯越來越複雜且對可靠性的需求也逐步遞增。

這時,就需要多臺伺服器來應對單臺伺服器在效能、單點等方面凸顯出來的問題,進行效能的水平擴充套件和災備。但客戶端的流量要如何順利訪問到這麼多不同的伺服器是個問題。

對於這個問題,可選擇使用 DNS 做負載,對客戶端不同 IP 地址進行解析,使得流量直接到達不同的應用伺服器。

但這個排程策略相對簡單卻不能很好的滿足業務需求,當改變排程策劃後,DNS 各級節點的快取不能及時在客戶端生效,會導致很嚴重的延時性問題。這時可選擇使用負載均衡,如下圖:

DNS

如圖所示,客戶端的流量先到達負載均衡伺服器,再由負載均衡伺服器採用一些排程演算法,把流量分發到不同的應用伺服器。

與此同時,負載均衡伺服器對應用伺服器進行週期性健康檢查,一旦查出故障節點,直接剔除,進而保證應用的高可用。

負載均衡分為四層和七層兩種,如下圖:

負載均衡

四層負載均衡

核心是轉發,收到客戶端流量,對資料包地址資訊進行修改後,轉發到應用伺服器,整個過程均在 OSI 模型的傳輸層完成。

七層複雜均衡

核心是代理,當接收到客戶端的流量後,通過完整的 TCP/IP 協議棧對應用層流量進行解析。之後,基於排程演算法選擇合適的應用伺服器,並與應用伺服器建立連線將請求傳送,整個過程在 OSI 模型的應用層完成。

四層負載均衡的轉發模式

轉發是四層負載均衡的主要工作,目前主要有四種轉發模式:

  • DR 模式。
  • NAT 模式。
  • TUNNEL 模式。
  • FULLNAT 模式。

如下是四種轉發模式的優缺點對比圖表:

從圖中可以直觀的看到四種轉發模式的優缺點:

  • DR 模式(三角傳輸)的實現方式是修改資料包的目的 MAC 地址。
  • NAT 模式的實現方式是修改資料包的目的 IP 地址。
  • TUNNEL 模式的實現方式和 DR 相同,但要求應用伺服器必須支援 TUNNEL 功能。

美團點評最終選擇 FULLNAT 作為 MGW 的轉發模式,它是在 NAT 模式的基礎上做一次源地址轉換(即 SNAT)。

此模式的優勢在於可使應答流量經過正常三層路由迴歸負載均衡,負載均衡就不需以網管的形式存在於網路中,進而降低了對網路環境的要求。不足之處在於做 SNAT,應用伺服器會出現丟失客戶端的真實 IP 地址的情況。

如下圖,是 FULLNAT 轉發模式的具體實現流程:

FULLNAT

如圖在負載均衡上佈設 localip 池,做 SNAT 的過程中,源 IP 取自其中。當客戶端流量到達負載均衡裝置,負載均衡依據排程策略在應用伺服器池中選擇一個應用伺服器,將資料包的目的 IP 改為應用伺服器 IP。

同時從 localip 池中選擇一個 localip 將資料包的源 IP 改為 localip,這樣應用伺服器在應答時,目的 IP 是 localip,而 localip 是真實存在於負載均衡上的 IP 地址,因此可以經過正常的三層路由到達負載均衡。

與 NAT 模式相比,FULLNAT 模式會多做一次 SNAT,過程中還有選埠的操作,在效能方面要稍弱於 NAT 模式,但 FULLNAT 模式在網路適應性方面要佔優勢。

採用四層負載均衡的原因

為什麼美團點評會選擇自研的四層負載均衡呢?主要有兩個原因:

  • 業務流量遞增,LVS 出現效能瓶頸及運維成本的上升。
  • 採用硬體負載均衡,成本是門檻。

LVS 方面,美團點評最初的方式是使用 LVS 做四層負載均衡、Nginx 做七層負載均衡。

Nginx

如上圖所示,流量高速增長,LVS 在效能上出現瓶頸,故障率增加。

在硬體負載均衡方面,凸顯出來的問題有三方面:

  • 異常高的硬體成本
  • 人力成本。
  • 時間成本。

綜合這兩個原因,美團點評決定自研四層負載均衡 MGW 來替換 LVS,MGW 需要滿足高效能、高可靠兩個特性。那麼如何實現高效能、高可靠呢?

MGW 實現高效能的四大問題和解決方法

對比 LVS 的一些效能瓶頸,我們可以直觀瞭解 MGW 實現高效能的解決方法。這裡主要涉及 LVS 遇到的中斷、過長的協議棧路徑、鎖和上下文切換這四大問題。

MGW 應對這些問題的解決辦法分別是 PMD 驅動、Kernelbypass、無鎖的設計和 CPU 繫結、隔離。

1.中斷

LVS 是做在核心中的,而核心在設計時要兼顧通用性,所以採用中斷的方式感知流量的接收。

中斷對 LVS 效能的影響非常大,如果每秒處理 500 萬資料包,每 5 個數據包產生一個硬體中斷,那麼一秒會產生 100 萬個硬體中斷,每一次產生的硬體中斷都會打斷正在進行密集計算的負載均衡程式,中間產生大量的 cache miss,這對效能的影響非常大。

2.過長協議棧路徑

因為 LVS 是基於核心 netfilter 開發的應用程式,而 netfilter 是執行在核心協議棧的一個鉤子框架。

當資料包到達 LVS 時,要經過一段很長的協議棧處理。實際情況是負載均衡在接到流量以後,只需對 MAC、IP、埠進行修改發出即可,並不需要這段處理來增加額外的消耗。

 LVS

針對中斷和過長協議棧路徑這兩個問題的解決方法是採用由 DPDK 提供的使用者態 PMD 驅動以及做 Kernelbypass。

DPDK 在設計過程中,使用了大量硬體特性,如 numa、 memory channel、 DDIO 等對效能提升很大。同時提供網路方面的庫,進而減小開發難度,提升開發效率。

這些都是美團點評選擇 DPDK 作為 MGW 的開發框架的因素。

3.鎖

核心是較通用的應用程式,為了兼顧不同硬體,在設計過程中會加一些鎖。而自主研發只需對某些特定場景進行定製設計,不用兼顧很多硬體,進而通過一些方法去掉鎖,實現完全無鎖的狀態,方便後續擴充套件。

先來看看什麼情況需要進行鎖的設計,首先介紹一下 RSS(Receive Side Scaling)。

RSS 是一個通過資料包的元組資訊將資料包雜湊到不同網絡卡佇列的功能,這時不同 CPU 再去對應的網絡卡佇列讀取資料進行處理,就可充分利用 CPU 資源。

MGW 使用的是 FULLNAT 模式,會將資料包的元組資訊全部改變。

這樣同一個連線,請求和應答方向的資料包有可能會被 RSS 雜湊到不同的網絡卡佇列中,在不同的網絡卡佇列也就意味著在被不同的 CPU 進行處理,這時在訪問 session 結構就需要對這個結構進行加鎖保護。

解決這個問題,一般情況下有如下兩種方案:

  • 在做 SNAT 選埠時,通過選擇一個埠 lport0 讓 RSS(cip0,cport0,vip0,vport0) = RSS(dip0,dport0,lip0,lport0)相等。
  • 為每個 CPU 分配一個 localip,做 SNAT 選 IP 時,不同 CPU 選擇自有的 localip,當應答回來後,再通過 lip 和 CPU 的對映關係,將指定目的 IP 的資料包送到指定佇列上。

由於第二種方法恰好可被網絡卡的 flow director 特性支援,因此 MCW 選擇第二種方法來去掉 session 結構的鎖。

CPU

flow director 可依據一定策略將指定的資料包送到指定網絡卡佇列,其在網絡卡中的優先順序要比 RSS 高,因此在做初始化的時候就為每個 CPU 分配一個 localip。

如為 cpu0 分配 lip0,為 cpu1 分配 lip1,為 cpu2 分配 lip2,為 cpu3 分配 lip3。

當一個請求包(cip0,cport0,vip0,vport0)到達負載均衡後,被 RSS 雜湊到了佇列 0 上,這時這個包被 cpu0 處理。

cpu0 在對其做 fullnat 時,選擇 cpu0 自己的 localiplip0,然後將資料包(lip0,lport0,dip0,dport0)發到應用伺服器,在應用伺服器應答後,應答資料包(dip0,dport0,lip0,lport0)被髮到負載均衡伺服器。

這樣就可以在 flow director 下一條將目的 IP 為 lip0 的資料包傳送到佇列 0 的規則,這樣應答資料包就會被送到佇列 0 讓 cpu0 處理。

這時 CPU 在對同一個連線兩個方向的資料包進行處理的時候就是完全序列的一個操作,也就不需再對 session 結構進行加鎖保護。

4.上下文切換

針對上下文切換的問題,MGW 設計有對 CPU 進行分組,實現控制平面與資料平面完全分離的狀態,可以很好的避免資料平面做處理時被打斷的現象,如下圖所示:

MGW

同時,對資料平面 CPU 進行隔離,實現控制平面程序不會排程資料平面這組 CPU;對資料平面執行緒進行 CPU 繫結,實現每個資料執行緒獨佔一個 CPU。

控制平面的程式均跑在控制平面的 CPU 上,如 Linux kernel、SSH 等。

MGW實現高可靠的解決方法

1.MGW 叢集

叢集的高可靠主要涉及三部分:

  • session 同步。
  • 故障切換。
  • 故障恢復與擴容。

如下圖所示,MGW 採用 ECMP+OSPF 的模式組成叢集:

 MGW 叢集

ECMP 的作用是將資料包雜湊到叢集中各個節點,再通過 OSPF 保證單臺機器故障以後將這臺機器的路由動態的剔除出去,這樣 ECMP 就不會再給這臺機器分發流量,也就做到了動態的 failover。

session同步

傳統 ECMP 在演算法方面存在一個嚴重問題,當複雜均衡叢集中節點數量發生變化時,會導致大部分流量的路徑發生改變。

當發生改變的流量到達其他 MGW 節點上,找不到自身的 session 結構,就會導致大量的連接出現異常,對業務影響很大,且當在對叢集做升級操作時會將每個節點都進行一次下線操作,這樣就加重了這個問題的影響。

傳統的解決方式是使用支援一致性 hash 的交換機,如下圖所示:

交換機

當節點發生變化的時候,只對發生變化的節點上面的連線會有影響,其他連線都會保持正常,但是支援這種演算法的交換機比較少,並且也沒有完全實現高可用。

因此需要做叢集間的 session 同步功能,如下圖所示:

session

叢集中每個節點都會全量的將自身 session 進行同步,使叢集中每個節點都維護一份全域性 session 表。當節點發生變化,流量路徑無論發生任何形式的改變,流量都可以找到自身的 session 結構。

故障切換與故障恢復及擴容是在做叢集間的 session 同步功能的整個過程中首要考慮的兩大問題。

故障切換

針對故障切換問題,當機器發生故障後,交換機可立刻將流量切到其他機器,避免大量丟包的情況。

經過調研測試,MGW 採用如下圖所示的操作方法,可做到升級操作 0 丟包,主程式故障 0 丟包,其他異常(網線等)會有一個最長 500ms 的丟包,因為這種異常需要靠自檢程式去檢測,而自檢程式的週期是 500ms。

具體包括如下四方面內容:

  • 交換機側不使用虛擬介面。全部使用物理介面且伺服器側對介面進行斷電時,交換機會瞬間將流量切換到其他機器。

    通過 100ms 發兩個包的測試(客戶端和服務端各發一個),表明這種操作方法可實現 0 丟包。

  • 半秒一次機器健康自檢。故障切換主要依賴於交換機的感知,當伺服器上出現異常,交換機感知不到時,交換機就無法進行故障切換操作。

    因此需要一個健康自檢程式,每半秒進行一次健康自檢,當發現伺服器存在異常時就對伺服器執行網口斷電操作,從而將流量立刻切走。

  • 檢測到故障自動給網口斷電。故障切換主要依賴於網口斷電操作且網絡卡驅動是跑在主程式裡面的,當主程式掛掉以後,就無法再對網口執行斷電操作。
  • 主程序會捕獲異常訊號。針對主程式掛掉後,就無法再對網口執行斷電操作的現象,主程序會捕獲異常訊號,當發現異常時就對網絡卡進行斷電操作,在斷電操作結束後再繼續將訊號發給系統進行處理。

故障恢復與擴容

系統進行故障恢復、擴容操作時,會使得叢集節點數量發生變化,進一步導致流量路徑發生變化。

因為已經發生變化的流量到達叢集中原有節點時,原有節點都維護一個全域性 session 表,所以這些流量是可以被正常轉發的;但當流量到達一個新機器,這臺新機器由於沒有全域性 session 表,這部分流量會全部被丟棄。

針對這個問題,MGW 在上線後會經歷預上線的中間狀態,此狀態下,MGW 不會讓交換機感知到自己上線,所以交換機也就不會把流量切過來。

實現流程是首先 MGW 會對叢集中其他節點發送批量同步的請求,其他節點收到請求以後會將自己的 session 全量同步到新上線的節點,新上線節點在收到全部 session 以後才會讓交換機感知到自己上線,這時交換機再將流量切入,就可正常被轉發。

這裡需要提醒兩點:

  • 由於叢集中並沒有一個主控節點來維護一個全域性的狀態,如果 request 報資料丟失或者 session 同步的資料丟失的話,那麼新上線節點就沒辦法維護一個全域性的 session 狀態。

    但考慮到所有節點都維護著一個全域性的 session 表,因此所有節點擁有的 session 數量都是相同的,那麼就可以在所有節點每次做完批量同步以後傳送一個 finish 訊息,finish 訊息中帶著自己擁有的 session 數量。

    當新上線節點收到 finish 訊息以後,便會以自己的 session 數量與 finish 中的數量做對比。當達到數量要求以後,新上線節點就控制自己進行上線操作。否則在等待一定的超時時間以後,重新進行一次批量同步操作,直到達到要求為止。

  • 在進行批量同步操作時,如果出現新建連線,那麼新建連線就不會通過批量同步同步到新上線的機器上。如果新建連線特別多,就會導致新上線機器一直達不到要求。

    因此,需要保證處於預上線狀態的機器能接收到增量同步資料,因為新建連線可以通過增量同步同步出來。通過增量同步和批量同步就可以保證新上線機器可以最終獲得一個全域性的 session 表。

2.MGW單機

自動化測試平臺

單機高可靠方面,MGW 研發了自動化測試平臺,通過連通性和配置的正確性來判斷一個測試用例是否執行成功,失敗的測試用例平臺可以通過郵件通知測試人員。

在每次新功能迭代結束以後,都會將新功能的測試用例加到自動化平臺裡面,這樣在每次上線之前都進行一次自動化測試,可以大大避免改動引發的問題。

自動化測試

在這之前,每次上線之前都需要進行一次手動的迴歸測試,迴歸測試非常耗時並且很容易遺漏用例,但是為了避免改動引發新問題又不得不做,有了自動化測試平臺以後,可提升迴歸測試的效率和可靠性。

3.應用伺服器

在應用伺服器靠性方面,MGW 佈設節點平滑下線和一致性源 IP Hash 排程器兩大功能。

節點平滑下線

伺服器

節點平滑下線功能主要是為了解決當用戶需要對 RS 進行升級操作時,如果直接將需要升級的 RS 下線,那這個 RS 上存在的所有連線都會失敗,影響到業務。

此時如果呼叫 MGW 的平滑下線功能,MGW 就可以保證此 RS 已有連線正常工作,但不會往上面排程新的連線。

當所有已有連線結束以後,MGW 會上報一個結束的狀態,使用者就可以根據這個結束的狀態對 RS 進行升級操作,升級後再呼叫上線介面讓這個 RS 進行正常的服務。

如果使用者平臺支援自動化應用部署,那就可以通過接入雲平臺使用平滑下線功能,實現完全自動化且對業務無影響的升級操作。

一致性源 IP Hash 排程器

 IP Hash 排程器

源 IP Hash 排程器主要是保證相同的客戶端的連線被排程到相同應用伺服器上,也就是說建立一個客戶端與應用伺服器一對一的對映關係。

普通源 IP Hash 排程器在應用伺服器發生變化以後會導致對映關係發生改變,會對業務造成影響。

一致性源 IP Hash 排程器,保證在應用伺服器叢集發生變化時,只有發生變化的應用伺服器與客戶端的對映關係發生改變,其他保持不變。

為了保證流量的均衡,首先在 Hash 環上分配固定數量的虛擬節點,然後將虛擬機器節點均衡的重分佈到物理節點上,重分佈演算法需要保證:

  • 在物理節點發生變化時,只有少數虛擬節點對映關係發生變化,也就是要保證一致性 Hash 的基本原則。
  • 因為 MGW 是以叢集的形式存在的,當多個應用伺服器發生上線、下線操作時,反饋到不同的 MGW 節點上就有可能會出現順序不一致的問題。

    因此無論不同的 MGW 節點產生何種應用伺服器上下線順序,都需要保證最終的對映關係一致,因為如果不一致就導致相同客戶端的連線會被不同的 MGW 節點排程到不同的應用伺服器上,也就違背源 IP Hash 排程器的原則。

技術展望

未來,美團點評將主要在這三方面發力:升級自動化、集中式配置管理和降低運維成本。

  • 升級自動化。最初自研 MGW 是為解決 LVS 的效能問題,在這些問題被解決之後,隨著業務的快速發展,IDC 不斷增長,負載叢集越來越多,像某個新 IDC 上線,一般都要上線兩套叢集,分別用於 IDC 入口和內部業務。

    這樣的情況下,又湧現出更大的問題,如一個新功能釋出時, 週期會非常長,所以需要實現自動化升級。當然,在完善監控措施方面也要同步,對異常進行監控。

  • 集中式配置管理。目前業務配置已經實現集中式配置,未來希望機器的配置也能實現。
  • 更低的運維成本。實現自動化升級,集中式配置最根本的目的就是降低運維成本。

原文來自微信公眾號:51CTO技術棧