Springboot 提供了一个 @RestControllerAdvice 注解以及 @ExceptionHandler 注解,前者是用来开启全局的异常捕获,后者则是说明捕获哪些异常,对那些异常进行处理。

①项目提供的全局异常处理位于 gczjt-common-sentinel 模块

@ConditionalOnExpression 限制了 gczjt 的全局异常处理只会对 OAuth 2.0 的资源服务器有效

@ExceptionHandler 可以捕获具体的异常,进行相关的格式化处理

④ 业务异常交由 sentinel 记录 Tracer.trace(e); 这个非常重要,作为熔断等的重要指标项
package com.hzjt.gczjt.common.sentinel.feign;

import com.alibaba.cloud.sentinel.feign.SentinelContractHolder;
import com.alibaba.cloud.sentinel.feign.SentinelInvocationHandler;
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 com.hzjt.gczjt.common.core.util.R;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.MethodMetadata;
import feign.Target;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;

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} 支持自动降级注入
    *

  • @author gczjt

  • @date 2020/6/9
    */
    @Slf4j
    public class GczjtSentinelInvocationHandler implements InvocationHandler {

    public static final String EQUALS = “equals”;

    public static final String HASH_CODE = “hashCode”;

    public static final String TO_STRING = “toString”;

    private final Target<?> target;

    private final Map<Method, InvocationHandlerFactory.MethodHandler> dispatch;

    private FallbackFactory fallbackFactory;

    private Map<Method, Method> fallbackMethodMap;

    GczjtSentinelInvocationHandler(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);

    }

    GczjtSentinelInvocationHandler(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 (HASH_CODE.equals(method.getName())) {
         return hashCode();
     }
     else if (TO_STRING.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 {
                     // 若是R类型 执行自动降级返回R
                     if (R.class == method.getReturnType()) {
                         log.error("feign 服务间调用异常", ex);
                         return R.failed(ex.getLocalizedMessage());
                     }
                     else {
                         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 SentinelInvocationHandler) {
         GczjtSentinelInvocationHandler other = (GczjtSentinelInvocationHandler) 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;

    }

}

文档更新时间: 2021-08-10 17:04   作者:admin