09、SpringCloud之Gateway网关组件学习笔记(上)

举报
长路 发表于 2022/11/28 20:10:34 2022/11/28
【摘要】 Gitee仓库、Github仓库博客目录索引(持续更新)动力节点最新SpringCloud视频教程|最适合自学的springcloud+springcloudAlibabaPS本章节中部分图片是直接引用学习课程课件,如有侵权,请联系删除。SpringCloudGateway是SpringCloud的一个全新的API网关项目,目的是为了替换掉Zuul1。技术选型性能方面性能⾼于Zuul,官⽅测试,S

@[toc]

前言

本节配套案例代码:Gitee仓库Github仓库

所有博客文件目录索引:博客目录索引(持续更新)

学习视频:动力节点最新SpringCloud视频教程|最适合自学的springcloud+springcloudAlibaba

PS:本章节中部分图片是直接引用学习课程课件,如有侵权,请联系删除。

一、背景

1.1、网关的背景

在分布式微服务架构中,某个服务可以会有多个实例来去注册到注册中心,那么如何去调用如此多个的服务也成为了一个比较大的问题。

此时客户端去调用服务就会出现以下问题:①客户端访问地址配置问题。②多个服务的认证授权问题,造成鉴权认证功能重复冗余情况。③服务访问量大造成的重构问题。

如何解决上面的问题呢?微服务引入了 网关 的概念,网关为微服务架构的系统提供简单、有效且统一的API路由管理,作为系统的统一入口,提供内部服务的路由中转,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等。

原本的对应服务客户端去直接调服务实例接口转变为统一走gateway网关来进行服务转发:

image-20220729110334653

1.2、是否有网关的对比

没有网关:客户端直接访问我们的微服务,会需要在客户端配置很多的 ip:port,如果 user-service 并发比较大,则无法完成负载均衡。

  • 试想:若是某个服务实例采用集群,那么我们在进行负载均衡配置时难道也要一一配置真实的服务地址嘛,那这就会出现很大的人力问题。

有网关:客户端访问网关,网关来访问微服务,(网关可以和注册中心整合,通过服务名称找到目标的 ip:prot)这样只需要使用服务名称即可访问微服务,可以实现负载均衡,可 以实现 token 拦截,权限验证,限流等操作 。

  • 好处:使用网关呢,我们就不需要在nginx负载均衡中配置大量的服务实例地址,而是只需要配置gateway网关的集群地址,某个请求通过nginx走到网关,接着在网关进行服务发现+负载均衡去访问某个服务的实例。

1.3、网关的技术实现

本章节介绍其中的Gateway:是 Spring Cloud 官方提供的用来取代 zuul(netflix)的新一代网关组件

Zuul 1.0 : Netflix开源的网关,使用Java开发,基于Servlet架构构建,本质就是 web 组件 web 三大组件(监听器 过滤器 servlet)便于二次开发。因为基于Servlet内部延迟严重,并发场景不友好,一个线程只能处理一次连接请求。

  • 性能:使用的是 BIO(Blocking IO) tomcat7.0 以前都是 BIO 性能一般。

Zuul 2.0 : 采用Netty实现异步非阻塞编程模型,一个CPU一个线程,能够处理所有的请求和响应,请求响应的生命周期通过事件和回调进行处理,减少线程数量,开销较小。

  • 性能:性能好采用的是NIO,AIO 异步非阻塞,基于 spring5.x,springboot2.x 和 ProjectReactor 等技术。

GateWay : 是Spring Cloud的一个全新的API网关项目,替换Zuul开发的网关服务,基于Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发,性能高于Zuul

Nginx+lua : 性能要比上面的强很多,使用Nginx的反向代码和负载均衡实现对API服务器的负载均衡以及高可用,lua作为一款脚本语言,可以编写一些简单的逻辑,但是无法嵌入到微服务架构中。

Kong : 基于OpenResty(Nginx + Lua模块)编写的高可用、易扩展的,性能高效且稳定,支持多个可用插件(限流、鉴权)等,开箱即可用,只支持HTTP协议,且二次开发扩展难,缺乏更易用的管理和配置方式


二、认识Springcloud Gateway

2.1、简介

Spring Cloud Gateway 是Spring Cloud的一个全新的API网关项目,目的是为了替换掉Zuul1。

技术选型:基于Spring5.0 + SpringBoot2.0 + WebFlux(基于⾼性能的Reactor模式响应式通信框架Netty,异步⾮阻塞模型)等技术开发。

性能方面:性能⾼于Zuul,官⽅测试,Spring Cloud GateWay是Zuul的1.6倍 ,旨在为微服务架构提供⼀种简单有效的统⼀的API路由管理⽅式。

特点:Spring Cloud Gateway 里明确的区分了 Router 和 Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。

  • 比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者 Query 来做路由。
  • 比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局 Filter,也都可以直接用。当然自定义 Filter 也非常方便。

2.2、gateway的三大核心概念

三个核心:路由、断言、过滤器

路由(Route)能够与注册中心来结合作动态路由。是GateWay中最基本的组件之一,表示一个具体的路由信息载体,主要有一个ID、一个目标URI、一组断言和一组过滤器来定义,具体如下:

  1. id:路由唯一标识,区别于其他的route。
  2. url: 路由指向的目的地URL,客户端请求最终被转发到的微服务。
  3. order: 用于多个Route之间的排序,数值越小越靠前,匹配优先级越高。
  4. predicate:断言的作用是进行条件判断,只有断言为true,才执行路由。
  5. filter: 过滤器用于修改请求和响应信息。

断言(Predicate)返回一个bool类型,用于表示在不同状态情况下,该请求是否符合要求。输入的类型是一个ServerWebExchange,可以使用其来匹配HTTP请求的任何内容,例如headers、cookie等等。

过滤器(filter):在gateway中分为两种类型filter:①Gateway filter。②Global filter。

  • 效果:能够对请求和响应进行修改处理。
  • 两种路由各自用途:
    • ①:一个是针对某一个路由(路径)的 filter,例如对某一个接口做限流。
    • ②:一个是针对全局的 filter token ip 黑名单。

2.3、gateway的工作流程

image-20220729112004917

执行流程如下

1、Gateway ClientSpring Cloud Gateway 发送请求,请求首先会被 HttpWebHandlerAdapter 进行提取组装成网关上下文。

3、此时网关的上下文会传递到 DispatcherHandler ,它负责将请求分发给 RoutePredicateHandlerMapping

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

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

6、通过特定于请求的 Fliter 链运行请求,Filter 被虚线分隔的原因是Filter可以在发送代理请求之前(pre)和之后(post)运行逻辑。

7、执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行“post”过滤器逻辑。

8、处理完毕之后将 Response 返回到 Gateway 客户端。

针对于代理请求pre与post的分别应用场景

  • Filter在pre类型的过滤器可以做参数效验、权限效验、流量监控、日志输出、协议转换等。
  • Filter在post类型的过滤器可以做响应内容、响应头的修改、日志输出、流量监控等

2.4、实际应用的服务架构

若是想要我们的网关达到高可用,那么就需要进行部署集群,并在gateway网关上层配置一个负载均衡层:

image-20220729110725372

2.5、Nginx与Gateway的区别

Nginx:在做路由,负载均衡,限流之前,都有修改 nginx.conf 的配置文件,把需要负载均衡,路由,限流的规则加在里面。

  • 需要手动配置。

gateway:gateway 自动的负载均衡和路由,gateway 和 eureka 高度集成,实现自动的路由,和 Ribbon 结合,实现了负载均衡(

lb),gateway 也能轻易的实现限流和权限验证。

区别

1、Nginx需要去自行配置路由、负载均衡等规则;gateway有动态路由,可与注册中心结合使用。

2、Nginx(c语言)比gateway(gateway)的性能高一点。

3、Nginx是服务器级别的;Gateway是项目级别的。

比较合适的搭配效果如下

image-20220729131146480


三、实战1:搭建SpringCloud Gateway服务

项目版本:SpringBoot 2.3.12.RELEASESpringCloud Hoxton.SR12

3.1、搭建基础Gateway服务,实现路由转发(暂无注册中心)

本小节只需要使用一个gateway与一个服务实例即可。

image-20220729112601422

实现目标:网关实现路由转发的效果。(之后小结会进行动态路由的实现)

login-service:登录模块服务

image-20220729112727197

当前该服务仅仅只要引入web模块即可,当前还没有涉及到注册中心。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

controller/LoginController.java

package com.changlu.loginservice.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * @Description:
 * @Author: changlu
 * @Date: 8:20 PM
 */
@RestController
public class LoginController {

    @GetMapping("/doLogin")
    public String doLogin() {
        return UUID.randomUUID().toString();
    }

}

gateway-service:网关服务

依赖配置:对应springboot、springcloud版本号在三章节下有说明

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

image-20220729112907513

配置文件:application.yaml

server:
  port: ${SERVER_PORT:81}   # 默认是81端口,可以通过命令行读取参数 -DSERVER_PORT=82

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true   # 默认开启,只要加了网关依赖
      routes:
        - id: login-service-route  # 路由id,保持唯一
          uri: http://localhost:8081  # uri
          predicates:
            - Path=/doLogin  # 匹配路径规则 只要你Path匹配上了/doLogin 就往 uri 转发 并且将路径带上
  • 实现:①开启网关(其实是默认开启的)。②编写路由。(若是要进行转发我们只需要配置好id、uri以及断言匹配路径)
  • id:表示的该组路由的id,需要是唯一的。
  • uri:表示匹配到的路由进行转发的地址。
  • predicates:当前是进行一个路径匹配的uri接口。

目前的话我们无需编写任何代码,就可以使用一个路由转发的效果,接下来我们来进行测试一下!

测试:访问路径http://localhost:81/doLogin,即可在gateway中进行转发到我们的login-service服务实例上。

image-20220729113251968

注意:当前仅仅是路由转发指定的地址,并没有去注册中心拉取服务实例进行访问!


3.2、Gateway的两种路由配置方式(暂无注册中心)

方式一:代码路由方式

可参考官方案例:https://spring.io/projects/spring-cloud-gateway#overview

实现目标:路由转发到百度一下

image-20220729132835039

可以看到百度的路由地址是:https://www.baidu.com/s?wd=123

思路:将/s即之后的内容转发到指定的百度网址,实际上与3.1中配置的大体参数类似

package com.changlu.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Description:
 * @Author: changlu
 * @Date: 1:15 PM
 */
@Configuration
public class RouteConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                //路由
                .route("baidu_route", r -> r.path("/s").uri("https://www.baidu.com/"))
                .build();
    }

}

ok,此时我们来测试一下:http://localhost:81/s?wd=123

image-20220729133052224


方式二:yaml配置方式

实际上就是3.1中进行配置的内容:

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true   # 默认开启,只要加了网关依赖
      routes:
        - id: login-service-route  # 路由id,保持唯一
          uri: http://localhost:8081  # uri
          predicates:
            - Path=/doLogin  # 匹配路径规则 只要你Path匹配上了/doLogin 就往 uri 转发 并且将路径带上
        - id: user-service-route  # 路由id,保持唯一
          uri: http://localhost:8082  # uri
          predicates:
            - Path=/info/**  # 匹配路径规则 只要你Path匹配上了/doLogin 就往 uri 转发 并且将路径带上

对于下面的/info/,就是会匹配所有前缀为/info/*的内容

例如访问http://localhost:81/info/doLogin,实际上就会进行转发http://localhost:8081/info/doLogin


3.3、实现动态路由(搭配注册中心)

配置过程

之前3.1中案例并没有搭配注册中心,我们就以3.1中的案例来进行集成实现!

同样是对前两个进行改造:

image-20220729135838681

两个服务都添加如下依赖及配置:

①引入依赖:

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

②配置application.yaml添加eureka连接信息:

# 配置eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    hostname: localhost
    instance-id: ${eureka.instance.hostname}:${spring.application.name}:${server.port}

③开启服务发现:在启动器类上添加

@EnableEurekaClient  //开启服务注册

最后在``gateway-server`服务的yaml配置里进行开启动态路由:

image-20220729140136948

# 服务发现相关配置
discovery:
    locator:
        enabled: true   # 开启动态路由  开启通用应用名称 找到服务的功能
        lower-case-service-id: true  # 开启服务名称小写,因为在eureka中默认服务名是大写的

当前我们已经实现了动态路由了,完全就只需要进行配置即可!

测试动态路由

我们来启动网关、生产者服务以及eureka注册中心:

image-20220729140552786

  • 注册中心的代码案例使用之前博文中的eureka案例:image-20220729140631775

image-20220729140651020

在对login-servicegateway-server都开启了服务注册之后,以及开启了gateway-server的动态路由,我们就可以来实现根据服务名调用指定注册中心中的服务实例了。

再此之前我们进行路由代码配置访问的是:http://localhost:81/doLogin

针对于动态路由,我们在/doLogin前添加一个服务名,例如在eureka中心中login-service注册的服务名为login-service:http://localhost:81/login-service/doLogin

测试一下:ok能够进行测试访问

image-20220729140704866

3.4、非动态路由的配置实现动态路由效果

之前3.1、3.2节是我们进行静态绑定的,那么我们如何来实现静态绑定的方式来达到动态路由的效果呢?

那么我们只需要对对应uri来进行操作即可:

image-20220729141129041

将原本指明服务ip地址以及port端口的更改为负载均衡协议lb://服务名

#          uri: http://localhost:8081  # uri
           uri: lb://login-service   # 实现负载均衡

测试一下

image-20220729141109178

没得问题!

四、实战2:搭建Gateway集群

4.1、搭建详细过程

目标效果:访问nginx,通过nginx来进行负载均衡转发请求到集群gateway中,此时gateway里同样去搭配注册中心进行服务发现来进行负载均衡访问服务实例!下面就开始吧。

准备:nginx服务器+gateway两个不同端口(实际上是不同ip地址)服务+生产者服务实例。

生产者服务实例:使用的是login-service,也就是3.1节中的对外提供了一个接口。

gateway服务实例准备

直接使用的是3.1章节中的gateway,如何创建不同端口的多实例呢?

我们针对yaml配置文件来编写可接收命令传参:

server:
  port: ${SERVER_PORT:81}   # 默认是81端口,可以通过命令行读取参数 -DSERVER_PORT=82

image-20220729114057073

image-20220729114106551

-DSERVER_PORT=82

接着我们来进行启动服务实例:

image-20220729114246658

nginx配置

下载地址:http://nginx.org/en/download.html

image-20220729114323303

我们打开nginx文件夹中的nginx.conf来进行编辑:

image-20220729114446081

  • 第一个框+第二个框:主要目的是为了能够查看到访问请求进行负载均衡的一个日志情况。
  • 第三个框+第四个框时进行负载均衡配置。

配置如下:

log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"'
              '$connection $upstream_addr '
              'upstream_response_time $upstream_response_time request_time $request_time ';

access_log  logs/access.log  main;

upstream www.gateway.com {
    server localhost:81;
    server localhost:82;
}

server {
    location / {
        # root   html;
        # index  index.html index.htm;
        # 代理服务地址
        proxy_pass http://www.gateway.com;
    }
}

nginx相关的启动、关闭命令:

start nginx # 启动,下载好之后到指定目录下执行启动命令即可
nginx -s quit  # 关闭nginx
nginx -s reload  # 优雅重启

# 若是想要停止服务还可以使用这个命令或者使用任务管理器找到nginx.exe来关闭
taskkill /IM nginx.exe /F   # 关闭所有正在启动的nginx服务

4.2、测试集群

接下来我们来访问nginx的80端口:http://localhost/doLogin

image-20220729130136167

image-20220729130216387

那么我们如何来看负载均衡访问服务器的地址呢?

查看nginx的日志:``access.log`即可

可以看到默认的nginx负载均衡是轮训:

image-20220729130303551

注意:默认的日志打印内容是没有访问服务器的地址的,在前面nginx中我是有进行自主配置加上打印的服务器地址这里才会显示的。

五、Predicate断言工厂

核心Predicate就是为了实现一组匹配规则,让请求过来找到对应的 Route 进行处理。

5.1、认识断言

在 gateway 启动时会去加载一些路由断言工厂**(判断一句话是否正确 一个 boolean 表达式** ) ,例如我们3.1中搭建的案例在启动时就会出现如下的一些断言信息:

image-20220729141559021

本质:满足条件的返回true放行,不满足的false进行拦截。

介绍:Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础架构的一部分进行匹配。Spring Cloud Gateway 包括许多内置的路由断言工厂。所有这些断言都与 HTTP 请求的不同属性匹配。您可以将多个路由断言可以组合使用。

源码:Spring Cloud Gateway 创建对象时,使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。

image-20220729141925604

image-20220729142047320

5.2、断言详细配置

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true   # 默认开启,只要加了网关依赖
      routes:
        - id: login-service-route  # 路由id,保持唯一
#          uri: http://localhost:8081  # uri
          uri: lb://login-service   # 实现负载均衡
          predicates:
            - Path=/doLogin  # 匹配路径规则 只要你Path匹配上了/doLogin 就往 uri 转发 并且将路径带上
            - After=2020-01-20T17:42:47.789-07:00[Asia/Shanghai] #此断言匹配发生在指定 日期时间之后的请求,ZonedDateTime dateTime=ZonedDateTime.now()获得
            - Before=2020-06-18T21:26:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定 日期时间之前的请求
            - Between=2020-06-18T21:26:26.711+08:00[Asia/Shanghai],2020-06-18T21:32:26.711+08:00[Asia/Shanghai] #此断言匹配发生在指定日期时间之间的请求
            - Cookie=name,xiaobai #Cookie 路由断言工厂接受两个参数,Cookie 名称和 regexp(一 个 Java 正则表达式)。此断言匹配具有给定名称且其值与正则表达式匹配的 cookie
            - Header=token,123456 #头路由断言工厂接受两个参数,头名称和 regexp(一个 Java 正 则表达式)。此断言与具有给定名称的头匹配,该头的值与正则表达式匹配。
            - Host=**.bai*.com:* #主机路由断言工厂接受一个参数:主机名模式列表。该模式是一 个 ant 样式的模式。作为分隔符。此断言匹配与模式匹配的主机头
            - Method=GET,POST #方法路由断言工厂接受一个方法参数,该参数是一个或多个参数: 要匹配的 HTTP 方法
            - Query=username,cxs #查询路由断言工厂接受两个参数:一个必需的 param 和一个 可选的 regexp(一个 Java 正则表达式)。
            - RemoteAddr=192.168.1.1/24 #RemoteAddr 路由断言工厂接受一个源列表(最小大小 1), 这些源是 cidr 符号(IPv4 或 IPv6)字符串,比如 192.168.1.1/24(其中 192.168.1.1 是 IP 地址,24 是子网掩码)。

其他额外的包含有权重属性:下面

spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      enabled: true  
      routes:
        - id: weight_high
          uri: https://weighthigh.org
          predicates:
            - Weight=group1, 2  # 权重
        - id: weight_low
          uri: https://weightlow.org
          predicates:
            - Weight=group1, 8 # 权重

5.3、实战3:配置一个After断言

效果:指定某个接口只能在指定时间后才能够进行访问,否则无法访问,报出404异常。

配置内容如下:- After=2022-07-29T17:23:21.719+08:00[Asia/Shanghai]

image-20220729162636404

image-20220729163444743

可以看到当前时间是四点多,配置After是五点多,也就是说这个接口在五点多才能够访问,再此之前不能够访问!

测试效果

image-20220729162545663

5.4、自定义断言器

自定义步骤

image-20220729165619972

1、编写一个断言工厂类:注意工厂类的名字尽量为xxxRoutePredicateFactory,因为之后配置文件要进行配置

package com.changlu.config;


import lombok.Data;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.function.Predicate;

/**
 * @Description:
 * @Author: changlu
 * @Date: 4:38 PM
 */
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

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

    //此时Config也就是自定义配置的一些参数
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            System.out.println("当前进入到CheckAuthRoutePredicateFactory:" + config.getName());
            return config.getName().equals("changlu");
        };
    }

    @Data
    static class Config {
        private String name;
    }
}

2、yaml来进行配置

image-20220729165728721

  • -name表示的是工厂名称。
  • args.name:其中的name就是工厂参数。
- name: CheckAuth #自定义路由断言工厂的名称xxxRoutePredicateFactory,这个xxx就是在这里指明
    args:
    name: changlu1  #传入到自定义路由断言工厂的参数

测试

果然由于配置文件的name与在断言方法类中的值不一致,此时该接口就访问不到了

image-20220729165859698

将name修改为changlu,再次尝试一下:

image-20220729165956161

六、Filter过滤器工厂

6.1、介绍

介绍:gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入Http 请求和返回Http响应

分类

按照生命周期:pre(在业务逻辑前)、post(在业务逻辑后)。

  • Filter在pre类型的过滤器可以做参数效验、权限效验、流量监控、日志输出、协议转换、限流token认证等。
  • Filter在post类型的过滤器可以做响应内容、响应头的修改、日志输出、流量监控等

按照种类区别:路由过滤器(某个路由单独使用)、全局过滤器(所有路由)。

  • 路由过滤器(GatewayFilter):需要配置某个路由,才能过滤。如果需要使用全局路由,需要配置 Default。
  • 全局过滤器(GlobalFilter):不需要配置路由,系统初始化作用到所有路由上 。

官网

官网-gatewayfilter-factories:包含31中单一路由过滤器。

官网-global-filters:包含9种全局路由过滤器。


6.2、自定义全局过滤器

自定义过程

我们基于之前的3.1案例的gateway-server来进行自定义。

image-20220729213430957

自定义全局filter实现了一个GlobalFilter(过滤方法)、Ordered(执行顺序):

package com.changlu.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
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.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;

/**
 * @Description: 自定义全局过滤器
 * @Author: changlu
 * @Date: 9:17 PM
 */
@Component
public class MyGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求体以及请求对象
        ServerHttpRequest request = exchange.getRequest();
        // HttpServletRequest  这个是web里面的
        // ServerHttpRequest  webFlux里面 响应式里面的
        ServerHttpResponse response = exchange.getResponse();
        //通过请求对象可以拿到请求的一系列内容
        String path = request.getURI().getPath();//uri路径
        System.out.println("path:" + path);
        HttpHeaders headers = request.getHeaders();//请求头
        System.out.println("headers:" + headers);
        String name = request.getMethod().name();//请求方法名,也就是对应ip:port/xxx,这个/xxx
        System.out.println("method name:"+ name);
        String ip = request.getHeaders().getHost().getHostString();//获取到ip主机名
        System.out.println("ip:" + ip);

        //来进行测试响应数据
        // 用了微服务 肯定是前后端分离的 前后端分离 一般前后通过 json
        // {"code":200,"msg":"ok"}
        //1、设置响应头
        response.getHeaders().set("content-type", "application/json;charset=utf-8");
        //2、响应结果集封装
        HashMap<String, Object> result = new HashMap<>();
        result.put("code", HttpStatus.UNAUTHORIZED.value());
        result.put("msg", "暂未授权");
        ObjectMapper objectMapper = new ObjectMapper();//jackson工具类
        byte[] data = new byte[0];
        try {
            data = objectMapper.writeValueAsBytes(result);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        //通过使用buffer工厂类来将其转为一个数据包(底层是基于netty,该对象底层是nio的bytebuffer)
        DataBuffer wrap = response.bufferFactory().wrap(data);
//        return response.writeWith(Mono.just(wrap));
        //放行过滤器
        return chain.filter(exchange);
    }

    //越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

其中放行是执行chain的方法:

return chain.filter(exchange);

若是直接拦截结束,则是对response进行写数据:

//这个wrap包装成netty中的DataBuffer
return response.writeWith(Mono.just(wrap));

测试

直接gateway中进行拦截响应

image-20220730083837230

image-20220730084012998

放行效果

image-20220730083722617

此时可以直接访问对应login-service接口:

image-20220730083803255

该过滤器打印的一些request请求对象的信息:

image-20220730104606411

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。