一、概述
在微服務架構中,每個服務都是一個可以獨立開發和運行的組件,而一個完整的微服務架構由一系列獨立運行的微服務組成。其中每個服務都只會完成特定領域的功能,比如訂單服務提供與訂單業務場景有關的功能、商品服務提供商品展示功能等。各個微服務之間通過輕量級通信機制 REST API 或者 RPC 完成通信。 微服務之后在某些層面會帶來一定的影響,比如,一個用戶查看一個商品的詳情,對於客戶端來說,可能需要調用商品服務、評論服務、庫存服務、營銷服務等多個服務來完成數據的渲染在這個場景中,客戶端雖然能通過調用多個服務實現數據的獲取,但是會存在一 些問題,比如:
- 客戶端需要發起多次請求,增加了網絡通信的成本及客戶端處理的復雜性。
- 服務的鑒權會分布在每個微服務中處理,客戶端對於每個服務的調用都需要重復鑒權。
- 在后端的微服務架構中,可能不同的服務采用的協議不同,比如有 HTTP、RPC 等。客戶端如果需要調用多個服務,需要對不同協議進行適配
二、微服務網關的作用
所以,我們可以在微服務之前增加一個前置節點,這個節點就是網關,網關就是一個網絡連接到另一個網絡的“關口”。也就是網絡。而在微服務架構中,它不僅僅只是一個網絡互連的一個關口,還有更多的作用,以前面分析的這個場景為例,增加網關之后所有請求的下發都由網關下發;對於商品詳情展示的場景來說,增加了 API 網關之后,在 API 網關層可以把后端的多個服務進行整合,然后提供一個唯一的業務接口,客戶端只需要調用這個接口即可完成數據的獲取及展示。在網關中會再去消費后端的多個微服務進行統一的整合,給客戶端返回一個唯一的響應。去消費后端的多個微服務進行統一的整合,給客戶端返回一個唯一的響應。
- 針對所有請求進行統一鑒權、限流、熔斷、日志。
- 協議轉化。針對后端多種不同的協議,在網關層統一處理后以 HTTP 協議對外提供服務。
- 用過 Dubbo 框架的應該知道,針對 Dubbo 服務還需要提供一個 Web 應用來進行協議轉化。
- 統一錯誤碼處理。
- 請求轉發,並且可以基於網關實現內外網隔離
2.1、網關的作用
- 性能:API高可用,負載均衡,容錯機制。
- 安全:權限身份認證、脫敏,流量清洗,后端簽名(保證全鏈路可信調用),黑名單(非法調用的限制)。
- 日志:日志記錄(spainid,traceid)一旦涉及分布式,全鏈路跟蹤必不可少。
- 緩存:數據緩存。
- 監控:記錄請求響應數據,api耗時分析,性能監控。
- 限流:流量控制,錯峰流控,可以定義多種限流規則。
- 灰度:線上灰度部署,可以減小風險。
- 路由:動態路由規則。
三、服務網關的要求
從上面的案例來看,網關成了所有流量的入口,那么對於這樣一個角色來說,它需要在某些方面有很高的要求
- 穩定性,
- 安全性,防止惡意請求,以及保障數據傳輸的安全性
- 高性能、可用性,
- 網關作為所有流量的入口,那么對於性能這塊的要求就非常高了,因為一旦網關的性能出現瓶頸,就算后端的服務性能再高,意義也不大
- 網關必須要支持集群部署,這個是分布式架構的基本要求。否則網關服務掛掉就會導致整個系統不可用
- 擴展性,可維護性,對於定制化需求方面,如何實現可擴展;
常見的網關方案
- OpenResty(Nginx+lua)
- Kong,是基於openresty之上的一個封裝,提供了更簡單的配置方式。 它還提供了付費的商業插件
- Tyk(開源、輕量級),Tyk 是一個開源的、輕量級的、快速可伸縮的 API 網關,支持配額和速度限制,支持認證和數據分析,支持多用戶多組織,提供全 RESTful API。它是基於go語言開發的組件。
- Zuul,是spring cloud生態下提供的一個網關服務,性能相對來說不是很高
- Spring Cloud Gateway,是Spring團隊開發的高性能網關
網關選型
- 部署和維護成本
- 開源還是閉源
- 是否私有化部署
- 功能是否滿足當前需求
- 社區資料的完善以及版本迭代和功能維護
四、Spring Cloud Gateway的核心概念
- Route 路由,它是網關的基礎元素,包含ID、目標URI、斷言、過濾器組成,當前請求到達網關時,會通過Gateway Handler Mapping,基於斷言進行路由匹配,當斷言為true時,匹配到路由進行轉發
- Predicate,斷言,學過java8的應該知道這個函數,它可以允許開發人員去匹配HTTP請求中的元素,一旦匹配為true,則表示匹配到合適的路由進行轉發
- Filter,過濾器,可以在請求發出的前后進行一些業務上的處理,比如授權、埋點、限流等。
它的整體工作原理如下。
其中,predicate就是我們的匹配條件;而filter,就可以理解為一個無所不能的攔截器。有了這兩個元素,再加上目標uri,就可以實現一個具體的路由了。客戶端向 Spring Cloud Gateway 發出請求,如果請求與網關程序定義的路由匹配,則該請求就會被發送到網關 Web 處理程序,此時處理程序運行特定的請求過濾器鏈。過濾器之間用虛線分開的原因是過濾器可能會在發送代理請求的前后執行邏輯。所有 pre 過濾器邏輯先執行,然后執行代理請求;代理請求完成后,執行 post 過濾器邏輯
五、應用實戰
新建一個spring-cloud-gateway服務
在spring-cloud-gateway服務中導入以下包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
然后在配置文件中配置如下
server: port: 9100 spring: application: name: spring-cloud-gateway cloud: gateway: routes: - predicates: - Path=/service/** filters: #過濾 - StripPrefix=1 uri: http://localhost:8080/ eureka: instance: hostname: localhost client: serviceUrl: defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/ #指向服務注冊中心的地址
啟動項目通過網關訪問業務服務,發現可以直接通過網關的端口發起訪問
5.1、斷言
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-after-route-predicate-factory
這是官方提供的11種斷言方法,有興趣可以按官網的去配置玩下,cloud整個體系配置相對來說很簡單的
下面就寫一個關於常用的Cookie配置下;
5.2、自定義斷言
如果官網提供的斷言不滿足自己的要求,官網也支持自定義斷言;自定義斷言要繼承AbstractRoutePredicateFactory這個抽象工廠,我們自定義的斷言類名后綴必須是RoutePredicateFactory;自定義也很簡單也就是看別人怎么寫的然后抄就完了
然后把之前測試工具的Cookies刪除,調用成功
5.3、GatewayFilter
Factories
官網有說明這玩意其實就是一個路由過濾器工廠。官網提供了31種,建議大家看下;下面還是和前面一樣先抄下源碼自定義一個看怎么玩,隨便選一個過濾器,例如
Retry GatewayFilter
Factory這個過濾器
類命名要求和以前一樣后綴要相同
然后加上我們自定義的過濾器,隨便找個地方加上就行
然后訪問瀏覽器
再看控制台,這里我沒寫過濾邏輯,但進來了說明如果官網的過濾邏輯用的不爽或者公司想定義一個自己的規則,那就可以玩了
前面玩完了GlobalFilter現在就玩下RouteFiler;前面路由是通過HTTP+端口號的方式綁定業務服務的,這種方式是不可取的,第一端口改變了,那網關上配置也跟着改,第二個問題就是,你這么玩不能做負載均衡呀,能不能像zuul像配置服務名就能找到業務服務,還真可以,配置如果下
然后訪問服務,如果是多個節點可以發現做負載了
最后再來說下比較常用的另一個東西:限流器;https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#the-requestratelimiter-gatewayfilter-factory操作官方說的很清楚
然后自己寫個類寫限制規則
因為電腦重裝后我虛擬機都清零了,所以redis都沒了,今天懶得搞環境了,如果我記得沒錯的話,在瀏覽器上訪問http://localhost:9100/requestratelimiter/user這個路徑過快的話,令牌不夠會報429的錯,有興趣的可以自己驗證下
5.3、動態路由
https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#actuator-api
在現在的getway中我是把所有路由規則寫死在配置文件中,但是在有些業務場景中,需要將路由規則存到配置中心,或者是存在數據庫中,這個需求就引出了動態路由;這個在getway中有提供接口,但需要我們自己實現;要想實現這功能也要導入下面包
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
然后在網關的配置中加入以下內容,看過前面的應該知道這配置作用這里就不說明了
management: endpoints: web: exposure: include: "*"
搞完后看官網,官網有說怎么看getway的網關路由的元數據信息
里面也有說明創建和刪除路由規則的操作說明
那按官網來玩下,按以下配置然后執行下
然后按官網要求刷新下
再訪問瀏覽器,剛剛添加的路由上去了
雖然這樣可以添加和刪除路由,但現在這操作是存在內存中,如果服務重啟的話就都白搞了,所以要想持久化保存就要換個方法;其實我們剛剛所有操作的接口都在GatewayControllerEndpoint類中能找到,看它繼承的AbstractGatewayControllerEndpoint類,里面有個觸點RouteDefinitionWriter
通過類關系圖我們可以找到它的最終實現InMemoryRouteDefinitionRepository類,這個類里面的操作是基於內存的定義,看到這里,靈感應該來了,你竟然能實現基於內存的定義,那我也來繼承自己造個輪子來基於數據庫、緩存不就完了唄
下面來擴展下RouteDefinitionRepository,下面操作完成后,后面增加的信息就存在緩存服務器里面了