¶Ô±ÈÐÂÎļþ |
| | |
| | | /* |
| | | * 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<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; |
| | | } |
| | | |
| | | } |