Springcloud 框架spring cloud

Spring Cloud——微服务网关Spring Cloud

2021-08-21  本文已影响0人  小波同学

Spring Cloud GateWay

Spring 自己开发的新一代API网关产品,基于NIO异步处理,摒弃了Zuul基于Servlet同步通信的设计。

Spring Cloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

关键特征:

在性能方面,根据官方提供的基准测试, Spring Cloud Gateway 的 RPS(每秒请求数)是Zuul 的 1.6 倍。

Spring Cloud Gateway十分优秀,Spring Cloud Alibaba也默认选用该组件作为网关产品。

相关概念:

工作流程:

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

Spring Cloud Gateway 的特征:

Spring Cloud GateWay 快速上手

Spring Cloud Gateway 网关路由有两种配置方式:

这两种方式是等价的,建议使用 yml 方式进配置。

引入依赖:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies> 

<dependencyManagement>
    <dependencies>
        <!--整合Spring Cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR12</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合Spring Cloud Alibaba-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement> 

Spring Cloud Gateway 是使用 netty+webflux 实现因此不需要再引入 web 模块。

gateWay的主要功能之一是转发请求,转发规则的定义主要包含三个部分:

注意:其中Route和Predicate必须同时申明

路由配置方式

基础URI路由配置方式

//通过配置文件配置
spring:
  cloud:
    gateway:
      routes:
        - id: gate_route
          uri: http://localhost:9023
          predicates:
          ## 当请求的路径为gate、rule开头的时,转发到http://localhost:9023服务器上
            - Path=/gate/**,/rule/**
        ### 请求路径前加上/app
          filters:
          - PrefixPath=/app     

基于代码的路由配置方式

转发功能同样可以通过代码来实现,我们可以在启动类 GateWayApplication 中添加方法 customRouteLocator() 来定制转发规则。

@SpringBootApplication
public class GatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
 
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/csdn")
                    .uri("https://blog.csdn.net"))
                .build();
    }
}

上面配置了一个 id 为 path_route 的路由,当访问地址http://localhost:8080/about时会自动转发到地址:https://blog.csdn.net/csdn和上面的转发效果一样,只是这里转发的是以项目地址/csdn格式的请求地址。

和注册中心相结合的路由配置方式

在uri的schema协议部分为自定义的lb:类型,表示从微服务注册中心(如Eureka)订阅服务,并且进行服务的路由。

server:
  port: 8084
spring:
  cloud:
    gateway:
      routes:
      -id: seckill-provider-route
        uri: lb://seckill-provider
        predicates:
        - Path=/seckill-provider/**

      -id: message-provider-route
        uri: lb://message-provider
        predicates:
        -Path=/message-provider/**

application:
  name: cloud-gateway

eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/

注册中心相结合的路由配置方式,与单个URI的路由配置,区别其实很小,仅仅在于URI的schema协议不同。单个URI的地址的schema协议,一般为http或者https协议。

路由匹配规则

Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。

Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。

Predicate 断言条件(转发规则)介绍

Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。

说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。

规则 实例 说明
Path - Path=/gate/,/rule/ 当请求的路径为gate、rule开头的时,转发到http://localhost:9023服务器上
Before - Before=2017-01-20T17:42:47.789-07:00[America/Denver] 在某个时间之前的请求才会被转发到 http://localhost:9023服务器上
After - After=2017-01-20T17:42:47.789-07:00[America/Denver] 在某个时间之后的请求才会被转发
Between - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] 在某个时间段之间的才会被转发
Cookie - Cookie=chocolate, ch.p 名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发
Header - Header=X-Request-Id, \d+ 携带参数X-Request-Id或者满足\d+的请求头才会匹配
Host - Host=www.hd123.com 当主机名为www.hd123.com的时候直接转发到http://localhost:9023服务器上
Method - Method=GET 只有GET方法才会匹配转发请求,还可以限定POST、PUT等请求方式

通过请求参数匹配

server:
  port: 8080
spring:
  application:
     name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Query=smile

只要请求中包含 smile 属性的参数即可匹配路由,不带 smile 参数则不会匹配。

curl localhost:8080?smile=x&id=2
server:
  port: 8080
spring:
  application:
     name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Query=keep, pu.

这样只要当请求中包含 keep 属性并且参数值是以 pu 开头的长度为三位的字符串才会进行匹配和路由。

curl localhost:8080?keep=pub

通过 Header 属性匹配

Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Header=X-Request-Id, \d+

使用 curl 测试,命令行输入:

curl http://localhost:8080 -H "X-Request-Id:88"

则返回页面代码证明匹配成功。将参数-H "X-Request-Id:88"改为-H "X-Request-Id:spring"再次执行时返回404证明没有匹配。

通过 Cookie 匹配

Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Cookie=sessionId, test

使用 curl 测试,命令行输入:

curl http://localhost:8080 --cookie "sessionId=test"

则会返回页面代码,如果去掉--cookie "sessionId=test",后台汇报 404 错误。

通过 Host 匹配

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Host=**.baidu.com

使用 curl 测试,命令行输入:

curl http://localhost:8080 -H "Host: www.baidu.com"

curl http://localhost:8080 -H "Host: md.baidu.com"

经测试以上两种 host 均可匹配到 host_route 路由,去掉 host 参数则会报 404 错误。

通过请求方式匹配

可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Method=GET

使用 curl 测试,命令行输入:

# curl 默认是以 GET 的方式去请求

curl http://localhost:8080

测试返回页面代码,证明匹配到路由,我们再以 POST 的方式请求测试。

# curl 默认是以 GET 的方式去请求

curl -X POST http://localhost:8080

返回 404 没有找到,证明没有匹配上路由。

通过请求路径匹配

Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: http://ityouknow.com
          order: 0
          predicates:
            - Path=/foo/{segment}

如果请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar。

使用 curl 测试,命令行输入:

curl http://localhost:8080/foo/1

curl http://localhost:8080/foo/xx

curl http://localhost:8080/boo/xx

经过测试第一和第二条命令可以正常获取到页面返回值,最后一个命令报404,证明路由是通过指定路由来匹配。

通过请求 ip 地址进行匹配

Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - RemoteAddr=192.168.1.1/24

可以将此地址设置为本机的 ip 地址进行测试。

curl localhost:8080

如果请求的远程地址是 192.168.1.10,则此路由将匹配。

组合使用

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Host=**.foo.org
            - Path=/headers
            - Method=GET
            - Header=X-Request-Id, \d+
            - Query=foo, ba.
            - Query=baz
            - Cookie=chocolate, ch.p

各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。

一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发。

过滤器规则(Filter)

过滤规则 实例 说明
PrefixPath - PrefixPath=/app 在请求路径前加上app
RewritePath - RewritePath=/test, /app/test 访问localhost:9022/test,请求会转发到localhost:8001/app/test
SetPath SetPath=/app/{path} 通过模板设置路径,转发的规则时会在路径前增加app,{path}表示原请求路径
RedirectTo 重定向
RemoveRequestHeader 去掉某个请求头信息

注意:当配置多个filter时,优先定义的会被调用,剩余的filter将不会生效

PrefixPath

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - PrefixPath=/mypath

访问/hello的请求被发送到https://example.org/mypath/hello

RedirectTo

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - RedirectTo=302, https://acme.org

RemoveRequestHeader

spring:
  cloud:
    gateway:
      routes:
        - id: removerequestheader_route
          uri: https://example.org
          filters:
            - RemoveRequestHeader=X-Request-Foo

去掉请求头信息 X-Request-Foo

RemoveResponseHeader

spring:
  cloud:
    gateway:
      routes:
        - id: removerequestheader_route
          uri: https://example.org
          filters:
            - RemoveResponseHeader=X-Request-Foo

RemoveRequestParameter

spring:
  cloud:
    gateway:
      routes:
        - id: removerequestparameter_route
          uri: https://example.org
          filters:
            - RemoveRequestParameter=red

RewritePath

spring:
  cloud:
    gateway:
      routes:
        - id: rewrite_filter
          uri: http://localhost:8081
          predicates:
            - Path=/test/**
          filters:
            - RewritePath=/where(?<segment>/?.*), /test(?<segment>/?.*)

/where/... 改成 test/...

使用代码改下路径

RouteLocatorBuilder.Builder builder = routeLocatorBuilder.routes();
builder.route("path_rote_at_guigu", r -> r.path("/guonei")
                .uri("http://news.baidu.com/guonei"))
        .route("csdn_route", r -> r.path("/csdn")
                .uri("https://blog.csdn.net"))
        .route("blog3_rewrite_filter", r -> r.path("/blog3/**")
                .filters(f -> f.rewritePath("/blog3/(?<segment>.*)", "/$\\{segment}"))
                .uri("https://blog.csdn.net"))
        .route("rewritepath_route", r -> r.path("/baidu/**")
                .filters(f -> f.rewritePath("/baidu/(?<segment>.*)", "/$\\{segment}"))
                .uri("http://www.baidu.com"))
        .build();

SetPath

spring:
  cloud:
    gateway:
      routes:
        - id: setpath_route
          uri: https://example.org
          predicates:
            - Path=/red/{segment}
          filters:
            - SetPath=/{segment}

如/red/blue的请求被转发到/blue。

SetRequestHeader

spring:
  cloud:
    gateway:
      routes:
        - id: setrequestheader_route
          uri: https://example.org
          filters:
            - SetRequestHeader=X-Request-Red, Blue

SetStatus

spring:
  cloud:
    gateway:
      routes:
        - id: setstatusint_route
          uri: https://example.org
          filters:
            - SetStatus=401

StripPrefix

spring:
  cloud:
    gateway:
      routes:
        - id: nameRoot
          uri: https://nameservice
          predicates:
            - Path=/name/**
          filters:
            - StripPrefix=2

请求/name/blue/red会转发到/red。

RequestSize

spring:
  cloud:
    gateway:
      routes:
        - id: request_size_route
          uri: http://localhost:8080/upload
          predicates:
            - Path=/upload
          filters:
            - name: RequestSize
              args:
                maxSize: 5000000

超过5M的请求会返回413错误。

Default-filters

spring:
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=X-Response-Default-Red, Default-Blue
        - PrefixPath=/httpbin

通过代码进行配置

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("path_route", r -> r.path("/get")
            .uri("http://httpbin.org"))
        .route("host_route", r -> r.host("*.myhost.org")
            .uri("http://httpbin.org"))
        .route("rewrite_route", r -> r.host("*.rewrite.org")
            .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
            .uri("http://httpbin.org"))
        .route("hystrix_route", r -> r.host("*.hystrix.org")
            .filters(f -> f.hystrix(c -> c.setName("slowcmd")))
            .uri("http://httpbin.org"))
        .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
            .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
            .uri("http://httpbin.org"))
        .route("limit_route", r -> r
            .host("*.limited.org").and().path("/anything/**")
            .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
            .uri("http://httpbin.org"))
        .build();
}

统一配置跨域请求:

现在的请求通过经过gateWay网关时,需要在网关统一配置跨域请求,需求所有请求通过

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-headers: "*"
            allow-credentials: true
            allowed-methods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

Gatway 网关的过滤器开发

过滤器的执行次序

Spring-Cloud-Gateway 基于过滤器实现,同 zuul 类似,有pre和post两种方式的 filter,分别处理前置逻辑和后置逻辑。客户端的请求先经过pre类型的 filter,然后将请求转发到具体的业务服务,收到业务服务的响应之后,再经过post类型的 filter 处理,最后返回响应到客户端。

过滤器执行流程如下,order 越大,优先级越低


Spring Cloud Gateway根据作用范围划分为GatewayFilter和GlobalFilter,二者区别如下:

Spring Cloud Gateway框架内置的GlobalFilter如下:


定义全局过滤器

实现 GlobalFilter 和 Ordered,重写相关方法,加入到spring容器管理即可,无需配置,全局过滤器对所有的路由都有效。

全局过滤器举例:代码如下:

@Configuration
public class FilterConfig {

    @Bean
    @Order(-1)
    public GlobalFilter a() {
        return new AFilter();
    }

    @Bean
    @Order(0)
    public GlobalFilter b() {
        return new BFilter();
    }

    @Bean
    @Order(1)
    public GlobalFilter c() {
        return new CFilter();
    }


    @Slf4j
    public class AFilter implements GlobalFilter, Ordered {

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("AFilter前置逻辑");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("AFilter后置逻辑");
            }));
        }

        //值越小,优先级越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 100;
        }
    }

    @Slf4j
    public class BFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("BFilter前置逻辑");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("BFilter后置逻辑");
            }));
        }

        //值越小,优先级越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 200;
        }
    }

    @Slf4j
    public class CFilter implements GlobalFilter, Ordered {

        @Override 
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("CFilter前置逻辑");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("CFilter后置逻辑");
            }));
        }

        //值越小,优先级越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 300;
        }
    }
}

定义局部过滤器

步骤:

局部过滤器举例, 对请求头部的 user-id 进行校验,代码如下:

@Slf4j
public class UserIdCheckGateWayFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getPath().pathWithinApplication().value();
        log.info("请求URL:" + url);
        log.info("method:" + exchange.getRequest().getMethod());
        //获取param 请求参数
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        //获取header
        String userId = exchange.getRequest().getHeaders().getFirst("user-id");
        log.info("userId:" + userId);

        if (StringUtils.isEmpty(userId)) {
            log.info("*****头部验证不通过,请在头部输入  user-id");
            //终止请求,直接回应
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    // 值越小,优先级越高
    //int HIGHEST_PRECEDENCE = -2147483648;
    //int LOWEST_PRECEDENCE = 2147483647;
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}
@Component
public class UserIdCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    
    @Override
    public GatewayFilter apply(Object config) {
        return new UserIdCheckGateWayFilter();
    }
}
- id: service_provider_demo_route_filter
  uri: lb://service-provider-demo
  predicates:
    - Path=/filter/**
  filters:
    - RewritePath=/filter/(?<segment>.*), /provider/$\{segment}
    - UserIdCheck

参考:
http://www.ityouknow.com/springcloud/2018/12/12/spring-cloud-gateway-start.html

http://www.likecs.com/show-50293.html

https://zhuanlan.zhihu.com/p/299608850?utm_source=wechat_session

https://juejin.cn/post/6844903965352525838

https://blog.csdn.net/weixin_38361347/article/details/114108368

http://www.zyiz.net/tech/detail-98256.html

https://www.cnblogs.com/crazymakercircle/p/11704077.html

https://blog.csdn.net/forezp/article/details/85057268

上一篇下一篇

猜你喜欢

热点阅读