自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

攜程定制化路由代理網(wǎng)關實現(xiàn)

開發(fā)
本文主要介紹攜程軟負載產(chǎn)品,在業(yè)務增長及路由需求日漸復雜的背景下,如何從傳統(tǒng)Nginx反向代理逐漸發(fā)展成支持多元化路由的網(wǎng)關產(chǎn)品。以及其中利用的開源框架OpenResty的主要功能,和我們在路由轉發(fā)場景的落地實踐及探索。

一、背景

攜程軟負載產(chǎn)品(SLB)基于Nginx實現(xiàn),為攜程幾乎所有HTTP請求流量提供負載均衡服務,目前每天處理千億次請求。最開始時,SLB的主要職責是管理業(yè)務的HTTP路由和實現(xiàn)反向代理,取代更傳統(tǒng)的硬件負載為業(yè)務集群提供應用層負載均衡。

近幾年,隨著業(yè)務增長,以及多機房容災、混合云部署等背景,逐漸衍生出了多機房容災,跨區(qū)域流量調(diào)度等定制化需求,一個請求需要基于不同條件在機房間轉發(fā)。傳統(tǒng)的反向代理功能已經(jīng)無法滿足業(yè)務需求,如何在現(xiàn)有Nginx技術棧上支持越來越多的定制化路由需求,管理路由配置,并快速迭代功能成為了我們面臨的問題。

二、流量鏈路

SLB需要處理攜程內(nèi)網(wǎng)和外網(wǎng)所有的HTTP流量,SLB為業(yè)務應用提供HTTP層負載均衡能力,管理維護所有域名訪問入口和業(yè)務應用集群的關聯(lián)關系。在早期,公司整體的流量模式比較單一,SLB的職責也較為單一,主要作為HTTP等協(xié)議的反向代理。舉一個最簡單的Nginx樣例配置,我們將foo.bar.com/hello的請求轉發(fā)到后端一個三臺機器組成的固定集群上,這也是早期SLB大量配置的模式,將HTTP請求轉發(fā)到固定的某組機器。

圖片

server {
  listen 80;
  server_name foo.bar.com


  location ~* ^/hello {
    proxy_pass http://backend_499;
  }
}

upsstream backend_499 {
  server 10.10.10.1:80 weight=5 max_fails=0 fail_timeout=30;
  server 10.10.10.2:80 weight=5 max_fails=0 fail_timeout=30;
  server 10.10.10.3:80 weight=5 max_fails=0 fail_timeout=30;
}

后來隨著公司業(yè)務的增長以及對可用性更高的要求,有了混合云部署、異地容災等場景后,公司業(yè)務的流量鏈路也更加復雜,業(yè)務也提出了很多定制化場景。

2.1 多機房容災

攜程各業(yè)務線需要在私有云,公有云的多個機房部署,日常情況由各個機房部署的機器共同分擔處理業(yè)務流量。如果有機房大面積故障,需要從路由摘除整個故障機房,使外部流量導向正常機房。另外當流量高峰時,支持將高峰流量向容量相對更多的公有云機房泄洪。容災是業(yè)務高可用的重要保障,需要SLB在全局、應用和服務層面,有機房間流量動態(tài)分配切換的能力。

圖片

2.2 多元化需求

不同業(yè)務和場景往往關心請求中不同的維度和數(shù)據(jù),比如有些場景往往希望同一個用戶的請求鏈路能夠保持一致,實現(xiàn)set化的場景,而另一些場景可能更關心如何分流給不同的后端應用和不同的機房。功能上除了流量調(diào)度,也有請求標記,請求響應數(shù)據(jù)采集等需求。我們希望能有一個通用的方案來實現(xiàn)這些定制化的需求。

上述復雜場景和需求,顯然是無法通過樸素的反向代理模式和Nginx靜態(tài)配置實現(xiàn)的,這些需求推動SLB向集成API網(wǎng)關功能的產(chǎn)品前進。

三、面臨的問題

在SLB著手實現(xiàn)和落地這些需求的過程中,我們碰到了一些困難和痛點,總結下來有這幾方面。

3.1 路由能力

Nginx原生API提供的路由和請求處理能力都比較有限,像一些比較復雜的條件路由,即需要根據(jù)運行時的情況動態(tài)選擇路由目標或對請求做一些處理,Nginx很難以優(yōu)雅的方式支持。

3.2 動態(tài)更新

Nginx的配置更新需要一次reload操作,reload操作過程中master進程會用新配置fork出新的worker進程,這是一個非常耗資源的操作。Nginx和客戶端服務端的連接都需要重建,進程創(chuàng)建和加載配置本身也需要消耗資源,并且對于一些高頻請求的業(yè)務,reload會導致有請求失敗的情況。而在實際場景里,配置變更又是一個高頻操作,業(yè)務的一次灰度切流就需要好幾次有損變更,我們需要尋求一種沒有reload的變更生效方式。

dyups也是一種常見的動態(tài)更新方式,我們也通過這種方式來動態(tài)更新upstream。但實踐過程中我們發(fā)現(xiàn),大量dyups實際也會阻塞Nginx進程,我們需要更優(yōu)雅的方式。

3.3 集群管理

SLB在部署上,需要涵蓋并對齊公司所有網(wǎng)絡環(huán)境和機房,線上目前有上百套集群,我們的一些版本迭代需要消耗非常大的人力去做發(fā)布、版本對齊和兼容等工作。實際部署上,不同集群的職責也有差異,我們希望這些功能切面的升級更新可以獨立升級、動態(tài)生效。另外,每次路由邏輯的更新,路由的切換,需要能以灰度的方式進行,避免帶來災難性后果。

3.4 Nginx Lua

某種程度上,為了進一步拓展SLB的路由功能,我們引入了OpenResty的Nginx Lua模塊來嘗試解決這個問題。OpenResty開源的Nginx Lua模塊為Nginx嵌入了LuaJIT,提供了豐富的指令和API,包括但不限于:

1)讀寫請求(header, url, body),實現(xiàn)一些請求標記,rewrite等功能

2)請求轉發(fā),實現(xiàn)自定義的路由邏輯,不再局限于固定的upstream和server

3)共享內(nèi)存,Nginx進程間共享數(shù)據(jù)

4)網(wǎng)絡編程,實現(xiàn)一些旁路請求

我們通過Lua腳本在Nginx處理請求的各種階段,執(zhí)行自定義的腳本,實現(xiàn)不同的功能。涵蓋了nginx初始化、請求處理、請求轉發(fā)、響應和日志等非常完整的請求和響應流程,并且在很多公司都有實際應用。

圖片

在SLB早期方案中我們使用Nginx Lua實現(xiàn)一些相對固定的邏輯如集群間流量灰度。業(yè)務可以配置某個路徑的請求在新老集群間的流量比例,或指定某個集群路由,舉個例子:

server {
     location /foo/bar {
         content_by_lua_block {
             # 取隨機數(shù)
             local r = math.random()
     if (ngx.var.flag == "group_1" or ngx.var.flag == "group_2" ) then
         ngx.exec("@" .. ngx.var.flag)     # 指定訪問某個集群
     elseif (r >= 0.00 and r < 0.20) then  # 一定比例到一個集群
         ngx.exec("@group_1")
     elseif (r >= 0.20 and r <= 1.00) then # 一定比例到另一個集群
         ngx.exec("@group_2")
     end
         }
     }


     location @group_1 {
         content_by_lua_block {
             ngx.say("I am group_1")
         }
     }


     location @group_2 {
         content_by_lua_block {
             ngx.say("I am group_2")
         }
     }
 }

Nginx Lua一定程度上解決了傳統(tǒng)反向代理路由能力的問題,但在動態(tài)更新上仍然存在問題:變更仍然需要reload生效,一次業(yè)務灰度從0到100需要經(jīng)歷多次reload。

四、解決方案

4.1 核心:邏輯和數(shù)據(jù)

一個較為理想的方案來解決傳統(tǒng)路由動態(tài)更新所需的reload和它帶來的開銷問題:在Nginx中將數(shù)據(jù)和邏輯進行隔離。比如我們可以直接在Nginx配置中引用一個Lua文件,由Lua文件提供一個固定的方法入口,讀取內(nèi)存中的數(shù)據(jù)進行計算和轉發(fā)。通過這種方式,流量調(diào)度邏輯是相對固定的,動態(tài)和高頻變更的部分在數(shù)據(jù)模型,這樣我們就能避免reload,業(yè)務就可以進行高頻操作和修改。

server {
     location /foo/bar {
         content_by_lua_block {
             local foo = require("foo.lua")     # lua 庫
             local destination = foo.bar(data)  # 根據(jù)數(shù)據(jù)模型執(zhí)行
             ngx.exec(destination)              # 轉發(fā)
         }
     }


     location @somewhere {
         server xxxx;
     }
 }




 -- foo.lua
 function bar(data)
   return some_func(data)
 end

內(nèi)存數(shù)據(jù)可以是基于不同路由場景自定義的數(shù)據(jù)結構,來描述期望的配置、關聯(lián)關系、路由目標等。比如上述的集群間流量灰度,如果要實現(xiàn)流量根據(jù)不同比例轉發(fā)到兩個集群,這個結構可以類似于:

{
  "groups": [
    "group_1": {"weight": 20},
    "group_2": {"weight": 80}
  ]
}

類似的,如果要實現(xiàn)根據(jù)請求不同條件轉發(fā):

{
  "groups": [
    "group_1": {"header": "foo"},
    "group_2": {"header": "bar"}
  ]
}

對于每個流量調(diào)度場景,我們只要定義流量調(diào)度邏輯和相應的數(shù)據(jù)模型,就能比較方便的實現(xiàn)我們需要的功能。

4.2 整體架構

圖片

方案整體上分為三個職責不同的模塊:

1)API模塊:作為控制面集群的核心組件,API模塊基于Java開發(fā),它負責管理Lua文件和數(shù)據(jù)模型的生命周期。這包括對Lua文件和數(shù)據(jù)模型的創(chuàng)建、更新、刪除和持久化等操作,支持灰度下發(fā)。此外,API模塊還負責集群配置的管理,包括節(jié)點的注冊和發(fā)現(xiàn)、路由配置等。

2)Agent模塊:Agent模塊是連接控制面和數(shù)據(jù)面的橋梁,承擔著數(shù)據(jù)的加工和傳遞的任務。它負責將控制面下發(fā)的Lua文件和數(shù)據(jù)模型持久化到本地存儲,以便在數(shù)據(jù)面進行實時的路由計算和流量處理。Agent模塊還負責與管理層的API模塊進行通信,下發(fā)最新的Lua腳本文件和數(shù)據(jù)模型的更新,并將其應用到本地存儲中,以確保數(shù)據(jù)面能夠及時獲取最新的路由規(guī)則。

3)Nginx & Lua模塊:作為數(shù)據(jù)面集群的核心組件,Nginx & Lua模塊承擔著實際的請求處理和路由邏輯的執(zhí)行。Nginx Lua提供了靈活的編程能力,使得我們可以根據(jù)業(yè)務需求定制化地處理請求和路由流量。Lua腳本可以訪問實時更新的數(shù)據(jù)模型,根據(jù)其中的信息進行動態(tài)的路由計算,并將請求轉發(fā)到預期的目標。這種動態(tài)路由的能力使得系統(tǒng)能夠根據(jù)實時的業(yè)務需求和環(huán)境變化來靈活地調(diào)整流量分配。

4.3 數(shù)據(jù)模型生命周期

在方案實踐中比較重要且復雜的地方是路由邏輯依賴的數(shù)據(jù)模型如何更新和生效。我們采用類似于Nginx配置文件的工作和生效方式,把數(shù)據(jù)模型存儲在磁盤,而不同之處在于我們旁路將數(shù)據(jù)模型從磁盤讀取到內(nèi)存,運行時從內(nèi)存讀取模型,這樣就不再依賴reload生效。

具體來說,Nginx是一個多進程的應用,會存在多個worker進程處理請求,進程間天然存在內(nèi)存隔離。在worker的生命周期里,在每個worker初始化時先加載數(shù)據(jù)到內(nèi)存,運行時動態(tài)同步更新內(nèi)存數(shù)據(jù)。為了把更新推送到每個worker進程,我們基于Nginx Lua提供的共享內(nèi)存功能,在共享內(nèi)存中緩存每個數(shù)據(jù)模型的最新版本,而worker進程通過一個timer來不斷輪詢共享內(nèi)存,發(fā)現(xiàn)版本有更新后從磁盤讀取數(shù)據(jù)模型,加載到內(nèi)存,對于每個場景可以在Lua中自定義具體的數(shù)據(jù)加載邏輯。

圖片

相比于worker直接或旁路輪詢數(shù)據(jù)接口,共享內(nèi)存最小化了請求和解析數(shù)據(jù)對運行時的影響。另外Agent把每個數(shù)據(jù)模型都持久化到磁盤解決了進程重啟時內(nèi)存數(shù)據(jù)丟失的問題,worker進程可以在初始化時主動從磁盤重新讀取數(shù)據(jù)。這樣即使我們的Nginx由于一些意外重啟或者退出(發(fā)布,OOM,機器重啟等),至少還能以磁盤上持久化的數(shù)據(jù)繼續(xù)工作,并且不依賴其他組件(API、Agent)的存活。

4.4 實踐和落地情況

在實踐中我們注意到,Lua或者說數(shù)據(jù)面應用的處理邏輯應盡可能簡單,但對于運維和開發(fā)同學來說,一般都是希望有全局視角的數(shù)據(jù)來掌控全局。

所以在數(shù)據(jù)結構設計上,我們一般會進行拆分:API和運維人員及其他系統(tǒng)的交互通常采用全局數(shù)據(jù),會有多個層級。而在和Agent中間層交互時主動加工數(shù)據(jù)模型,去掉運行時不需要的部分,如其他集群的配置,被關閉的配置等;到了真正處理請求的數(shù)據(jù)面,往往我們只希望從一個扁平化的數(shù)據(jù)模型以類似key-value的形式讀取。比方說一個應用在多數(shù)據(jù)中心的流量分配,在全局視角來看類似于:

{
  "app_1": {
    "idc_1": {"target": "group1"},
    "idc_2": {"target": "group2"},
    "idc_3": {"target": "group3"}
  }
}

對于某個IDC集群的數(shù)據(jù)面來說,Lua中讀取的理想形式是盡可能平面化的Table:

{
  "app_1": "group1"
}

避免了運行時Lua做過多的數(shù)據(jù)解析邏輯,是較為合理的職責分配方式。

目前我們通過該方案實現(xiàn)了應用和服務粒度的流量分配,使得業(yè)務應用能夠在多個數(shù)據(jù)中心提供服務。同時,該方案還提供了全局及應用粒度的故障降級功能,以增強業(yè)務的可用性,經(jīng)過多次機房故障演練驗證,切換可以在秒級生效。除了實現(xiàn)復雜的請求路由功能之外,我們通過Lua也落地了請求響應數(shù)據(jù)采集,不同渠道流量標記等旁路功能。

五、結語

流量鏈路隨著公司技術和業(yè)務的拓展,走向多元、動態(tài)和定制化的發(fā)展方向,不斷對負載均衡和網(wǎng)關產(chǎn)品提出新的場景和需求。定制化路由解決方案解決了傳統(tǒng)反向代理在路由能力和動態(tài)更新上的問題,降低了大規(guī)模集群部署下的研發(fā)和迭代成本,幫助SLB產(chǎn)品從傳統(tǒng)的反向代理中間件向綜合性的API網(wǎng)關逐漸靠攏。方案對不同場景有良好的擴展性和可靠性,未來也會有更多的流量調(diào)度模式通過統(tǒng)一方案快速落地交付。

在產(chǎn)品技術棧上,不同的編程語言和技術各有所長,我們希望能使不同技術達成優(yōu)勢互補,像Lua的動態(tài),Nginx的性能。需要我們持續(xù)在這些技術上不斷學習和投入,加深我們對產(chǎn)品技術棧的理解,揚長避短。

在未來,如何更好的利用現(xiàn)有技術棧,方便Java開發(fā)的同學能快速上手Lua開發(fā),如何優(yōu)化Lua責任鏈和異常處理,優(yōu)化Lua的內(nèi)存管理,還有進一步努力和探索的空間。

責任編輯:張燕妮 來源: 攜程技術
相關推薦

2014-12-25 17:51:07

2023-08-18 10:49:14

開發(fā)攜程

2023-08-25 09:51:21

前端開發(fā)

2025-01-03 14:33:41

2013-06-27 14:32:13

華為網(wǎng)絡虛擬化虛擬化Hyper-V

2021-11-24 08:55:38

代理網(wǎng)關Netty

2022-07-15 12:58:02

鴻蒙攜程華為

2022-05-13 09:27:55

Widget機票業(yè)務App

2024-05-23 17:14:49

2021-08-20 11:00:04

Redis攜程數(shù)據(jù)庫

2013-08-12 16:58:12

博科太網(wǎng)矩陣虛擬化

2022-10-21 10:40:08

攜程酒店MySQL慢查詢

2017-04-11 15:11:52

ABtestABT變量法

2015-05-29 13:59:53

2022-07-15 09:20:17

性能優(yōu)化方案

2022-08-12 08:34:32

攜程數(shù)據(jù)庫上云

2023-02-08 16:34:05

數(shù)據(jù)庫工具

2024-11-05 09:56:30

2022-07-08 09:38:27

攜程酒店Flutter技術跨平臺整合

2024-01-12 09:31:08

Java代碼
點贊
收藏

51CTO技術棧公眾號