spring cloud可以设置请求超时和响应超时时间,一般我们都是在yml文件中直接设置的如下:

//请求连接时间
spring.cloud.gateway.httpclient.connect-timeout=60
//请求响应时间
spring.cloud.gateway.httpclient.response-timeout=60

工作中遇到一个新需求,动态的给每个请求设置超时时间,spring cloud底层是用的HttpClient请求的,源代码只看到了响应时间处理,我这里用的响应超时时间,超过时间提示用户504 网关超时。

源代码gateway过滤器经过的最后一个过滤器NettyRoutingFilter实现GlobalFilter, Ordered

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.gateway.filter;

import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.config.HttpClientProperties;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter;
import org.springframework.cloud.gateway.filter.headers.HttpHeadersFilter.Type;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.cloud.gateway.support.TimeoutException;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.NettyDataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.AbstractServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.netty.NettyPipeline.SendOptions;
import reactor.netty.http.client.HttpClient;
import reactor.netty.http.client.HttpClientResponse;
import reactor.netty.http.client.HttpClient.RequestSender;

public class NettyRoutingFilter implements GlobalFilter, Ordered {
    private final HttpClient httpClient;
    private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
    //超时时间设置类
    private final HttpClientProperties properties;

    public NettyRoutingFilter(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFilters, HttpClientProperties properties) {
        this.httpClient = httpClient;
        this.headersFilters = headersFilters;
        this.properties = properties;
    }

    public int getOrder() {
        return 2147483647;
    }

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        URI requestUrl = (URI)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
        String scheme = requestUrl.getScheme();
        if (!ServerWebExchangeUtils.isAlreadyRouted(exchange) && ("http".equals(scheme) || "https".equals(scheme))) {
            ServerWebExchangeUtils.setAlreadyRouted(exchange);
            ServerHttpRequest request = exchange.getRequest();
            HttpMethod method = HttpMethod.valueOf(request.getMethodValue());
            String url = requestUrl.toString();
            HttpHeaders filtered = HttpHeadersFilter.filterRequest((List)this.headersFilters.getIfAvailable(), exchange);
            DefaultHttpHeaders httpHeaders = new DefaultHttpHeaders();
            filtered.forEach(httpHeaders::set);
            String transferEncoding = request.getHeaders().getFirst("Transfer-Encoding");
            boolean chunkedTransfer = "chunked".equalsIgnoreCase(transferEncoding);
            boolean preserveHost = (Boolean)exchange.getAttributeOrDefault(ServerWebExchangeUtils.PRESERVE_HOST_HEADER_ATTRIBUTE, false);
            Flux<HttpClientResponse> responseFlux = ((RequestSender)this.httpClient.chunkedTransfer(chunkedTransfer).request(method).uri(url)).send((req, nettyOutbound) -> {
                req.headers(httpHeaders);
                if (preserveHost) {
                    String host = request.getHeaders().getFirst("Host");
                    req.header("Host", host);
                }

                return nettyOutbound.options(SendOptions::flushOnEach).send(request.getBody().map((dataBuffer) -> {
                    return ((NettyDataBuffer)dataBuffer).getNativeBuffer();
                }));
            }).responseConnection((res, connection) -> {
                ServerHttpResponse response = exchange.getResponse();
                HttpHeaders headers = new HttpHeaders();
                res.responseHeaders().forEach((entry) -> {
                    headers.add((String)entry.getKey(), (String)entry.getValue());
                });
                String contentTypeValue = headers.getFirst("Content-Type");
                if (StringUtils.hasLength(contentTypeValue)) {
                    exchange.getAttributes().put("original_response_content_type", contentTypeValue);
                }

                HttpStatus status = HttpStatus.resolve(res.status().code());
                if (status != null) {
                    response.setStatusCode(status);
                } else {
                    if (!(response instanceof AbstractServerHttpResponse)) {
                        throw new IllegalStateException("Unable to set status code on response: " + res.status().code() + ", " + response.getClass());
                    }

                    ((AbstractServerHttpResponse)response).setStatusCodeValue(res.status().code());
                }

                HttpHeaders filteredResponseHeaders = HttpHeadersFilter.filter((List)this.headersFilters.getIfAvailable(), headers, exchange, Type.RESPONSE);
                if (!filteredResponseHeaders.containsKey("Transfer-Encoding") && filteredResponseHeaders.containsKey("Content-Length")) {
                    response.getHeaders().remove("Transfer-Encoding");
                }

                exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_HEADER_NAMES, filteredResponseHeaders.keySet());
                response.getHeaders().putAll(filteredResponseHeaders);
                exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR, res);
                exchange.getAttributes().put(ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR, connection);
                return Mono.just(res);
            });
            //响应超时时间处理
            if (this.properties.getResponseTimeout() != null) {
                responseFlux = responseFlux.timeout(this.properties.getResponseTimeout(), Mono.error(new TimeoutException("Response took longer than timeout: " + this.properties.getResponseTimeout()))).onErrorMap(TimeoutException.class, (th) -> {
                    return new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, (String)null, th);
                });
            }

            return responseFlux.then(chain.filter(exchange));
        } else {
            return chain.filter(exchange);
        }
    }
}

动态的处理每个路由请求,重写超时时间设置类,重写在自定义过滤器继承AbstractGatewayFilterFactory<MyRewritePathGatewayFilterFactory.Config>过滤器上,

    private final reactor.netty.http.client.HttpClient httpClient;
	private final ObjectProvider<List<HttpHeadersFilter>> headersFilters;
	private final HttpClientProperties properties;

	public 自定义过滤器名(HttpClient httpClient, ObjectProvider<List<HttpHeadersFilter>> headersFilters, HttpClientProperties properties) {
		super(Config.class);
		this.httpClient = httpClient;
		this.headersFilters = headersFilters;
		this.properties = properties;
	}

类中直接设置请求时间和响应时间

this.properties.setConnectTimeout(1);
this.properties.setResponseTimeout(Duration.ofMillis(600));

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐