API 网关 —— Gateway简单认识

核心概念

  • Spring Cloud Gateway旨在为微服务架构提供简单、有效和统一的API路由管理方式
  • 供统一的路由方式,并且还基于Filter链的方式提供了网关基本的功能,例如:安全、监控/埋点、限流等。
  • 前端请求都请求到该微服务中,该微服务再负责转发到别的服务中去
  • 和 Zuul 区别:
    • Zuul:拦截单个url规则、默认去除拦截前缀、重复的url规则后面的会将前面覆盖
    • Gateway:拦截多个url规则、默认不去除拦截前缀、重复url规则前面的会将后面覆盖
  • 规则:
    • 断言的条件必须全部满足,才会路由转发
    • 所有的断言规则,必须全部为真,才能够进行请求转发
    • 只要断言为假,就会返回404状态码

简单用法

导入依赖

<!--网关的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--网关的负载均衡的起步依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

配置application.yml

spring:
cloud:
# Gateway网关配置信息
gateway:
# 网关路由配置,包含断言、过滤器、请求转发
routes:
# 路由的唯一标识
- id: toBaidu
# 断言规则
predicates:
# 校验路径拦截规则,当出现相同的拦截路径时,前面的会将后面覆盖
- Path: /
uri: https://www.baidu.com # 请求转发的url地址
- id: toGoogle
predicates:
# 定义多个路径的拦截规则
- Path: /, /guonei, /guoji
uri: https://www.google.com
- id: toConsumer
predicates:
# 拦截规则,可以通过通配符,进行加载
# 默认不去除拦截前缀:http://localhost:5002/consumer/02 转发还是 http://localhost:8002/consumer/02
- Path: /consumer/**
uri: http://localhost:8002

更多应用

实现负载均衡

spring:
cloud:
gateway:
routes:
- id: toConsumer
predicates:
- Path: /consumer/**
# 负载均衡访问规则
# lb 即 load balanced
# lb://ip+端口
# lb://微服务名称
# uri 后面就是与当前服务构成负载均衡的服务
uri: lb://eureka-client-consumer # uri: http://localhost:8002

更多断言规则

spring:
cloud:
gateway:
routes:
- id: toConsumer
# 全部断言规则为真,才可以进行路由访问操作
# 如果断言规则为假,会范围404页面
predicates:
- Path=/consumer/**
# 以下为常见断言规则:
# 指定时间之后才允许访问
- After=2021-12-23T14:44:47.789-07:00[Asia/Shanghai]
# 指定时间之前才允许访问
- Before=2021-12-23T14:51:47.789-07:00[Asia/Shanghai]
# 指定时间之间才允许访问
- Between=2021-12-23T14:51:47.789-07:00[Asia/Shanghai], 2021-12-23T15:00:47.789-07:00[Asia/Shanghai]
# 指定Cookie才允许访问
- Cookie=abc, bcd
# 指定请求头才允许访问
- Header=X-Request-Id, abc
# 指定请求方式才允许访问
- Method=GET, POST
# 指定请求参数才允许访问(只能以地址栏的键值对方式进行校验)
- Query=aaa, bbb
uri: http://localhost:8002

实现过滤器

spring:
cloud:
gateway:
routes:
- id: toConsumer
predicates:
- Path=/consumer/**, /api/**
uri: lb://eureka-client-consumer
# 过滤器工厂(给请求添加以下数据)
filters:
# 给请求添加请求头,后面 blue 是值
- AddRequestHeader=X-Request-Header, blue
# 给请求添加请求参数
- AddRequestParameter=key1, value1
# 给请求添加响应头(可在浏览器中进行查看)
- AddResponseHeader=ccc, ddd
# 给请求添加前缀访问路径
- PrefixPath=/api
# 去除访问前缀的过滤器,1代表去除1级路径,2代表去除两级路径
# http://localhost:5002/api/consumer/02 -> http://localhost:5002/consumer/02
- StripPrefix=1
# 请求路径重写
# http://localhost:5002/api/abc/02 -> http://localhost:5002/consumer/02
- RewritePath=/api/abc/?(?<segment>.*), /consumer/$\{segment}
# 给请求设置响应码
- SetStatus=401
# 需先引入redis
# 通过网关和Redis结合,根据令牌桶算法,对请求进行限流操作,只有在令牌桶获取到令牌的请求才允许访问
# 当请求过多,被限流时,返回的是429的响应码
- name: RequestRateLimiter
args:
# 当令牌生成时,获取的请求会被存储到Redis中,当请求执行完成,Redis会清理掉之前存入的数据
# 每秒钟令牌生成速率
redis-rate-limiter.replenishRate: 1
# 令牌桶的容量
redis-rate-limiter.burstCapacity: 2
# 每次请求获取的令牌数,默认就是1个
redis-rate-limiter.requestedTokens: 1
# xxx 为注入容器的一个对象(只能有一个),该对象可设置过滤器的条件
key-resolver: "#{@xxx}"

以上Redis网关限流需先注入 key-resolver 对象

//yaml中的 key-resolver 修改为 #{@userKeyResolver}
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user")); //请求中的参数必须包含key为 “user”
}

实现自定义全局过滤器

@Slf4j
@Configuration
public class CustomFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("过滤器执行了...");
//最简单的权限拦截操作
String token = exchange.getRequest().getQueryParams().getFirst("token");
if(token == null){
return errorInfo(exchange,1,"Token校验失败...");
}
//放行操作,访问后续的过滤器或访问请求转发到网关操作
return chain.filter(exchange);
}
/**
* 过滤器的优先级
* 数字越小,优先级越高
* int HIGHEST_PRECEDENCE = -2147483648;
* int LOWEST_PRECEDENCE = 2147483647;
* @return
*/
@Override
public int getOrder() {
return 0;
}
public Mono<Void> errorInfo(ServerWebExchange exchange, Integer code, String message) {
//封装返回值结果集
Map<String, Object> errorMap = new HashMap<>();
errorMap.put("code", code);
errorMap.put("message", message);
errorMap.put("data", null);
return Mono.defer(() -> {
byte[] bytes = null;
try {
//结果集转换为字节数组
bytes = new ObjectMapper().writeValueAsBytes(errorMap);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
//获取 response 对象
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().add("Content-Type", MediaType.APPLICATION_PROBLEM_JSON_UTF8.toString());
DataBuffer wrap = response.bufferFactory().wrap(bytes);
return response.writeWith(Flux.just(wrap));
});
}
}

扩展:SpringSession的校验

同一台机器不同微服务所使用的session空间不相同,无法共享数据,此时我们可以使用 SpringSession 来解决

示意图如下:
在这里插入图片描述
在这里插入图片描述
用法:
引入依赖

<!--Redis起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--SpringSession起步依赖-->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

启动类加上注解

//开启SpringSession操作
@EnableRedisHttpSession
public class Application {

此时存取session的数据都是从同一个redis中存取

posted @   Acegzx  阅读(166)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· DeepSeek “源神”启动!「GitHub 热点速览」
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 我与微信审核的“相爱相杀”看个人小程序副业
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~
点击右上角即可分享
微信分享提示