PaaS 7層動態(tài)路由的若干實現(xiàn)
隨著Docker的出現(xiàn),PaaS、CaaS(Container As A Service)、甚至DCOS(DataCenter OS)呈現(xiàn)了爆發(fā)式的發(fā)展。而在PaaS中,因為實例一般默認為動態(tài)IP,對于7層調(diào)用(比如http請求),需要7層動態(tài)路由獲取應(yīng)用域名(或虛IP)和后端實例的映射關(guān)系,以提供7層服務(wù);而對于4層調(diào)用(比如rpc調(diào)用),可以通過動態(tài)LVS或名字服務(wù)(或基于zookeeper/etcd等實現(xiàn)的服務(wù)注冊和發(fā)現(xiàn)工具)進行調(diào)用。
簡單舉例,開發(fā)者在PaaS里創(chuàng)建了一個APP,包含若干個實例,然后為APP綁定某個域名。用戶對APP發(fā)出請求,需要經(jīng)過后端實例的處理才能正確的返回。那么作為7層動態(tài)路由,核心就是獲取域名(或虛IP)與后端實例的對應(yīng)關(guān)系并作為反向代理完成請求轉(zhuǎn)發(fā)。
這里簡單討論下7層動態(tài)路由的若干實現(xiàn)。因為nginx是一個高性能的反向代理服務(wù)器,以是否基于nginx實現(xiàn)7層動態(tài)路由,可以將這些實現(xiàn)大概分為兩大類。
***類,不依賴nginx,項目自身實現(xiàn)了反向代理的功能。
CloudFoundry可以說是***代的開源PaaS項目,其模塊gorouter即為一個動態(tài)路由實現(xiàn)(同時支持4層和7層)。以 CloudFoundry (release v164)為例,使用nats作為消息總線,對各模塊調(diào)用和消息傳遞進行解耦。可以看下gorouter (tag 45ca951297)的代碼,registry/registry.go里實現(xiàn)了相應(yīng)邏輯代碼,以獲取域名和后端實例的對應(yīng)關(guān)系。其大概流程為:
實例啟動后向nats發(fā)布消息,gorouter則會訂閱這些消息,從而獲取應(yīng)用域名和后端實例的對應(yīng)關(guān)系;同時gorouter使用goroutine實現(xiàn)了高性能的請求處理和轉(zhuǎn)發(fā),支持4層和7層調(diào)用。
Docker出現(xiàn)后,基于Docker的輕量級PaaS紛紛涌現(xiàn),大家可能會把實例信息(如IP信息等)存儲在redis(或其它數(shù)據(jù)存儲)。開源項目DINP基本就是這樣的實現(xiàn),dinp-router fork自CloudFoundry的gorouter (tag 45ca951297),更改了部分代碼,以獲取應(yīng)用域名和redis中存儲的后端實例的對應(yīng)關(guān)系,從而實現(xiàn)了7層動態(tài)路由的功能。
類似的,dotCloud的hipache則是利用nodejs的http庫實現(xiàn)了請求轉(zhuǎn)發(fā),后端實例信息則可以存儲在redis中。
當然,很多工程師在7層的選型上還是更信賴nginx,畢竟nginx在性能、穩(wěn)定性、擴展性上都是不二之選。基于nginx來實現(xiàn)7層動態(tài)路由,大概又有兩種實現(xiàn)思路。
其一,基于名字服務(wù)(或基于zookeeper/etcd等實現(xiàn)的服務(wù)注冊和發(fā)現(xiàn)工具),通過watch或定時調(diào)度,將注冊的后端實例更新到 nginx配置文件的upstream中,從而實現(xiàn)后端的(準)實時變化。這方面也有如confd等的開源工具。confd基于golang的 template庫,將nginx配置文件作為模板;支持consul/etcd/redis/zookeeper等諸多后端存儲,通過watch或定時調(diào)度從這些后端獲取實例信息,并更新到nginx配置文件模板,從而實現(xiàn)(準)實時的7層動態(tài)路由。這種實現(xiàn)邏輯簡單,穩(wěn)定性高,但在大規(guī)模應(yīng)用時 nginx可能會較頻繁的reload。
其二,基于nginx-lua實現(xiàn)。每次用戶請求到達相應(yīng)upstream時,通過nginx-lua從redis等數(shù)據(jù)存儲中獲得后端實例信息,從而實現(xiàn)請求的轉(zhuǎn)發(fā)。nginx獲取redis數(shù)據(jù)需要進行一次網(wǎng)絡(luò)請求,同機房的時延一般是毫秒級,但在大訪問量時可能存在一定問題,因此可以使用 lua-shared-dict作為系統(tǒng)緩存。
參考:
https://github.com/openresty/lua-nginx-module
http://segmentfault.com/a/1190000004128807?luicode=10000359&luicode=10000359