在不修改源代码情况下,解决Sentinel在配置全局异常处理器导致的无法感知到异常的解决方案

解决Sentinel在配置全局异常处理器导致的无法感知到异常

​ 在不修改源代码的情况下去增强Sentinel对异常的感知。原理 通过使用BeanPostProcessor在bean初始化的时候去做增强将异常处理器bean做代理操作。在异常方法调用之前会先before次数我们检测参数中是否有异常参数将参数记录保存到HttpServletRequest的Attribute中。在对SentinelWebInterceptor进行包装重写traceExceptionAndExit去检测我们的HttpServletRequest Attribute中是否含有我们保存的异常信息将其通过Tracer.traceEntry(processorException, entry);返回给Sentinel框架达到目的 以下是一些示例代码。

@Slf4j
@RestControllerAdvice
public class GlobalExceptionProcessor {
    @ExceptionHandler(value = Exception.class)
    public ResultData<Object> exception(Exception e, HttpServletRequest request) {
        this.printStackTrace(e);
        return ResultData.bind(ServiceStatus.ERROR);
    }
}
@Slf4j
@Component
public class GlobalExceptionProcessorBeanPostProcessor implements BeanPostProcessor, Aspect {

    @Override
    public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException {
        if (bean instanceof GlobalExceptionProcessor) {
            log.info(" <<<==== @EnableSentinelEnhance Bean {} BeanName {} Proxy Enhance", bean, beanName);
            return ProxyUtil.proxy(bean, this);
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }


    @Override
    public boolean before(Object target, Method method, Object[] args) {
        HttpServletRequest request = RequestContextTools.getRequest();
        //匹配是异常的参数
        Stream.of(args).filter(ex -> ex instanceof Exception).findFirst().ifPresent(ex -> {
            //增强Sentinel解决由于全局异常处理器导致无法记录异常数量
            request.setAttribute(SentinelWebInterceptorWrapper.GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY, ex);
        });
        return true;
    }

    @Override
    public boolean after(Object target, Method method, Object[] args, Object returnVal) {
        return true;
    }

    @Override
    public boolean afterException(Object target, Method method, Object[] args, Throwable e) {
        return true;
    }
}

public class RequestContextTools {

    public static HttpServletRequest getRequest() {
        return getServletRequestAttributes().getRequest();
    }

    public static HttpServletResponse getResponse() {
        return getServletRequestAttributes().getResponse();
    }

    public static ServletRequestAttributes getServletRequestAttributes() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) getRequestAttributes();
        if (requestAttributes == null) {
            throw new NullPointerException("getServletRequestAttributes.requestAttributes null");
        }
        return requestAttributes;
    }

    public static RequestAttributes getRequestAttributes() {
        return RequestContextHolder.getRequestAttributes();
    }
}

public class ResponseUtil {

    public static void writer(HttpServletResponse response, int status, String mediaType, String body) throws IOException {
        response.setStatus(status);
        response.setContentType(mediaType);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            writer.write(body);
        } finally {
            if (writer != null) {
                writer.flush();
                writer.close();
            }
        }
    }
}

@Slf4j
@Component
public class SentinelWebInterceptorBeanPostProcessor implements BeanPostProcessor {

    private final SentinelWebMvcConfig sentinelWebMvcConfig;

    @Autowired
    public SentinelWebInterceptorBeanPostProcessor(SentinelWebMvcConfig sentinelWebMvcConfig) {
        this.sentinelWebMvcConfig = sentinelWebMvcConfig;
    }

    @Override
    public Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException {
        if (bean instanceof SentinelWebInterceptor) {
            log.info(" <<<==== @EnableSentinelEnhance Bean {} BeanName {} To SentinelWebInterceptorWrapper Enhance", bean, beanName);
            return new SentinelWebInterceptorWrapper(sentinelWebMvcConfig);
        }
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }
}

@Slf4j
public class SentinelWebInterceptorWrapper extends SentinelWebInterceptor  {

    public final static String GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY = "__GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY__";

    public SentinelWebInterceptorWrapper(SentinelWebMvcConfig config) {
        super(config);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        super.afterCompletion(request, response, handler, ex);
    }

    @Override
    protected void traceExceptionAndExit(Entry entry, Exception ex) {
        //如果没有异常那么检查异常处理器有没有异常
        if (ex == null) {
            if (entry != null) {
                //获取HttpServletRequest对象
                HttpServletRequest request = RequestContextTools.getRequest();
                //获取 GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY 异常
                Exception processorException = (Exception) request.getAttribute(GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY);
                //记录异常信息
                Tracer.traceEntry(processorException, entry);
                entry.exit();
                //删除 GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY 异常
                request.removeAttribute(GLOBAL_EXCEPTION_PROCESSOR_EXCEPTION_KEY);
            }
        } else {
            super.traceExceptionAndExit(entry, ex);
        }
    }
}

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值