天天看點

萬字長文詳解微服務網關(中)

作者:馬士兵教育

3.微服務網關GateWay

Zuul 1.x 是一個基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于Netty,也是非阻塞的,支援長連接配接)才釋出,但 Spring Cloud 暫時還沒有整合計劃。Spring CloudGateway 比 Zuul 1.x 系列的性能和功能整體要好。

3.1Gateway簡介

3.1.1 簡介

Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的網關,旨在為微服務架構提供一種簡單而有效的統一的 API 路由管理方式,統一通路接口。SpringCloud Gateway 作為 Spring Cloud 生态系中的網關,目标是替代 Netflflix ZUUL,其不僅提供統一的路由方式,并且基于 Filter 鍊的方式提供了網關基本的功能,例如:安全,監控/埋點,和限流等。它是基于Nttey的響應式開發模式。

萬字長文詳解微服務網關(中)

上表為Spring Cloud Gateway與Zuul的性能對比,從結果可知,Spring Cloud Gateway的RPS是Zuul的1.6倍

3.1.2 核心概念

萬字長文詳解微服務網關(中)

1. 路由(route)路由是網關最基礎的部分,路由資訊由一個ID、一個目的URL、一組斷言工廠和一組Filter組成。如果斷言為真,則說明請求URL和配置的路由比對。

2. 斷言(predicates) Java8中的斷言函數,Spring Cloud Gateway中的斷言函數輸入類型是Spring5.0架構中的ServerWebExchange。Spring Cloud Gateway中的斷言函數允許開發者去定義比對來自Http Request中的任何資訊,比如請求頭和參數等。

3. 過濾器(fifilter)

一個标準的Spring webFilter,Spring Cloud Gateway中的Filter分為兩種類型,分别是Gateway Filter和Global Filter。過濾器Filter可以對請求和響應進行處理。

3.2 入門案例

3.2.1 入門案例

(1)建立工程導入依賴

在項目中添加新的子產品 shop_gateway_server ,并導入依賴

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>           

注意SpringCloud Gateway使用的web架構為webflflux,和SpringMVC不相容。引入的限流元件是hystrix。redis底層不再使用jedis,而是lettuce。

(2)配置啟動類

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

(3)編寫配置檔案

建立 application.yml 配置檔案

server:
port: 8080 #服務端口
spring:
application:
name: api-gateway #指定服務名
cloud:
gateway:
routes:
- id: product-service
uri: http://127.0.0.1:9002
predicates:
- Path=/product/**           
  • uri:目标服務位址
  • predicates:路由條件,Predicate 接受一個輸入參數,傳回一個布爾值結果。該接口包含多種預設方法來将 Predicate 組合成其他複雜的邏輯(比如:與,或,非)。
  • fifilters:過濾規則,暫時沒用。

上面這段配置的意思是,配置了一個 id 為 product-service的路由規則,當通路網關請求位址以product 開頭時,會自動轉發到位址: http://127.0.0.1:9002/ 。配置完成啟動項目即可在浏覽器通路進行測試,當我們通路位址 http://localhost:8080/product/1 時會展示頁面展示如下:

萬字長文詳解微服務網關(中)

3.2.2 路由規則

Spring Cloud Gateway 的功能很強大,前面我們隻是使用了 predicates 進行了簡單的條件比對,其實Spring Cloud Gataway 幫我們内置了很多 Predicates 功能。在 Spring Cloud Gateway 中 Spring 利用Predicate 的特性實作了各種路由比對規則,有通過 Header、請求參數等不同的條件來進行作為條件比對到對應的路由。

萬字長文詳解微服務網關(中)

示例

#路由斷言之後比對
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://xxxx.com
#路由斷言之前比對
predicates:
- After=xxxxx
#路由斷言之前比對
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://xxxxxx.com
predicates:
- Before=xxxxxxx
#路由斷言之間
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://xxxx.com
predicates:
- Between=xxxx,xxxx
#路由斷言Cookie比對,此predicate比對給定名稱(chocolate)和正規表達式(ch.p)
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://xxxx.com
predicates:
- Cookie=chocolate, ch.p
#路由斷言Header比對,header名稱比對X-Request-Id,且正規表達式比對\d+
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://xxxx.com
predicates:
- Header=X-Request-Id, \d+
#路由斷言比對Host比對,比對下面Host主機清單,**代表可變參數
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://xxxx.com
predicates:
- Host=**.somehost.org,**.anotherhost.org
#路由斷言Method比對,比對的是請求的HTTP方法
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://xxxx.com
predicates:
- Method=GET
#路由斷言比對,{segment}為可變參數
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://xxxx.com
predicates:
- Path=/foo/{segment},/bar/{segment}
#路由斷言Query比對,将請求的參數param(baz)進行比對,也可以進行regexp正規表達式比對 (參數包含
foo,并且foo的值比對ba.)
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://xxxx.com
predicates:
- Query=baz 或 Query=foo,ba.
#路由斷言RemoteAddr比對,将比對192.168.1.1~192.168.1.254之間的ip位址,其中24為子網路遮罩位
數即255.255.255.0
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24           

3.2.3 動态路由

和zuul網關類似,在SpringCloud GateWay中也支援動态路由:即自動的從注冊中心中擷取服務清單并通路。

(1)添加注冊中心依賴

在工程的pom檔案中添加注冊中心的用戶端依賴(這裡以Eureka為例)

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

(2)配置動态路由

修改 application.yml 配置檔案,添加eureka注冊中心的相關配置,并修改通路映射的URL為服務名稱

server:
port: 8080 #服務端口
spring:
application:
name: api-gateway #指定服務名
cloud:
gateway:
routes:
- id: product-service
uri: lb://shop-service-product
predicates:
- Path=/product/**
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
registry-fetch-interval-seconds: 5 # 擷取服務清單的周期:5s
instance:
preferIpAddress: true
ip-address: 127.0.0.1           
  • uri : uri以 lb: //開頭(lb代表從注冊中心擷取服務),後面接的就是你需要轉發到的服務名稱

3.2.4 重寫轉發路徑

在SpringCloud Gateway中,路由轉發是直接将比對的路由path直接拼接到映射路徑(URI)之後,那麼在微服務開發中往往沒有那麼便利。這裡就可以通過RewritePath機制來進行路徑重寫。

(1)案例改造

修改 application.yml ,将比對路徑改為 /product-service/**

萬字長文詳解微服務網關(中)

重新啟動網關,我們在浏覽器通路http://127.0.0.1:8080/product-service/product/1,會抛出404。這是由于路由轉發規則預設轉發到商品微服務( http://127.0.0.1:9002/productservice/product/1 )路徑上,而商品微服務又沒有 product-service 對應的映射配置。

(2)添加RewritePath重寫轉發路徑

修改 application.yml ,添加重寫規則。

spring:
application:
name: api-gateway #指定服務名
cloud:
gateway:
routes:
- id: product-service
uri: lb://shop-service-product
predicates:
- Path=/product-service/**
filters:
- RewritePath=/product-service/(?<segment>.*), /$\{segment}           

通過RewritePath配置重寫轉發的url,将/product-service/(?.*),重寫為{segment},然後轉發到訂單微服務。比如在網頁上請求http://localhost:8080/product-service/product,此時會将請求轉發到http://127.0.0.1:9002/product/1( 值得注意的是在yml文檔中 $ 要寫成 $\ )

3.3 過濾器

Spring Cloud Gateway除了具備請求路由功能之外,也支援對請求的過濾。通過Zuul網關類似,也是通過過濾器的形式來實作的。那麼接下來我們一起來研究一下Gateway中的過濾器

3.3.1 過濾器基礎

(1)過濾器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那麼豐富,它隻有兩個:“pre” 和 “post”。

  • PRE:這種過濾器在請求被路由之前調用。我們可利用這種過濾器實作身份驗證、在叢集中選擇請求的微服務、記錄調試資訊等。
  • POST:這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應添加标準的 HTTPHeader、收集統計資訊和名額、将響應從微服務發送給用戶端等。
萬字長文詳解微服務網關(中)

(2)過濾器類型

Spring Cloud Gateway 的 Filter 從作用範圍可分為另外兩種GatewayFilter 與 GlobalFilter。

  • GatewayFilter:應用到單個路由或者一個分組的路由上。
  • GlobalFilter:應用到所有的路由上。

3.3.2 局部過濾器

局部過濾器(GatewayFilter),是針對單個路由的過濾器。可以對通路的URL過濾,進行切面處理。在Spring Cloud Gateway中通過GatewayFilter的形式内置了很多不同類型的局部過濾器。這裡簡單将Spring Cloud Gateway内置的所有過濾器工廠整理成了一張表格,雖然不是很詳細,但能作為速覽使用。如下:

萬字長文詳解微服務網關(中)
萬字長文詳解微服務網關(中)

每個過濾器工廠都對應一個實作類,并且這些類的名稱必須以 GatewayFilterFactory 結尾,這是Spring Cloud Gateway的一個約定,例如 AddRequestHeader 對應的實作類為AddRequestHeaderGatewayFilterFactory 。對于這些過濾器的使用方式可以參考官方文檔

3.3.3 全局過濾器

全局過濾器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定義了Global Filter接口,用可以自定義實作自己的Global Filter。通過全局過濾器可以實作對權限的統一校驗,安全性驗證等功能,并且全局過濾器也是程式員使用比較多的過濾器。

Spring Cloud Gateway内部也是通過一系列的内置全局過濾器對整個路由轉發進行處理如下:

萬字長文詳解微服務網關(中)

3.4 統一鑒權

内置的過濾器已經可以完成大部分的功能,但是對于企業開發的一些業務功能處理,還是需要我們自己編寫過濾器來實作的,那麼我們一起通過代碼的形式自定義一個過濾器,去完成統一的權限校驗。

3.4.1 鑒權邏輯

開發中的鑒權邏輯:

  • 當用戶端第一次請求服務時,服務端對使用者進行資訊認證(登入)
  • 認證通過,将使用者資訊進行加密形成token,傳回給用戶端,作為登入憑證
  • 以後每次請求,用戶端都攜帶認證的token
  • 服務端對token進行解密,判斷是否有效。
萬字長文詳解微服務網關(中)

如上圖,對于驗證使用者是否已經登入鑒權的過程可以在網關層統一檢驗。檢驗的标準就是請求中是否攜帶token憑證以及token的正确性。

3.4.2 代碼實作

下面的我們自定義一個GlobalFilter,去校驗所有請求的請求參數中是否包含“token”,如何不包含請求參數“token”則不轉發路由,否則執行正常的邏輯。

@Component
public class AuthorizeFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain
chain) {
//String url = exchange.getRequest().getURI().getPath();
//忽略以下url請求
//if(url.indexOf("/login") >= 0){
// return chain.filter(exchange);
// }
String token = exchange.getRequest().getQueryParams().getFirst("token");
if (StringUtils.isBlank(token)) {
log.info( "token is empty ..." );
exchange.getResponse().setStatusCode( HttpStatus.UNAUTHORIZED );
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}           
  • 自定義全局過濾器需要實作GlobalFilter和Ordered接口。
  • 在fifilter方法中完成過濾器的邏輯判斷處理
  • 在getOrder方法指定此過濾器的優先級,傳回值越大級别越低
  • ServerWebExchange 就相當于目前請求和響應的上下文,存放着重要的請求-響應屬性、請求實
  • 例和響應執行個體等等。一個請求中的request,response都可以通過 ServerWebExchange 擷取調用 chain.filter 繼續向下遊執行

3.5 網關限流

3.5.1 常見的限流算法

(1)計數器

計數器限流算法是最簡單的一種限流實作方式。其本質是通過維護一個機關時間内的計數器,每次請求計數器加1,當機關時間内計數器累加到大于設定的門檻值,則之後的請求都被拒絕,直到機關時間已經過去,再将計數器重置為零

萬字長文詳解微服務網關(中)

(2)漏桶算法

漏桶算法可以很好地限制容量池的大小,進而防止流量暴增。漏桶可以看作是一個帶有常量服務時間的單伺服器隊列,如果漏桶(包緩存)溢出,那麼資料包會被丢棄。 在網絡中,漏桶算法可以控制端口的流量輸出速率,平滑網絡上的突發流量,實作流量整形,進而為網絡提供一個穩定的流量。

萬字長文詳解微服務網關(中)

為了更好的控制流量,漏桶算法需要通過兩個變量進行控制:一個是桶的大小,支援流量突發增多時可以存多少的水(burst),另一個是水桶漏洞的大小(rate)。

(3)令牌桶算法

令牌桶算法是對漏桶算法的一種改進,桶算法能夠限制請求調用的速率,而令牌桶算法能夠在限制調用的平均速率的同時還允許一定程度的突發調用。在令牌桶算法中,存在一個桶,用來存放固定數量的令牌。算法中存在一種機制,以一定的速率往桶中放令牌。每次請求調用需要先擷取令牌,隻有拿到令牌,才有機會繼續執行,否則選擇選擇等待可用的令牌、或者直接拒絕。放令牌這個動作是持續不斷的進行,如果桶中令牌數達到上限,就丢棄令牌,是以就存在這種情況,桶中一直有大量的可用令牌,這時進來的請求就可以直接拿到令牌執行,比如設定qps為100,那麼限流器初始化完成一秒後,桶中就已經有100個令牌了,這時服務還沒完全啟動好,等啟動完成對外提供服務時,該限流器可以抵擋瞬時的100個請求。是以,隻有桶中沒有令牌時,請求才會進行等待,最後相當于以一定的速率執行。

萬字長文詳解微服務網關(中)

3.5.2 基于Filter的限流

SpringCloudGateway官方就提供了基于令牌桶的限流支援。基于其内置的過濾器工廠RequestRateLimiterGatewayFilterFactory 實作。在過濾器工廠中是通過Redis和lua腳本結合的方式進行流量控制。

(1)環境搭建

導入redis的依賴

首先在工程的pom檔案中引入gateway的起步依賴和redis的reactive依賴,代碼如下:

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifatId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>           

準備redis

(2)修改application.yml配置檔案

在application.yml配置檔案中加入限流的配置,代碼如下:

spring:
application:
name: api-gateway #指定服務名
cloud:
gateway:
routes:
- id: order-service
filters:
- RewritePath=/order-service/(?<segment>.*), /$\{segment}
- name: RequestRateLimiter
args:
# 使用SpEL從容器中擷取對象
key-resolver: '#{@pathKeyResolver}'
# 令牌桶每秒填充平均速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的總容量
redis-rate-limiter.burstCapacity: 3
redis:
host: localhost
port: 6379           

在 application.yml 中添加了redis的資訊,并配置了RequestRateLimiter的限流過濾器:

  • burstCapacity,令牌桶總容量。
  • replenishRate,令牌桶每秒填充平均速率。
  • key-resolver,用于限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據#
  • {@beanName}從 Spring 容器中擷取 Bean 對象。

(3)配置KeyResolver

為了達到不同的限流效果和規則,可以通過實作 KeyResolver 接口,定義不同請求類型的限流鍵。

@Configuration
public class KeyResolverConfiguration {
/**
* 基于請求路徑的限流
*/
@Bean
public KeyResolver pathKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getPath().toString()
);
}
/**
* 基于請求ip位址的限流
*/
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getHeaders().getFirst("X-Forwarded-For")
);
}
/**
* 基于使用者的限流
*/
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(
exchange.getRequest().getQueryParams().getFirst("user")
);
}
}
           

使用Jmetter模拟5組線程通路,會發現如下結果,當達到令牌桶的總容量3時,其他的請求會傳回429錯誤。

萬字長文詳解微服務網關(中)

通過reids的MONITOR可以監聽redis的執行過程。這時候Redis中會有對應的資料:

萬字長文詳解微服務網關(中)

大括号中就是我們的限流Key,這邊是IP,本地的就是localhost

  • timestamp:存儲的是目前時間的秒數,也就是System.currentTimeMillis() / 1000或者Instant.now().getEpochSecond()
  • tokens:存儲的是目前這秒鐘的對應的可用的令牌數量

Spring Cloud Gateway目前提供的限流還是相對比較簡單的,在實際中我們的限流政策會有很多種情況,比如:

  • 對不同接口的限流
  • 被限流後的友好提示

這些可以通過自定義RedisRateLimiter來實作自己的限流政策,這裡我們不做讨論

3.5.3 基于Sentinel的限流

Sentinel 支援對 Spring Cloud Gateway、Zuul 等主流的 API Gateway 進行限流。

萬字長文詳解微服務網關(中)

從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的适配子產品,可以提供兩種資源次元的限流:

  • route 次元:即在 Spring 配置檔案中配置的路由條目,資源名為對應的 routeId
  • 自定義 API 次元:使用者可以利用 Sentinel 提供的 API 來自定義一些 API 分組

Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 子產品,此子產品中包含網關限流的規則和自定義 API 的實體和管理邏輯:

  • route 或自定義的 API 分組進行限流,支援針對請求中的參數、Header、來源 IP 等進行定制化的限流。
  • ApiDefinition :使用者自定義的 API 定義分組,可以看做是一些 URL 比對的組合。比如我們可以定義一個 API 叫 my_api ,請求 path 模式為 /foo/** 和 /baz/** 的都歸到 my_api 這個 API
  • 分組下面。限流的時候可以針對這個自定義的 API 分組次元進行限流。

(1)環境搭建

導入Sentinel 的響應依賴

<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
<version>x.y.z</version>
</dependency>           

(2)編寫配置類

public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>>
viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers =
viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 配置限流的異常處理器:SentinelGatewayBlockExceptionHandler
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler
sentinelGatewayBlockExceptionHandler() {
return new SentinelGatewayBlockExceptionHandler(viewResolvers,
serverCodecConfigurer);
}
/**
* 配置限流過濾器
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 配置初始化的限流參數
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(
new GatewayFlowRule("order-service") //資源名稱
.setCount(1) // 限流門檻值
.setIntervalSec(1) // 統計時間視窗,機關是秒,預設是 1 秒
);
GatewayRuleManager.loadRules(rules);
}
}           
  • 基于Sentinel 的Gateway限流是通過其提供的Filter來完成的,使用時隻需注入對應的SentinelGatewayFilter 執行個體以及 SentinelGatewayBlockExceptionHandler 執行個體即可。
  • @PostConstruct定義初始化的加載方法,用于指定資源的限流規則。這裡資源的名稱為 orderservice ,統計時間是1秒内,限流門檻值是1。表示每秒隻能通路一個請求。

(3)網關配置

spring:
application:
name: api-gateway #指定服務名
redis:
host: 127.0.0.1
port: 6379
database: 0
cloud:
gateway:
routes:
- id: order-service
uri: lb://shop-service-order
predicates:
- Path=/order-service/**
filters:
- RewritePath=/order-service/(?<segment>.*), /$\{segment}           

在一秒鐘内多次通路http://localhost:8080/order-service/order/buy/1就可以看到限流啟作用了。

萬字長文詳解微服務網關(中)

(4)自定義異常提示

當觸發限流後頁面顯示的是Blocked by Sentinel: FlowException。為了展示更加友好的限流提示,Sentinel支援自定義異常處理。

您可以在 GatewayCallbackManager 注冊回調進行定制:

  • setBlockHandler :注冊函數用于實作自定義的邏輯處理被限流的請求,對應接口為BlockRequestHandler 。預設實作為 DefaultBlockRequestHandler ,當被限流時會傳回類似于下面的錯誤資訊: Blocked by Sentinel: FlowException 。
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
public Mono<ServerResponse> handleRequest(ServerWebExchange
serverWebExchange, Throwable throwable) {
Map map = new HashMap<>();
map.put("code", 001);
map.put("message", "對不起,接口限流了");
return ServerResponse.status(HttpStatus.OK).
contentType(MediaType.APPLICATION_JSON_UTF8).
body(BodyInserters.fromObject(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}           
萬字長文詳解微服務網關(中)

(5)參數限流

上面的配置是針對整個路由來限流的,如果我們隻想對某個路由的參數做限流,那麼可以使用參數限流方式

rules.add(new GatewayFlowRule("order-service")
.setCount(1)
.setIntervalSec(1)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM).setFi
eldName("id")
)
);           

通過指定PARAM_PARSE_STRATEGY_URL_PARAM表示從url中擷取參數,setFieldName指定參數名稱

(6)自定義API分組

@PostConstruct
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("product_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
//以/product-service/product 開頭的請求
add(new ApiPathPredicateItem().setPattern("/product
service/product/**").
setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
ApiDefinition api2 = new ApiDefinition("order_api")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
///order-service/order 完成的url路徑比對
add(new ApiPathPredicateItem().setPattern("/order-service/order"));
}});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}           

繼續閱讀