網關:Zuul

來源:
《微服務架構實戰160講》
https://www.jianshu.com/p/d1e61f9fc13a?utm_source=oschina-app

API網關的基本功能:
在這裏插入圖片描述

Zuul網關

1 場景

Zuul實現了跨區高可用、防爬防攻擊、健康檢查、屏蔽壞節點、流量判斷等諸多功能

  • API網關

類似單體應用中的過濾器,在請求到達業務服務器之前可以做一些操作

  • 紅綠部署

類似配置中心Apollo可以實現的藍綠部署(流量判斷)

  • 開發者測試分支

也是通過流量判斷,使部分流量達到測試代碼而不是全部

  • 埋點測試

也是通過流量判斷,使部分流量達到埋點代碼而不是全部

  • 壓力測試

也是通過流量判斷

  • 調試路由

也是通過流量判斷,將代碼放到線上來調試

  • 金絲雀測試

也是通過流量判斷,部分流量測試金絲雀代碼,如果沒問題,再全量切換

  • 粘性金絲雀

同金絲雀測試,不過固定了用戶,如果第一次到達了金絲雀代碼,下一次就依然會到達此處,而不是隨機分配

  • 失敗注入測試

注入錯誤,看看業務服務器的容錯性

  • 降級測試

網關檢測到錯誤後,能執行降級,將流量打向降級的集羣,可以跟失敗注入測試結合來測試降級能力

2 架構設計+源碼簡析(1.0)

Zuul網關的核心是一系列的過濾器,可以對請求或者響應結果做一系列過濾,在zuul中過濾器分爲四種類型(Type),每一種Type中可以有多個攔截器,開發人員可自行指定執行順序(ExecutionOrder),只要滿足指定的條件(Criteria),就執行指定的動作(Action):

1 PRE Filters(前置過濾器) :當請求會路由轉發到具體後端服務器前執行的過濾器,比如鑑權過濾器,日誌過濾器,路由選擇過濾器

2 ROUTING Filters (路由過濾器):該過濾器作用是把請求具體轉發到後端服務器上,一般是通過Apache HttpClient 或者 Netflix Ribbon把請求發送到具體的後端服務器上

3 POST Filters(後置過濾器):當把請求路由到具體後端服務器後執行的過濾器,可以添加標準http 響應頭、收集一些統計數據(比如請求耗時等)、寫入請求結果到請求方等。

4 ERROR Filters(錯誤過濾器):當任何一個其他類型過濾器執行出錯時候執行該過濾器

  • 總體架構+原理
    在這裏插入圖片描述
    開發/運維人員通過管理模塊來管理過濾器,可以設置不同的狀態,如用於金絲雀測試的狀態等,然後存儲到FilterPersister中,如數據庫,需要做一個表出來存儲

FilterPoller會定期掃描FilterPersister(通過ZuulFilterDaoFactory,工廠模式),加載新的過濾器進FilterDirectory,不同類型的過濾器有各自的目錄,FileManager組合了這幾個目錄,使用一個線程掃描FilterDirectory,加載到FilterLoader中,最終送到FilterRunner中

FilterRunner則完成所有請求流程的過濾,是最核心的組件

請求入口:ZuulServlet本質是一個HttpServlet,會攔截所有請求,執行四個過濾器方法,方法內部會將具體實現交給FilterRunner(委派模式),FilterRunner則交給FilterProcessor真正執行對應類型的過濾器(從FilterLoader中取出相應類型的過濾器,使用for循環依次處理,爲責任鏈模式)

過濾器的執行順序:ZuulServlet#service

public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    ...
    try {
        preRoute();
    } catch (ZuulException e) {
        error(e);
        postRoute();
        return;
    }
    try {
        route();
    } catch (ZuulException e) {
        error(e);
        postRoute();
        return;
    }
    try {
        postRoute();
    } catch (ZuulException e) {
        error(e);
        return;
    }
    ...
}

RequestContext存儲整個請求的一些數據,使得過濾器之間可以共享數據,且線程安全,即每個請求有各自的局部RequestContext,由所有過濾器共享(其實就是一個請求裏的數據共享)
RequestContext使用ThreadLocal實現,其本身簡單wrap了ConcurrentHashMap,核心源碼:

public class RequestContext extends ConcurrentHashMap<String, Object> {

    protected static Class<? extends RequestContext> contextClass = RequestContext.class;
	//使用泛型,這樣可以通過繼承RequestContext來定製RequestContext
    protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
        @Override
        protected RequestContext initialValue() {
            try {
                return contextClass.newInstance();
            } catch (Throwable e) {
                throw new RuntimeException(e);
            }
        }
    };
    //方法內部都是通過ConcurrentHashMap的put和get
    public void setRequest(HttpServletRequest request) {
        put("request", request);
    }
    ...
    public static RequestContext getCurrentContext() {
        if (testContext != null) return testContext;

        RequestContext context = threadLocal.get();
        return context;
    }
}
  • 請求處理生命週期
    在這裏插入圖片描述

3 部署案例

  • 整體架構
    在這裏插入圖片描述
  • 以無線網關爲例
    在這裏插入圖片描述
  • 如何獲取路由表
    在這裏插入圖片描述
    使用Eureka作爲中介,Zuul通過集成Eureka客戶端Ribbon就可以自動發現服務的ip+端口了

也可以基於域名或基於Apollo來實現,不過沒有Eureka好,因爲無法實現自發現,即自動化

4 Zuul2

使用了非阻塞異步的模式(Netty),增加了一些功能點,架構做了一些修改

5 Zuul1最佳實踐

  • 使用異步 AsyncServlet 來優化連接數

需要自實現

  • 使用Apollo配置中心集成動態配置

  • Hystrix熔斷限流

信號量隔離和線程池隔離兩種方式建議使用信號量隔離,如果使用線程池隔離,則每個服務都要配一個線程池,會導致Zuul1上的線程更多,更容易使網關資源被耗盡

  • 對連接池進行管理

  • CAT和Hystrix 監控

  • 過濾器調試技巧

使用java代碼調試,完成後再改成groovy去上傳

  • 網關不要有業務邏輯

  • 自助路由 (需定製擴展 )

提供一些工具、文檔,讓前後端開發人員可以自助擴展網關

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章