Oceanus是美團基礎架構部研發的統一HTTP服務治理框架,基於Nginx和ngx_lua擴展,主要提供服務註冊與發現、動態負載均衡、可視化管理、定製化路由、安全反扒、session ID複用、熔斷降級、一鍵截流和性能統計等功能。本文主要講述Oceanus如何經過策略抽象、查詢、渲染和分組動態更新,實現HTTP請求的定製化路由。 隨着公司業務的高速發展,路由場景也愈來愈複雜。好比:html
因爲公司早期的業務場景相對比較簡單,因此均經過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
爲了解決上述問題,Oceanus開始探索如何實現HTTP流量的定製化路由。算法
經過初步調研,發現業界有一套開源的ABTestingGateway(如下簡稱AB)框架:數據庫
因爲AB框架只支持4種策略類型,對於業務要根據請求Cookie、自定義header控制轉發的狀況,均須要開發新的策略類型和發佈上線。另外,策略類型和業務場景緊密相關,致使AB系統的擴展性極差,很難快速支持新業務的路由需求。後端
不管是Nginx if指令,仍是AB框架,要麼須要reload從新加載才能生效,要麼沒法支持某些業務場景下的分流需求,因此都很難做爲解決公司級分流框架的有效手段。針對它們所存在的不足,Oceanus開發了一套應用級、高可擴展的動態分流框架,不只動態支持各類業務場景的分流需求,並且保證了請求轉發的性能,下文將闡述咱們如何解決分流機制的幾個核心問題。api
關於分流機制,咱們主要從如下四個方面來說述:數組
以AB框架爲例,只支持iprange、uidrange、uidsuffix、uidappoint四種場景,對策略類型和匹配方式太具體化,致使沒法支持更多普適性的業務場景。從分流的本質出發,即根據請求特徵完成流量的定製化路由。結合Nginx if指令的幾個組成部分:條件判斷依賴的變量、條件判斷要匹配的value、條件表達式、匹配後要執行的proxy_pass,一個策略必需要包含請求特徵描述、定製化路由描述以及二者的關係描述。其中請求特徵描述包含特徵關鍵字、關鍵字的上下文傳輸方式,定製化路由描述經過Upstream表示,Upstream能夠預先設置,也能夠動態指定,二者的關係經過泛型表達式表示。那麼一個策略就須要包含下面幾個屬性:緩存
其中switch、graylist字段主要用於策略的上下線操做,這裏不作過多討論。下面重點介紹上面的策略定義是如何表述業務場景的:
分流策略分爲私有策略和公共策略。私有策略是面向服務的,並且和該服務建立的分組緊密相關。不一樣服務的私有策略徹底獨立,能夠相同,也能夠不一樣。一個服務能夠配置多個私有策略,也能夠關聯多個Host的Location,Location之間的策略使用徹底獨立,一個Location能夠啓用該服務的一個或者多個私有策略。若是經過Host+location_path直接關聯策略數據,不一樣Location關聯同一個私有策略時,會存在大量的數據冗餘。因此咱們經過服務標識(appkey,惟一標識一個應用服務)關聯具體的策略數據,Host+location_path只關聯當前Location使用的策略名列表,策略之間支持指定順序。 公共策略與具體服務無關,策略名全局惟一,可使用策略名關聯策略數據便可。綜上,策略的拓撲關係描述以下:
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存儲策略。 更新機制以下圖所示:
爲了解決timer worker和其它worker在讀寫策略數據時的競態關係,咱們採用了雙buffer機制,實現了業務層策略數據的無鎖讀寫。另外,經過設置timer的時間爲0,保證在全部worker處理請求前,策略數據已經在共享內存中完成初始化。
查詢算法以下圖所示:
備註:公共策略以"oceanus"開頭,區別於私有策略的命名。
查詢到請求開啓的策略後,Oceanus須要運行時判斷是否匹配,以私有策略爲例,執行流以下圖所示:
分組列表的動態化是分流框架的重要一環。更新機制以下圖所示:
經過Oceanus分流機制在美團外賣、酒旅、到店餐飲等多個業務線的普遍使用,基礎架構部幫助業務同胞解決了多個定製化路由的需求,好比服務set化、鏈路壓測、灰度發佈、泳道環境建設等等。目前,Oceanus分流機制只關注了流量轉發方向,還不支持更復雜的轉發動做,好比根據策略調整請求的Parameter、header、Cookie,也不支持根據請求的URL實現動態路由等,將來咱們還將逐一完善這些問題,固然也歡迎你們跟咱們一塊兒交流,共同進步。
周峯,美團高級工程師,2015年7月加入美團基礎架構部,前後負責統一密鑰管理服務、智能反爬服務和HTTP負載均衡,目前主要負責HTTP服務治理Oceanus的相關工做,致力於探索和研究服務的自動化、智能化、和高性能等方向。
招聘廣告:若是你對大規模分佈式環境下的HTTP服務治理、分佈式會話鏈路追蹤等系統感興趣,誠摯歡迎投遞簡歷至:zhangzhitong#meituan.com。