Zuul is the front door for all requests from devices and web sites to the backend of the Netflix streaming application.
創(chuàng)新互聯(lián)建站主要從事網(wǎng)站制作、做網(wǎng)站、網(wǎng)頁設(shè)計(jì)、企業(yè)做網(wǎng)站、公司建網(wǎng)站等業(yè)務(wù)。立足成都服務(wù)博樂,10多年網(wǎng)站建設(shè)經(jīng)驗(yàn),價格優(yōu)惠、服務(wù)專業(yè),歡迎來電咨詢建站服務(wù):18980820575
Zuul 是從設(shè)備和網(wǎng)站到后端應(yīng)用程序或服務(wù)的所有請求的入口,為內(nèi)部服務(wù)提供了可配置的對外URL到服務(wù)的映射關(guān)系。
Zuul 網(wǎng)關(guān)提供了如下功能:
1、路由轉(zhuǎn)發(fā):接收一切外界請求,轉(zhuǎn)發(fā)到后端的微服務(wù)上去;
2、過濾器Filter:在服務(wù)網(wǎng)關(guān)中可以完成一系列的橫切功能,例如權(quán)限校驗(yàn)、限流以及監(jiān)控等,這些都可以通過過濾器完成(其實(shí)路由轉(zhuǎn)發(fā)也是通過過濾器實(shí)現(xiàn)的)。
Zuul Server 工程中需要引入的依賴項(xiàng)
如果前端、移動端要調(diào)用后端系統(tǒng),統(tǒng)一從Zuul網(wǎng)關(guān)進(jìn)入,由Zuul網(wǎng)關(guān)轉(zhuǎn)發(fā)請求給對應(yīng)的服務(wù)
在Spring Boot主函數(shù)上通過注解 @EnableZuulProxy 來開啟網(wǎng)關(guān)路由功能,這樣可以將請求轉(zhuǎn)發(fā)到對應(yīng)的服務(wù)。 按照約定, 一個ID為"client"的服務(wù)會收到 /client 請求路徑的代理請求(前綴會被剝離)。
Zuul使用Ribbon定位服務(wù)注冊中的實(shí)例, 并且所有的請求都在hystrix的command中執(zhí)行, 所以失敗信息將會展現(xiàn)在Hystrix Dashboard中, 并且一旦斷路器打開, 代理請求將不會嘗試去鏈接服務(wù)。
如下是Eureka,Zuul,Ribbon 交互圖:
網(wǎng)關(guān)=反向代理+負(fù)載均衡+各種策略,技術(shù)實(shí)現(xiàn)也有多種多樣,有基于 nginx 使用 lua 的實(shí)現(xiàn),比如 openresty、kong;也有基于 zuul 的通用網(wǎng)關(guān);還有就是 golang 的網(wǎng)關(guān),比如 tyk。
這篇文章主要是講如何基于 golang 實(shí)現(xiàn)一個簡單的網(wǎng)關(guān)。
轉(zhuǎn)自: troy.wang/docs/golang/posts/golang-gateway/
整理:go語言鐘文文檔:
啟動兩個后端 web 服務(wù)(代碼)
這里使用命令行工具進(jìn)行測試
具體代碼
直接使用基礎(chǔ)庫 httputil 提供的NewSingleHostReverseProxy即可,返回的reverseProxy對象實(shí)現(xiàn)了serveHttp方法,因此可以直接作為 handler。
具體代碼
director中定義回調(diào)函數(shù),入?yún)?http.Request,決定如何構(gòu)造向后端的請求,比如 host 是否向后傳遞,是否進(jìn)行 url 重寫,對于 header 的處理,后端 target 的選擇等,都可以在這里完成。
director在這里具體做了:
modifyResponse中定義回調(diào)函數(shù),入?yún)?http.Response,用于修改響應(yīng)的信息,比如響應(yīng)的 Body,響應(yīng)的 Header 等信息。
最終依舊是返回一個ReverseProxy,然后將這個對象作為 handler 傳入即可。
參考 2.2 中的NewSingleHostReverseProxy,只需要實(shí)現(xiàn)一個類似的、支持多 targets 的方法即可,具體實(shí)現(xiàn)見后面。
作為一個網(wǎng)關(guān)服務(wù),在上面 2.3 的基礎(chǔ)上,需要支持必要的負(fù)載均衡策略,比如:
隨便 random 一個整數(shù)作為索引,然后取對應(yīng)的地址即可,實(shí)現(xiàn)比較簡單。
具體代碼
使用curIndex進(jìn)行累加計(jì)數(shù),一旦超過 rss 數(shù)組的長度,則重置。
具體代碼
輪詢帶權(quán)重,如果使用計(jì)數(shù)遞減的方式,如果權(quán)重是5,1,1那么后端 rs 依次為a,a,a,a,a,b,c,a,a,a,a…,其中 a 后端會瞬間壓力過大;參考 nginx 內(nèi)部的加權(quán)輪詢,或者應(yīng)該稱之為平滑加權(quán)輪詢,思路是:
后端真實(shí)節(jié)點(diǎn)包含三個權(quán)重:
操作步驟:
具體代碼
一致性 hash 算法,主要是用于分布式 cache 熱點(diǎn)/命中問題;這里用于基于某 key 的 hash 值,路由到固定后端,但是只能是基本滿足流量綁定,一旦后端目標(biāo)節(jié)點(diǎn)故障,會自動平移到環(huán)上最近的那么個節(jié)點(diǎn)。
實(shí)現(xiàn):
具體代碼
每一種不同的負(fù)載均衡算法,只需要實(shí)現(xiàn)添加以及獲取的接口即可。
然后使用工廠方法,根據(jù)傳入的參數(shù),決定使用哪種負(fù)載均衡策略。
具體代碼
作為網(wǎng)關(guān),中間件必不可少,這類包括請求響應(yīng)的模式,一般稱作洋蔥模式,每一層都是中間件,一層層進(jìn)去,然后一層層出來。
中間件的實(shí)現(xiàn)一般有兩種,一種是使用數(shù)組,然后配合 index 計(jì)數(shù);一種是鏈?zhǔn)秸{(diào)用。
具體代碼
Zuul 網(wǎng)關(guān)是具體核心業(yè)務(wù)服務(wù)的看門神,相比具體實(shí)現(xiàn)業(yè)務(wù)的系統(tǒng)服務(wù)來說它是一個邊緣服務(wù),主要提供動態(tài)路由,監(jiān)控,彈性,安全性等功能。在分布式的微服務(wù)系統(tǒng)中,系統(tǒng)被拆為了多套系統(tǒng),通過zuul網(wǎng)關(guān)來對用戶的請求進(jìn)行路由,轉(zhuǎn)發(fā)到具體的后臺服務(wù)系統(tǒng)中。
本 Chat 主要內(nèi)容如下:
網(wǎng)關(guān)是具體核心業(yè)務(wù)服務(wù)的看門神,相比具體實(shí)現(xiàn)業(yè)務(wù)的系統(tǒng)服務(wù)來說它是一個邊緣服務(wù),主要提供動態(tài)路由,監(jiān)控,彈性,安全性等功能,下面我們從單體應(yīng)用到多體應(yīng)用的演化過程來講解網(wǎng)關(guān)的演化歷程。
一般業(yè)務(wù)系統(tǒng)發(fā)展歷程都是基本相似的,從單體應(yīng)用到多應(yīng)用,從本地調(diào)用到遠(yuǎn)程調(diào)用。對應(yīng)單體應(yīng)用架構(gòu)模式(如下圖1),由于只需一個應(yīng)用,所有業(yè)務(wù)模塊的功能都打包為了一個 War 包進(jìn)行部署,這樣可以減少機(jī)器資源和部署的繁瑣。
圖1 單體應(yīng)用
在單體應(yīng)用中,網(wǎng)關(guān)模塊是和應(yīng)用部署到同一個jvm進(jìn)程里面的,當(dāng)外部移動設(shè)備或者web站點(diǎn)訪問單體應(yīng)用的功能時候,請求是先被應(yīng)用的網(wǎng)關(guān)模塊攔截的,網(wǎng)關(guān)模塊對請求進(jìn)行鑒權(quán)、限流等動作后在把具體的請求轉(zhuǎn)發(fā)到當(dāng)前應(yīng)用對應(yīng)的模塊進(jìn)行處理。
隨著業(yè)務(wù)的發(fā)展,網(wǎng)站的流量會越來越大,在單體應(yīng)用中簡單的通過加機(jī)器的方式可以帶來的承受流量沖擊的能力也越來越低,這時候就會考慮根據(jù)業(yè)務(wù)將單體應(yīng)用拆成若干個功能獨(dú)立的應(yīng)用,單體應(yīng)用拆為多個應(yīng)用后,由于不同的應(yīng)用開發(fā)對應(yīng)的功能,所以多應(yīng)用開發(fā)之間可以獨(dú)立開發(fā)而不用去理解對方的業(yè)務(wù),另外不同的應(yīng)用模塊只承受對應(yīng)業(yè)務(wù)流量的壓力,不會對其他應(yīng)用模塊造成影響,這時候多體的分布式系統(tǒng)就出現(xiàn)了,如下圖2。
圖2 多體應(yīng)用
如上圖在多體應(yīng)用中業(yè)務(wù)模塊A和B單獨(dú)起了個應(yīng)用,每個應(yīng)用里面有自己的網(wǎng)關(guān)模塊,如果業(yè)務(wù)模塊多了,那么每個應(yīng)用都有自己的網(wǎng)關(guān)模塊,這樣復(fù)用性不好,所以可以考慮把網(wǎng)關(guān)模塊提起出來,單獨(dú)作為一個應(yīng)用來做服務(wù)路由,如下圖3:
如上圖當(dāng)移動設(shè)備發(fā)起請求時候是具體發(fā)送到網(wǎng)關(guān)應(yīng)用的,經(jīng)過鑒權(quán)后請求會被轉(zhuǎn)發(fā)到具體的后端服務(wù)應(yīng)用上,對應(yīng)前端移動設(shè)備來說他們不在乎也不知道后端服務(wù)器應(yīng)用是一個還是多個,他們只能感知到網(wǎng)關(guān)應(yīng)用的存在。
Zuul是Netflix開源的一個網(wǎng)關(guān)組件,在Netflix內(nèi)部系統(tǒng)中Zuul被用來作為內(nèi)部系統(tǒng)的門面,如下圖是Zuul在Netflix內(nèi)部使用的一個架構(gòu)圖:
如上圖最上層的移動設(shè)備或者網(wǎng)站首先通過aws負(fù)載均衡器把請求路由到zuul網(wǎng)關(guān)上,zuul網(wǎng)關(guān)則負(fù)責(zé)把請求路由到具體的后端service上。
Zuul開源地址
Zuul網(wǎng)關(guān)的核心是一系列的過濾器,這些過濾器可以對請求或者響應(yīng)結(jié)果做一系列過濾,Zuul 提供了一個框架可以支持動態(tài)加載,編譯,運(yùn)行這些過濾器,這些過濾器是使用責(zé)任鏈方式順序?qū)φ埱蠡蛘唔憫?yīng)結(jié)果進(jìn)行處理的,這些過濾器直接不會直接進(jìn)行通信,但是通過責(zé)任鏈傳遞的RequestContext參數(shù)可以共享一些東西。
雖然Zuul 支持任何可以在jvm上跑的語言,但是目前zuul的過濾器只能使用Groovy腳本來編寫。編寫好的過濾器腳本一般放在zuul服務(wù)器的固定目錄,zuul服務(wù)器會開啟一個線程定時去輪詢被修改或者新增的過濾器,然后動態(tài)進(jìn)行編譯,加載到內(nèi)存,然后等后續(xù)有請求進(jìn)來,新增或者修改后的過濾器就會生效了。
在zuul中過濾器分為四種:
如下圖為zuul1.0的工作原理:
如上圖,當(dāng)zuul接受到請求后,首先會由前置過濾器進(jìn)行處理,然后在由路由過濾器具體把請求轉(zhuǎn)發(fā)到后端應(yīng)用,然后在執(zhí)行后置過濾器把執(zhí)行結(jié)果寫會到請求方,當(dāng)上面任何一個類型過濾器執(zhí)行出錯時候執(zhí)行該過濾器。
本節(jié)作者使用zuul的版本:
...
....
總結(jié):zuul1.0時候當(dāng)zuul接受到一個請求后會同步執(zhí)行前置過濾器、路由過濾器、后置過濾器,等執(zhí)行完畢后在同步把結(jié)果返回為調(diào)用方,調(diào)用方在整個過程中是阻塞的。其實(shí)SpringBoot集成的zuul就是自己實(shí)現(xiàn)了個前置過濾器做選擇路由,然后自己實(shí)現(xiàn)了個路由過濾器根據(jù)前置過濾器選擇的路由具體做路由轉(zhuǎn)發(fā)。
Netty作為高性能異步網(wǎng)絡(luò)通訊框架,在dubbo,rocketmq,sofa等知名開源框架中都有使用,如下圖zuul2.0使用netty server作為網(wǎng)關(guān)監(jiān)聽服務(wù)器監(jiān)聽客戶端發(fā)來的請求,然后把請求轉(zhuǎn)發(fā)到前置過濾器(inbound filters)進(jìn)行處理,處理完畢后在把請求使用netty client代理到具體的后端服務(wù)器進(jìn)行處理,處理完畢后在把結(jié)果交給后者過濾器(outbound filters)進(jìn)行處理,然后把處理結(jié)果通過nettyServer寫回客戶端。
...
總: 在zuul1.0時候客戶端發(fā)起的請求后需要同步等待zuul網(wǎng)關(guān)返回,zuul網(wǎng)關(guān)這邊對每個請求會分派一個線程來進(jìn)行處理,這會導(dǎo)致并發(fā)請求數(shù)量有限。而zuul2.0使用netty作為異步通訊,可以大大加大并發(fā)請求量。
在前面的學(xué)習(xí)中,我們使用Spring Cloud實(shí)現(xiàn)微服務(wù)的架構(gòu)基本成型,大致是這樣的:
我們使用Spring Cloud Netflix中的Eureka實(shí)現(xiàn)了服務(wù)注冊中心以及服務(wù)注冊與發(fā)現(xiàn);而服務(wù)間通過Ribbon或Feign實(shí)現(xiàn)服務(wù)的消費(fèi)以及負(fù)載均衡;為了使得服務(wù)集群更為健壯,使用Hystrix的熔斷機(jī)制 來避免在微服務(wù)架構(gòu)中個別服務(wù)出現(xiàn)異常時引起的故障蔓延。
在該架構(gòu)中,我們的服務(wù)集群包括:內(nèi)部服務(wù)Service A和Service B,他們都會注冊與訂閱服務(wù)至Eureka Server,而Open Servc時一個對外的服務(wù),通過負(fù)載均衡公開至服務(wù)調(diào)用方。我們把焦點(diǎn)聚集在對外服務(wù)這塊,這種實(shí)現(xiàn)方式是否合理,有沒有更好的實(shí)現(xiàn)方式呢?
這種架構(gòu)不足之處:
對于上面的問題,最好的解決方法是——服務(wù)網(wǎng)關(guān)。
為了解決上面的問題,我們需要將權(quán)限控制這樣的東西從我們的服務(wù)單元中抽離出去,而最適合這些邏輯的地方就是處于對外訪問最前端的地方,我們需要一個更加強(qiáng)大的負(fù)載均衡器——服務(wù)網(wǎng)關(guān)。
服務(wù)網(wǎng)關(guān)是微服務(wù)架構(gòu)中不可或缺的的部分。通過服務(wù)網(wǎng)關(guān)統(tǒng)一向外系統(tǒng)提供REST API的過程中,除了具備服務(wù)路由、負(fù)載均衡之外,它還具備了權(quán)限控制等功能。Spring Cloud NetFlix中的Zuul就具備這樣的功能。
Zuul是NetFlix開源的微服務(wù)網(wǎng)關(guān),它可以和Eureka、Ribbon、Hystrix等組件配合使用。Zuul的核心是一些列的過濾器,這些過濾器可以完成以下功能。
Spring Cloud對Zuul進(jìn)行了整合與增強(qiáng)。目前,Zuul使用的默認(rèn)HTTP客戶端是Apache HTTP Client,也可以使用使用其他的。
使用Zuul之后的架構(gòu)如圖所示:
從圖中可以看出,客戶端請求微服務(wù)時,先經(jīng)過Zuul之后再請求,這樣就可以將一些類似于校驗(yàn)的業(yè)務(wù)邏輯放到zuul中去完成,而微服務(wù)本身只需要關(guān)注自己的業(yè)務(wù)邏輯即可。
首先新建一個gateway模塊,并且導(dǎo)入相關(guān)的依賴
然后再編寫啟動類增加@EnableZuulProxy注解。
接著編寫application.yml文件,并添加路由規(guī)則
然后啟動測試,發(fā)現(xiàn)可以通過zuul訪問到商品微服務(wù)地址
在上面的配置中,我們通過路由規(guī)則的配置,訪問到了商品微服務(wù)。但是如果商品微服務(wù)的地址發(fā)生變化,我們要修改配置問題。所以,我們應(yīng)該通過走Eureka注冊中心來獲取地址。首先,我們需要添加Eureka依賴。
然后修改application.yml配置文件,將自己注冊到注冊中心。
隨后,通過測試發(fā)現(xiàn)可以通過zuul訪問商品微服務(wù),一切正常。
過濾器是Zuul的重要組件。在我們的一般的單體應(yīng)用中,也會使用過濾器過濾請求,或者完成相關(guān)的權(quán)限配置等,例如shiro框架就是用過濾器管理權(quán)限的。Zuul中的過濾器名為ZuulFilter:
ZuulFilter是一個抽象類,其實(shí)現(xiàn)類需要實(shí)現(xiàn)4個方法:
過濾器的執(zhí)行流程如圖所示:
需求:通過編寫過濾器實(shí)現(xiàn)用戶是否登錄的檢查
實(shí)現(xiàn):通過判斷請求中是否有token,如果有認(rèn)為就是已經(jīng)登錄的,如果沒有就認(rèn)為時非法請求,響應(yīng)401.
接下來,我們需要編寫一個UserLoginZuulFilter,邏輯如下:
隨后,我們打開瀏覽器進(jìn)行測試,可以看到過濾器已經(jīng)生效: