為什么要監控
服務化接口是提供服務的,接口正確性、穩定性是最最重要的,在保證正確的同時需要盡量提高接口響應時間。
有的團隊會有專門的工具來對系統響應時間、吞吐量做監控,但如果團隊沒有這種“待遇”就需要自己來做一些工具為自己的代碼提供服務。
自己動手,豐衣足食
AOP + Annotation 簡陋實現,能達到目的
AOP : 使用環繞方式對接口攔截,在攔截接口前后記錄時間最后計算用時
Annotation : 自定義注解在接口上設置超時時間(timeout)和超時是否發送郵件選項(emailIfTimeout)
通過對接口實際執行時間和配置的超時時間比較,系統可以計算出接口是否超時,此時可使用日志(或其他能通知到開發人員的方式)記錄具體哪個接口、什么參數以及執行時間
注解可以提供更多的選項,來為自己接口服務,比如支持注解到類上,批量為接口設置了默認超時時間、支持日志中顯示的處理方法名稱 等等...
代碼實施
接口Annotation定義
/** * 接口自定義屬性 * * @author tianshu on 16/8/30 下午4:55. */ @Target(value = {ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface InterfaceProperty { /** * 接口超時時間,單位毫秒.默認值100毫秒 * @return 設置的超時時間 */ int timeout() default 400; /** * 當接口響應超時時,是否發送郵件.默認發送 * @return 返回ture需要發送郵件 */ boolean emailIfTimeout() default true; }
AOP實現
/** * @author tianshu on 16/1/28 下午10:35. */ @Component @Aspect public class SystemRequestAspect { /** 日志 */ private static final Logger LOG = LoggerFactory.getLogger(SystemRequestAspect.class); /** 接口超時日志 */ private static final Logger INTERFACE_TIMEOUT_LOG = LoggerFactory.getLogger("INTERFACE_TIMEOUT_LOG"); @Around(value = "execution(* com.xx.xx.xx.xx..*.*(..))", argNames="pjp") public Object validator(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); /** 攔截的方法名稱 */ String methodName = pjp.getTarget().getClass().getSimpleName() + "." + pjp.getSignature().getName(); try { long start = System.currentTimeMillis(); Object obj = pjp.proceed(args); long finish = System.currentTimeMillis(); long useTime = finish - start;
/** 接口響應時間監控 */ interfaceUseTimeMonitor(pjp.getTarget().getClass(), pjp.getSignature().getName(), args, useTime); return obj; } catch(Throwable e) {//處理你的異常 } finally {//處理其他 } } /** * 接口響應時間監控 * * @param targetClass 接口實現class * @param methodName 接口方法 * @param args 接口如參 * @param useTime 調用接口實際使用時間 */ private void interfaceUseTimeMonitor(Class targetClass, String methodName, Object[] args, long useTime) { /** 與接口注解最高用時做比較,符合條件發送郵件 */ try { Class[] classArray = new Class[args.length]; for(int i = 0; i < args.length ; ++i) { classArray[i] = args[i].getClass(); } Method method = targetClass.getMethod(methodName, classArray); if(method.isAnnotationPresent(InterfaceProperty.class)) { InterfaceProperty interfaceProperty = method.getAnnotation(InterfaceProperty.class); if(useTime >= interfaceProperty.timeout()) { if(INTERFACE_TIMEOUT_LOG.isInfoEnabled()) { INTERFACE_TIMEOUT_LOG.info("接口超時,interface:[{}].useTime:[{}].settingUseTime:[{}].traceId:[{}]", new Object[]{targetClass.getSimpleName() + "." + methodName, useTime, interfaceProperty.timeout(), TraceUtils.getTrace()}); } } } } catch(Throwable e) { /** 監控邏輯處理錯誤什么都不做 */ } } }