SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客
Web 有三大组件(监听器 过滤器 servlet),Spring Cloud GateWay 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念
1、Route(路由)
2、Predicate(断言)
3、Filter(过滤)
Gateway有两种配置路由方式
参考官网给出demo Spring Cloud Gateway
- @SpringBootApplication
- public class DemogatewayApplication {
- @Bean
- public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
- return builder.routes()
- .route("path_route", r -> r.path("/get")
- .uri("http://httpbin.org"))
- .route("host_route", r -> r.host("*.myhost.org")
- .uri("http://httpbin.org"))
- .route("rewrite_route", r -> r.host("*.rewrite.org")
- .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
- .uri("http://httpbin.org"))
- .route("hystrix_route", r -> r.host("*.hystrix.org")
- .filters(f -> f.hystrix(c -> c.setName("slowcmd")))
- .uri("http://httpbin.org"))
- .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
- .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
- .uri("http://httpbin.org"))
- .route("limit_route", r -> r
- .host("*.limited.org").and().path("/anything/**")
- .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
- .uri("http://httpbin.org"))
- .build();
- }
- }

在 springcloud-7-service-eureka-gateway 模块总 自定义一个配置类 GatewayRouteConfig
- 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;
-
- /**
- * 路由配置类
- */
- @Configuration
- public class GatewayRouteConfig {
-
- /**
- * 代码的路由 和 application.yml文件 不冲突,都可以用
- * 如果 uri后面给了一个访问地址 和匹配地址相同,就不会再拼接
- */
- @Bean
- public RouteLocator customRoiteLocator(RouteLocatorBuilder builder){
- //以下url从B站中引用
- return builder.routes()
- .route("movie-id",r->r.path("/movie").uri("https://www.bilibili.com/movie/?spm_id_from=333.1007.0.0"))
- .route("douga-id",r->r.path("/v/douga").uri("https://www.bilibili.com/v/douga/?spm_id_from=333.1007.0.0"))
- .build();
- }
- }
启动springboot 类
输入访问 http://localhost:81/movie
即会跳转到相应 url 进行访问资源

即 SpringCloud - Spring Cloud 之 Gateway网关(十三)_MinggeQingchun的博客-CSDN博客
文中的 三、Gateway应用方式
application.properties
- server.port=81
-
- #eureka注册中心首页的Application这一栏
- spring.application.name=springcloud-7-service-eureka-gateway
-
- #每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
- eureka.instance.lease-renewal-interval-in-seconds=2
- #告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
- eureka.instance.lease-expiration-duration-in-seconds=10
- #告诉服务端,服务实例以IP作为链接,不是取机器名
- eureka.instance.prefer-ip-address=false
-
- #注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
- eureka.instance.instance-id=${spring.application.name}:${server.port}
- #注册中心的链接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
- eureka.client.service-url.defaultZone=http://localhost:8761/eureka
-
- #网关路由配置
- #开启网关,默认开启
- spring.cloud.gateway.enabled=true
- #节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
- #路由 id,没有固定规则,但唯一
- spring.cloud.gateway.routes[0].id=login-service-route
- #匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
- spring.cloud.gateway.routes[0].uri=http://localhost:9001
- #以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
- #断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
- #也可以全局匹配,如 /service/**
- spring.cloud.gateway.routes[0].predicates[0]=Path=/doLogin
- #只能是 GET 请求时,才能访问
- spring.cloud.gateway.routes[0].predicates[0]=Method=GET,POST
-
- #配置第二个路由规则
- spring.cloud.gateway.routes[1].id=admin-service-route
- spring.cloud.gateway.routes[1].uri=http://localhost:9001
- spring.cloud.gateway.routes[1].predicates[0]=Path=/doAdmin
- spring.cloud.gateway.routes[1].predicates[0]=Method=GET,POST
-
- #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
- spring.cloud.gateway.discovery.locator.enabled=true
- #是将请求路径上的服务名配置为小写(服务注册的时候,向注册中心注册时将服务名转成大写了),如以/service/*的请求路径被路由转发到服务名为service的服务上
- spring.cloud.gateway.discovery.locator.lower-case-service-id=true
application.yml
- server:
- port: 81
-
- #eureka注册中心首页的Application这一栏
- spring:
- application:
- name: springcloud-7-service-eureka-gateway
- #网关路由配置
- cloud:
- gateway:
- #开启网关,默认开启
- enabled: true
- #节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
- routes:
- #路由 id,没有固定规则,但唯一
- - id: login-service-route
- #匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
- uri: http://localhost:9001
- #以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
- predicates:
- #断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
- #也可以全局匹配,如 /service/**
- - Path=/doLogin
- #只能是 GET,POST 请求时,才能访问
- - Method=GET,POST
- #配置第二个路由规则
- - id: admin-service-route
- uri: http://localhost:9001
- predicates:
- - Path=/doAdmin
- - Method=GET,POST
- #表明gateway开启服务注册和发现的功能,并且spring cloud gateway自动根据服务发现为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
- discovery:
- locator:
- enabled: true
- #是将请求路径上的服务名配置为小写(服务注册的时候,向注册中心注册时将服务名转成大写了),如以/service/*的请求路径被路由转发到服务名为service的服务上
- lower-case-service-id: true
-
- eureka:
- instance:
- #每间隔5s,向Eureka服务注册中心发送一次心跳,证明服务是否依然“存活”
- lease-renewal-interval-in-seconds: 2
- #告诉服务端,如果10s之内没有发送心跳,就代表故障,将本服务踢出
- lease-expiration-duration-in-seconds: 10
- #告诉服务端,服务实例以IP作为链接,不是取机器名
- prefer-ip-address: false
- #注册服务实例ID,,服务ID必须唯一 springcloud-7-service-eureka-gateway
- instance-id: ${spring.application.name}:${server.port}
- #注册中心的链接地址 http://eureka8761:8761/eureka,http://eureka8762:8762/eureka,http://eureka8763:8763/eureka
- client:
- service-url:
- defaultZone: http://localhost:8761/eureka
-
-
我们设置的 routes的 uri 都是写死的,这样不符合微服务的要求,微服务是只要知道服务的名字,根据名字去找,且直接写死URI就没有负载均衡的效果
默认情况下 Gateway 会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路
由进行转发,从而实现动态路由的功能
uri 的协议(如 http 协议)为 lb(load Balance),表示启用 Gateway 的负载均衡功能。
lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri
- #匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
- #uri 的协议为 lb(load Balance),表示启用 Gateway 的负载均衡功能
- #lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri;serviceName要和启动的微服务名保持一致
- spring.cloud.gateway.routes[0].uri=lb://springcloud-7-service-eureka-gateway-login

- #eureka注册中心首页的Application这一栏
- spring:
- application:
- name: springcloud-7-service-eureka-gateway
- #网关路由配置
- cloud:
- gateway:
- #开启网关,默认开启
- enabled: true
- #节点 routes 是一个List 对象,其中 routes 集合中中又包含多个对象,每个对象有三个属性(一个 索引[0]代表一个对象)
- routes:
- #路由 id,没有固定规则,但唯一
- - id: login-service-route
- #匹配后提供服务的路由地址;uri统一资源定位符 url 统一资源标识符
- #uri: http://localhost:9001
- #uri 的协议为 lb(load Balance),表示启用 Gateway 的负载均衡功能
- #lb://serviceName 是 spring cloud gateway 在微服务中自动为我们创建的负载均衡 uri;serviceName要和启动的微服务名保持一致
- uri: lb://springcloud-7-service-eureka-gateway-login
- #以下是断言条件,必选全部符合条件;断言是给某一个路由来设定的一种匹配规则 默认不能作用在动态路由上
- predicates:
- #断言,路径匹配,只要Path匹配上了/doLogin 就往 uri 转发 并且将路径带上 注意:Path 中 P 为大写
- #也可以全局匹配,如 /service/**
- - Path=/doLogin
- #只能是 GET,POST 请求时,才能访问
- - Method=GET,POST

浏览器输入访问http://localhost:81/springcloud-7-service-eureka-gateway-login/doLogin

Gateway 启动时会去加载一些路由断言工厂(一个 boolean 表达式 )
我们可以通过上述官网查看断言表达式
断言就是路由添加一些条件(通俗的说,断言就是一些布尔表达式,满足条件的返回 true,不满足的返回 false)
Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础架构的一部分进行匹配。
Spring Cloud Gateway 包括许多内置的路由断言工厂。所有这些断言都与 HTTP请求的不同属性匹配。可以将多个路由断言可以组合使用
Spring Cloud Gateway 创建对象时,使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。

| 规则 | 实例 | 说明 |
|---|---|---|
| Path | - Path=/gate/,/rule/ | ## 当请求的路径为gate、rule开头的时,转发到http://localhost:9023服务器上 |
| Before | - Before=2020-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之前的请求才会被转发到 http://localhost:9023服务器上 |
| After | - After=2020-01-20T17:42:47.789-07:00[America/Denver] | 在某个时间之后的请求才会被转发 |
| Between | - Between=2020-01-20T17:42:47.789-07:00[America/Denver],2020-01-21T17:42:47.789-07:00[America/Denver] | 在某个时间段之间的才会被转发 |
| Cookie | - Cookie=chocolate, ch.p | 名为chocolate的表单或者满足正则ch.p的表单才会被匹配到进行请求转发 |
| Header | - Header=X-Request-Id, \d+ | 携带参数X-Request-Id或者满足\d+的请求头才会匹配 |
| Host | - Host=www.hd123.com | 当主机名为www.hd123.com的时候直接转发到http://localhost:9023服务器上 |
| Method | - Method=GET | 只有GET方法才会匹配转发请求,还可以限定POST、PUT等请求方式 |
application.yml配置文件
- server:
- port: 81
-
- spring:
- application:
- name: gateway-81
- cloud:
- gateway:
- enabled: true #开启网关,默认是开启的
- routes: #设置路由,注意是数组,可以设置多个,按照 id 做隔离
- - id: user-service #路由 id,没有要求,保持唯一即可
- uri: lb://provider #使用 lb 协议 微服务名称做负均衡
- predicates: #断言匹配
- - Path=/info/** #和服务中的路径匹配,是正则匹配的模式
- - 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 是子网掩码)。
Gateway 里面的过滤器和 Servlet 里面的过滤器,功能差不多,路由过滤器可以用于修改进入
Http 请求和返回 Http 响应
Gateway 中过滤器
1、按生命周期
pre 在业务逻辑之前
post 在业务逻辑之后
2、按种类
GatewayFilter 需要配置某个路由,才能过滤。如果需要使用全局路由,需要配置 DefaultFilters
GlobalFilter 全局过滤器,不需要配置路由,系统初始化作用到所有路由上
全局过滤器 统计请求次数;限流;token 的校验;ip 黑名单拦截 ;跨域本质(filter) ;144 开头的电话;限制一些 ip 的访问
GlobalFilter 是一种作用于所有的路由上的全局过滤器,通过它,我们可以实现一些统一化的业务功能,例如权限认证、IP 访问限制等。当某个请求被路由匹配时,那么所有的 GlobalFilter 会和该路由自身配置的 GatewayFilter 组合成一个过滤器链
- 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;
-
- /**
- * 自定义全局过滤器
- */
- @Component
- public class GlobalFilterConfig implements GlobalFilter, Ordered {
-
- /**
- * 过滤方法
- * 过滤器链模式;责任链模式
- */
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- //针对请求的过滤,拿到请求的header、url、参数等
-
- // HttpServletRequest 是web里面的
- // ServerHttpRequest 是webFlux里面(响应式)
- ServerHttpRequest request = exchange.getRequest();
-
- String path = request.getURI().getPath();
- System.out.println("path====" + path);
-
- HttpHeaders headers = request.getHeaders();
- System.out.println("headers====" + headers);
-
- String methodName = request.getMethod().name();
- System.out.println("methodName====" + methodName);
-
- //IPV4、IPV6地址
- String hostName = request.getRemoteAddress().getHostName();
- System.out.println("hostName====" + hostName);
-
- String ip = request.getHeaders().getHost().getHostString();
- System.out.println("ip====" + ip);
-
-
- // 响应相关的数据
- ServerHttpResponse response = exchange.getResponse();
- //响应头设置编码
- response.getHeaders().set("content-type","application/json;charset=utf-8");
-
- HashMap<String ,Object> map = new HashMap<>(4);
- map.put("code", HttpStatus.UNAUTHORIZED.value());
- map.put("msg","你未授权");
-
- ObjectMapper objectMapper = new ObjectMapper();
- //将一个map转成一个字节数组
- byte[] bytes = new byte[0];
- try {
- bytes = objectMapper.writeValueAsBytes(map);
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- //通过buffer工厂将字节数组包装成一个数据包
- DataBuffer wrap = response.bufferFactory().wrap(bytes);
- return response.writeWith(Mono.just(wrap));
-
- // 放行 到下一个过滤器
- // return chain.filter(exchange);
- }
-
- /**
- * 指定顺序的方法,越小越先执行
- */
- @Override
- public int getOrder() {
- return 0;
- }
- }

1、token认证拦截
- import com.fasterxml.jackson.core.JsonProcessingException;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import org.springframework.beans.factory.annotation.Autowired;
- 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.data.redis.core.StringRedisTemplate;
- 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.util.CollectionUtils;
- import org.springframework.util.StringUtils;
- import org.springframework.web.server.ServerWebExchange;
- import reactor.core.publisher.Mono;
-
- import java.util.Arrays;
- import java.util.HashMap;
- import java.util.List;
-
- /**
- * token校验
- */
- @Component
- public class TokenCheckFilter implements GlobalFilter, Ordered {
- /**
- * 指定好放行的路径
- */
- public static final List<String> ALLOW_URL = Arrays.asList("/springcloud-7-service-eureka-gateway-login/doLogin", "/myUrl","/doLogin");
-
- @Autowired
- private StringRedisTemplate redisTemplate;
-
- /**
- * 和前端约定好 一般放在请求头里面 一般 key Authorization value bearer token
- * 1、拿到请求url
- * 2、判断放行
- * 3、拿到请求头
- * 4、拿到token
- * 5、校验
- * 6、放行/拦截
- */
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- ServerHttpRequest request = exchange.getRequest();
- String path = request.getURI().getPath();
- if (ALLOW_URL.contains(path)) {
- return chain.filter(exchange);
- }
- // 检查
- HttpHeaders headers = request.getHeaders();
- List<String> authorization = headers.get("Authorization");
- if (!CollectionUtils.isEmpty(authorization)) {
- String token = authorization.get(0);
- if (StringUtils.hasText(token)) {
- // 约定好的有前缀的 bearer token
- String realToken = token.replaceFirst("bearer ", "");
- if (StringUtils.hasText(realToken) && redisTemplate.hasKey(realToken)) {
- return chain.filter(exchange);
- }
- }
- }
- // 拦截
- ServerHttpResponse response = exchange.getResponse();
- response.getHeaders().set("content-type","application/json;charset=utf-8");
-
- HashMap<String, Object> map = new HashMap<>(4);
- // 返回401
- map.put("code", HttpStatus.UNAUTHORIZED.value());
- map.put("msg","未授权");
- ObjectMapper objectMapper = new ObjectMapper();
- byte[] bytes = new byte[0];
- try {
- bytes = objectMapper.writeValueAsBytes(map);
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- DataBuffer wrap = response.bufferFactory().wrap(bytes);
- return response.writeWith(Mono.just(wrap));
- }
-
- /**
- * 这个顺序 最好在0附近 -2 -1 0 1
- * @return
- */
- @Override
- public int getOrder() {
- return 1;
- }
2、IP认证拦截
- 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.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.Arrays;
- import java.util.HashMap;
- import java.util.List;
-
- /**
- * 网关里面 过滤器
- * ip拦截
- * 请求都有一个源头
- * 电话 如:144 开头
- * 请求------->gateway------->service
- * 黑名单 black_list
- * 白名单 white_list
- * 根据数量
- * 像具体的业务服务 一般黑名单
- * 一般像数据库 用白名单
- */
- @Component
- public class IPCheckFilter implements GlobalFilter, Ordered {
-
- /**
- * 网关的并发比较高,不要在网关里面直接操作mysql
- * 后台系统可以查询数据库,用户量并发量不大
- * 如果并发量大 可以查redis 或者 在内存中写好
- */
- public static final List<String> BLACK_LIST = Arrays.asList("127.0.0.1", "144.128.139.137");
-
- /**
- * 1、获取到ip
- * 2、校验ip是否符合规范
- * 3、放行 OR 拦截
- */
- @Override
- public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
- ServerHttpRequest request = exchange.getRequest();
- String ip = request.getHeaders().getHost().getHostString();
-
- // 查询数据库 看这个ip是否存在黑名单里面
- if (!BLACK_LIST.contains(ip)){
- return chain.filter(exchange);
- }
-
- //拦截
- //注:此处如果拦截IP,就要放开 GlobalFilter 的拦截
- ServerHttpResponse response = exchange.getResponse();
- response.getHeaders().set("content-type","application/json;charset=utf-8");
- HashMap<String, Object> map = new HashMap<>(4);
- map.put("code", 438);
- map.put("msg","你是黑名单");
-
- ObjectMapper objectMapper = new ObjectMapper();
- byte[] bytes = new byte[0];
- try {
- bytes = objectMapper.writeValueAsBytes(map);
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- DataBuffer wrap = response.bufferFactory().wrap(bytes);
- return response.writeWith(Mono.just(wrap));
- }
-
- @Override
- public int getOrder() {
- return 0;
- }
- }

限流就是限制一段时间内,用户访问资源的次数,减轻服务器压力,限流大致分为两种:
1、IP 限流(5s 内同一个 ip 访问超过 3 次,则限制不让访问,过一段时间才可继续访问)
2.、请求量限流(只要在一段时间内(窗口期),请求次数达到阀值,就直接拒绝后面来的访问了,
过一段时间才可以继续访问)(粒度可以细化到一个 api(url),一个服务)
限流模型:漏斗算法 ,令牌桶算法,窗口滑动算法 计数器算法

(1)所有的请求在处理之前都需要拿到一个可用的令牌才会被处理;
(2)根据限流大小,设置按照一定的速率往桶里添加令牌
(3)桶设置最大的放置令牌限制,当桶满时、新添加的令牌就被丢弃或者拒绝;
(4)请求达到后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理完业务逻辑之后,将令牌直接删除;
(5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令
牌,以此保证足够的限流
Spring Cloud Gateway 已经内置了一个 RequestRateLimiterGatewayFilterFactory,可以直接使用。
目前 RequestRateLimiterGatewayFilterFactory 的实现依赖于 Redis,所以我们还要引入 spring-boot-starter-data-redis-reactive 依赖
- <!--限流要引入 Redis-->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
- </dependency>
配置类
- import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.context.annotation.Primary;
- import reactor.core.publisher.Mono;
-
- /**
- * 自定义请求限流
- */
- @Configuration
- public class RequestRateLimiterConfig {
-
- // 针对某一个接口,ip来限流,如/doLogin,每一个ip,10s只能访问3次
- @Bean
- @Primary // 主候选的
- public KeyResolver ipKeyResolver() {
- return exchange -> Mono.just(exchange.getRequest().getHeaders().getHost().getHostString());
- }
-
- // 针对这个路径来限制 /doLogin
- // api 就是 接口 外面一般把gateway api网关 新一代网关
- @Bean
- public KeyResolver apiKeyResolver() {
- return exchange -> Mono.just(exchange.getRequest().getPath().value());
- }
- }
需要添加一个 @Primary // 主候选的 注解,不然报如下错误

启动快速访问
- @Configuration
- public class CorsConfig {
- @Bean
- public CorsWebFilter corsFilter() {
- CorsConfiguration config = new CorsConfiguration();
- config.addAllowedMethod("*");
- config.addAllowedOrigin("*");
- config.addAllowedHeader("*");
- UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
- source.registerCorsConfiguration("/**", config);
- return new CorsWebFilter(source);
- }
- }
- spring:
- cloud:
- gateway:
- globalcors:
- corsConfigurations: '[/**]': // 针对哪些路径
- allowCredentials: true // 这个是可以携带 cookie
- allowedHeaders: '*'
- allowedMethods: '*' allowedOrigins: '*'