Spring Cloud Gateway 网关

Spring Cloud Gateway 网关

1、简介

1.1什么是SpringCloudGateway

1.2Gateway加入后的架构

1.3核心概念

2、入门案例

2.1创建工程,引入依赖

2.2编写启动类

2.3编写application配置文件

3、面向服务的路由

3.1修改映射配置,通过服务名称获取

4、路由前缀

4.1添加前缀

4.2去除前缀

5、过滤器

5.1简介

5.2配置全局默认过滤器

5.3执行生命周期

5.4使用场景

6、自定义过滤器

6.1自定义局部过滤器

6.2自定义全局过滤器

7、Gateway跨域配置

1、简介

1.1什么是SpringCloudGateway

Spring Cloud Gateway是Spring官网基于Spring5.0、Spring Boot2.0、Project Reactor等技术开发的网关服务。
Spring Cloud Gateway基于Filter链提提供网关基本功能:安全、监控/埋点、限流等功能。
Spring Cloud Gateway为微服务架构提供简单、有效且统一的API路由管理方式。
Spring Cloud Gateway是替代Netflix Zuul的一套解决方案。

Spring Cloud Gateway组件的核心是一系列的过滤器,通过这些过滤器可以将客户端发送的请求转发(路由)到对应的微服务。Spring Cloud Gateway是加在整个微服务最前沿的防火墙和代理器,隐藏微服务节点IP端口信息,从而加强安全保护。Spring Cloud Gateway 本身也是一个微服务,需要注册到Eureka服务注册中心。
网关的核心功能是: 过滤和路由

1.2Gateway加入后的架构


不管是来自于客户端(PC或移动端)的请求,还是服务内部调用。一切对服务的请求都可经过网关,然后再由网关来实现 鉴权、动态路由等等操作。Gateway就是我们服务的统一入口。

1.3核心概念

路由(route)路由信息的组成:由一个ID、一个目的URL、一组断言工厂、一组Filter组成。如果路由断言为真,说明请求URL和配置路由匹配。
断言(predicate)Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway的断言函数允许开发者去定义匹配来自于HTTP Request中的任何信息。比如请求头和参数。
过滤器(Filter)一个标准的Spring WebFilter。Spring Cloud Gateway中的Filter分为两种类型的Filter,分别是Gateway Filter和Global Filter。过滤器Filter将会对请求和响应进行修改和处理。

2、入门案例

2.1创建工程,引入依赖

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

2.2编写启动类

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

编写application配置文件

server:
  port: 10010
spring:
  application:
    name: api-gateway
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

需要用网关来代理user-service服务,先看一下控制面板中的服务状态

ip为:127.0.0.1
端口为:9091
修改application.yml文件为:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
      #路由id,可以随便写
        - id: user-service-route
        #代理服务的地址
          uri: HTTP://127.0.0.1:9091
          predicates:
            - Path=/user/**
# ************/
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

将符合 Path 规则的一切请求,都代理到 uri 参数指定的地址 本例中,我们将路径中包含有 /user/** 开头的请求,
代理到http://127.0.0.1:9091

访问的路径中需要加上配置规则的映射路径,我们访问:http://localhost:10010/user/7(截图省略)
即:http://localhost:10010/user/7 -> http://localhost:9091/user/7

3、面向服务的路由

3.1修改映射配置,通过服务名称获取

在刚才的路由规则中,把路径对应的服务地址写死了!如果同一服务有多个实例的话,这样做显然不合理。 应该根
据服务的名称,去Eureka注册中心查找 服务对应的所有实例列表,然后进行动态路由!
修改application.yml文件,通过服务名称获取:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
      #路由id,可以随便写
        - id: user-service-route
       #代理服务地址
           uri: HTTP://127.0.0.1:9091
          uri: lb://user-service
          #路由断言,可以配置映射路径
          predicates:
            - Path=/user/**
 #*********************/
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

路由配置中uri所用的协议为lb时(以uri: lb://user-service为例),gateway将使用LoadBalancerClient把user-service通过eureka解析为实际的主机和端口,并进行ribbon负载均衡。

4、路由前缀

客户端的请求地址与微服务的服务地址如果不一致的时候,可以通过配置路径过滤器实现路径前缀的添加和去除。(主要也还是为了隐藏真实微服务地址)

提供服务的地址:http://127.0.0.1:9091/user/8

  • 添加前缀:对请求地址添加前缀路径之后再作为代理的服务地址;
    http://127.0.0.1:10010/8 --> http://127.0.0.1:9091/user/8 添加前缀路径/user
  • 去除前缀:将请求地址中路径去除一些前缀路径之后再作为代理的服务地址;
    http://127.0.0.1:10010/api/user/8 --> http://127.0.0.1:9091/user/8 去除前缀路径/api

4.1添加前缀

在gateway中可以通过配置路由的过滤PrefixPath,实现映射路径中地址的添加;

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
      #路由id,可以随意写
        - id: user-service-route
         # 代理的服务地址
          uri: lb://user-service
          # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/user/**
          filters:
          # 添加请求路径的前缀
             - PrefixPath=/user
 #******************/
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

通过 PrefixPath=/xxx 来指定了路由要添加的前缀。 也就是:

  • PrefixPath=/user http://localhost:10010/8 --》http://localhost:9091/user/8
  • PrefixPath=/user/abc http://localhost:10010/8 --》http://localhost:9091/user/abc/8

4.2去除前缀

在gateway中可以通过配置路由的过滤StripPrefix,实现映射路径中地址的去除;

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
      # 路由id,可以随意写
        - id: bill-server-route
        # 代理的服务地址
          uri: lb://user-service
          # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/user/**
          filters:
            # 表示过滤1个路径,2表示两个,一次类推
            - StripPrefix=1
#**************/
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true

通过 StripPrefix=1 来指定了路由要去掉的前缀个数。如:路径 /api/user/1 将会被代理到 /user/1 。 也就是:

  • StripPrefix=1 http://localhost:10010/api/user/8 --》http://localhost:9091/user/8
  • StripPrefix=2 http://localhost:10010/api/user/8 --》http://localhost:9091/8

5、过滤器

5.1简介

Gateway作为网关的其中一个重要功能,就是实现请求的鉴权。而这个动作往往是通过网关提供的过滤器来实现的。前面的 路由前缀 章节中的功能也是使用过滤器实现的。
Gateway自带过滤器有几十个,常见自带过滤器有:

过滤器名称说明
AddRequestHeader对匹配上的请求加上Header
AddRequestParameters对匹配上的请求路由添加参数
AddResponseHeader对从网关返回的响应添加Header
StripPrefix对匹配上的请求路径去除前缀

5.2配置全局默认过滤器

这些自带的过滤器可以和使用 路由前缀 章节中的用法类似,也可以将这些过滤器配置成不只是针对某个路由;而
是可以对所有路由生效,也就是配置默认过滤器:

server:
  port: 10010
spring:
  application:
    name: api-gateway
cloud:
 gateway:
  routes:
    # 路由id,可以随意写
    - id: user-service-route
      # 代理的服务地址
      uri: lb://user-service
      # 路由断言,可以配置映射路径
      predicates:
        - Path=/api/user/**
      filters:
        # 表示过滤1个路径,2表示两个路径,以此类推
        - StripPrefix=1
      # 默认过滤器,对所有路由都生效
    default-filters:
      - AddResponseHeader=X-Response-Foo, Bar
      - AddResponseHeader=abc-myname,lxs
#*****************/
eureka:
 client:
  service-url:
   defaultZone: http://127.0.0.1:10086/eureka
 instance:
  prefer-ip-address: true

上述配置后,再访问 http://localhost:10010/api/user/7 的话;那么可以从其响应中查看到如下信息:

过滤器类型:Gateway实现方式上,有两种过滤器;

  • 局部过滤器:通过 spring.cloud.gateway.routes.filters 配置在具体路由下,只作用在当前路由上;如果配置spring.cloud.gateway.default-filters 上会对所有路由生效也算是全局的过滤器;但是这些过滤器 的实现上都是要实现GatewayFilterFactory接口。
  • 全局过滤器:不需要在配置文件中配置,作用在所有的路由上;实现 GlobalFilter 接口即可。

5.3执行生命周期

Spring Cloud Gateway 的 Filter 的生命周期也类似Spring MVC的拦截器有两个:“pre” 和 “post”。“pre”和 “post”分别会在请求被执行前调用和被执行后调用

这里的 pre 和 post 可以通过过滤器的 GatewayFilterChain 执行filter方法前后来实现

5.4使用场景

常见的应用场景如下:
  • 请求鉴权:一般 GatewayFilterChain 执行filter方法前,如果发现没有访问权限,直接就返回空。
  • 异常处理:一般 GatewayFilterChain 执行filter方法后,记录异常并返回。
  • 服务调用时长统计: GatewayFilterChain 执行filter方法前后根据时间统计。

6、自定义过滤器

6.1 自定义局部过滤器

需求:
在过滤器(MyParamGatewayFilterFactory)中将http://localhost:10010/api/user/8?name=lxs中的参数name的
值获取到并输出到控制台;并且参数名是可变的,也就是不一定每次都是name;需要可以通过配置过滤器的时候做到配置参数名。

在application.yml中对某个路由配置过滤器,该过滤器可以在控制台输出配置文件中指定名称的请求参数的 值。
1)编写过滤器

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.HTTP.server.reactive.ServerHTTPRequest;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class MyParamGatewayFilterFactory extends
AbstractGatewayFilterFactory<MyParamGatewayFilterFactory.Config> {
   static final String PARAM_NAME = "param";
   public MyParamGatewayFilterFactory() {
       super(Config.class);
}
   
   public List<String> shortcutFieldOrder() {
       return Arrays.asList(PARAM_NAME);

}
   @Override
   public GatewayFilter apply(Config config) {
       return (exchange, chain) -> {
         // http://localhost:10010/api/user/8?name=lxs config.param ==>name
         //获取请求参数中param对应的参数名 的参数值
         ServerHTTPRequest request = exchange.getRequest();
         if(request.getQueryParams().containsKey(config.param)){
              request.getQueryParams().get(config.param).
                      forEach(value -> System.out.printf("------------局部过滤器--------%s = %s-
-----", config.param, value));
          }
        return chain.filter(exchange);
   };
}
   public static class Config{
      //对应在配置过滤器的时候指定的参数名
      private String param;
      public String getParam() {
          return param;
     }
     public void setParam(String param) {
          this.param = param;
     }
  }
}

2)修改配置文件

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由id,可以随意写
        - id: user-service-route
      # 代理的服务地址
          uri: lb://user-service
      # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/user/**
           
          filters:
        # 表示过滤1个路径,2表示两个路径,以此类推
            - StripPrefix=1
            # 自定义过滤器
            - MyParam=name
      # 默认过滤器,对所有路由都生效
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=abc-myname,lxs
 #******************/
eureka:
  client:
    service-url:
      defaultZone: http://127.0.0.1:10086/eureka
  instance:
    prefer-ip-address: true
注意:自定义过滤器的命名应该为:***GatewayFilterFactory

测试访问:http://localhost:10010/api/user/7?name=lxs检查后台是否输出name和lxs;但是若访问http://localhost:10010/api/user/7?name2=kaikeba 则是不会输出的

6.2自定义全局过滤器

需求:编写全局过滤器,在过滤器中检查请求中是否携带token请求头。如果token请求头存在则放行;如果token为空或者不存在则设置返回的状态码为:未授权也不再执行下去。

在lxs-gateway工程编写全局过滤器类MyGlobalFilter

@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
       System.out.println("--------------全局过滤器MyGlobalFilter-----------------");
       String token = exchange.getRequest().getHeaders().getFirst("token");
       if(StringUtils.isBlank(token)){
          //设置响应状态码为未授权
          exchange.getResponse().setStatusCode(HTTPStatus.UNAUTHORIZED);
          return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
   }
    @Override
    public int getOrder() {
    //值越小越先执行
    return 1;
   }
}

访问 http://localhost:10010/api/user/7

访问 http://localhost:10010/api/user/7?token=abc

7、Gateway跨域配置

一般网关都是所有微服务的统一入口,必然在被调用的时候会出现跨域问题。
跨域:在js请求访问中,如果访问的地址与当前服务器的域名、ip或者端口号不一致则称为跨域请求。若不解决则不能获取到对应地址的返回结果。
如:从在http://localhost:9090中的js访问 http://localhost:9000的数据,因为端口不同,所以也是跨域请求。
在访问Spring Cloud Gateway网关服务器的时候,出现跨域问题的话;可以在网关服务器中通过配置解决,允许哪
些服务是可以跨域请求的;具体配置如下:

server:
  port: 10010
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        # 路由id,可以随意写
        - id: user-service-route
          # 代理的服务地址
          uri: lb://user-service
          # 路由断言,可以配置映射路径
          predicates:
            - Path=/api/user/**

          filters:
            # 表示过滤1个路径,2表示两个路径,以此类推
            - StripPrefix=1
            # 自定义过滤器
            - MyParam=name
      # 默认过滤器,对所有路由都生效
      default-filters:
        - AddResponseHeader=X-Response-Foo, Bar
        - AddResponseHeader=abc-myname,lxs
      globalcors:
        corsConfigurations:
          '[/**]':
            #allowedOrigins: * # 这种写法或者下面的都可以,*表示全部
            allowedOrigins:
              - "http://docs.spring.io"
            allowedMethods:
              - GET
       
 #******************/
eureka:
 client:
  service-url:
   defaultZone: http://127.0.0.1:10086/eureka
  instance:
   prefer-ip-address: true
hystrix:
 command:
  default:
   execution:
    isolation:
     thread:
      timeoutInMilliseconds: 6000 #服务降级超时时间,默认1S
ribbon:
 ConnectTimeout: 1000 # 连接超时时长
 ReadTimeout: 2000 # 数据通信超时时长
 MaxAutoRetries: 0 # 当前服务器的重试次数
 MaxAutoRetriesNextServer: 0 # 重试多少次服务
说明

上述配置表示:可以允许来自 http://docs.spring.io 的get请求方式获取服务数据。
allowedOrigins 指定允许访问的服务器地址,如:http://localhost:10000 也是可以的。
‘[/**]’ 表示对所有访问到网关服务器的请求地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值