Cyan Blog Cyan Blog
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
首页
  • Java (opens new window)
  • JUC (opens new window)
  • JVM (opens new window)
  • Redis

    • Redis安装 (opens new window)
    • Redis基础 (opens new window)
    • Redis实战 (opens new window)
    • Redis集群安装 (opens new window)
    • Redis分布式缓存 (opens new window)
    • Redis多级缓存 (opens new window)
    • Redis原理 (opens new window)
  • 管理工具

    • Maven (opens new window)
    • Git (opens new window)
  • SSM

    • Spring (opens new window)
    • SpringBoot (opens new window)
    • Mybatis (opens new window)
    • MybatisPlus (opens new window)
  • 微服务

    • Docker (opens new window)
    • RabbitMQ (opens new window)
    • SpringCloud (opens new window)
    • Dubbo (opens new window)
    • MongoDB (opens new window)
    • Zookeeper (opens new window)
  • Java面试题 (opens new window)
  • JUC面试题 (opens new window)
  • JVM面试题 (opens new window)
  • Linux面试题 (opens new window)
  • SQL面试题 (opens new window)
  • Maven面试题 (opens new window)
  • Redis面试题 (opens new window)
  • SSM面试题 (opens new window)
  • SpringCloud面试题 (opens new window)
  • Linux (opens new window)
  • C++ (opens new window)
  • 数据库

    • MySQL (opens new window)
    • NoSQL (opens new window)
  • 软件测试

    • 软件测试 (opens new window)
  • 加密解密 (opens new window)
  • bilibili字幕提取 (opens new window)
  • 道理 (opens new window)
  • 关于博主

    • Github (opens new window)
    • CSDN (opens new window)
  • 关于本站

    • 如何搭建博客网站 (opens new window)
  • SSM

  • SpringBoot

  • SpringCloud

    • SpringCloud
    • Nacos
    • OpenFeign
    • Sentinel
    • Gateway
      • 基础入门
      • Predicate - 断言
      • Filter - 过滤器
    • Seata——分布式事务
  • Docker
  • Dubbo
  • MongoDB
  • Zookeeper
  • Spring生态
  • SpringCloud
2025-05-25
0
0
目录

Gateway

# 基础入门

# 功能

image-20250528192355261

# 创建项目

使用网关需要用到注册中心nacos

引入 spring-cloud-starter-gateway 、 spring-cloud-starter-alibaba-nacos-discovery

<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>

将网关注册到注册中心中,创建 application.yml 编写如下配置

spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848
server:
  port: 80

在启动类中标注@EnableDiscoveryClient启用注册到nacos

# 配置网关

创建 application-route.yml 编写如下配置,并将文件导入application.yml中

spring:
  profiles:
    include: route  # 加载名为application-route.yml的配置文件
--- # 或者
spring:
  config:
    import:
      - classpath:application-route.yml  # 显式指定文件路径
spring:
  cloud:
    gateway:
      routes:
        - id: order
          uri: lb://service-order
          predicates:
            - Path=/api/order/**
        - id: product
          uri: lb://service-product
          predicates:
            - Path=/api/product/**

predicates断言:找到匹配的路径就会转发给uri指定路径

此时启动后提示服务不可用,是因为spring将负载均衡单独抽取出来,需要在单独导入

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

# 改造微服务

为 service-order、service-prduct 添加 /api 基础路径

此时访问会404,因为网关只是将路径转发,而我们的项目web前缀路径是没有api的,所以可以为Controller类指定一个前缀路径@RequestMapper("api/order"),但是@RequestMapper不一定可以标注在@SentinelClient上,@SentinelClient注解提供了一个参数path可以指定前缀路径

# 原理

每个网关路由规则都有一个全局唯一的ID;

路由规则根据断言匹配到请求会转发给URI目的地;

转发途中会经过我们设置Filter过滤器;

规则按照顺序匹配,第一个规则匹配后就不会匹配后面的路由规则,可以通过order指定匹配顺序,越小越优先;

image-20250528212929859

# Predicate - 断言

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://example.org
          predicates:
            - name: Path
            args: 
            	patterns: /api/order/**
            	matchTrailingSlash: true

matchTrailingSlash匹配末尾''/'',如果设置为false,/api/order/**和/api/order/**/是两个路径

在idea中可以ctrl+h查看RoutePredicateFactory的实现中看到所有断言的名字

# 全写法Fully Expanded Arguments

spring:
  cloud:
    gateway:
      routes:
        - id: bing_route
          uri: https://cn.bing.com
          predicates:
            - name: Path
            args: 
            	patterns: /search
            - name: Query
            args: 
            	param: q
            	regexp: haha

# 短写法

spring:
  cloud:
    gateway:
      routes:
        - id: bing_route
          uri: https://cn.bing.com
          predicates:
            - name: Path
            args: 
            	patterns: /search
            - Query=q,haha

# 断言规则

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: https://example.org
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]
名 参数(个数/类型) 作用
After 1/datetime 在指定时间之后
Before 1/datetime 在指定时间之前
Between 2/datetime 在指定时间区间内
Cookie 2/string,regexp(正则) 包含cookie名且必须匹配指定值
Header 2/string,regexp 包含请求头且必须匹配指定值
Host N/string 请求host必须是指定枚举值
Method N/string 请求方式必须是指定枚举值
Query 2/string,regexp 包含指定请求参数
RemoteAddr 1/List 请求来源于指定网络域(CIDR写法)
Weight 2/string,int 按指定权重负载均衡
XForwardedRem oteAddr 1/List 从X-Forwarded-For请求头中解析请求来源, 并判断 是否来源于指定网络域

例子:

spring:
  cloud:
    gateway:
      routes:
        - id: bing-route
          uri: https://cn.bing.com/
          predicates:
            - name: Path
              args:
                patterns: /search
            - name: Query
              args:
                param: q
                regexp: haha
          order: 10
          metadata:
            hello: world

匹配条件:

  • 路径:/search
  • 请求参数:q的值为haha

# 自定义断言工厂⭐

继承AbstractRoutePredicateFactory并自定义配置文件VipRoutePredicateFactory,带两个参数param、value

重写shortcutFieldOrder

package com.hmdp.predicate;

import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

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

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

    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                HttpServletRequest request = serverWebExchange.getRequest();
                String value = request.getQueryParams().getFirst(config.getParam());
                return StringUtils.hasText(value) && value.equals(config.getValue());
            }
        }
    }

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


    @Validated
    public static class Config {
        @NotEmpty
        private String param;
        @NotEmpty
        private String value;

        public @NotEmpty String getParam() {
            return param;
        }

        public void setParam(@NotEmpty String param) {
            this.param = param;
        }

        public @NotEmpty String getValue() {
            return value;
        }

        public void setValue(@NotEmpty String value) {
            this.value = value;
        }
    }
}

  • 断言名字会自动匹配为~RoutePredicateFactory前的名字,即Vip
  • 短写法的两个参数顺序会匹配到Arrays.asList("param", "value")上的顺序
spring:
  cloud:
    gateway:
      routes:
        - id: bing_route
          uri: https://cn.bing.com
          predicates:
            - name: Path
            args: 
            	patterns: /search
            - name: Query
            args: 
            	param: q
            	regexp: haha
            - Vip=user,hehe

路径需要满足/search?q=haha&user=hehe

# Filter - 过滤器

image-20250529003826110
名 参数(个数/类型) 作⽤
AddRequestHea der 2/string 添加请求头
AddRequestHea dersIfNotPresen t 1/List 如果没有则添加请求头, key:value⽅式
AddRequestPar ameter 2/string 、string 添加请求参数
AddResponseHe ader 2/string 、string 添加响应头
CircuitBreaker 1/string 仅⽀持forward:/inCaseOfFailureUseThis⽅式进⾏ 熔断
CacheRequestB ody 1/string 缓存请求体
DedupeRespons eHeader 1/string 移除重复响应头, 多个⽤空格分割
FallbackHeader s 1/string 设置Fallback头
JsonToGrpc 请求体Json转为gRPC
LocalResponse Cache 2/string 响应数据本地缓存
MapRequestHea der 2/string 把某个请求头名字变为另—个名字
ModifyRequestB ody 仅 Java 代码⽅式 修改请求体
ModifyRespons eBody 仅 Java 代码⽅式 修改响应体
PrefixPath 1/string ⾃动添加请求前缀路径
PreserveHostHe ader 0 保护Host头
RedirectTo 3/string 重定向到指定位置
RemoveJsonAttr ibutesResponse Body 1/string 移除响应体中的某些Json字段, 多个⽤ ,分割
RemoveRequest Header 1/string 移除请求头
RemoveRequest Parameter 1/string 移除请求参数
RemoveRespon seHeader 1/string 移除响应头
RequestHeader Size 2/string 设置请求⼤⼩, 超出则响应431状态码
RequestRateLim iter 1/string 请求限流
RewriteLocation ResponseHeade r 4/string 重写Location响应头
RewritePath 2/string 路径重写
RewriteRequest Parameter 2/string 请求参数重写
RewriteRespons eHeader 3/string 响应头重写
SaveSession 0 session保存, 配合spring-session框架
SecureHeaders 0 安全头设置
SetPath 1/string 路径修改
SetRequestHea der 2/string 请求头修改
SetResponseHe ader 2/string 响应头修改
SetStatus 1/int 设置响应状态码
StripPrefix 1/int 路径层级拆除
Retry 7/string 请求重试设置
RequestSize 1/string 请求⼤⼩限定
SetRequestHost Header 1/string 设置Host请求头
TokenRelay 1/string OAuth2的token转发

# 例子

filters:
    - RewritePath=/api/order/?(?<segment>.*),/$\{segment} # 路径重写
    - AddResponseHeader=X-Response-Abc,123 # 添加请求头

# 默认DefaultFliter

    filters:
        - RewritePath=/api/order/?(?<segment>.*),/S\{segment}
#		- AddResponseHeaderX-Response-Abc,123
default-filters:
	AddResponseHeader=X-Response-Abc,123

# 全局GlobalFilter

对所有请求都生效的Filter,

package com.hmdp;

import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@SLf4j
public class RtGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        String uri = request.getURI().tostring();
        long start = System.currentTimeMillis();
        log.info("请求【{}】开始,时问,{}", uri, start);
        //=====================以上是前置逻======================
        Mono<Void> filter = chain.filter(exchange)
                .doFinally((result) -> {
                    long end = System.currentTimeMillis();
                    log.info("请【{}】结束:时问:{},E时:ms", uri, end, end - start);
                });//放行 10s
        //=============以下是后置逻提================
        return null;
    }

    @Override
    public int getorder() {
        return 0;
    }
}
特性 default-filters globalFilter
配置方式 通过配置文件(YAML/Properties)集中定义 通过代码实现(GlobalFilter 接口)
执行顺序 在路由级 filters 之前 执行 通过 @Order 或 Ordered 控制
灵活性 仅支持内置过滤器(如 AddResponseHeader) 支持完全自定义逻辑
适用场景 简单的全局响应/请求修改 复杂的全局逻辑(如鉴权、日志)

# 自定义Filter⭐

命名规则:~GatewayFilterFactory

package com.hmdp.predicate;

import org.springframework.http.HttpHeaders;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import reactor.core.publisher.Mono;

@Component
public class OnceTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {

    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    ServerHttpResponse response = exchange.getResponse();
                    HttpHeaders headers=response.getHeaders();
                    String value = config.getValue();
                    if ("uuid".equalsIgnoreCase(value)) {
                        value=UUID.randomUUID().toString();
                    }
                    if ("jwt".equalsIgnoreCase(value)) {
                        value=JwtUtils.generateToken();
                    }
                    headers.add(config.getName(),value);
                }));
            }
        };
    }

}

例子

spring:
  cloud:
    gateway:
      routes:
        - id: bing_route
          uri: https://cn.bing.com
          predicates:
            - name: Path
            args: 
            	patterns: /api/order/**
          filters:
          	- RewritePath=/api/order/?(?<segment>,*),/$\{segment}
          	- OnceToken=X-Response-Token, uuid #/base64/jwt # 自定义Filter

# 面试题——如何解决跨域问题

如果Controller过多,就需要在每个Controller上都添加@CrosOrigin注解 而过滤器CrosFilter需要在每一个服务都配置 在分布式系统中可以使用gateway网关中配置过滤器设置统一跨域

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations: # 是一个Map,/**允许所有的请求
          '[/**]':
            allowed-origin-patterns: '*'
            allowed-headers: '*'
            allowed-methods: '*'

针对性的允许跨域

spring:
  cloud:
    gateway:
      routes:
        - id: cors_route
          uri: https://example.org
          predicates:
            - Path=/service/**
          metadata:
            cors:
              allowedorigins: '*'
              allowedMethods:
                - GET
                - POST
              allowedHeaders: '*'
              maxAge: 30

# 面试题——微服务之间的调用经过网关吗

不经过,微服务之间的调用通过从注册中心获取服务的地址,然后直接进行调用,

也可以经过网关,将FeignClient(openfeign)的服务名改为网关地址,网关会根据路由规则(如/api/service/**)转发到对应服务。

在实际使用中经过网关调用服务不符合结构设计,网关是前端与后台的之间调用,而且通过网关调用服务中间多了一层

上次更新: 2025/6/10 17:21:17
Sentinel
Seata——分布式事务

← Sentinel Seata——分布式事务→

最近更新
01
md
06-10
02
Redis
06-10
03
HBase
06-10
更多文章>
Theme by Vdoing | Copyright © 2025-2025 Cyan Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式