cn.herodotus.engine.facility.alibaba.autoconfigure.sentinel.enhance.HerodotusSentinelInvocationHandler Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of facility-alibaba-spring-boot-starter Show documentation
Show all versions of facility-alibaba-spring-boot-starter Show documentation
基于 Spring Authorization Server 的 Dante Cloud 基础核心组件模块
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2020-2030 郑庚伟 ZHENGGENGWEI (码匠君), Licensed under the AGPL License
*
* This file is part of Herodotus Engine.
*
* Herodotus Engine is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Herodotus Engine is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
package cn.herodotus.engine.facility.alibaba.autoconfigure.sentinel.enhance;
import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.context.ContextUtil;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import feign.Feign;
import feign.InvocationHandlerFactory.MethodHandler;
import feign.MethodMetadata;
import feign.Target;
import org.springframework.cloud.openfeign.FallbackFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.LinkedHashMap;
import java.util.Map;
import static feign.Util.checkNotNull;
/**
* {@link InvocationHandler} handle invocation that protected by Sentinel.
*
* @author Jim
*/
public class HerodotusSentinelInvocationHandler implements InvocationHandler {
private final Target> target;
private final Map dispatch;
private FallbackFactory> fallbackFactory;
private Map fallbackMethodMap;
HerodotusSentinelInvocationHandler(Target> target, Map dispatch, FallbackFactory> fallbackFactory) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
this.fallbackFactory = fallbackFactory;
this.fallbackMethodMap = toFallbackMethod(dispatch);
}
HerodotusSentinelInvocationHandler(Target> target, Map dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch");
}
static Map toFallbackMethod(Map dispatch) {
Map result = new LinkedHashMap<>();
for (Method method : dispatch.keySet()) {
method.setAccessible(true);
result.put(method, method);
}
return result;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
switch (method.getName()) {
case "equals" -> {
try {
Object otherHandler = args.length > 0 && args[0] != null
? Proxy.getInvocationHandler(args[0])
: null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
}
case "hashCode" -> {
return hashCode();
}
case "toString" -> {
return toString();
}
}
Object result;
MethodHandler methodHandler = this.dispatch.get(method);
// only handle by HardCodedTarget
if (target instanceof Target.HardCodedTarget> hardCodedTarget) {
MethodMetadata methodMetadata = SentinelContractHolder.METADATA_MAP
.get(hardCodedTarget.type().getName()
+ Feign.configKey(hardCodedTarget.type(), method));
// resource default is HttpMethod:protocol://url
if (methodMetadata == null) {
result = methodHandler.invoke(args);
} else {
String resourceName = methodMetadata.template().method().toUpperCase()
+ ":" + hardCodedTarget.url() + methodMetadata.template().path();
Entry entry = null;
try {
ContextUtil.enter(resourceName);
entry = SphU.entry(resourceName, EntryType.OUT, 1, args);
result = methodHandler.invoke(args);
} catch (Throwable ex) {
// fallback handle
if (!BlockException.isBlockException(ex)) {
Tracer.traceEntry(ex, entry);
}
if (fallbackFactory != null) {
try {
return fallbackMethodMap.get(method).invoke(fallbackFactory.create(ex), args);
} catch (IllegalAccessException e) {
// shouldn't happen as method is public due to being an
// interface
throw new AssertionError(e);
} catch (InvocationTargetException e) {
throw new AssertionError(e.getCause());
}
} else {
// throw exception if fallbackFactory is null
throw ex;
}
} finally {
if (entry != null) {
entry.exit(1, args);
}
ContextUtil.exit();
}
}
} else {
// other target type using default strategy
result = methodHandler.invoke(args);
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof HerodotusSentinelInvocationHandler sentinelInvocationHandler) {
return target.equals(sentinelInvocationHandler.target);
}
return false;
}
@Override
public int hashCode() {
return target.hashCode();
}
@Override
public String toString() {
return target.toString();
}
}