1. 程式人生 > >SpringCloud進擊 | 五淺出:服務閘道器 - 路由(Zuul Router)【Finchley版本】

SpringCloud進擊 | 五淺出:服務閘道器 - 路由(Zuul Router)【Finchley版本】


1.前言

上一節:SpringCloud進擊 | 四淺出:斷路器與容錯(Hystrix)【Finchley版本】

路由,微服務體系結構的一個組成部分,是 Netflix 基本 JVM 的路由器和服務端的負載均衡器。形象一點就是我們經常會看到的像請求路徑:/api/user 對映到使用者服務,/api/cart 對映到購物車服務。是的,路由(/)可以對映到你的Web應用程式,到某個模組,到某個具體的服務。

我們知道,在微服務中,後臺服務往往不是直接開放給呼叫者,而是通過 Gateway 閘道器根據請求的 URL,路由到相應的服務。這種情況下,Gateway一般都封裝了一套API,當新增 Gateway API 閘道器後,在第三方呼叫者和服務提供方之間就建立了一面無形的牆,這面牆直接與呼叫方通訊,進行許可權控制後將請求均衡分發給後臺服務端。所以,我們看似完成的微服務架構其實還是少考慮了一個問題,這也是我們這節的主要內容,即,外部的應用怎麼來訪問內部各種各樣的微服務呢?


2.準備

使用並分別啟動前幾節我們已經建立好的以下模組的啟動類:

  • 服務註冊中心:wei-eureka-server,埠號:8090
  • 服務提供者:wei-service-provider,埠號:8010
  • 服務消費者:wei-consumer-ribbon,埠號:8020 或者 wei-consumer-feign,埠號:8030

 

3.進擊

在原有專案上建立一個新的基於 Spring Boot 模組,並起名為 wei-gateway-zuul。建立過程與第一節建立模組的過程類似。但在Dependencies選擇依賴時需要注意,選擇左側的 Cloud Routing 後,這裡需要鉤上 Zuul 項。所以,最終我們需要勾選左側 Cloud Discovery 的 Eureka Discovery,和左側 Cloud Routing 的 Zuul,以及左側 Web 的 Web 依賴。

3.1.服務閘道器之路由/對映

3.1.1.pom.xml 依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wei</groupId>
    <artifactId>wei-gateway-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>wei-gateway-zuul</name>
    <description>Demo project for Spring Cloud Zuul</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <spring-cloud.version>Finchley.SR1</spring-cloud.version>
    </properties>

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

3.1.2.application.yml 配置

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址

3.1.3.啟動類

package com.wei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

/**
 * 註解@EnableZuulProxy,開啟Zuul功能,自帶熔斷
 */
@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
public class WeiGatewayZuulApplication {

    public static void main(String[] args) {
        SpringApplication.run(WeiGatewayZuulApplication.class, args);
    }
}

3.1.4.測試

服務註冊中心檢視服務,如上,註冊成功。

首先,可以正常訪問服務提供者:http://localhost:8010/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010

其次,也可以正常通過服務消費者消費服務:http://localhost:8020/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010[Ribbon + REST]

 

那麼,加上 Zuul 路由後會有什麼不一樣呢?

由於 Spring Cloud Zuul 在整合了 Eureka 之後,具備預設的服務路由功能,即:當我們構建 Zuul 的實現 wei-gateway-zuul 模組,啟動並註冊到 Eureka 之後,服務閘道器會發現上面我們啟動的兩個服務 wei-service-provider 和 wei-consumer-ribbon,這時候 Zuul 就會建立兩個路由規則。每個路由規則都包含兩部分,一部分是外部請求的匹配規則,另一部分是路由的服務ID(application.name配置):

  • 轉發到 wei-service-provider 服務提供者的請求規則為 /wei-service-provider/**
  • 轉發到 wei-consumer-ribbon 服務消費者的請求規則為 /wei-consumer-ribbon/**

我們來驗證一下:

訪問:http://localhost:8060/wei-service-provider/demo/info?name=tester
輸出:

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010

訪問:http://localhost:8060/wei-consumer-ribbon/demo/info?name=tester
輸出:

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010[Ribbon + REST]

驗證成功。到這裡,一個基於 Spring Cloud Zuul 的預設的服務閘道器就已經構建完畢。

 

但是,或者,你也發現,如果每次都使用服務名,可能不太好,它們的名字或長或短,也可能會使呼叫變得複雜。所以,我們需要自定義服務對映地址,Zuul 也提供了自定義服務對映地址的功能,我們只需修改相關配置就能實現。

 

3.2.自定義服務對映地址

3.2.1.服務名稱對映

改造 application.yml 配置檔案:

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**

通過配置項 zuul.routes.applicationName 就可以給指定的服務新增地址對映。

配置完成後,重啟 Zuul 模組的啟動類,並訪問地址:http://localhost:8060/providerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010

有沒有,其結果與之前的 http://localhost:8060/wei-service-provider/demo/info?name=tester 一樣。

3.2.2.path繫結對映

我們也可以使用 path 屬性自定義對映地址,同樣只需改造 application.yml 配置檔案:

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**
    consumerGroup:    # 保證唯一
      serviceId: wei-consumer-ribbon    # 給指定的服務做對映
      path: /consumerGroup/**    # path繫結對映。配置對映的路徑,當前配置是將/wei-consumer-ribbon/**對映為/consumerGroup/**

配置完成後,重啟 Zuul 模組的啟動類,並訪問地址:http://localhost:8060/consumerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010[Ribbon + REST]

同樣的,其結果與之前的 http://localhost:8060/wei-consumer-ribbon/demo/info?name=tester 一樣。

3.2.3.url繫結對映

除了上面兩種方法,還可以使用 url 屬性直接繫結對應 url。將上面的配置 serviceId: wei-consumer-ribbon 改造為 url: http://localhost:8020/ ,然後重啟啟動類即可。

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**
    consumerGroup:    # 保證唯一
      url: http://localhost:8020/    # url繫結對映
      #serviceId: wei-consumer-ribbon    # 給指定的服務做對映
      path: /consumerGroup/**    # path繫結對映。配置對映的路徑,當前配置是將/wei-consumer-ribbon/**對映為/consumerGroup/**

配置完成後,重啟 Zuul 模組的啟動類,並訪問地址:http://localhost:8060/consumerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010[Ribbon + REST]

其結果也完全一樣。

這三種方式都可以指定服務對映地址,需要注意的是這些簡單的URL路由不會被執行為 HystrixCommand,也不能使用 Ribbon 對多個URL進行負載均衡。

 

3.3.實現負載均衡

如果想要達到負載均衡的效果,需要使用 serviceId 配置的方式再加上 ribbon.listOfServers 配置。改造 application.yml

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**
    consumerGroup:    # 保證唯一
      #url: http://localhost:8020/    # url繫結對映
      serviceId: wei-consumer-ribbon    # 給指定的服務做對映
      path: /consumerGroup/**    # path繫結對映。配置對映的路徑,當前配置是將/wei-consumer-ribbon/**對映為/consumerGroup/**
ribbon:
  eureka:
    enabled: false    # 在eureka中禁用 ribbon 的負載均衡
wei-consumer-ribbon:
  ribbon:    # 給配置serviceId對應的服務指定ribbon負載均衡,從listOfServers配置的服務地址中分配服務,多個用半形逗號分隔
    listOfServers: http://localhost:8010/, http://localhost:8011/

給配置serviceId對應的服務指定ribbon負載均衡,從listOfServers配置的服務地址中分配服務,多個用半形逗號分隔。

改造完成後,重啟 Zuul 的實現模組的啟動類。然後還需要再開一個服務提供者例項,修改 wei-service-provider 的埠號為8011,執行其啟動類。這樣,此時會有兩個服務提供者 8010、8011。

訪問URL:http://localhost:8060/consumerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8010

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8011

可以看到,通過 Zuul 呼叫,和之前的 第二節 配置 Ribbon負載均衡效果一樣,瀏覽器輸出的日誌在埠為8010和8011的服務之間切換列印,預設輪詢。

 

3.4.新增字首對映

改造 application.yml 配置屬性:zuul.prefix,指定路由字首。

server:
  port: 8060    # 自定義程式啟動埠
spring:
  application:
    name: wei-gateway-zuul    # 指定進行服務註冊時該服務的名稱,服務與服務之間相互呼叫一般都是根據這個name
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8090/eureka/    # 指定進行服務註冊的地址
zuul:
  routes:    # 配置路由對映
    wei-service-provider: /providerGroup/**    # 服務名稱對映。給指定的服務做對映,當前配置是將/wei-service-provider/**對映為/providerGroup/**
    consumerGroup:    # 保證唯一
      #url: http://localhost:8020/    # url繫結對映
      serviceId: wei-consumer-ribbon    # 給指定的服務做對映
      path: /consumerGroup/**    # path繫結對映。配置對映的路徑,當前配置是將/wei-consumer-ribbon/**對映為/consumerGroup/**
  prefix: /api
ribbon:
  eureka:
    enabled: false    # 在eureka中禁用 ribbon 的負載均衡
wei-consumer-ribbon:
  ribbon:    # 給配置serviceId對應的服務指定ribbon負載均衡,從listOfServers配置的服務地址中分配服務,多個用半形逗號分隔
    listOfServers: http://localhost:8010/, http://localhost:8011/

這裡,我們設定了路由字首 /api,這將會給對應服務的所有對映新增該指定的字首。

訪問URL:http://localhost:8060/api/consumerGroup/demo/info?name=tester

Hi,tester,我是服務,我被呼叫了,服務名為:wei-service-provider,埠為:8011

列印結果與上面 3.3.實現負載均衡 不加字首的結果一致。同樣也有負載均衡的效果。

通過zuul.stripPrefix=false去關閉新增字首的行為,關閉的字首只能是zuul.prefix設定的字首

 

4.總結

Zuul 在雲平臺上提供動態路由,監控,彈性,安全等邊緣服務的框架。
Zuul 相當於是裝置和 Netflix 流應用的 Web 網站後端所有請求的前門。

官方參考文件:https://springcloud.cc/spring-cloud-netflix.html


原始碼:https://github.com/itanping/wei-springcloud/tree/master/chapter06-zuul

下一節,請繼續關注:SpringCloud進擊 | 七淺出:服務閘道器 - 過濾器(Zuul Filter)【Finchley版本】