Spring Cloud之網(wǎng)關(guān)-Gateway(一)

因?yàn)樽罱疽灿?jì)劃將原有的項(xiàng)目改造成微服務(wù)的架構(gòu)伴找,加上之前自己面試過程中也發(fā)現(xiàn)自己在這方面也比較欠缺,所以準(zhǔn)備好好的來學(xué)習(xí)一下微服務(wù)的各個(gè)常用的組件查库。其實(shí)微服務(wù)發(fā)展到現(xiàn)在出現(xiàn)的各種架構(gòu)都比較成熟了鼎姊,我覺得具體情況具體分析就好了,不存在最好只有最適合箍鼓,當(dāng)然如果不知道怎么選型崭参,那我覺得選擇spring cloud官方的組件應(yīng)該也不會(huì)差。 上周開始項(xiàng)目組就在研究技術(shù)選型的問題款咖,因?yàn)闆]有架構(gòu)師所以很多東西需要項(xiàng)目組來確定何暮,另外也要參考公司的其他部門的經(jīng)驗(yàn)奄喂,大概介紹一下我們的技術(shù)組件吧:Nacos做注冊中心和配置中心;網(wǎng)關(guān)就是這次學(xué)習(xí)的spring cloud gateway海洼;日志框架就是ELK跨新,這個(gè)之前我也搭建過;緩存的話選擇的Redis坏逢;微服務(wù)監(jiān)控選擇的是admin域帐;另外就是跟蹤鏈選擇的是skywalking,其實(shí)上周有幾天我一直在熟悉skywalking的部署是整,還是踩了一些坑肖揣,后面有機(jī)會(huì)我專門再寫一篇吧。其他的比如jekins浮入、docker龙优、k8s等等,目前技術(shù)框架整體就是這樣一個(gè)情況舵盈,這些內(nèi)容也需要后面去學(xué)習(xí)陋率。
今天主要學(xué)習(xí)下微服務(wù)網(wǎng)關(guān),API 網(wǎng)關(guān)可以說是整個(gè)微服務(wù)的統(tǒng)一入口秽晚,其可以提供請求路由瓦糟、協(xié)議轉(zhuǎn)換、安全認(rèn)證赴蝇、服務(wù)鑒權(quán)菩浙、流量控制等等一系列服務(wù)。之前spring cloud推薦的網(wǎng)關(guān)是Zuul句伶,它的1.0版本是基于阻塞IO的網(wǎng)關(guān)劲蜻,其性能相比spring cloud gateway是要差很多的,不過Zuul也出了2.0版本考余,基于Netty先嬉、非阻塞、支持長鏈接楚堤,性能會(huì)有較大提升疫蔓,只是目前spring cloud還沒有整合。
Gateway是建立在Spring WebFlux身冬、Spring Boot2.x衅胀、Project Reactor之上,這是一個(gè)全新的項(xiàng)目酥筝。不管是從性能還是說以后的可擴(kuò)展性考慮滚躯,我們項(xiàng)目組對原有項(xiàng)目的改造在網(wǎng)關(guān)上就選擇了使用GatewayGateway有幾個(gè)比較新的特征:1、基于java 8掸掏,2 茁影、基于Spring 5,Spring Boot 2.x;3、支持動(dòng)態(tài)路由等丧凤。這個(gè)我建議多看下文檔呼胚,介紹的比較詳細(xì),spring gateway 文檔息裸。我覺得看了文檔后使用上應(yīng)該沒有什么問題,下圖是Gateway的工作流程沪编『襞瑁客戶端請求會(huì)先打到Gateway,具體的講應(yīng)該是DispacherHandler(因?yàn)?code>Gateway引入了WebFlux蚁廓,作用可以類比MVC的DispacherServlet)访圃,Gateway根據(jù)用戶的請求找到相應(yīng)的HandlerMapping,請求和具體的handler之間有一個(gè)映射關(guān)系相嵌,網(wǎng)關(guān)會(huì)對請求進(jìn)行路由腿时,handler會(huì)匹配到RoutePredicateHandlerMapping,匹配請求對應(yīng)的Route饭宾,然后到達(dá)Web處理器批糟,WebHandler代理了一系列網(wǎng)關(guān)過濾器和全局過濾器的實(shí)例,這些過濾器可以對請求和響應(yīng)進(jìn)行修改看铆,最后由代理服務(wù)完成用戶請求徽鼎,并將結(jié)果返回。

圖-1.png

接下來我們還是創(chuàng)建一個(gè)項(xiàng)目弹惦,引入相關(guān)的依賴:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

之所以引入eureka是因?yàn)榫W(wǎng)關(guān)在微服務(wù)的架構(gòu)中也是作為一個(gè)服務(wù)存在的否淤,因此需要將gateway注冊到eureka。

自定義路由

gateway有幾個(gè)核心的概念棠隐,需要我們注意石抡,1:Route,即路由助泽,它是gateway非硢福基礎(chǔ)也非常重要的一個(gè)塊,主要由一個(gè)id报咳,一個(gè)目標(biāo)URI侠讯,一個(gè)predicates集合和一個(gè)filter集合幾部分組成。2暑刃、Predicate厢漩,這個(gè)java 8中的一個(gè)斷言函數(shù),它的返回結(jié)果是boolean岩臣,這里就不再介紹了溜嗜。它的入?yún)⑹?code>ServerWebExchange宵膨,它封裝了ServerHttpRequestServerHttpResponse,這樣開發(fā)過程中就可以針對用戶請求和響應(yīng)進(jìn)行一些操作炸宵。3:Filter辟躏,這些filter都是由相關(guān)的工廠創(chuàng)建 出來的GatewayFilter實(shí)例,在這些過濾器里面可以對請求和響應(yīng)進(jìn)行修改土全,然后傳遞給下游捎琐。
下面的配置文件就是自己定義的一個(gè)路由:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:9010
          predicates:
            - Path=/user/**
        - id: order-service
          uri: http://localhost:9020
          predicates:
            - Path=/order/**
            - Query=role,ABO

因?yàn)槲疫€沒有創(chuàng)建其他服務(wù),所以只是隨意配置了兩個(gè)路由裹匙,id屬性可以自己隨意指定瑞凑,如果不顯式指定的話就是一個(gè)UUID;uri是具體的目標(biāo)URI概页;predicates則是一個(gè)List籽御,可以定義多個(gè)不同的PredicateDefinition,即請求都會(huì)找到具體的路由信息惰匙,然后判斷是否滿足定義的predicates條件技掏。比如上面我定義的id為order-service的路由,在進(jìn)行路由時(shí)會(huì)判斷請求的path是不是滿足"/order/**"项鬼,且請求參數(shù)中是不是有參數(shù)名稱為"role"哑梳,且值為"ABO",只有同時(shí)滿足這兩個(gè)條件才對請求路由绘盟。因?yàn)樽约哼€沒完整的搭建好相關(guān)的服務(wù)涧衙,所以關(guān)于這塊只能后期完善之后在進(jìn)行測試了。
除了使用配置文件奥此,我們也可以通過配置類來完成這些配置內(nèi)容弧哎,比如下面使用代碼定義了上面配置文件的路由斷言,代碼如下:

@Configuration
public class GatewayConfig {

    @Bean
    public RouterFunction<ServerResponse> pathPredicate() {
        RouterFunction routerFunction = RouterFunctions.route(
                RequestPredicates.path("/user/**"),
                serverRequest -> ServerResponse.ok().body(BodyInserters.fromValue("custom path route predicate")));

        return routerFunction;
    }

    @Bean
    public RouterFunction<ServerResponse> pathAndQueryPredicate() {
        RouterFunction routerFunction = RouterFunctions.route(
                RequestPredicates.path("/order/**").and(RequestPredicates.queryParam("role",Predicate.isEqual("ABO"))),
                serverRequest -> ServerResponse.ok().body(BodyInserters.fromValue("custom path and query param route predicate")));

        return routerFunction;
    }
}

此外也可以通過代碼實(shí)現(xiàn)自定義的過濾器稚虎,如下:

    @Bean
    public RouteLocator customRoute(RouteLocatorBuilder locatorBuilder) {
        log.info(">>>> custom routeLocator <<<<");
        RouteLocator routeLocator = locatorBuilder.routes().route("custom-route",
                r -> r.path("/order/hello").
                        filters(f -> f.addRequestParameter("role","ABO")).uri("http://localhost:9010"))
                .build();

        return routeLocator;
    }

上面的代碼中自定義了一個(gè)過濾器撤嫩,當(dāng)請求為"/order/hello"時(shí),網(wǎng)關(guān)會(huì)將請求轉(zhuǎn)發(fā)給"localhost:9010"蠢终,且在會(huì)增加一個(gè)請求參數(shù)"role"序攘,其值為"ABO"。
關(guān)于使用上這里就不再介紹了寻拂,官方文檔非常的詳細(xì)程奠,需要的時(shí)候看下官方文檔我覺得應(yīng)該沒什么問題。有一點(diǎn)可能會(huì)比較好奇祭钉,就是微服務(wù)是有多個(gè)實(shí)例的瞄沙,那么網(wǎng)關(guān)在進(jìn)行路由的時(shí)候是怎么做負(fù)載均衡的呢?這一點(diǎn)其實(shí)只需要在自定義路由時(shí)進(jìn)行配置就可以了,依然用上面的配置文件舉例距境,我們需要對每個(gè)服務(wù)進(jìn)行路由配置申尼,比如有"user-service"和"order-service"兩個(gè)服務(wù),負(fù)載均衡可以如下:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user/**
        - id: order-service
          uri: lb://user-service
          predicates:
            - Path=/order/**
            - Query=role,ABO

這樣網(wǎng)關(guān)就可以對我們的路由進(jìn)行負(fù)載均衡垫桂,但是負(fù)載均衡的實(shí)現(xiàn)還不是很了解师幕,包括負(fù)載均衡的策略,這些都是以后自己需要學(xué)習(xí)的方向诬滩。

GlobalFilter

另外有一個(gè)很重要的一點(diǎn)霹粥,不管我們使用Gateway是做鑒權(quán)還是認(rèn)證都其實(shí)都需要一個(gè)過濾器作為入口,這個(gè)過濾器在Gateway中就是GlobalFilter疼鸟,這個(gè)接口的特殊之處在于它默認(rèn)的會(huì)應(yīng)用于所有的路由蒙挑,也就是說當(dāng)一個(gè)請求進(jìn)來之后,GlobalFilter的實(shí)例和其他的過濾器都會(huì)被添加到過濾器鏈愚臀,當(dāng)然這些過濾器鏈中的過濾器都是有序的,這些過濾器是按照org.springframework.core.Ordered進(jìn)行排序的矾利。另外一點(diǎn)姑裂,就是過濾器是雙向的,也就是說請求時(shí)第一個(gè)進(jìn)入的過濾器男旗,在響應(yīng)時(shí)是最后一個(gè)出去的舶斧。接下來我們代碼來實(shí)現(xiàn)一個(gè)GlobalFilter,代碼如下:

@Component
public class CustomFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest httpRequest = exchange.getRequest();
        ServerHttpResponse httpResponse = exchange.getResponse();
        // 業(yè)務(wù)代碼省略
        ....
        
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

在這個(gè)過濾器里面我們可以獲取到請求和響應(yīng)察皇,所以不管我們想針對用戶的某個(gè)請求或者響應(yīng)做一些特殊處理都可以在這里完成茴厉,使用上應(yīng)該還是很簡單的。


其實(shí)就使用上來講Gateway應(yīng)該沒什么問題什荣,而且官方的文檔內(nèi)容也是比較詳細(xì)的矾缓,但是比較遺憾的是我這次沒有準(zhǔn)備項(xiàng)目,按理講其實(shí)我應(yīng)該先準(zhǔn)備好幾個(gè)小的demo稻爬,然后再來學(xué)習(xí)Gateway會(huì)好一些嗜闻,比如我自定義的路由是不是生效,負(fù)載均衡有沒有問題桅锄,怎么自定義負(fù)載均衡策略琉雳,全局過濾器的使用等等。不過自己之后慢慢的會(huì)把微服務(wù)的各個(gè)組件會(huì)搭建起來友瘤,這些問題就以后再講吧翠肘。其實(shí)我比較關(guān)心的可能還是一些底層的實(shí)現(xiàn),特別是源碼部分辫秧,但是因?yàn)樽约阂彩亲罱佑|束倍,所以這里暫時(shí)不對源碼進(jìn)行分析了,等對Gateway熟悉之后,我們再來學(xué)習(xí)它的源碼肌幽。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晚碾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喂急,更是在濱河造成了極大的恐慌格嘁,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件廊移,死亡現(xiàn)場離奇詭異糕簿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狡孔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門懂诗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苗膝,你說我怎么就攤上這事殃恒。” “怎么了辱揭?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵离唐,是天一觀的道長。 經(jīng)常有香客問我问窃,道長亥鬓,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任域庇,我火速辦了婚禮嵌戈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘听皿。我一直安慰自己熟呛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布尉姨。 她就那樣靜靜地躺著惰拱,像睡著了一般。 火紅的嫁衣襯著肌膚如雪啊送。 梳的紋絲不亂的頭發(fā)上偿短,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機(jī)與錄音馋没,去河邊找鬼昔逗。 笑死,一個(gè)胖子當(dāng)著我的面吹牛篷朵,可吹牛的內(nèi)容都是我干的勾怒。 我是一名探鬼主播婆排,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼笔链!你這毒婦竟也來了段只?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鉴扫,失蹤者是張志新(化名)和其女友劉穎赞枕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坪创,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡炕婶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了莱预。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柠掂。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖依沮,靈堂內(nèi)的尸體忽然破棺而出涯贞,到底是詐尸還是另有隱情,我是刑警寧澤危喉,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布宋渔,位于F島的核電站,受9級特大地震影響姥饰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜孝治,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一列粪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧谈飒,春花似錦岂座、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至手素,卻和暖如春鸳址,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背泉懦。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工稿黍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人崩哩。 一個(gè)月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓巡球,卻偏偏與公主長得像言沐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子酣栈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容