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版本】