1. 程式人生 > >SpringCloud-Zuul

SpringCloud-Zuul

什麼是 Zuul ?

zuul 是從裝置和網站到後端應用程式所有請求的前門,為內部服務提供可配置的對外 URL 到服務的對映關係. 基於 JVM 的後端路由器.其具備一下功能:

  • 認證與鑑權
  • 壓力控制
  • 金絲雀測試
  • 動態路由
  • 負載削減
  • 靜態響應處理
  • 主動流量管理

其地層是基於 Servlet, 本質元件是一系列的 Filter 所構成的責任鏈.

示例

建立 Eureka Server 註冊中心, Eureka Client (服務提供者工程), Zuul Server 閘道器中心. 三個工程

Eureka Client 工程:

匯入依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

配置檔案:

server:
  port: 8081
spring:
  application:
    name: eureka-clientA
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

主程式啟動類

@SpringBootApplication
@EnableDiscoveryClient
public class EurelaClientAApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurelaClientAApplication.class, args);
    }
}

測試介面:

@RestController
public class TestController {

    @RequestMapping("/add")
    public Integer add(Integer a, Integer b) {
        return a + b;
    }
}

 

Zuul Server 工程:

依賴:

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

配置檔案:

server:
  port: 8080
spring:
  application:
    name: zuul-server
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
zuul:
  routes:
    clienta:
      path: /client/**    #凡是 /client 開頭的 HTTP 請求, 都會被對映到 clientA 這個服務例項上去, 假設 clientA 的服務例項的埠為 8081
                          # 那麼 http://localhost:8080/client/getuser 的請求,在內部會呼叫對映的地址 http://localhost:8081/getuser 進行服務
                          # 請求, 獲取到結果,然後返回
      serviceId: eureka-clientA  #服務例項 ID

主程式啟動類:

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulServerApplication.class, args);
    }
}

 

然後依次啟動 Eureka Server, Eureka Client, Zuul Server 三個工程, 然後先直接訪問以下介面: http://localhost:8081/add?a=10&b=20 可以看到輸出, 說明服務能正常訪問:

然後通過 zuul 去訪問服務: http://localhost:8080/client/add?a=10&b=20 可以看到以下輸出, 說明通過 zuul 能正確訪問服務:

當我們呼叫 http://localhost:8080/client/add?a=10&b=20 地址的時候, 實際上在內部呼叫的時候呼叫的是: http://localhost:8081/add?a=10&b=20 , 這是因為我們在 zuul 中配置了路由規則, 當向 zuul 發起請求的時候, 它會去 Eureka 註冊中心拉取服務列表, 如果發現有指定的路由規則, 就會按照規則路由到相應的服務介面上去.

 

zuul 路由配置:

單例項 ServiceId 對映: 上例中我們使用了 

zuul:
  routes:
    clienta:
      path: /client/**    #凡是 /client 開頭的 HTTP 請求, 都會被對映到 clientA 這個服務例項上去, 假設 clientA 的服務例項的埠為 8081
                          # 那麼 http://localhost:8080/client/getuser 的請求,在內部會呼叫對映的地址 http://localhost:8081/getuser 進行服務
                          # 請求, 獲取到結果,然後返回
      serviceId: eureka-clientA  #服務例項 ID

其實這個寫法可以簡化成這樣 (第一種寫法)

zuul:
  routes:
    eureka-clientA: /client/**

還可以簡化成這樣, 對映規則與 ServiceId 都不用寫 (第二種寫法):

zuul:
  routes:
    eureka-clientA:

在這種情況下,  Zuul 會為  eureka-clientA 新增一個預設的對映規則 /client-clienta/** , 也就是相當於 下面這樣寫

zuul:
  routes:
    eureka-clientA:
      path: /eureka-clienta/**
      serviceId: eureka-clientA

單例項 url 對映

除了路由到服務之外, 還可以路由到具體的地址, 只需要將 serviceId 替換成 url 就可以了.如下所示:

#對映到具體的url
zuul:
  routes:
    eureka-clientA:
      path: /client/**
      url: http://localhost:8081  # eureka-clientA 所在的地址

多例項路由

在預設情況下 Zuul 會使用 Eureka 中整合的負載均衡功能, 如果想要使用 Ribbon 的輔助均衡功能, 就需要指定一個 serviceId, 此操作需要禁止 Ribbon 使用 Eureka. 在 E 版之後,新增了負載均衡策略的配置,如下

zuul:
  routes:
    eureka-clientA:
      path: /client/**
      serviceId: eureka-clientA #服務例項ID
ribbon:
  eureka:
    enabled: false #禁止 ribbon 使用eureka

#為服務例項ID 配置 負載均衡策略
eureka-clientA:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList  #用於獲取服務例項列表的類
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule  #負載均衡策略
    listOfServers: localhost:8081, localhost:8082, localhost:8083 # 所有可用的服務提供者的服務例項地址,

然後在 eureka-clientA 服務提供者個工程的對外介面中增加一個方法, 

@RequestMapping("/gethost")
    public String getHostAndPort(HttpServletRequest request) {
        return String.valueOf(request.getServerPort()); //獲取埠
    }

然後依次eureka 註冊中心, 並且以不同的埠啟動 eureka-clientA 服務的3 個例項, 然後在啟動 zuul , 然後在瀏覽器訪問:http://localhost:8080/client/gethost 可以看到

這幾個埠隨機出現, 說明使用了我們定義好的 隨機策略. 至於為什麼要 Ribbon 禁用 Eureka , 這個還在研究當中

 

forward 本地跳轉

有時候我們在 zuul 中做一些邏輯處理, 我們希望在訪問 /client 介面的時候轉到指定的方法上來處理, 就需要用到 zuul 的本地跳轉,如:

修改 zuul 配置檔案

# zuul 配置本地轉發, 當請求/client 開頭的介面時, 會被 API閘道器轉到到 API 閘道器中以 /client 為字首的請求上
# 假如: 當API 閘道器接收到 /client/add 之類的請求的時候, 因為符合規則, 所以請求會被 API 閘道器轉發到閘道器的
# /client/add 上.
zuul:
  routes:
    eureka-clientA:
      path: /client/**    #當符合該路由規則的請求到達時, 會被 API閘道器轉到到 API 閘道器中以 /client 為字首的請求上
                          # 個人理解是, 本地轉發的時候, 會用 本地轉發指定的字首, 替換掉 路由匹配的字首, 然後把後面的地址
                          #在拼接起來, 如, 請求 http://localhost:8080/client/a/b/c/getuser ,因為符合路由規則, 所以會轉發到
                          # zuul 中的 http://localhost:8080/user/a/b/c/getuser.
      url: forward:/user

在 zuul 中新增一個對外介面類, 根據上面的描述 介面的地址必須為: /user/add 否則會報 404 錯誤

@RestController
public class TestControlelr {

    @RequestMapping("/user/add")
    public String add(Integer a, Integer b) {
        return "本地跳轉" + (a + b);
    }
}

啟動服務, 然後訪問 http://localhost:8080/client/add?a=10&b=20 可以看到輸出, 說明本地跳轉成了, 

如果將 zuul 閘道器中的介面地址改成了 /user, 然後在訪問介面, 這個時候本地轉發就會錯誤了. 

所以要按照規則處理.

如果有這樣一種配置:

zuul:
  routes:
    eureka-clientA:
      path: /client/**
      serviceId: eureka-clientA
    eureka-clientB:
      path: /client/**
      serviceId: eureka-clientB

路由規則是一樣的, 經過測試, 它總是會匹配到配置檔案後面的那個服務上去. 這裡是 eureka-clientB 服務上.yml直譯器在 工作的時候, 如果同一個對映對應多個服務, 按照載入順序, 最末尾載入的對映會覆蓋前面載入的對映.

zuul 還可以配置路由字首, 服務遮蔽, 路徑遮蔽, 敏感頭資訊(也就是切換敏感資訊與下游伺服器的互動),配置重定向(比如隱藏真實服務的 IP, 暴露zuul 閘道器的IP), 重試機制等. 

 

參考<重新定義 Spring Cloud> 一書