PhänomenNachdem die Anwendung auf den MySQL-Treiber 8.0 aktualisiert wurde und die Parallelität hoch ist, werden die Überwachungspunkte geprüft. Die Zeit, die der Druid-Verbindungspool zum Herstellen der Verbindung und Ausführen von SQL benötigt, beträgt meist mehr als 200 ms. Beim Stresstest des Systems wurde festgestellt, dass eine große Anzahl von Threads blockiert war. Die Thread-Dump-Informationen lauten wie folgt: "http-nio-5366-exec-48" #210 Daemon prio=5 os_prio=0 tid=0x00000000023d0800 nid=0x3be9 wartet auf Monitoreintrag [0x00007fa4c1400000] java.lang.Thread.State: BLOCKIERT (auf dem Objektmonitor) bei org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader.loadClass(TomcatEmbeddedWebappClassLoader.java:66) - Wartet auf die Sperre <0x0000000775af0960> (ein java.lang.Object) bei org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1186) bei com.alibaba.druid.util.Utils.loadClass(Utils.java:220) bei com.alibaba.druid.util.MySqlUtils.getLastPacketReceivedTimeMs(MySqlUtils.java:372) Ursachenanalyseöffentliche Klasse MySqlUtils { öffentliche statische lange getLastPacketReceivedTimeMs (Verbindung conn) wirft SQLException { wenn (class_connectionImpl == null und !class_connectionImpl_Error) { versuchen { class_connectionImpl = Utils.loadClass("com.mysql.jdbc.MySQLConnection"); } catch (Auslösbarer Fehler) { class_connectionImpl_Error = wahr; } } wenn (class_connectionImpl == null) { Rückgabe -1; } wenn (method_getIO == null und !method_getIO_error) { versuchen { method_getIO = class_connectionImpl.getMethod("getIO"); } catch (Auslösbarer Fehler) { method_getIO_error = wahr; } } wenn (method_getIO == null) { Rückgabe -1; } wenn (class_MysqlIO == null && !class_MysqlIO_Error) { versuchen { Klasse_MysqlIO = Utils.loadClass("com.mysql.jdbc.MysqlIO"); } catch (Auslösbarer Fehler) { Klasse_MysqlIO_Error = wahr; } } wenn (class_MysqlIO == null) { Rückgabe -1; } if (method_getLastPacketReceivedTimeMs == null && !method_getLastPacketReceivedTimeMs_error) { versuchen { Methode Methode = class_MysqlIO.getDeclaredMethod("getLastPacketReceivedTimeMs"); Methode.SetAccessible(true); method_getLastPacketReceivedTimeMs = Methode; } catch (Auslösbarer Fehler) { method_getLastPacketReceivedTimeMs_error = true; } } wenn (method_getLastPacketReceivedTimeMs == null) { Rückgabe -1; } versuchen { Objekt connImpl = conn.unwrap(class_connectionImpl); wenn (connImpl == null) { Rückgabe -1; } Objekt mysqlio = method_getIO.invoke(connImpl); Lange ms = (Lang) method_getLastPacketReceivedTimeMs.invoke(mysqlio); return ms.langerWert(); } Fang (IllegalArgumentException e) { neue SQLException werfen("getLastPacketReceivedTimeMs-Fehler", e); } Fang (IllegalAccessException e) { neue SQLException werfen("getLastPacketReceivedTimeMs-Fehler", e); } Fang (InvocationTargetException e) { neue SQLException werfen("getLastPacketReceivedTimeMs-Fehler", e); } } Die Methode getLastPacketReceivedTimeMs() in MySqlUtils lädt die Klasse com.mysql.jdbc.MySQLConnection, aber der Klassenname wird im MySQL-Treiber 8.0 in com.mysql.cj.jdbc.ConnectionImpl geändert, sodass com.mysql.jdbc.MySQLConnection im MySQL-Treiber 8.0 nicht geladen werden kann. Wenn bei der Implementierung der Methode getLastPacketReceivedTimeMs() das Laden der Klasse durch Utils.loadClass("com.mysql.jdbc.MySQLConnection") fehlschlägt und eine Ausnahme auslöst, wird die Variable class_connectionImpl_Error geändert und die Klasse wird beim nächsten Aufruf nicht geladen. öffentliche Klasse Utils { öffentliche statische Klasse<?> loadClass(String Klassenname) { Klasse<?> clazz = null; wenn (Klassenname == null) { gibt null zurück; } versuchen { gibt Class.forName(Klassenname) zurück; } Fang (ClassNotFoundException e) { // überspringen } ctxClassLoader = Thread.currentThread().getContextClassLoader(); if (ctxClassLoader != null) { versuchen { clazz = ctxClassLoader.loadClass(Klassenname); } Fang (ClassNotFoundException e) { // überspringen } } Rückgabeklazz; } Allerdings wird die ClassNotFoundException auch in der loadClass()-Methode von Utils abgefangen. Dies bedeutet, dass loadClass() keine Ausnahme auslöst, wenn die Klasse nicht geladen werden kann. Dies führt dazu, dass die MySQLConnection-Klasse bei jedem Aufruf der getLastPacketReceivedTimeMs()-Methode einmal geladen wird. Aus den Thread-Dump-Informationen geht hervor, dass der Thread beim Aufruf der loadClass()-Methode von TomcatEmbeddedWebappClassLoader blockiert wird. öffentliche Klasse TomcatEmbeddedWebappClassLoader erweitert ParallelWebappClassLoader { öffentliche Klasse<?> loadClass(Stringname, Boolesche Auflösung) wirft ClassNotFoundException { synchronisiert (JreCompat.isGraalAvailable() ? dies : getClassLoadingLock(name)) { Klasse<?> Ergebnis = findExistingLoadedClass(Name); Ergebnis = (Ergebnis != null)? Ergebnis: doLoadClass(Name); wenn (Ergebnis == null) { wirf eine neue ClassNotFoundException(Name); } returniere „solveIfNecessary“ (Ergebnis, Lösung); } } Dies liegt daran, dass TomcatEmbeddedWebappClassLoader beim Laden einer Klasse eine synchronisierte Sperre hinzufügt, was dazu führt, dass com.mysql.jdbc.MySQLConnection bei jedem Aufruf der Methode getLastPacketReceivedTimeMs() einmal geladen wird, aber nie geladen wird. Beim Laden einer Klasse wird eine synchronisierte Sperre hinzugefügt, sodass es zu Threadblockierungen und Leistungseinbußen kommt. Aufrufzeit der Methode getLastPacketReceivedTimeMs()öffentliche abstrakte Klasse DruidAbstractDataSource erweitert WrapperAdapter implementiert DruidAbstractDataSourceMBean, DataSource, DataSourceProxy, Serializable { geschützter Boolescher Wert testConnectionInternal(DruidConnectionHolder-Inhaber, Verbindungsverbindung) { Zeichenfolge sqlFile = JdbcSqlStat.getContextSqlFile(); Zeichenfolge sqlName = JdbcSqlStat.getContextSqlName(); if (sqlFile != null) { JdbcSqlStat.setContextSqlFile(null); } if (sqlName != null) { JdbcSqlStat.setContextSqlName(null); } versuchen { if (validConnectionChecker != null) { Boolescher Wert gültig = validConnectionChecker.isValidConnection(conn, validationQuery, validationQueryTimeout); lange currentTimeMillis = System.currentTimeMillis(); wenn (Inhaber != null) { Inhaber.letzteValidTimeMillis = aktuelleTimeMillis; Inhaber.lastExecTimeMillis = aktuelleTimeMillis; } if (valid && isMySql) { // nicht ausgenommener Zweig lange lastPacketReceivedTimeMs = MySqlUtils.getLastPacketReceivedTimeMs(conn); wenn (lastPacketReceivedTimeMs > 0) { lange mysqlIdleMillis = aktuelleZeitMillis - letztePaketempfangszeitMs; wenn (lastPacketReceivedTimeMs > 0 // && mysqlIdleMillis >= timeBetweenEvictionRunsMillis) { Verbindung verwerfen(Inhaber); String errorMsg = "Lange Zeit keine empfangene Verbindung verwerfen." + ", jdbcUrl: " + jdbcUrl + ", jdbcUrl: " + jdbcUrl + ", lastPacketReceivedIdleMillis: " + mysqlIdleMillis; LOG.error(Fehlermeldung); gibt false zurück; } } } if (gültig && bei schwerwiegendem Fehler) { sperre.sperre(); versuchen { if (bei schwerwiegendem Fehler) { bei schwerwiegendem Fehler = falsch; } Endlich sperren.entsperren(); } } Rückgabe gültig; } wenn (conn.isClosed()) { gibt false zurück; } if (null == Validierungsabfrage) { gibt true zurück; } Anweisung stmt = null; Ergebnismenge rset = null; versuchen { stmt = conn.createStatement(); wenn (getValidationQueryTimeout() > 0) { stmt.setQueryTimeout(validationQueryTimeout); } rset = stmt.executeQuery(Validierungsabfrage); wenn (!rset.next()) { gibt false zurück; } Endlich JdbcUtils.close(rset); JdbcUtils.close(stmt); } if (bei schwerwiegendem Fehler) { sperre.sperre(); versuchen { if (bei schwerwiegendem Fehler) { bei schwerwiegendem Fehler = falsch; } Endlich sperren.entsperren(); } } gibt true zurück; } fangen (Wurfbeispiel) { // überspringen gibt false zurück; Endlich if (sqlFile != null) { JdbcSqlStat.setContextSqlFile(sqlFile); } if (sqlName != null) { JdbcSqlStat.setContextSqlName(sqlName); } } } Die Methode getLastPacketReceivedTimeMs() wird nur in der Methode testConnectionInternal() von DruidAbstractDataSource aufgerufen. testConnectionInternal() wird verwendet, um zu prüfen, ob die Verbindung gültig ist. Diese Methode kann aufgerufen werden, wenn eine Verbindung hergestellt oder zurückgegeben wird, abhängig von den Parametern, die Druid verwendet, um zu prüfen, ob die Verbindung gültig ist. Parameter für Druid, um festzustellen, ob die Verbindung gültig ist:
LösungEs wurde bestätigt, dass dieser Fehler bei Verwendung von Druid 1.x Version <= 1.1.22 auftritt. Die Lösung besteht darin, auf Druid 1.x Version >= 1.1.23 oder Druid 1.2.x Version zu aktualisieren. GitHub-Problem: https://github.com/alibaba/druid/issues/3808 Dies ist das Ende dieses Artikels über den Druid-Verbindungspool mit niedriger Version + MySQL-Treiber 8.0, der Thread-Blockierungen und eingeschränkte Leistung verursacht. Weitere Informationen zum Druid-Verbindungspool mit niedriger Version des MySQL-Treibers 8.0 finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird! Das könnte Sie auch interessieren:
|
<<: IDEA verwendet das Docker-Plugin (Tutorial für Anfänger)
>>: Vue implementiert eine einfache Notizblockfunktion
1. Hintergrund Ich habe vor Kurzem eine Website n...
Warum sollten wir CSS-Animationen anstelle von JS...
Elasticsearch erfreut sich derzeit großer Beliebt...
In diesem Artikel erfahren Sie, wie Sie Excel-Dat...
Inhaltsverzeichnis 1. Hash-Tabellenprinzip 2. Das...
1. Schwebendes Layout 1. Lassen Sie zuerst das Di...
CJK ist die Abkürzung für CJK Unified Ideographs,...
Bei der Installation von MySQL sind mir mehrere P...
Wie kann die Seiten-Rendering-Zeit im Browser so ...
1. Laden Sie das Alpenbild herunter [root@docker4...
Vorwort Die MySQL-Datenbanksperre ist ein wichtig...
Inhaltsverzeichnis 1. Zeigen Sie die Speicher-Eng...
Dieser Artikel stellt hauptsächlich ein Beispiel ...
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ...
Inhaltsverzeichnis Gemeinsame Funktionen von Linu...