/* * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * Neither the name of the dreamlu.net developer nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * Author: Chill 庄骞 (smallchill@163.com) */ 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 dispatch; private FallbackFactory fallbackFactory; private Map fallbackMethodMap; public BladeSentinelInvocationHandler(Target target, Map 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 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 toFallbackMethod(Map dispatch) { Map result = new LinkedHashMap<>(); for (Method method : dispatch.keySet()) { method.setAccessible(true); result.put(method, method); } return result; } }