springcloud?gateway實現(xiàn)簡易版灰度路由步驟詳解
前言
前陣子時間和朋友聊天,他們有個sass微服務,因為之前拆分過細,導致服務不僅調用鏈路過長,而且浪費服務資源,他們后面做了服務合并的重構,并即將上線。他覺得上線不能直接把線上的租戶都全切到重構版的sass微服務,而是需要實現(xiàn)如下的效果
他就問我說,有沒有啥開源平臺可以快速支持,因為之前時間都耗費在重構業(yè)務上,這塊就沒考慮周全,現(xiàn)在臨近上線,預留的時間不多。后面和他細聊,得知他們這套sass服務,租戶不多,其次他們微服務API網(wǎng)關是springcloud gateway。了解到這個信息后,我就跟他說直接拿API網(wǎng)關稍微改造一下,就可以達到他目前想要的效果。下面就來聊聊如何利用springcloud gateway實現(xiàn)簡易版灰度路由
實現(xiàn)關鍵
?springcloud gateway 自定義斷言工廠 + 開啟服務發(fā)現(xiàn)路由定位器 + PropertiesRouteDefinitionLocator 生成的route與DiscoveryClientRouteDefinitionLocator生成route path映射保持一致
實現(xiàn)步驟
注: 本示例注冊中心使用eureka,其他注冊中心也可以
1、項目POM引入相關GAV
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>
2、自定義斷言工廠
@Slf4j public class ParamRoutePredicateFactory extends AbstractRoutePredicateFactory<ParamRoutePredicateFactory.Config> { public static final String PARAM_KEY = "param"; public static final String PARAM_VALUES = "values"; public static final String SEPARATOR = "&"; public ParamRoutePredicateFactory() { super(Config.class); } @Override public List<String> shortcutFieldOrder() { return Arrays.asList(PARAM_KEY,PARAM_VALUES); } @Override public ShortcutType shortcutType() { return ShortcutType.DEFAULT; } @Override public Predicate<ServerWebExchange> apply(Config config) { return exchange -> isHitTargetParam(config, exchange); } private boolean isHitTargetParam(Config config, ServerWebExchange exchange) { boolean hasParamkey = HttpRequestParserUtils.hasKey(config.param.toLowerCase(), exchange); if(hasParamkey){ String value = HttpRequestParserUtils.parse(config.param.toLowerCase(), exchange); if(StringUtils.hasText(config.values) && config.values.contains(SEPARATOR)){ String[] valueArr = config.values.split(SEPARATOR); for (String targetValue : valueArr) { if(targetValue.equals(value)){ log.info(">>>>>>>>>>>>>>>>>>>> Request Key --> 【{}】 Hit Value --> 【{}】 In Target Values 【{}】", config.param,value, config.values); return true; } } } } return false; } @Validated public static class Config { @NotEmpty private String param; private String values; public String getParam() { return param; } public Config setParam(String param) { this.param = param; return this; } public String getValues() { return values; } public Config setValues(String values) { this.values = values; return this; } @Override public String toString() { return "Config{" + "param='" + param + '\'' + ", values=" + values + '}'; } }
3、配置斷言工程自動裝配
@Configuration @ConditionalOnProperty(name = "spring.cloud.gateway.ext.enabled", havingValue = "true",matchIfMissing = true) @AutoConfigureBefore({ GatewayDiscoveryClientAutoConfiguration.class}) @ConditionalOnClass(DispatcherHandler.class) public class GatewayAutoExtConfiguration { @Bean @ConditionalOnMissingBean @ConditionalOnProperty(name = "spring.cloud.gateway.properties-route-definition-locator.load.first", havingValue = "true",matchIfMissing = true) public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator( GatewayProperties properties) { return new PropertiesRouteDefinitionLocator(properties); } @Bean @ConditionalOnMissingBean public ParamRoutePredicateFactory paramRoutePredicateFactory(){ return new ParamRoutePredicateFactory(); } }
注: 這邊有些細節(jié)點說明一下,該配置先于GatewayDiscoveryClientAutoConfiguration裝配,主要是實現(xiàn)PropertiesRouteDefinitionLocator 比DiscoveryClientRouteDefinitionLocator優(yōu)先加載,為啥這么做,后面說
4、在application.yml文件開啟服務發(fā)現(xiàn)路由定位器
spring: cloud: gateway: discovery: locator: enabled: true lower-case-service-id: true
測試灰度路由
1、測試微服務comsumer1
a、測試配置
spring: application: name: ${APPLICATION_NAME:comsumer} profiles: active: eureka
b、編寫測試控制器
@RestController @RequestMapping("echo") public class EchoController { @GetMapping("{message}") public String echo(@PathVariable("message") String message){ System.out.println("comsumer:" + message); return "comsumer :" + message; } }
2、測試微服務comsumer2
a、測試配置
spring: application: name: ${APPLICATION_NAME:otherComsumer} profiles: active: eureka
b、編寫測試控制器
@RestController @RequestMapping("echo") public class EchoController { @GetMapping("{message}") public String echo(@PathVariable("message") String message){ System.out.println("otherComsumer:" + message); return "otherComsumer :" + message; } }
注:這個兩個服務主要用來模擬新老集群數(shù)據(jù)
3、網(wǎng)關添加測試路由配置
spring: cloud: gateway: routes: - id: route-springboot-gray-comsumer-to-other-comsumer uri: http://localhost:8083 predicates: - Path=/comsumer/** ## 多個租戶用&分割 - Param=tenantId,10000&10001&10002 filters: - StripPrefix=1 order: 0
注: 這個配置心細的朋友,可能會發(fā)現(xiàn)貓膩了。這個PATH和開啟服務發(fā)現(xiàn)路由定位器生成的PATH是一樣,我們再來說下為啥上面實現(xiàn)PropertiesRouteDefinitionLocator 比DiscoveryClientRouteDefinitionLocator優(yōu)先加載,因為路由定位器產生的route是有順序性,而當PropertiesRouteDefinitionLocator 和DiscoveryClientRouteDefinitionLocator配置的PATH一樣時,如果DiscoveryClientRouteDefinitionLocator優(yōu)于PropertiesRouteDefinitionLocator加載,就會導致訪問相同路徑時,會優(yōu)先訪問DiscoveryClientRouteDefinitionLocator生成的route,就不會去走我們自定義配置的route。不過這個結論為時尚早,留個懸念,待會說明
4、測試
1、當我們請求頭、cookie、query不加tenantId參數(shù)或者tenantId不為測試10000&10001&10002的值時
2、當tenantId滿足10000&10001&10002的其中任意值時
可以發(fā)現(xiàn)已經(jīng)路由到我們配置的地址
3、當我們對網(wǎng)關做如下配置
spring: cloud: gateway: properties-route-definition-locator: load: first: false
該配置主要是為了讓我們自定義的PropertiesRouteDefinitionLocator 的BEAN失效,這樣他就會按默認的加載邏輯,即DiscoveryClientRouteDefinitionLocator會先于PropertiesRouteDefinitionLocator 加載
同時路由做如下配置
spring: cloud: gateway: routes: - id: route-springboot-gray-comsumer-to-other-comsumer uri: http://localhost:8083 predicates: - Path=/comsumer/** ## 多個租戶用&分割 - Param=tenantId,10000&10001&10002 filters: - StripPrefix=1 order: -1000
即將order的數(shù)值調低。我們再驗證下
會發(fā)現(xiàn)效果和我們之前演示的效果是一樣的。其實這邊實現(xiàn)路由的關鍵點,是抓住route的順序性,相同路徑,誰先加載,誰先路由。所以我實現(xiàn)PropertiesRouteDefinitionLocator 比DiscoveryClientRouteDefinitionLocator會優(yōu)先加載,就是為了實現(xiàn)當path一樣時,PropertiesRouteDefinitionLocator 生成的route都比DiscoveryClientRouteDefinitionLocator生成route優(yōu)先,當然也可以通過配置order改變這個順序
總結
?本示例主要講解如何利用springcloud gateway實現(xiàn)簡易版灰度路由,不過該實現(xiàn)比較適用于灰度規(guī)則比較簡單的場景。如果需要復雜規(guī)則,就需要深層次的定制,或者采用用istio來實現(xiàn)也是一個挺好的選擇
https://github.com/lyb-geek/springboot-learning/tree/master/springboot-gateway-simple-gray
以上就是springcloud gateway實現(xiàn)簡易版灰度路由步驟詳解的詳細內容,更多關于spring cloud gateway灰度路由的資料請關注腳本之家其它相關文章!
相關文章
springboot中如何通過main方法調用service或dao
這篇文章主要介紹了springboot中如何通過main方法調用service或dao,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02SpringBoot2.x 整合 thumbnailator 圖片處理的示例代碼
這篇文章主要介紹了SpringBoot2.x 之整合 thumbnailator 圖片處理,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10Spring boot調用Oracle存儲過程的兩種方式及完整代碼
這篇文章主要給大家介紹了關于Spring boot調用Oracle存儲過程的兩種方式及完整代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2020-08-08JDK1.8下載、安裝和環(huán)境配置超詳細教程(最新最完整)
jdk1.8是一款功能強大的Java語音軟件開發(fā)工具包,JDK是學好Java的第一步,本文重點給大家介紹JDK1.8下載、安裝和環(huán)境配置教程,需要的朋友可以參考下2022-11-11Java數(shù)據(jù)長度獲取方式對比之length屬性、length()和size()方法詳解
在Java編程語言中l(wèi)ength、length()和size()是三個常見的用來獲取不同數(shù)據(jù)類型對象長度或大小的方法,但它們各自適用于不同的上下文,這篇文章主要給大家介紹了關于Java數(shù)據(jù)長度獲取方式對比之length屬性、length()和size()方法詳解2024-07-07