package cn.langpy.kotime.data; import cn.langpy.kotime.constant.KoSqlConstant; import cn.langpy.kotime.model.*; import cn.langpy.kotime.service.GraphService; import cn.langpy.kotime.util.*; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.sql.DataSource; import java.lang.reflect.Parameter; import java.math.BigDecimal; import java.sql.Connection; import java.sql.SQLException; import java.util.*; import java.util.logging.Logger; import static java.util.stream.Collectors.toList; /** * zhangchang */ @Component("database") @Lazy public class DataBase implements GraphService { private static Logger log = Logger.getLogger(DataBase.class.toString()); private Connection readConnection; private Connection writeConnection; public DataBase() { Runtime.getRuntime().addShutdownHook( new Thread(() -> { try { if (null != readConnection) { readConnection.close(); } if (null != writeConnection) { writeConnection.close(); } } catch (SQLException throwables) { throwables.printStackTrace(); } finally { log.info("kotime=>closed database connections..."); } }) ); initConnection(); } public void initConnection() { getReadConnection(); getWriteConnection(); } public Connection getReadConnection() { try { if (null == readConnection || readConnection.isClosed()) { DataSource dataSource = Context.getDataSource(); if (null == dataSource) { if ("database".equals(Context.getConfig().getSaver())) { throw new DataBaseException("`ko-time.saver=database` needs a DataSource for MySQl or Oracle, or you can use `ko-time.saver=memory` to store data!"); } } else { readConnection = dataSource.getConnection(); } } } catch (SQLException throwables) { throwables.printStackTrace(); } return readConnection; } public Connection getWriteConnection() { try { if (null == writeConnection || writeConnection.isClosed()) { DataSource dataSource = Context.getDataSource(); if (null == dataSource) { if ("database".equals(Context.getConfig().getSaver())) { throw new DataBaseException("`ko-time.saver=database` needs a DataSource for MySQl or Oracle, or you can use `ko-time.saver=memory` to store data!"); } } else { writeConnection = dataSource.getConnection(); } } } catch (SQLException throwables) { throwables.printStackTrace(); } return writeConnection; } @Override public void addMethodNode(MethodNode methodNode) { if (null == methodNode) { return; } boolean existsById = DataBaseUtil.existsById(getWriteConnection(), KoSqlConstant.queryMethod, methodNode.getId()); if (!existsById) { Object[] params = new Object[]{ methodNode.getId(), methodNode.getName(), methodNode.getClassName(), methodNode.getMethodName(), methodNode.getRouteName(), methodNode.getMethodType().name() }; DataBaseUtil.insert(getWriteConnection(), KoSqlConstant.addMethod, params); } else { if (methodNode.getMethodType() == MethodType.Controller && !StringUtils.isEmpty(methodNode.getRouteName())) { Object[] params = new Object[]{ methodNode.getName(), methodNode.getClassName(), methodNode.getMethodName(), methodNode.getRouteName(), methodNode.getMethodType().name(), methodNode.getId(), }; DataBaseUtil.update(getWriteConnection(), KoSqlConstant.updateMethod, params); } } } @Override public synchronized void addExceptionNode(ExceptionNode exceptionNode) { boolean existsById = DataBaseUtil.existsById(getWriteConnection(), KoSqlConstant.queryException, exceptionNode.getId()); if (!existsById) { Object[] params = new Object[]{ exceptionNode.getId(), exceptionNode.getName(), exceptionNode.getClassName(), exceptionNode.getMessage() }; DataBaseUtil.insert(getWriteConnection(), KoSqlConstant.addException, params); } } @Override public synchronized MethodRelation addMethodRelation(MethodNode sourceMethodNode, MethodNode targetMethodNode) { if (null == sourceMethodNode || null == targetMethodNode) { return null; } if (sourceMethodNode.getId().equals(targetMethodNode.getId())) { return null; } try { List> query = DataBaseUtil.query(getWriteConnection(), KoSqlConstant.queryMethodRe, new Object[]{sourceMethodNode.getId() + targetMethodNode.getId()}); if (query.size() > 0) { Map old = query.get(0); double oldAvg = Double.valueOf(old.get("avg_run_time") + ""); double oldMax = Double.valueOf(old.get("max_run_time") + ""); double oldMin = Double.valueOf(old.get("min_run_time") + ""); BigDecimal bg = BigDecimal.valueOf((targetMethodNode.getValue() + oldAvg) / 2.0); double avg = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); double max = targetMethodNode.getValue() > oldMax ? targetMethodNode.getValue() : oldMax; double min = targetMethodNode.getValue() < oldMin ? targetMethodNode.getValue() : oldMin; Object[] params = new Object[]{ sourceMethodNode.getId(), targetMethodNode.getId(), avg, max, min, sourceMethodNode.getId() + targetMethodNode.getId() }; DataBaseUtil.update(getWriteConnection(), KoSqlConstant.updateMethodRe, params); return null; } else { Object[] params = new Object[]{ sourceMethodNode.getId() + targetMethodNode.getId(), sourceMethodNode.getId(), targetMethodNode.getId(), targetMethodNode.getValue(), targetMethodNode.getValue(), targetMethodNode.getValue() }; DataBaseUtil.insert(getWriteConnection(), KoSqlConstant.addMethodRe, params); } } catch (Exception e) { e.printStackTrace(); } return null; } @Override public synchronized ExceptionRelation addExceptionRelation(MethodNode sourceMethodNode, ExceptionNode exceptionNode) { boolean existsById = DataBaseUtil.existsById(getWriteConnection(), KoSqlConstant.queryExceptionRe, sourceMethodNode.getId() + exceptionNode.getId()); if (!existsById) { Object[] params = new Object[]{ sourceMethodNode.getId() + exceptionNode.getId(), sourceMethodNode.getId(), exceptionNode.getId(), exceptionNode.getValue() }; DataBaseUtil.insert(getWriteConnection(), KoSqlConstant.addExceptionRe, params); } return null; } @Override public synchronized void addParamAnalyse(String methodId, Parameter[] names, Object[] values, double v) { String paramsKey = Common.getPramsStr(names, values); List> query = DataBaseUtil.query(getWriteConnection(), KoSqlConstant.queryParamsAna, new Object[]{methodId, paramsKey}); if (query.size() == 0) { Object[] params = new Object[]{ methodId, paramsKey, v, v, v }; DataBaseUtil.insert(getWriteConnection(), KoSqlConstant.addParamsAna, params); } else { Map old = query.get(0); double oldAvg = Double.valueOf(old.get("avg_run_time") + ""); double oldMax = Double.valueOf(old.get("max_run_time") + ""); double oldMin = Double.valueOf(old.get("min_run_time") + ""); BigDecimal bg = BigDecimal.valueOf((v + oldAvg) / 2.0); double avg = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); double max = v > oldMax ? v : oldMax; double min = v < oldMin ? v : oldMin; Object[] params = new Object[]{ avg, max, min, methodId, paramsKey }; DataBaseUtil.update(getWriteConnection(), KoSqlConstant.updateParamsAna, params); } } @Override public MethodInfo getTree(String methodId) { MethodInfo rootInfo = new MethodInfo(); List methodNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethod, new Object[]{methodId}, MethodNode.class); if (methodNodes.size() == 0) { return rootInfo; } MethodNode methodNode = methodNodes.get(0); rootInfo.setId(methodNode.getId()); rootInfo.setName(methodNode.getName()); rootInfo.setClassName(methodNode.getClassName()); rootInfo.setMethodName(methodNode.getMethodName()); rootInfo.setMethodType(methodNode.getMethodType()); rootInfo.setRouteName(methodNode.getRouteName()); List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodReByTarget, new Object[]{methodId}, MethodRelation.class); if (relations.size() == 0) { return rootInfo; } MethodRelation methodRelation = relations.get(0); rootInfo.setValue(methodRelation.getAvgRunTime()); rootInfo.setAvgRunTime(methodRelation.getAvgRunTime()); rootInfo.setMaxRunTime(methodRelation.getMaxRunTime()); rootInfo.setMinRunTime(methodRelation.getMinRunTime()); List exceptionInfos = getExceptions(methodId); rootInfo.setExceptionNum(exceptionInfos.size()); rootInfo.setExceptions(exceptionInfos); List methodInfos = new ArrayList<>(); recursionMethod(rootInfo, methodInfos); methodInfos.clear(); return rootInfo; } public void recursionMethod(MethodInfo rootInfo, List methodInfos) { List children = getChildren(rootInfo.getId()); if (children != null && children.size() > 0) { if (!methodInfos.contains(rootInfo.getId())) { methodInfos.add(rootInfo.getId()); rootInfo.setChildren(children); for (MethodInfo child : children) { recursionMethod(child, methodInfos); } } } } @Override public Map getMethodParamGraph(String methodId) { Map paramMetricMap = new HashMap<>(); List paramAnas = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryParamsAnaBySource, new Object[]{methodId}, ParamAna.class); if (paramAnas.size() == 0) { return paramMetricMap; } for (ParamAna paramAna : paramAnas) { if (!paramMetricMap.containsKey(paramAna.getSourceId())) { ParamMetric paramMetric = new ParamMetric(); paramMetric.setAvgRunTime(paramAna.getAvgRunTime()); paramMetric.setMaxRunTime(paramAna.getMaxRunTime()); paramMetric.setMinRunTime(paramAna.getMinRunTime()); paramMetricMap.put(paramAna.getParams(), paramMetric); } } return paramMetricMap; } @Override public SystemStatistic getRunStatistic() { SystemStatistic systemStatistic = new SystemStatistic(); List controllerApis = getControllers(); if (null == controllerApis || controllerApis.size() == 0) { return systemStatistic; } int delayNum = 0; int totalNum = controllerApis.size(); double max = Double.MIN_VALUE; double min = Double.MAX_VALUE; double avg = controllerApis.get(0).getAvgRunTime(); for (MethodInfo controllerApi : controllerApis) { double avgRunTime = controllerApi.getAvgRunTime(); if (avgRunTime >= Context.getConfig().getThreshold()) { delayNum++; } if (avgRunTime > max) { max = avgRunTime; } if (avgRunTime < min) { min = avgRunTime; } avg = (avgRunTime + avg) / 2; } systemStatistic.setDelayNum(delayNum); systemStatistic.setNormalNum(totalNum - delayNum); systemStatistic.setTotalNum(totalNum); BigDecimal bg = BigDecimal.valueOf(avg); avg = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); systemStatistic.setMaxRunTime(max); systemStatistic.setMinRunTime(min); systemStatistic.setAvgRunTime(avg); return systemStatistic; } @Override public List getControllers() { List methodInfos = new ArrayList<>(); List controllers = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryController, new Object[]{MethodType.Controller.name()}, MethodInfo.class); for (MethodInfo methodNode : controllers) { String id = methodNode.getId(); List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodReByTarget, new Object[]{id}, MethodRelation.class); if (relations.size() == 0) { continue; } MethodRelation relation = relations.get(0); MethodInfo methodInfo = new MethodInfo(); methodInfo.setId(methodNode.getId()); methodInfo.setName(methodNode.getName()); methodInfo.setClassName(methodNode.getClassName()); methodInfo.setMethodName(methodNode.getMethodName()); methodInfo.setMethodType(methodNode.getMethodType()); methodInfo.setRouteName(methodNode.getRouteName()); methodInfo.setValue(relation.getAvgRunTime()); methodInfo.setAvgRunTime(relation.getAvgRunTime()); methodInfo.setMaxRunTime(relation.getMaxRunTime()); methodInfo.setMinRunTime(relation.getMinRunTime()); if (!methodInfos.contains(methodInfo)) { methodInfos.add(methodInfo); } } return methodInfos; } @Override public List getCondidates(String question) { List methodNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodLikeName, new Object[]{"%" + question + "%"}, MethodNode.class); List methodInfos = new ArrayList<>(); if (methodNodes.size() > 0) { methodInfos = methodNodes.stream().map(MethodNode::getName).collect(toList()); } return methodInfos; } @Override public List searchMethods(String question) { List methodInfos = new ArrayList<>(); List methodNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodLikeName, new Object[]{"%" + question + "%"}, MethodNode.class); for (MethodNode methodNode : methodNodes) { String id = methodNode.getId(); List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodReByTarget, new Object[]{id}, MethodRelation.class); if (relations.size() == 0) { continue; } MethodRelation relation = relations.get(0); MethodInfo methodInfo = new MethodInfo(); methodInfo.setId(methodNode.getId()); methodInfo.setName(methodNode.getName()); methodInfo.setClassName(methodNode.getClassName()); methodInfo.setMethodName(methodNode.getMethodName()); methodInfo.setMethodType(methodNode.getMethodType()); methodInfo.setRouteName(methodNode.getRouteName()); methodInfo.setValue(relation.getAvgRunTime()); methodInfo.setAvgRunTime(relation.getAvgRunTime()); methodInfo.setMaxRunTime(relation.getMaxRunTime()); methodInfo.setMinRunTime(relation.getMinRunTime()); if (!methodInfos.contains(methodInfo)) { methodInfos.add(methodInfo); } } return methodInfos; } @Override public List getChildren(String methodId) { List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethodReBySource, new Object[]{methodId}, MethodRelation.class); List methodInfos = new ArrayList<>(); for (MethodRelation methodRelation : relations) { List methodNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethod, new Object[]{methodRelation.getTargetId()}, MethodNode.class); if (methodNodes.size() == 0) { continue; } MethodNode methodNode = methodNodes.get(0); MethodInfo methodInfo = new MethodInfo(); methodInfo.setId(methodNode.getId()); methodInfo.setName(methodNode.getName()); methodInfo.setClassName(methodNode.getClassName()); methodInfo.setMethodName(methodNode.getMethodName()); methodInfo.setRouteName(methodNode.getRouteName()); methodInfo.setMethodType(methodNode.getMethodType()); methodInfo.setValue(methodRelation.getAvgRunTime()); methodInfo.setAvgRunTime(methodRelation.getAvgRunTime()); methodInfo.setMaxRunTime(methodRelation.getMaxRunTime()); methodInfo.setMinRunTime(methodRelation.getMinRunTime()); List exceptionInfos = getExceptions(methodNode.getId()); methodInfo.setExceptionNum(exceptionInfos.size()); methodInfo.setExceptions(exceptionInfos); if (!methodInfos.contains(methodInfo)) { methodInfos.add(methodInfo); } } return methodInfos; } @Override public List getExceptionInfos(String exceptionId) { List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryExceptionReByTarget, new Object[]{exceptionId}, ExceptionRelation.class); List exceptionInfos = new ArrayList<>(); for (ExceptionRelation relation : relations) { String sourceMethodId = relation.getSourceId(); List methodNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethod, new Object[]{sourceMethodId}, MethodNode.class); if (methodNodes.size() == 0) { continue; } MethodNode methodNode = methodNodes.get(0); List exceptions = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryMethod, new Object[]{exceptionId}, ExceptionNode.class); if (methodNodes.size() == 0) { continue; } ExceptionNode exceptionNode = exceptions.get(0); ExceptionInfo exceptionInfo = new ExceptionInfo(); exceptionInfo.setId(exceptionNode.getId()); exceptionInfo.setName(exceptionNode.getName()); exceptionInfo.setClassName(exceptionNode.getClassName()); exceptionInfo.setLocation(relation.getLocation()); exceptionInfo.setMessage(exceptionNode.getMessage()); exceptionInfo.setMethodName(methodNode.getMethodName()); exceptionInfo.setOccurClassName(methodNode.getClassName()); if (!exceptionInfos.contains(exceptionInfo)) { exceptionInfos.add(exceptionInfo); } } return exceptionInfos; } @Override public List getExceptions(String methodId) { List exceptionInfos = new ArrayList<>(); List relations = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryExceptionReByTarget, new Object[]{methodId}, ExceptionRelation.class); for (ExceptionRelation relation : relations) { String exceptionId = relation.getTargetId(); List exceptionNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryException, new Object[]{exceptionId}, ExceptionNode.class); if (exceptionNodes.size() == 0) { continue; } ExceptionNode exceptionNode = exceptionNodes.get(0); ExceptionInfo exceptionInfo = new ExceptionInfo(); exceptionInfo.setId(exceptionNode.getId()); exceptionInfo.setName(exceptionNode.getName()); exceptionInfo.setClassName(exceptionNode.getClassName()); exceptionInfo.setMessage(exceptionNode.getMessage()); exceptionInfo.setLocation(relation.getLocation()); if (!exceptionInfos.contains(exceptionInfo)) { exceptionInfos.add(exceptionInfo); } } return exceptionInfos; } @Override public List getExceptions() { List exceptionNodes = DataBaseUtil.query(getReadConnection(), KoSqlConstant.queryExceptions, null, ExceptionNode.class); return exceptionNodes; } }