SpringCloudGateWay耗时统计

https://blog.csdn.net/qq_18661793/article/details/124938063

先说背景:压测发现耗时很久,使用postman偶发性的耗时久,怀疑是网络不稳定,但是又拿不出证据。 为了验证到底是网络问题还是程序性能问题,所以就有了在网关层面进行耗时统计的需求。

思路:网关的功能有路由分发、请求发起等功能。

只要在网关的所有过滤器之前再放入1个过滤器TakeTimeLogBeginFilter,在TakeTimeLogBeginFilter中在request的attrbute里放入1个属性comsuming-start-time。 在网关的所有过滤器之后再放入1个过滤器TakeTimeLogEndFilter,取出第一个过滤器放入的comsuming-start-time。然后用当前时间减去comsuming-start-time即可。

都知道过滤器是有顺序的只要实现Ordered接口重写getOrder方法即可。

int HIGHEST_PRECEDENCE = -2147483648; int LOWEST_PRECEDENCE = 2147483647;

getOrder方法返回值越小过滤器的优先级越高。 getOrder方法返回值越大过滤器的优先级越低。

基于这种猜想

我们把TakeTimeLogBeginFilter的优先级改为HIGHEST_PRECEDENCE

把TakeTimeLogBeginFilter的优先级LOWEST_PRECEDENCE。

理想很丰满,现实很骨感。

what?

TakeTimeLogBeginFilter确实排在了第一位。

TakeTimeLogBeginFilter为什么跑到了最后一位?

看了下FilteringWebHandler#loadFilters的源码

private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) { return filters.stream().map(filter -> { GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter); if (filter instanceof Ordered) { int order = ((Ordered) filter).getOrder(); return new OrderedGatewayFilter(gatewayFilter, order); } return gatewayFilter; }).collect(Collectors.toList()); }

发现好像并没有地方可以对filter的排序进行扩展或者干预,后面仔细看了下还是有的!

```java import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.cloud.gateway.filter.*; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;

import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import java.util.Objects;

@Component @Slf4j public class TakeTimeLogBeginFilter implements GlobalFilter, Ordered {

/***
 * 放在request中的属性,用于记录调用filter链开始的时间。
 */
public static final String START_TIME = "consuming-time-start";

/****
 * FilteringWebHandler的静态内部类DefaultGatewayFilterChain的class全限定名
 */
public static final String DEFAULT_GATEWAY_FILTER_CHAIN_CLASS_NAME = "org.springframework.cloud.gateway.handler.FilteringWebHandler$DefaultGatewayFilterChain";

/***
 * FilteringWebHandler的静态内部类DefaultGatewayFilterChain的index字段
 */
public static final String FIELD_INDEX = "index";

/***
 * FilteringWebHandler的静态内部类DefaultGatewayFilterChain的filters字段
 */
public static final String METHOD_GET_FILTERS = "getFilters";

/****
 * FilteringWebHandler的静态内部类GatewayFilterAdapter的class全限定名
 */
public static final String GATEWAY_FILTER_ADAPTER_CLASS_NAME = "org.springframework.cloud.gateway.handler.FilteringWebHandler$GatewayFilterAdapter";

/***
 * FilteringWebHandler的静态内部类DefaultGatewayFilterChain的index字段
 */
public static final String FIELD_DELEGATE = "delegate";

public static Class<?> DEFAULT_GATEWAY_FILTER_CHAIN_CLASS = null;

public static Class<?> GATEWAY_FILTER_ADAPTER_CLASS = null;

static {
    //反射获取DefaultGatewayFilterChain
    try {
        DEFAULT_GATEWAY_FILTER_CHAIN_CLASS = Class.forName(DEFAULT_GATEWAY_FILTER_CHAIN_CLASS_NAME, true, TakeTimeLogBeginFilter.class.getClassLoader());
    } catch (Exception e) {
        log.info("反射加载DefaultGatewayFilterChainClass失败");
    }

    //反射获取GatewayFilterAdapter
    try {
        GATEWAY_FILTER_ADAPTER_CLASS = Class.forName(GATEWAY_FILTER_ADAPTER_CLASS_NAME, true, TakeTimeLogBeginFilter.class.getClassLoader());
    } catch (Exception e) {
        log.info("反射加载GatewayFilterAdapterClass失败");
    }
}


public TakeTimeLogBeginFilter() {
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    //反射加载类失败 直接放行
    if (Objects.isNull(GATEWAY_FILTER_ADAPTER_CLASS) || Objects.isNull(DEFAULT_GATEWAY_FILTER_CHAIN_CLASS)) {
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }

    //获取DefaultGatewayFilterChain的index字段的值
    int indexValue = 0;
    try {
        Field index = DEFAULT_GATEWAY_FILTER_CHAIN_CLASS.getDeclaredField(FIELD_INDEX);
        index.setAccessible(true);
        indexValue = index.getInt(chain);
    } catch (Exception e) {
        log.info("获取DefaultGatewayFilterChainClass的字段index失败");
        //失败直接放行
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }
    //用于存放DefaultGatewayFilterChain中的所有filter
    List<GatewayFilter> filters;
    try {
        //反射调用getFilters 获取DefaultGatewayFilterChain中的所有filter
        Method getFiltersMethod = DEFAULT_GATEWAY_FILTER_CHAIN_CLASS.getDeclaredMethod(METHOD_GET_FILTERS);
        getFiltersMethod.setAccessible(true);
        Object chainFilters = getFiltersMethod.invoke(chain);

        if (Objects.nonNull(chainFilters) && chainFilters instanceof List) {
            filters = (List<GatewayFilter>) chainFilters;
            //TakeTimeLogEndFilter 在filters中对应的下标
            int endFilterIndex = 0;
            //NettyRoutingFilter 在filters中对应的下标
            int nettyFilterIndex = 0;
            //ForwardRoutingFilter 在filters中对应的下标
            int forwardFilterIndex = 0;
            //获取适配器类GatewayFilterAdapter 为什么要获取?
            //因为FilteringWebHandler.loadFilters把GlobalFilter都用GatewayFilterAdapter做了一层适配,GatewayFilterAdapter的delegate才是真正的GlobalFilter。
            //并且做了1个判断 如果GlobalFilter instanceof Ordered 那么需要包装成 new OrderedGatewayFilter(gatewayFilterAdapter, order);
            //下面需要的判断是真正的GlobalFilter的类型 因此需要一层一层剥开

            Field adapterDeleteField = GATEWAY_FILTER_ADAPTER_CLASS.getDeclaredField(FIELD_DELEGATE);
            adapterDeleteField.setAccessible(true);

            if (CollectionUtils.isNotEmpty(filters)) {
                for (int i = 0; i < filters.size(); i++) {
                    GatewayFilter gatewayFilter = filters.get(i);
                    if (gatewayFilter instanceof OrderedGatewayFilter) {
                        OrderedGatewayFilter orderedGatewayFilter = (OrderedGatewayFilter) gatewayFilter;
                        GatewayFilter gatewayFilterAdapter = orderedGatewayFilter.getDelegate();
                        if (gatewayFilterAdapter.getClass() == GATEWAY_FILTER_ADAPTER_CLASS){
                            Object globalFilter = null;
                            try {
                                globalFilter = adapterDeleteField.get(gatewayFilterAdapter);
                            } catch (Exception e) {
                                log.info("反射获取globalFilter失败");
                                globalFilter = null;
                            }
                            //耗时统计结束过滤器
                            if (globalFilter instanceof TakeTimeLogEndFilter) {
                                endFilterIndex = i;
                            }
                            //路由分发与发起请求过滤器
                            if (globalFilter instanceof NettyRoutingFilter) {
                                nettyFilterIndex = i;
                            }
                            //转发过滤器
                            if (globalFilter instanceof ForwardRoutingFilter) {
                                forwardFilterIndex = i;
                            }
                        }
                    }
                }
                //调换顺序 将耗时统计的Filter放到 NettyRoutingFilter 和  ForwardRoutingFilter 之前
                GatewayFilter endFilter = filters.get(endFilterIndex);
                GatewayFilter nettyFilter = filters.get(nettyFilterIndex);
                GatewayFilter forwardFilter = filters.get(forwardFilterIndex);
                if (Objects.nonNull(endFilter)
                        && Objects.nonNull(nettyFilter)
                        && Objects.nonNull(forwardFilter)
                        && endFilterIndex != 0
                        && nettyFilterIndex != 0
                        && forwardFilterIndex != 0) {
                    filters.set(endFilterIndex, nettyFilter);
                    filters.set(nettyFilterIndex, forwardFilter);
                    filters.set(forwardFilterIndex, endFilter);
                }
            }
        }
    } catch (Exception e) {
        log.info("调整filter执行顺序失败!");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }
    //放入耗时统计的开始时间
    exchange.getAttributes().put(TakeTimeLogBeginFilter.START_TIME, System.currentTimeMillis());
    return chain.filter(exchange).then(Mono.fromRunnable(() -> {
    }));
}

@Override
public int getOrder() {
    return Ordered.HIGHEST_PRECEDENCE;
}

} ```

```java import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.CollectionUtils; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono;

import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.List; import java.util.Objects;

@Component @Slf4j public class TakeTimeLogEndFilter implements GlobalFilter, Ordered {

public TakeTimeLogEndFilter() {
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

    //反射加载类失败 直接放行
    if (Objects.isNull(TakeTimeLogBeginFilter.GATEWAY_FILTER_ADAPTER_CLASS) || Objects.isNull(TakeTimeLogBeginFilter.DEFAULT_GATEWAY_FILTER_CHAIN_CLASS)) {
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }

    int indexValue = 0;
    try {
        Field index = TakeTimeLogBeginFilter.DEFAULT_GATEWAY_FILTER_CHAIN_CLASS.getDeclaredField(TakeTimeLogBeginFilter.FIELD_INDEX);
        index.setAccessible(true);
        indexValue = index.getInt(chain);
    } catch (Exception e) {
        log.info("获取DefaultGatewayFilterChainClass的字段index失败");
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        }));
    }

    int finalIndexValue = indexValue;
    return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        List<GatewayFilter> filters;
        try {
            Method method = TakeTimeLogBeginFilter.DEFAULT_GATEWAY_FILTER_CHAIN_CLASS.getDeclaredMethod(TakeTimeLogBeginFilter.METHOD_GET_FILTERS);
            method.setAccessible(true);
            Object invoke = method.invoke(chain);
            Long startTime = exchange.getAttribute(TakeTimeLogBeginFilter.START_TIME);
            if (Objects.nonNull(invoke) && invoke instanceof List) {
                filters = (List<GatewayFilter>) invoke;
                if (CollectionUtils.isNotEmpty(filters)) {
                    if (startTime != null) {
                        Long executeTime = (System.currentTimeMillis() - startTime);
                        Integer rawStatusCode = null;
                        if (Objects.nonNull(exchange.getResponse())) {
                            rawStatusCode = exchange.getResponse().getRawStatusCode();
                        }
                        log.info("==================请求路径:{},过滤器数量:{},当前下标:{},累计耗时:{},响应编码信息:{}", exchange.getRequest().getURI().getRawPath(), filters.size(), finalIndexValue, executeTime, rawStatusCode);
                    }
                }
            }
        } catch (Exception e) {
            log.info("TakeTimeLogEndFilter#filter耗时统计失败");
        }
    }));
}

@Override
public int getOrder() {
    return Ordered.LOWEST_PRECEDENCE;
}

} ```

虽然这样也可以但是总感觉不优雅,又重新翻了一下GatewayAutoConfiguration的源码。

源码如下。

@Bean public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) { return new FilteringWebHandler(globalFilters); }

拍大腿啊!

我们只需要在注入FilteringWebHandler的时候,拿到globalFilters

globalFilters前后加入自定义统计耗时的filter即可。

具体点:从容器获取FilteringWebHandler获取globalFiltersglobalFilters前后加入自定义统计耗时的filter即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在进行Spring Cloud Gateway的性能调优时,可以考虑以下几个方面: 1. 版本升级:确保使用最新的Spring Boot、Spring Cloud和Spring Cloud Alibaba版本,因为新版本通常会修复一些性能问题并引入一些优化。 2. 配置调整:根据实际情况对Spring Cloud Gateway的相关配置进行调整。比如,可以通过调整`spring.cloud.gateway.httpclient.pool.type`参数来选择合适的连接池类型(例如ELASTIC、FIXED或DISABLED)。此外,还可以调整连接池的最大连接数(`spring.cloud.gateway.httpclient.pool.max-connections`)和其他相关连接参数,以提高性能。 3. 路由优化:检查并优化Gateway的路由配置。可以通过减少路由规则的数量、简化路由规则表达式、合并相似的路由规则等方式来优化路由的性能。 4. 缓存机制:如果可以,考虑使用缓存机制来减轻后端服务的负载。可以使用Spring Cloud Gateway的内置缓存功能或者结合其他缓存组件来实现。 5. 异步处理:对于处理比较耗时请求,可以考虑使用异步处理来提高性能。可以使用Spring Cloud Gateway的异步处理功能,将请求转发给异步处理的线程池进行处理。 总结起来,对于Spring Cloud Gateway的性能调优,可以从版本升级、配置调整、路由优化、缓存机制和异步处理等方面入手,根据具体需求和场景进行优化。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [Spring Cloud Gateway 服务器性能异常,以及优化策略](https://blog.csdn.net/weixin_42321034/article/details/123284715)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [spring cloud gateway性能优化](https://blog.csdn.net/weixin_42161936/article/details/123395773)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值