我有一个非常简单的带有过滤器的 spring-cloud-gateway (它扩展了AbstractGatewayFilterFactory
(。
在过滤器内部,我正在使用 Spring 的RestTemplate
进行 REST API 调用。其余调用工作一次,但来自筛选器的每个后续调用都会挂起,并且响应永远不会返回到客户端。 我启用了日志记录进行跟踪,但是当 REST 调用挂起时,日志中没有任何内容。
我从这里的一条评论中了解到@spencergibb,任何阻塞 SCG 主线程的东西都从根本上被破坏了。但是在我将请求转发到下游服务之前,我需要确实阻止此 API 调用。
以下是我对过滤器的实现(修剪(:
@Component
public class ApiRequestHeaderFilter extends AbstractGatewayFilterFactory<ApiRequestHeaderFilter.Config> {
private static RestTemplate restTemplate = new RestTemplate();
public ApiRequestHeaderFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
String someHeaderValue = Objects.requireNonNull(request.getHeaders().get("SOME_HEADER")).get(0);
callRestApi();
return chain.filter(exchange);
};
}
private void callRestApi() {
UriComponentsBuilder uriBuilder = .... //build the API URL
final ResponseEntity<List<MyCustomObject>> response = restTemplate.exchange(uriBuilder.toUriString(), HttpMethod.GET, null, new ParameterizedTypeReference<List<MyCustomObject>>() {
});
.... //process the response
....
}
static class Config {
}
}
在使用 restTemplate 进行调用之前,不要读取(通过读取来记录(请求正文。 Spring Cloud Gateway 需要记录请求正文的内容,但请求正文只能读取一次。如果读取后未封装请求正文,则后一个服务将无法读取正文数据。遵循这个
我相信从GatewayFilter
调用另一个服务的推荐方法是请求链接。
例如,为了为每个过滤的请求调用谷歌:
@Override
public GatewayFilter apply(Config config) {
return (exchange, chain) -> WebClient.create("https://www.google.com")
.method(HttpMethod.GET)
.retrieve()
.bodyToMono(String.class)
.map(s -> {
System.out.println(s);
return exchange;
})
.flatMap(chain::filter);
}
在此示例中使用了WebClient
,它是与RestTemplate
不同的非阻塞客户端。
bodyToMono
方法生成Mono
,这些可以在所需操作后映射到exchange
,以便继续进行筛选。
欲了解更多信息,请参阅这个伟大的baeldung指南。