SpringCloud(六)zuul 微服務網關

微服務架構體系中,通常一個業務系統會有很多的微服務,比如:OrderService、ProductService、UserService...,爲了讓調用更簡單,一般會在這些服務前端再封裝一層,類似下面這樣:

前面這一層俗稱爲“網關層”,其存在意義在於,將"1對N"問題 轉換成了"1對1”問題,同時在請求到達真正的微服務之前,可以做一些預處理,比如:來源合法性檢測,權限校驗,反爬蟲之類...

傳統方式下,最土的辦法,網關層可以人肉封裝,類似以下示例代碼:

LoginResult login(...){<br>   //TODO 預處理...

   return userService.login();//調用用戶服務的登錄方法 

}

 

Product queryProduct(...){<br>   //TODO 預處理...

  return productService.queryProduct();//調用產品服務的查詢方法 

}

 

Order submitOrder(...){<br>   //TODO 預處理...

   return orderService.submitOrder();//調用訂單服務的查詢方法

}

這樣做,當然能跑起來,但是維護量大,以後各個微服務增加了新方法,都需要在網關層手動增加相應的方法封裝,而spring cloud 中的zuul很好的解決了這一問題,示意圖如下:

Zuul做爲網關層,自身也是一個微服務,跟其它服務Service-1,Service-2, ... Service-N一樣,都註冊在eureka server上,可以相互發現,zuul能感知到哪些服務在線,同時通過配置路由規則(後面會給出示例),可以將請求自動轉發到指定的後端微服務上,對於一些公用的預處理(比如:權限認證,token合法性校驗,灰度驗證時部分流量引導之類),可以放在所謂的過濾器(ZuulFilter)裏處理,這樣後端服務以後新增了服務,zuul層幾乎不用修改。

使用步驟:

一、添加zuul依賴的jar包

1

compile 'org.springframework.cloud:spring-cloud-starter-zuul'

 

二、application.yml裏配置路由

1

2

3

4

5

6

7

8

9

zuul:

  routes:

    api-a:

      path: /api-user/**

      service-id: service-provider

      sensitive-headers:

    api-b:

      path: /api-order/**

      service-id: service-consumer 

解釋一下:上面這段配置表示,/api-user/開頭的url請求,將轉發到service-provider這個微服務上,/api-order/開頭的url請求,將轉發到service-consumer這個微服務上。

 

三、熔斷處理

如果網關後面的微服務掛了,zuul還允許定義一個fallback類,用於熔斷處理,參考下面的代碼:

+ View Code

開發人員只要在getRoute這個方法裏指定要處理的微服務實例,然後重寫fallbackResponse即可。

此時,如果觀察/health端點,也可以看到hystrix處於融斷開啓狀態

 

四、ZuulFilter過濾器

過濾器是一個很有用的機制,下面分幾種經典場景演示下:

4.1、token校驗/安全認證

網關直接暴露在公網上時,終端要調用某個服務,通常會把登錄後的token傳過來,網關層對token進行有效性驗證,如果token無效(或沒傳token),提示重新登錄或直接拒絕。另外,網關後面的微服務,如果設置了spring security中的basic Auth(即:不允許匿名訪問,必須提供用戶名、密碼),也可以在Filter中處理。參考下面的代碼:

+ View Code

Filter一共有4種類型,其常量值在org.springframework.cloud.netflix.zuul.filters.support.FilterConstants 中定義

+ View Code

安全校驗,一般放在請求真正處理之前,所以上面的示例filterType指定爲pre,剩下的只要在shouldFilter()、run()方法中重寫自己的邏輯即可。

4.2 動態修改請求參數

zuulFilter可以攔截所有請求參數,並對其進行修改,比如:終端發過來的數據,出於安全要求,可能是經過加密處理的,需要在網關層進行參數解密,再傳遞到後面的服務;再比如:用戶傳過來的token值,需要轉換成userId/userName這些信息,再傳遞到背後的微服務。參考下面的run方法:

+ View Code

更多filter的示例,可以參考官網:https://github.com/spring-cloud-samples/sample-zuul-filters

4.3 灰度發佈(Gated Launch/Gray Release) 

大型分佈式系統中,灰度發佈是保證線上系統安全生產的重要手段,一般的做法爲:從集羣中指定一臺(或某幾臺)機器,每次做新版本發佈前,先只發布這些機器上,先觀察一下是否正常,如果穩定運行後,再發布到其它機器。這種策略(相當於按部分節點來灰度),大多數情況下可以滿足要求,但是有一些特定場景,可能不太適用。

比如:筆者所在的“美味不用等”公司,主要B端用戶爲各餐飲品牌的商家,多數情況下,如果新上了一個功能,希望找一些規模較小的餐廳做試點,先看看上線後的運行情況,如果運行良好,再推廣到其它商家。

再比如:後端服務有N多個版本在同時運行,比如V1、V2,現在新加了一個V3版本(這在手機app應用中很常見),希望只有部分升級了app的用戶訪問最新的V3版本服務,其它用戶仍然訪問舊版本,待系統穩定後,再大規模提示用戶升級。

對於這些看上去需求各異的灰度需求,其實本質是一樣的:將請求(根據參數內容+業務規則),將其轉向到特定的灰度機器上。Spring Cloud MicroService中有一個metadata-map(元數據)設置,可以很好的滿足這類需求。

首先要引入一個jar包:(這是github上開源的一個項目ribbon-discovery-filter-spring-cloud-starter

1

compile 'io.jmnarloch:ribbon-discovery-filter-spring-cloud-starter:2.1.0'

示例如下:

在各個服務的application.yml中設置以下metadata-map

1

2

3

4

eureka:

  instance:

    metadata-map:

      gated-launch: false

即:所有節點發布後,默認灰度模式爲false。然後把特定的灰度機器上的配置,該參數改成true(表明這臺機器是用於灰度驗證的)。

然後在ZuulFilter中參考下面的代碼:

+ View Code

注意18-23行,這裏演示了通過特定的token參數值,將請求引導到gated-lanuch=true的機器上。(注:參考這個原理,大家可以把參數值,換成自己的version-版本號,shopId-商家Id之類)。只要請求參數中的token=1234567890,這次請求就會轉發到灰度節點上。

如果有朋友好奇這是怎麼做到的,可以看下io.jmnarloch.spring.cloud.ribbon.predicate.MetadataAwarePredicate 這個類:

+ View Code

大致原理就是拿上下文中,開發人員設置的屬性 與 服務節點裏的metadata-map 進行比較,如果metadata-map中包括開發人員設置的屬性,就返回成功(即:選擇這臺服務器)

示例源碼:https://github.com/yjmyzz/spring-cloud-demo 

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