詳解SpringCloud-gateway動態路由兩種方式,以及路由載入過程
gateway配置路由主要有兩種方式,一種是用yml配置檔案,一種是寫程式碼裡,這兩種方式都是不支援動態配置的。如:
下面就來看看gateway是如何載入這些配置資訊的。
1 路由初始化
無論是yml還是程式碼,這些配置最終都是被封裝到RouteDefinition物件中。
一個RouteDefinition有個唯一的ID,如果不指定,就預設是UUID,多個RouteDefinition組成了gateway的路由系統。
所有路由資訊在系統啟動時就被載入裝配好了,並存到了記憶體裡。我們從原始碼來看看。
圓圈裡就是裝配yml檔案的,它返回的是PropertiesRouteDefinitionLocator,該類繼承了RouteDefinitionLocator,RouteDefinitionLocator就是路由的裝載器,裡面只有一個方法,就是獲取路由資訊的。該介面有多個實現類,分別對應不同方式配置的路由方式。
通過這幾個實現類,再結合上面的AutoConfiguration裡面的Primary資訊,就知道載入配置資訊的順序。
PropertiesRouteDefinitionLocator-->|配置檔案載入初始化| CompositeRouteDefinitionLocator
RouteDefinitionRepository-->|儲存器中載入初始化| CompositeRouteDefinitionLocator
DiscoveryClientRouteDefinitionLocator-->|註冊中心載入初始化| CompositeRouteDefinitionLocator
這是第一順序,就是從CachingRouteLocator中獲取路由資訊,我們可以開啟該類進行驗證。
不管發起什麼請求,必然會走上面的斷點處。請求一次,走一次。這是將路由資訊快取到了Map中。配置資訊一旦請求過一次,就會被快取到上圖的CachingRouteLocator類中,再次發起請求後,會直接從map中讀取。
如果想動態重新整理配置資訊,就需要發起一個RefreshRoutesEvent的事件,上圖的cache會監聽該事件,並重新拉取路由配置資訊。
通過下圖,可以看到如果沒有RouteDefinitionRepository的例項,則預設用InMemoryRouteDefinitionRepository。而做動態路由的關鍵就在這裡。即通過自定義的RouteDefinitionRepository類,來提供路由配置資訊。
例如:
在getRouteDefinitions方法返回你自定義的路由配置資訊即可。這裡可以用資料庫、nosql等等任意你喜歡的方式來提供。而且配置資訊修改後,發起一次RefreshRoutesEvent事件即可讓配置生效。這就是動態配置路由的核心所在,下面來看具體程式碼實現。
2 基於資料庫、快取的動態路由
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">
<modelVersion>4.0.0</modelVersion>
<groupId>com.maimeng</groupId>
<artifactId>apigateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>apigateway</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
<!--<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
注意這裡是SR1,經測試SR2有bug,會出問題。
@Configuration
public class RedisConfig {
@Bean(name = {"redisTemplate", "stringRedisTemplate"})
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(factory);
return redisTemplate;
}
}
核心類:
@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
public static final String GATEWAY_ROUTES = "geteway_routes";
@Resource
private StringRedisTemplate redisTemplate;
@Override
public Flux<RouteDefinition> getRouteDefinitions() {
List<RouteDefinition> routeDefinitions = new ArrayList<>();
redisTemplate.opsForHash().values(GATEWAY_ROUTES).stream()
.forEach(routeDefinition -> routeDefinitions.add(JSON.parseObject(routeDefinition.toString(), RouteDefinition.class)));
return Flux.fromIterable(routeDefinitions);
}
@Override
public Mono<Void> save(Mono<RouteDefinition> route) {
return null;
}
@Override
public Mono<Void> delete(Mono<String> routeId) {
return null;
}
}
主要是在get方法裡,此處從redis裡獲取配置好的Definition。
然後我們的工作就是將配置資訊,放到redis裡即可。
下面就是我模擬的一個配置,等同於在yml裡
spring: cloud: gateway: routes: - id: header uri: http://localhost:8888/header filters: - AddRequestHeader=header, addHeader - AddRequestParameter=param, addParam predicates: - Path=/jd
@Resource
private StringRedisTemplate redisTemplate;
@PostConstruct
public void main() {
RouteDefinition definition = new RouteDefinition();
definition.setId("id");
URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
// URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
definition.setUri(uri);
//定義第一個斷言
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName("Path");
Map<String, String> predicateParams = new HashMap<>(8);
predicateParams.put("pattern", "/jd");
predicate.setArgs(predicateParams);
//定義Filter
FilterDefinition filter = new FilterDefinition();
filter.setName("AddRequestHeader");
Map<String, String> filterParams = new HashMap<>(8);
//該_genkey_字首是固定的,見org.springframework.cloud.gateway.support.NameUtils類
filterParams.put("_genkey_0", "header");
filterParams.put("_genkey_1", "addHeader");
filter.setArgs(filterParams);
FilterDefinition filter1 = new FilterDefinition();
filter1.setName("AddRequestParameter");
Map<String, String> filter1Params = new HashMap<>(8);
filter1Params.put("_genkey_0", "param");
filter1Params.put("_genkey_1", "addParam");
filter1.setArgs(filter1Params);
definition.setFilters(Arrays.asList(filter, filter1));
definition.setPredicates(Arrays.asList(predicate));
System.out.println("definition:" + JSON.toJSONString(definition));
redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
}
定義好後,將其放到redis裡,之後啟動專案訪問/jd,再啟動後臺的localhost:8888專案。即可進行驗證。
之後如果要動態修改配置,就可以通過類似於上面的方式,來獲取json字串,然後將字串放到redis裡進行替換。替換後,需要通知gateway主動重新整理一下。
重新整理時,可以定義一個controller,然後呼叫一下notifyChanged()方法,就能完成新配置的替換了。
3 通過REST介面
gateway是自帶介面能增刪改查配置的,這個網上有比較多的教程,隨便找個看看就明白了。譬如:
我發個類作為參考
package com.maimeng.apigateway.route;
import com.alibaba.fastjson.JSON;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.filter.FilterDefinition;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static com.maimeng.apigateway.repository.RedisRouteDefinitionRepository.GATEWAY_ROUTES;
/**
* @author wuweifeng wrote on 2018/10/25.
*/
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Resource
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
private void notifyChanged() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
/**
* 增加路由
*
*/
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
notifyChanged();
return "success";
}
/**
* 更新路由
*/
public String update(RouteDefinition definition) {
try {
this.routeDefinitionWriter.delete(Mono.just(definition.getId()));
} catch (Exception e) {
return "update fail,not find route routeId: " + definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
notifyChanged();
return "success";
} catch (Exception e) {
return "update route fail";
}
}
/**
* 刪除路由
*
*/
public String delete(String id) {
try {
this.routeDefinitionWriter.delete(Mono.just(id));
notifyChanged();
return "delete success";
} catch (Exception e) {
e.printStackTrace();
return "delete fail";
}
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
@Resource
private StringRedisTemplate redisTemplate;
@PostConstruct
public void main() {
RouteDefinition definition = new RouteDefinition();
definition.setId("id");
URI uri = UriComponentsBuilder.fromHttpUrl("http://127.0.0.1:8888/header").build().toUri();
// URI uri = UriComponentsBuilder.fromHttpUrl("http://baidu.com").build().toUri();
definition.setUri(uri);
//定義第一個斷言
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName("Path");
Map<String, String> predicateParams = new HashMap<>(8);
predicateParams.put("pattern", "/jd");
predicate.setArgs(predicateParams);
//定義Filter
FilterDefinition filter = new FilterDefinition();
filter.setName("AddRequestHeader");
Map<String, String> filterParams = new HashMap<>(8);
//該_genkey_字首是固定的,見org.springframework.cloud.gateway.support.NameUtils類
filterParams.put("_genkey_0", "header");
filterParams.put("_genkey_1", "addHeader");
filter.setArgs(filterParams);
FilterDefinition filter1 = new FilterDefinition();
filter1.setName("AddRequestParameter");
Map<String, String> filter1Params = new HashMap<>(8);
filter1Params.put("_genkey_0", "param");
filter1Params.put("_genkey_1", "addParam");
filter1.setArgs(filter1Params);
definition.setFilters(Arrays.asList(filter, filter1));
definition.setPredicates(Arrays.asList(predicate));
System.out.println("definition:" + JSON.toJSONString(definition));
redisTemplate.opsForHash().put(GATEWAY_ROUTES, "key", JSON.toJSONString(definition));
}
}
相關推薦
詳解SpringCloud-gateway動態路由兩種方式,以及路由載入過程
gateway配置路由主要有兩種方式,一種是用yml配置檔案,一種是寫程式碼裡,這兩種方式都是不支援動態配置的。如: 下面就來看看gateway是如何載入這些配置資訊的。 1 路由初始化 無論是yml還是程式碼,這些配置最終都是被封裝到RouteDefinition
詳解Python拼接字串的七種方式
幾乎任何一種程式語言,都把字串列為最基礎和不可或缺的資料型別。而拼接字串是必備的一種技能。今天,我跟大家一起來學習Python拼接字串的七種方式。 1、來自C語言的%方式 print('%s %s' % ('Hello', 'world')) >
07.Spring-配置詳解-Spirng建立物件的三種方式
測試類 package vc.helloworld.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springfra
動態代理 兩種方式
代理類Proxy和RealSubject應該實現了相同的功能介面,在面向物件的程式設計之中,如果想要兩個物件實現相同的功能,有以下兩種方式: 1) 定義一個功能介面,然後代理類Proxy和真實類
詳解PHP處理密碼的幾種方式
在 PHP中,經常會對使用者身份進行認證。本文意在討論對密碼的處理,也就是對密碼的加密處理。 MD5 相信很多PHP開發者在最先接觸PHP的時候,處理密碼的首選加密函式可能就是MD5了,我當時就是這樣的: $password = md5($_POST["password"]); 上面這段程式碼是不是很
虛擬機器下 solr7.1 cloud 叢集搭建 (手動解壓和官方指令碼兩種方式)
準備工作: vmware workstation 12,OS使用的是ubuntu16.04,三臺虛擬機器搭建一個solr叢集,zookeeper共用這三臺虛擬機器組成zookeeper叢集。 zookeeper的版本為3.4.10,solr版本為7.1,不使用
二進位制詳解:世界上有10種人,一種懂二進位制,一種不懂。
目錄 一、十進位制整數轉二進位制、八進位制、十六進位制 二、二進位制轉十進位制、八進位制、十六進位制 三、十進位制、八進位制、十六進位制轉二進位制 四、二進位制小數與十進位制小數 一、十進位制整數轉二進位制、八進位制、十六進位制 首先是一張 十進位制
javascript消除字符串兩邊空格的兩種方式,面向對象和函數式編程
xxx 字符 name tco 由於 pre 字符串 對象實例 () 主要是javascript中消除字符串空格,比較兩種方式的不同 //面向對象,消除字符串兩邊空格 String.prototype.trim = function() { return this.rep
對Java代碼加密的兩種方式,防止反編譯
java加密使用Virbox Protector對Java項目加密有兩種方式,一種是對War包加密,一種是對Jar包加密。Virbox Protector支持這兩種文件格式加密,可以加密用於解析class文件的java.exe,並且可以實現項目源碼綁定制定設備,防止部署到客戶服務器的項目被整體拷貝。兩種加密方
SpringBoot配置Bean的兩種方式--註解以及配置文件
cep tms ast doc ice print str PE 寫實 一、註解方式編寫實體類:package com.example.bean;import org.springframework.boot.context.properti
操作系統,編程語言分類,執行python兩種方式,變量,內存管理,定義變量的三個特征
什麽 height 取代 沒有 一個 Coding 開發 軟件 簡單 操作系統 1、什麽是操作系統 操作系統位於計算機硬件與應用軟件之間 是一個協調、管理、控制計算機硬件資源與軟件資源的控制程序2、為何要有操作系統? 1、控制硬件 2、把對硬件的復雜
python制作電腦定時關機辦公神器,另含其它兩種方式,無需編程!
自動 靜態 自己的 基礎 win src 成功 他會 bsp 小編本人目前就是在電腦面前工作,常常會工作到淩晨兩三點還在為自己的夢想奮鬥著。有時在辦公椅上就稀裏糊塗睡著了,我相信有很多朋友和我一樣,這樣是很不好的。第一對身體不好,第二對電腦不好。 對身體方面,小編也
實現多線程的兩種方式,賣票場景,親測可用
fig his nts conf desc end scrip thread tar 寫在開始 賣票場景: 多線程共同賣票,總票數在多個賣票窗口共享 實現方式: 1繼承Thread類; 2實現Runnable接口 正文開始 方式1 Thread繼承 packa
Maven執行TestNG的testcase 兩種方式,一種testng.xml,一種testngCase.java
詳情參照: http://maven.apache.org/surefire/maven-surefire-plugin/examples/inclusion-exclusion.html 1.maven通過maven-surefire-plugin來執行maven專案中src/test/j
Axios傳參的兩種方式,表單資料和json字串(Form Data和Request Payload)
第一種方式:Form Data Axios引數配置: 1、引入 import Qs from 'qs' 2、 return request({ headers: { 'Content-Type': 'application/x-www-form-
java之執行緒建立的兩種方式,六種狀態和匿名內部類建立子類或實現類物件
一.匿名內部類建立子類或實現類物件 new Test(){} 相當於建立了Test類的子類物件 並且沒有類名 建立介面實現類 new 介面名() {};介面實現類的物件 注意 : new 後邊是類或者介面名 大括號內是類或者介面中的方法 public
PHP實現無限極分類的兩種方式,遞迴和引用
https://blog.csdn.net/falcom_fans/article/details/75579663 說到無限極分類,比較常見的做法是在建表的時候,增加一個PID欄位用來區別自己所屬的分類 由於展示資料的時候,需要表達出這種所屬關係,所以必然要在讀取資料
android開啟另外的app兩種方式,內建到自己本身的app,重新開啟app,
android開啟另外的app兩種方式,內建到自己本身的app空間,重新開啟app空間, 目錄 1、內建到自己本身的app空間, 被開啟的app的activity的主配置檔案的程式碼編寫、 <activity android:n
KEIL / MDK生成BIN檔案的兩種方式,直接複製就能用
在After Build/Rebuild選項卡中,勾選 "Run # 1",在後面輸入框寫入bin檔案生成方式,如下 圖1 第一種方式:設定絕對路徑(個人實踐過,沒有成功,不知道為什麼) "D:\Program Files\MDK516\ARM\ARMCC\bin\
Java ZXing 生成QRCode二維碼的兩種方式,可設定圖片大小,和外白色邊框大小
QRGen在ZXing基礎上開發,這個庫使得利用Java生成QR碼變為小菜一碟。它需要依賴ZXing,所以生成圖案時你同時需要ZXing和QRGen的jar包。 QR 碼最常見的應用便是為網站中一個特定的網頁或下載頁帶來流量。因此,QR碼常常會編碼URL或網站地址,使用者可以通過手機攝像頭掃描,並在其瀏覽器中