主人稍等一会喵`(*>﹏<*)

Spring Cloud Gateway网关学习笔记


Gateway核心概念

  1. 路由(Route)

    是gateway中最基本的组件之一,表示一个具体的路由信息载体,由ID、目标URI、Predicate集合、Filter集合组成。主要定义了下面的几个信息:

    • id,路由标识符,区别于其他 Route。
    • uri,路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
    • order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
    • predicate,断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
    • fifilter,过滤器用于修改请求和响应信息。
  2. 断言(Predicate)

    是Java8中引入的函数式接口,提供了断言的功能,它可以匹配Http请求中的任何内容。只有断言都返回真,才会真正的执行路由。

  3. 过滤器(Filter)

    过滤器就是在请求的传递过程中,为请求提供前置和后置的过滤

执行流程大体如下:

  1. Gateway Client向Gateway Server发送请求

  2. 请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文

  3. 然后网关的上下文会传递到DispatcherHandler,它负责将请求分发给RoutePredicateHandlerMapping

  4. RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用

  5. 如果过断言成功,由FilteringWebHandler创建过滤器链并调用

  6. 请求会一次经过PreFilter–微服务-PostFilter的方法,最终返回响应

测试项目

一、 自定义一个断言工厂,匹配规则为请求参数中count和money字段都存在,且两者值的乘积小于1000大于0。
创建网关项目访问微服务
  1. 创建一个父项目,pom文件中添加常见的依赖版本。

    <!-- 统一管理jar包版本 -->
       <properties>
           <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
           <maven.compiler.source>1.8</maven.compiler.source>
           <maven.compiler.target>1.8</maven.compiler.target>
           <junit.version>4.12</junit.version>
           <log4j.version>1.2.17</log4j.version>
           <lombok.version>1.16.18</lombok.version>
           <mysql.version>8.0.15</mysql.version>
           <druid.version>1.1.16</druid.version>
           <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
       </properties>
       
       <dependencyManagement>
           <dependencies>
               <!--spring boot 2.2.2-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-dependencies</artifactId>
                   <version>2.2.2.RELEASE</version>
                   <type>pom</type>
                   <scope>import</scope>
               </dependency>
               <!--spring cloud Hoxton.SR1-->
               <dependency>
                   <groupId>org.springframework.cloud</groupId>
                   <artifactId>spring-cloud-dependencies</artifactId>
                   <version>Hoxton.SR1</version>
                   <type>pom</type>
                   <scope>import</scope>
               </dependency>
               <!--spring cloud alibaba 2.1.1.RELEASE-->
               <dependency>
                   <groupId>com.alibaba.cloud</groupId>
                   <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                   <version>2.1.1.RELEASE</version>
                   <type>pom</type>
                   <scope>import</scope>
               </dependency>
               <dependency>
                   <groupId>mysql</groupId>
                   <artifactId>mysql-connector-java</artifactId>
                   <version>${mysql.version}</version>
               </dependency>
               <dependency>
                   <groupId>com.alibaba</groupId>
                   <artifactId>druid</artifactId>
                   <version>${druid.version}</version>
               </dependency>
               <dependency>
                   <groupId>com.alibaba</groupId>
                   <artifactId>fastjson</artifactId>
                   <version>1.2.70</version>
               </dependency>
               <dependency>
                   <groupId>org.mybatis.spring.boot</groupId>
                   <artifactId>mybatis-spring-boot-starter</artifactId>
                   <version>${mybatis.spring.boot.version}</version>
               </dependency>
               <dependency>
                   <groupId>junit</groupId>
                   <artifactId>junit</artifactId>
                   <version>${junit.version}</version>
               </dependency>
               <dependency>
                   <groupId>log4j</groupId>
                   <artifactId>log4j</artifactId>
                   <version>${log4j.version}</version>
               </dependency>
               <dependency>
                   <groupId>org.projectlombok</groupId>
                   <artifactId>lombok</artifactId>
                   <version>${lombok.version}</version>
                   <optional>true</optional>
               </dependency>
           </dependencies>
       </dependencyManagement>
  2. 创建Gateway5001模块,pom文件中引入gateway依赖

    注意:gateway使用的netty+webflux实现,不要加入web依赖(不要引用webmvc),否则初始化会报错 ,需要加入webflux依赖。

    <dependencies>
            <!--gateway-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
  3. 创建主类

    @SpringBootApplication
    public class GatewayMain5001 {
        public static void main(String[] args) {
            SpringApplication.run(GatewayMain5001.class,args);
        }
    }
  4. 添加配置文件

    server:
      port: 5001
    
    spring:
      application:
        name: springcloud-gateway
      cloud:
        gateway:
          routes:
            - id: order_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001 #匹配后提供服务的路由地址
          predicates: #匹配后提供服务的路由地址
            - Path=/gateway/order  # 断言,路径相匹配的进行路由
          filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 # 转发之前去掉1层路径
  5. 新建一个Order8001模块,引入springbootweb起步依赖,新建controller类用于测试

    @RestController
    public class Mycontroller {
    
        @RequestMapping("/order")
        public String order(){
            return "访问到了/order资源";
        }
    }

6.启动两个项目进行测试,浏览器访问localhost:5001/gateway/order地址,成功访问到了Order8001服务

![](Spring Cloud Gateway网关学习笔记/1.png)

自定义断言工厂
  1. 在Gateway5001网关配置中的断言添加- Count=0,1000

    spring:
      application:
        name: springcloud-gateway
      cloud:
        gateway:
          routes:
            - id: order_route #路由的ID,没有固定规则但要求唯一,建议配合服务名
              uri: http://localhost:8001  #匹配后提供服务的路由地址
              predicates: #匹配后提供服务的路由地址
                - Path=/gateway/order  # 断言,路径相匹配的进行路由
                - Count=0,1000 #自定义断言工厂
              filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
                - StripPrefix=1 # 转发之前去掉1层路径
  2. 自定义一个断言工厂类,继承AbstracRoutePredicateFactory类,实现断言方法

    注意断言工厂类的名称要在路由配置中的自定义断言名称+RoutePredicateFactory。如在这里就应该为CountRoutePredicateFactory

@Component
public class CountRoutePredicateFactory extends AbstractRoutePredicateFactory<CountRoutePredicateFactory.Config> {
    //构造函数
    public CountRoutePredicateFactory() {
        super(CountRoutePredicateFactory.Config.class);
    }

    //读取配置文件中的参数值,并赋值到配置类的属性上
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("min", "max");
    }

    //断言逻辑
    @Override
    public Predicate<ServerWebExchange> apply(CountRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange ex) {
                String money = ex.getRequest().getQueryParams().getFirst("money");
                String count = ex.getRequest().getQueryParams().getFirst("count");
                if (!StringUtils.isEmpty(count) && !StringUtils.isEmpty(money)) {
                    int res = Integer.parseInt(money) * Integer.parseInt(count);
                    return res > 0 && res < 1000;
                }
                return false;
            }
        };
    }
    
    //配置类,接受配置文件中的对应参数
    @Data
    @NoArgsConstructor
    public static class Config {
        private int min;
        private int max;
    }
}
  1. 在网页上输入localhost:5001/gateway/order?money=1&count=1000 因为money*count=1000,不符合自定义断言规则,故无法访问

    ![](Spring Cloud Gateway网关学习笔记/2.png)

    输入localhost:5001/gateway/order?money=1&count=999,成功访问,测试自定义断言工厂成功

二、自定义全局过滤器解决跨域问题
什么是跨域

出于浏览器的同源策略限制。浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。此时浏览器会阻止读取后端传来的数据。

在前后端分离的模式下,前后端的域名是不一致的,此时就会发生跨域访问问题。下面列出在gateway中解决跨域访问问题两种方法。

在gateway端增加CorsFilter过滤器
@Component
public class CorsWebFilter implements WebFilter {
    private static final String ALL = "*";
    private static final String MAX_AGE = "18000L";

    @Override
    public Mono<Void> filter(ServerWebExchange ctx, WebFilterChain chain) {
        ServerHttpRequest request = ctx.getRequest();
        String path = request.getPath().value();
        ServerHttpResponse response = ctx.getResponse();
        if ("/favicon.ico".equals(path)) {
            response.setStatusCode(HttpStatus.OK);
            return Mono.empty();
        }
        if (!CorsUtils.isCorsRequest(request)) {
            return chain.filter(ctx);
        }
        HttpHeaders requestHeaders = request.getHeaders();
        HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod();
        HttpHeaders headers = response.getHeaders();

        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, requestHeaders.getOrigin());
        headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, requestHeaders.getAccessControlRequestHeaders());
        if (requestMethod != null) {
            headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, requestMethod.name());
        }

        headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
        headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, ALL);
        headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
        if (request.getMethod() == HttpMethod.OPTIONS) {
            response.setStatusCode(HttpStatus.OK);
            return Mono.empty();
        }
        return chain.filter(ctx);
    }
}
修改Gataway配置文件

CorsFilter已经在Gateway里了,不需要写代码实现,直接修改配置文件即可

spring:
  application:
    name: springcloud-gateway
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"

文章作者: Razuberi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Razuberi !
评论
  目录