為什么需要網(wǎng)關(guān)呢?
施秉網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。創(chuàng)新互聯(lián)建站從2013年開(kāi)始到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)建站。
我們知道我們要進(jìn)入一個(gè)服務(wù)本身,很明顯我們沒(méi)有特別好的辦法,直接輸入IP地址+端口號(hào),我們知道這樣的做法很糟糕的,這樣的做法大有問(wèn)題,首先暴露了我們實(shí)體機(jī)器的IP地址,別人一看你的IP地址就知道服務(wù)部署在哪里,讓別人很方便的進(jìn)行攻擊操作。
第二,我們這么多服務(wù),我們是不是要挨個(gè)調(diào)用它呀,我們這里假設(shè)做了個(gè)權(quán)限認(rèn)證,我們每一個(gè)客戶訪問(wèn)的都是跑在不同機(jī)器上的不同的JVM上的服務(wù)程序,我們每一個(gè)服務(wù)都需要一個(gè)服務(wù)認(rèn)證,這樣做煩不煩呀,明顯是很煩的。
那么我們這時(shí)候面臨著這兩個(gè)及其總要的問(wèn)題,這時(shí)我們就需要一個(gè)辦法解決它們。首先,我們看IP地址的暴露和IP地址寫(xiě)死后帶來(lái)的單點(diǎn)問(wèn)題,我是不是對(duì)這么服務(wù)本身我也要?jiǎng)討B(tài)的維護(hù)它服務(wù)的列表呀,我需要調(diào)用這服務(wù)本身,是不是也要一個(gè)負(fù)載均衡一樣的玩意,
還有關(guān)于IP地址暴露的玩意,我是不是需要做一個(gè)代理呀,像Nginx的反向代理一樣的東西,還有這玩意上部署公共的模塊,比如所有入口的權(quán)限校驗(yàn)的東西。因此我們現(xiàn)在需要Zuul API網(wǎng)關(guān)。它就解決了上面的問(wèn)題,你想調(diào)用某個(gè)服務(wù),它會(huì)給你映射,把你服務(wù)的IP地址映射成
某個(gè)路徑,你輸入該路徑,它匹配到了,它就去替你訪問(wèn)這個(gè)服務(wù),它會(huì)有個(gè)請(qǐng)求轉(zhuǎn)發(fā)的過(guò)程,像Nginx一樣,服務(wù)機(jī)器的實(shí)例具體實(shí)力,它不會(huì)直接去訪問(wèn)IP,它會(huì)去Eureka注冊(cè)中心拿到服務(wù)的實(shí)例ID,即服務(wù)的名字。我再次使用客戶端的負(fù)載均衡ribbon訪問(wèn)其中服務(wù)實(shí)例中的一臺(tái)。
API網(wǎng)關(guān)主要為了服務(wù)本身對(duì)外的調(diào)用該怎么調(diào)用來(lái)解決的,還有解決權(quán)限校驗(yàn)的問(wèn)題,你可以在這里整合調(diào)用一系列過(guò)濾器的,例如整合shiro,springsecurity之類的東西。
Zuul可以通過(guò)加載動(dòng)態(tài)過(guò)濾機(jī)制,從而實(shí)現(xiàn)以下各項(xiàng)功能:
1.驗(yàn)證與安全保障: 識(shí)別面向各類資源的驗(yàn)證要求并拒絕那些與要求不符的請(qǐng)求。
2.審查與監(jiān)控: 在邊緣位置追蹤有意義數(shù)據(jù)及統(tǒng)計(jì)結(jié)果,從而為我們帶來(lái)準(zhǔn)確的生產(chǎn)狀態(tài)結(jié)論。
3.動(dòng)態(tài)路由: 以動(dòng)態(tài)方式根據(jù)需要將請(qǐng)求路由至不同后端集群處。
4.壓力測(cè)試: 逐漸增加指向集群的負(fù)載流量,從而計(jì)算性能水平。
5.負(fù)載分配: 為每一種負(fù)載類型分配對(duì)應(yīng)容量,并棄用超出限定值的請(qǐng)求。
6.靜態(tài)響應(yīng)處理: 在邊緣位置直接建立部分響應(yīng),從而避免其流入內(nèi)部集群。
7.多區(qū)域彈性: 跨越AWS區(qū)域進(jìn)行請(qǐng)求路由,旨在實(shí)現(xiàn)ELB使用多樣化并保證邊緣位置與使用者盡可能接近。
接著下來(lái)進(jìn)行實(shí)戰(zhàn)小Demo
第一步,在原來(lái)的工程下,新建一個(gè)Zuul模塊,引入依賴,代碼如下:
org.springframework.cloud spring-cloud-starter-eureka 1.3.5.RELEASE org.springframework.cloud spring-cloud-starter-zuul 1.3.5.RELEASE
接著在啟動(dòng)類上打上@EnableZuulProxy注解,代碼如下:
server: port: 5000 spring: application: name: api-geteway zuul: routes: #標(biāo)識(shí)你服務(wù)的名字,這里可以自己定義,一般方便和規(guī)范來(lái)講還是跟自己服務(wù)的名字一樣 hello-service: #服務(wù)映射的路徑,通過(guò)這路徑就可以從外部訪問(wèn)你的服務(wù)了,目的是為了不爆露你機(jī)器的IP,面向服務(wù)的路由了,給你選一個(gè)可用的出來(lái), #這里zuul是自動(dòng)依賴hystrix,ribbon的,不是面向單機(jī) path: /hello-service/** #這里一定要是你Eureka注冊(cè)中心的服務(wù)的名稱,是所以這里配置serviceId因?yàn)楦鷈ureka結(jié)合了,如果單獨(dú)使用zuul,那么就必須寫(xiě)自己機(jī)器的IP了, #如url:http://localhost:8080/ 這樣的不好就是寫(xiě)死IP了,萬(wàn)一這IP掛了,這高可用性,服務(wù)注冊(cè)那套東西就用不起來(lái)了 serviceId: hello-service eureka: #客戶端 client: #注冊(cè)中心地址 service-url: defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
接著啟動(dòng)先前文章中的注冊(cè)中心和兩個(gè)hello-service服務(wù)提供者,接著我們運(yùn)行,看一下它的請(qǐng)求轉(zhuǎn)發(fā)功能,看他有沒(méi)有輪詢進(jìn)入兩個(gè)服務(wù),
輸入localhost:5000/hello-service/hello,如下:
接著再刷新一遍:
可以看到zuul進(jìn)行了請(qǐng)求分發(fā)了。它是根據(jù)你的服務(wù)名字hello-servie來(lái)映射到具體的機(jī)器上,這不就是一個(gè)反向代理的功能嗎?
zuul還能進(jìn)行請(qǐng)求過(guò)濾,那么我們進(jìn)行一下token校驗(yàn)來(lái)演示一下,首先我們需要先新建一個(gè)TokenFilter類來(lái)繼承ZuulFilter這個(gè)類,實(shí)現(xiàn)它的四個(gè)接口,代碼如下:
package hjc.zuul; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import javax.servlet.http.HttpServletRequest; /** * Created by cong on 2018/5/18. */ public class TokenFilter extends ZuulFilter { //四種類型:pre,routing,error,post //pre:主要用在路由映射的階段是尋找路由映射表的 //routing:具體的路由轉(zhuǎn)發(fā)過(guò)濾器是在routing路由器,具體的請(qǐng)求轉(zhuǎn)發(fā)的時(shí)候會(huì)調(diào)用 //error:一旦前面的過(guò)濾器出錯(cuò)了,會(huì)調(diào)用error過(guò)濾器。 //post:當(dāng)routing,error運(yùn)行完后才會(huì)調(diào)用該過(guò)濾器,是在最后階段的 @Override public String filterType() { return "pre"; } //自定義過(guò)濾器執(zhí)行的順序,數(shù)值越大越靠后執(zhí)行,越小就越先執(zhí)行 @Override public int filterOrder() { return 0; } //控制過(guò)濾器生效不生效,可以在里面寫(xiě)一串邏輯來(lái)控制 @Override public boolean shouldFilter() { return true; } //執(zhí)行過(guò)濾邏輯 @Override public Object run() { RequestContext context = RequestContext.getCurrentContext(); HttpServletRequest request = context.getRequest(); String token = request.getParameter("token"); if (token == null){ context.setSendZuulResponse(false); context.setResponseStatusCode(401); context.setResponseBody("unAuthrized"); return null; } return null; } }
filterType:返回一個(gè)字符串代表過(guò)濾器的類型,在zuul中定義了四種不同生命周期的過(guò)濾器類型,具體如下:
1. pre
:可以在請(qǐng)求被路由之前調(diào)用,用在路由映射的階段是尋找路由映射表的
2.route
:在路由請(qǐng)求時(shí)候被調(diào)用,具體的路由轉(zhuǎn)發(fā)過(guò)濾器是在routing路由器具體的請(qǐng)求轉(zhuǎn)發(fā)的時(shí)候會(huì)調(diào)用
3. error
:處理請(qǐng)求時(shí)發(fā)生錯(cuò)誤時(shí)被調(diào)用
4. post
:當(dāng)routing,error運(yùn)行完后才會(huì)調(diào)用該過(guò)濾器,是在最后階段的
這里聲明一下zuul過(guò)濾器執(zhí)行網(wǎng)絡(luò)請(qǐng)求發(fā)生的異常,過(guò)濾器里面是不能直接將try-catch捕捉的異常拋出給頁(yè)面的。應(yīng)用程序拋出的異常是可以返回出的需解決辦法就是在catch里面用context.set()方法返回給頁(yè)面。如下:
try{ 業(yè)務(wù)邏輯...... }catch(Exception e){ RequestContext context = RequestContext.getCurrentContext(); context.set("error.status_code",401); context.set("error.exception",e); context.set("error.message","sfdfsdf"); }
接著,你還需要把這個(gè)過(guò)濾器加入spring中,讓spring管理,代碼如下:
package hjc; import hjc.zuul.TokenFilter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; import org.springframework.context.annotation.Bean; @SpringBootApplication @EnableZuulProxy public class ZuulApplication { public static void main(String[] args) { SpringApplication.run(ZuulApplication.class, args); } //將過(guò)濾器交給Spring管理 @Bean public TokenFilter tokenFilter(){ return new TokenFilter(); } }
接著,讓我們啟動(dòng)啟動(dòng)類,先進(jìn)行不帶token的訪問(wèn),如下:
可以看到,返回一個(gè)沒(méi)權(quán)限的信息,這里要說(shuō)一下,Token一般都是放在請(qǐng)求頭中的,這里我們只是為了演示才沒(méi)那么干,
接著將token帶上再去訪問(wèn),如下:
可以看到這是已經(jīng)將我們的請(qǐng)求放過(guò)去了。
這里我還要講一下什么是默認(rèn)路由,將zuul的配置刪除路由配置,如下:
server: port: 5000 spring: application: name: api-geteway eureka: #客戶端 client: #注冊(cè)中心地址 service-url: defaultZone: http://localhost:8888/eureka/,http://localhost:8889/eureka/
接著,重啟繼續(xù)訪問(wèn),如下:
、
可以看到,還是能繼續(xù)訪問(wèn),我們什么都沒(méi)配,居然還能訪問(wèn),那是因?yàn)?,這里默認(rèn)用你的服務(wù)名字hello-service自動(dòng)聲明了。
那么,如果說(shuō)我不想讓它幫我自動(dòng)聲明,我要我自己定義,那么可以在yml配置文件中使用zuu.ignored-services就可以把自己像過(guò)濾的過(guò)濾,如下:”
zuul: #如果ignored-services:* 表示所有的默認(rèn)路由都失效了,要自己一個(gè)個(gè)配,沒(méi)人會(huì)那么操蛋,除非遇到奇葩業(yè)務(wù) ignored-services:
接著我們?cè)僬f(shuō)一下映射規(guī)則,比方說(shuō)
zuul: routes: #標(biāo)識(shí)你服務(wù)的名字,這里可以自己定義,一般方便和規(guī)范來(lái)講還是跟自己服務(wù)的名字一樣 hello-service: #服務(wù)映射的路徑,通過(guò)這路徑就可以從外部訪問(wèn)你的服務(wù)了,目的是為了不爆露你機(jī)器的IP,面向服務(wù)的路由了,給你選一個(gè)可用的出來(lái), #這里zuul是自動(dòng)依賴hystrix,ribbon的,不是面向單機(jī) path: /hello-service/** #這里一定要是你Eureka注冊(cè)中心的服務(wù)的名稱,是所以這里配置serviceId因?yàn)楦鷈ureka結(jié)合了,如果單獨(dú)使用zuul,那么就必須寫(xiě)自己機(jī)器的IP了, #如url:http://localhost:8080/ 這樣的不好就是寫(xiě)死IP了,萬(wàn)一這IP掛了,這高可用性,服務(wù)注冊(cè)那套東西就用不起來(lái)了 serviceId: hello-service zuul: routes: hello-service: path: /hello-service/ext/** serviceId: hello-service
這里的兩個(gè)zuul配置映射路徑都有/hello-service/,可以看到/hello-service/**是包括/hello-service/ext/**的,這兩個(gè)路徑進(jìn)行匹配的時(shí)候是不是有沖突呀,怎么處理呢?誰(shuí)先匹配呢?
這里是yml中定義的順序來(lái)匹配的。如果是application.properties格式的配置文件,它這個(gè)順序是不能保證的,yml格式的配置文件是有順序的,可以保證,這里要注意下一下。
如果我們想定義一下匹配規(guī)則怎么辦呢?那么我們就需要在啟動(dòng)類中定義一個(gè)bean,這個(gè)類就是決定你的路由的,如下:
這里就不演示了,需要用到的時(shí)候自己再去慢慢查找資料吧。
還有就是ignored-patterns:,如下:
zuul: routes: #標(biāo)識(shí)你服務(wù)的名字,這里可以自己定義,一般方便和規(guī)范來(lái)講還是跟自己服務(wù)的名字一樣 hello-service: #服務(wù)映射的路徑,通過(guò)這路徑就可以從外部訪問(wèn)你的服務(wù)了,目的是為了不爆露你機(jī)器的IP,面向服務(wù)的路由了,給你選一個(gè)可用的出來(lái), #這里zuul是自動(dòng)依賴hystrix,ribbon的,不是面向單機(jī) path: /hello-service/** #這里一定要是你Eureka注冊(cè)中心的服務(wù)的名稱,是所以這里配置serviceId因?yàn)楦鷈ureka結(jié)合了,如果單獨(dú)使用zuul,那么就必須寫(xiě)自己機(jī)器的IP了, #如url:http://localhost:8080/ 這樣的不好就是寫(xiě)死IP了,萬(wàn)一這IP掛了,這高可用性,服務(wù)注冊(cè)那套東西就用不起來(lái)了 serviceId: hello-service ignored-patterns: /hello/**
ignored-patterns:表示屏蔽掉/hello/**的路徑,就算你/hello-service/hello/**也不行,照樣屏蔽。這個(gè)配置我們可以進(jìn)一步細(xì)化,比如說(shuō)我不想給/hello接口路由,那我們可以按照上面方式配置
如果我們還想配置一個(gè)服務(wù)的前綴該怎么辦?代碼如下:
zuul: routes: #標(biāo)識(shí)你服務(wù)的名字,這里可以自己定義,一般方便和規(guī)范來(lái)講還是跟自己服務(wù)的名字一樣 hello-service: #服務(wù)映射的路徑,通過(guò)這路徑就可以從外部訪問(wèn)你的服務(wù)了,目的是為了不爆露你機(jī)器的IP,面向服務(wù)的路由了,給你選一個(gè)可用的出來(lái), #這里zuul是自動(dòng)依賴hystrix,ribbon的,不是面向單機(jī) path: /hello-service/** #這里一定要是你Eureka注冊(cè)中心的服務(wù)的名稱,是所以這里配置serviceId因?yàn)楦鷈ureka結(jié)合了,如果單獨(dú)使用zuul,那么就必須寫(xiě)自己機(jī)器的IP了, #如url:http://localhost:8080/ 這樣的不好就是寫(xiě)死IP了,萬(wàn)一這IP掛了,這高可用性,服務(wù)注冊(cè)那套東西就用不起來(lái)了 serviceId: hello-service prefix: /api/**
可以看到那么你訪問(wèn)的服務(wù)都必須要加/api/前綴,例如/api/hello-service/**
如果我們還想進(jìn)行一個(gè)路徑訪問(wèn)就跳轉(zhuǎn)到我的本地,那該怎么辦呢?
我希望用戶在訪問(wèn)/local時(shí)能夠自動(dòng)跳轉(zhuǎn)到這個(gè)方法上來(lái)處理,那么此時(shí)我們需要用到Zuul的本地跳轉(zhuǎn),配置方式如下:
zuul: prefix: /api ignored-patterns: /**/hello/** routes: local: path: /hello-service/** url: forward:/local
我們常用的一些,對(duì)接springsecurity,或者是一些第三方組件,它們會(huì)獲取你的一些cookie信息,那么Zuul網(wǎng)關(guān)為了安全起見(jiàn),把你的cookie信息都給干掉了,這個(gè)是沒(méi)辦法去搞cookie的。它是默認(rèn)干掉的。
這里Zuul提供了zuul.sensitive-headers來(lái)給你搞這些cookie,header,這些信息不要進(jìn)行過(guò)濾??刂颇愕拿舾行畔?。
默認(rèn)情況下,敏感的頭信息無(wú)法經(jīng)過(guò)API網(wǎng)關(guān)進(jìn)行傳遞,我們可以通過(guò)如下配置使之可以傳遞:
zuul: routes: hello-service: path: /hello-service/** serviceId: hello-service sensitive-headers: cookie,header之類額東西
還可以配合Hystrix的一些詳細(xì)配置一起使用,前面也講過(guò)了。這里就不說(shuō)了
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。