目录
一、SpringCloud Gateway如何单独使用而不集成注册中心
二、如何查看使用的路由规则(route)和过滤器(filter)
四、filter定制时pre阶段、post阶段各有几种实现方式
一、SpringCloud Gateway如何单独使用而不集成注册中心
1.由于不需要引入注册中心,所以pom.xml只需要如下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.4.4</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.0.2</version>
</dependency>
2.java代码中使用@SpringBootApplication,不要使用@SpringCloudApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
import org.springframework.context.annotation.Configuration;
@Configuration
@SpringBootApplication
@EnableAutoConfiguration(exclude = { ErrorMvcAutoConfiguration.class })
public class SpringCloudGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayApplication.class, args);
}
}
如果使用@SpringCloudApplication,在低版本的SpringCloud中可能会报错
二、如何查看使用的路由规则(route)和过滤器(filter)
1.pom.xml中添加actuator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.4.4</version>
</dependency>
2.在application.properties文件中开放管理端口
management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=gateway
3.查看所有全局filter
/actuator/gateway/globalfilters
{
“org.springframework.cloud.gateway.filter.GatewayMetricsFilter@65b66b08”:0,
“org.springframework.cloud.gateway.filter.NettyRoutingFilter@27a97e08”:2147483647,
“org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@77e7246b”:-1,
“org.springframework.cloud.gateway.config.GatewayNoLoadBalancerClientAutoConfiguration$NoLoadBalancerClientFilter@4726927c”:10150,
“org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@5017e1”:2147483646,
“org.springframework.cloud.gateway.filter.ForwardPathFilter@1f44ddab”:0,
“org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@18eec010”:-2147482648,
“org.springframework.cloud.gateway.filter.ForwardRoutingFilter@51ce6f85”:2147483647,
“org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@5918c260”:-2147483648,
“org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@3d7b1f1c”:10000
}
4.查看所有可在route中使用的filter
/actuator/gateway/routefilters
{
“[SetResponseHeaderGatewayFilterFactory@7487b142 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]”:null,
“[PreserveHostHeaderGatewayFilterFactory@13da7ab0 configClass = Object]”:null,
“[RemoveRequestParameterGatewayFilterFactory@3724b43e configClass = AbstractGatewayFilterFactory.NameConfig]”:null,
“[RewriteLocationResponseHeaderGatewayFilterFactory@199bc830 configClass = RewriteLocationResponseHeaderGatewayFilterFactory.Config]”:null,
“[RemoveResponseHeaderGatewayFilterFactory@77eb5790 configClass = AbstractGatewayFilterFactory.NameConfig]”:null,
“[AddRequestParameterGatewayFilterFactory@602ae7b6 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]”:null,
“[ModifyResponseBodyGatewayFilterFactory@700f518a configClass = ModifyResponseBodyGatewayFilterFactory.Config]”:null,
“[ModifyRequestBodyGatewayFilterFactory@71ad3d8a configClass = ModifyRequestBodyGatewayFilterFactory.Config]”:null,
“[DedupeResponseHeaderGatewayFilterFactory@47af099e configClass = DedupeResponseHeaderGatewayFilterFactory.Config]”:null,
“[SetRequestHostHeaderGatewayFilterFactory@81b5db0 configClass = SetRequestHostHeaderGatewayFilterFactory.Config]”:null,
“[PrefixPathGatewayFilterFactory@b835727 configClass = PrefixPathGatewayFilterFactory.Config]”:null,
“[RewritePathGatewayFilterFactory@68e7c8c3 configClass = RewritePathGatewayFilterFactory.Config]”:null,
“[StripPrefixGatewayFilterFactory@3e17a0a1 configClass = StripPrefixGatewayFilterFactory.Config]”:null,
“[RewriteResponseHeaderGatewayFilterFactory@7139bd31 configClass = RewriteResponseHeaderGatewayFilterFactory.Config]”:null,
“[RequestHeaderSizeGatewayFilterFactory@150ede8b configClass = RequestHeaderSizeGatewayFilterFactory.Config]”:null,
“[RequestHeaderToRequestUriGatewayFilterFactory@790a251b configClass = AbstractGatewayFilterFactory.NameConfig]”:null,
“[MapRequestHeaderGatewayFilterFactory@3901f6af configClass = MapRequestHeaderGatewayFilterFactory.Config]”:null,
“[RemoveRequestHeaderGatewayFilterFactory@260ff5b7 configClass = AbstractGatewayFilterFactory.NameConfig]”:null,
“[AddResponseHeaderGatewayFilterFactory@10cd6753 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]”:null,
“[RetryGatewayFilterFactory@319c3a25 configClass = RetryGatewayFilterFactory.RetryConfig]”:null,
“[SecureHeadersGatewayFilterFactory@ef1695a configClass = SecureHeadersGatewayFilterFactory.Config]”:null,
“[SetRequestHeaderGatewayFilterFactory@58860997 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]”:null,
“[SaveSessionGatewayFilterFactory@27b45ea configClass = Object]”:null,
“[RequestSizeGatewayFilterFactory@4d8286c4 configClass = RequestSizeGatewayFilterFactory.RequestSizeConfig]”:null,
“[AddRequestHeaderGatewayFilterFactory@d8d9199 configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]”:null,
“[SetStatusGatewayFilterFactory@4b3fe06e configClass = SetStatusGatewayFilterFactory.Config]”:null,
“[RedirectToGatewayFilterFactory@2c8662ac configClass = RedirectToGatewayFilterFactory.Config]”:null,
“[SetPathGatewayFilterFactory@238bfd6c configClass = SetPathGatewayFilterFactory.Config]”:null
}
5.查看自定义的route规则,其中包括在这条路由规则中使用的filter
/actuator/gateway/routes
[{
“predicate”:”Paths: [/**], match trailing slash: true”,
“route_id”:”d98947ea-2593-4a80-898a-73028b6e1321″,
“filters”:[“com.tech.gw.CustomerFilter@70e44883”],
“uri”:”no://op”,
“order”:0
}]
规则最终使用的所有filter,是route规则中使用的filter+全局filter的合集
三、过滤器(filter)的执行顺序是怎样的
SpringCloud Gateway中的filter是使用order定义优先级的,值越小优先级越高,pre阶段执行越靠前,post阶段执行越靠后。
以下只列出了GlobalFilter的执行顺序:
规则如下:
1.全局filter都已定义了order的值,由order值决定优先级。如果order值相同,则先注册的优先级高
2.route规则中添加的filter需要指定order的值,如果不指定,则默认值为0。如果有多个filter的order值相同,则按它们在路由中的添加顺序来决定优先级,先添加的优先级高
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().route(p -> p.path("/**").filters(f -> f.filter(filter1).filter(filter2).filter(filter)).uri("no://op")).build();
}
优先级:filter1 > filter2 > filter
3.在执行过程中,是会将全局filter和route规则定义的filter一起按order排优先级的。如果全局filter与route规则定义的filter的order值一样,则全局filter优先级高
四、filter定制时pre阶段、post阶段各有几种实现方式
通过以下样例进行展示
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class GatewayFilter1 implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Mono<Void> mono1 = Mono.fromRunnable(() -> {
System.out.println("GatewayFilter1:before chain.filter in mono"); // 1
});
Mono<Void> mono2 = Mono.fromRunnable(() -> {
System.out.println("GatewayFilter1:after chain.filter in mono"); // 2
});
System.out.println("GatewayFilter1:before chain.filter"); // 3
Mono<Void> retMono = mono1.then(chain.filter(exchange)).then(mono2);
System.out.println("GatewayFilter1:after chain.filter"); // 4
return retMono;
}
}
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class GatewayFilter2 implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
Mono<Void> mono1 = Mono.fromRunnable(() -> {
System.out.println("GatewayFilter2:before chain.filter in mono"); // 1
});
Mono<Void> mono2 = Mono.fromRunnable(() -> {
System.out.println("GatewayFilter2:after chain.filter in mono"); // 2
});
System.out.println("GatewayFilter2:before chain.filter"); // 3
Mono<Void> retMono = mono1.then(chain.filter(exchange)).then(mono2);
System.out.println("GatewayFilter2:after chain.filter"); // 4
return retMono;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
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
@SpringBootApplication
@EnableAutoConfiguration(exclude = { ErrorMvcAutoConfiguration.class })
public class SpringCloudGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayApplication.class, args);
}
@Autowired
private GatewayFilter1 filter1;
@Autowired
private GatewayFilter2 filter2;
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().route(p -> p.path("/**").filters(f -> f.filter(filter1).filter(filter2)).uri("no://op"))
.build();
}
}
执行结果:
GatewayFilter1:before chain.filter
GatewayFilter1:after chain.filter
GatewayFilter1:before chain.filter in mono
GatewayFilter2:before chain.filter
GatewayFilter2:after chain.filter
GatewayFilter2:before chain.filter in mono
GatewayFilter2:after chain.filter in mono
GatewayFilter1:after chain.filter in mono
从结果中可以看出,在pre阶段执行可以在1、2、3处理添加代码;而在post阶段执行则可在4处添加代码。
再看一下更复杂的例子
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
public class GatewayFilter1 implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequestDecorator request = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
System.out.println(Thread.currentThread() + ":GatewayFilter1:in chain.request"); // 1
return super.getBody().map(dataBuffer -> {
System.out.println(Thread.currentThread() + ":GatewayFilter1:in chain.request map"); // 2
return dataBuffer;
});
}
};
ServerHttpResponseDecorator response = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
System.out.println(Thread.currentThread() + ":GatewayFilter1:in chain.response"); // 3
Flux<DataBuffer> bodyFlux = Flux.from(body);
bodyFlux = bodyFlux.map(dataBuffer -> {
System.out.println(Thread.currentThread() + ":GatewayFilter1:in chain.response map"); // 4
return dataBuffer;
});
return super.writeWith(bodyFlux);
}
@Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
System.out.println(Thread.currentThread() + ":ModifiedServerHttpResponse2");
return writeWith(Flux.from(body).flatMapSequential(p -> p));
}
};
Mono<Void> mono1 = Mono.fromRunnable(() -> {
System.out.println(Thread.currentThread() + ":GatewayFilter1:before chain.filter in mono"); // 5
});
Mono<Void> mono2 = Mono.fromRunnable(() -> {
System.out.println(Thread.currentThread() + ":GatewayFilter1:after chain.filter in mono"); // 6
});
System.out.println(Thread.currentThread() + ":GatewayFilter1:before chain.filter"); // 7
Mono<Void> retMono = mono1.then(chain.filter(exchange.mutate().request(request).response(response).build()))
.then(mono2);
System.out.println(Thread.currentThread() + ":GatewayFilter1:after chain.filter"); // 8
return retMono;
}
}
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@Component
public class GatewayFilter2 implements GatewayFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequestDecorator request = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
System.out.println(Thread.currentThread() + ":GatewayFilter2:in chain.request"); // 1
return super.getBody().map(dataBuffer -> {
System.out.println(Thread.currentThread() + ":GatewayFilter2:in chain.request map"); // 2
return dataBuffer;
});
}
};
ServerHttpResponseDecorator response = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
System.out.println(Thread.currentThread() + ":GatewayFilter2:in chain.response"); // 3
Flux<DataBuffer> bodyFlux = Flux.from(body);
bodyFlux = bodyFlux.map(dataBuffer -> {
System.out.println(Thread.currentThread() + ":GatewayFilter2:in chain.response map"); // 4
return dataBuffer;
});
return super.writeWith(bodyFlux);
}
@Override
public Mono<Void> writeAndFlushWith(Publisher<? extends Publisher<? extends DataBuffer>> body) {
return writeWith(Flux.from(body).flatMapSequential(p -> p));
}
};
Mono<Void> mono1 = Mono.fromRunnable(() -> {
System.out.println(Thread.currentThread() + ":GatewayFilter2:before chain.filter in mono"); // 5
});
Mono<Void> mono2 = Mono.fromRunnable(() -> {
System.out.println(Thread.currentThread() + ":GatewayFilter2:after chain.filter in mono"); // 6
});
System.out.println(Thread.currentThread() + ":GatewayFilter2:before chain.filter"); // 7
Mono<Void> retMono = mono1.then(chain.filter(exchange.mutate().request(request).response(response).build()))
.then(mono2);
System.out.println(Thread.currentThread() + ":GatewayFilter2:after chain.filter"); // 8
return retMono;
}
}
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class CustomFilter implements GatewayFilter, Ordered {
@Override
public int getOrder() {
return RouteToRequestUrlFilter.ROUTE_TO_URL_FILTER_ORDER + 1;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
try {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, new URI("http://127.0.0.1:9090/TestWebApp/testServlet"));
} catch (URISyntaxException e) {
e.printStackTrace();
}
Mono<Void> mono1 = Mono
.fromRunnable(() -> System.out.println(Thread.currentThread() + ":CustomerFilter:before"));
Mono<Void> mono2 = Mono
.fromRunnable(() -> System.out.println(Thread.currentThread() + ":CustomerFilter:after"));
Mono<Void> ret = mono1.then(chain.filter(exchange)).then(mono2);
return ret;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration;
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
@SpringBootApplication
@EnableAutoConfiguration(exclude = { ErrorMvcAutoConfiguration.class })
public class SpringCloudGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudGatewayApplication.class, args);
}
@Autowired
private GatewayFilter1 filter1;
@Autowired
private GatewayFilter2 filter2;
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().route(p -> p.path("/**")
.filters(f -> f.filter(filter1, -2).filter(filter2, -2).filter(filter)).uri("no://op")).build();
}
@Autowired
private CustomFilter filter;
}
这里要注意,order值要小于-1,因为只有小于-1(即比NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER小),writeWith方法才会被调用。
运行结果:
Thread[reactor-http-nio-2,5,main]:GatewayFilter1:before chain.filter
Thread[reactor-http-nio-2,5,main]:GatewayFilter1:after chain.filter
Thread[reactor-http-nio-2,5,main]:GatewayFilter1:before chain.filter in mono
Thread[reactor-http-nio-2,5,main]:GatewayFilter2:before chain.filter
Thread[reactor-http-nio-2,5,main]:GatewayFilter2:after chain.filter
Thread[reactor-http-nio-2,5,main]:GatewayFilter2:before chain.filter in mono
Thread[reactor-http-nio-2,5,main]:CustomerFilter:before
Thread[reactor-http-nio-4,5,main]:GatewayFilter2:in chain.request
Thread[reactor-http-nio-4,5,main]:GatewayFilter1:in chain.request
Thread[reactor-http-nio-2,5,main]:GatewayFilter1:in chain.request map
Thread[reactor-http-nio-2,5,main]:GatewayFilter2:in chain.request map
Thread[reactor-http-nio-4,5,main]:CustomerFilter:after
Thread[reactor-http-nio-4,5,main]:GatewayFilter2:in chain.response
Thread[reactor-http-nio-4,5,main]:GatewayFilter1:in chain.response
Thread[reactor-http-nio-4,5,main]:GatewayFilter2:in chain.response map
Thread[reactor-http-nio-4,5,main]:GatewayFilter1:in chain.response map
Thread[reactor-http-nio-2,5,main]:GatewayFilter2:after chain.filter in mono
Thread[reactor-http-nio-2,5,main]:GatewayFilter1:after chain.filter in mono
可以看到执行顺序为:7 > 8 > 5 > 1 > 2 > 3 > 4 > 6
五、如何中止filter调用链
将ServerHttpResponse设置成complete就可以中止filter调用链
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return response.setComplete();
六、调用超时后是如何处理
超时可以通过如下方式配置
spring.cloud.gateway.httpclient.connect-timeout=10000
spring.cloud.gateway.httpclient.response-timeout=5s
如果调用外部服务后出现超时,gateway会返回类似如下报文
{
“timestamp”: “2021-04-05T09:58:39.460+00:00”,
“path”: “/test”,
“status”: 504,
“error”: “Gateway Timeout”,
“message”: “”,
“requestId”: “5cee82b7-2”
}
参考文档