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 + " ");
}
}
}
}