什么是网关
- api网关是只微服务架构下的统一入口,提供服务内部的服务路由中转,为客户端提供统一的服务一些与业务无关的公共逻辑可以在这里实现,比如认证、授权、监控、路由转发等。
- 常用的网关组件有,zuul、nginx+lua、Kong等
GatWay
- 该项目提供了一个建立在 Spring 生态系统之上的 API 网关,包括:Spring 6、Spring Boot 3 和 Project Reactor。Spring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 API,并为其提供跨领域关注点,例如:安全性、监控/指标和弹性。
- Spring Cloud Gateway 有两种不同的版本:服务器和代理交换。每种风格都提供 WebFlux 和 MVC 兼容性。
- Server 变体是一个功能齐全的 API 网关,可以独立使用,也可以嵌入到 Spring Boot 应用程序中。
- Proxy Exchange 变体专门用于基于注释的 WebFlux 或 MVC 应用程序,并允许使用特殊 ProxyExchange 对象作为 Web 处理程序方法的参数。
工作原理
基本概念
- 路由是gateway中最基础的组件之一,表示一个具体的路由信息载体,主要包括以下几个参数:
- id:路由的唯一标识,
- uri:路由目的地地址,即最终被转发到的服务器
- order:用于路由之间排序,数值越小,排序越靠前,匹配优先级越高
- predicate:断言,它的作用是进行条件判断,当所有断言为true时,才会执行路由跳转
- filter:过滤器,用于修改请求和响应的信息
执行流程
- 客户端发送请求到gateway
- 请求首先被HttpWebHandlerAdapter进行提取组长成网关上下文
- 然后网关上下文会传递到DispatcherHandler,它负责将请求分发给RouterPredicateHandlerMapping
- RouterPredicateHandlerMapping负责路由查找,并根据路由断言判断是否可用
- 如果断言成功,由FilteringWebHandler创建过滤器链并调用
- 请求会经过:Prefilter-微服务-PostFilter的方法最终返回响应
案例
使用配置文件简单配置
gateway的pom.xml
<!--注册到nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--nacos拉取配置-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!--gateway-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
gateway的配置文件
spring:
application:
name: SMS-gateway
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: xx.xx.xxx.xxx:8848
config:
server-addr: xx.xx.xxx.xxx:8848
file-extension: yml
#gateway配置
gateway:
routes:
#第一个路由
- id: route1
uri: http://localhost:10001/sms-api
predicates:
- Path=/sms-api/**
#第二个路由
- id: route2
uri: http://localhost:10002/sms-cache
predicates:
- Path=/sms-cache/**
sms-api服务的配置文件
server:
port: 10001
servlet:
context-path: /sms-api/
注意:
gateway网关中路由的地址是https://localhost:10001/sms-api,sms-api服务中的项目上下文应该配置‘/sms-api/’,不然找不到路由
使用配置类配置路由
将配置文件中gateway配置用配置文件替换
@Configuration
public class GatewayConf {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
RouteLocatorBuilder.Builder routes = builder.routes();
routes.route("router1", r -> r.path("/sms-api/").uri("http://localhost:10001/sms-api"))
.route("router2", r -> r.path("/sms-cache/").uri("http://localhost:10002/cache"));
return routes.build();
}
}
自动路由,基于nacos负载均衡
spring:
application:
name: SMS-gateway
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: xx.xx.xxx.xxx:8848
config:
server-addr: xx.xx.xxx.xxx:8848
file-extension: yml
#gateway配置
gateway:
#开启nacos自动负载均衡
discovery:
locator:
enabled: true
注意:
- 如果不配置路由,gateway会根据服务名寻找相关服务,如http://localhost:10004/sms-api/test,则在被gateway路由的服务的服务中不必配置servlet.context-path属性
- 注册的服务名必须为小写,因为路由地址为小写
- 如果被gateway路由的服务中由多个服务,需要负载均衡,必须引入负载均衡的loadbalance依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
手动负载均衡
spring:
application:
name: SMS-gateway
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: xx.xx.xxx.xxx:8848
config:
server-addr: xx.xx.xxx.xxx:8848
file-extension: yml
gateway:
#手动负载均衡
routes:
- id: route1
#标识使用loadbalancer将sms-api/**的请求路由到服务名为SMS-api的服务上
uri: lb://SMS-api
predicates:
- Path=/sms-api/**
filters:
#剥离匹配的请求路径,即path中配置的sms-api部分
- StripPrefix=1
Predicates
spring:
application:
name: SMS-gateway
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: xx.xx.xxx.xxx:8848
config:
server-addr: xx.xx.xxx.xxx:8848
file-extension: yml
gateway:
routes:
- id: route1
uri: lb://SMS-api
predicates:
- Path=/sms-api/** # 1. 路径匹配进行路由
- After=2024-07-01T00:00:00.789-07:00 # 2. 在指定时间之后才能被路由
- Before=2024-07-01T00:00:00.789-07:00 # 3. 在指定时间之后才被路由
- Between=2024-07-01T00:00:00.789-07:00,2024-07-02T00:00:00.789-07:00 # 4. 在指定时间内才被路由
- Cookie=key,[a-z]+ # 5. 请求Cookie中携带key属性并且匹配后面正则表达式时候才被路由
- Header=key,/d+ # 6. 请求Header中携带key属性并且匹配后面正则表达式时候才被路由
- Host=**.shaoby.com # 7. 匹配当前主机发出的请求才被路由
- Method=GET,POST # 8. 匹配指定请求方式
- Query=id # 9. 匹配请求参数,如果需要匹配多个参数可以配置多个Query
# - Weight=group1,20 10. 配置路由的权重,按组分配,所以分配权重的路由group要相同
filters:
#剥离匹配的请求路径,即path中配置的sms-api部分
- StripPrefix=1
Filter
内置Filter
- 内置过滤器生命周期分为pre业务处理之前post业务处理之后
- 内置过滤器分为两种GateWayFilter(单一)、GlobalFilter(全局)
单一过滤器
- StripPrefix:指定数量,表示在将请求路由到下游之前要从请求中剥离的路径中部分数
- AddRequestHeader=X-Request-red, blue:将标头添加到 X-Request-red:blue 所有匹配请求的下游请求的标头中
- AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-2:green:此列表为所有匹配请求添加 2 个标头 X-Request-Color-1:blue 和 X-Request-Color-2:green 下游请求的标头。这与工作方式 AddRequestHeader 类似,但与它不同的 AddRequestHeader 是,只有在标头不存在时才会这样做。否则,将发送客户端请求中的原始值。此外,若要设置多值标头,请多次使用标头名称,例如 AddRequestHeadersIfNotPresent=X-Request-Color-1:blue,X-Request-Color-1:green 。 还支持用于匹配路径或主机的 URI 变量。
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/red/{segment}
filters:
- AddRequestHeadersIfNotPresent=X-Request-Red:Blue-{segment}
- AddRequestParameter=red, blue:在请求参数中添加red=blue,另外支持URI 变量可以在值中使用
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
predicates:
- Host: {segment}.myhost.org
filters:
- AddRequestParameter=foo, bar-{segment}
- AddResponseHeader=X-Response-Red, Blue:在响应头中添加值
- 等等,参考官网:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/gatewayfilter-factories.html
全局过滤器
- Combined Global Filter and GatewayFilter Ordering
- The Gateway Metrics Filter
- The Local Response Cache Filter
- Forward Routing Filter
- The Netty Routing Filter
- The Netty Write Response Filter
- RouteToRequestUrl Filter
- The Websocket Routing Filter
- Marking An Exchange As Routed
参考文档:https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway/global-filters.html#routetorequesturl-filter
自定义Filter
实现Ordered,GlobalFilter两个接口,重写filter,getOrder两个方法,在filter中可以实现在业务处理之前和之后做一些操作
package com.shaoby.gatway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @Author Cookie
* @Date 2024/7/1 23:19
*/
@Component
public class MyFilter implements Ordered, GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
/** 处理业务逻辑之前的操作*/
System.out.println(exchange.getRequest().getQueryParams().get("userName"));
System.out.println("处理业务逻辑之前的操作");
Mono<Void> filter = chain.filter(exchange);
/** 处理业务逻辑之后的操作*/
System.out.println("处理业务逻辑之后的操作");
System.out.println(exchange.getResponse().getStatusCode());
return filter;
}
/**
* 用于过滤器排序,越小,位置越往前
* @return
*/
@Override
public int getOrder() {
return 0;
}
}