--- 统一请求入口 Gateway ---

spring cloud gateway 官方文档 Spring Cloud Gateway 中文文档

什么是api网关

对于微服务的每个接口,我们都需要校验请求的权限是否足够,而微服务把项目细化除了许多个接口,若这些接口都要对服务进行权限校验的话,那么无疑加重的代码负担和运行熟读,而如果我使用一个统一的服务来对所有的请求进行权限校验并将请求转发到对应的服务,而服务的接口不对外暴露,那么就可以确保服务收到的请求是服务之间调用的,而不是用户发起的请求调用的,而这个服务就是我们的网关,他是服务的守门神

api网关的结构如图

他通常是后端服务的唯一入口,类似于门面模式,所有的请求都经过他手并受他掉调度,协调,过滤,如果有有多个相同的服务启动了还可以使用负载均衡来平衡压力

常见的开源的api网关的实现有zuul,gateway,nginx,kong

部署Gateway服务

引入依赖

复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>4.0.6</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

gateway服务也需要注册再注册中心nacos中,这个spring-cloud-xxx(loadbalancer)和spring-cloud-starter-xxx(loadbalancer)依赖的区别是前者提供了负载均衡的核心api,但是没有负载均衡的依赖的依赖,如果需要使用某些功能需要自己导入对应的依赖,而后者stater他提供开箱即用的体验,他是一个空的jar包,但是会有一些元数据来告诉他需要导入哪些依赖来确保依赖运行

对项目写一

个启动类之后就可以路由配置了

gateway路由规则

可以写在代码中 不过很麻烦

写在配置文件中要简便许多

uri也可以使用ws://或wss://来进行websocket请求的转发可以直接转发到固定的url或者通过服务发现,ws和wss的区别是前者是未加密的websocket连接后者是加密的websocket连接

http://和https:// 转发到固定的http和https地址

Route Predicate Factories 路由断言工厂

gateway提供了多种路由适配规则来适配多种情况

|------------|-------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
| After | 需要指定一个ZonedDateTime类型的时间,他会匹配这个时间之后的请求,判断依据是当前收到请求的时间 | predicates: # 路由条件 - After=2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
| Before | 在这个时间之前的请求 | predicates: # 路由条件 - Before=2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
| Between | 匹配俩个时间之间的请求 | predicates: # 路由条件 - Between=2025-09-02T13:45:35.359524+08:00[Asia/Shanghai], 2025-09-02T13:38:51.323390200+08:00[Asia/Shanghai] |
| Cookie | 包含指定的cookie,这个cookie的key是cool v是 这个hh可以是正则表达式 | predicates: - Cookie=cool, hh |
| Header | 该请求包含指定的Header字段 且key为haha v满足\d+的正则表达式 | predicates: - Header=haha, \d+ |
| Host | 对请求头中的Host进行匹配 | predicates: - Host=**.some.com, nlog.daxuesoutijiang.com |
| Method | 匹配请求的获取方法 Get Post | predicates: - Method=Get,Post |
| Path | 匹配url的路径 | predicates: - Path=/getServer/** |
| Query | 匹配url的查询参数? 后面的参数,需要一个param,和可选的正则表达式regexp ?hhh=ww | predicates: - Query=hhh, ww |
| RemoteAddr | 匹配的是请求的远程地址 192.168.0.1是ip地址,/16是子网掩码,表示 匹配的是192.168.0.1 - 192.168.255.255 | predicates: - RemoteAddr=192.168.0.1/16 |
| Weight | 将请求分流,如果有请求满足了weight_high和weight_low的规则,那么就会根据weight的权重来进行分配到那个服务中进行,这里是通过相同的权重key(group1)来进*行权重的划分的 | - id: weight_high uri: https://weighthigh.org predicates: - Weight=group1, 8 - id: weight_low uri: https://weightlow.org predicates: - Weight=group1, 2 |

192.168.0.1这后面的.0.1拆开其实是 00000000.00000001 8为二进制,16表示从有往左的16为是网络为那么就对应的这一段地址 192.168.0.1 - 192.168.255.255

Gateway Filter Factory 网关过滤器工厂

predicate决定了请求要经过那个路由处理,如果要对请求进行加工的话就要是使用Filter了

|----------------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------|
| AddRequestParameter | 再url中添加字段,最终到url上是 127.0.0.1:8080/hh?hh=ww&www=hhh 这时多个参数的写法 | filters: - AddRequestParameter=hh,ww AddRequestParameter=www,hhh |
| AddRequestHeader | 再请求头中添加字段h=h1 | filters: - AddRequestHeader=h,h1 |
| AddResponseHeader | 再响应头中添加字段 | filters: - AddResponseHeader=r1,h1 |
| RemoveRequestHeader | 删除请求头的某个字段 | filters: - RemoveRequestHeader=del |
| RemoveResponseHeader | 删除响应头的某个字段 | filters: - emoveResponseHeader=del |
| RequestSize | 限制请求的最大大小单位字节,默认5M | filters: - name: RequestSize args: maxSize: 50000000 |

Retry

若后端响应失败会重试3次,Retry还支持很多其他的参数, 比如

methord:应该初始的http方法,用org.springframework.http.HttpMethod表示

status:要重试的状态代码,org.springframework.http.HttpStatus这个类来表示的

series:要重试的状态代码系列,用 org.springframework.http.HttpStatus.Series表示

exceptions:后端抛出异常的类型要重试

还有个backoff可以去原文档看

如果使用了Retry,他的默认配置是

  • retries: 三次

  • series: 5XX系列

  • methods: GET 请求

  • exceptions: IOExceptionTimeoutException

  • backoff: disabled

复制代码
filters:
  - name: Retry
    args:
      retries: 3
      statuses: BAD_GATEWAY

RequestRateLimiter

对请求的流量进行限制,若请求被限流则会返回

HTTP 429 - Too Many Requests

使用的是令牌桶算法来限流,需要配置一个KeyResolver类型的bean,他会根据请求的类型来算出一个key,然后通过这个key来获取到一个令牌桶,所以RequestRateLimiter是通过某种规则来对同种类型(相同key)的请求进行限流,而不是全局限流

使用的时候需要加入依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency> 

实现KeyResolver方法

@Bean
KeyResolver ipKeyResolver() {
    return exchange -> {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    };
}

这个方法会将请求的ip当作key

复制代码
filters:
  - name: RequestRateLimiter
  args:
    redis-rate-limiter.burstCapacity: 20
    redis-rate-limiter.replenishRate: 12
    redis-rate-limiter.requestedTokens: 1
    key-resolver: "#{@ipKeyResolver}" # 这个就对应了实现的KeyResolver bean

redis-rate-limiter.replenishRate 这个代表的是每秒钟允许多少个请求,每秒钟往令牌桶中添加的令牌数

redis-rate-limiter.burstCapacity 每秒钟能允许的最大的令牌数,令牌桶的最大容量

redis-rate-limiter.requestedTokens 每次请求消耗的令牌数

这里的name表示的是要加入哪个过滤器,args中的是参数

Defualt Filters

之前设置的filter只对当前路由生效,而如果想要使Filter对所有的路由生效就可以添加

spring.cloud.gateway.default-filters属性

复制代码
spring:
  cloud:
    gateway:      
      default-filters:
      - AddRequestParameter=p1,p1

GlobalFilter 全局过滤

GlobalFilter是gateway的全局过滤器,他和GatewayFilter的作用是相同的,他会应用到全局路由中,通常用于实现与安全性,性能检测,日志打印相关联的全局功能

查看GlobalFilter的监控信息

引入依赖

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

添加一些配置文件

复制代码
spring:
  cloud:
    gateway:
      # 开启了一些指标信息的搜集 请求计数 耗时什么的
      metrics:
        enabled: true
management:
  #  暴露所有 Actuator 端点 这样就鞥看到更详细的监控端口
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    # 更详细的健康信息
    health:
      show-details: always
    # 允许远程关闭服务 调用actuator/shutdown发起post请求
    shutdown:
      enabled: true

访问127.0.0.1:8080/actuator 就可以看到详细的监控信息了

当然不添加刚刚哪些配置文件也能访问

过滤器的执行顺序

当配置了global filter和gateway filter之后,网关会把他们合并到一个集合链中来以此执行,每一个过滤器都必须指定一个int值,值越小的优先级越高越先执行,filter通过实现order接口或者添加@order注解来决定int值

对于spring cloud filter的过滤器由spring来决定

用户自定义的过滤器由用户自己决定

当order的值相同时,会按照default filter -> gateway filter -> global filter顺序执行

自定义gateway filter

要自定义filter,那么就需要实现GatewayFilterFactory接口,spring提供了一个实现了GatewayFilterFactory的抽象类,我们只需要实现过滤逻辑和优先级就好

复制代码
@Service
public class CustomizeGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomizeConfig> implements Ordered {

    public CustomizeGatewayFilterFactory() {
        super(CustomizeConfig.class);
    }

    @Override
    public GatewayFilter apply(CustomizeConfig config) {
        return  ((exchange, chain) -> {
          log.info("request url" + exchange.getRequest().getURI() + "  " + config.toString());
            return chain.filter(exchange);
            }));
        });
    }

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

这个过滤器会把请求的url打印出来

如果请求通过的话,就把请求传递到下一个断言 return chain.filter(exchange)

如果不通过那么可以这样直接拒绝 return exchange.getResponse().setComplete()

也可以把通过过滤器的url打印出来

复制代码
            return chain.filter(exchange).then(Mono.defer(() -> {
                //这样获取到的是原来的url,因为这个ServerHttpRequest是不可以修改的,要获取到最后的url需要重新获取到ServerHttpRequest对象
                log.info("end origin: {}", exchange.getRequest().getURI());
                URI routedUri = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
                log.info("end routed: {}", routedUri);
                return Mono.empty();
            }));

自定义 全局过滤器 glocal filter

只需要实现GlobalFilter接口就号

复制代码
@Service
@Slf4j
public class CustomizeGlobalFiler implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info(exchange.getRequest().getURI().toString());  
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

和gateway 实现很相似

end ~~

相关推荐
波波烤鸭2 天前
深入理解 Gateway 网关:原理、源码解析与最佳实践
java·spring·gateway
DO_Community2 天前
DigitalOcean Kubernetes 现已支持 Gateway API 托管服务
容器·kubernetes·gateway
T_Ghost2 天前
SpringCloud微服务网关Gateway
spring cloud·微服务·gateway
Rysxt_4 天前
Spring Boot Gateway 教程:从入门到精通
spring boot·网关·gateway
月夕·花晨4 天前
Gateway -网关
java·服务器·分布式·后端·spring cloud·微服务·gateway
sanggou5 天前
License 集成 Spring Gateway:解决 WebFlux 非阻塞与 Spring MVC Servlet 阻塞兼容问题
spring·gateway·mvc
摘星编程10 天前
Nginx 502 Bad Gateway:从 upstream 日志到 FastCGI 超时复盘
网络·nginx·gateway·php-fpm·fastcgi
网硕互联的小客服10 天前
504 Gateway Timeout:服务器作为网关或代理时未能及时获得响应如何处理?
运维·服务器·gateway
Pierre_11 天前
通过SpringCloud Gateway实现API接口镜像请求(陪跑)网关功能
spring·spring cloud·gateway