Spring?Cloud?Gateway編碼實(shí)現(xiàn)任意地址跳轉(zhuǎn)的示例
這里分類(lèi)和匯總了欣宸的全部原創(chuàng)(含配套源碼):https://github.com/zq2599/blog_demos
本篇概覽
作為《Spring Cloud Gateway實(shí)戰(zhàn)》系列的第十四篇,本文會(huì)繼續(xù)發(fā)掘Spring Cloud Gateway的潛力,通過(guò)編碼體驗(yàn)操控網(wǎng)關(guān)的樂(lè)趣,開(kāi)發(fā)出一個(gè)實(shí)用的功能:讓Spring Cloud Gateway應(yīng)用在收到請(qǐng)求后,可以按照業(yè)務(wù)的需要跳轉(zhuǎn)到任意的地址去
一般路由規(guī)則
先來(lái)看一個(gè)普通的路由規(guī)則,如下所示,意思是將所有/hello/**的請(qǐng)求轉(zhuǎn)發(fā)到http://127.0.0.1:8082這個(gè)地址去:
spring: application: name: hello-gateway cloud: gateway: routes: - id: path_route uri: http://127.0.0.1:8082 predicates: - Path=/hello/**
上述規(guī)則的功能如下圖所示,假設(shè)這就是生產(chǎn)環(huán)境的樣子,192.168.50.99:8082是提供服務(wù)的后臺(tái)應(yīng)用:
?
特殊規(guī)則
以上是常規(guī)情況,但也有些特殊情況,要求SpringCloud Gateway把瀏覽器的請(qǐng)求轉(zhuǎn)發(fā)到不同的服務(wù)上去
如下圖所示,在之前的環(huán)境中增加了另一個(gè)服務(wù)(即藍(lán)色塊),假設(shè)藍(lán)色服務(wù)代表測(cè)試環(huán)境
瀏覽器發(fā)起的/hello/str請(qǐng)求中,如果header中帶有tag-test-user,并且值等于true,此時(shí)要求SpringCloud Gateway把這個(gè)請(qǐng)求轉(zhuǎn)發(fā)到測(cè)試環(huán)境
如果瀏覽器的請(qǐng)求header中沒(méi)有tag-test-user,SpringCloud Gateway需要像以前那樣繼續(xù)轉(zhuǎn)發(fā)到192.168.50.99:8082
很明顯,上述需求難以通過(guò)配置來(lái)實(shí)現(xiàn),因?yàn)檗D(zhuǎn)發(fā)的地址和轉(zhuǎn)發(fā)邏輯都是圍繞業(yè)務(wù)邏輯來(lái)定制的,這也就是本篇的目標(biāo):對(duì)同一個(gè)請(qǐng)求path,可以通過(guò)編碼轉(zhuǎn)發(fā)到不同的地方去
實(shí)現(xiàn)上述功能的具體做法是:自定義過(guò)濾器
設(shè)計(jì)
- 編碼之前先設(shè)計(jì),把關(guān)鍵點(diǎn)想清楚再動(dòng)手
- 今天咱們要開(kāi)發(fā)一個(gè)SpringCloud Gateway應(yīng)用,里面新增一個(gè)自定義過(guò)濾器
- 實(shí)現(xiàn)這個(gè)功能需要三個(gè)知識(shí)點(diǎn)作為基礎(chǔ),也就是說(shuō),您會(huì)通過(guò)本篇實(shí)戰(zhàn)掌握以下知識(shí)點(diǎn):
- 自定義過(guò)濾器
- 自定義過(guò)濾器的配置參數(shù)和bean的映射
- 編碼構(gòu)造Route實(shí)例
用思維導(dǎo)圖將具體工作內(nèi)容展開(kāi),如下圖所示,咱們就按部就班的實(shí)現(xiàn)吧:
?
源碼下載
本篇實(shí)戰(zhàn)中的完整源碼可在GitHub下載到,地址和鏈接信息如下表所示(https://github.com/zq2599/blog_demos):
名稱 | 鏈接 | 備注 |
---|---|---|
項(xiàng)目主頁(yè) | https://github.com/zq2599/blog_demos | 該項(xiàng)目在GitHub上的主頁(yè) |
git倉(cāng)庫(kù)地址(https) | https://github.com/zq2599/blog_demos.git | 該項(xiàng)目源碼的倉(cāng)庫(kù)地址,https協(xié)議 |
git倉(cāng)庫(kù)地址(ssh) | git@github.com:zq2599/blog_demos.git | 該項(xiàng)目源碼的倉(cāng)庫(kù)地址,ssh協(xié)議 |
這個(gè)git項(xiàng)目中有多個(gè)文件夾,本篇的源碼在spring-cloud-tutorials文件夾下,如下圖紅框所示:
spring-cloud-tutorials內(nèi)部有多個(gè)子項(xiàng)目,本篇的源碼在gateway-dynamic-route文件夾下,如下圖紅框所示:
?
編碼
新建名為gateway-dynamic-route的maven工程,其pom.xml內(nèi)容如下:
<?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>
啟動(dòng)類(lèi)是普通的SpringBoot啟動(dòng)類(lèi):
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); } }
接下來(lái)是本篇的核心,自定義過(guò)濾器類(lèi),代碼中已經(jīng)添加了詳細(xì)的注釋,有幾處要注意的地方稍后會(huì)提到:
上述代碼中要注意的地方如下:
BizLogicRouteConfig是過(guò)濾器的配置類(lèi),可以在使用過(guò)濾器時(shí)在配置文件中配置prodEnvUri和testEnvUri的值,在代碼中可以通過(guò)這兩個(gè)字段取得配置值
過(guò)濾器的工廠類(lèi)名為BizLogicRouteGatewayFilterFactory,按照規(guī)則,過(guò)濾器的名字是BizLogicRoute
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) -> { // 本次的請(qǐng)求對(duì)象 ServerHttpRequest request = exchange.getRequest(); // 調(diào)用方請(qǐng)求時(shí)的path String rawPath = request.getURI().getRawPath(); log.info("rawPath [{}]", rawPath); // 請(qǐng)求頭 HttpHeaders headers = request.getHeaders(); // 請(qǐng)求方法 HttpMethod httpMethod = request.getMethod(); // 請(qǐng)求參數(shù) MultiValueMap<String, String> queryParams = request.getQueryParams(); // 這就是定制的業(yè)務(wù)邏輯,isTestUser等于ture代表當(dāng)前請(qǐng)求來(lái)自測(cè)試用戶,需要被轉(zhuǎn)發(fā)到測(cè)試環(huán)境 boolean isTestUser = false; // 如果header中有tag-test-user這個(gè)key,并且值等于true(不區(qū)分大小寫(xiě)), // 就認(rèn)為當(dāng)前請(qǐng)求是測(cè)試用戶發(fā)來(lái)的 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("這是測(cè)試用戶的請(qǐng)求"); // 從配置文件中得到測(cè)試環(huán)境的uri uri = UriComponentsBuilder.fromHttpUrl(config.getTestEnvUri() + rawPath).queryParams(queryParams).build().toUri(); } else { log.info("這是普通用戶的請(qǐng)求"); // 從配置文件中得到正式環(huán)境的uri uri = UriComponentsBuilder.fromHttpUrl(config.getProdEnvUri() + rawPath).queryParams(queryParams).build().toUri(); } // 生成新的Request對(duì)象,該對(duì)象放棄了常規(guī)路由配置中的spring.cloud.gateway.routes.uri字段 ServerHttpRequest serverHttpRequest = request.mutate().uri(uri).method(httpMethod).headers(httpHeaders -> httpHeaders = httpHeaders).build(); // 取出當(dāng)前的route對(duì)象 Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR); //從新設(shè)置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); // 鏈?zhǔn)教幚?,交給下一個(gè)過(guò)濾器 return chain.filter(exchange.mutate().request(serverHttpRequest).build()); }; } /** * 這是過(guò)濾器的配置類(lèi),配置信息會(huì)保存在此處 */ @Data @ToString public static class BizLogicRouteConfig { // 生產(chǎn)環(huán)境的服務(wù)地址 private String prodEnvUri; // 測(cè)試環(huán)境的服務(wù)地址 private String testEnvUri; } }
在apply方法中,重新創(chuàng)建ServerHttpRequest和Route對(duì)象,它們的參數(shù)可以按照業(yè)務(wù)需求隨意設(shè)置,然后再將這兩個(gè)對(duì)象設(shè)置給SpringCloud gateway的處理鏈中,接下來(lái),處理鏈上的其他過(guò)濾拿到的就是新的ServerHttpRequest和Route對(duì)象了
配置
假設(shè)生產(chǎn)環(huán)境地址是http://127.0.0.1:8082,測(cè)試環(huán)境地址是http://127.0.0.1:8087,整個(gè)SpringCloud Gateway應(yīng)用的配置文件如下,可見(jiàn)使用了剛剛創(chuàng)建的過(guò)濾器,并且為此過(guò)濾器配置了兩個(gè)參數(shù):
server: #服務(wù)端口 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
至此,編碼完成了,啟動(dòng)這個(gè)服務(wù)
開(kāi)發(fā)和啟動(dòng)后臺(tái)服務(wù),模擬生產(chǎn)和測(cè)試環(huán)境
接下來(lái)開(kāi)始驗(yàn)證功能是否生效,咱們要準(zhǔn)備兩個(gè)后臺(tái)服務(wù):
模擬生產(chǎn)環(huán)境的后臺(tái)服務(wù)是provider-hello,監(jiān)聽(tīng)端口是8082,其/hello/str接口的返回值是Hello World, 2021-12-12 10:53:09
模擬測(cè)試環(huán)境的后臺(tái)服務(wù)是provider-for-test-user,監(jiān)聽(tīng)端口是8087,其/hello/str接口的返回值是Hello World, 2021-12-12 10:57:11 (from test enviroment)(和生產(chǎn)環(huán)境相比,返回內(nèi)容多了個(gè)(from test enviroment)),對(duì)應(yīng)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()); } /** * 返回字符串類(lèi)型 * @return */ @GetMapping("/str") public String helloStr() { return Constants.HELLO_PREFIX + ", " + dateStr() + " (from test enviroment)"; } }
以上兩個(gè)服務(wù),對(duì)應(yīng)的代碼都在我的Github倉(cāng)庫(kù)中,如下圖紅框所示:
啟動(dòng)gateway-dynamic-route、provider-hello、provider-for-test-user服務(wù)
此時(shí),SpringCloud gateway應(yīng)用和兩個(gè)后臺(tái)服務(wù)都啟動(dòng)完成,情況如下圖,接下來(lái)驗(yàn)證剛才開(kāi)發(fā)的過(guò)濾器能不能像預(yù)期那樣轉(zhuǎn)發(fā):
?
驗(yàn)證
用postman工具向gateway-dynamic-route應(yīng)用發(fā)起一次請(qǐng)求,返回值如下圖紅框所示,證明這是provider-hello的響應(yīng),看來(lái)咱們的請(qǐng)求已經(jīng)正常到達(dá):
再發(fā)送一次請(qǐng)求,如下圖,這次在header中加入鍵值對(duì),得到的結(jié)果是provider-for-test-user的響應(yīng)
至此,過(guò)濾器的開(kāi)發(fā)和驗(yàn)證已經(jīng)完成,通過(guò)編碼,可以把外部請(qǐng)求轉(zhuǎn)發(fā)到任何咱們需要的地址去,并且支持參數(shù)配置,這個(gè)過(guò)濾器還有一定的可配置下,減少了硬編碼的比率,如果您正在琢磨如何深度操控SpringCloud Gateway,希望本文能給您一些參考
到此這篇關(guān)于Spring Cloud Gateway編碼實(shí)現(xiàn)任意地址跳轉(zhuǎn)的示例的文章就介紹到這了,更多相關(guān)Spring Cloud Gateway 任意地址跳轉(zhuǎn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用idea生成webservice客戶端超詳解步驟(wsdl文件的使用)
這篇文章主要給大家介紹了關(guān)于利用idea生成webservice客戶端超詳解步驟,第一次接觸webservice,從采坑到采坑,算是了解了一些,明白了一些,文中通過(guò)代碼以及圖文介紹的非常詳細(xì),需要的朋友可以參考下2023-12-12Java正則表達(dá)式驗(yàn)證固定電話號(hào)碼符合性
這篇文章主要介紹了Java正則表達(dá)式驗(yàn)證固定電話號(hào)碼符合性的實(shí)例代碼,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-09-09java操作mysql實(shí)現(xiàn)增刪改查的方法
這篇文章主要介紹了java操作mysql實(shí)現(xiàn)增刪改查的方法,結(jié)合實(shí)例形式分析了java操作mysql數(shù)據(jù)庫(kù)進(jìn)行增刪改查的具體實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-05-05使用監(jiān)聽(tīng)器對(duì)Spring bean id進(jìn)行唯一校驗(yàn)過(guò)程解析
這篇文章主要介紹了使用監(jiān)聽(tīng)器對(duì)Spring bean id進(jìn)行唯一校驗(yàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-08-08關(guān)于在IDEA熱部署插件JRebel使用問(wèn)題詳解
這篇文章主要介紹了關(guān)于在IDEA熱部署插件JRebel使用問(wèn)題詳解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Spring Batch讀取txt文件并寫(xiě)入數(shù)據(jù)庫(kù)的方法教程
這篇文章主要給大家介紹了Spring Batch讀取txt文件并寫(xiě)入數(shù)據(jù)庫(kù)的方法,SpringBatch 是一個(gè)輕量級(jí)、全面的批處理框架。這里我們用它來(lái)實(shí)現(xiàn)文件的讀取并將讀取的結(jié)果作處理,處理之后再寫(xiě)入數(shù)據(jù)庫(kù)中的功能。需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-04-04三行Java代碼實(shí)現(xiàn)計(jì)算多邊形的幾何中心點(diǎn)
因?yàn)楣ぷ餍枰?jì)算采煤機(jī)工作面的中心點(diǎn),如果套用數(shù)學(xué)的計(jì)算公式,用java去實(shí)現(xiàn),太麻煩了。本文將利用java幾何計(jì)算的工具包,幾行代碼就能求出多變形的中心,簡(jiǎn)直yyds!還不快跟隨小編一起學(xué)起來(lái)2022-10-10springboot使用alibaba的druid數(shù)據(jù)庫(kù)連接池錯(cuò)誤的問(wèn)題及解決
這篇文章主要介紹了springboot使用alibaba的druid數(shù)據(jù)庫(kù)連接池錯(cuò)誤的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-02-02