微服务学习顺序:
微服务之Nacos
微服务之OpenFeign
微服务之网关
什么是网关

微服务架构下,分散的服务需要一个 "统一管理者",来解决外部请求对接、服务治理等共性问题,避免每个服务重复开发相同功能,同时提升系统的安全性、可维护性和稳定性。
- 解决 "多服务入口混乱" 问题
微服务架构中,系统被拆分为多个独立服务(如用户服务、订单服务、商品服务),每个服务都有自己的地址和端口。如果没有网关,外部客户端(APP、网页)需要记住所有服务的地址,分别调用,不仅逻辑复杂,还会增加客户端的维护成本。网关提供统一入口,客户端只需访问一个地址,由网关负责转发请求,简化了调用流程。 - 避免 "重复开发共性功能"
权限认证、日志记录、限流、跨域处理等功能 ,是所有服务都需要的共性需求。如果没有网关,每个服务都要单独开发这些功能 ,不仅导致代码冗余、开发效率低,还可能出现实现不统一的问题(如不同服务的认证逻辑不一致)。网关在入口层统一实现这些功能,所有服务直接复用,减少重复工作量。 - 提升 "系统安全性与可控性"
外部请求直接访问后端服务存在安全风险(如恶意请求直接冲击核心服务)。网关作为 "屏障",可以统一拦截非法请求(如未登录、Token 无效的请求)、过滤危险参数,还能隐藏后端服务的真实地址,降低服务暴露风险。同时,通过网关的监控和限流功能,可实时掌控流量情况,避免高并发压垮后端服务。 - 适配 "复杂的架构需求"
微服务架构中常涉及灰度发布、服务熔断、协议转换等场景。网关可直接集成这些能力:比如灰度发布时,通过网关将部分请求转发到新版本服务;后端服务故障时,网关触发熔断返回兜底响应;外部客户端使用的协议(如 HTTP)与后端服务协议(如 RPC)不匹配时,网关负责协议转换,实现兼容。
简单说,网关 就是微服务的 "统一入口 ",既帮客户端简化了调用,又帮后端服务解决了共性问题,是微服务架构中不可或缺的中间层。

GeteWay能做什么
首先要知道网关也是一个java项目。
- 反向代理
- 鉴权(可用过滤器来做)
- 流量控制
- 熔断降级
- 日志监控

核心概念
Router(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由。
Predicate(断言):断言说简单点,就是请求匹配条件。断言是定义匹配条件,如果请求符合条件,则该请求匹配断言所属的路由。
Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改。
Gateway搭建
这里项目是在我的另一篇文章中的项目基础上进行创建:微服务之OpenFeign
在父级项目cloud-alibaba-test下创建springboot项目cloud-gateway-8080

勾选Spring Web、Gateway

删除默认生成的demos.web目录

配置pom文件,引入依赖
xml
<!--引入父工程cloud-alibaba-test,并指定要打成jar包-->
<!--指定父级-->
<parent>
<groupId>com.example</groupId>
<artifactId>cloud-alibaba-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<!--指定打包方式-->
<packaging>jar</packaging>
<!--网关需要使用webflux,这里需要把web依赖替换为换成webflux依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<!--网关依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
nacos注册中心(nacos-discovery)和loadbalancer(使用OpenFeign时需要)已经在父工程cloud-alibaba-test引过了,子项目可以调用,这里就不需要再引入了。
Spring Cloud Gateway是基于webflux的,如果非要web支持的话需要导入spring-boot-starter-webflux而不是spring-boot-start-web。
在父级项目引入项目cloud-gateway-8080
xml
<!--指定子模块-->
<modules>
<module>bill-7780</module>
<module>bill-7781</module>
<module>provider-7790</module>
<module>cloud-common</module>
<module>cloud-gateway-8080</module>
</modules>
启动类添加注解@EnableDiscoveryClient,开启nacos服务注册与发现
java
package com.lp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class CloudGateway8080Application {
public static void main(String[] args) {
SpringApplication.run(CloudGateway8080Application.class, args);
}
}
自动路由
编写application.yml
yml
server:
port: 8080
spring:
application:
name: cloud-gateway-8080 # 服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8850 # nacos服务地址,注册服务
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: true #开启gateway自动发现所处nacos注册中心的所有服务的功能,并为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
lower-case-service-id: true # 服务名转为小写(如服务名是BILL-7780,请求时用/bill-7780/控制器方法访问路径 即可匹配)
网关会自动捕捉放到注册中心的服务,然后生成一个访问路径
启动项目bill-7780、provider-7790、cloud-gateway-8080,注册到nacos服务

浏览器输入路径 本机ip:gateway端口号/要请求的服务名/控制器方法访问路径,如:http://localhost:8080/bill-7780/bill/list

测试成功!!!!
注意:有些版本无法使用,权限不足,需要配置自定义路由。
自定义路由
修改yml文件信息
yml
server:
port: 8080
spring:
application:
name: cloud-gateway-8080 # 服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8850 # nacos服务地址,注册服务
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: false #开启gateway自动发现所处nacos注册中心的所有服务的功能,并为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
lower-case-service-id: true # 服务名转为小写(如服务名是BILL-7780,请求时用 /bill-7780/控制器方法访问路径 即可匹配)
routes:
- id: bill-7780 # 可以随便写,但一般都是服务名,且要唯一
uri: lb://bill-7780 #lb(loadbalancer负载均衡)://服务名。如果没有loadbalancer,就只能使用http://服务IP:端口号
predicates: # 断言(判断请求是否符合当前路由的条件)
- Path=/billApi/** #当请求路径以/billApi开头时,触发当前路由。到此访问路径应为127.0.0.1:8080/billApi/bill/list,但我的controller中没有/billApi访问路径,会访问失败,这里只是演示,配合下面的filters才能访问成功
filters: # 过滤器(对请求/响应进行处理)
- StripPrefix=1 # 去掉请求路径中的第一个路径参数,即去掉/billApi。此时访问路径为127.0.0.1:8080/billApi/bill/list。网关将处理后的/bill/list转发到bill-7780服务,与后端接口路径完全匹配,因此可以正常访问
- id: provider-7790
uri: lb://provider-7790
predicates:
- Path=/providerApi/**
filters:
- StripPrefix=1
- 注意yml格式,尤其是空格。
- 关闭gateway开启服务注册和发现的功能(enabale: false),也可以删去
enabale: false,否则自定义不生效。- routes 后面的路由可以配置多个 ,相当于配置一个数组,一个
-开头的配置就是其中的一个数组元素。
路由主要有四个配置:
- 路由id(id),可以随便写,但一般都是服务名。
- 路由目标(uri),目标服务地址(转发的终点)
uri 定义了请求最终要转发到的服务地址,决定了 "转发到哪里",常见格式有两种:- 负载均衡模式 (推荐,适用于微服务。通过服务名来访问,不用担心服务ip变动问题)
格式:lb://服务名(lb 代表启用负载均衡)- 规则:网关会从服务注册中心(如 Nacos)获取 bill-7780 服务的所有实例(IP + 端口),自动通过负载均衡选择一个实例转发请求。
- 注意:服务名必须与注册中心中注册的服务名完全一致(区分大小写,除非配置了 lower-case-service-id: true 转为小写)。
- 固定地址模式(通过ip访问,若ip变动,就访问不到了。适用于非注册中心的服务)
格式:http://服务IP:端口或https://服务IP:端口- 规则:直接将请求转发到指定的 IP 和端口,不经过服务发现和负载均衡。
- 负载均衡模式 (推荐,适用于微服务。通过服务名来访问,不用担心服务ip变动问题)
- 路由断言(predicates):路径匹配规则(触发转发的条件)
- predicates 是一组 "断言条件",只有当客户端请求满足条件时,才会命中当前路由并转发。最常用的是 Path 断言(路径匹配),比如Path=/billApi,则请求路径必须 以
/billApi开头。
- predicates 是一组 "断言条件",只有当客户端请求满足条件时,才会命中当前路由并转发。最常用的是 Path 断言(路径匹配),比如Path=/billApi,则请求路径必须 以
- 路由过滤器(filters):路径处理规则(转发前的路径转换 )
StripPrefix=n:移除路径的前 n 层前缀
例 :filters: - StripPrefix=1,配合 predicates: Path=/billApi/**
客户端请求路径:/billApi/bill/list。
处理后路径:/bill/list(移除第 1 层前缀 /billApi)。
最终转发到目标服务的路径:/bill/list。- 规则:n 表示要移除的前缀层数(以 / 分隔为一层),如 /a/b/c 中,a 是第 1 层,b 是第 2 层。
PrefixPath=/前缀:给路径添加前缀- 核心规则:
过滤器仅修改 "转发到后端服务的路径",不影响客户端请求网关的原始路径。
多个过滤器可以组合使用,按配置顺序执行。
在自定义路由模式下,访问路径与服务名完全无关。客户端只需按照网关 predicates 中定义的路径规则访问即可,无需知道后端服务的真实名称(如 bill-7780),服务名仅作为网关内部转发时定位目标服务的标识。
注意:当两个自定义路由的 Path 断言值完全相同时(例如均为 /billApi/**),网关会按yml配置文件中 routes 列表的书写顺序依次匹配路由,会导致请求只会匹配第一个路由,第二个路由永远不会被触发。
重启cloud-gateway-8080项目,注册到nacos服务。

浏览器访问路径:本机ip+gateway端口号/billApi/controller方法访问路径


自动路由VS自定义路由
| 路由模式 | 访问路径是否包含服务名 | 服务名的作用范围 |
|---|---|---|
| 自定义路由 | 不包含 | 仅网关内部用于定位服务(uri 中) |
| 自动路由 | 包含(作为路径前缀) | 客户端必须知道服务名才能访问 |
路由过滤器

客户端请求先找到路由,路由匹配时经过过滤器层层筛选,最终访问到微服务。
请求头过滤器配置示例(局部过滤器)
yml
filters: # 过滤器配置
- AddRequestHeader=name,张三# 添加请求头

给所有进入bill-7780的请求添加一个请求头。
请求头的key为name,value为123456。

重启cloud-gateway-8080、bill-7780项目,浏览器访问http://localhost:8080/billApi/bill/list,查看控制台

默认过滤器配置示例(全局过滤器)

default-filters 的配置和routes平级。
只要配置在 default-filters 下面的过滤器,会对routes配置的所有路由都生效。

重启cloud-gateway-8080、bill-7780项目,浏览器访问http://localhost:8080/billApi/bill/list,查看控制台

自定义全局路由过滤器
有时候SpringCloudGateWay提供的过滤器工厂不能满足自己的要求。
可能有时候需要在过滤时做一些其它的逻辑操作,比如过滤的请求中的一些参数,我们要在过滤器中验证这些参数是否合法(符合规范),如果合法则放行,否则返回错误信息。
那么这时候可以选择使用java代码自定义全局过滤器。
创建GateWayFilter类 实现接口GlobalFilter,使用@Order注解设置过滤器优先级

java
package com.lp.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Order(1)
@Component
public class GateWayFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//getHeaders().getFirst("lp") 获取的是 HTTP请求头 中的第一个token参数,可用来校验token。
//前面在yml文件中在全局过滤器中配置过请求头信息中lp的值为666,这里把它当作token来模拟获取请求头中的token
String token = exchange.getRequest().getHeaders().getFirst("lp");
System.out.println("token==="+token);
//1. 获取请求参数
//这里的request并不是servlet中的request
//返回值是一个多键的map集合、也就是说这个map集合的键可以重复
//getQueryParams() 获取的是GET请求路径后通过?拼接的参数
MultiValueMap<String, String> map = exchange.getRequest().getQueryParams();
//2. 获取id参数
String id = map.getFirst("id");
System.out.println("id==="+id);
//3. 校验。我们这里设置如果id为3则放行
if(id.equals("3")){
//放行
System.out.println("进入放行。。。");
return chain.filter(exchange);
}
//4. 拦截
//4.1 禁止访问,设置状态码。当前请求被拦截时,返回的状态码为403,让浏览器不至于显示空白页面
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);//状态码
//4.2 结束处理
return exchange.getResponse().setComplete();
}
}
启动bill-7780、provider-7790、cloud-gateway-8080服务(需要提前启动nacos,单例启动即可)
nacos注册中心

浏览器访问http://localhost:8080/providerApi/bill/listByProId?id=1,被拦截


浏览器访问http://localhost:8080/providerApi/bill/listByProId?id=3,访问成功


当有多个过滤器(或者说一条过滤链)时,Order的值决定了过滤器的执行顺序。
实现Ordered接口的方式无法在GateWayFilter中获得token,建议使用注解@Order
数值越小,优先级越高,越先执行。(例如 order=-1 比 order=0 先执行)
在 Spring Cloud Gateway 中,default-filters(YAML 配置的全局过滤器)和自定义全局过滤器类(实现 GlobalFilter 接口)的优先级,由它们的 order 值 决定,而非 "配置方式"。
default-filters中内置过滤器的默认order :例如内置过滤器AddRequestHeader 的默认 order=1(不同过滤器的默认 order 由 Spring 预先定义)。自定义全局过滤器的 order:由开发者指定,例如 @Order(1) 或 getOrder() { return 1; }。无论哪种全局过滤器,后执行的过滤器都能获取前序过滤器设置的请求头、属性等信息(通过 ServerWebExchange 共享)。
Spring Cloud Gateway 中,当两个过滤器的 order 相同时,执行顺序由它们的 注册顺序 决定:
- default-filters 中的过滤器是 在网关初始化时优先注册 的(属于框架内置配置,加载时机更早)。
- 自定义全局过滤器是 在 Spring 容器扫描时注册 的(加载时机晚于 default-filters)。
因此,当 order 相同时,default-filters 中的 AddRequestHeader 会先于自定义过滤器注册,进而先执行。
当全局过滤器的 注册顺序相同 且 order 值也相同 时,执行顺序可视为随机(不确定)。
建议:为所有过滤器设置唯一且明确的 order 值,通过 order 数值大小严格控制执行顺序,而非依赖注册顺序。
过滤器间的数据共享机制
所有过滤器通过 ServerWebExchange 对象共享请求上下文(包括请求头、属性等):
- AddRequestHeader 先执行时,会将指定的请求头(如 lp: 666)添加到 ServerWebExchange 的请求对象中。
- 而自定义过滤器后执行时,通过 exchange.getRequest().getHeaders() 就能获取到前序过滤器设置的请求头。
设置Order的值有两种方式:
- 使用
@Order注解(推荐),如上 - 实现Ordered接口,并且重写getOrder方法
java
public class GateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
...
return null;
}
@Override
public int getOrder() {
return 1;//返回值即为Order值
}
}
网关的跨域问题
使用CORS方式。
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
一般都是在网关配置跨域,而与网关在同一个nacos服务注册中心的其它服务(比如项目中的bill-7780、provider-7790等)不需要跨域,网关能直接访问这些服务。
方式一:配置application.yml文件
yml
spring:
cloud:
gateway:
globalcors: # 全局的跨域配置
add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
# options请求 就是一种询问服务器是否浏览器可以跨域的请求
# 如果每次跨域都有询问服务器是否浏览器可以跨域对性能也是损耗
# 可以配置本次跨域检测的有效期maxAge
# 在maxAge设置的时间范围内,不去询问,统统允许跨域
corsConfigurations:
'[/**]':
allowedOrigins: # 允许哪些网站的跨域请求
- "http://localhost:8090"
allowedMethods: # 允许的跨域ajax的请求方式
- "GET"
- "POST"
- "DELETE"
- "PUT"
- "OPTIONS"
allowedHeaders: "*" # 允许在请求中携带的头信息
allowCredentials: true # 允许在请求中携带cookie
maxAge: 360000 # 本次跨域检测的有效期(单位毫秒)
# 有效期内,跨域请求不会一直发option请求去增大服务器压力
方式二:使用编码方式定义配置类
java
package com.lp.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.cors.reactive.CorsUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
@Configuration
public class CorsConfig {
private static final String MAX_AGE = "18000L";
@Bean
public WebFilter corsFilter() {
return (ServerWebExchange ctx, WebFilterChain chain) -> {
ServerHttpRequest request = ctx.getRequest();
// 使用SpringMvc自带的跨域检测工具类判断当前请求是否跨域
if (!CorsUtils.isCorsRequest(request)) {
return chain.filter(ctx);
}
HttpHeaders requestHeaders = request.getHeaders();
// 获取请求头
ServerHttpResponse response = ctx.getResponse();
// 获取响应对象
HttpMethod requestMethod = requestHeaders.getAccessControlRequestMethod(); // 获取请求方式对象
HttpHeaders headers = response.getHeaders();
// 获取响应头
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN,
requestHeaders.getOrigin()); // 把请求头中的请求源(协议+ip+端口)添加到响应头中(相当于yml中的allowedOrigins)
headers.addAll(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
requestHeaders.getAccessControlRequestHeaders());
if (requestMethod != null) {
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS,
requestMethod.name()); // 允许被响应的方法(GET/POST等,相当于yml中的allowedMethods)
}
headers.add(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
// 允许在请求中携带cookie(相当于yml中的allowCredentials)
headers.add(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "*");
// 允许在请求中携带的头信息(相当于yml中的allowedHeaders)
headers.add(HttpHeaders.ACCESS_CONTROL_MAX_AGE, MAX_AGE);
// 本次跨域检测的有效期(单位毫秒,相当于yml中的maxAge)
if (request.getMethod() == HttpMethod.OPTIONS) {
// 直接给option请求反回结果
response.setStatusCode(HttpStatus.OK);
return Mono.empty();
}
return chain.filter(ctx);
// 不是option请求则放行
};
}
}

网关限流
网关可以做很多的事情,比如,限流。当我们的系统被频繁的请求(请求量过大)的时候,就有可能将系统压垮。所以为了解决这个问题,需要在每一个微服务中做限流操作,这是很麻烦的。但是如果有了网关,那么就可以在网关系统做限流,因为所有的请求都需要先通过网关系统才能路由到微服务中。

令牌桶算法是比较常见的限流算法之一,大概描述如下:
- 所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
- 根据限流大小,设置自动按照一定的速率 往桶里添加令牌;
- 桶设置最大的放置令牌数量限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
- 请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
- 令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流;

网关限流代码实现
需求:每个ip地址1秒内只能发送1次请求,多出来的请求返回429错误。
spring cloud gateway 默认使用 redis 的 RateLimter 限流算法来实现。所以我们要使用首先需要在网关服务项目的pom文件中引入redis的依赖
xml
<!--redis 限流-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
定义KeyResolver
- 可以在启动类添加如下代码, KeyResolver 用于计算某一个类型的限流的KEY也就是说,可以通过KeyResolver 来指定限流的Key。
java
//定义一个KeyResolver,对IP地址进行限流
@Bean
public KeyResolver ipKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return
Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
- 也可以专门创建一个限流配置类(推荐)

java
package com.lp.config;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
//限流配置类
@Configuration
public class ResolverConfig {
//定义一个KeyResolver,对IP地址进行限流
@Bean
public KeyResolver ipKeyResolver() {
return new KeyResolver() {
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
return Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
};
}
}
修改application.yml中配置项,指定限制流量的配置以及 redis 的配置
yml
server:
port: 8080
spring:
application:
name: cloud-gateway-8080 # 服务名称
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8850 # nacos服务地址,注册服务
username: nacos
password: nacos
gateway:
discovery:
locator:
enabled: false #开启gateway自动发现所处nacos注册中心的所有服务的功能,并为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务。
lower-case-service-id: true # 服务名转为小写(如服务名是BILL-7780,请求时用 /bill-7780/控制器方法访问路径 即可匹配)
routes:
- id: bill-7780 # 可以随便写,但一般都是服务名,且要唯一
uri: lb://bill-7780 #lb(loadbalancer负载均衡)://服务名。如果没有loadbalancer,就只能使用http://服务IP:端口号
predicates: # 断言(判断请求是否符合当前路由的条件)
- Path=/billApi/** #当请求路径以/billApi开头时,触发当前路由。到此访问路径应为127.0.0.1:8080/billApi/bill/list,但我的controller中没有/billApi访问路径,会访问失败,这里只是演示,配合下面的filters才能访问成功
filters: # 过滤器(对请求/响应进行处理)
- StripPrefix=1 # 去掉请求路径中的第一个路径参数,即去掉/billApi。此时访问路径为127.0.0.1:8080/billApi/bill/list。网关将处理后的/bill/list转发到bill-7780服务,与后端接口路径完全匹配,因此可以正常访问
- AddRequestHeader=name,123456 # 添加请求头
- id: provider-7790
uri: lb://provider-7790
predicates:
- Path=/providerApi/**
filters:
- StripPrefix=1
# 限制流量配置
- name: RequestRateLimiter # 请求限流,不能随便乱写
args:
key-resolver: "#{@ipKeyResolver}" #用于限流的键的解析器的Bean对象的名字,即前面限流配置类中的方法名(ipKeyResolver)。它使用SpEL表达式根据#{@beanName}从Spring容器中获取Bean对象
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
redis-rate-limiter.burstCapacity: 2 #令牌桶总容量。
default-filters: # 全局过滤器
- AddRequestHeader=lp,666 # 添加请求头
# redis配置
redis:
host: 127.0.0.1
port: 6380
password: 123456

解释:
- burstCapacity :令牌桶总容量。
- replenishRate :令牌桶每秒填充平均速率。
- key-resolver :用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL表达式根据 #{@beanName} 从 Spring 容器中获取 Bean 对象。
最好用高版本的redis,如3.2.1版本。否则可能会报错。
先启动redis,再启动项目cloud-gateway-8080(nacos也要处于启动状态),浏览器访问http://localhost:8080/providerApi/bill/listByProId?id=3

快速刷新,请求失败就会报错429(因为令牌桶里的令牌不够了,导致请求不通过)

其他限流规则
参数限流:key-resolver: "#{@parameterKeyResolver}"
java
@Bean
public KeyResolver parameterKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("id"));
}
URI限流(路径限流):key-resolver: "#{@pathKeyResolver}",限制同一个请求路径在一定时间内只能请求一定次数
java
@Bean
public KeyResolver pathKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}