一.Spring Cloud Gataway
在Spring Cloud官方定义了SpringCloud Gateway 的如下特点:
- 基于 Spring 5,Project Reactor , Spring Boot 2.0
- 默认集成 Hystrix 断路器
- 默认集成 Spring Cloud DiscoveryClient
- Predicates 和 Filters 作用于特定路由,易于编写的 Predicates 和 Filters
- 支持动态路由、限流、路径重写.
二.Spring Cloud Gataway的核心概念
Spring Cloud Gateway的Filter和Zuul的过滤器类似
,可以在请求发出前后进行一些业务上的处理 ,这里分为`两种类型的Filter,分别是Gateway Filter网关filter和Global Filter全局Filter.
网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。说白了就是把url请求路由到对应的资源(服务),或者说是一个请求过来Gateway应该怎么把这个请求转发给下游的微服务,转发给谁。
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容
,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。简单理解就是处理HTTP请求的匹配规则,在什么样的请情况下才能命中资源继续访问。
三.Spring Cloud Gateway的工作方式
Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序通过特定于请求的过滤器链来运行请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前和之后运行逻辑。所有“前置”过滤器逻辑均被执行。然后发出代理请求。发出代理请求后,将运行“后”过滤器逻辑。
四.服务示例
创建项目导入依赖
1 2 3 4 5 6 7 8 9
| <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
|
配置类
1 2 3 4 5 6 7
| @SpringBootApplication @EnableDiscoveryClient public class GatewayServerApp { public static void main(String[] args) { SpringApplication.run(GatewayServerApp.class); } }
|
yml配置
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 35 36 37
| server: port: 9099 eureka: client: service-url: defaultZone: http://localhost:10086/eureka instance: prefer-ip-address: true instance-id: gateway-server:${server.port} spring: application: name: gateway-server cloud: gateway: discovery: locator: enabled: false lower-case-service-id: true routes: - id: application-user uri: lb://user-server predicates: - Path=/servers/user/** filters: - StripPrefix=1 - id: application-order uri: lb://order-server predicates: - Path=/servers/order/** filters: - StripPrefix=1 - id: application-pay uri: lb://pay-server predicates: - Path=/servers/pay/** filters: - StripPrefix=1
|
五.Gateway 的 Filter 过滤器
Gateway的Filter的zuul的Filter有相似之处,与zuul不同的是,Gateway的filter从生命周期上可以为“pre”和“post”类型。根据作用范围可分为针对于单个路由的gateway filter,和针对于所有路由的Global Filer
。
内置的Gateway filter
针对单个路由的Filter
, 它允许以某种方式修改HTTP请求或HTTP响应。过滤器可以作用在某些特定的请求路径上。Gateway内置了很多的GatewayFilter工厂。如果要使用这些Filter只需要在配置文件配置GatewayFilter Factory的名称。下面拿一个内置的Gateway Filter举例:
AddRequestHeader GatewayFilter Factory
该Filter是Gateway内置的,它的作用是在请求头加上指定的属性。配置如下:
1 2 3 4 5 6 7 8
| spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org filters: - AddRequestHeader=X-Request-red, blue
|
在spring.cloud.gateway.routes.filters
配置项配置了一个AddRequestHeader
,他是“AddRequestHeader GatewayFilter Factory
”的名称,意思是在请求头中添加一个“X-Request-red
”的属性,值为blue
。
自定义Gateway Filter
在Spring Cloud Gateway自定义过滤器,过滤器需要实现GatewayFilter和Ordered这两个接口。我们下面来演示自定义filter计算请求的耗时。
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
| public class RequestTimeFilter implements GatewayFilter, Ordered { private static final Log log = LogFactory.getLog(GatewayFilter.class); private static final String COUNT_Start_TIME = "countStartTime";
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { exchange.getAttributes().put(COUNT_Start_TIME, System.currentTimeMillis()); return chain.filter(exchange).then( Mono.fromRunnable(() -> { Long startTime = exchange.getAttribute(COUNT_Start_TIME); Long endTime=(System.currentTimeMillis() - startTime); if (startTime != null) { log.info(exchange.getRequest().getURI().getRawPath() + ": " + endTime + "ms"); } }) ); }
@Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } }
|
提示: getOrder返回filter的优先级,越大的值优先级越低 , 在filterI方法中计算了请求的开始时间和结束时间
最后我们还需要把该Filter配置在对应的路由上,配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @Configuration public class FilterConfig {
@Bean public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r.path("/servers/user/**") .filters(f -> f.stripPrefix(2) .filter(new RequestTimeFilter()) .addResponseHeader("X-Response-test", "test")) .uri("lb://user-server") .order(0) .id("test-RequestTimeFilter") ).build(); } } }
|
提示:这里将 RequestTimeFilter 添加到 “/user/**”这里路由上,当请求包含/user就会触发Filter的执行。
自定义GlobalFilter
GlobalFilter:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务的请求地址的核心过滤器,不需要配置,系统初始化时加载,并作用在每个路由上。
这里我们模拟了一个登陆检查的Filter.
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 35 36 37 38 39 40 41 42 43 44
| @Component @Slf4j public class TimeGlobleFilter implements GlobalFilter , Ordered {
@Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
List<String> token = exchange.getRequest().getHeaders().get("token");
log.info("检查 TOKEN = {}" ,token); if(token == null || token.isEmpty()){ ServerHttpResponse response = exchange.getResponse(); HashMap<String,Object> data = new HashMap<>(); data.put("code",401); data.put("message","未登录");
DataBuffer buffer = null; try { byte[] bytes = JSON.toJSONString(data).getBytes("utf-8"); buffer = response.bufferFactory().wrap(bytes);
response.setStatusCode(HttpStatus.UNAUTHORIZED); response.getHeaders().add("Content-Type","application/json;charset=UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); }
return response.writeWith(Mono.just(buffer)); }
log.info("Token不为空 ,放行"); return chain.filter(exchange); }
@Override public int getOrder() { return 0; } }
|
如果请求头中没有 token ,就返回咩有权限的状态吗。
使用 buffer = response.bufferFactory().wrap(bytes)
构建响应内容,通过response.writeWith(Mono.just(buffer));
把内容写给客户端。
Gateway跨域配置
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 35 36 37 38 39
| server: port: 10010 spring: application: name: gateway cloud: nacos: server-addr: localhost:8848 gateway: routes: - id: user-service uri: lb://userservice predicates: - Path=/user/**
- id: order-service uri: lb://orderservice predicates: - Path=/order/** - Before=2027-01-20T17:42:47.789-07:00[Asia/Shanghai] default-filters: - AddRequestHeader=Truth, coderyech is freaking awesome globalcors: add-to-simple-url-handler-mapping: true corsConfigurations: '[/**]': allowedOrigins: - "http://localhost:8090" - "http://www.leyou.com" allowedMethods: - "GET" - "POST" - "DELETE" - "PUT" - "OPTIONS" allowedHeaders: "*" allowCredentials: true maxAge: 360000
|