微服务のGeteWay

目录

概念:

三大核心:

工作流程:

9527网关如何做路由映射:

GetWay高级特性:

按服务名动态路由服务:

[断言Route Predicate Factories :](#断言Route Predicate Factories :)

获取当前时区时间:

[After Route :](#After Route :)

例:

[Before,Between 同理:](#Before,Between 同理:)

[Cookie Route :](#Cookie Route :)

[Header Route :](#Header Route :)

[自定义断言 :](#自定义断言 :)

Filter过滤器:

[1. Filter的作用](#1. Filter的作用)

[2. Filter的分类](#2. Filter的分类)

[1. Pre Filter(前置过滤器)](#1. Pre Filter(前置过滤器))

[2. Post Filter(后置过滤器)](#2. Post Filter(后置过滤器))

[3. Error Filter(错误过滤器)](#3. Error Filter(错误过滤器))

[请求头AddRequestHeader :](#请求头AddRequestHeader :)

[请求参数 RemoveRequestParameter和AddRequestParameter](#请求参数 RemoveRequestParameter和AddRequestParameter)

请求回应ResponseHeader:

前缀和路径相关组:

全局过滤器:

自定义条件Filter:


概念:

微服务中网关在哪里?

三大核心:

Route路由 ,Predicate断言,Filter过滤器

工作流程:

核心逻辑:路由转发,断言判断,执行过滤器链

9527网关如何做路由映射:

前提:

我们需要提前把GeteWay服务注册到consul当中

核心:配置yml

复制代码
server:
  port: 9527

spring:
  application:
    name: cloud-gateway #以微服务注册进consul或nacos服务列表内
  cloud:
    consul: #配置consul地址
      host: localhost
      port: 8500
      discovery:
        prefer-ip-address: true
        service-name: ${spring.application.name}

    gateway:
      routes:
        - id: pay_routh1 #pay_routh1                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/get/**              # 断言,路径相匹配的进行路由


        - id: pay_routh2 #pay_routh2                #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名
          uri: http://localhost:8001                #匹配后提供服务的路由地址
          predicates:
            - Path=/pay/gateway/info/**              # 断言,路径相匹配的进行路由

隐真示假 ,让用户不知道我们真正的地址:

没有使用GeteWay之前访问的路径是 http://localhost:8001/pay/gateway/get/1

使用了GeteWay之后可以使用http://localhost:9527/pay/gateway/get/1

将feignclient请求的微服务更改为注册中心的geteway:

先启动 8001微服务和feign80,可以看到请求失败:

启动9527网关后,请求成功,说明现在请求微服务必须通过geteway,配置成功:

GetWay高级特性:

按服务名动态路由服务:

格式:

uri:lb://service

微服务名为cloud-payment-service,通过使用注册中心的微服务名称,就可以动态的配置路由地址,配置如下:

断言Route Predicate Factories :

官网地址:点击跳转

通过断言判断请求是否路由到微服务。

获取当前时区时间:

在讲解之前,我们需要先知道如何获取默认时区时间

复制代码
public static void main(String[] args)
{
    ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
    System.out.println(zbj);
}

After Route :

官网描述如下:

表示只有在当前时间之后,该路由才会被匹配。

例:

yml配置格式 ,我配置的时间是大于当前时间的,但我们必须要在该时间后才能访问到:

spring:

cloud:

gateway:

routes:

  • id: route1

uri: http://localhost:8081

predicates:

  • Path=/api/**

  • After=2024-12-26T17:31:14.523838700+08:00[Asia/Shanghai]

访问结果如下:

Before,Between 同理:

官方描述如下:

通过键值对的方式配置Cookie,访问时必须带上Cookie,否则访问不到服务。

Header Route :

请求头要有X-Request-Id属性并且值为整数的正则表达式

其他的断言用到了请查阅官方文档,写得很详细

自定义断言 :

java 复制代码
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config>
{
    public MyRoutePredicateFactory()
    {
        super(MyRoutePredicateFactory.Config.class);
    }

    @Validated
    public static class Config{
        @Setter
        @Getter
        @NotEmpty
        private String userType; //钻、金、银等用户等级
    }

    @Override
    public Predicate<ServerWebExchange> apply(MyRoutePredicateFactory.Config config)
    {
        return new Predicate<ServerWebExchange>()
        {
            @Override
            public boolean test(ServerWebExchange serverWebExchange)
            {
                //检查request的参数里面,userType是否为指定的值,符合配置就通过
                String userType = serverWebExchange.getRequest().getQueryParams().getFirst("userType");

                if (userType == null) return false;

                //如果说参数存在,就和config的数据进行比较
                if(userType.equals(config.getUserType())) {
                    return true;
                }

                return false;
            }
        };
    }

    //开启shortcut支持
    @Override
    public List<String> shortcutFieldOrder() {
        return Collections.singletonList("userType");
    }
}

YML配置:

必须加上usertype=diamond才能访问的服务

Filter过滤器:

官方文档:点击跳转

1. Filter的作用

Filters 通常用于以下几种场景:

  • 请求预处理:对请求进行检查、修改、验证或转换,如请求数据格式化、认证、限流、日志记录等。
  • 响应后处理:在响应数据返回客户端之前进行处理,如数据加密、响应格式化、缓存处理等。
  • 路由转发:基于请求中的一些特定信息(如URL路径、请求头、请求参数等)决定如何路由请求。

2. Filter的分类

在不同的网关架构中,Filter的分类有所不同。以 Spring Cloud GatewayAPI Gateway 为例,它们通常将 Filter 分为以下几种类型:

1. Pre Filter(前置过滤器)

前置过滤器在请求到达服务之前执行。它们可以用来进行:

  • 身份验证:如检查JWT令牌是否合法。
  • 日志记录:记录请求的信息。
  • 限流:检查请求是否超过某些限制。
2. Post Filter(后置过滤器)

后置过滤器在服务响应后,客户端接收到响应之前执行。它们可以用来:

  • 修改响应:比如修改响应体或响应头。
  • 性能监控:计算请求处理时间,进行性能分析。
  • 缓存处理:根据某些条件返回缓存的响应。
3. Error Filter(错误过滤器)

错误过滤器用于处理请求过程中发生的错误。例如:

  • 统一错误处理:当发生异常时,返回特定的错误响应。
  • 日志记录:记录错误信息以便后续分析。

请求头AddRequestHeader :

是 Spring Cloud Gateway 中的一个 GatewayFilter 工厂,用于在请求转发之前,向请求中添加自定义的 HTTP 请求头。

向服务端新增方法:

java 复制代码
@GetMapping(value = "/pay/gateway/filter")
    public ResultData<String> getGatewayFilter(HttpServletRequest request)
    {
        String result = "";
        Enumeration<String> headers = request.getHeaderNames();
        while(headers.hasMoreElements())
        {
            String headName = headers.nextElement();
            String headValue = request.getHeader(headName);
            System.out.println("请求头名: " + headName +"\t\t\t"+"请求头值: " + headValue);
            if(headName.equalsIgnoreCase("X-Request-atguigu1")
                    || headName.equalsIgnoreCase("X-Request-atguigu2")) {
                result = result+headName + "\t " + headValue +" ";
            }
        }
        return ResultData.success("getGatewayFilter 过滤器 test: "+result+" \t "+ DateUtil.now());
    }

yml配置:

复制代码
- id: pay_routh3 #pay_routh3
  uri: lb://cloud-payment-service                #匹配后提供服务的路由地址
  predicates:
    - Path=/pay/gateway/filter/**              # 断言,路径相匹配的进行路由
  filters:
    - AddRequestHeader=X-Request-atguigu1,atguiguValue1  # 请求头kv,若一头含有多参则重写一行设置
    - AddRequestHeader=X-Request-atguigu2,atguiguValue2

运行结果:

请求参数 RemoveRequestParameter和AddRequestParameter

打印输入的参数

java 复制代码
System.out.println("=============================================");
        String customerId = request.getParameter("customerId");
        System.out.println("request Parameter customerId: "+customerId);

        String customerName = request.getParameter("customerName");
        System.out.println("request Parameter customerName: "+customerName);
        System.out.println("=============================================");

yml添加:

复制代码
- AddRequestParameter=customerId,9527001 # 新增请求参数Parameter:k ,v
- RemoveRequestParameter=customerName   # 删除url请求参数customerName,你传递过来也是null

测试请求的路径;

http://localhost:9527/pay/gateway/filter?customerId=9999&customerName=z3

打印结果:

可以发现添加到参数会覆盖原有的值,移除的参数就算是传值也会被置为null

请求回应ResponseHeader:

修改之前的响应头:

配置yml:

复制代码
- AddResponseHeader=X-Response-atguigu, BlueResponse # 新增请求参数X-Response-atguigu并设值为BlueResponse
- SetResponseHeader=Date,2099-11-11 # 设置回应头Date值为2099-11-11
- RemoveResponseHeader=Content-Type # 将默认自带Content-Type回应属性删除

修改后响应头:

前缀和路径相关组:

The PrefixPath GatewayFilter Factory

The SetPath GatewayFilter Factory

The RedirectTo GatewayFilter Factory

全局过滤器:

官方文档:点击跳转

新建global类:

java 复制代码
package com.atguigu.cloud.mygateway;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Map;

@Component
@Slf4j
public class MyGlobalFilter implements GlobalFilter, Ordered {

    public final static String BEGIN_VISIT_TIME = "beginVisitTime";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //记录访问接口开始时间
        exchange.getAttributes().put(BEGIN_VISIT_TIME, System.currentTimeMillis());
        //返回统计的各个结果给后台
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            Long beginVisitTime = exchange.getAttribute(BEGIN_VISIT_TIME);
            if(beginVisitTime != null){
                log.info("访问接口主机:"+exchange.getRequest().getURI().getHost());
                log.info("访问接口端口:"+exchange.getRequest().getURI().getPort());
                log.info("访问接口URL:"+exchange.getRequest().getURI().getPath());
                log.info("访问接口URL后面的参数:"+exchange.getRequest().getURI().getRawQuery());
                log.info("访问接口时长:"+(System.currentTimeMillis()-beginVisitTime)+"毫秒");
                log.info("=================分割线======================");
                System.out.println();
            }
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

yml:

测试结果:

自定义条件Filter:

过滤条件是 必须带有 'yuanshen'的请求参数

java 复制代码
package com.atguigu.cloud.mygateway;

import lombok.Getter;
import lombok.Setter;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.SetPathGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

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

@Component
public class MyGatewayFilterFactory extends AbstractGatewayFilterFactory<MyGatewayFilterFactory.Config>
{
    public MyGatewayFilterFactory()
    {
        super(MyGatewayFilterFactory.Config.class);
    }


    @Override
    public GatewayFilter apply(MyGatewayFilterFactory.Config config)
    {
        return new GatewayFilter()
        {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
            {
                ServerHttpRequest request = exchange.getRequest();
                System.out.println("进入了自定义网关过滤器MyGatewayFilterFactory,status:"+config.getStatus());
                if(request.getQueryParams().containsKey("atguigu")){
                    return chain.filter(exchange);
                }else{
                    exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

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

    public static class Config
    {
        @Getter@Setter
        private String status;//设定一个状态值/标志位,它等于多少,匹配和才可以访问
    }
}
//单一内置过滤器GatewayFilter

yml:

补充:

对应关系:

相关推荐
前行的小黑炭18 分钟前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
android·java·kotlin
Java技术小馆23 分钟前
如何设计一个本地缓存
java·面试·架构
数据智能老司机1 小时前
CockroachDB权威指南——SQL调优
数据库·分布式·架构
数据智能老司机1 小时前
CockroachDB权威指南——应用设计与实现
数据库·分布式·架构
XuanXu1 小时前
Java AQS原理以及应用
java
数据智能老司机1 小时前
CockroachDB权威指南——CockroachDB 模式设计
数据库·分布式·架构
风象南4 小时前
SpringBoot中6种自定义starter开发方法
java·spring boot·后端
mghio13 小时前
Dubbo 中的集群容错
java·微服务·dubbo
咖啡教室18 小时前
java日常开发笔记和开发问题记录
java