前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringCloud之Gateway

SpringCloud之Gateway

作者头像
用户3467126
发布2019-07-03 18:06:26
1.3K0
发布2019-07-03 18:06:26
举报
文章被收录于专栏:爱编码爱编码

简介

Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到api,并为它们提供交叉关注点,例如:安全性、监视/度量和弹性。

特点

Spring Cloud Gateway功能:

•基于Spring Framework 5、Project Reactor和Spring Boot 2.0构建•能够匹配任何请求属性上的路由。•谓词和过滤器是特定于路由的。•Hystrix断路器集成。•Spring Cloud DiscoveryClient集成•易于编写Predicates and Filters•请求速率限制•路径重写

相关术语

•Route:路由是网关的基本构件。它由ID、目标URI、谓词集合和过滤器集合定义。如果聚合谓词为真,则匹配路由。•Predicate:参照Java8的新特性Predicate。这允许开发人员匹配HTTP请求中的任何内容,比如头或参数。•Filter:在这里,可以在发送下游请求之前或之后修改请求和响应。

工作原理

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送给网关Web处理程序。此处理程序运行通过特定于请求的过滤器链发送请求。过滤器被虚线分隔的原因是过滤器可以在发送代理请求之前或之后执行逻辑。执行所有“预”筛选器逻辑,然后发出代理请求。发出代理请求后,执行“post”筛选器逻辑。

注:在没有端口的路由中定义的uri将得到HTTP和HTTPS uri的默认端口分别设置为80和443。

Predicate

Spring Cloud Gateway作为Spring WebFlux HandlerMapping基础设施的一部分匹配路由。Spring Cloud Gateway包含许多内置的路由谓词工厂。所有这些谓词都匹配HTTP请求的不同属性。可以组合多个路由谓词工厂,并通过逻辑和组合它们。

实例

本文使用springcloud2.0.3和springcloud的Finchley版。

1. 依赖如下:

代码语言:javascript
复制
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

2.使用内置的predicates

springcloud Gateway内置了许多的predicates:

注:图片来自互联网

下面application.yml文件配置针对以上的进行了简单的操作。

代码语言:javascript
复制
server:
  port: 8081
spring:
  profiles:
    ##指定你要激活的文件配置,对应下面所有配置的profiles的值。
    active: host_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://httpbin.org:80/get
        ## 断言:在这个时间之后的可以访问。也就说,下面这个时间设置,你访问的话是报404的。
        predicates:
        - After=2020-01-20T17:42:47.789-07:00[America/Denver]
  profiles: after_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://httpbin.org:80/get
        ## 断言:加头部信息
        predicates:
        - Header=X-Request-Id, \d+
  profiles: header_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://httpbin.org:80/get
        ## 断言:加Cookie信息。
        predicates:
        - Cookie=name, forezp
  profiles: cookie_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://httpbin.org:80/get
        ## 断言:按域名拦截
        predicates:
        - Host=**.xbmchina.cn
  profiles: host_route


---
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://httpbin.org:80/get
          ## 断言:按请求方式拦截
        predicates:
        - Method=GET
  profiles: method_route



---
spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: http://httpbin.org:80/get
          ## 断言:按地址路由拦截
        predicates:
        - Path=/foo/{segment}
  profiles: path_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://httpbin.org:80/get
          ## 断言:按查询参数拦截
        predicates:
        - Query=zero, hehe.
  profiles: query_route

Filter

Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

Spring Cloud Gateway内置有很多网关的工厂类。其中可分为两大类:

GatewayFilter 和 GlobalFilters

实例

下面有些常用栗子,更多可参考官网[1]。

SpringCloud内置的过滤器工厂类有如下:

通过application.yml进行配置即可使用

代码语言:javascript
复制
server:
  port: 8082
spring:
  profiles:
    active: elapse_route

---
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://httpbin.org:80/get
        ## 添加请求头
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  profiles: add_request_header_route

---
 ## 重写路由,跟Nginx的反向代理很像。
spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://blog.csdn.net
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(?<segment>.*), /$\{segment}
  profiles: rewritepath_route

自定义过滤器Filter

•1.一个拦截请求,打印请求时间的过滤器RequestTimeFilter

代码语言:javascript
复制
public class RequestTimeFilter implements GatewayFilter, Ordered {

    private static final Log log = LogFactory.getLog(GatewayFilter.class);
    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain 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) {
                        log.info(exchange.getRequest().getURI().getRawPath() + ": " + (System.currentTimeMillis() - startTime) + "ms");
                    }
                })
        );

    }

    @Override
    public int getOrder() {
        return 0;
    }
}

1.在启动类Application中注入路由拦截的规则。

代码语言:javascript
复制
    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
        // @formatter:off
        return builder.routes()
                .route(r -> r.path("/customer/**")
                        .filters(f -> f.filter(new RequestTimeFilter())
                                .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                        .uri("http://httpbin.org:80/get")
                        .order(0)
                        .id("customer_filter_router")
                )
                .build();
        // @formatter:on
    }

•3.同样是上面的需求,下面是通过写一个Filter工厂来实现。

代码语言:javascript
复制
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;
        }

    }
}

4.同样要注入到Spring容器中,交由Spring管理实例。

代码语言:javascript
复制
 @Bean
  public RequestTimeGatewayFilterFactory elapsedGatewayFilterFactory() {
      return new RequestTimeGatewayFilterFactory();
  }

5.最后一步,在application.yml中应用上面的工厂类进行配置

代码语言:javascript
复制
server:
  port: 8082
spring:
  profiles:
    active: elapse_route
---
spring:
  cloud:
    gateway:
      routes:
      - id: elapse_route
        uri: http://httpbin.org:80/get
        ## 自定义工厂类日志打印出访问时间
        filters:
        - RequestTime=false
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  profiles: elapse_route

自定义全局过滤器Filter

实例

下面例子主要是全局拦截进行token校验

代码语言:javascript
复制
public class TokenFilter implements GlobalFilter, Ordered {

    Logger logger= LoggerFactory.getLogger( TokenFilter.class );
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            logger.info( "token is empty..." );
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

限流

在高并发的系统中,往往需要在系统中做限流,一方面是为了防止大量的请求使服务器过载,导致服务不可用,另一方面是为了防止网络攻击。

更详细的教程参考【方志朋大神博客[2]】

实例

1.在上面的依赖基础上需要添加redis的相关依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifatId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2.application.yml 主要是配置redis和gateway整合。

代码语言:javascript
复制
server:
  port: 8084
spring:
  cloud:
    gateway:
      routes:
      - id: limit_route
        uri: http://httpbin.org:80/get
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: '#{@hostAddrKeyResolver}'
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 3
  application:
    name: gateway-limiter
  redis:
    host: localhost
    port: 6379
    database: 0

3.以上工作仅仅是完成配置工作。如果你需要对某个主机进行限流,或者对于某个接口进行限流,还是说对于某位用户进行限流都是可以的。

比如下面的代码

代码语言:javascript
复制
public class HostAddrKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
         //地址限流
          return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
//        return Mono.just(exchange.getRequest().getURI().getPath());//uri拦截限流
//        return Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));//用户限流
    }
}

与Eureka整合

跟以往的文章一样,都是加入Eureka的客户端依赖,配置文件连接到Eureka服务端即可。

application.yml配置如下:

代码语言:javascript
复制
server:
  port: 8081

spring:
  application:
    name: sc-gateway-server
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false
          lowerCaseServiceId: true
      routes:
      - id: cloud-eureka-client
        uri: lb://CLOUD-EUREKA-CLIENT
        predicates:
          - Path=/demo/**
        filters:
          - StripPrefix=1


eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

与hystrix整合

与服务调用一样的操作,添加hystrix的依赖,配置错误处理的方法。

样例如下:

代码语言:javascript
复制
 @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        String httpUri = "http://httpbin.org:80";
        return builder.routes()
                .route(p -> p
                        .path("/get")
                        .filters(f -> f.addRequestHeader("Hello", "World"))
                        .uri(httpUri))
                .route(p -> p
                        .host("*.hystrix.com")
                        .filters(f -> f
                                .hystrix(config -> config
                                        .setName("mycmd")
                                        .setFallbackUri("forward:/fallback")))
                        .uri(httpUri))
                .build();
    }

    @RequestMapping("/fallback")
    public Mono<String> fallback() {
        return Mono.just("fallback");
    }

总结

Gateway完全可以替代zuul,并且有很多的功能zuul都没有的。本文过长下次再比较一下两者的区别。

References

[1] 官网: http://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.0.0.RELEASE/single/spring-cloud-gateway.html#_gatewayfilter_factories [2] 方志朋大神博客: https://blog.csdn.net/forezp/article/details/85081162

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 特点
  • 相关术语
  • 工作原理
  • Predicate
  • 实例
  • Filter
  • 实例
  • 自定义过滤器Filter
  • 自定义全局过滤器Filter
  • 实例
  • 限流
  • 实例
  • 与Eureka整合
  • 与hystrix整合
  • 总结
  • References
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档