天天看点

Spring gateway 源码解析之Route Predicate组件的类图 06

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。

详细类图

Spring gateway 源码解析之Route Predicate组件的类图 06

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