Open Owenxh opened 3 years ago
I think that would be a useful addition. 99% of the time should be in the API provider. This new metric might help to flush out issues with filters / predicates.
In order to resolve the problem, I enhanced the NettyRoutingFilter with cglib. It's a temporey method. Here is the code.
public class RoutingLatencyInterceptor implements MethodInterceptor {
private static final String FILTER_METHOD = "filter";
@SuppressWarnings("unchecked")
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ?
AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
if (isInterceptorMethod(userDeclaredMethod)) {
final long start = System.currentTimeMillis();
return ((Mono<Void>) invoke0(invocation))
.doOnSuccess(aVoid -> calculateRoutingLatency(invocation, start))
.doOnError(throwable -> calculateRoutingLatency(invocation, start));
}
return invoke0(invocation);
}
public boolean isInterceptorMethod(Method method) {
if (!FILTER_METHOD.equals(method.getName())) {
return false;
}
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 2) {
return ServerWebExchange.class.equals(parameterTypes[0])
&& GatewayFilterChain.class.equals(parameterTypes[1]);
}
return false;
}
private Object invoke0(MethodInvocation invocation) throws Throwable {
return invocation.proceed();
}
private void calculateRoutingLatency(MethodInvocation invocation, long start) {
Object[] args = invocation.getArguments();
if (args == null || args.length != 2) {
return;
}
ServerWebExchange exchange = (ServerWebExchange) args[0];
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
calculateRoutingLatency0(exchange, start);
}
else {
response.beforeCommit(() -> {
calculateRoutingLatency0(exchange, start);
return Mono.empty();
});
}
}
private void calculateRoutingLatency0(ServerWebExchange exchange, long start) {
long routingLatency = System.currentTimeMillis() - start;
exchange.getAttributes().put("routingLatency", Math.max(routingLatency, 0L));
}
}
public class RoutingPostProcessor implements BeanPostProcessor, BeanClassLoaderAware {
private ClassLoader classLoader;
private final MethodInterceptor methodInterceptor;
public RoutingPostProcessor(MethodInterceptor methodInterceptor) {
Objects.requireNonNull(methodInterceptor);
this.methodInterceptor = methodInterceptor;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if (bean instanceof NettyRoutingFilter) {
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.addAdvice(methodInterceptor);
proxyFactory.setProxyTargetClass(true);
return proxyFactory.getProxy(classLoader);
}
return bean;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
}
GatewayMetricsFilter has collect the total latency of a request. We want to collect the latency of the API provider server. NettyRoutingFilter doesn't collect the routing latency. Adding the corresponding latency collect feature in NettyRoutingFilter and then put as an attribute to ServerWebExchange. Then we cat get the routing latency.