一、Gateway介绍

  Spring Cloud Gateway是Spring Cloud的一个全新项目,基于Spring 5,Spring Boot 2和 Project Reactor等技术开发的网关,它旨在为微服务框架提供一种简单有效的统一的API路由管理方式,以及基于Filter方式提供一些强大的过滤功能,例如:熔断、限流、重试等

  Spring Cloud Gateway作为spring Cloud 生态系统中的网关,目标是替代Zuul,在Spring Cloud 2.0以上版本中,没有对新版本Zuul 2.0以上最新高性能版本进行集成,仍然还是Zuul 1.x非Reactor模式的老版本,而为了提升网关的性能,Spring Cloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

  Spring Cloud Gateway功能:

  1、基于Spring Framework 5,Project Reactor和Spring Boot 2.0构建

  2、能够匹配任何请求属性上的路由。

  3、断言和过滤器特定于路由。

  4、Hystrix断路器集成。

  5、Spring Cloud DiscoveryClient集成

  6、易于编写的断言和过滤器

  7、请求速率限制

  8、路径改写

  Spring Cloud 架构图

  

  由此可以知道网关在整个SpringCloud 中的位置

二、Gateway工作流程

  工作流程图

  

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

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

三、搭建项目

  • pom.xml

<dependencies>

        <!--gateway 该依赖与spring-boot-starter-web冲突-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--进行排除-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>test</scope><!--定义scope为test,打包则不会出现-->
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

    </dependencies>
  • application.yml

server:
  port: 9009 #端口号
spring:
  application:
    name: microServiceCloudGateway  #服务注册中心注册的服务名
  security:
    user:
      name: root
      password: root
  cloud:
    gateway: #网关路由配置
      discovery:
        locator:
          enabled: true #默认值为 true,即默认开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
        #将 micro-service-cloud-provider-dept-8001 提供的服务隐藏起来,不暴露给客户端,只给客户端暴露 API 网关的地址 9009
        - id: provider_dept_list_routh   #路由 id,没有固定规则,但唯一,建议与服务名对应
          uri: http://localhost:8001 #动态路由,使用服务名代替上面的具体带端口
          predicates:
            #以下是断言条件,必须全部符合条件(可以写多个predicate)
            - Path=/payment/**    #断言,路径匹配 注意:Path 中 P 为大写
            - Weight=group1, 8 #这条路线会将大约80%的流量转发到http://localhost:8001,将大约20%的流量转发到http://localhost:9001
            #- Method=GET #只能时 GET 请求时,才能访问
            #- After=2020-01-01T00:00:00.308+08:00[Asia/Shanghai] #符合2020年01月01日00:00时区时间(上海)之后的任何请求。
            #- Before=2030-01-01T23:59:59.308+08:00[Asia/Shanghai] #符合2030年01月01日23:59时区时间(上海)之后的任何请求。
            #- Between=2020-04-20T23:57:57.308+08:00[Asia/Shanghai], 2020-04-21T23:57:57.308+08:00[Asia/Shanghai] #……
            #- Cookie=username, wangshuo #此路由匹配具有名称为username与wangshuo正则表达式匹配的cookie的请求。
            #- Header=X-Request-Id, \d+ #请求具有名为X-Request-Id其值与\d+正则表达式匹配的标头(即,其值为一个或多个数字),则此路由匹配。#测试请求命令:curl http://localhost:9009/payment/get/1 -H "X-Request-Id:123"
            #- Host=**.x.com #请求具有这种路由匹配Host用的头值**.x.com
            #- Query=green #请求包含green查询参数,则前面的路由匹配。 #测试请求命令:curl http://localhost:9527/payment/get/1?green=1
        - id: weight_low
          uri: http://localhost:9001
          predicates:
            - Weight=group1, 2 #这条路线会将大约80%的流量转发到http://localhost:8001,将大约20%的流量转发到http://localhost:9001
eureka:
  instance:
    instance-id: micro-service-cloud-gateway-9527
    hostname: micro-service-cloud-gateway
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@eureka1.com:8887/eureka
  • App

 1 package com.sdkj;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
 6 
 7 /**
 8  * @Author wangshuo
 9  * @Date 2022/5/24, 22:03
10  * Please add a comment
11  */
12 @EnableEurekaClient
13 @SpringBootApplication
14 public class App {
15 
16     public static void main(String[] args) {
17         SpringApplication.run(App.class, args);
18     }
19 }

四、通过配置类来配置路由

  • MyGateWayFilter(指定访问参数)

 1 package com.sdkj.config;
 2 
 3 import org.springframework.cloud.gateway.filter.GatewayFilterChain;
 4 import org.springframework.cloud.gateway.filter.GlobalFilter;
 5 import org.springframework.core.Ordered;
 6 import org.springframework.stereotype.Component;
 7 import org.springframework.web.server.ServerWebExchange;
 8 import reactor.core.publisher.Mono;
 9 
10 /**
11  * @Author wangshuo
12  * @Date 2022/5/25, 10:10
13  * Please add a comment
14  */
15 @Component
16 public class MyGateWayFilter implements GlobalFilter, Ordered {
17     @Override
18     public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
19         String uname = exchange.getRequest().getQueryParams().getFirst("uname");
20         if (uname == null) {
21             System.out.println("非法访问");
22             throw new RuntimeException("非法访问,请输入用户名");
23         }
24         // 成功
25         return chain.filter(exchange);
26     }
27 
28     @Override
29     public int getOrder() {
30         return 0;
31     }
32 }
  • GatewayConfig(配置路由转发)

 1 package com.sdkj.config;
 2 
 3 import org.springframework.cloud.gateway.route.RouteLocator;
 4 import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
 5 import org.springframework.context.annotation.Bean;
 6 import org.springframework.context.annotation.Configuration;
 7 
 8 /**
 9  * @Author wangshuo
10  * @Date 2022/5/25, 8:29
11  * Please add a comment
12  */
13 @Configuration
14 public class GatewayConfig {
15 
16     @Bean
17     public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
18         // 路由构造器
19         RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
20         // 设置路径
21         routes.route("baidu_news_guonei_routh", r -> {
22             return r.path("/guonei").uri("http://news.baidu.com/guonei");
23         });
24         routes.route("baidu_news_guoji_routh", r -> {
25             return r.path("/guoji").uri("http://news.baidu.com/guoji");
26         });
27 
28         return routes.build();
29     }
30 }