Spring云网关版本:4.1.0
我们正在使用 Spring Cloud Gateway 框架开发 API 网关应用程序。 该应用程序有几个作为全局有序过滤器实现的前置过滤器和后置过滤器。 我们正在开发一个预过滤器作为全局过滤器,它将验证用户会话令牌,以在路由到目标端点之前对传入请求进行身份验证。 使用另一个微服务公开的 REST API 来验证用户会话令牌。 预过滤器的研制如下:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
final String sessionToken = exchange.getRequest().getHeader().getFirst("sessionToken");
final boolean isValid = restApiConnector.validateSessionToken(exchange, sessionToken) // REST API to validate token invoked using WebClient
.subscribeOn(Schedulers.boundedElastic())
.publishOn(Scheduleers.boundedElastic())
.toFuture().get(); // blocking call
exchanges.getAttributes().put("sessionStatus", isValid);
return chain.filter(exchange);
}
restApiConnector 是使用 Spring WebClient 调用 REST API 的连接器类。 validateSessionToken 是通过使用 Spring WebClient 调用验证会话 REST API 来验证提供的令牌的方法。
这里故意对 REST API 的调用进行阻塞(同步),因为所有后续预过滤器都依赖于此会话验证预过滤器的结果。此外,仅当此预过滤器发现会话令牌有效时,请求才会被路由到下游端点。因此,预过滤器必须在任何后续预过滤器和下游端点以及任何后过滤器之前执行。 subscribeOn 和 publishOn 的使用基于以下建议:长期 I/O 或网络调用必须在不同的线程中执行,以避免阻塞 Netty 主线程。
当请求发送到 API 网关并且路由发生在会话验证后时,上述代码可以正常工作。如果在上次请求后一分钟内发送了多个请求,则应用程序可以正常工作。 但是,如果请求是在上次请求一分钟后(空闲时间)发送的,则请求处理会在 REST API 调用时挂起,并且不会将任何请求发送到会话验证服务器。 API 网关应用程序此时能够接受后续传入请求,但所有后续请求都显示相同的挂起行为。 请指导在此类场景中同步调用网络 REST API 调用的最佳实践。
我们已经在互联网上阅读了多篇文章,其中大部分提到了对发布者使用
block()调用,但是它总是会导致每个请求出现异常,因此使用 toFuture().get() 。 此处不能使用 subscribe(),因为它会使 REST API 调用异步,并且在执行预过滤器和后过滤器之后可能会调用 REST API,这不是请求处理的预期流程。
block
或
subscribe
,因为此方法 (filter
) 已经是反应流的一部分并返回 Mono
。应该是这样的:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
final String sessionToken = exchange.getRequest().getHeader().getFirst("sessionToken");
restApiConnector.validateSessionToken(exchange, sessionToken)
.doOnNext { isValid ->
exchanges.getAttributes().put("sessionStatus", isValid)
}
.then(chain.filter(exchange))
}