在不修改源代码的情况下去增强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);
}
}
}