從0開始學(xué)架構(gòu): 3. 微服務(wù)網(wǎng)關(guān)源碼剖析

一、微服務(wù)網(wǎng)關(guān)層的整體架構(gòu)思考

先回顧下網(wǎng)關(guān)層的功能:

1.請求鑒權(quán)

發(fā)布商品,登錄鑒權(quán)。

2. 數(shù)據(jù)完整性檢查

數(shù)據(jù)定長Header,變長body

3. 協(xié)議轉(zhuǎn)換

JSON-> HashMap(string,obj)
如果value不支持json嵌套的話,就string就可以。hashmap(string,string)

4. 路由轉(zhuǎn)發(fā)

根據(jù)CMD轉(zhuǎn)發(fā)到不同業(yè)務(wù)邏輯層

5. 服務(wù)治理

限流,降級,熔斷等。

其中最核心的是1和4.
1涉及到session存到哪,
4涉及到如何把眾多的HTTP請求路由到邏輯層的RPC接口。新上一個業(yè)務(wù)(邏輯層),網(wǎng)關(guān)不需要的重啟的情況下如何發(fā)現(xiàn)它。

二、自研網(wǎng)關(guān)的各個關(guān)鍵點

2.1 自研網(wǎng)關(guān)需求剖析

打造一個高性能的分布式網(wǎng)關(guān)(SaaS),實現(xiàn)HTTP請求轉(zhuǎn)發(fā)到RPC服務(wù)、接口請求鑒權(quán)、反作弊(風(fēng)控、antispam)等相關(guān)功能。

要實現(xiàn)如下功能:

  1. 高性能分布式模塊(這里模塊指的Process)
  2. 鑒權(quán)功能
  3. 路由能力
  4. 簡單實用的反作弊

落實到技術(shù)層面,解決方案如下:

  1. 無狀態(tài)設(shè)計
  2. 過濾器責(zé)任鏈設(shè)計(攔截器)
  3. 反作弊設(shè)計(攔截器)
    可以理解為責(zé)任鏈的一部分
  4. 路由方案設(shè)計

架構(gòu)參考

整體架構(gòu)基于Spring boot框架。


image.png
  • 網(wǎng)關(guān)本質(zhì)上是一個websever。

  • 注意上面的Filter責(zé)任鏈模式。 跨域問題
    比如,訪問www.baidu.com,調(diào)用的js中有www.iqiyi.com。那么到了iqiyi的網(wǎng)關(guān)層,它發(fā)現(xiàn)主域名是來自于其他網(wǎng)站,就會考慮讓不讓它訪問iqiyi,這就是一個跨域問題。

  • 網(wǎng)關(guān)屬于高并發(fā)模塊
    所以要邏輯簡單,業(yè)務(wù)邏輯剝離到logic層

  • 緩存設(shè)計、異步線程設(shè)計
    反作弊模塊,希望有一個黑名單緩存,比如map或set。(因為和外部交互影響性能)。黑名單應(yīng)該是由數(shù)據(jù)分析得到的,比如放到redis里。那么我如何在進(jìn)程內(nèi)緩存呢?
    這時就要一個異步加載機(jī)制,比如每隔5s從redis里讀出來覆蓋set。(最終一致性),即進(jìn)程內(nèi)和進(jìn)程外通訊。

  • 進(jìn)程外緩存,配置平臺
    進(jìn)程外緩存就指的redis集群
    配置平臺是路由邏輯用的,后面詳解。

image.png

上面的圖不對,需要換一下

2.2 跨域問題

從一個源(www.baidu.com)加載的文檔或腳本(js),不能訪問另一個源(www.iqiyi.com)的資源。

image.png

  • Access-Control-Allow-Origin
    栗子:
    比如baidu的網(wǎng)頁想嵌入iqiyi的一個url資源,那么它會先去iqiyi的網(wǎng)關(guān)層探測我能不能訪問你的資源,如果iqiyi允許的話,會在網(wǎng)關(guān)層建立一個豁免清單(允許哪些第三方的域名訪問)。

  • 跨cookie Access-control-allow-credentials
    cokie是種在url里的,比如種在api1.baidu.com里的,那么api2.baidu.com就不行了。如果設(shè)置為允許跨cokie,那么cokie在任何一個二級域名里都可以(*.baidu.com).

  • 允許哪些方法進(jìn)行跨域訪問

2.3 Session設(shè)計

image.png

2.3.1 session綁定

本質(zhì)就是將uid hash到固定的節(jié)點。
問題是高可用怎么保證呢? session一般是沒必要持久化的。
思路是網(wǎng)關(guān)冗余,模仿數(shù)據(jù)庫主從復(fù)制。


image.png

但是,網(wǎng)關(guān)間的數(shù)據(jù)復(fù)制如何設(shè)計呢?(要考慮vip、主從復(fù)制等,簡單問題復(fù)制化了)。所以session綁定在工業(yè)界很少用。
所以一般用session綁定

2.3.2 session 復(fù)制

image.png

而且是最終一致性的,存在session重寫。這種方案也是不合理的。

2.3.3 session共享

image.png

這里的redis是redis cluster。
使用緩存服務(wù)Redis,統(tǒng)一存儲Session。

優(yōu)點

  • 網(wǎng)關(guān)層無狀態(tài)
  • 緩存服務(wù)本身高可用

缺點

  • 多一次服務(wù)調(diào)用IO
    思考如果減少IO調(diào)用,且網(wǎng)關(guān)是無狀態(tài)化的。很自然的想到session存在客戶端。

2.3.4 Session 客戶端緩存

image.png

可以存在app的sqlite里
優(yōu)點:

  • 簡單高性能
  • 網(wǎng)關(guān)層無狀態(tài)
    缺點:
  • 依賴客戶端Cookie(session)存儲
    每次帶cookie,消耗一些app端的流量。

這個用的比較多,因為響應(yīng)延遲低,且高可用。

2.3.5 session 生成算法

image.png

session=AES(uid + TTL + checksum)
用戶第一次登錄時,網(wǎng)關(guān)生成session,然后返回給客戶端,之后每次登錄時帶著這個session。
本地算法,AES解密看TTL是否過期,checksum是否正確。

遠(yuǎn)程校驗,是因為session在很多公司是在業(yè)務(wù)邏輯層生成的(因為更多的是業(yè)務(wù)邏輯的判斷),而不在網(wǎng)關(guān)層生成。TTL過期后要到業(yè)務(wù)邏輯層去校驗,業(yè)務(wù)邏輯層返回給GW一個新的session,GW返回給APP。這個可能很少概率。

2.3.6 session 攔截器

image.png

攔截器中會生成logid,logid是app請求的唯一標(biāo)識。

以上主要兩部分功能,1 是session校驗,2是生成logid。

2.4 網(wǎng)關(guān)層反作弊需求

  • 針對惡意流量,從網(wǎng)關(guān)層面進(jìn)行攔截,防止對后端服務(wù)造成高并發(fā)壓力
  • 為了防止誤傷,對于黑名單數(shù)據(jù)定期釋放能力


    image.png

分析:

  1. 初期階段
    數(shù)據(jù)量小的時候,可以內(nèi)存中l(wèi)ocal cache。

  2. 高可用設(shè)計
    進(jìn)程內(nèi)緩存解決不了高可用

  3. 黑名單
    持久化怎么做

image.png

上面是數(shù)據(jù)量比較小的時候,那么數(shù)據(jù)量大的時候怎么做,幾十G的話進(jìn)程內(nèi)緩存不了。
風(fēng)控挖掘的時候可以存在redis里,但是為了這種小概率的事讀redis會導(dǎo)致耗時增加。
所以可以用布隆過濾器。

思考

  • 數(shù)據(jù)量大怎么做(布隆過濾器)
    如果每次讀redis,為了這小概率事件開銷太大
    但是布隆過濾器具體怎么做?后面詳解
  • 實時性怎么破?
    如果5s拉一次可能不夠?qū)崟r,如果實時性要求高,可以改成redis有更新然后push的方式。

2.5 自研網(wǎng)關(guān)各個擊破:之路由

image.png

RPC客戶端比如Dubbo,用來和網(wǎng)關(guān)后面的業(yè)務(wù)邏輯層的RPC services通信。

架構(gòu)設(shè)計原則

  1. 初期階段約定大于配置:
    url什么規(guī)范,接口的約定。

  2. 服務(wù)端簡潔化設(shè)計

  • 功能夠簡單
  • 使用用夠方便

負(fù)載均衡和服務(wù)發(fā)現(xiàn)

  • RPC框架實現(xiàn)

熔斷設(shè)計

Hystrix

協(xié)議約定

  • 網(wǎng)關(guān)和APP間的數(shù)據(jù)協(xié)議是JSON
  • 網(wǎng)關(guān)層調(diào)用業(yè)務(wù)邏輯層入?yún)⒔y(tǒng)一為Map<string,string>
  • 業(yè)務(wù)邏輯層返回統(tǒng)一的Result對象{code,data,msg}
    code:0, data:xxx, msg: success
    網(wǎng)關(guān)層拿到后再轉(zhuǎn)化成json,然后返回給APP。

2.5.1 路由設(shè)計

image.png

上圖其中Sevrice比如分別對應(yīng)用戶、商品、交易的logic。
其中RPC框架比如是Dubbo的RPC客戶端,調(diào)用Dubbo的RPC服務(wù)端(即后端的logic)。

uri->Service(比如User logic),Method對應(yīng)User logic中的get user方法。
這塊主要做兩件事:

  1. 建立url和服務(wù)間的映射關(guān)系(服務(wù)啟動初始化,內(nèi)存里建立映射)
  2. RPC通過反射實現(xiàn)遠(yuǎn)程調(diào)用。(反射生成對應(yīng)的對象供調(diào)用)

25.2 具體實現(xiàn):

image.png
image.png
image.png

上面講的其實網(wǎng)關(guān)1.0的路由實現(xiàn),有如下幾個問題:

  1. 啟動耗時大
    比如有幾百個業(yè)務(wù)邏輯,都去網(wǎng)關(guān)建立路由映射關(guān)系,那么服務(wù)啟動初始化的時候就會非常耗時。

  2. 業(yè)務(wù)升級需要重啟,耦合嚴(yán)重,降低開發(fā)效率。
    比如新加個服務(wù),需要重新到網(wǎng)關(guān)層注冊(建立路由映射),那么網(wǎng)關(guān)就需要重啟進(jìn)程。

那么2.0就需要解決這兩個問題。

2.6 網(wǎng)關(guān)2.0

image.png
image.png
  • 路由到通用接口。接口再去路由到具體的服務(wù)。
    這樣就不需要引入service,不需要重啟?;诮涌诰幊?。
    proxy每增加一個服務(wù)的時候,需要上報CLASS和Method給存儲層(Mysql),網(wǎng)關(guān)層每隔5s掃描存儲層加載到內(nèi)存里。這樣網(wǎng)關(guān)層根據(jù)CMD對應(yīng)的Class和Method拿到Service IP,然后調(diào)用固定的接口。
image.png
image.png

思考 網(wǎng)關(guān)3.0的演進(jìn)

  • 進(jìn)一步解耦如何做?
    一定需要mysql作為存儲層嗎? 可否用注冊中心或配置中心來做?
  • 泛化調(diào)用的邏輯?

三、開源框架推薦

image.png
image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容