1.Route兩種定義方式,都是等價的
routes:
# =====================================
# to run server
# $ wscat --listen 9000
# to run client
# $ wscat --connect ws://localhost:8080/echo
- id: websocket_test
uri: ws://localhost:9000
order: 9000
predicates:
- Path=/echo
- Host=www.baidu.com
builder.routes()
.route(r ->
//形成一個連結清單
r.host("**.abc.org").and().path("/anything/png").or().path("abc").and().path("123")
.filters(f ->
f.prefixPath("/httpbin")
.addResponseHeader("X-TestHeader", "foobar"))
.uri(uri)
)
2.需要實作的功能點:
1:支援同步或者異步的方式執行,并且同步和異步類型可以轉換。
2:需要支援不同方式來定義Route (yaml 或者 代碼)。
4:java 方式定義Route 的文法是 鍊式操作(也叫FluentAPI ),同時一個Route可以配置多個的斷言,比如(a&b&c|d)
答:實作原理是通過GatewayFilterSpec,PredicateSpec, BooleanSpec這三個繼承了UriSpec的類實作的,然後外加BooleanSpec的 BooleanOpSpec繼承了PredicateSpec,進而支援鍊式文法操作
比如 and(),or() 等方法傳回的是 BooleanOpSpec 就可以關聯PredicateSpec支援的方法after,before 等等,這樣就可以 path("/anything/png").or().path("abc").and().path("123") 這種寫法。
yaml 配置檔案是通過RouteDefinitionRouteLocator::combinePredicates 來生成 AsyncPredicate對象的。AsyncPredicate 裡面是有 left ,right 屬性 用來儲存連結清單操作的關聯關系。
3:需要支援Route清單緩存。
答:通過CacheRouteLocator來實作的。
5: 根據斷言規則比對到路由後會 執行gateway filter 和 gloable filter。
詳細類圖
自定義Predicate
public class RandomRoutePredicateFactory extends AbstractRoutePredicateFactory<RandomRoutePredicateFactory.Config> {
public RandomRoutePredicateFactory() {
super(RandomRoutePredicateFactory.Config.class);
}
@Override
public ShortcutType shortcutType() {
return ShortcutType.GATHER_LIST;
}
@Override
public List<String> shortcutFieldOrder() {
return Collections.singletonList("random");
}
@Override
public Predicate<ServerWebExchange> apply(Config config) {
return new GatewayPredicate() {
@Override
public boolean test(ServerWebExchange exchange) {
String token = exchange.getRequest().getHeaders().getFirst("random");
return null == token ? false : validate(token);
}
private boolean validate(String token) {
// 可以替換成shiro、jwt、oauth或者cas/sso之類的,,其實這個場景更适合用filter
// 這裡隻是示範一下predicate,傳遞的參數如果是以這個token開頭就運作,走指定路由
return token.toLowerCase().startsWith(config.getRandom().toLowerCase());
}
@Override
public String toString() {
return String.format("random: %s", config.getRandom());
}
};
}
public static class Config {
private String random;
public String getRandom() {
return random;
}
public void setRandom(String random) {
this.random = random;
}
public RandomRoutePredicateFactory.Config setToken(String random) {
this.random = random;
return this;
}
}
}
routes:
# =====================================
# to run server
# $ wscat --listen 9000
# to run client
# $ wscat --connect ws://localhost:8080/echo
- id: websocket_test
uri: ws://localhost:9000
order: 9000
predicates:
- Path=/echo
- Host=www.baidu.com
# =====================================
# - id: default_path_to_httpbin
# uri: ${test.uri}
# order: 10000
# predicates:
# - Path=/**
- id: random-test
uri: ${test.uri}
order: 10000
predicates:
- Path=/**
- name: Random
args:
random=123
- Token=CC
備注:
1: 這個類圖涉及了 Builder (https://www.runoob.com/w3cnote/builder-pattern.html)和 政策 設計模式,java的 predicate 和 function 接口.
public class TestFunction {
public static void main(String[] args) {
System.out.println(testFunction(i -> i * 2 + 1));
}
public static int testFunction( Function<Integer,Integer> function) {
return function.apply(2);
}
}
public class PredicateDemo {
public static void main(String[] args) {
//給list添加參數
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
System.out.println("輸出所有參數字:");
eval(list, n -> true);
System.out.println("\n輸出能被2整除的數字:");
eval(list, n -> n%2==0);
System.out.println("\n輸出大于3的數字:");
eval(list, n-> n > 3 );
}
//自定義方法
public static void eval(List<Integer> list, Predicate<Integer> predicate) {
for(Integer n: list) {
if(predicate.test(n)) {
//可以将滿足條件的參數傳回,這裡隻做輸出
System.out.print(n + " ");
}
}
}
}