springcloud:4.1 GateWay

概述

Gateway

简介

Spring Cloud Gateway基于Spring 5.0、SpringBoot 2.0和Project Reactor等技术开发
旨在为微服务架构提供一种简单有效的、统一的API路由管理方式,并为微服务架构提供安全、监控、指标和弹性等功能
其目标是替代Zuul

特点

易于编写谓词和过滤器,其Predicates和Filters可作用于特定路由
支持路径重写
支持动态路由
集成了Spring Cloud DiscoveryClient

底层原理

简介

Spring Cloud Gateway 用"Netty + Webflux"实现,不要加入Web依赖,否则会报错,它需要加入Webflux依赖

Netty

Netty 是一个基于NIO的客户、服务器端的编程框架。
提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序

WebFlux

1.Webflux模式替换了旧的Servlet线程模型。用少量的线程处理request和response io操作,这些线程称为Loop线程,
而业务交给响应式编程框架处理,响应式编程是非常灵活的,用户可以将业务中阻塞的操作提交到响应式框架的work线程中执行,
而不阻塞的操作依然可以在Loop线程中进行处理,大大提高了Loop线程的利用率
2.Webflux虽然可以兼容多个底层的通信框架,但是一般情况下,底层使用的还是Netty,毕竟,Netty是目前业界认可的最高性能的通信框架。
而Webflux的Loop线程,正好就是著名的Reactor模式IO处理模型的Reactor线程,如果使用的是高性能的通信框架Netty

三大核心概念

路由

路由是 Gateway 的⼀个基本单元
这是网关的基本构建块,它由一个ID,一个目标URI,一组断言和一组过滤器定义,如果断言为真,则路由匹配

断言

也称谓词,实际上是路由的判断规则,一个路由中可以添加多个谓词的组合

输入类型是一个ServerWebExchange。我们可以使用它来匹配来自HTTP请求的任何内容,例如headers或参数

过滤

可以在请求被路由前或者之后对请求进行修改

Gateway 组件使用了⼀种 FilterChain的模式对请求进行处理,每⼀个服务请求(Request)在发送到目标标服务之前都要被⼀串FilterChain处理。

同理,在 Gateway接收服务响应的过程中也会被 FilterChain 处理⼀把

搭建【cloud-gateway9527】

依赖

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

application.yml

server:
  port: 9527
spring:
  application:
    name: cloud-gateway9527
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false    #不开启服务注册和发现的功能
          lower-case-service-id: true #请求路径上的服务名称转换为小写
      #路由配置
      routes:
        #cloud-eureka-pro接口路由
        - id: cloud-eureka-pro
          uri: lb://cloud-eureka-pro
          predicates:
            - Path=/eureka/pro/**
      #跨域配置
      globalcors:
        # 解决options请求被拦截问题
        add-to-simple-url-handler-mapping: true
        cors-configurations:
          # 拦截的请求
          '[/**]':
            # 允许跨域的请求
            #allowedOrigins: "*" # spring boot2.4以前的配置
            allowedOriginPatterns: "*" # spring boot2.4以后的配置
            # 允许请求中携带的头信息
            allowedHeaders: "*"
            # 运行跨域的请求方式
            allowedMethods: "*"
            # 是否允许携带cookie
            allowCredentials: true
            # 跨域检测的有效期,单位s
            maxAge: 36000
logging:
  pattern:
    console: '%d{MM/dd HH:mm:ss.SSS} %clr(%-5level) ---  [%-15thread] %cyan(%-50logger{50}):%msg%n'

启动类

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class Main {
    public static void main(String[] args) {
        SpringApplication.run(Main.class, args);
        log.info("********** Gateway网关 服务启动成功 *********");
    }
}

相关接口

1.搭建eureka客户端【test-provider8001】

http://t.csdnimg.cn/kIloZ

2.测试网关:http://localhost:9527/payment/index

路由

配置路由

配置文件

spring:
  cloud:
    #路由配置
    routes:
      #cloud-eureka-pro接口路由
      - id: cloud-eureka-pro
        uri: lb://cloud-eureka-pro
        predicates:
          - Path=/eureka/pro/**

配置类

/**
 * JAVA API构建路由规则
 */
@Configuration
public class GatewayConfig {
    @Bean//http://localhost:9527/guonei
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //设置路由 路由ID(随意取,不重复即可)|路径匹配规则|跳转的路径
        routes.route("path_rote",r -> r.path("/guonei").uri("https://www.baidu.com/guonei")).build();
        return routes.build();
    }

}

uri的写法

直接写法

- id: route_example
uri: http://example.com
predicates:
- Path=/api/example/**

表示将 `/api/example/**` 的请求路由到 `http://example.com`

转发片段写法

- id: route_example
uri: forward:/internal-service
predicates:
- Path=/api/example/**
表示将/api/example/**的请求转发到网关内部的/internal-service服务  

重定向写法

- id: route_example
uri: redirect:http://new-url.com
predicates:
- Path=/old-url/**
表示将/old-url/**的请求重定向到http://new-url.com

使用 lb:// 前缀实现负载均衡

- id: route_example
uri: lb://service-name
predicates:
- Path=/api/example/**
表示将 /api/example/**的请求通过负载均衡策略转发到名为service-name的服务实例  

使用 DiscoveryClient 做动态路由

- id: route_example
uri: lb://service-name
predicates:
- Path=/api/example/**
filters:
- RewritePath=/api/example/(?<path>.*), /$\{path}
表示将 `/api/example/**` 的请求通过DiscoveryClient进行动态路由,并使用RewritePath过滤器进行路径重写  

动态路由

简介

默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能

添加Eureka依赖并修改配置文件

我们需要把网关注册到eureka上,然后才能找到注册中心的服务列表

实现思路

搭建两个生产者,但是他们的服务名是一致的,正在配置网关的时候uri写服务名,此时会轮询去调用这两个生产者服务

配置文件示例

#cloud-eureka-pro/cloud-eureka-pro81【动态路由-测试负载均衡】
- id: cloud-eureka-pro-dotailuyou
  uri: lb://cloud-eureka-pro
  predicates:
    - Path=/eureka/pro/port
          

断言

时间方面

注意

需要用UTC时间格式的时间参数

UTC时间格式的时间参数时间生成方法

 public static void main(String[] args) {
        ZonedDateTime now = ZonedDateTime.now();
        System.out.println(now);
    }

After

在该日期时间之后发生的请求都将被匹配

predicates:
  - Path=/payment/**
  - After=2030-02-15T14:54:23.317+08:00[Asia/Shanghai]

Before

在该日期时间之前发生的请求都将被匹配

predicates:
  - Path=/payment/**
  - Before=2030-02-15T14:54:23.317+08:00[Asia/Shanghai]

Between

在datetime1和datetime2之间的请求将被匹配

predicates:
  - Path=/payment/**
  - Between=2030-02-15T14:54:23.317+08:00[Asia/Shanghai],2030-02-15T14:54:23.317+08:00[Asia/Shanghai]
        

请求方面

域名

根据域名断言,是这个域名则通过,不是则不匹配
Host文件新增配置
127.0.0.1 itbaizhan

predicates:
- Path=/payment/**
- Host=itbaizhan

请求方法

这个断言是专门验证HTTP Method的
predicates:
- Path=/payment/**
- Method=GET

请求参数

会从ServerHttpRequest中的Parameters列表中查询指定的属性
predicates:
- Path=/payment/**
- Query=username,\d+

Cookie

顾名思义,Cookie验证的是Cookie中保存的信息,Cookie断言和上面介绍的两种断言使用方式大同小异,
唯一的不同是它必须连同属性值一同验证,不能单独只验证属性是否存在

predicates:
- Path=/payment/**
- Cookie=username,zzyy   

请求头

这个断言会检查Header中是否包含了响应的属性,通常可以用来验证请求是否携带了访问令牌
predicates:
- Path=/payment/**
#请求头要有X-Request-Id属性并且值为整数的正则表达式
- Header=X-Request-Id, \d+

网关过滤器

网关过滤器【内置】

简介

应用在单个路由或者一个分组的路由上

示例:SetStatus过滤器

filters:
    - SetStatus=250 # 修改原始响应的状态码            

内置的网关过滤器

AddRequestHeader    为原始请求添加Header  Header的名称及值
AddRequestParameter    为原始请求添加请求参数    参数名称及值
AddResponseHeader  为原始响应添加Header  Header的名称及值
DedupeResponseHeader   剔除响应头中重复的值 需要去重的Header名称及去重策略
Hystrix    为路由引入Hystrix的断路器保护 HystrixCommand的名称
FallbackHeaders    为fallbackUri的请求头中添加具体的异常信息 Header的名称
PrefixPath 为原始请求路径添加前缀    前缀路径
PreserveHostHeader 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host   无
RequestRateLimiter 用于对请求限流,限流算法为令牌桶   keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 将原始请求重定向到指定的URL    http状态码及重定向的url
RemoveHopByHopHeadersFilter    为原始请求删除IETF组织规定的一系列Header  默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader    为原始请求删除某个Header    Header名称
RemoveResponseHeader   为原始响应删除某个Header    Header名称
RewritePath    重写原始的请求路径  原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader  重写原始响应中的某个Header   Header名称,值的正则表达式,重写后的值
SaveSession    在转发请求之前,强制执行WebSession::save操作 无
secureHeaders  为原始响应添加一系列起安全作用的响应头    无,支持修改这些安全响应头的值
SetPath    修改原始的请求路径  修改后的路径
SetResponseHeader  修改原始响应中某个Header的值  Header名称,修改后的值
SetStatus  修改原始响应的状态码 HTTP 状态码,可以是数字,也可以是字符串
StripPrefix    用于截断原始请求的路径    使用数字表示要截断的路径的数量
Retry  针对不同的响应进行重试    retries、statuses、methods、series
RequestSize    设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
ModifyRequestBody  在转发请求之前修改原始请求体内容   修改后的请求体内容
ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容
Default    为所有路由添加过滤器 过滤器工厂名称及值

网关过滤器【自定义】

需求

通过过滤器,配置是否在控制台输出日志信息,以及是否记录日

实现步骤

1.类名必须叫做XxxGatewayFilterFactory,注入到Spring容器后使用时的名称就叫做Xxx。
2.创建一个静态内部类Config, 里面的属性为配置文件中配置的参数, - 过滤器名称=参数1,参数2…
3.类必须继承 AbstractGatewayFilterFactory,让父类帮实现配置参数的处理。
4.重写shortcutFieldOrder()方法,返回List参数列表为Config中属性集合
5.无参构造方法中super(Config.class)
6.编写过滤逻辑 public GatewayFilter apply(Config config)

自定义局部过滤器

@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
    public LogGatewayFilterFactory(){
        super(Config.class);
    }
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("consoleLog");
    }
    @Override
    public GatewayFilter apply(Config config) {
        return ((exchange, chain) -> {
            if (config.consoleLog) {
                System.out.println("console日志已开启...");
            }
            return chain.filter(exchange);
        });
    }
    //过滤器使用的配置内容,如 - Log=true中的true会传到这里
    @Data
    public static class Config{private boolean consoleLog;}
}

配置使用

filters:
    # 控制日志是否开启
    - Log=true
          

全局过滤器

全局过滤器【内置】

简介

应用在所有的路由上

内置的全局过滤器

路由过滤器(Forward)
路由过滤器(LoadBalancerClient)
Netty路由过滤器
Netty写响应过滤器(Netty Write Response F)
RouteToRequestUrl 过滤器
路由过滤器 (Websocket Routing Filter)
网关指标过滤器(Gateway Metrics Filter)
组合式全局过滤器和网关过滤器排序(Combined Global Filter and GatewayFilter Ordering)
路由(Marking An Exchange As Routed)

全局过滤器【自定义】

需求

对于验证用户是否已经登录及鉴权的过程,可以在网关统一校验
自定义一个GlobalFIlter,去校验所有请求的请求参数中是否包含“token”,如果不包含请求参数“token”则不转发路由,否则执行正常逻辑

自定义全局过滤器

package com.itbaizhan.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;


/**
 * 自定义全局过滤器,需要实现GlobalFilter和Ordered接口
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (StringUtils.isEmpty(token)) {
            System.out.println("鉴权失败。确少token参数。");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }


        if (!"jack".equals(token)) {
            System.out.println("token无效...");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }


        // 继续执行filter链
        return chain.filter(exchange);
    }


    /**
     * 顺序,数值越小,优先级越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
          

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

月木@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值