GateWay
基于 WebFlux 框架实现,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty(非阻塞异步)。提供统一的路由方式且基于 Filter 链的方式提供了网关基本的功能。
![]()
三大核心
- Route(路由):路由是构建网关的基本模块,它由 ID、目标 URI、一系列的断言和过滤器组成。
- Predicate(断言):开发人员可以匹配 HTTP 请求中的所有内容,如果请求与断言相匹配则进行路由。
- Filter(过滤):可以在请求被路由之前或者之后对请求进行修改。
工作流程
![]()
客户端向 SpringCloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 GatewayWeb Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。
过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前或之后执行业务逻辑。过滤之前可以做参数校验、权限校验、流量监控、日志输出和协议转换等,过滤之后可以做响应内容、响应头的修改,日志的输出和流量监控等。过滤器有着非常重要的作用。
核心逻辑:路由转发 + 执行过滤器链。
搭建
准备工作
创建一个 Module,一个空的 Maven 项目。并导入下面的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>ml.guest997</groupId> <artifactId>Commons</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
|
创建主启动类。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package ml.guest997;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication @EnableEurekaClient public class Gateway { public static void main(String[] args) { SpringApplication.run(Gateway.class, args); } }
|
编写配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| server: port: 9527
spring: application: name: gateway cloud: gateway: routes: - id: payment_routh uri: http://localhost:8001 predicates: - Path=/payment/get/** - id: payment_routh2 uri: http://localhost:8001 predicates: - Path=/payment/lb
eureka: instance: hostname: gateway client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
|
测试
分别启动 Eureka-Server、Payment 和 Gateway 模块后,浏览器访问:127.0.0.1:8001/payment/get/1,能够正常调用。再访问:127.0.0.1:9527/payment/get/1,发现也是能够正常调用的。(/payment/lb 路径同理)
Config 路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package ml.guest997.config;
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 GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("path_route_blog", r -> r.path("/articles").uri("https://guest997.ml/articles")).build(); return routes.build(); } }
|
测试
分别启动 Eureka-Server 和 Gateway 模块后,浏览器访问:127.0.0.1:9527/articles,会发现请求被转发到了我的博客地址。
动态路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| spring: application: name: gateway cloud: gateway: discovery: locator: enabled: true routes: - id: payment_routh
uri: lb://payment predicates: - Path=/payment/get/** - id: payment_routh2
uri: lb://payment predicates: - Path=/payment/lb
|
测试
分别启动 Eureka-Server、Payment、Payment2 和 Gateway 模块后,浏览器多次访问:127.0.0.1:9527/payment/lb,会发现端口是互相切换的。
常用断言
- The After Route Predicate Factory
- The Before Route Predicate Factory
- The Between Route Predicate Factory
- The Cookie Route Predicate Factory
- The Header Route Predicate Factory
- The Host Route Predicate Factory
- The Method Route Predicate Factory
- The Path Route Predicate Factory
- The Query Route Predicate Factory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| predicates: #能够使用正则表达式
- After=2022-03-24T14:56:33.082+08:00[Asia/Shanghai] #ZonedDateTime 类的地区时间显示 - Before=2022-03-23T14:56:33.082+08:00[Asia/Shanghai] - Between=2022-03-23T14:56:33.082+08:00[Asia/Shanghai], 2022-03-24T14:56:33.082+08:00[Asia/Shanghai]
- Cookie=username, guest997
- Header=X-Request-Id, \d+
- Host=**.guest997.ml, **.guest997.cf
- Method=GET
- Path=/articles/**
- Query=password, guest.
|
过滤
其本身具有几十种过滤器,有兴趣的可以自己去网上找一下,下面讲的是自定义的全局过滤器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| package ml.guest997.filter;
import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;
import java.util.Date;
@Component @Slf4j public class MyLogGateWayFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("进入过滤器的时间:" + new Date()); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if (uname == null) { log.info("用户名不存在"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); }
//表示当前过滤器的优先级 @Override public int getOrder() { return 0; } }
|
测试
分别启动 Eureka-Server、Payment、Payment2 和 Gateway 模块后,浏览器访问:127.0.0.1:9527/payment/lb,会发现访问失败,而访问:127.0.0.1:9527/payment/lb?uname=Guest997,就能正常访问。