Spring Cloud - 網關

語言: CN / TW / HK

老張正準備下班,路過小齊的位置,發現他正在閲讀Spring Cloud Gataway的官方文檔

看了看時間,等公交車還有一會兒,順便就和小齊交流一下,便問道:“你在學習Spring Cloud網關嗎?”

小齊聽到聲音後轉過身來,便看到老張拉了一把椅子坐下。正好自己有一些疑惑的地方,也想找人交流學習一下。

“是啊,我最近不是在學習微服務這部分知識嗎,現在學到網關這部分了。其實我不是特別明白,為什麼我們的微服務需要網關呢?”

什麼是網關

老張神祕一笑,説道:“我給你看兩張圖你就明白了。”然後在小齊的電腦上搜出了這篇文章裏面的兩張圖。

“我們假設你現在要做一個電商應用,前端是移動端的APP,後端是各種微服務。那你可能某個頁面需要調用多個服務的數據來展示。如果沒有網關,你的系統看起來就是這個樣子的:”

沒有網關
沒有網關

“而如果加上了網關,你的系統就會變成這個樣子:”

有網關
有網關

“嗯,這看起來是一個‘高內聚,低耦合’的思想,如果不使用網關,可能有什麼壞處呢?”小齊問道。

“這個其實從剛剛的第一張圖你就看得出來,如果沒有網關,你的客户端就會變得非常麻煩。對每一個微服務,可能都要做權限認證、負載均衡等工作;而且後端的微服務可能還使用了不同的消息通信協議;最重要的是,如果你想對微服務進行重構,比如拆分或者合併為服務器,那也必須得修改客户端,增加了複雜性。”

網關的技術選型

小齊:“那看來網關確實是至關重要,現在市面上有哪些微服務網關呢?”

“我們在實現網關的時候,需要考慮到的是它的性能和可擴展性。對於現成的產品來説,NGINX Plus是一個很不錯的選擇,NGINX Plus 提供了一個成熟的、可擴展的、高性能 web 服務器和一個易於部署的、可配置可編程的反向代理。NGINX Plus 可以管理身份驗證、訪問控制、負載均衡請求、緩存響應,並提供應用程序可感知的健康檢查和監控。除此之外,你還可以使用Node.js或者Netty根據自己的需求去自己寫一個網關。而且Spring Cloud也提供了一些已經寫好的微服務網關框架,你知道有哪些嗎?”

“我查了一下,好像Spring Cloud Gataway和Netflix的Zuul都挺不錯的。但Netflix的套件暫停開發了,所以我現在主要在看Spring Cloud Gateway。”

老張點點頭,道:“是的,Netflix很多組件到停止開發了。前兩天我們討論的Hystrix也是,但是Hystrix的替代產品還在孵化中。但網關方面,Spring Cloud Gateway已經孵化完成了,可以用來替代Zuul。那你知道Spring Cloud Gateway的原理嗎?”

Spring Cloud Gateway

小齊把剛剛自己正在看的官方文檔打開,然後給老張看了官網文檔的這個圖,一邊説道:“Spring Cloud Gateway是基於Spring Boot 2.0, Spring WebFlux, Reactor項目構建的,底層是Netty。它的流程其實和Zuul很相似,使用一系列的過濾器去處理請求,達到我們自己想要的定製化。”

原理圖
原理圖

老張問:“那裏剛剛看完了官方文檔,可以跟我總結一下它有哪些功能嗎?”

“Spring Cloud Gateway幾乎可以滿足絕大多數網關的需求。它主要有下面一些功能:”

斷言 Predicate

斷言可以指定在什麼情況下可以匹配某個路由。斷言是基於Java 8提供的Predicate接口。Gateway(以下簡稱Spring Cloud Gateway為Gateway)提供了一些默認的斷言。你可以在官方文檔查看具體有哪些斷言以及它們的用法。

支持時間、Cookie、Header、Host、HTTP Method、相對路徑、URL查詢語句、遠程IP等來做斷言。比如:

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://example.org
        predicates:
        - Method=GET
複製代碼

網關過濾器 GatewayFilter

Gateway也提供了一些已經寫好的過濾器。可以查看官方文檔。主要有重寫request或者response的header、parameter、body、session、安全、重寫路徑、重試以及對熔斷的一些支持。

全局過濾器 Global Filters

全局過濾器與網關過濾器類似,只是默認會給所有的請求都添加上全局過濾器。當然,也可以自己定義全局過濾器。過濾器的順序可以通過@Order註解來指定:

@Bean
@Order(-1)
public GlobalFilter a() {
    return (exchange, chain) -> {
        log.info("first pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("third post filter");
        }));
    };
}

@Bean
@Order(0)
public GlobalFilter b() {
    return (exchange, chain) -> {
        log.info("second pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("second post filter");
        }));
    };
}

@Bean
@Order(1)
public GlobalFilter c() {
    return (exchange, chain) -> {
        log.info("third pre filter");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("first post filter");
        }));
    };
}
複製代碼

其它功能

Gateway還提供了其它很多功能,比如説SSL方面的配置、基於netty的access log、跨域、actuator端點等等。具體的用法可以參考官方文檔。

尤其是actuator端點,它可以讓我們使用HTTP請求去動態地獲取、新增、刪除路由。具體的使用方法和參數查看官方文檔

定製化

除此以外,Gateway提供了一些接口和抽象類。用户還可以自己定製化斷言、Filter、路由等,實現更個性化的要求。參考文檔在這裏,但是目前文檔還不完善,可以自己看看源碼。

與註冊中心配合

老張:“嗯,上面基本上把Spring Cloud Gateway的功能總結全了。它還可以與Spring Cloud的註冊中心無縫集成。”

小齊接着老張的話,説道:“是的,Gateway可以很方便地與註冊中心配合起來使用,默認使用Ribbon實現了負載均衡。你只需要加入某個註冊中心的依賴,比如consul-discovery,然後把配置打開就行了:”

spring.cloud.gateway.discovery.locator.enabled=true
複製代碼

“這樣你就可以請求Gataway的/service-id/path來訪問服務名叫service-id的某個微服務的/path路徑了。比如:”

http://gateway:8080/service-user/hello

->

http://service-user:8080/hello

也可以對某個微服務做個性化的配置,參考文檔

網關本身的負載均衡

説完後,小齊問道:“那所有微服務就只有一個網關,萬一併發量上去了,網關承受不住怎麼辦?”

老張笑道:“正如你上面所説,Spring Cloud Gateway底層是Netty的,它本身就能承受比較大的併發。如果還是承受不了併發量,那可以註冊多個Gateway實例,然後在前面弄一個Nginx或者F5等負載均衡器。大概圖是這樣:”

網關負載均衡
網關負載均衡

老張看了看時間,差不多該去趕車了。“今天就到這吧,下次再交流~”

關於作者

微信公眾號:編了個程

個人網站:http://yasinshaw.com

筆名Yasin,一個有深度,有態度,有温度的程序員。工作之餘分享編程技術和生活,如果喜歡我的文章,可以順手關注一下公眾號,也歡迎轉發分享給你的朋友~

在公眾號回覆“面試”或者“學習”可以領取相應的資源哦~

公眾號
公眾號