L'analyse de fermeture de la Connexion Exception dans Spring/JPA/Mysql/Tomcat app
PROBLÈME
Récemment, j'ai été chargé d'une application web Java avec le code déjà écrit et en place. L'application reçoit moyennement élevés de trafic et a des heures de pointe de trafic entre 11h à 3h tous les jours.
L'application utilise Spring, JPA(Hibernate), de bases de données MYSQL.
Le printemps a été configuré pour utiliser tomcat pool de connexions jdbc pour établir des connexions à la bd.
(Les détails de la configuration à la fin du post)
Depuis quelques jours, lors de l'application de la charge de pointe heures, la demande a été de descendre en raison de tomcat va de répondre aux demandes. Il est nécessaire de tomcat être redémarré plusieurs fois.
En passant par le tomcat catalina.rondins, j'ai remarqué un tas de
Caused by: java.sql.SQLException: Connection has already been closed.
at org.apache.tomcat.jdbc.pool.ProxyConnection.invoke(ProxyConnection.java:117)
at org.apache.tomcat.jdbc.pool.JdbcInterceptor.invoke(JdbcInterceptor.java:109)
at org.apache.tomcat.jdbc.pool.DisposableConnectionFacade.invoke(DisposableConnectionFacade.java:80)
at com.sun.proxy.$Proxy28.prepareStatement(Unknown Source)
at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:505)
at org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:423)
at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:139)
at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1547)
at org.hibernate.loader.Loader.doQuery(Loader.java:673)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
... 115 more
Elles apparaissent souvent juste avant le crash.
Aller plus loin, plus avant ces exceptions près, j'ai remarqué un tas de Connexions être abandonné juste avant la fermeture de la Connexion des exceptions.
WARNING: Connection has been abandoned PooledConnection[com.mysql.jdbc.Connection@543c2ab5]:java.lang.Exception
at org.apache.tomcat.jdbc.pool.ConnectionPool.getThreadDump(ConnectionPool.java:1065)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:782)
at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:618)
at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:188)
at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:128)
at org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider.getConnection(InjectedDataSourceConnectionProvider.java:47)
at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:423)
at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:144)
at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:139)
Ces semblent souvent juste avant la fermeture de la Connexion des exceptions. Et ceux-ci semblent être les premiers symptômes d'une catastrophe imminente dans les journaux.
ANALYSE
En passant par les journaux, j'ai décidé de voir si il n'y a aucun pool de connexion de configuration/configuration de mysql qui pourraient être à l'origine du problème.
Est passé par un couple d'excellents articles qui montrent le réglage de la piscine pour un environnement de Production. Liens Un & Deux
Passant par ces articles, j'ai remarqué que:
- La ligne ci-dessous dans JHanik l'article (lien 1) mentionne cette
Réglage de la valeur de abandonWhenPercentageFull à 100 signifie que les connexions ne sont pas > considérée comme abandonnée si nous avons atteint notre maxActive limite.
J'ai pensé que cela pourrait être important dans mon cas, parce que je vois beaucoup de connexions être abandonné.
- Mon max_connections configuration ne correspond pas à ce qui est recommandé (lien 2)
mysql max_connections doit être égale à max_active+max_idle
CE QUE J'AI ESSAYÉ
Donc, conformément aux recommandations de l'article, j'ai fait deux choses:
- Changé abandonWhenPercentageFull à 100
- Dans mon serveur MYSQL, max_connections avait été fixé à 500. Il a augmenté à 600
Dans mes paramètres du pool de connexions, max_active était de 200 et max_idle était de 50.
Changé max_active=350, max_idle = 250
CELA N'AIDE PAS
Le lendemain, les observations suivantes ont été faites pendant les heures de pointe:
- Tomcat de ne pas descendre. L'app est resté sur place à travers des heures de pointe.
Cependant, la performance est allé de mal en pis et puis l'application a été à peine utilisable, même si elle n'a pas vraiment aller vers le bas. - DB pool de Connexion, mais ont augmenté en taille, a été totalement utilisé, et j'ai pu voir à 350 connexions actives à la DB à un point.
ENFIN, MA QUESTION:
Clairement ressemble, il y a des problèmes avec la façon DB connexions sont réalisées à partir de l'application serveur.
J'ai donc deux orientations à prendre cette analyse de l'avant.
Ma question est de savoir lequel de ces dois-je prendre?
1. Le problème n'est pas avec les paramètres du pool de connexions. Le code est ce qui est à l'origine du problème
Il pourrait y avoir des endroits dans le code où DB connections ne sont pas fermés. Qui est à l'origine du nombre élevé de connexions ouvertes.
Le code utilise une GenericDao qui se prolonge dans chaque classe Dao. Le GenericDao utilise du Printemps JpaTemplate pour aller chercher un EntityManager instance, qui à son tour est utilisé pour tous les DB opérations. Ma compréhension est à l'aide de la JpaTemplate gère le nitty gritty de clôture DB connexions à l'interne.
Alors, où, exactement, dois-je recherche de la possibilité de connexion à des fuites?
2. Le problème est avec le pool de connexions/mysql config paramètres. Cependant, les optimisations que j'ai mis dans la nécessité d'être à l'écoute de plus amples
Si oui, quels paramètres dois-je regarder?
Devrais-je être à la collecte de certaines données à utiliser pour déterminer les valeurs appropriées pour mon pool de connexion. (Pour exemple, pour max_active, max_idle, max_connections)
Addendum: Compléter le Pool de Connexion de configuration
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://xx.xx.xx.xx" />
<property name="username" value="xxxx" />
<property name="password" value="xxxx" />
<property name="initialSize" value="10" />
<property name="maxActive" value="350" />
<property name="maxIdle" value="250" />
<property name="minIdle" value="90" />
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="60" />
<property name="abandonWhenPercentageFull" value="100" />
<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
<property name="validationInterval" value="30000" />
<property name="logAbandoned" value="true" />
<property name="jmxEnabled" value="true" />
</bean>
OriginalL'auteur ncmadhan | 2014-02-11
Vous devez vous connecter pour publier un commentaire.
C'est terriblement en retard pour l'OP, mais peut-être que cela aidera quelqu'un d'autre dans l'avenir:
Je suis tombé sur quelque chose de semblable à cela dans un environnement de production à long exécution de travaux par lots. Le problème est que si votre code a besoin d'une connexion plus longtemps que la durée spécifiée par la propriété:
name="removeAbandonedTimeout" value="60
et que vous avez activé:
<property name="removeAbandoned" value="true" />
elle sera déconnecté pendant le traitement au bout de 60 secondes. Une solution de contournement possible (qui ne fonctionne pas pour moi) est de permettre à l'intercepteur:
jdbcInterceptors="ResetAbandonedTimer"
Ceci permet de réinitialiser l'abandon de la minuterie pour que la connexion pour chaque lecture/écriture qui se produit. Malheureusement, dans mon cas, le traitement serait encore parfois prendre plus de temps que le délai d'attente avant que quelque chose a été lue/écrite à la base de données. J'ai donc été obligé de remonter la durée du délai d'attente, ou de désactiver le removeAbandonded (j'ai choisi la première solution).
Espère que cela aide quelqu'un d'autre s'ils ont quelque chose de semblable!
Cela m'a vraiment aidé à comprendre la sémantique d'une des implications de "removeAbandoned". À partir de mes observations, je soupçonne que l'abandonedTimeout est cumulée sur plusieurs contrats de location de la même connexion.
OriginalL'auteur Ryan P.
M'a récemment demandé de chercher pourquoi le système de production va parfois jusqu'à. Je voulais partager mes conclusions, puisqu'il implique une corrélation des événements à prendre une machine virtuelle java tomcat app avec JDBC questions exposées ci-dessus pour en fait le plantage de l'application. C'est en utilisant mysql comme un backend donc sans doute plus utile pour ce scénario, mais si la question a frappé sur une autre plateforme cause susceptible d'être la même.
Tout simplement par l'obtention de fermeture de la connexion n'implique pas l'application est cassé
C'est en vertu d'un graal de la demande, mais sera par rapport à l'ensemble de la JVM liées apps:
tomcat/context.xml
db configuration, de l'avis de très petites db piscine etremoveAbandonedTimeout="10"
vous à droite, nous voulons des choses pour casserUn quartz de travail qui s'exécute à chaque minute, non pas que cela importe l'application que je pense meurt sur la première tentative:
Maintenant notre testService avec des commentaires, veuillez suivre les commentaires:
C'est seulement lorsque la requête puis prend plus de temps que prévu du temps, mais il y a un autre élément, la requête elle-même, puis a frappé assis sur MYSQL, même si il est tué. MYSQL est-il manger à l'écart de traitement.
Je pense que ce qui se passe est
Si par ce temps tâche1 n'a pas achevé puis nous n'avons plus rien dans la piscine bien app est tout simplement cassé maintenant.. jdbc erreurs jeté etc.. jamais l'esprit si elle se termine après le crash..
Vous pouvez surveiller ce qui se passe par la vérification de mysql
Il est apparu à courir pour une plus longue période qui va à l'encontre de ce qu'ils ont suggéré cette valeur doit être fait, mais là encore peut-être que ce n'est pas vraiment basé sur tout cela, et se rapporte à un problème ailleurs.
Alors que les tests remarqué qu'il y a deux états: l'Envoi des données /de l'Envoi au client:
Sseconds plus tard:
Il se peut qu'un script doit être créé pour surveiller la liste des processus et peut-être un approfondissement de l'ensemble de résultats contenant des requêtes exactes de course de travailler vos requêtes événements est en train de tuer votre application
OriginalL'auteur Vahid
C'est probablement à l'origine de votre problème, vous ne devriez pas utiliser la
JpaTemplate
pour obtenir leEntityManager
cela vous donnera de l'onu non géréEntitymanager
. En fait, vous ne devriez pas être à l'aide deJpaTemplate
à tous.Il est recommandé d'écrire daos basée sur la plaine
EntityManager
API et simplement injecter laEntityManager
comme vous le feriez normalement faire (avec@PersistenceContext
).Si vous voulez vraiment utiliser le
JpaTemplate
utiliser leexecute
méthode et passer dans unJpaCallback
qui vous donnera un gérésEntityManager
.Assurez-vous également que vous avez configuré les transactions correctement sans une tx de configuration des connexions ne sera pas fermé, comme le printemps ne sait pas qu'il doit fermer la connexion.
OriginalL'auteur M. Deinum