Oceanus:美團HTTP流量定製化路由的實踐

背景簡述

Oceanus是美團基礎架構部研發的統一HTTP服務治理框架,基於Nginx和ngx_lua擴展,主要提供服務註冊與發現、動態負載均衡、可視化管理、定製化路由、安全反扒、session ID複用、熔斷降級、一鍵截流和性能統計等功能。本文主要講述Oceanus如何經過策略抽象、查詢、渲染和分組動態更新,實現HTTP請求的定製化路由。 隨着公司業務的高速發展,路由場景也愈來愈複雜。好比:html

  • 團購秒殺要靈活控制壓測流量,實現線上服務單節點、各機房、各地域等多維度的壓測。
  • 外賣業務要作流量隔離,把北方地域的流量轉發到分組a,南方地域的流量轉發到分組b。
  • 酒旅業務要對App新版本進行灰度,讓千分之一的用戶試用新版本,其餘用戶訪問老版本。
  • QA部門要經過請求的自定義參數指定轉發分組,構建穩定且高可用的測試環境。

因爲公司早期的業務場景相對比較簡單,因此均經過Nginx if指令支持。好比某業務要把來源IP爲10.4.242.16的請求轉發到後端節點10.4.232.110,其它請求轉發到後端節點10.4.232.111和10.4.232.112,就能夠進行以下配置:nginx

upstream backend_aaa {
    server 10.4.232.110:8080 weight=10;
}
upstream backend_bbb {
    server 10.4.232.111:8080 weight=10;
    server 10.4.232.112:8080 weight=10;
}
location /abc {
    if($remote_ip = "10.4.242.16") {
        proxy_pass http://backend_aaa; #路由到backend_aaa集羣
    }
    proxy_pass http://backend_bbb; #路由到backend_bbb集羣
}
複製代碼

上述方式雖然不須要額外開發,性能方面也接近原生的Nginx框架,可是使用場景比較受限,由於if指令僅支持比較簡單的condition類型,官方描述以下:git

若是該業務要把IP段10.4.242.16/34的請求轉發到10.4.232.110時,if指令勉強還能夠支持。但對於上述的複雜業務場景,if指令均沒法支持。除此以外,這種方式還存在如下兩點不足:github

  • 規則調整不支持動態化:若是要把客戶端10.4.242.16調整爲10.4.242.17,須要對Nginx進行reload,而reload操做會使Nginx的併發能力降低,業務高峯時甚至會致使請求504或502。
  • 指令坑太多:if指令和set、rewrite指令等一塊兒使用時,不少時候會出現不符合預期的行爲,嚴重時甚至會致使段錯誤,最好的方法就是避免使用。

爲了解決上述問題,Oceanus開始探索如何實現HTTP流量的定製化路由。算法

業界調研

經過初步調研,發現業界有一套開源的ABTestingGateway(如下簡稱AB)框架:數據庫

由上圖所示,AB框架使用Redis存儲策略數據,key是Host字段,value是策略對象,包括策略類型、匹配區間和要分發的Upstream。策略的增刪改查能夠經過基於Nginx搭建的Web服務的API實現,運行時根據請求的Host字段從lua-shared-dict或Redis獲取關聯的策略,根據策略類型(iprange/uidrange/uidsuffix/uidappoint)選擇對應的Lua腳本從請求中獲取相關參數(IP、UID)查詢是否匹配策略,若匹配,就修改請求的Upstream上下文完成分流的目的。 相比if指令的方式,AB框架有下面兩個優勢:

  • 策略調整動態生效:已有策略類型中的策略變動都可以經過HTTP API進行動態管理。
  • 分流策略豐富:支持IP段、UID段等策略,也能夠經過新增策略類型對策略庫進行擴展。

因爲AB框架只支持4種策略類型,對於業務要根據請求Cookie、自定義header控制轉發的狀況,均須要開發新的策略類型和發佈上線。另外,策略類型和業務場景緊密相關,致使AB系統的擴展性極差,很難快速支持新業務的路由需求。後端

不管是Nginx if指令,仍是AB框架,要麼須要reload從新加載才能生效,要麼沒法支持某些業務場景下的分流需求,因此都很難做爲解決公司級分流框架的有效手段。針對它們所存在的不足,Oceanus開發了一套應用級、高可擴展的動態分流框架,不只動態支持各類業務場景的分流需求,並且保證了請求轉發的性能,下文將闡述咱們如何解決分流機制的幾個核心問題。api

Oceanus定製化路由的核心設計&實現

關於分流機制,咱們主要從如下四個方面來說述:數組

  • 策略抽象:合理定義策略結構,適用儘量多的業務場景。
  • 策略的高效查詢:接口粒度關聯,應用維度管理。
  • 運行時策略渲染:渲染策略模板,判斷是否匹配策略,實現動態路由。
  • 分組動態更新:分組數據增刪改,均不須要reload。

策略的結構定義

以AB框架爲例,只支持iprange、uidrange、uidsuffix、uidappoint四種場景,對策略類型和匹配方式太具體化,致使沒法支持更多普適性的業務場景。從分流的本質出發,即根據請求特徵完成流量的定製化路由。結合Nginx if指令的幾個組成部分:條件判斷依賴的變量、條件判斷要匹配的value、條件表達式、匹配後要執行的proxy_pass,一個策略必需要包含請求特徵描述、定製化路由描述以及二者的關係描述。其中請求特徵描述包含特徵關鍵字、關鍵字的上下文傳輸方式,定製化路由描述經過Upstream表示,Upstream能夠預先設置,也能夠動態指定,二者的關係經過泛型表達式表示。那麼一個策略就須要包含下面幾個屬性:緩存

  • name:策略名,沒有實際意義,能夠根據業務場景進行定義。
  • key:分流時依賴的關鍵字,好比要根據城市地域進行分發路由時,key就是regionid。
  • passway:關鍵字在HTTP協議中的傳輸方式,能夠是Parameter、Cookie、header、body中的一種。
  • condition:表達式模板,支持四則運算/取模、關係運算符、邏輯運算符等。
  • group:後端服務集羣,即匹配策略後,轉發請求的目標節點,通常是策略所屬應用集羣中的部分節點。
  • category:策略類型,若是爲1,表示某個服務的私有策略;若是爲2,表示公共策略,主要用於策略數據管理。
  • switch:策略開關,用於控制當前策略是在線仍是離線。
  • graylist:灰度列表,用於策略變動的線上灰度校驗。

其中switch、graylist字段主要用於策略的上下線操做,這裏不作過多討論。下面重點介紹上面的策略定義是如何表述業務場景的:

備註:應用apk1和apk2分別配置2個私有策略,apk3使用公共策略。 如上圖所示,不管業務根據請求的哪些特徵進行分流,策略結構都可以支持。 以私有策略gray-deploy爲例,在Oceanus管理平臺進行添加,以下圖所示:

備註:這裏省略了策略的非核心字段好比switch、graylist等。

如何實現策略的高效查詢?

策略拓撲關係

分流策略分爲私有策略和公共策略。私有策略是面向服務的,並且和該服務建立的分組緊密相關。不一樣服務的私有策略徹底獨立,能夠相同,也能夠不一樣。一個服務能夠配置多個私有策略,也能夠關聯多個Host的Location,Location之間的策略使用徹底獨立,一個Location能夠啓用該服務的一個或者多個私有策略。若是經過Host+location_path直接關聯策略數據,不一樣Location關聯同一個私有策略時,會存在大量的數據冗餘。因此咱們經過服務標識(appkey,惟一標識一個應用服務)關聯具體的策略數據,Host+location_path只關聯當前Location使用的策略名列表,策略之間支持指定順序。 公共策略與具體服務無關,策略名全局惟一,可使用策略名關聯策略數據便可。綜上,策略的拓撲關係描述以下:

如上圖所示,以應用apk1爲例,關聯了兩個Location接口,分別爲/api和/list,總共部署了8個節點,建立了2個分組ups-cq和ups-gray,其中節點10.5.23.6和10.5.24.72屬於分組ups-cq,節點10.7.46.32和10.7.72.232屬於分組ups-gray。應用配置了兩個私有策略stress-testing和gray-deploy,其中策略stress-testing被接口/api啓用,匹配策略的流量路由到分組ups-cq,策略gray-deploy被接口/list啓用,匹配策略的流量路由到ups-gray。

運行時獲取Location path

Nginx在解析Location配置時,經過不一樣的字段區分不一樣類型的Location,沒有記錄配置中的Location path。若是要運行時獲取,通常有兩種方式:一種是根據相關字段逆向還原path,另外一種是爲框架新增變量。因爲Nginx在處理正則Location時,對因而否忽略大小寫的狀況,並無作標記,即解析的過程是不可逆的,因此咱們選擇了第二種方式。在覈心模塊的變量數組ngx_http_core_variables中新增了內置變量,記錄下原始的Location path,變量屬性定義以下:

{ngx_string("loc_mod"), NULL, ngx_http_variable_loc_mod,
  0, NGX_HTTP_VAR_NOCACHEABLE, 0},
{ngx_string("loc_name"), NULL, ngx_http_variable_loc_name,
  0, NGX_HTTP_VAR_NOCACHEABLE, 0}
複製代碼

loc_mod和loc_name之間用一個空格符鏈接,格式和Oceanus管理平臺保持一致。

異步更新機制

爲了保證運行時獲取策略數據的高效性,咱們經過異步定時拉取,把策略數據全量同步到本地的共享內存中。基於穩定性和靈活性的考慮,咱們採用了關係型數據庫MySQL存儲策略。 更新機制以下圖所示:

  1. Oceanus在init_worker階段隨機選擇某個worker進程,嵌入timer。
  2. 被選中的worker會異步非阻塞地從MySQL定時拉取策略數據。
  3. timer worker把拉取到的策略數據解析,按照策略的拓撲關係,更新到當前共享內存中的寫緩存區,完成更新後,切換讀寫緩存區,保證最新的策略當即生效。
  4. worker進程在處理請求時,從當前共享內存中的讀緩存區獲取策略數據。

爲了解決timer worker和其它worker在讀寫策略數據時的競態關係,咱們採用了雙buffer機制,實現了業務層策略數據的無鎖讀寫。另外,經過設置timer的時間爲0,保證在全部worker處理請求前,策略數據已經在共享內存中完成初始化。

策略查詢機制

查詢算法以下圖所示:

  1. worker進程從request上下文中獲取請求的Host,以及所匹配Location的location_path。
  2. 根據Host+location_path,到共享內存中查詢所開啓的策略名。
  3. 若是是公共策略,直接根據策略名去查詢策略數據。
  4. 若是是私有策略,從request上下文獲取Location關聯的Upstream,即應用標識appkey,到共享內存讀緩存區獲取具體的策略數據。

備註:公共策略以"oceanus"開頭,區別於私有策略的命名。

運行時策略渲染

查詢到請求開啓的策略後,Oceanus須要運行時判斷是否匹配,以私有策略爲例,執行流以下圖所示:

  1. 在rewrite phase,Oceanus經過rewrite_by_lua_file嵌入回調,觸發請求處理,進入分流框架的主流程。
  2. 經過上面的策略查詢機制獲取請求的策略,進行解析,獲取策略的key和passway。
  3. 根據passway從請求對應的上下文獲取key的value。
  4. 用3獲取到的value渲染策略的condition,把condition中的佔位符替換爲value。
  5. 基於Lua VM,經過load計算condition的結果,即true或false。
  6. 從策略中獲取condition的value和group數據。
  7. 若是condition爲true,就用group覆蓋請求的Upstream上下文,不然,不作處理。

分組動態更新

分組列表的動態化是分流框架的重要一環。更新機制以下圖所示:

  1. 分組數據使用ZooKeeper存儲,變動經過watcher機制實現增量同步。
  2. Oceanus也會定時拉取,進行全量同步。
  3. Oceanus把全部變動都經過本地的HTTP調用同步到Nginx內存。
  4. worker處理變動請求前,會先搶鎖,讀取共享內存中的消息隊列,同步其它worker進行的歷史變動。
  5. 把此次變動同步到當前worker的Upstream main上下文中,完成當前worker的更新。
  6. 把變動封裝成消息,加入到共享內存中的隊列。
  7. 其它worker經過timer或者本身處理變動消息前讀取消息隊列,完成更新。

總結與展望

經過Oceanus分流機制在美團外賣、酒旅、到店餐飲等多個業務線的普遍使用,基礎架構部幫助業務同胞解決了多個定製化路由的需求,好比服務set化、鏈路壓測、灰度發佈、泳道環境建設等等。目前,Oceanus分流機制只關注了流量轉發方向,還不支持更復雜的轉發動做,好比根據策略調整請求的Parameter、header、Cookie,也不支持根據請求的URL實現動態路由等,將來咱們還將逐一完善這些問題,固然也歡迎你們跟咱們一塊兒交流,共同進步。

做者簡介

周峯,美團高級工程師,2015年7月加入美團基礎架構部,前後負責統一密鑰管理服務、智能反爬服務和HTTP負載均衡,目前主要負責HTTP服務治理Oceanus的相關工做,致力於探索和研究服務的自動化、智能化、和高性能等方向。

招聘廣告:若是你對大規模分佈式環境下的HTTP服務治理、分佈式會話鏈路追蹤等系統感興趣,誠摯歡迎投遞簡歷至:zhangzhitong#meituan.com

參考文獻

  1. ngx_http_rewrite_module:nginx.org/en/docs/htt…
  2. AB框架:github.com/CNSRE/ABTes…

相關文章
相關標籤/搜索