IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> druid源码解读--为监控而生,具体监控如何做 -> 正文阅读

[网络协议]druid源码解读--为监控而生,具体监控如何做

druid --为监控而生,具体监控如何做

结合昨天FilterChainImpl执行的FilterEventAdapter的拦截器处理逻辑,今天针对源码中的StatFilter源码进行分析,关注拦截sql执行中的监控参数处理。

connection_connect

连接时的拦截方法

public ConnectionProxy connection_connect(FilterChain chain, Properties info) throws SQLException {
  //连接代理类
    ConnectionProxy connection = null;

    long startNano = System.nanoTime();
    long startTime = System.currentTimeMillis();

    long nanoSpan;
    long nowTime = System.currentTimeMillis();
		//数据源统计
    JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
  //连接之前 connectingCount 数量++ 连接的最大数量 连接的最近时间
    dataSourceStat.getConnectionStat().beforeConnect();
    try {
      //执行连接 使用ConnectionProxyImpl连接代理类获取连接
        connection = chain.connection_connect(info);
        nanoSpan = System.nanoTime() - startNano;
    } catch (SQLException ex) {
      //连接异常记录
        dataSourceStat.getConnectionStat().connectError(ex);
        throw ex;
    }
//创建连接之后 激活的count++ 
  dataSourceStat.getConnectionStat().afterConnected(nanoSpan);

    if (connection != null) {
      //获取连接的详情
        JdbcConnectionStat.Entry statEntry = getConnectionInfo(connection);

        //数据源连接监控增加该连接
      dataSourceStat.getConnections().put(connection.getId(), statEntry);

        statEntry.setConnectTime(new Date(startTime));
        statEntry.setConnectTimespanNano(nanoSpan);
        statEntry.setEstablishNano(System.nanoTime());
        statEntry.setEstablishTime(nowTime);
        statEntry.setConnectStackTrace(new Exception());

//设置激活数量        
      dataSourceStat.getConnectionStat().setActiveCount(dataSourceStat.getConnections().size());
    }

    return connection;
}

connection_close

连接关闭的拦截方法

@Override
public void connection_close(FilterChain chain, ConnectionProxy connection) throws SQLException {
    if (connection.getCloseCount() == 0) {
        long nowNano = System.nanoTime();
//数据源统计
      
        JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
//closeCount数量修改
      dataSourceStat.getConnectionStat().incrementConnectionCloseCount();
 //获取连接的详情
      
        JdbcConnectionStat.Entry connectionInfo = getConnectionInfo(connection);

        long aliveNanoSpan = nowNano - connectionInfo.getEstablishNano();
 //监控池移除改连接
      
        JdbcConnectionStat.Entry existsConnection = dataSourceStat.getConnections().remove(connection.getId());
        if (existsConnection != null) {
          //激活的数量 激活的最小时间 属性修改
            dataSourceStat.getConnectionStat().afterClose(aliveNanoSpan);
        }
    }
		// 关闭连接
    chain.connection_close(connection);
}

connection_commit

@Override
public void connection_commit(FilterChain chain, ConnectionProxy connection) throws SQLException {
    chain.connection_commit(connection);

    JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
//commitCount数量++    
  dataSourceStat.getConnectionStat().incrementConnectionCommitCount();
}

@Override
public void connection_rollback(FilterChain chain, ConnectionProxy connection) throws SQLException {
    chain.connection_rollback(connection);

    JdbcDataSourceStat dataSourceStat = chain.getDataSource().getDataSourceStat();
//rollbackCount数量++       
  dataSourceStat.getConnectionStat().incrementConnectionRollbackCount();
}

internalBeforeStatementExecute

private final void internalBeforeStatementExecute(StatementProxy statement, String sql) {
    JdbcDataSourceStat dataSourceStat = statement.getConnectionProxy().getDirectDataSource().getDataSourceStat();
  //执行之前runningCount、concurrentMax属性设置
    dataSourceStat.getStatementStat().beforeExecute();
//获取连接代理类
    final ConnectionProxy connection = statement.getConnectionProxy();
  //获取连接
    final JdbcConnectionStat.Entry connectionCounter = getConnectionInfo(connection);
//设置最近执行时间
    statement.setLastExecuteStartNano();
//设置最近执行sql
    connectionCounter.setLastSql(sql);

    if (connectionStackTraceEnable) {
      //是否是可追踪的连接 
        connectionCounter.setLastStatementStatckTrace(new Exception());
    }

    // //SQL
		//获取sql
    JdbcSqlStat sqlStat = statement.getSqlStat();
    if (sqlStat == null || sqlStat.isRemoved()) {
      //如果没有sql统计 则创建 其中这块源码中有个有趣的点就是获取sqlstat的时候有个mergeSql的方法,如果有同样的sql的话会合并成一个SQLStatement吗?? TODO 后面研究一下
        sqlStat = createSqlStat(statement, sql);
        statement.setSqlStat(sqlStat);
    }

  //获取stat的上下文
    JdbcStatContext statContext = JdbcStatManager.getInstance().getStatContext();
    if (statContext != null) {
        sqlStat.setName(statContext.getName());
        sqlStat.setFile(statContext.getFile());
    }

    boolean inTransaction = false;
    try {
      //是否在事务中
        inTransaction = !statement.getConnectionProxy().getAutoCommit();
    } catch (SQLException e) {
        LOG.error("getAutoCommit error", e);
    }

    if (sqlStat != null) {
//更新执行的最近时间
      sqlStat.setExecuteLastStartTime(System.currentTimeMillis());
      //runningCountUpdater、concurrentMaxUpdater 属性修改 
      sqlStat.incrementRunningCount();

        if (inTransaction) {
          //事务中的inTransactionCountUpdater属性++
            sqlStat.incrementInTransactionCount();
        }
    }
// jdbc执行的count++
    StatFilterContext.getInstance().executeBefore(sql, inTransaction);

    String mergedSql;
    if (sqlStat != null) {
        mergedSql = sqlStat.getSql();
    } else {
        mergedSql = sql;
    }
  // ???
    Profiler.enter(mergedSql, Profiler.PROFILE_TYPE_SQL);
}

private final void internalAfterStatementExecute(StatementProxy statement, boolean firstResult,
                                                 int... updateCountArray) {
    final long nowNano = System.nanoTime();
    final long nanos = nowNano - statement.getLastExecuteStartNano();
		// 获取数据源
    JdbcDataSourceStat dataSourceStat = statement.getConnectionProxy().getDirectDataSource().getDataSourceStat();
  //在执行前设置参数
    dataSourceStat.getStatementStat().afterExecute(nanos);

    final JdbcSqlStat sqlStat = statement.getSqlStat();

    if (sqlStat != null) {
      //设置
        sqlStat.incrementExecuteSuccessCount();

        sqlStat.decrementRunningCount();
        sqlStat.addExecuteTime(statement.getLastExecuteType(), firstResult, nanos);
        statement.setLastExecuteTimeNano(nanos);
        if ((!firstResult) && statement.getLastExecuteType() == StatementExecuteType.Execute) {
            try {
              //获取更新次数
                int updateCount = statement.getUpdateCount();
              //增加更新次数
                sqlStat.addUpdateCount(updateCount);
            } catch (SQLException e) {
                LOG.error("getUpdateCount error", e);
            }
        } else {
            for (int updateCount : updateCountArray) {
              //更新的countArray 增加更新次数、fetchcount
                sqlStat.addUpdateCount(updateCount);
                sqlStat.addFetchRowCount(0);
                StatFilterContext.getInstance().addUpdateCount(updateCount);
            }
        }

        long millis = nanos / (1000 * 1000);
      //慢sql的监控
        if (millis >= slowSqlMillis) {
          //构建慢的参数
            String slowParameters = buildSlowParameters(statement);
          //设置参数
            sqlStat.setLastSlowParameters(slowParameters);

          //最近的执行sql
            String lastExecSql = statement.getLastExecuteSql();
            if (logSlowSql) {
              //慢sql的日志
                String msg = "slow sql " + millis + " millis. " + lastExecSql + "" + slowParameters;
                switch (slowSqlLogLevel) {
                    case "WARN":
                        LOG.warn(msg);
                        break;
                    case "INFO":
                        LOG.info(msg);
                        break;
                    case "DEBUG":
                        LOG.debug(msg);
                        break;
                    default:
                        LOG.error(msg);
                }
            }
						//处理慢sql
            handleSlowSql(statement);
        }
    }
		// 获取最近的执行的sql
    String sql = statement.getLastExecuteSql();
  //执行之后 设置执行时间、错误等等
    StatFilterContext.getInstance().executeAfter(sql, nanos, null);

    Profiler.release(nanos);
}

额外:mergedSql

  //在看前面源码中看到了mergedSql的处理比较有趣,也正是druid的工具类ParameterizedOutputVisitorUtils中比较好的一个功能,进行SQL模板的抽取。下一次就不用对相同的模板的SQL进行相关操作。
  
  public static String parameterize(String sql
          , DbType dbType
          , SQLSelectListCache selectListCache
          , List<Object> outParameters
          , SQLParserFeature[] features
          , VisitorFeature ...visitorFeatures) {
          //根据不同的方言获取sql的语句分析器
      SQLStatementParser parser = SQLParserUtils.createSQLStatementParser(sql, dbType, features);
  
      if (selectListCache != null) {
      //查询列表缓存有的话 直接设置
          parser.setSelectListCache(selectListCache);
      }
  
      List<SQLStatement> statementList = parser.parseStatementList();
      if (statementList.size() == 0) {
      //如果获取到之前的处理过的模版直接返回
          return sql;
      }
  
      StringBuilder out = new StringBuilder(sql.length());
      //Visitor类可以定义遇到某个SQL元素后的处理方法,或者遇到某个SQL元素后的处理方法。OutputVisitor用来把AST(抽象语法树)输出为字符串
      //根据不同的方言获取不同的处理方法ParameterizedOutputVisitor用来合并未参数化的SQL进行统计
      ParameterizedVisitor visitor = createParameterizedOutputVisitor(out, dbType);
      if (outParameters != null) {
      //输出参数不为空
          visitor.setOutputParameters(outParameters);
      }
  	//配置功能
      configVisitorFeatures(visitor, visitorFeatures);
  
      for (int i = 0; i < statementList.size(); i++) {
      //sql SQLStatement代表一条SQL,解析器会把一个字符串解析成一个SQL的列表,如果字符串中有多个SQL,每个SQL用分号分隔,会返回一个对应个SQLStatement对象的列表。
          SQLStatement stmt = statementList.get(i);
  
          if (i > 0) {
              SQLStatement preStmt = statementList.get(i - 1);
  
              if (preStmt.getClass() == stmt.getClass()) {
                  StringBuilder buf = new StringBuilder();
                  //获取处理方法
                  ParameterizedVisitor v1 = createParameterizedOutputVisitor(buf, dbType);
                  preStmt.accept(v1);
                  if (out.toString().equals(buf.toString())) {
                      continue;
                  }
              }
  
              if (!preStmt.isAfterSemi()) {
              //拼接分号 分隔不同的sql
                  out.append(";\n");
              } else {
                  out.append('\n');
              }
          }
  
          if (stmt.hasBeforeComment()) {
          
              stmt.getBeforeCommentsDirect().clear();
          }
  
          Class<?> stmtClass = stmt.getClass();
          if (stmtClass == SQLSelectStatement.class) { // only for performance
          //查询
              SQLSelectStatement selectStatement = (SQLSelectStatement) stmt;
              
              visitor.visit(selectStatement);
              visitor.postVisit(selectStatement);
          } else {
              stmt.accept(visitor);
          }
      }
  	//未用原始sql判读
      if (visitor.getReplaceCount() == 0
              && parser.getLexer().getCommentCount() == 0
              && sql.charAt(0) != '/') {
  		
          boolean notUseOriginalSql = false;
          if (visitorFeatures != null) {
              for (VisitorFeature visitorFeature : visitorFeatures) {
                  if (visitorFeature == VisitorFeature.OutputParameterizedZeroReplaceNotUseOriginalSql) {
                      notUseOriginalSql = true;
                  }
              }
          }
          if (!notUseOriginalSql) {
              int ddlStmtCount = 0;
              for (SQLStatement stmt : statementList) {
                  if (stmt instanceof SQLDDLStatement) {
                      ddlStmtCount++;
                  }
              }
              if (ddlStmtCount == statementList.size()) {
                  notUseOriginalSql = true;
              }
          }
  
          if (!notUseOriginalSql) {
              return sql;
          }
      }
  
      return out.toString();
  }

总结

今天主要针对StatFilter进行了源码学习,整个过滤器的逻辑处理都是围绕着监控的各个指标参数,也确实印证了druid为监控而生的定义。在看这块代码实现的时候感觉很有趣的就是他的抽象类模版方法这些设计思想,目前项目中也在用,在实际写业务代码的时候也可以尝试去采用架构思想进行设计自己的业务实现。我一直任务并不是一定要接触很牛逼的项目才能提升自己,提升自己的最好方法就是提高自己的定位,在做一个简单的业务功能的时候可以站的怎么让自己的功能高复用、怎么更灵活、怎么能够后续拓展,这个其实就是设计模式的最开始的想法,慢慢总结实践,这样经验一样很宝贵。后面针对Visitor进行细化解读,明确具体sql解析的原理。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章           查看所有文章
加:2021-11-14 22:06:46  更:2021-11-14 22:09:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/3 21:45:15-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码