SpringCloud 12:服务网关Gateway

Gateway 简介

  • 是一个构建在Spring生态系统之上的API网关,基于spring5、springboot2和projectreactor。springcloudgateway旨在提供一种简单而有效的方法来路由到api,并为api提供横切关注点,比如:安全性、监控/度量和弹性。

具有如下特性:

  • 基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 进行构建;
  • 能够在任何请求属性上匹配路由;
  • 可以对路由指定 Predicate(断言)和 Filter(过滤器);
  • 集成了断路器;
  • 集成了SpringCloud服务发现功能;
  • 易于自定义 Predicate(断言)和 Filter(过滤器);
  • 限流;
  • 路径重写。

以上来源于官方文档https://spring.io/projects/spring-cloud-gateway

术语

  • Route(路由):网关的基本模块。它由一个ID、一个目标URI、一组断言和一组过滤器定义。如果断言为true,则匹配该路由。
  • Predicate(断言):这是一个Java8的Function Predicate,输入类型是spring框架内的ServerWebExchange,这使得开发人员可以匹配HTTP请求中的任何内容,例如头或参数。
  • Filter(过滤器):这些是用特定工厂构建的spring框架内的 GatewayFilter 实例。可以在发送下游请求之前或之后修改请求和响应。

创建GateWay模块

1.pom.xml

  • 这里需要注意的是不能引入spring-boot-starter-web这个jar包,不能是一个springweb项目

在这里插入图片描述
在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <!--springcloud版本号-->
        <spring.cloud.version>Hoxton.SR1</spring.cloud.version>
        <!--springcloudalibaba版本号-->
        <spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring.cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring.cloud.alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <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-discovery</artifactId>
        </dependency>
        <!--配置中心-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2. bootstrap.yml

  • 这里之配了nacos的配置信息,端口和路由规则都后面会配置在nacos中,就不需要重启项目了

在这里插入图片描述

spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

3. 启动类

在这里插入图片描述

package com.example.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(GatewayApplication.class, args);
    }

}

4. 启动服务

在这里插入图片描述
在这里插入图片描述

  • nacos中gateway的配置文件

在这里插入图片描述

  • 配置了端口号,还有路由规则
    在这里插入图片描述

Route Predicate Factories

在这里插入图片描述

1. After Route

  • 在指定时间之后的请求会匹配该路由,+08:00表示东八区
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://www.baidu.com/
        predicates:
        - After=2017-01-20T17:42:47+08:00[Asia/Shanghai]
  • http://localhost:9090/

在这里插入图片描述

2. Before Route

  • 在指定时间之前的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://www.taobao.com/
        predicates:
        - Before=2022-12-29T18:30:00+08:00[Asia/Shanghai]
  • http://localhost:9090/

在这里插入图片描述

3. Between Route

  • 在指定时间区间内的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://www.iqiyi.com/
        predicates:
        - Between=2017-01-20T17:42:47+08:00[Asia/Shanghai],2022-12-29T18:30:00+08:00[Asia/Shanghai]

4. Cookie Route

  • 带有指定Cookie的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://www.qq.com/
        predicates:
        - Cookie=name,fisher
  • 使用curl发送带有cookie为name=fisher的请求
curl http://localhost:9090/ --cookie "name=fisher"

在这里插入图片描述

5. Header Route

  • 带有指定请求头的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://www.jd.com/
        predicates:
        - Header=X-Request-Id, \d+
  • 发送请求头为X-Request-Id:123的请求
curl http://localhost:9090/ -H "X-Request-Id:123"

在这里插入图片描述

6. Host Route

  • 带有指定Host的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://www.baidu.com/
        predicates:
        - Host=activate.navicat.com:9090,**.fisher.com
  • host文件配置

在这里插入图片描述

  • http://activate.navicat.com:9090/

在这里插入图片描述

  • 使用curl发送请求头带有Host的请求
 curl http://localhost:9090/ -H "Host:www.fisher.com"

在这里插入图片描述

7. Method Route

  • 发送指定方法的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://www.baidu.com/
        predicates:
        - Method=GET
  • get请求http://localhost:9090/

在这里插入图片描述

  • 使用post方式请求
curl -X POST http://localhost:9090/

在这里插入图片描述

8. Path Route

  • 发送指定路径的请求会匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8081
        predicates:
        - Path=/order/{
    
    segment}
      - id: host_route2
        uri: http://localhost:8091
        predicates:
        - Path=/web/{
    
    segment}
      - id: host_route3
        uri: https://www.baidu.com
        predicates:
        - Path=/s
  • http://localhost:8081/order/index

在这里插入图片描述
在这里插入图片描述

  • http://localhost:8091/web/index

在这里插入图片描述
在这里插入图片描述

  • 请求http://localhost:9090/order/index,路由到order工程

在这里插入图片描述

  • 请求http://localhost:9090/web/index,路由到web工程

在这里插入图片描述

  • 请求http://localhost:9090/s,路由到百度

在这里插入图片描述

  • 请求http://localhost:9090/,会404

在这里插入图片描述

9. Query Route

  • 带指定查询参数的请求可以匹配该路由
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://www.baidu.com/
        predicates:
        - Query=green
  • http://localhost:9090/?green
    在这里插入图片描述

10. RemoteAddr Route

  • 从指定远程地址发起的请求可以匹配该路由
  • 127.0.0.1是客户端的ip地址,24是子网掩码
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://www.baidu.com/
        predicates:
        - RemoteAddr=127.0.0.1/24
  • 请求http://127.0.0.1:9090/

在这里插入图片描述

11. Weight Route

  • 使用权重来路由相应请求,80%的请求会到百度,20%的请求会到腾讯
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://www.baidu.com/
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://www.qq.com/
        predicates:
        - Weight=group1, 2

在这里插入图片描述
在这里插入图片描述

Custom Global Filters

  • 自定义全局filter,可以用@Bean的方式,也可以实现GlobalFilter接口

在这里插入图片描述
在这里插入图片描述

  • 检验cookie
package com.example.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

@Component
public class CustomGlobalFilter implements GlobalFilter {
    
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    
    
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, HttpCookie> cookies = request.getCookies();

        //验证是否存在key=fisher的cookie
        for (String s : cookies.keySet()) {
    
    
            if (Objects.equals(s, "fisher")) {
    
    
                System.out.println("cookies验证成功");
                return chain.filter(exchange);
            } else {
    
    
                System.out.println("cookies不存在");
            }
        }
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }
}

  • 请求curl http://localhost:9090/ --cookie “fisher=aaa”

在这里插入图片描述
在这里插入图片描述

  • 直接请求http://localhost:9090/,返回错误码401

在这里插入图片描述
在这里插入图片描述

Custom GatewayFilter Factories

  • 自定义局部filter
  • 配置路由规则,路由带有green参数的请求,lb://micro-order表示负载均衡到micro-order服务上,Custom表示自定义路由的名称(过滤器的名称可以随便取,但必须是这个名称后面+GatewayFilterFactory,不然gateway识别不了Custom这个名字),true,2是要传到过滤器中的参数(如果过滤器不需要也可以不传)
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: lb://micro-order
        predicates:
        - Query=green
        filters:
          - Custom=true,2
  • 先停用全局filter,只使用这个局部filter,检验cookie

在这里插入图片描述

package com.example.gateway;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;

import java.util.Arrays;
import java.util.List;

@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
    
    

    public CustomGatewayFilterFactory() {
    
    
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
    
    
        return (exchange, chain) -> {
    
    
            if (!config.enable) {
    
    
                return chain.filter(exchange);
            }
            ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, HttpCookie> cookies = request.getCookies();

            //验证是否存在key=fisher的cookie
            for (String s : cookies.keySet()) {
    
    
                if (s.equals("fisher")) {
    
    
                    System.out.println("--------CustomGatewayFilterFactory--------------");
                    return chain.filter(exchange);
                }
            }
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return response.setComplete();
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
    
    
        //表示从路由规则中获取的参数对应哪些key,通过Config中set方法赋值
        return Arrays.asList("enable", "count");
    }

    public static class Config {
    
    
        private boolean enable;
        private int count;

        public int getCount() {
    
    
            return count;
        }

        public void setCount(int count) {
    
    
            System.out.println("count=" + count);
            this.count = count;
        }

        public boolean isEnable() {
    
    
            return enable;
        }

        public void setEnable(boolean enable) {
    
    
            System.out.println("enable=" + enable);
            this.enable = enable;
        }
    }
}

  • 启动项目,成功获取路由中配置的参数

在这里插入图片描述

  • 不带cookie请求http://localhost:9090/order/index?green,返回401

在这里插入图片描述

  • 带cookie请求
curl http://localhost:9090/order/index?green --cookie "fisher=xxx"

在这里插入图片描述

Custom Route Predicate Factories

  • 自定义断言规则,需要继承AbstractRoutePredicateFactory,命名规则要求类的名字为断言名+RoutePredicateFactory ,这里使用Custom
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: lb://micro-order
        predicates:
        - Custom=green
  • 这里就判断一下查询参数key是否等于green
package com.example.gateway;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;

@Component
public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
    
    

    public CustomRoutePredicateFactory() {
    
    
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
    
    
        return exchange -> {
    
    
            ServerHttpRequest request = exchange.getRequest();
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            for (String s : queryParams.keySet()) {
    
    
                if (Objects.equals(config.getName(), s)) {
    
    
                    System.out.println("Green is a good color!");
                    return true;
                }
            }
            System.out.println("other color is bad!");
            return false;
        };

    }

    @Override
    public List<String> shortcutFieldOrder() {
    
    
        return Arrays.asList("name");
    }

    public static class Config {
    
    
        private String name;

        public String getName() {
    
    
            return name;
        }

        public void setName(String name) {
    
    
            this.name = name;
        }
    }
}

  • http://localhost:9090/order/index?green

在这里插入图片描述
在这里插入图片描述

  • http://localhost:9090/order/index?red

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_40977118/article/details/109673980