為什么要監控
服務化接口是提供服務的,接口正確性、穩定性是最最重要的,在保證正確的同時需要盡量提高接口響應時間。
有的團隊會有專門的工具來對系統響應時間、吞吐量做監控,但如果團隊沒有這種“待遇”就需要自己來做一些工具為自己的代碼提供服務。
自己動手,豐衣足食
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) {
/** 監控邏輯處理錯誤什么都不做 */
}
}
}
