mirror of
https://github.com/chillzhuang/blade-tool
synced 2024-09-21 04:04:20 +08:00
169 lines
5.3 KiB
Java
169 lines
5.3 KiB
Java
/**
|
|
* Copyright (c) 2018-2028, Chill Zhuang 庄骞 (smallchill@163.com).
|
|
* <p>
|
|
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* <p>
|
|
* http://www.gnu.org/licenses/lgpl.html
|
|
* <p>
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.springblade.core.cloud.sentinel;
|
|
|
|
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;
|
|
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 com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler} 适配最新API
|
|
*
|
|
* @author Chill
|
|
*/
|
|
public class BladeSentinelInvocationHandler implements InvocationHandler {
|
|
|
|
private final Target<?> target;
|
|
|
|
private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;
|
|
|
|
private FallbackFactory fallbackFactory;
|
|
|
|
private Map<Method, Method> fallbackMethodMap;
|
|
|
|
public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch,
|
|
FallbackFactory fallbackFactory) {
|
|
this.target = checkNotNull(target, "target");
|
|
this.dispatch = checkNotNull(dispatch, "dispatch");
|
|
this.fallbackFactory = fallbackFactory;
|
|
this.fallbackMethodMap = toFallbackMethod(dispatch);
|
|
}
|
|
|
|
public BladeSentinelInvocationHandler(Target<?> target, Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
|
|
this.target = checkNotNull(target, "target");
|
|
this.dispatch = checkNotNull(dispatch, "dispatch");
|
|
}
|
|
|
|
@Override
|
|
public Object invoke(final Object proxy, final Method method, final Object[] args)
|
|
throws Throwable {
|
|
if ("equals".equals(method.getName())) {
|
|
try {
|
|
Object otherHandler = args.length > 0 && args[0] != null
|
|
? Proxy.getInvocationHandler(args[0]) : null;
|
|
return equals(otherHandler);
|
|
} catch (IllegalArgumentException e) {
|
|
return false;
|
|
}
|
|
} else if ("hashCode".equals(method.getName())) {
|
|
return hashCode();
|
|
} else if ("toString".equals(method.getName())) {
|
|
return toString();
|
|
}
|
|
|
|
Object result;
|
|
InvocationHandlerFactory.MethodHandler methodHandler = this.dispatch.get(method);
|
|
// only handle by HardCodedTarget
|
|
if (target instanceof Target.HardCodedTarget) {
|
|
Target.HardCodedTarget hardCodedTarget = (Target.HardCodedTarget) target;
|
|
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.trace(ex);
|
|
}
|
|
if (fallbackFactory != null) {
|
|
try {
|
|
Object fallbackResult = fallbackMethodMap.get(method)
|
|
.invoke(fallbackFactory.create(ex), args);
|
|
return fallbackResult;
|
|
} 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 BladeSentinelInvocationHandler) {
|
|
BladeSentinelInvocationHandler other = (BladeSentinelInvocationHandler) obj;
|
|
return target.equals(other.target);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode() {
|
|
return target.hashCode();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return target.toString();
|
|
}
|
|
|
|
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {
|
|
Map<Method, Method> result = new LinkedHashMap<>();
|
|
for (Method method : dispatch.keySet()) {
|
|
method.setAccessible(true);
|
|
result.put(method, method);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
}
|