從源碼分析微服務網關的設計
微服務網關的功能
一個高性能的分佈式網關,負責將 HTTP 請求轉發到 RPC 服務、進行接口權限校驗、反作弊攔截等相關功能,如下圖所示。
定長 Header 包含的內容如上圖所示。
網關很重要的一個作用就是將外部的 HTTP 請求轉化爲內部的 RPC 請求。
RPC 的架構可以看下圖:
https://www.huaweicloud.com/articles/8502a2cd50c9ade2327ca09ae0897fe1.html
RPC 有如下框架:
目前 RPC 的實現有兩大類。
跟語言平臺綁定的開源 RPC 框架主要有下面幾種。
-
Dubbo:國內最早開源的 RPC 框架,由阿里巴巴公司開發並於 2011 年末對外開源,僅支持 Java 語言。
-
Motan:微博內部使用的 RPC 框架,於 2016 年對外開源,僅支持 Java 語言。
-
Tars:騰訊內部使用的 RPC 框架,於 2017 年對外開源,僅支持 C++ 語言。
-
Spring Cloud:國外 Pivotal 公司 2014 年對外開源的 RPC 框架,僅支持 Java 語言
而跨語言平臺的開源 RPC 框架主要有以下幾種。
-
gRPC:Google 於 2015 年對外開源的跨語言 RPC 框架,支持多種語言。
-
Thrift:最初是由 Facebook 開發的內部系統跨語言的 RPC 框架,2007 年貢獻給了 Apache 基金,成爲 Apache 開源項目之一,支持多種語言。
-
hprose:一個 MIT 開源許可的新型輕量級跨語言跨平臺的面向對象的高性能遠程動態通訊中間件。它支持衆多語言: nodeJs, C++, .NET, Java, Delphi, Objective-C, ActionScript, JavaScript, ASP, PHP, Python, Ruby, Perl, Golang。
微服務網關的高性能設計
在介紹了 API 網關基本功能過後,接下來我們介紹設計出高性能的分佈式網關的考慮點。
從增加吞吐量、降低延時的角度,實現網關的無狀態化是很關鍵的。
接下來,我們先看 SpringBoot 的 WebServer 實現網關。
在上圖中,Serverlet 好理解,不再贅述。
Filter 中包含跨域、反作弊、Session 鑑權三大功能。
- 什麼是跨域?
跨域指的是跨網絡 domain 訪問。例如客戶端是 1.david.com。與,目標端是 10.wei.com。從 david.com 到 wei.com 的訪問就叫跨域訪問。
- 什麼是反作弊 / 風控?
客戶端有沒有權限,哪些用戶(uid)是黑名單,哪些 ip 是黑名單,哪些 token 是黑名單?
這些沒名單最好加載到網關的本地緩存。因此,在網關層內部,通常設置一個本地緩存,用於存儲 hashmap。這樣客戶端請求到來以後,去 haspmap 去匹配。黑名單中存在則拒絕,不存在則放行。如果緩存不放在本地,會增加相響應延遲。但緩存中的內容需要定期更新。因此需要異步加載的機制。異步加載機制是異步計算出黑名單(通過訪問記錄的 spark 計算)
接下來,我們查看網關時序圖:
跨域的實現
接下來,我們先看一下跨域的代碼實現:
-
cross-origin resources sharing CROS:建立豁免清單
-
access-control-allow-credentials 是否允許 cookike 跨域
-
access-control-allow-origin 對 http 請求頭設置豁免
-
access-control-allow-method 允許提交請求的方法
建立豁免清單:在代碼中,開啓跨域訪問,允許源端爲 1.david.com 的域訪問請求。也就是說從這個源加載腳本或者 API,可以訪問目標域。
public class OptionCheckFilter extends BaseFilter {
private static final String ALLOWED_HOST = "http://1.david.com";
容許Cookie跨域,做Session檢查
Session 的無狀態設計
接下來,我們看 session 設計。
讀寫請求的上下文對象,我們稱之爲 session(username+password)。
session 和登錄用戶數有關,有一個用戶登錄就有一個 session。如果允許一個用戶多端登錄,那 session 會更多。因此 session 不能在網關上單機存儲。需要設置 session 的分佈式。
Session 的綁定。這種方法是有狀態的,不靠譜。
Session 全複製,session 多了後,這個方案不靠譜!
通過 Redis 存儲 session:
Session 存儲在客戶端:
接下來,我們介紹一下 Session 的生成算法。
Session 是遺傳具有一定時效性的加密字符串字符串(通常有效期 7 天),通常由服務端生成和解析。
Session 通常包含的字段有:deviceid、clientType、uid、ts、checksum 等。總之要和業務相關。加密方案通常採用 AES,加密和解析都在服務端。
網關的反作弊 / 風控設計
針對惡意流量,從網關層面進行攔截,反正對後端服務造成高併發壓力。Eileen 防止誤傷,對黑名單數據定期釋放的能力。通過黑名單,規避爬蟲、網絡攻擊的問題。
黑名單可以緩存在網關內部的緩存,使用 Key-List 存儲。
在上面的方案中,如果業務量特別大時,使用進程內緩存就不靠譜。就需要將黑名單存在外部緩存,但這時候可以增加一個布隆過濾器。(https://juejin.cn/post/6844904007790673933)
網關的路由邏輯設計
訪問請求通過網關的 Filter 層以後,就會進入 Controller 層。這時會先進行請求參數處理。對請求做一個解析,拿出請求的 URI 和 parm 參數。接下來通過 RPC 的框架調用 ServiceName,然後調用 Service 的 Method。
也就是,首先建立 URI 和 Service 的聯繫,然後建立 URI 和 Method 的聯繫。而 URI 就是一個請求包裏的 CMD。
上圖的核心實現:
-
掃描出 URI 對 ServiceName 的映射
-
RPC 通過反射實現遠程調用(通過 Invoker)
下面代碼是業務邏輯層的代碼。
客戶端調用方法如下:
api.wei.com/david/getSettleBillDetaild
網關通過掃描註解獲取映射關係。
下面代碼是網關上的。
路由的本質是通過 URI 找到 method,通過反射進行 Service 的調用
但是,以上方法很不優雅。主要問題在於,當我們業務邏輯層增加新的模塊時,需要重啓網關。
我們在業務邏輯層增加 Proxy,他負責 URI->Class.method 的映射。將 URI 到 ServerName 的映射放到存儲層,如 MySQL 中。
通過上圖展示的邏輯,當業務邏輯層有新的模塊,網關也不需要重啓生效。因爲 Proxy 會自動上報給存儲層,而網關會定期自動掃描存儲層。
我們以客戶端請求如下鏈接爲例:10.wei.com/user/get?uid=1
最後,我們看一下 proxy 的啓動和代理階段:
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/lf5Vo-DoBGAS9WzyfbBCQw