万世威 2020-10-16
基于 Spring 5, Spring Boot 2 and Project Reactor
动态路由
Predicates 和 filters 作用于特定路由
集成了 Hystrix 断路器
集成了 Spring Cloud DiscoveryClient
易于编写的 Predicates 和 Filters
限流
路径重写

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
spring:
  application:
    name: nacos-gateway-server
  cloud:
    gateway:
      routes:
        - id: provider
          uri: http://127.0.0.1:9002/
          predicates:
            - Path=/dist/** # url 中匹配这个地址后,转发到 uri 上,uri+Path 组成
问题:基本配置里面硬编码 uri 地址,不能做到负载、服务自动发现
解决:配置之前的 Nacos 动态服务功能实现
<!-- 引入依赖 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
/* main 方法上开启服务发现功能 */
@EnableDiscoveryClient
public class GatewayMain {}
spring:
  application:
    name: nacos-gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: provider
          #        uri: http://127.0.0.1:9002/
          uri: lb://quick-provider #配置提供者的注册的服务名spring.application.name
          predicates:
            - Path=/dist/**

# after
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
# cookie
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p
# method
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST
更多可参考官方文档:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#gateway-request-predicates-factories
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
    public AgeRoutePredicateFactory(){
        super(AgeRoutePredicateFactory.Config.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minAge", "maxAge");
    }
    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return serverWebExchange -> {
            String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
            if (!StringUtils.isEmpty(ageStr)) {
                int age = Integer.parseInt(ageStr);
                return age >= config.getMinAge() && age <= config.getMaxAge();
            }
            return false;
        };
    }
    public static class Config {
        private int minAge;
        private int maxAge;
        public Config() { }
      	// 省略 getter、setter
    }
}
predicates:
  - Age=18,30
1、Config 类 必须要有 构造函数
2、配置中 Age 和类名保持一致,且类名格式固定,以 RoutePredicateFactory 结尾
3、18-30,按照 shortcutFieldOrder 方法参数顺序注入值
4、具体可参考内置的 Predicate Factory,采用 xxxRoutePredicateFactory
 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factories
/* 使用此方式可在 yaml 中配置 */
@Component
public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {
    private static final Log log = LogFactory.getLog(GatewayFilter.class);
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";
    private static final String KEY = "withParams";
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(KEY);
    }
    public RequestTimeGatewayFilterFactory() {
        super(Config.class);
    }
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
            return chain.filter(exchange).then(
                    Mono.fromRunnable(() -> {
                        Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                        if (startTime != null) {
                            StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
                                    .append(": ")
                                    .append(System.currentTimeMillis() - startTime)
                                    .append("ms");
                            if (config.isWithParams()) {
                                sb.append(" params:").append(exchange.getRequest().getQueryParams());
                            }
                            log.info(sb.toString());
                        }
                    })
            );
        };
    }
    public static class Config {
        private boolean withParams;
        public boolean isWithParams() {
            return withParams;
        }
        public void setWithParams(boolean withParams) {
            this.withParams = withParams;
        }
    }
}
gateway:
	routes:
	- id: provider
		uri: lb://quick-provider
    filters:
    - RequestTime=true
    predicates:
    - Path=/dist/**
 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html#_global_filters
public class AuthFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("invoke auth filter..");
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;// 返回值越小,优先级越高
    }
}
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.1.1.RELEASE/single/spring-cloud-gateway.html
https://github.com/spring-cloud/spring-cloud-gateway