• 如何实现业务链路监控


     

    trace的一个demo版本,可以查看接口调用的整个链路,各种常规指标的统计,比如接口耗时,接口异常等。在此基础上手机日志后,可以做一些简单的logview展示。在实际工程中,根据业务需要,记录相关监控指标数据,可以在此基础上进行扩展,本demo展示的是trace链路原理上的处理方案。

    1.注解类:对所有需要进行拦截的类添加该注解后,就会被扫描到做切面操作。

    @Target({ElementType.METHOD,ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface TraceAnno {
        String desc() default "";
    }

    2.trace拦截类:对于注解类切面后的操作,记录程序入口和接口整个链路

    @Aspect
    public class TraceApsect {
    
        private final static Logger logger = LoggerFactory.getLogger(TraceApsect.class);
        
        @Pointcut("@annotation(com.demo.it.trace.TraceAnno) " + "|| @within(com.demo.it.trace.TraceAnno)")
        public void pointCut() {
        }
    
        @Around("pointCut()")
        public Object invoke(final ProceedingJoinPoint joinPoint) throws Throwable {
            if (!TraceSwitch.getInstance().isOpenProfilerTree()) {
                return joinPoint.proceed();
            }
            String methodName = this.getClassAndMethodName(joinPoint);
            if (null == methodName) {
                return joinPoint.proceed();
            }
            try {
                if (TraceProfiler.getEntry() == null) {
                    TraceProfiler.start(methodName);
                } else {
                    TraceProfiler.enter(methodName);
                }
                return joinPoint.proceed();
            } catch (Throwable e) {
                // 异常通知
                logger.error("The method " + methodName + " occurs expection : " + e);
                TraceProfiler.error(e);
                return e.getMessage();
            } finally {
                TraceProfiler.release();
                // 当root entry为状态为release的时候,打印信息,并做reset操作
                TraceProfiler.Entry rootEntry = TraceProfiler.getEntry();
                if (rootEntry != null) {
                    if (rootEntry.isReleased()) {
                        long duration = rootEntry.getDuration();
                        if (duration > TraceSwitch.getInstance().getInvokeTimeout()) {
                            logger.warn(TraceProfiler.dump());
                        } else {
                            logger.info(TraceProfiler.dump());
                        }
                        TraceProfiler.reset();
                    }
                }
            }
        }
    
        private String getClassAndMethodName(ProceedingJoinPoint joinPoint) {
            try {
                MethodSignature sign = (MethodSignature) joinPoint.getSignature();
                String clazzName = joinPoint.getTarget().toString();
                StringBuilder sb = new StringBuilder();
                sb.append(TraceProfiler.split(clazzName, "@")[0]);
                sb.append(":").append(sign.getMethod().getName());
                sb.append("(param:").append(sign.getMethod().getParameterTypes().length);
                sb.append(")");
                return sb.toString();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

    3.trace 需要统计的内容:错误日志,耗时,ip, 接口调用连等

    public final class TraceProfiler {
        /** 构建实体的存储缓存体 */
        private static final ThreadLocal<Entry> entryStack = new ThreadLocal<Entry>();
    
        /** 开始计时 */
        public static void start() {
            start((String) null);
        }
    
        /** 开始计时,创建一个Entry的实体对象 */
        public static void start(String message) {
            entryStack.set(new Entry(message, null, null));
        }
    
        /** threadLocal缓存清理,由于现在大多是线程池的设置,所以要做一个清理 */
        public static void reset() {
            entryStack.set(null);
        }
    
        /** 由于Entry自身是树状结构,所以如果是进入非Root的节点,那就需要enter来搞 */
        public static void enter(String message) {
            Entry currentEntry = getCurrentEntry();
            if (currentEntry != null) {
                currentEntry.enterSubEntry(message);
            }
        }
        
        /** 保存异常信息 */
        public static void error(Throwable e){
            Entry currentEntry = getCurrentEntry();
            if (currentEntry != null) {
                currentEntry.setErr(e);
            }
        }
    
        /** 方法运行结束之后,把当前的Entry的endTime来设置成当前时间 */
        public static void release() {
            Entry currentEntry = getCurrentEntry();
            if (currentEntry != null) {
                currentEntry.release();
            }
        }
    
        /** 获取start和end的时间差 */
        public static long getDuration() {
            Entry entry = (Entry) entryStack.get();
            if (entry != null) {
                return entry.getDuration();
            } else {
                return -1;
            }
        }
    
        /** 把Entry的信息dump出来,可以打印到日志中去 */
        public static String dump() {
            Entry entry = (Entry) entryStack.get();
            if (entry != null) {
                String str=JSONObject.toJSONString(entry);
                return str;
            } 
            return "";
        }
    
        /** 获取Entry信息 */
        public static Entry getEntry() {
            return (Entry) entryStack.get();
        }
    
        /** entry中含有subentry,如此这样来进行循环来保持树状的结构 */
        private static Entry getCurrentEntry() {
            Entry subEntry = (Entry) entryStack.get();
            Entry entry = null;
            if (subEntry != null) {
                do {
                    entry = subEntry;
                    subEntry = entry.getUnreleasedEntry();
                } while (subEntry != null);
            }
            return entry;
        }
        
        public static final class CONSTANT {
            public final static String DATE_FORMAT="yyyy-MM-dd hh:mm:ss SSS";
        }
        /**
         * 代表一个计时单元。
         */
        public static final class Entry {
            // subEntries来表示树状的子节点
            private final List<Entry> subEntries = new ArrayList<Entry>();
            private final Object message;
            private final Entry firstEntry;
            private final long startTime;
            private long endTime;
            private Throwable err;
    
            private Entry(Object message/* 描述信息 */, Entry parentEntry/* 父节点信息 */,
                    Entry firstEntry/* 第一个节点 */) {
                this.message = message;
                this.startTime = TraceSwitch.getInstance().isOpenProfilerNanoTime() == true ? System
                        .nanoTime() : System.currentTimeMillis();
                this.firstEntry = (Entry) defaultIfNull(firstEntry, this);
            }
    
            /**
             * 取得entry的信息。
             */
            public String getMessage() {
                return defaultIfEmpty((String) message, null);
            }
    
            public static String defaultIfEmpty(String str, String defaultStr) {
                return ((str == null) || (str.length() == 0)) ? defaultStr : str;
            }
    
            public static Object defaultIfNull(Object object, Object defaultValue) {
                return (object != null) ? object : defaultValue;
            }
    
            /** 获取当前节点的开始时间 */
            public String getStartTime() {
                SimpleDateFormat fmt=new SimpleDateFormat(CONSTANT.DATE_FORMAT);
                return fmt.format(startTime)  ;
            }
    
            /** 获取当前节点的结束时间 */
            public String getEndTime() {
                SimpleDateFormat fmt=new SimpleDateFormat(CONSTANT.DATE_FORMAT);
                return fmt.format(endTime)  ;
            }
    
            /** 获取持续时间 */
            public long getDuration() {
                if (endTime < startTime) {
                    return -1;
                } else {
                    return endTime - startTime;
                }
            }
    
            /** 取得entry自身所用的时间,即总时间减去所有子entry所用的时间。 */
            public long getDurationOfSelf() {
                long duration = getDuration();
                if (duration < 0) {
                    return -1;
                } else if (subEntries.isEmpty()) {
                    return duration;
                } else {
                    for (int i = 0; i < subEntries.size(); i++) {
                        Entry subEntry = (Entry) subEntries.get(i);
                        duration -= subEntry.getDuration();
                    }
                    if (duration < 0) {
                        return -1;
                    } else {
                        return duration;
                    }
                }
            }
    
            /** 取得所有子entries。 */
            public List<Entry> getSubEntries() {
                return Collections.unmodifiableList(subEntries);
            }
    
            /** 结束当前entry,并记录结束时间。 */
            private void release() {
                endTime = TraceSwitch.getInstance().isOpenProfilerNanoTime() == true ? System
                        .nanoTime() : System.currentTimeMillis();
            }
    
            /** 判断当前entry是否结束。 */
            public boolean isReleased() {
                return endTime > 0;
            }
    
            /** 创建一个新的子entry */
            private void enterSubEntry(Object message) {
                Entry subEntry = new Entry(message, this, firstEntry);
                subEntries.add(subEntry);
            }
            
            /** 添加异常信息 */
            public String getError() {
                return err==null?"":err.getMessage();
            }
            
            /** 设置异常信息 */
            public void setErr(Throwable e){
                this.err=e;
            }
    
            /** 取得未结束的子entry,链表中的最后一个元素 */
            private Entry getUnreleasedEntry() {
                Entry subEntry = null;
                if (!subEntries.isEmpty()) {
                    subEntry = (Entry) subEntries.get(subEntries.size() - 1);
                    if (subEntry.isReleased()) {
                        subEntry = null;
                    }
                }
                return subEntry;
            }
        }
    
        public static String[] split(String str, String separatorChars) {
            return split(str, separatorChars, -1);
        }
    
        private static String[] split(String str, String separatorChars, int max) {
            if (str == null) {
                return null;
            }
    
            int length = str.length();
    
            if (length == 0) {
                return new String[0];
            }
    
            List<String> list = new LinkedList<String>();
            int sizePlus1 = 1;
            int i = 0;
            int start = 0;
            boolean match = false;
    
            if (separatorChars == null) {
                // null表示使用空白作为分隔符
                while (i < length) {
                    if (Character.isWhitespace(str.charAt(i))) {
                        if (match) {
                            if (sizePlus1++ == max) {
                                i = length;
                            }
    
                            list.add(str.substring(start, i));
                            match = false;
                        }
    
                        start = ++i;
                        continue;
                    }
    
                    match = true;
                    i++;
                }
            } else if (separatorChars.length() == 1) {
                
                char sep = separatorChars.charAt(0);
    
                while (i < length) {
                    if (str.charAt(i) == sep) {
                        if (match) {
                            if (sizePlus1++ == max) {
                                i = length;
                            }
    
                            list.add(str.substring(start, i));
                            match = false;
                        }
    
                        start = ++i;
                        continue;
                    }
    
                    match = true;
                    i++;
                }
            } else {
                // 一般情形
                while (i < length) {
                    if (separatorChars.indexOf(str.charAt(i)) >= 0) {
                        if (match) {
                            if (sizePlus1++ == max) {
                                i = length;
                            }
    
                            list.add(str.substring(start, i));
                            match = false;
                        }
    
                        start = ++i;
                        continue;
                    }
    
                    match = true;
                    i++;
                }
            }
    
            if (match) {
                list.add(str.substring(start, i));
            }
            return list.toArray(new String[list.size()]);
        }
    
    }

    4.开关,各种指标监控

    public class TraceSwitch {
    
        private static TraceSwitch instance = new TraceSwitch();public static TraceSwitch getInstance(){
            return instance;
        }
        
    
        /**
         * 是否打开打印日志的开关
         */
        private boolean openProfilerTree =true;/**
         * 超时时间
         */
        private long invokeTimeout =500;/**
         * 是否打印纳秒
         * @return
         */
        private boolean openProfilerNanoTime = false;public boolean isOpenProfilerTree() {
            return openProfilerTree;
        }
    
        public void setOpenProfilerTree(boolean openProfilerTree) {
            this.openProfilerTree = openProfilerTree;
        }
    
        public long getInvokeTimeout() {
            return invokeTimeout;
        }
    
        public void setInvokeTimeout(long invokeTimeout) {
            this.invokeTimeout = invokeTimeout;
        }
    
        public boolean isOpenProfilerNanoTime() {
            return openProfilerNanoTime;
        }
    
        public void setOpenProfilerNanoTime(boolean openProfilerNanoTime) {
            this.openProfilerNanoTime = openProfilerNanoTime;
        }
        
        
    }
  • 相关阅读:
    从学算法体会如何更好的学习
    java数据结构与算法
    数据结构与算法资料汇总
    Oracle元数据查询总结
    Antlr词法分析之技巧——修改某个token
    动态规划公共子序列
    k8s笔记
    MiniDao1.9.0 版本发布,轻量级Java持久化框架
    autpoi 1.4.3版本发布—Excel傻瓜式API,快速实现Excel导入导出、Word模板导出
    喜讯!喜讯!JeecgBoot Github超 30000 Star—这个低代码平台你还不知道吗?
  • 原文地址:https://www.cnblogs.com/huane/p/6025498.html
Copyright © 2020-2023  润新知