SpringCloud:使用SpringCloud Gateway过程中一些问题的说明

19 篇文章 0 订阅
10 篇文章 0 订阅

目录

一、SpringCloud Gateway如何单独使用而不集成注册中心

二、如何查看使用的路由规则(route)和过滤器(filter)

三、过滤器(filter)的执行顺序是怎样的

四、filter定制时pre阶段、post阶段各有几种实现方式

五、如何中止filter调用链

六、调用超时后是如何处理


一、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"
}

参考文档

Concurrency in Spring WebFlux

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值