先说背景:压测发现耗时很久,使用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的排序进行扩展或者干预,后面仔细看了下还是有的!
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;
}
}
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
获取globalFilters
在globalFilters
前后加入自定义统计耗时的filter即可。