手把手教你使用 Spring Cloud Gateway

古時的風箏發表於2020-03-17

微信搜尋公眾號「古時的風箏」,一個不只有技術的技術公眾號。 Spring Cloud 系列文章已經完成,可以到 我的github 上檢視系列完整內容。

閘道器可提供請求路由與組合協議轉換安全認證服務鑑權流量控制日誌監控等服務。可選的閘道器有不少,比如 Nginx、高效能閘道器 OpenResty、Linkerd 以及 Spring Cloud Gateway。

如果是真的追求高效能,那肯定是選擇 Nginx 或者 OpenResty 無疑了, 但是對效能要求不是很高的話,並且又在用 Spring Cloud 系列,那當然就要選擇 Spring Cloud Gateway 了。

閘道器的基礎就是路由功能,通俗解釋就是地址轉發,將一個請求地址轉發到實際的服務地址。比如請求的是 http://xxx.com/api 的路由地址,實際上會被轉發到 http://xxx.com:8888 上來,這就是個最簡單的路由方式。

我們可以理解為 Spring Cloud Gateway 就是針對進來的請求做各種判斷和處理,比如說判斷請求的合法性、許可權驗證,請求地址改寫,請求引數、頭資訊、cookie 資訊的分析和改寫,請求速率控制,日誌留存等。而這些都可以方便的通過 Predicate 和 GatewayFilter 來組合實現。

建立 Spring Cloud Gateway 專案

Spring Cloud 版本是 Greenwich.SR2,Spring Boot 版本 2.1.6.RELEASE,JDK 1.8。

接下來正式建立一個 Gateway 專案。

首先做兩個微服務,當做路由轉發的目標服務

兩個微服務是以 consul 作為服務註冊中心的,可以看這篇文章服務註冊發現、配置中心集一體的 Spring Cloud Consul

1、建立 consul-order 服務,具體可以去 github 上看程式碼(github.com/huzhicheng/…),很簡單的一個服務。建立的 RESTful Controller 如下:

@RestController
@RequestMapping(value = "order")
public class OrderController {

@Value("${spring.application.name}")
private String applicationName;


@GetMapping(value = "get")
public CustomerOrder getOrder(){
CustomerOrder customerOrder = new CustomerOrder();
customerOrder.setOrderId("9999");
customerOrder.setProductName("MacBook Pro");
customerOrder.setClient(applicationName);
return customerOrder;
}
}
複製程式碼

總之,最後直接訪問這個介面的地址為 http://localhost:5006/order/get

2、建立 consul-user 服務,具體程式碼可以到 github 上檢視(github.com/huzhicheng/…)。建立的 RESTful Controller 內容如下:

@RestController
@RequestMapping(value = "user")
public class UserController {

@GetMapping(value = "get")
public User getUserInfo(){
User user = new User();
user.setName("古時的風箏");
user.setAge(8);
user.setLocation("北京");
return user;
}
}
複製程式碼

和上面的微服務有點區別的就是設定了 context-path

server:
port: 5005
servlet:
context-path: /user-service
複製程式碼

之所以這樣不同的設定,是因為下面要驗證一個 filter。總之,最後上述介面的訪問地址為:http://localhost:5005/user-service/user/get

建立一個專案,並引入 maven 包

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
複製程式碼

配置簡單的路由轉發

路由配置有兩種方式。一種是配置檔案,另外一種是程式碼方式配置,WebFlux 的反應式程式設計方式。所以我們 pom 檔案中要引入 WebFlux 的包。這是 Spring 5 的新特性。

1、先看第一種配置檔案方式配置:

server:
port: 10000
spring:
application:
name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: false # 是否將服務id轉換為小寫
routes:
- id: userServiceRouter
uri: lb://consul-user
predicates:
- Path=/user-service/**
- id: orderServiceRouter
uri: lb://consul-order
predicates:
- Path=/order-service/**
filters:
- StripPrefix=1
consul:
host: localhost #註冊gateway閘道器到consul
port: 8500
discovery:
service-name: service-gateway
複製程式碼

其中包括 Spring Boot 專案的基本配置,name、port ,還有關於 consul 的配置,要將閘道器服務註冊到註冊中心。

上面配置中建立了兩條路由規則,路由規則名稱通過 id 設定,分別是 userServiceRouter 和 orderServiceRouter,通過 predicates.Path 設定待轉發的 url,通過 uri 設定轉發後的目標地址。上面配置將以/user-service/開頭的地址轉發到 lb://consul-user,固定格式 lb + 服務id,在有註冊中心的情況下要這樣寫,如過沒有註冊中心,可以直接寫目標 url。

下面的路由規則中多了一個 StripPrefix 的 filter ,這個是 Gateway 的內建 filter,作用就是去掉 Path 中的指定部分,StripPrefix=1,就是以 / 分隔,去掉第一部分,比如 /a/b/c 這個地址,在 StripPrefix=1 的作用下,就會轉發到 /b/c/,當 StripPrefix=2 的時候,就會轉發到 /c/。

配置好上述介面,然後啟動閘道器服務。訪問規則就會有如下對應關係:

http://localhost:10000/user-service/user/get->http://localhost:5005/user-service/user/get

http://localhost:10000/order-service/order/get->http://localhost:5006/order/get

當然這只是針對每一個目標服務只有一個例項的情況,如果有多個例項,就會按照負載策略落到對應的例項中。

2、程式碼方式的路由配置

@Bean
public RouteLocator kiteRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("userRouter", r -> r.path("/user-service/**")
.filters(f ->
f.addResponseHeader("X-CustomerHeader", "kite"))
.uri("lb://consul-user")
)
.route("orderRouter", r -> r.path("/order-service/**")
.filters(f -> f.stripPrefix(1)).uri("lb://consul-order")
)
.build();
}
複製程式碼

上面的這段程式碼和前面的配置檔案的內容是同樣的作用。只要實現一個返回型別為 RouteLocator,引數為 RouteLocatorBuilder型別的 Bean。

你看後面那一連串的 r.path().filters().uri() 了嗎,用它們就可以簡單的配置出路由規則,而且可讀性也比較強。另外,Gateway 還套用了 Predicate 的規則來構建更加靈活、複雜的路由規則。Predicate 是 Java 8 增加的邏輯計算庫,有 negate()、and()、or()、isEqual()幾個方法。具體的程式碼在 PredicateSpec 和 UriSpec 這兩個類裡,一目瞭然。

PredicateSpec 裡有這麼多方法,都可以結合 and、or 組合起來使用。

接下來就說到 filter,Gateway 內建了很多的 filter,可以在 GatewayFilterSpec 類下找到方法封裝,每一個 filter 都由一個 factory 的 apply 實現,都在 org.springframework.cloud.gateway.filter.factory包下,有必要的話可以直接看原始碼。 比如上面用到的 StripPrefix。還有 addResponseHeader,它的作用是在 Response 物件的 header 中新增請求頭。

啟動閘道器服務

啟動閘道器,並訪問兩個介面測試,介面分別為 http://localhost:10000/user-service/user/get和http://localhost:10000/order-service/order/get,正常返回資料,則說明閘道器服務配置正常。

巧用 StripPrefix filter

微服務多了之後,路由的轉發規則也就多了,比方說訂單相關請求要轉發到訂單微服務叢集,使用者相關請求要轉發到使用者微服務叢集,最終開放給終端的介面也要能表明是哪個微服務的,除了介面文件裡說明之外,介面本身最好也能明確標識。

一種方式是在微服務的配置檔案中配置上server.servlet.context-path

還有一種方式就是在路由規則的 path 中配置,然後加上 StripPrefix 配置,選擇性的去掉請求 url 中的某些部分。比如我們請求 Gateway的地址為 order-service/order/get,則經過 StripPrefix(1) 之後,會把請求地址變為 order/get,然後根據路由規則定向到具體的微服務地址或者特定的 url。

本篇就介紹 Spring Cloud Gateway 的基本用法,後續還會有關於整合安全認證、鑑權、限流、日誌等相關內容,敬請關注。

參考資料:

cloud.spring.io/spring-clou…

創作不易,點贊是美德,還能給我創作的動力。不用客氣了,讚我!

微信搜尋公眾號「古時的風箏」,也可以直接掃下面二維碼。關注之後可加微信,與群裡小夥伴交流學習,另有阿里等大廠同學可以直接內推。

本文對應的原始碼:請點這裡檢視

手把手教你使用 Spring Cloud Gateway

相關文章