From 981c995691041f888edbdc11a3f7f63dafcd967d Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Thu, 13 Nov 2025 10:43:28 +0800 Subject: [PATCH 01/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E6=8C=81?= =?UTF-8?q?=E6=9C=89=E8=BF=9E=E6=8E=A5=E7=9A=84=E7=BA=BF=E7=A8=8B=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录持有连接的线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/logback/logback-base.xml | 33 ++ .../store/mysql/NeatLogicBasicDataSource.java | 86 ++++- .../store/mysql/NeatLogicConnection.java | 329 ++++++++++++++++++ 3 files changed, 446 insertions(+), 2 deletions(-) create mode 100644 src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java diff --git a/src/main/java/neatlogic/framework/logback/logback-base.xml b/src/main/java/neatlogic/framework/logback/logback-base.xml index de34ead75..e981a2466 100644 --- a/src/main/java/neatlogic/framework/logback/logback-base.xml +++ b/src/main/java/neatlogic/framework/logback/logback-base.xml @@ -277,6 +277,36 @@ true + + ${log4j.home}/SQLTransientConnectionException.log + + ${log4j.home}/SQLTransientConnectionException.log.%i + 1 + 5 + + + ERROR + ACCEPT + DENY + + + 100MB + + + [%-5level]%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %logger{36}[%line] [%tenant] %requestUrl- %msg%n + + + + + + + 0 + 50 + + true + + ${log4j.home}/sqlTimeout.log @@ -357,6 +387,9 @@ + + + diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 097435a98..5b693dbc4 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -14,22 +14,104 @@ package neatlogic.framework.store.mysql; import com.zaxxer.hikari.HikariDataSource; import neatlogic.framework.asynchronization.threadlocal.UserContext; +import neatlogic.framework.common.config.Config; import neatlogic.framework.common.util.RC4Util; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.jdbc.CannotGetJdbcConnectionException; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; import java.sql.Connection; import java.sql.SQLException; +import java.sql.SQLTransientConnectionException; import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource - private final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); + private static final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); + + // 保存上次查询ShowProcesslist命令的时间毫秒数 + private static volatile long lastShowProcesslistMilliseconds = -1; + // 持有数据库连接的线程Map + private final static Map holdingConnectionThreadMap = new ConcurrentHashMap<>(); + + public static void addHoldingConnectionThreadByConnection(Connection connection) { + int size = holdingConnectionThreadMap.size(); + if (size <= Config.DATASOURCE_MAXIMUN_POOL_SIZE()) { + holdingConnectionThreadMap.put(connection, Thread.currentThread()); + } else { + logger.error("持有数据库连接的线程Map大小为{}, 数据库连接池最大数量为{}", size, Config.DATASOURCE_MAXIMUN_POOL_SIZE()); + } + } + + public static Thread removeHoldingConnectionThreadByConnection(Connection connection) { + return holdingConnectionThreadMap.remove(connection); + } + + public static String getHoldingConnectionThreadStackTrace(Map holdingConnectionThreadMap) { + ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = mxBean.getThreadInfo(mxBean.getAllThreadIds(), 0); + Map threadInfoMap = new HashMap<>(); + for (ThreadInfo threadInfo : threadInfos) { + threadInfoMap.put(threadInfo.getThreadId(), threadInfo); + } + StringBuilder stringBuilder = new StringBuilder(System.lineSeparator()); + stringBuilder.append("总线程数为: ").append(holdingConnectionThreadMap.size()).append(System.lineSeparator()); + for (Map.Entry entry : holdingConnectionThreadMap.entrySet()) { + Thread thread = entry.getValue(); + stringBuilder.append("[").append(thread.getName()).append("] prio=").append(thread.getPriority()) + .append(" tid=").append(thread.getId()) + .append(" ").append(thread.getState()) + .append(" ").append(thread.isDaemon() ? "deamon" : "worker"); + ThreadInfo threadInfo = threadInfoMap.get(thread.getId()); + if (threadInfo != null) { + stringBuilder.append(" native=").append(threadInfo.isInNative()) + .append(", suspended=").append(threadInfo.isSuspended()) + .append(", block=").append(threadInfo.getBlockedCount()) + .append(", wait=").append(threadInfo.getWaitedCount()) + .append(" lock=").append(threadInfo.getLockName()) + .append(" owned by ").append(threadInfo.getLockOwnerName()) + .append(" (").append(threadInfo.getLockOwnerId()) + .append("), cpu=").append(mxBean.getThreadCpuTime(threadInfo.getThreadId()) / 1000000L) + .append(", user=").append(mxBean.getThreadUserTime(threadInfo.getThreadId()) / 1000000L); + } + stringBuilder.append(System.lineSeparator()); + StackTraceElement[] stackTrace = thread.getStackTrace(); + for (StackTraceElement stackTraceElement : stackTrace) { + stringBuilder.append(" at ").append(stackTraceElement).append(System.lineSeparator()); + } + } + return stringBuilder.toString(); + } + + private synchronized void audit(Map holdingConnectionThreadMap) { + long currentTimeMillis = System.currentTimeMillis(); + long interval = currentTimeMillis - lastShowProcesslistMilliseconds; + if (interval > TimeUnit.MINUTES.toMillis(1)) { + lastShowProcesslistMilliseconds = currentTimeMillis; + Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); + SQLTransientConnectionExceptionAuditLogger.error(getHoldingConnectionThreadStackTrace(holdingConnectionThreadMap)); + } + } @Override public Connection getConnection() throws SQLException { - Connection conn = super.getConnection(); + Connection conn = null; + try { + conn = super.getConnection(); + addHoldingConnectionThreadByConnection(conn); + conn = new NeatLogicConnection(conn); + } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { + audit(new HashMap<>(holdingConnectionThreadMap)); + throw ex; + } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); try (Statement statement = conn.createStatement()) { if (Objects.equals(DatasourceManager.getDatabaseId(), DatabaseVendor.MYSQL.getDatabaseId())) { diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java new file mode 100644 index 000000000..73ce446e4 --- /dev/null +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java @@ -0,0 +1,329 @@ +/* + * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +package neatlogic.framework.store.mysql; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +public class NeatLogicConnection implements Connection { + + private final Connection connection; + + public NeatLogicConnection(Connection connection) { + this.connection = connection; + } + + @Override + public Statement createStatement() throws SQLException { + return this.connection.createStatement(); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + return this.connection.prepareStatement(sql); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return this.connection.prepareCall(sql); + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return this.connection.nativeSQL(sql); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + this.connection.setAutoCommit(autoCommit); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return this.connection.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + this.connection.commit(); + } + + @Override + public void rollback() throws SQLException { + this.connection.rollback(); + } + + @Override + public void close() throws SQLException { + NeatLogicBasicDataSource.removeHoldingConnectionThreadByConnection(this.connection); + this.connection.close(); + } + + @Override + public boolean isClosed() throws SQLException { + return this.connection.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return this.connection.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + this.connection.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLException { + return this.connection.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + this.connection.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + return this.connection.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + this.connection.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return this.connection.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return this.connection.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + this.connection.clearWarnings(); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return this.connection.createStatement(resultSetType, resultSetConcurrency); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override + public Map> getTypeMap() throws SQLException { + return this.connection.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + this.connection.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + this.connection.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + return this.connection.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return this.connection.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return this.connection.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + this.connection.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + this.connection.releaseSavepoint(savepoint); + } + + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return this.connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return this.connection.prepareStatement(sql, autoGeneratedKeys); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return this.connection.prepareStatement(sql, columnIndexes); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return this.connection.prepareStatement(sql, columnNames); + } + + @Override + public Clob createClob() throws SQLException { + return this.connection.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return this.connection.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return this.connection.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return this.connection.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return this.connection.isValid(timeout); + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + this.connection.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + this.connection.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return this.connection.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return this.connection.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return this.connection.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return this.connection.createStruct(typeName, attributes); + } + + @Override + public void setSchema(String schema) throws SQLException { + this.connection.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + return this.connection.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + this.connection.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + this.connection.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return this.connection.getNetworkTimeout(); + } + + @Override + public void beginRequest() throws SQLException { + this.connection.beginRequest(); + } + + @Override + public void endRequest() throws SQLException { + this.connection.endRequest(); + } + + @Override + public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLException { + return this.connection.setShardingKeyIfValid(shardingKey, superShardingKey, timeout); + } + + @Override + public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException { + return this.connection.setShardingKeyIfValid(shardingKey, timeout); + } + + @Override + public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLException { + this.connection.setShardingKey(shardingKey, superShardingKey); + } + + @Override + public void setShardingKey(ShardingKey shardingKey) throws SQLException { + this.connection.setShardingKey(shardingKey); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return this.connection.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return this.connection.isWrapperFor(iface); + } +} -- Gitee From 990c00bcc764efd9e69ed09aeb44f3a846f1b545 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Fri, 14 Nov 2025 18:49:17 +0800 Subject: [PATCH 02/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E6=8C=81?= =?UTF-8?q?=E6=9C=89=E8=BF=9E=E6=8E=A5=E7=9A=84=E7=BA=BF=E7=A8=8B=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录持有连接的线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../neatlogic/framework/common/config/Config.java | 12 +++++++++--- .../store/mysql/NeatLogicBasicDataSource.java | 10 +++++++--- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java index 4ed2d325a..d74752aac 100644 --- a/src/main/java/neatlogic/framework/common/config/Config.java +++ b/src/main/java/neatlogic/framework/common/config/Config.java @@ -54,7 +54,7 @@ public class Config { private static String DB_HOST; private static Integer DB_PORT; private static String DB_URL; - private static String DB_TRANSACTION_TIMEOUT;// 事务超时时间 + private static Integer DB_TRANSACTION_TIMEOUT;// 事务超时时间 private static int DATASOURCE_CONNECT_TIMEOUT;//连接池连接超时时间 private static Integer DATASOURCE_MAXIMUM_POOL_SIZE;//连接数 private static Integer DATASOURCE_MAX_LIFETIME;//控制池中连接的最大生存期 @@ -62,6 +62,7 @@ public class Config { private static Integer DATASOURCE_VALIDATION_TIMEOUT;//此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout private static Integer DATASOURCE_IDLE_TIMEOUT;//此属性控制允许连接在池中处于空闲状态的最长时间 private static Long DATASOURCE_KEEPALIVE_TIME;//此属性控制允许连接在池中心跳时间,不能比DATASOURCE_MAX_LIFETIME大 + private static Boolean DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE; // 数据库连接持有者(线程)跟踪开启 private static String DATA_HOME;// 存储文件路径 private static String AUDIT_HOME;// 审计日志存储文件路径 private static int SERVER_HEARTBEAT_RATE;// 心跳频率 @@ -249,7 +250,7 @@ public class Config { return DB_URL; } - public static String DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 + public static Integer DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 return DB_TRANSACTION_TIMEOUT; } @@ -281,6 +282,10 @@ public class Config { return DATASOURCE_IDLE_TIMEOUT; } + public static boolean DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE() { + return DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE; + } + public static String JMS_URL() { return JMS_URL; } @@ -628,7 +633,7 @@ public class Config { USER_EXPIRETIME = prop.getProperty("user.expiretime", "60"); LOGIN_CAPTCHA_EXPIRED_TIME = Integer.parseInt(prop.getProperty("login.captcha.expired.time", "60")); LOGIN_FAILED_TIMES_CAPTCHA = Integer.parseInt(prop.getProperty("login.failed.times.captcha", "3")); - DB_TRANSACTION_TIMEOUT = prop.getProperty("db.transaction.timeout"); + DB_TRANSACTION_TIMEOUT = Integer.parseInt(prop.getProperty("db.transaction.timeout")); DATASOURCE_CONNECT_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.connect.timeout", "5000")); DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "250")); DATASOURCE_KEEPALIVE_TIME = Long.parseLong(prop.getProperty("datasource.keepalive.time", "180000")); @@ -636,6 +641,7 @@ public class Config { DATASOURCE_MINIMUM_IDLE = Integer.parseInt(prop.getProperty("datasource.minimum.idle", "20")); DATASOURCE_VALIDATION_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.validation.timeout", "5000")); DATASOURCE_IDLE_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.idle.timeout", "600000")); + DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE = Boolean.parseBoolean(prop.getProperty("datasource.connection.holder.track.enable", "true")); DB_URL = prop.getProperty("db.url"); DB_HOST = prop.getProperty("db.host", "localhost"); DB_PORT = Integer.parseInt(prop.getProperty("db.port", "3306")); diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 5b693dbc4..0b30fe1c7 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -106,10 +106,14 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 Connection conn = null; try { conn = super.getConnection(); - addHoldingConnectionThreadByConnection(conn); - conn = new NeatLogicConnection(conn); + if (Config.DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE()) { + addHoldingConnectionThreadByConnection(conn); + conn = new NeatLogicConnection(conn); + } } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { - audit(new HashMap<>(holdingConnectionThreadMap)); + if (Config.DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE()) { + audit(new HashMap<>(holdingConnectionThreadMap)); + } throw ex; } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); -- Gitee From db8629224a4437ee8f83fb71adcc871f82c6670f Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 18:23:58 +0800 Subject: [PATCH 03/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../threadlocal/InterceptorContext.java | 75 ++++ .../framework/common/config/Config.java | 12 +- .../framework/dao/config/mybatis-config.xml | 1 + .../dao/plugin/DataSchemaInterceptor.java | 6 +- .../dao/plugin/ExecutingSQLInterceptor.java | 63 ++++ ...ModifyResultMapTypeHandlerInterceptor.java | 10 +- .../dao/plugin/SqlCostInterceptor.java | 65 ++-- .../dao/plugin/ThreadLocalInterceptor.java | 44 +++ .../store/mysql/NeatLogicBasicDataSource.java | 104 ++---- .../store/mysql/NeatLogicConnection.java | 329 ------------------ .../neatlogic/framework/util/ThreadUtil.java | 59 ++++ 11 files changed, 323 insertions(+), 445 deletions(-) create mode 100644 src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java create mode 100644 src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java create mode 100644 src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java delete mode 100644 src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java create mode 100644 src/main/java/neatlogic/framework/util/ThreadUtil.java diff --git a/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java b/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java new file mode 100644 index 000000000..304b4993e --- /dev/null +++ b/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java @@ -0,0 +1,75 @@ +/* + * + * * + * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * * This file is part of the NeatLogic software. + * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * * You may use this file only in compliance with the License. + * * See the LICENSE file distributed with this work for the full license text. + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * + * + */ + +package neatlogic.framework.asynchronization.threadlocal; + +import org.apache.ibatis.mapping.MappedStatement; + +import java.io.Serial; +import java.io.Serializable; + +public class InterceptorContext implements Serializable { + + private static final ThreadLocal instance = new ThreadLocal<>(); + @Serial + private static final long serialVersionUID = -5420998728515359636L; + + private MappedStatement mappedStatement; + + private Object parameter; + // 判断是否查询了数据库 + private Boolean queryFromDatabase; + + private InterceptorContext() { + + } + + public static InterceptorContext init() { + InterceptorContext interceptorContext = new InterceptorContext(); + instance.set(interceptorContext); + return instance.get(); + } + + public static InterceptorContext get() { + return instance.get(); + } + + public void release() { + instance.remove(); + } + + public MappedStatement getMappedStatement() { + return mappedStatement; + } + + public void setMappedStatement(MappedStatement mappedStatement) { + this.mappedStatement = mappedStatement; + } + + public Object getParameter() { + return parameter; + } + + public void setParameter(Object parameter) { + this.parameter = parameter; + } + + public Boolean getQueryFromDatabase() { + return queryFromDatabase; + } + + public void setQueryFromDatabase(Boolean queryFromDatabase) { + this.queryFromDatabase = queryFromDatabase; + } +} diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java index d74752aac..4ed2d325a 100644 --- a/src/main/java/neatlogic/framework/common/config/Config.java +++ b/src/main/java/neatlogic/framework/common/config/Config.java @@ -54,7 +54,7 @@ public class Config { private static String DB_HOST; private static Integer DB_PORT; private static String DB_URL; - private static Integer DB_TRANSACTION_TIMEOUT;// 事务超时时间 + private static String DB_TRANSACTION_TIMEOUT;// 事务超时时间 private static int DATASOURCE_CONNECT_TIMEOUT;//连接池连接超时时间 private static Integer DATASOURCE_MAXIMUM_POOL_SIZE;//连接数 private static Integer DATASOURCE_MAX_LIFETIME;//控制池中连接的最大生存期 @@ -62,7 +62,6 @@ public class Config { private static Integer DATASOURCE_VALIDATION_TIMEOUT;//此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout private static Integer DATASOURCE_IDLE_TIMEOUT;//此属性控制允许连接在池中处于空闲状态的最长时间 private static Long DATASOURCE_KEEPALIVE_TIME;//此属性控制允许连接在池中心跳时间,不能比DATASOURCE_MAX_LIFETIME大 - private static Boolean DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE; // 数据库连接持有者(线程)跟踪开启 private static String DATA_HOME;// 存储文件路径 private static String AUDIT_HOME;// 审计日志存储文件路径 private static int SERVER_HEARTBEAT_RATE;// 心跳频率 @@ -250,7 +249,7 @@ public class Config { return DB_URL; } - public static Integer DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 + public static String DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 return DB_TRANSACTION_TIMEOUT; } @@ -282,10 +281,6 @@ public class Config { return DATASOURCE_IDLE_TIMEOUT; } - public static boolean DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE() { - return DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE; - } - public static String JMS_URL() { return JMS_URL; } @@ -633,7 +628,7 @@ public class Config { USER_EXPIRETIME = prop.getProperty("user.expiretime", "60"); LOGIN_CAPTCHA_EXPIRED_TIME = Integer.parseInt(prop.getProperty("login.captcha.expired.time", "60")); LOGIN_FAILED_TIMES_CAPTCHA = Integer.parseInt(prop.getProperty("login.failed.times.captcha", "3")); - DB_TRANSACTION_TIMEOUT = Integer.parseInt(prop.getProperty("db.transaction.timeout")); + DB_TRANSACTION_TIMEOUT = prop.getProperty("db.transaction.timeout"); DATASOURCE_CONNECT_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.connect.timeout", "5000")); DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "250")); DATASOURCE_KEEPALIVE_TIME = Long.parseLong(prop.getProperty("datasource.keepalive.time", "180000")); @@ -641,7 +636,6 @@ public class Config { DATASOURCE_MINIMUM_IDLE = Integer.parseInt(prop.getProperty("datasource.minimum.idle", "20")); DATASOURCE_VALIDATION_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.validation.timeout", "5000")); DATASOURCE_IDLE_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.idle.timeout", "600000")); - DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE = Boolean.parseBoolean(prop.getProperty("datasource.connection.holder.track.enable", "true")); DB_URL = prop.getProperty("db.url"); DB_HOST = prop.getProperty("db.host", "localhost"); DB_PORT = Integer.parseInt(prop.getProperty("db.port", "3306")); diff --git a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml index ab4479288..aec63c08b 100644 --- a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml +++ b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml @@ -46,5 +46,6 @@ along with this program. If not, see .--> + diff --git a/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java index 61b93c421..0f41d2405 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/DataSchemaInterceptor.java @@ -12,6 +12,7 @@ package neatlogic.framework.dao.plugin; +import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; import neatlogic.framework.asynchronization.threadlocal.TenantContext; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; @@ -31,7 +32,10 @@ public class DataSchemaInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { - SqlCostInterceptor.QUERY_FROM_DATABASE_INSTANCE.set(true); + InterceptorContext interceptorContext = InterceptorContext.get(); + if (interceptorContext != null) { + interceptorContext.setQueryFromDatabase(true); + } StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); BoundSql boundSql = statementHandler.getBoundSql(); diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java new file mode 100644 index 000000000..a0d117595 --- /dev/null +++ b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java @@ -0,0 +1,63 @@ +/* + * + * * + * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * * This file is part of the NeatLogic software. + * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * * You may use this file only in compliance with the License. + * * See the LICENSE file distributed with this work for the full license text. + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * + * + */ + +package neatlogic.framework.dao.plugin; + +import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; +import org.apache.ibatis.executor.statement.StatementHandler; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; + +import java.sql.Statement; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Intercepts({ + @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}), + @Signature(type = StatementHandler.class, method = "update", args = {Statement.class}), + @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class}), + @Signature(type = StatementHandler.class, method = "queryCursor", args = {Statement.class}), +}) +public class ExecutingSQLInterceptor implements Interceptor { + + private final static Map thread2ExecutingSQLMap = new ConcurrentHashMap<>(); + + public static Map getThread2ExecutingSQLMap() { + return new HashMap<>(thread2ExecutingSQLMap); + } + + public static void clearThread2ExecutingSQLMap() { + thread2ExecutingSQLMap.clear(); + } + + @Override + public Object intercept(Invocation invocation) throws Throwable { + try { + InterceptorContext interceptorContext = InterceptorContext.get(); + if (interceptorContext != null) { + MappedStatement mappedStatement = interceptorContext.getMappedStatement(); + String sqlId = mappedStatement.getId(); + thread2ExecutingSQLMap.put(Thread.currentThread(), sqlId); + } + return invocation.proceed(); + } finally { + thread2ExecutingSQLMap.remove(Thread.currentThread()); + } + } +} diff --git a/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java index c5dd0e73a..e3d955489 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ModifyResultMapTypeHandlerInterceptor.java @@ -12,6 +12,7 @@ package neatlogic.framework.dao.plugin; +import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; import org.apache.commons.collections4.CollectionUtils; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.mapping.MappedStatement; @@ -43,12 +44,15 @@ import java.util.List; public class ModifyResultMapTypeHandlerInterceptor implements Interceptor { Logger logger = LoggerFactory.getLogger(ModifyResultMapTypeHandlerInterceptor.class); - public static final ThreadLocal mappedStatementThreadLocal = new ThreadLocal<>(); @Override public Object intercept(Invocation invocation) throws Throwable { try { - MappedStatement mappedStatement = mappedStatementThreadLocal.get(); + MappedStatement mappedStatement = null; + InterceptorContext interceptorContext = InterceptorContext.get(); + if (interceptorContext != null) { + mappedStatement = interceptorContext.getMappedStatement(); + } if (mappedStatement != null) { Configuration configuration = mappedStatement.getConfiguration(); int resultMappingSize = 0; @@ -79,8 +83,6 @@ public class ModifyResultMapTypeHandlerInterceptor implements Interceptor { } } catch (Exception e) { logger.error(e.getMessage(), e); - } finally { - mappedStatementThreadLocal.remove(); } return invocation.proceed(); } diff --git a/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java index b5793d92a..7cbffc1d5 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/SqlCostInterceptor.java @@ -12,6 +12,7 @@ package neatlogic.framework.dao.plugin; +import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; import neatlogic.framework.asynchronization.threadlocal.RequestContext; import neatlogic.framework.asynchronization.threadlocal.TenantContext; import neatlogic.framework.asynchronization.threadlocal.UserContext; @@ -48,8 +49,6 @@ import java.util.regex.Matcher; }) public class SqlCostInterceptor implements Interceptor { Logger logger = LoggerFactory.getLogger(SqlCostInterceptor.class); - // 判断是否查询了数据库 - public static final ThreadLocal QUERY_FROM_DATABASE_INSTANCE = new ThreadLocal<>(); public static class SqlIdMap { private static final Set sqlSet = new HashSet<>(); @@ -101,7 +100,6 @@ public class SqlCostInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; - ModifyResultMapTypeHandlerInterceptor.mappedStatementThreadLocal.set(mappedStatement); long starttime = 0; SqlAuditVo sqlAuditVo = null; boolean hasCacheFirstLevel = false; @@ -150,43 +148,42 @@ public class SqlCostInterceptor implements Interceptor { } catch (Exception e) { logger.error(e.getMessage(), e); } - QUERY_FROM_DATABASE_INSTANCE.set(false); - try { - // 执行完上面的任务后,不改变原有的sql执行过程 - Object val = invocation.proceed(); - if (sqlAuditVo != null) { - if (QUERY_FROM_DATABASE_INSTANCE.get()) { - // sql语句被执行,没有使用到缓存 - sqlAuditVo.setUseCacheLevel(StringUtils.EMPTY); + InterceptorContext interceptorContext = InterceptorContext.get(); + if (interceptorContext != null) { + interceptorContext.setQueryFromDatabase(false); + } + // 执行完上面的任务后,不改变原有的sql执行过程 + Object val = invocation.proceed(); + if (sqlAuditVo != null) { + if (interceptorContext != null && interceptorContext.getQueryFromDatabase()) { + // sql语句被执行,没有使用到缓存 + sqlAuditVo.setUseCacheLevel(StringUtils.EMPTY); + } else { + if (hasCacheFirstLevel) { + sqlAuditVo.setUseCacheLevel("一级缓存"); } else { - if (hasCacheFirstLevel) { - sqlAuditVo.setUseCacheLevel("一级缓存"); - } else { - sqlAuditVo.setUseCacheLevel("二级缓存"); - } + sqlAuditVo.setUseCacheLevel("二级缓存"); } - sqlAuditVo.setTimeCost(System.currentTimeMillis() - starttime); - sqlAuditVo.setRunTime(new Date()); + } + sqlAuditVo.setTimeCost(System.currentTimeMillis() - starttime); + sqlAuditVo.setRunTime(new Date()); - if (val != null) { - if (val instanceof List) { - sqlAuditVo.setRecordCount(((List) val).size()); - } else { - sqlAuditVo.setRecordCount(1); - } - } - SqlAuditManager.addSqlAudit(sqlAuditVo); - RequestContext requestContext = RequestContext.get(); - if (requestContext != null) { - requestContext.addSqlAudit(sqlAuditVo); + if (val != null) { + if (val instanceof List) { + sqlAuditVo.setRecordCount(((List) val).size()); + } else { + sqlAuditVo.setRecordCount(1); } - //System.out.println("time cost:" + (System.currentTimeMillis() - starttime) + "ms"); - //System.out.println("###########################################################################"); } - return val; - } finally { - QUERY_FROM_DATABASE_INSTANCE.remove(); + SqlAuditManager.addSqlAudit(sqlAuditVo); + RequestContext requestContext = RequestContext.get(); + if (requestContext != null) { + requestContext.addSqlAudit(sqlAuditVo); + } + //System.out.println("time cost:" + (System.currentTimeMillis() - starttime) + "ms"); + //System.out.println("###########################################################################"); } + return val; } public static String getSql(MappedStatement mappedStatement, Object parameterObject) { diff --git a/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java new file mode 100644 index 000000000..badb6933c --- /dev/null +++ b/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java @@ -0,0 +1,44 @@ +/* + * + * * + * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * * This file is part of the NeatLogic software. + * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * * You may use this file only in compliance with the License. + * * See the LICENSE file distributed with this work for the full license text. + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * + * + */ + +package neatlogic.framework.dao.plugin; + +import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +@Intercepts({ + @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}), + @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), +}) +public class ThreadLocalInterceptor implements Interceptor { + + @Override + public Object intercept(Invocation invocation) throws Throwable { + InterceptorContext interceptorContext = InterceptorContext.init(); + try { + interceptorContext.setMappedStatement((MappedStatement) invocation.getArgs()[0]); + interceptorContext.setParameter(invocation.getArgs()[1]); + return invocation.proceed(); + } finally { + interceptorContext.release(); + } + } +} diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 0b30fe1c7..fa48dab72 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -14,24 +14,22 @@ package neatlogic.framework.store.mysql; import com.zaxxer.hikari.HikariDataSource; import neatlogic.framework.asynchronization.threadlocal.UserContext; -import neatlogic.framework.common.config.Config; import neatlogic.framework.common.util.RC4Util; +import neatlogic.framework.dao.plugin.ExecutingSQLInterceptor; +import neatlogic.framework.util.ThreadUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.CannotGetJdbcConnectionException; -import java.lang.management.ManagementFactory; -import java.lang.management.ThreadInfo; -import java.lang.management.ThreadMXBean; +import java.io.IOException; +import java.io.StringWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransientConnectionException; import java.sql.Statement; -import java.util.HashMap; import java.util.Map; import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource @@ -39,65 +37,41 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 // 保存上次查询ShowProcesslist命令的时间毫秒数 private static volatile long lastShowProcesslistMilliseconds = -1; - // 持有数据库连接的线程Map - private final static Map holdingConnectionThreadMap = new ConcurrentHashMap<>(); + private static volatile int count = -1; - public static void addHoldingConnectionThreadByConnection(Connection connection) { - int size = holdingConnectionThreadMap.size(); - if (size <= Config.DATASOURCE_MAXIMUN_POOL_SIZE()) { - holdingConnectionThreadMap.put(connection, Thread.currentThread()); - } else { - logger.error("持有数据库连接的线程Map大小为{}, 数据库连接池最大数量为{}", size, Config.DATASOURCE_MAXIMUN_POOL_SIZE()); - } - } - - public static Thread removeHoldingConnectionThreadByConnection(Connection connection) { - return holdingConnectionThreadMap.remove(connection); - } - - public static String getHoldingConnectionThreadStackTrace(Map holdingConnectionThreadMap) { - ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); - ThreadInfo[] threadInfos = mxBean.getThreadInfo(mxBean.getAllThreadIds(), 0); - Map threadInfoMap = new HashMap<>(); - for (ThreadInfo threadInfo : threadInfos) { - threadInfoMap.put(threadInfo.getThreadId(), threadInfo); - } - StringBuilder stringBuilder = new StringBuilder(System.lineSeparator()); - stringBuilder.append("总线程数为: ").append(holdingConnectionThreadMap.size()).append(System.lineSeparator()); - for (Map.Entry entry : holdingConnectionThreadMap.entrySet()) { - Thread thread = entry.getValue(); - stringBuilder.append("[").append(thread.getName()).append("] prio=").append(thread.getPriority()) - .append(" tid=").append(thread.getId()) - .append(" ").append(thread.getState()) - .append(" ").append(thread.isDaemon() ? "deamon" : "worker"); - ThreadInfo threadInfo = threadInfoMap.get(thread.getId()); - if (threadInfo != null) { - stringBuilder.append(" native=").append(threadInfo.isInNative()) - .append(", suspended=").append(threadInfo.isSuspended()) - .append(", block=").append(threadInfo.getBlockedCount()) - .append(", wait=").append(threadInfo.getWaitedCount()) - .append(" lock=").append(threadInfo.getLockName()) - .append(" owned by ").append(threadInfo.getLockOwnerName()) - .append(" (").append(threadInfo.getLockOwnerId()) - .append("), cpu=").append(mxBean.getThreadCpuTime(threadInfo.getThreadId()) / 1000000L) - .append(", user=").append(mxBean.getThreadUserTime(threadInfo.getThreadId()) / 1000000L); - } - stringBuilder.append(System.lineSeparator()); - StackTraceElement[] stackTrace = thread.getStackTrace(); - for (StackTraceElement stackTraceElement : stackTrace) { - stringBuilder.append(" at ").append(stackTraceElement).append(System.lineSeparator()); - } - } - return stringBuilder.toString(); - } - - private synchronized void audit(Map holdingConnectionThreadMap) { + /** + * 五分钟内只打印三次日志 + */ + private synchronized void audit() { + boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); long interval = currentTimeMillis - lastShowProcesslistMilliseconds; if (interval > TimeUnit.MINUTES.toMillis(1)) { - lastShowProcesslistMilliseconds = currentTimeMillis; - Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); - SQLTransientConnectionExceptionAuditLogger.error(getHoldingConnectionThreadStackTrace(holdingConnectionThreadMap)); + count = 0; + flag = true; + } else { + if (count < 3) { + count++; + flag = true; + } + } + if (flag) { + try { + Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); + StringWriter writer = new StringWriter(); + ThreadUtil.dumpTraces(writer); + writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); + for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { + Thread thread = entry.getKey(); + String value = entry.getValue(); + writer.write("[" + thread.getName() + "] 线程正在执行 " + value); + writer.write(System.lineSeparator()); + } + Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); + SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } } } @@ -106,14 +80,8 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 Connection conn = null; try { conn = super.getConnection(); - if (Config.DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE()) { - addHoldingConnectionThreadByConnection(conn); - conn = new NeatLogicConnection(conn); - } } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { - if (Config.DATASOURCE_CONNECTION_HOLDER_TRACK_ENABLE()) { - audit(new HashMap<>(holdingConnectionThreadMap)); - } + audit(); throw ex; } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java deleted file mode 100644 index 73ce446e4..000000000 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicConnection.java +++ /dev/null @@ -1,329 +0,0 @@ -/* - * - * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. - * This file is part of the NeatLogic software. - * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. - * You may use this file only in compliance with the License. - * See the LICENSE file distributed with this work for the full license text. - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * - */ - -package neatlogic.framework.store.mysql; - -import java.sql.*; -import java.util.Map; -import java.util.Properties; -import java.util.concurrent.Executor; - -public class NeatLogicConnection implements Connection { - - private final Connection connection; - - public NeatLogicConnection(Connection connection) { - this.connection = connection; - } - - @Override - public Statement createStatement() throws SQLException { - return this.connection.createStatement(); - } - - @Override - public PreparedStatement prepareStatement(String sql) throws SQLException { - return this.connection.prepareStatement(sql); - } - - @Override - public CallableStatement prepareCall(String sql) throws SQLException { - return this.connection.prepareCall(sql); - } - - @Override - public String nativeSQL(String sql) throws SQLException { - return this.connection.nativeSQL(sql); - } - - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - this.connection.setAutoCommit(autoCommit); - } - - @Override - public boolean getAutoCommit() throws SQLException { - return this.connection.getAutoCommit(); - } - - @Override - public void commit() throws SQLException { - this.connection.commit(); - } - - @Override - public void rollback() throws SQLException { - this.connection.rollback(); - } - - @Override - public void close() throws SQLException { - NeatLogicBasicDataSource.removeHoldingConnectionThreadByConnection(this.connection); - this.connection.close(); - } - - @Override - public boolean isClosed() throws SQLException { - return this.connection.isClosed(); - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return this.connection.getMetaData(); - } - - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - this.connection.setReadOnly(readOnly); - } - - @Override - public boolean isReadOnly() throws SQLException { - return this.connection.isReadOnly(); - } - - @Override - public void setCatalog(String catalog) throws SQLException { - this.connection.setCatalog(catalog); - } - - @Override - public String getCatalog() throws SQLException { - return this.connection.getCatalog(); - } - - @Override - public void setTransactionIsolation(int level) throws SQLException { - this.connection.setTransactionIsolation(level); - } - - @Override - public int getTransactionIsolation() throws SQLException { - return this.connection.getTransactionIsolation(); - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return this.connection.getWarnings(); - } - - @Override - public void clearWarnings() throws SQLException { - this.connection.clearWarnings(); - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return this.connection.createStatement(resultSetType, resultSetConcurrency); - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency); - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency); - } - - @Override - public Map> getTypeMap() throws SQLException { - return this.connection.getTypeMap(); - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - this.connection.setTypeMap(map); - } - - @Override - public void setHoldability(int holdability) throws SQLException { - this.connection.setHoldability(holdability); - } - - @Override - public int getHoldability() throws SQLException { - return this.connection.getHoldability(); - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return this.connection.setSavepoint(); - } - - @Override - public Savepoint setSavepoint(String name) throws SQLException { - return this.connection.setSavepoint(name); - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - this.connection.rollback(savepoint); - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - this.connection.releaseSavepoint(savepoint); - } - - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return this.connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return this.connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { - return this.connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); - } - - @Override - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - return this.connection.prepareStatement(sql, autoGeneratedKeys); - } - - @Override - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { - return this.connection.prepareStatement(sql, columnIndexes); - } - - @Override - public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { - return this.connection.prepareStatement(sql, columnNames); - } - - @Override - public Clob createClob() throws SQLException { - return this.connection.createClob(); - } - - @Override - public Blob createBlob() throws SQLException { - return this.connection.createBlob(); - } - - @Override - public NClob createNClob() throws SQLException { - return this.connection.createNClob(); - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return this.connection.createSQLXML(); - } - - @Override - public boolean isValid(int timeout) throws SQLException { - return this.connection.isValid(timeout); - } - - @Override - public void setClientInfo(String name, String value) throws SQLClientInfoException { - this.connection.setClientInfo(name, value); - } - - @Override - public void setClientInfo(Properties properties) throws SQLClientInfoException { - this.connection.setClientInfo(properties); - } - - @Override - public String getClientInfo(String name) throws SQLException { - return this.connection.getClientInfo(name); - } - - @Override - public Properties getClientInfo() throws SQLException { - return this.connection.getClientInfo(); - } - - @Override - public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - return this.connection.createArrayOf(typeName, elements); - } - - @Override - public Struct createStruct(String typeName, Object[] attributes) throws SQLException { - return this.connection.createStruct(typeName, attributes); - } - - @Override - public void setSchema(String schema) throws SQLException { - this.connection.setSchema(schema); - } - - @Override - public String getSchema() throws SQLException { - return this.connection.getSchema(); - } - - @Override - public void abort(Executor executor) throws SQLException { - this.connection.abort(executor); - } - - @Override - public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { - this.connection.setNetworkTimeout(executor, milliseconds); - } - - @Override - public int getNetworkTimeout() throws SQLException { - return this.connection.getNetworkTimeout(); - } - - @Override - public void beginRequest() throws SQLException { - this.connection.beginRequest(); - } - - @Override - public void endRequest() throws SQLException { - this.connection.endRequest(); - } - - @Override - public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLException { - return this.connection.setShardingKeyIfValid(shardingKey, superShardingKey, timeout); - } - - @Override - public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLException { - return this.connection.setShardingKeyIfValid(shardingKey, timeout); - } - - @Override - public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLException { - this.connection.setShardingKey(shardingKey, superShardingKey); - } - - @Override - public void setShardingKey(ShardingKey shardingKey) throws SQLException { - this.connection.setShardingKey(shardingKey); - } - - @Override - public T unwrap(Class iface) throws SQLException { - return this.connection.unwrap(iface); - } - - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - return this.connection.isWrapperFor(iface); - } -} diff --git a/src/main/java/neatlogic/framework/util/ThreadUtil.java b/src/main/java/neatlogic/framework/util/ThreadUtil.java new file mode 100644 index 000000000..9d86b1086 --- /dev/null +++ b/src/main/java/neatlogic/framework/util/ThreadUtil.java @@ -0,0 +1,59 @@ +/* + * + * * + * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * * This file is part of the NeatLogic software. + * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * * You may use this file only in compliance with the License. + * * See the LICENSE file distributed with this work for the full license text. + * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * + * + */ + +package neatlogic.framework.util; + +import neatlogic.framework.asynchronization.threadlocal.RequestContext; +import neatlogic.framework.common.config.Config; + +import java.io.IOException; +import java.io.Writer; +import java.lang.management.ManagementFactory; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class ThreadUtil { + + public static void dumpTraces(Writer writer) throws IOException { + ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); + ThreadInfo[] threadInfos = mxBean.getThreadInfo(mxBean.getAllThreadIds(), 0); + Map threadInfoMap = new HashMap<>(); + for (ThreadInfo threadInfo : threadInfos) { + threadInfoMap.put(threadInfo.getThreadId(), threadInfo); + } + Map stacks = Thread.getAllStackTraces(); + long now = System.currentTimeMillis(); + writer.write("=================" + stacks.size() + " thread of " + RequestContext.get().getRequest().getLocalAddr() + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " start.serverId is " + Config.SCHEDULE_SERVER_ID + "=================\n\n"); + for (Map.Entry entry : stacks.entrySet()) { + Thread thread = entry.getKey(); + writer.write("\"" + thread.getName() + "\" prio=" + thread.getPriority() + " tid=" + thread.getId() + " " + thread.getState() + " " + (thread.isDaemon() ? "deamon" : "worker")); + ThreadInfo threadInfo = threadInfoMap.get(thread.getId()); + if (threadInfo != null) { + writer.write(" native=" + threadInfo.isInNative() + ", suspended=" + threadInfo.isSuspended() + ", block=" + threadInfo.getBlockedCount() + ", wait=" + threadInfo.getWaitedCount()); + writer.write(" lock=" + threadInfo.getLockName() + " owned by " + threadInfo.getLockOwnerName() + " (" + threadInfo.getLockOwnerId() + "), cpu=" + (mxBean.getThreadCpuTime(threadInfo.getThreadId()) / 1000000L) + ", user=" + (mxBean.getThreadUserTime(threadInfo.getThreadId()) / 1000000L) + "\n"); + } + for (StackTraceElement element : entry.getValue()) { + writer.write("\t\t"); + writer.write(element.toString()); + writer.write("\n"); + } + writer.write("\n"); + } + writer.write("=================" + stacks.size() + " thread of " + RequestContext.get().getUrl() + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " end.=================\n\n"); + } +} -- Gitee From 4b6ca71fb1f0adb6945f42258b04be03148c02fe Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 18:35:46 +0800 Subject: [PATCH 04/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../threadlocal/InterceptorContext.java | 16 +++++++--------- .../framework/dao/config/mybatis-config.xml | 1 + .../dao/plugin/ExecutingSQLInterceptor.java | 16 +++++++--------- .../dao/plugin/ThreadLocalInterceptor.java | 16 +++++++--------- .../neatlogic/framework/util/ThreadUtil.java | 16 +++++++--------- 5 files changed, 29 insertions(+), 36 deletions(-) diff --git a/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java b/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java index 304b4993e..ab8bb9df8 100644 --- a/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java +++ b/src/main/java/neatlogic/framework/asynchronization/threadlocal/InterceptorContext.java @@ -1,14 +1,12 @@ /* * - * * - * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. - * * This file is part of the NeatLogic software. - * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. - * * You may use this file only in compliance with the License. - * * See the LICENSE file distributed with this work for the full license text. - * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ diff --git a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml index aec63c08b..72b4eca64 100644 --- a/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml +++ b/src/main/java/neatlogic/framework/dao/config/mybatis-config.xml @@ -41,6 +41,7 @@ along with this program. If not, see .--> + diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java index a0d117595..2162017ca 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java @@ -1,14 +1,12 @@ /* * - * * - * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. - * * This file is part of the NeatLogic software. - * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. - * * You may use this file only in compliance with the License. - * * See the LICENSE file distributed with this work for the full license text. - * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ diff --git a/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java index badb6933c..eae532ea4 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ThreadLocalInterceptor.java @@ -1,14 +1,12 @@ /* * - * * - * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. - * * This file is part of the NeatLogic software. - * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. - * * You may use this file only in compliance with the License. - * * See the LICENSE file distributed with this work for the full license text. - * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ diff --git a/src/main/java/neatlogic/framework/util/ThreadUtil.java b/src/main/java/neatlogic/framework/util/ThreadUtil.java index 9d86b1086..9203b81d1 100644 --- a/src/main/java/neatlogic/framework/util/ThreadUtil.java +++ b/src/main/java/neatlogic/framework/util/ThreadUtil.java @@ -1,14 +1,12 @@ /* * - * * - * * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. - * * This file is part of the NeatLogic software. - * * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. - * * You may use this file only in compliance with the License. - * * See the LICENSE file distributed with this work for the full license text. - * * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ -- Gitee From 3b48b515eb6b19dd5ddb4af606b065bca7770f6a Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 18:48:51 +0800 Subject: [PATCH 05/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../java/neatlogic/framework/util/ThreadUtil.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/neatlogic/framework/util/ThreadUtil.java b/src/main/java/neatlogic/framework/util/ThreadUtil.java index 9203b81d1..60b3be730 100644 --- a/src/main/java/neatlogic/framework/util/ThreadUtil.java +++ b/src/main/java/neatlogic/framework/util/ThreadUtil.java @@ -14,6 +14,7 @@ package neatlogic.framework.util; import neatlogic.framework.asynchronization.threadlocal.RequestContext; import neatlogic.framework.common.config.Config; +import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.Writer; @@ -36,7 +37,14 @@ public class ThreadUtil { } Map stacks = Thread.getAllStackTraces(); long now = System.currentTimeMillis(); - writer.write("=================" + stacks.size() + " thread of " + RequestContext.get().getRequest().getLocalAddr() + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " start.serverId is " + Config.SCHEDULE_SERVER_ID + "=================\n\n"); + String localAddr = StringUtils.EMPTY; + String url = StringUtils.EMPTY; + RequestContext requestContext = RequestContext.get(); + if (requestContext != null) { + localAddr = requestContext.getRequest().getLocalAddr(); + url = requestContext.getUrl(); + } + writer.write("\n=================" + stacks.size() + " thread of " + localAddr + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " start.serverId is " + Config.SCHEDULE_SERVER_ID + "=================\n\n"); for (Map.Entry entry : stacks.entrySet()) { Thread thread = entry.getKey(); writer.write("\"" + thread.getName() + "\" prio=" + thread.getPriority() + " tid=" + thread.getId() + " " + thread.getState() + " " + (thread.isDaemon() ? "deamon" : "worker")); @@ -52,6 +60,6 @@ public class ThreadUtil { } writer.write("\n"); } - writer.write("=================" + stacks.size() + " thread of " + RequestContext.get().getUrl() + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " end.=================\n\n"); + writer.write("=================" + stacks.size() + " thread of " + url + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " end.=================\n\n"); } } -- Gitee From 45b3d181164ce24b31be28d5f5d9ebb261f3598c Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 18:54:56 +0800 Subject: [PATCH 06/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/store/mysql/NeatLogicBasicDataSource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index fa48dab72..697861ad8 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -46,7 +46,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); long interval = currentTimeMillis - lastShowProcesslistMilliseconds; - if (interval > TimeUnit.MINUTES.toMillis(1)) { + if (interval > TimeUnit.MINUTES.toMillis(5)) { count = 0; flag = true; } else { -- Gitee From f17773dc1c7df7c76e8d7e7f0dfef6d4a9960984 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 19:04:49 +0800 Subject: [PATCH 07/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/store/mysql/NeatLogicBasicDataSource.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 697861ad8..4f5167e90 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -37,7 +37,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 // 保存上次查询ShowProcesslist命令的时间毫秒数 private static volatile long lastShowProcesslistMilliseconds = -1; - private static volatile int count = -1; + private static volatile int count = 0; /** * 五分钟内只打印三次日志 @@ -47,7 +47,8 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 long currentTimeMillis = System.currentTimeMillis(); long interval = currentTimeMillis - lastShowProcesslistMilliseconds; if (interval > TimeUnit.MINUTES.toMillis(5)) { - count = 0; + lastShowProcesslistMilliseconds = currentTimeMillis; + count = 1; flag = true; } else { if (count < 3) { -- Gitee From 41de6d96f630377e6094325b6201430100b2cb98 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Mon, 17 Nov 2025 19:06:37 +0800 Subject: [PATCH 08/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/store/mysql/NeatLogicBasicDataSource.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 4f5167e90..402664ccd 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -35,8 +35,8 @@ import java.util.concurrent.TimeUnit; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource private static final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); - // 保存上次查询ShowProcesslist命令的时间毫秒数 - private static volatile long lastShowProcesslistMilliseconds = -1; + // 保存上次输出日志的时间毫秒数 + private static volatile long lastAuditMilliseconds = -1; private static volatile int count = 0; /** @@ -45,9 +45,9 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 private synchronized void audit() { boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); - long interval = currentTimeMillis - lastShowProcesslistMilliseconds; + long interval = currentTimeMillis - lastAuditMilliseconds; if (interval > TimeUnit.MINUTES.toMillis(5)) { - lastShowProcesslistMilliseconds = currentTimeMillis; + lastAuditMilliseconds = currentTimeMillis; count = 1; flag = true; } else { -- Gitee From b04d43f79dacb57e705d3965c8e7b52f822a46be Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 08:42:06 +0800 Subject: [PATCH 09/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- src/main/java/neatlogic/framework/util/ThreadUtil.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/neatlogic/framework/util/ThreadUtil.java b/src/main/java/neatlogic/framework/util/ThreadUtil.java index 60b3be730..d606603d7 100644 --- a/src/main/java/neatlogic/framework/util/ThreadUtil.java +++ b/src/main/java/neatlogic/framework/util/ThreadUtil.java @@ -41,8 +41,12 @@ public class ThreadUtil { String url = StringUtils.EMPTY; RequestContext requestContext = RequestContext.get(); if (requestContext != null) { - localAddr = requestContext.getRequest().getLocalAddr(); - url = requestContext.getUrl(); + if (requestContext.getRequest() != null && StringUtils.isNotBlank(requestContext.getRequest().getLocalAddr())) { + localAddr = requestContext.getRequest().getLocalAddr(); + } + if (StringUtils.isNotBlank(requestContext.getUrl())) { + url = requestContext.getUrl(); + } } writer.write("\n=================" + stacks.size() + " thread of " + localAddr + " at " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z").format(new Date(now)) + " start.serverId is " + Config.SCHEDULE_SERVER_ID + "=================\n\n"); for (Map.Entry entry : stacks.entrySet()) { -- Gitee From 49df6a3da8deed2cb27d2ec302e37dd16f147017 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 11:00:02 +0800 Subject: [PATCH 10/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/store/mysql/NeatLogicBasicDataSource.java | 1 + src/main/java/neatlogic/framework/util/ThreadUtil.java | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 402664ccd..e6606c9e8 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -62,6 +62,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 StringWriter writer = new StringWriter(); ThreadUtil.dumpTraces(writer); writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); + writer.write(System.lineSeparator()); for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { Thread thread = entry.getKey(); String value = entry.getValue(); diff --git a/src/main/java/neatlogic/framework/util/ThreadUtil.java b/src/main/java/neatlogic/framework/util/ThreadUtil.java index d606603d7..7e4651bae 100644 --- a/src/main/java/neatlogic/framework/util/ThreadUtil.java +++ b/src/main/java/neatlogic/framework/util/ThreadUtil.java @@ -33,7 +33,9 @@ public class ThreadUtil { ThreadInfo[] threadInfos = mxBean.getThreadInfo(mxBean.getAllThreadIds(), 0); Map threadInfoMap = new HashMap<>(); for (ThreadInfo threadInfo : threadInfos) { - threadInfoMap.put(threadInfo.getThreadId(), threadInfo); + if (threadInfo != null) { + threadInfoMap.put(threadInfo.getThreadId(), threadInfo); + } } Map stacks = Thread.getAllStackTraces(); long now = System.currentTimeMillis(); -- Gitee From bfd5a6339b512611d440d756e4fb1948ad4b4f58 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 11:16:42 +0800 Subject: [PATCH 11/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../framework/dao/plugin/ExecutingSQLInterceptor.java | 8 ++++---- .../framework/store/mysql/NeatLogicBasicDataSource.java | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java index 2162017ca..f01211b4c 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java @@ -34,9 +34,9 @@ import java.util.concurrent.ConcurrentHashMap; }) public class ExecutingSQLInterceptor implements Interceptor { - private final static Map thread2ExecutingSQLMap = new ConcurrentHashMap<>(); + private final static Map thread2ExecutingSQLMap = new ConcurrentHashMap<>(); - public static Map getThread2ExecutingSQLMap() { + public static Map getThread2ExecutingSQLMap() { return new HashMap<>(thread2ExecutingSQLMap); } @@ -51,11 +51,11 @@ public class ExecutingSQLInterceptor implements Interceptor { if (interceptorContext != null) { MappedStatement mappedStatement = interceptorContext.getMappedStatement(); String sqlId = mappedStatement.getId(); - thread2ExecutingSQLMap.put(Thread.currentThread(), sqlId); + thread2ExecutingSQLMap.put(Thread.currentThread().getName(), sqlId); } return invocation.proceed(); } finally { - thread2ExecutingSQLMap.remove(Thread.currentThread()); + thread2ExecutingSQLMap.remove(Thread.currentThread().getName()); } } } diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index e6606c9e8..9f507dd78 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -58,15 +58,15 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 } if (flag) { try { - Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); + Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); StringWriter writer = new StringWriter(); ThreadUtil.dumpTraces(writer); writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); writer.write(System.lineSeparator()); - for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { - Thread thread = entry.getKey(); + for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { + String key = entry.getKey(); String value = entry.getValue(); - writer.write("[" + thread.getName() + "] 线程正在执行 " + value); + writer.write("[" + key + "] 线程正在执行 " + value); writer.write(System.lineSeparator()); } Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); -- Gitee From 9ab80f74bdc636c3f7971150af01b89e66ac70a7 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 12:01:35 +0800 Subject: [PATCH 12/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../store/mysql/NeatLogicBasicDataSource.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 9f507dd78..807921288 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -12,10 +12,13 @@ package neatlogic.framework.store.mysql; +import com.alibaba.fastjson.JSON; import com.zaxxer.hikari.HikariDataSource; +import com.zaxxer.hikari.HikariPoolMXBean; import neatlogic.framework.asynchronization.threadlocal.UserContext; import neatlogic.framework.common.util.RC4Util; import neatlogic.framework.dao.plugin.ExecutingSQLInterceptor; +import neatlogic.framework.dto.healthcheck.DataSourceInfoVo; import neatlogic.framework.util.ThreadUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; @@ -42,7 +45,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 /** * 五分钟内只打印三次日志 */ - private synchronized void audit() { + private synchronized void audit(DataSourceInfoVo dataSourceInfoVo, Map thread2ExecutingSQLMap) { boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); long interval = currentTimeMillis - lastAuditMilliseconds; @@ -58,7 +61,6 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 } if (flag) { try { - Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); StringWriter writer = new StringWriter(); ThreadUtil.dumpTraces(writer); writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); @@ -69,6 +71,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 writer.write("[" + key + "] 线程正在执行 " + value); writer.write(System.lineSeparator()); } + writer.write("连接池信息: " + JSON.toJSONString(dataSourceInfoVo)); Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); } catch (IOException e) { @@ -83,7 +86,17 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 try { conn = super.getConnection(); } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { - audit(); + DataSourceInfoVo dataSourceInfoVo = new DataSourceInfoVo(); + dataSourceInfoVo.setPoolName(this.getPoolName()); + HikariPoolMXBean hikariPoolMXBean = this.getHikariPoolMXBean(); + if (hikariPoolMXBean != null) { + dataSourceInfoVo.setIdleConnections(hikariPoolMXBean.getIdleConnections()); + dataSourceInfoVo.setActiveConnections(hikariPoolMXBean.getActiveConnections()); + dataSourceInfoVo.setThreadsAwaitingConnection(hikariPoolMXBean.getThreadsAwaitingConnection()); + dataSourceInfoVo.setTotalConnections(hikariPoolMXBean.getTotalConnections()); + } + Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); + audit(dataSourceInfoVo, thread2ExecutingSQLMap); throw ex; } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); -- Gitee From f6ac2a01a7b9f324095ed474a1e46a806932cd65 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 16:06:05 +0800 Subject: [PATCH 13/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../store/mysql/NeatLogicBasicDataSource.java | 86 ++++++++++--------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 807921288..e98703ac2 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -34,49 +34,45 @@ import java.sql.Statement; import java.util.Map; import java.util.Objects; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource private static final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); // 保存上次输出日志的时间毫秒数 - private static volatile long lastAuditMilliseconds = -1; - private static volatile int count = 0; - + private final static AtomicLong lastAuditMilliseconds = new AtomicLong(0); + private final static AtomicInteger count = new AtomicInteger(3); /** * 五分钟内只打印三次日志 */ - private synchronized void audit(DataSourceInfoVo dataSourceInfoVo, Map thread2ExecutingSQLMap) { - boolean flag = false; - long currentTimeMillis = System.currentTimeMillis(); - long interval = currentTimeMillis - lastAuditMilliseconds; - if (interval > TimeUnit.MINUTES.toMillis(5)) { - lastAuditMilliseconds = currentTimeMillis; - count = 1; - flag = true; - } else { - if (count < 3) { - count++; - flag = true; + private synchronized void audit() { + try { + DataSourceInfoVo dataSourceInfoVo = new DataSourceInfoVo(); + dataSourceInfoVo.setPoolName(this.getPoolName()); + HikariPoolMXBean hikariPoolMXBean = this.getHikariPoolMXBean(); + if (hikariPoolMXBean != null) { + dataSourceInfoVo.setIdleConnections(hikariPoolMXBean.getIdleConnections()); + dataSourceInfoVo.setActiveConnections(hikariPoolMXBean.getActiveConnections()); + dataSourceInfoVo.setThreadsAwaitingConnection(hikariPoolMXBean.getThreadsAwaitingConnection()); + dataSourceInfoVo.setTotalConnections(hikariPoolMXBean.getTotalConnections()); } - } - if (flag) { - try { - StringWriter writer = new StringWriter(); - ThreadUtil.dumpTraces(writer); - writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); + Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); + StringWriter writer = new StringWriter(); + ThreadUtil.dumpTraces(writer); + writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); + writer.write(System.lineSeparator()); + for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + writer.write("[" + key + "] 线程正在执行 " + value); writer.write(System.lineSeparator()); - for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - writer.write("[" + key + "] 线程正在执行 " + value); - writer.write(System.lineSeparator()); - } - writer.write("连接池信息: " + JSON.toJSONString(dataSourceInfoVo)); - Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); - SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); - } catch (IOException e) { - logger.error(e.getMessage(), e); } + writer.write("连接池信息: " + JSON.toJSONString(dataSourceInfoVo)); + Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); + SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); + } catch (IOException e) { + logger.error(e.getMessage(), e); } } @@ -86,17 +82,23 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 try { conn = super.getConnection(); } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { - DataSourceInfoVo dataSourceInfoVo = new DataSourceInfoVo(); - dataSourceInfoVo.setPoolName(this.getPoolName()); - HikariPoolMXBean hikariPoolMXBean = this.getHikariPoolMXBean(); - if (hikariPoolMXBean != null) { - dataSourceInfoVo.setIdleConnections(hikariPoolMXBean.getIdleConnections()); - dataSourceInfoVo.setActiveConnections(hikariPoolMXBean.getActiveConnections()); - dataSourceInfoVo.setThreadsAwaitingConnection(hikariPoolMXBean.getThreadsAwaitingConnection()); - dataSourceInfoVo.setTotalConnections(hikariPoolMXBean.getTotalConnections()); + // 五分钟内只打印三次日志 + boolean flag = false; + long currentTimeMillis = System.currentTimeMillis(); + long l = lastAuditMilliseconds.getAndUpdate(operand -> currentTimeMillis); + long interval = currentTimeMillis - l; + if (interval > TimeUnit.MINUTES.toMillis(5)) { + count.compareAndSet(3, 1); + flag = true; + } else { + if (count.get() < 3) { + count.incrementAndGet(); + flag = true; + } + } + if (flag) { + audit(); } - Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); - audit(dataSourceInfoVo, thread2ExecutingSQLMap); throw ex; } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); -- Gitee From 7a5656611b0f1bf505edfcd6c209ac57c1da1a0c Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Tue, 18 Nov 2025 18:01:21 +0800 Subject: [PATCH 14/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../store/mysql/NeatLogicBasicDataSource.java | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index e98703ac2..6668c9e43 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -40,9 +40,9 @@ import java.util.concurrent.atomic.AtomicLong; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource private static final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); - // 保存上次输出日志的时间毫秒数 - private final static AtomicLong lastAuditMilliseconds = new AtomicLong(0); - private final static AtomicInteger count = new AtomicInteger(3); + // 保存上次抛异常的时间毫秒数 + private final static AtomicLong lastThrowExceptionMillisecondsAtomicLong = new AtomicLong(0); + private final static AtomicInteger countAtomicInteger = new AtomicInteger(3); /** * 五分钟内只打印三次日志 */ @@ -85,14 +85,21 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 // 五分钟内只打印三次日志 boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); - long l = lastAuditMilliseconds.getAndUpdate(operand -> currentTimeMillis); - long interval = currentTimeMillis - l; + long lastThrowExceptionMilliseconds = lastThrowExceptionMillisecondsAtomicLong.getAndUpdate(operand -> currentTimeMillis); + long interval = currentTimeMillis - lastThrowExceptionMilliseconds; if (interval > TimeUnit.MINUTES.toMillis(5)) { - count.compareAndSet(3, 1); - flag = true; + if (countAtomicInteger.compareAndSet(3, 0)) { + flag = true; + } } else { - if (count.get() < 3) { - count.incrementAndGet(); + int count = countAtomicInteger.updateAndGet(operand -> { + if (operand < 3) { + return operand + 1; + } else { + return operand; + } + }); + if (count < 3) { flag = true; } } -- Gitee From 615a2f13073a7ae6a87e8cd7dc7d1396539f8c5f Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Wed, 19 Nov 2025 08:22:30 +0800 Subject: [PATCH 15/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../dao/plugin/ExecutingSQLInterceptor.java | 37 +++--- .../store/mysql/NeatLogicBasicDataSource.java | 74 +---------- .../SQLTransientConnectionExceptionAudit.java | 118 ++++++++++++++++++ 3 files changed, 136 insertions(+), 93 deletions(-) create mode 100644 src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java index f01211b4c..1b4cf8298 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java @@ -13,6 +13,9 @@ package neatlogic.framework.dao.plugin; import neatlogic.framework.asynchronization.threadlocal.InterceptorContext; +import neatlogic.framework.common.config.Config; +import neatlogic.framework.store.mysql.SQLTransientConnectionExceptionAudit; +import neatlogic.framework.util.SnowflakeUtil; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.Interceptor; @@ -22,9 +25,6 @@ import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.ResultHandler; import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; @Intercepts({ @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}), @@ -34,28 +34,23 @@ import java.util.concurrent.ConcurrentHashMap; }) public class ExecutingSQLInterceptor implements Interceptor { - private final static Map thread2ExecutingSQLMap = new ConcurrentHashMap<>(); - - public static Map getThread2ExecutingSQLMap() { - return new HashMap<>(thread2ExecutingSQLMap); - } - - public static void clearThread2ExecutingSQLMap() { - thread2ExecutingSQLMap.clear(); - } - @Override public Object intercept(Invocation invocation) throws Throwable { - try { - InterceptorContext interceptorContext = InterceptorContext.get(); - if (interceptorContext != null) { - MappedStatement mappedStatement = interceptorContext.getMappedStatement(); - String sqlId = mappedStatement.getId(); - thread2ExecutingSQLMap.put(Thread.currentThread().getName(), sqlId); + if (Config.DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE()) { + String key = Thread.currentThread().getName() + "#" + SnowflakeUtil.uniqueLong(); + try { + InterceptorContext interceptorContext = InterceptorContext.get(); + if (interceptorContext != null) { + MappedStatement mappedStatement = interceptorContext.getMappedStatement(); + String sqlId = mappedStatement.getId(); + SQLTransientConnectionExceptionAudit.putExecutingSQL(key, sqlId); + } + return invocation.proceed(); + } finally { + SQLTransientConnectionExceptionAudit.removeExecutingSQL(key); } + } else { return invocation.proceed(); - } finally { - thread2ExecutingSQLMap.remove(Thread.currentThread().getName()); } } } diff --git a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java index 6668c9e43..b124e19d5 100644 --- a/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java +++ b/src/main/java/neatlogic/framework/store/mysql/NeatLogicBasicDataSource.java @@ -12,69 +12,22 @@ package neatlogic.framework.store.mysql; -import com.alibaba.fastjson.JSON; import com.zaxxer.hikari.HikariDataSource; -import com.zaxxer.hikari.HikariPoolMXBean; import neatlogic.framework.asynchronization.threadlocal.UserContext; import neatlogic.framework.common.util.RC4Util; -import neatlogic.framework.dao.plugin.ExecutingSQLInterceptor; -import neatlogic.framework.dto.healthcheck.DataSourceInfoVo; -import neatlogic.framework.util.ThreadUtil; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.CannotGetJdbcConnectionException; -import java.io.IOException; -import java.io.StringWriter; import java.sql.Connection; import java.sql.SQLException; import java.sql.SQLTransientConnectionException; import java.sql.Statement; -import java.util.Map; import java.util.Objects; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的BasicDataSource - private static final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); - - // 保存上次抛异常的时间毫秒数 - private final static AtomicLong lastThrowExceptionMillisecondsAtomicLong = new AtomicLong(0); - private final static AtomicInteger countAtomicInteger = new AtomicInteger(3); - /** - * 五分钟内只打印三次日志 - */ - private synchronized void audit() { - try { - DataSourceInfoVo dataSourceInfoVo = new DataSourceInfoVo(); - dataSourceInfoVo.setPoolName(this.getPoolName()); - HikariPoolMXBean hikariPoolMXBean = this.getHikariPoolMXBean(); - if (hikariPoolMXBean != null) { - dataSourceInfoVo.setIdleConnections(hikariPoolMXBean.getIdleConnections()); - dataSourceInfoVo.setActiveConnections(hikariPoolMXBean.getActiveConnections()); - dataSourceInfoVo.setThreadsAwaitingConnection(hikariPoolMXBean.getThreadsAwaitingConnection()); - dataSourceInfoVo.setTotalConnections(hikariPoolMXBean.getTotalConnections()); - } - Map thread2ExecutingSQLMap = ExecutingSQLInterceptor.getThread2ExecutingSQLMap(); - StringWriter writer = new StringWriter(); - ThreadUtil.dumpTraces(writer); - writer.write("=================正在执行的SQL语句有" + thread2ExecutingSQLMap.size() + "条================="); - writer.write(System.lineSeparator()); - for (Map.Entry entry : thread2ExecutingSQLMap.entrySet()) { - String key = entry.getKey(); - String value = entry.getValue(); - writer.write("[" + key + "] 线程正在执行 " + value); - writer.write(System.lineSeparator()); - } - writer.write("连接池信息: " + JSON.toJSONString(dataSourceInfoVo)); - Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); - SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); - } catch (IOException e) { - logger.error(e.getMessage(), e); - } - } + private final Logger logger = LoggerFactory.getLogger(NeatLogicBasicDataSource.class); @Override public Connection getConnection() throws SQLException { @@ -82,30 +35,7 @@ public class NeatLogicBasicDataSource extends HikariDataSource {//替换dbcp2的 try { conn = super.getConnection(); } catch (CannotGetJdbcConnectionException | SQLTransientConnectionException ex) { - // 五分钟内只打印三次日志 - boolean flag = false; - long currentTimeMillis = System.currentTimeMillis(); - long lastThrowExceptionMilliseconds = lastThrowExceptionMillisecondsAtomicLong.getAndUpdate(operand -> currentTimeMillis); - long interval = currentTimeMillis - lastThrowExceptionMilliseconds; - if (interval > TimeUnit.MINUTES.toMillis(5)) { - if (countAtomicInteger.compareAndSet(3, 0)) { - flag = true; - } - } else { - int count = countAtomicInteger.updateAndGet(operand -> { - if (operand < 3) { - return operand + 1; - } else { - return operand; - } - }); - if (count < 3) { - flag = true; - } - } - if (flag) { - audit(); - } + SQLTransientConnectionExceptionAudit.audit(); throw ex; } conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); diff --git a/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java b/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java new file mode 100644 index 000000000..ce748d737 --- /dev/null +++ b/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java @@ -0,0 +1,118 @@ +/* + * + * Copyright (C) 2025 TechSure Co., Ltd. All Rights Reserved. + * This file is part of the NeatLogic software. + * Licensed under the NeatLogic Sustainable Use License (NSUL), Version 4.x – 2025. + * You may use this file only in compliance with the License. + * See the LICENSE file distributed with this work for the full license text. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +package neatlogic.framework.store.mysql; + +import com.alibaba.fastjson.JSON; +import com.zaxxer.hikari.HikariPoolMXBean; +import neatlogic.framework.common.config.Config; +import neatlogic.framework.dto.healthcheck.DataSourceInfoVo; +import neatlogic.framework.util.ThreadUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; + +public class SQLTransientConnectionExceptionAudit { + + private static final Logger logger = LoggerFactory.getLogger(SQLTransientConnectionExceptionAudit.class); + + // 保存上次抛异常的时间毫秒数 + private final static AtomicLong lastThrowExceptionMillisecondsAtomicLong = new AtomicLong(0); + private final static AtomicInteger countAtomicInteger = new AtomicInteger(3); + private final static Map executingSQLMap = new ConcurrentHashMap<>(); + + public static Map getExecutingSQL() { + return new HashMap<>(executingSQLMap); + } + + public static void clearExecutingSQL() { + executingSQLMap.clear(); + } + + public static void putExecutingSQL(String key, String value) { + executingSQLMap.put(key, value); + } + + public static void removeExecutingSQL(String key) { + executingSQLMap.remove(key); + } + + /** + * 五分钟内只打印三次日志 + */ + public static void audit() { + if (Config.DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE()) { + boolean flag = false; + long currentTimeMillis = System.currentTimeMillis(); + long lastThrowExceptionMilliseconds = lastThrowExceptionMillisecondsAtomicLong.getAndUpdate(operand -> currentTimeMillis); + long interval = currentTimeMillis - lastThrowExceptionMilliseconds; + if (interval > TimeUnit.MINUTES.toMillis(5)) { + if (countAtomicInteger.compareAndSet(3, 0)) { + flag = true; + } + } else { + int count = countAtomicInteger.updateAndGet(operand -> { + if (operand < 3) { + return operand + 1; + } else { + return operand; + } + }); + if (count < 3) { + flag = true; + } + } + if (flag) { + doAudit(); + } + } + } + + private static synchronized void doAudit() { + try { + DataSourceInfoVo dataSourceInfoVo = new DataSourceInfoVo(); + NeatLogicBasicDataSource datasource = DatasourceManager.getDatasource(); + dataSourceInfoVo.setPoolName(datasource.getPoolName()); + HikariPoolMXBean hikariPoolMXBean = datasource.getHikariPoolMXBean(); + if (hikariPoolMXBean != null) { + dataSourceInfoVo.setIdleConnections(hikariPoolMXBean.getIdleConnections()); + dataSourceInfoVo.setActiveConnections(hikariPoolMXBean.getActiveConnections()); + dataSourceInfoVo.setThreadsAwaitingConnection(hikariPoolMXBean.getThreadsAwaitingConnection()); + dataSourceInfoVo.setTotalConnections(hikariPoolMXBean.getTotalConnections()); + } + Map executingSQLSnapshotMap = new HashMap<>(executingSQLMap); + StringWriter writer = new StringWriter(); + ThreadUtil.dumpTraces(writer); + writer.write("=================正在执行的SQL语句有" + executingSQLSnapshotMap.size() + "条================="); + writer.write(System.lineSeparator()); + for (Map.Entry entry : executingSQLSnapshotMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + writer.write("[" + key + "] 线程正在执行 " + value); + writer.write(System.lineSeparator()); + } + writer.write("连接池信息: " + JSON.toJSONString(dataSourceInfoVo)); + Logger SQLTransientConnectionExceptionAuditLogger = LoggerFactory.getLogger("SQLTransientConnectionExceptionAudit"); + SQLTransientConnectionExceptionAuditLogger.error(writer.toString()); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + } +} -- Gitee From 83284bb8cb4c4f940dafa0fbc44afbf007214bcf Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Wed, 19 Nov 2025 08:23:24 +0800 Subject: [PATCH 16/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../neatlogic/framework/common/config/Config.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java index 4ed2d325a..5e143f451 100644 --- a/src/main/java/neatlogic/framework/common/config/Config.java +++ b/src/main/java/neatlogic/framework/common/config/Config.java @@ -54,7 +54,7 @@ public class Config { private static String DB_HOST; private static Integer DB_PORT; private static String DB_URL; - private static String DB_TRANSACTION_TIMEOUT;// 事务超时时间 + private static Integer DB_TRANSACTION_TIMEOUT;// 事务超时时间 private static int DATASOURCE_CONNECT_TIMEOUT;//连接池连接超时时间 private static Integer DATASOURCE_MAXIMUM_POOL_SIZE;//连接数 private static Integer DATASOURCE_MAX_LIFETIME;//控制池中连接的最大生存期 @@ -62,6 +62,7 @@ public class Config { private static Integer DATASOURCE_VALIDATION_TIMEOUT;//此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout private static Integer DATASOURCE_IDLE_TIMEOUT;//此属性控制允许连接在池中处于空闲状态的最长时间 private static Long DATASOURCE_KEEPALIVE_TIME;//此属性控制允许连接在池中心跳时间,不能比DATASOURCE_MAX_LIFETIME大 + private static Boolean DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE; // 开启数据库连接获取异常日志 private static String DATA_HOME;// 存储文件路径 private static String AUDIT_HOME;// 审计日志存储文件路径 private static int SERVER_HEARTBEAT_RATE;// 心跳频率 @@ -249,7 +250,7 @@ public class Config { return DB_URL; } - public static String DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 + public static Integer DB_TRANSACTION_TIMEOUT() {// root-context.xml中使用了该变量 return DB_TRANSACTION_TIMEOUT; } @@ -281,6 +282,10 @@ public class Config { return DATASOURCE_IDLE_TIMEOUT; } + public static Boolean DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE() { + return DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE; + } + public static String JMS_URL() { return JMS_URL; } @@ -628,14 +633,15 @@ public class Config { USER_EXPIRETIME = prop.getProperty("user.expiretime", "60"); LOGIN_CAPTCHA_EXPIRED_TIME = Integer.parseInt(prop.getProperty("login.captcha.expired.time", "60")); LOGIN_FAILED_TIMES_CAPTCHA = Integer.parseInt(prop.getProperty("login.failed.times.captcha", "3")); - DB_TRANSACTION_TIMEOUT = prop.getProperty("db.transaction.timeout"); + DB_TRANSACTION_TIMEOUT = Integer.parseInt(prop.getProperty("db.transaction.timeout")); DATASOURCE_CONNECT_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.connect.timeout", "5000")); - DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "250")); + DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "20")); DATASOURCE_KEEPALIVE_TIME = Long.parseLong(prop.getProperty("datasource.keepalive.time", "180000")); DATASOURCE_MAX_LIFETIME = Integer.parseInt(prop.getProperty("datasource.max.lifetime", "1800000")); DATASOURCE_MINIMUM_IDLE = Integer.parseInt(prop.getProperty("datasource.minimum.idle", "20")); DATASOURCE_VALIDATION_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.validation.timeout", "5000")); DATASOURCE_IDLE_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.idle.timeout", "600000")); + DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE = Boolean.parseBoolean(prop.getProperty("datasource.sql.transient.connection.exception.audit.enable", "true")); DB_URL = prop.getProperty("db.url"); DB_HOST = prop.getProperty("db.host", "localhost"); DB_PORT = Integer.parseInt(prop.getProperty("db.port", "3306")); -- Gitee From 1b8974c57d782fd7f38ad1a21c5997e2205ab536 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Wed, 19 Nov 2025 08:26:23 +0800 Subject: [PATCH 17/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- src/main/java/neatlogic/framework/common/config/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java index 5e143f451..ca48644c6 100644 --- a/src/main/java/neatlogic/framework/common/config/Config.java +++ b/src/main/java/neatlogic/framework/common/config/Config.java @@ -635,7 +635,7 @@ public class Config { LOGIN_FAILED_TIMES_CAPTCHA = Integer.parseInt(prop.getProperty("login.failed.times.captcha", "3")); DB_TRANSACTION_TIMEOUT = Integer.parseInt(prop.getProperty("db.transaction.timeout")); DATASOURCE_CONNECT_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.connect.timeout", "5000")); - DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "20")); + DATASOURCE_MAXIMUM_POOL_SIZE = Integer.parseInt(prop.getProperty("datasource.maximum.pool.size", "250")); DATASOURCE_KEEPALIVE_TIME = Long.parseLong(prop.getProperty("datasource.keepalive.time", "180000")); DATASOURCE_MAX_LIFETIME = Integer.parseInt(prop.getProperty("datasource.max.lifetime", "1800000")); DATASOURCE_MINIMUM_IDLE = Integer.parseInt(prop.getProperty("datasource.minimum.idle", "20")); -- Gitee From a486731c28458eeb6e4ea6ef6f892a45036f4b12 Mon Sep 17 00:00:00 2001 From: "1437892690@qq.com" <1437892690@qq.com> Date: Wed, 19 Nov 2025 10:32:08 +0800 Subject: [PATCH 18/18] =?UTF-8?q?[=E5=8A=9F=E8=83=BD]=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BA=93=E8=BF=9E=E6=8E=A5=E6=B1=A0=E8=8E=B7=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=E8=BF=9E=E6=8E=A5=E6=97=B6=E8=AE=B0=E5=BD=95=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 关联 #[1551594485678080]数据库连接池获取不到连接时记录线程信息 http://192.168.0.96:8090/demo/rdm.html#/story-detail/939050947543040/939050947543042/1551594485678080 --- .../java/neatlogic/framework/common/config/Config.java | 8 ++++---- .../framework/dao/plugin/ExecutingSQLInterceptor.java | 3 ++- .../store/mysql/SQLTransientConnectionExceptionAudit.java | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/neatlogic/framework/common/config/Config.java b/src/main/java/neatlogic/framework/common/config/Config.java index ca48644c6..ceed926a7 100644 --- a/src/main/java/neatlogic/framework/common/config/Config.java +++ b/src/main/java/neatlogic/framework/common/config/Config.java @@ -62,7 +62,7 @@ public class Config { private static Integer DATASOURCE_VALIDATION_TIMEOUT;//此属性控制测试连接是否活跃的最长时间。此值必须小于 connectionTimeout private static Integer DATASOURCE_IDLE_TIMEOUT;//此属性控制允许连接在池中处于空闲状态的最长时间 private static Long DATASOURCE_KEEPALIVE_TIME;//此属性控制允许连接在池中心跳时间,不能比DATASOURCE_MAX_LIFETIME大 - private static Boolean DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE; // 开启数据库连接获取异常日志 + private static Integer DATASOURCE_EXCEPTION_AUDIT; // 开启数据库连接获取异常日志 private static String DATA_HOME;// 存储文件路径 private static String AUDIT_HOME;// 审计日志存储文件路径 private static int SERVER_HEARTBEAT_RATE;// 心跳频率 @@ -282,8 +282,8 @@ public class Config { return DATASOURCE_IDLE_TIMEOUT; } - public static Boolean DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE() { - return DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE; + public static Integer DATASOURCE_EXCEPTION_AUDIT() { + return DATASOURCE_EXCEPTION_AUDIT; } public static String JMS_URL() { @@ -641,7 +641,7 @@ public class Config { DATASOURCE_MINIMUM_IDLE = Integer.parseInt(prop.getProperty("datasource.minimum.idle", "20")); DATASOURCE_VALIDATION_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.validation.timeout", "5000")); DATASOURCE_IDLE_TIMEOUT = Integer.parseInt(prop.getProperty("datasource.idle.timeout", "600000")); - DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE = Boolean.parseBoolean(prop.getProperty("datasource.sql.transient.connection.exception.audit.enable", "true")); + DATASOURCE_EXCEPTION_AUDIT = Integer.parseInt(prop.getProperty("datasource.exception.audit", "1")); DB_URL = prop.getProperty("db.url"); DB_HOST = prop.getProperty("db.host", "localhost"); DB_PORT = Integer.parseInt(prop.getProperty("db.port", "3306")); diff --git a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java index 1b4cf8298..b609e8ee0 100644 --- a/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java +++ b/src/main/java/neatlogic/framework/dao/plugin/ExecutingSQLInterceptor.java @@ -25,6 +25,7 @@ import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.session.ResultHandler; import java.sql.Statement; +import java.util.Objects; @Intercepts({ @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class}), @@ -36,7 +37,7 @@ public class ExecutingSQLInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { - if (Config.DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE()) { + if (Objects.equals(Config.DATASOURCE_EXCEPTION_AUDIT(), 1)) { String key = Thread.currentThread().getName() + "#" + SnowflakeUtil.uniqueLong(); try { InterceptorContext interceptorContext = InterceptorContext.get(); diff --git a/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java b/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java index ce748d737..022971998 100644 --- a/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java +++ b/src/main/java/neatlogic/framework/store/mysql/SQLTransientConnectionExceptionAudit.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -58,7 +59,7 @@ public class SQLTransientConnectionExceptionAudit { * 五分钟内只打印三次日志 */ public static void audit() { - if (Config.DATASOURCE_SQL_TRANSIENT_CONNECTION_EXCEPTION_AUDIT_ENABLE()) { + if (Objects.equals(Config.DATASOURCE_EXCEPTION_AUDIT(), 1)) { boolean flag = false; long currentTimeMillis = System.currentTimeMillis(); long lastThrowExceptionMilliseconds = lastThrowExceptionMillisecondsAtomicLong.getAndUpdate(operand -> currentTimeMillis); -- Gitee