5 votes

Test de charge de base de données Spring : Comment libérer les connexions

J'essayais simplement de simplifier mon problème dans une petite application de démonstration.
Il semble que la connexion ne se ferme pas tant que le thread n'est pas terminé. Jetez un œil

Informations sur les dépendances :

Moteur de servlet: Apache Tomcat/8.5.5
Fournisseur JPA: Hibernate

Mon fichier pom.xml :

    4.0.0

    ...
    ***-service
    0.0.1
    jar

    ...
    ...

        org.springframework.boot
        spring-boot-starter
        1.4.1.RELEASE

        UTF-8
        UTF-8
        1.8
        Brixton.SR2

            org.springframework.boot
            spring-boot-starter-thymeleaf

            org.springframework.boot
            spring-boot-starter-actuator

            org.springframework.boot
            spring-boot-starter-data-jpa

            org.springframework.boot
            spring-boot-starter-data-rest

            org.springframework.boot
            spring-boot-starter-web

            org.springframework.cloud
            spring-cloud-starter-eureka

            mysql
            mysql-connector-java
            runtime

            org.springframework.boot
            spring-boot-starter-test
            test

            org.springframework.restdocs
            spring-restdocs-mockmvc
            test

            com.fasterxml.jackson.core
            jackson-databind

            com.fasterxml.jackson.dataformat
            jackson-dataformat-xml

            org.jsoup
            jsoup
            1.9.2

                org.springframework.cloud
                spring-cloud-dependencies
                ${spring.cloud.release.version}
                pom
                import

        ${artifactId}

                org.springframework.boot
                spring-boot-maven-plugin

                    true
                    ${dir}

J'ai un serveur Spring Boot avec MYSQL comme base de données. Lorsque je teste la charge (JMeter) avec plus de 100 threads/utilisateurs, j'obtiens cette exception :

org.apache.tomcat.jdbc.pool.PoolExhaustedException: [http-nio-8015-exec-195] Timeout: Pool empty. Impossible de récupérer une connexion en 60 secondes, aucune disponible[taille:50; occupée:50; disponible:0; dernièreattente:60000].

Ma configuration des sources de données :

spring.datasource.tomcat.minIdle = 0
spring.datasource.tomcat.maxIdle = 10
spring.datasource.tomcat.maxActive = 50
spring.datasource.tomcat.maxWait = 60000
spring.datasource.tomcat.testOnBorrow = true
spring.datasource.tomcat.timeBetweenEvictionRunsMillis = 1800000 // Mis à jour
spring.datasource.tomcat.numTestsPerEvictionRun = 50  // Mis à jour
spring.datasource.tomcat.minEvictableIdleTimeMillis = 10
spring.datasource.tomcat.validationQuery = SELECT 1
spring.datasource.tomcat.testWhileIdle = true

La seule requête sur la base de données est un simple SELECT * FROM TABLE WHERE deletedat IS NULL. Elle retourne environ 4000 entrées en environ 0,01 seconde (via le terminal mysql).
J'ai installé Neor Profile SQL pour profiler ma base de données MYSQL. Lorsque je vérifie tous les processus, les processus sur mon schéma sont tous en veille. Il semble que ma configuration ne libère pas ces connexions.

J'utilise une simple interface qui étend CrudRepository :

public interface MyRepository extends CrudRepository {
    @Query("SELECT m from MyModel m WHERE m.deletedAt IS NULL")
    List findWhereDeletedAtIsNull();
}

et l'utilise

List myModelList = myRepository.findWhereDeletedAtIsNull();

Qu'est-ce que j'oublie?
Il semble que la configuration ou le code doivent être adaptés pour libérer les connexions.

Configuration Hibernate :

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

7voto

skadya Points 3329

Votre configuration JPA enregistrant l'OpenEntityManagerInViewInterceptor qui lie un EntityManager JPA au thread de travail pour l'ensemble du traitement de la requête. Ainsi, les connexions à la base de données sont fermées (ou retournées au pool) après la fin de la méthode de service (com.example.api.UserService.getAllUser()).

Cela garde votre connexion à la base de données ouverte plus longtemps et votre pool de connexions s'épuise pendant la charge.

Pour résoudre votre problème, vous pouvez désactiver l'enregistrement automatique du filtre OpenEntityManagerInViewInterceptor en ajoutant la propriété ci-dessous dans votre fichier application.properties

spring.jpa.open-in-view=false

Cette propriété est true par défaut.

Voir les problèmes connexes: 1. Considérez de ne pas enregistrer l'OpenEntityManagerInViewInterceptor par défaut

1voto

Ce qui est critique, ici, c'est d'activer logAbandoned. De cette manière, le pool JDBC de Tomcat vous indiquera les connexions sont allouées mais ne sont pas retournées. Pour l'instant, vous essayez simplement de récupérer le problème en le dissimulant : forcer le pool à libérer des connexions qui n'ont pas été correctement retournées.

Le java.sql.SQLException: Connection has already been closed se produit parce que vous avez une requête qui prend plus de 50 secondes pour s'achever, mais elle est correctement nettoyée par votre propre code. Ainsi, après 50 secondes, le pool ferme la connexion (car elle a été "abandonnée") et puis lorsque votre code reprend le contrôle, il ferme également la connexion. Envisagez d'augmenter le délai d'abandon des connexions.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X