package io.anyway.hera.service;
import io.anyway.hera.collector.MetricCollector;
import io.anyway.hera.collector.MetricHandler;
import io.anyway.hera.common.MetricQuota;
import io.anyway.hera.common.IdGenerator;
import io.anyway.hera.context.MetricTraceContext;
import io.anyway.hera.context.MetricTraceContextHolder;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.util.StringUtils;
import javax.servlet.ServletException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by yangzz on 16/8/13.
*/
@NonMetricService
public class ServiceMethodAdvisor implements MethodInterceptor,MetricCollector,Ordered {
private long pendingTime= 2*60*1000; //默认2分钟
final private ConcurrentMap<String,LongService> longServices = new ConcurrentHashMap<String,LongService>(2048);
private MetricHandler handler;
private List<Pattern> regExes= Collections.emptyList();
public void setHandler(MetricHandler handler){
this.handler= handler;
}
public void setPendingTime(long pendingTime){
this.pendingTime= pendingTime;
}
public void setPatterns(String patterns) throws ServletException {
if(!StringUtils.isEmpty(patterns)){
regExes= new LinkedList<Pattern>();
for(String each: patterns.split(",")){
regExes.add(Pattern.compile(each));
}
}
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Map<String,String> tags= new LinkedHashMap<String,String>();
Map<String,Object> props= new LinkedHashMap<String,Object>();
long beginTime= System.currentTimeMillis();
//设置调用方法名称
String methodName= invocation.getThis().getClass().getSimpleName()+"."+invocation.getMethod().getName();
tags.put("service",methodName);
if(!regExes.isEmpty()){
for(Pattern each: regExes){
Matcher matcher= each.matcher(methodName);
if(matcher.find()){
tags.put("pattern",each.pattern());
break;
}
}
}
//记录请求开始时间
props.put("beginTime",beginTime);
//自动生成方法标识
String spanId= IdGenerator.next();
//设置该请求的唯一ID
props.put("spanId",spanId);
boolean traceable= true;
//获取监控上下文
MetricTraceContext ctx= MetricTraceContextHolder.getMetricTraceContext();
//如果是本地调用
if (ctx== null) {
traceable= false;
String traceId= IdGenerator.next();
//构造监控上下文
ctx= new MetricTraceContext();
ctx.setTraceId(traceId);
ctx.setTraceStack(new Stack<String>());
ctx.setRemote("local");
MetricTraceContextHolder.setMetricTraceContext(ctx);
}
//把当前的路径入栈
ctx.getTraceStack().add(spanId);
//方便BLOCKSERVICE获取
props.put("traceId",ctx.getTraceId());
//保存服务调用信息,并发一万的数据丢弃
if(longServices.size()< 10000) {
longServices.put(spanId, new LongService(tags, props));
}
//执行业务方法
try{
return invocation.proceed();
}catch (Throwable ex){
//如果存在异常记录异常信息
if(!ctx.containException(ex)) {
ctx.addException(ex);
Map<String, String> xtags = new LinkedHashMap<String, String>();
xtags.put("class", ex.getClass().getSimpleName());
xtags.put("quota", MetricQuota.SERVICE.toString());
Map<String, Object> xprops = new LinkedHashMap<String, Object>();
xprops.put("message", ex.getMessage());
xprops.put("beginTime", System.currentTimeMillis());
handler.handle(MetricQuota.EXCEPTION, xtags, xprops);
}
throw ex;
}
finally {
//把当前的路径出栈
ctx.getTraceStack().pop();
//删除调用链信息
longServices.remove(spanId);
//记录结束时间
long endTime= System.currentTimeMillis();
//记录执行的时间
props.put("duration",endTime - beginTime);
//发送监控记录
handler.handle(MetricQuota.SERVICE,tags,props);
if(!traceable){
MetricTraceContextHolder.clear();
}
}
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
@Override
public void doCollect() {
for(Iterator<LongService> each = longServices.values().iterator(); each.hasNext();){
LongService longService= each.next();
Map<String,Object> props= longService.getProps();
if(System.currentTimeMillis()- (Long)props.get("beginTime")>= pendingTime){
handler.handle(MetricQuota.LONGSERVICE,longService.getTags(),props);
//从阻塞队列中删除
each.remove();
}
}
}
}
class LongService {
Map<String,String> tags;
Map<String,Object> props;
LongService(Map<String,String> tags, Map<String,Object> props){
this.tags= tags;
this.props= props;
}
Map<String,String> getTags(){
return tags;
}
Map<String,Object> getProps(){
return props;
}
}