歡迎訪問我的GitHub
這里分類和匯總了欣宸的全部原創(含配套原始碼):https://github.com/zq2599/blog_demos
本篇概覽
- 作為《Spring Cloud Gateway實戰》系列的第十四篇,本文會繼續發掘Spring Cloud Gateway的潛力,通過編碼體驗操控網關的樂趣,開發出一個實用的功能:讓Spring Cloud Gateway應用在收到請求后,可以按照業務的需要跳轉到任意的地址去
一般路由規則
- 先來看一個普通的路由規則,如下所示,意思是將所有/hello/**的請求轉發到http://127.0.0.1:8082這個地址去:
spring:
application:
name: hello-gateway
cloud:
gateway:
routes:
- id: path_route
uri: http://127.0.0.1:8082
predicates:
- Path=/hello/**
- 上述規則的功能如下圖所示,假設這就是生產環境的樣子,192.168.50.99:8082是提供服務的后臺應用:
特殊規則
- 以上是常規情況,但也有些特殊情況,要求SpringCloud Gateway把瀏覽器的請求轉發到不同的服務上去
- 如下圖所示,在之前的環境中增加了另一個服務(即藍色塊),假設藍色服務代表測驗環境
- 瀏覽器發起的/hello/str請求中,如果header中帶有tag-test-user,并且值等于true,此時要求SpringCloud Gateway把這個請求轉發到測驗環境
- 如果瀏覽器的請求header中沒有tag-test-user,SpringCloud Gateway需要像以前那樣繼續轉發到192.168.50.99:8082
- 很明顯,上述需求難以通過配置來實作,因為轉發的地址和轉發邏輯都是圍繞業務邏輯來定制的,這也就是本篇的目標:對同一個請求path,可以通過編碼轉發到不同的地方去
- 實作上述功能的具體做法是:自定義過濾器
設計
- 編碼之前先設計,把關鍵點想清楚再動手
- 今天咱們要開發一個SpringCloud Gateway應用,里面新增一個自定義過濾器
- 實作這個功能需要三個知識點作為基礎,也就是說,您會通過本篇實戰掌握以下知識點:
- 自定義過濾器
- 自定義過濾器的配置引數和bean的映射
- 編碼構造Route實體
- 用思維導圖將具體作業內容展開,如下圖所示,咱們就按部就班的實作吧:
原始碼下載
- 本篇實戰中的完整原始碼可在GitHub下載到,地址和鏈接資訊如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 鏈接 | 備注 |
---|---|---|
專案主頁 | https://github.com/zq2599/blog_demos | 該專案在GitHub上的主頁 |
git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該專案原始碼的倉庫地址,https協議 |
git倉庫地址(ssh) | [email protected]:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協議 |
- 這個git專案中有多個檔案夾,本篇的原始碼在spring-cloud-tutorials檔案夾下,如下圖紅框所示:
- spring-cloud-tutorials內部有多個子專案,本篇的原始碼在gateway-dynamic-route檔案夾下,如下圖紅框所示:
編碼
- 新建名為gateway-dynamic-route的maven工程,其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">
<parent>
<artifactId>spring-cloud-tutorials</artifactId>
<groupId>com.bolingcavalry</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>gateway-dynamic-route</artifactId>
<dependencies>
<dependency>
<groupId>com.bolingcavalry</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 如果父工程不是springboot,就要用以下方式使用插件,才能生成正常的jar -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bolingcavalry.gateway.GatewayDynamicRouteApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- 啟動類是普通的SpringBoot啟動類:
package com.bolingcavalry.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class GatewayDynamicRouteApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayDynamicRouteApplication.class,args);
}
}
- 接下來是本篇的核心,自定義過濾器類,代碼中已經添加了詳細的注釋,有幾處要注意的地方稍后會提到:
package com.bolingcavalry.gateway.filter;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriComponentsBuilder;
import java.net.URI;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
@Component
@Slf4j
public class BizLogicRouteGatewayFilterFactory extends AbstractGatewayFilterFactory<BizLogicRouteGatewayFilterFactory.BizLogicRouteConfig> {
private static final String TAG_TEST_USER = "tag-test-user";
public BizLogicRouteGatewayFilterFactory() {
super(BizLogicRouteConfig.class);
}
@Override
public GatewayFilter apply(BizLogicRouteConfig config) {
return (exchange, chain) -> {
// 本次的請求物件
ServerHttpRequest request = exchange.getRequest();
// 呼叫方請求時的path
String rawPath = request.getURI().getRawPath();
log.info("rawPath [{}]", rawPath);
// 請求頭
HttpHeaders headers = request.getHeaders();
// 請求方法
HttpMethod httpMethod = request.getMethod();
// 請求引數
MultiValueMap<String, String> queryParams = request.getQueryParams();
// 這就是定制的業務邏輯,isTestUser等于ture代表當前請求來自測驗用戶,需要被轉發到測驗環境
boolean isTestUser = false;
// 如果header中有tag-test-user這個key,并且值等于true(不區分大小寫),
// 就認為當前請求是測驗用戶發來的
if (headers.containsKey(TAG_TEST_USER)) {
String tageTestUser = headers.get(TAG_TEST_USER).get(0);
if ("true".equalsIgnoreCase(tageTestUser)) {
isTestUser = true;
}
}
URI uri;
if (isTestUser) {
log.info("這是測驗用戶的請求");
// 從組態檔中得到測驗環境的uri
uri = UriComponentsBuilder.fromHttpUrl(config.getTestEnvUri() + rawPath).queryParams(queryParams).build().toUri();
} else {
log.info("這是普通用戶的請求");
// 從組態檔中得到正式環境的uri
uri = UriComponentsBuilder.fromHttpUrl(config.getProdEnvUri() + rawPath).queryParams(queryParams).build().toUri();
}
// 生成新的Request物件,該物件放棄了常規路由配置中的spring.cloud.gateway.routes.uri欄位
ServerHttpRequest serverHttpRequest = request.mutate().uri(uri).method(httpMethod).headers(httpHeaders -> httpHeaders = httpHeaders).build();
// 取出當前的route物件
Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
//從新設定Route地址
Route newRoute =
Route.async().asyncPredicate(route.getPredicate()).filters(route.getFilters()).id(route.getId())
.order(route.getOrder()).uri(uri).build();
// 放回exchange中
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR,newRoute);
// 鏈式處理,交給下一個過濾器
return chain.filter(exchange.mutate().request(serverHttpRequest).build());
};
}
/**
* 這是過濾器的配置類,配置資訊會保存在此處
*/
@Data
@ToString
public static class BizLogicRouteConfig {
// 生產環境的服務地址
private String prodEnvUri;
// 測驗環境的服務地址
private String testEnvUri;
}
}
- 上述代碼中要注意的地方如下:
- BizLogicRouteConfig是過濾器的配置類,可以在使用過濾器時在組態檔中配置prodEnvUri和testEnvUri的值,在代碼中可以通過這兩個欄位取得配置值
- 過濾器的工廠類名為BizLogicRouteGatewayFilterFactory,按照規則,過濾器的名字是BizLogicRoute
- 在apply方法中,重新創建ServerHttpRequest和Route對象,它們的引數可以按照業務需求隨意設定,然后再將這兩個物件設定給SpringCloud gateway的處理鏈中,接下來,處理鏈上的其他過濾拿到的就是新的ServerHttpRequest和Route物件了
配置
- 假設生產環境地址是http://127.0.0.1:8082,測驗環境地址是http://127.0.0.1:8087,整個SpringCloud Gateway應用的組態檔如下,可見使用了剛剛創建的過濾器,并且為此過濾器配置了兩個引數:
server:
#服務埠
port: 8086
spring:
application:
name: gateway-dynamic-route
cloud:
gateway:
routes:
- id: path_route
uri: http://0.0.0.0:8082
predicates:
- Path=/hello/**
filters:
- name: BizLogicRoute
args:
prodEnvUri: http://127.0.0.1:8082
testEnvUri: http://127.0.0.1:8087
- 至此,編碼完成了,啟動這個服務
開發和啟動后臺服務,模擬生產和測驗環境
- 接下來開始驗證功能是否生效,咱們要準備兩個后臺服務:
- 模擬生產環境的后臺服務是provider-hello,監聽埠是8082,其/hello/str介面的回傳值是Hello World, 2021-12-12 10:53:09
- 模擬測驗環境的后臺服務是provider-for-test-user,監聽埠是8087,其/hello/str介面的回傳值是Hello World, 2021-12-12 10:57:11 (from test enviroment)(和生產環境相比,回傳內容多了個(from test enviroment)),對應Controller參考如下:
package com.bolingcavalry.provider.controller;
import com.bolingcavalry.common.Constants;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
@RestController
@RequestMapping("/hello")
public class Hello {
private String dateStr(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
}
/**
* 回傳字串型別
* @return
*/
@GetMapping("/str")
public String helloStr() {
return Constants.HELLO_PREFIX + ", " + dateStr() + " (from test enviroment)";
}
}
- 以上兩個服務,對應的代碼都在我的Github倉庫中,如下圖紅框所示:
- 啟動gateway-dynamic-route、provider-hello、provider-for-test-user服務
- 此時,SpringCloud gateway應用和兩個后臺服務都啟動完成,情況如下圖,接下來驗證剛才開發的過濾器能不能像預期那樣轉發:
驗證
- 用postman工具向gateway-dynamic-route應用發起一次請求,回傳值如下圖紅框所示,證明這是provider-hello的回應,看來咱們的請求已經正常到達:
- 再發送一次請求,如下圖,這次在header中加入鍵值對,得到的結果是provider-for-test-user的回應
- 至此,過濾器的開發和驗證已經完成,通過編碼,可以把外部請求轉發到任何咱們需要的地址去,并且支持引數配置,這個過濾器還有一定的可配置下,減少了硬編碼的比率,如果您正在琢磨如何深度操控SpringCloud Gateway,希望本文能給您一些參考;
歡迎關注博客園:程式員欣宸
學習路上,你不孤單,欣宸原創一路相伴...
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/556086.html
標籤:Java
下一篇:返回列表