Spring Boot JPA - configuration de la reconnexion automatique

107

J'ai une jolie petite application Web Spring Boot JPA. Il est déployé sur Amazon Beanstalk et utilise un Amazon RDS pour la persistance des données. Il n'est cependant pas utilisé si souvent et échoue donc après un certain temps avec ce type d'exception:

com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: le dernier paquet reçu avec succès du serveur remonte à 79 870 633 millisecondes.
Le dernier paquet envoyé avec succès au serveur remonte à 79 870 634 millisecondes. est plus longue que la valeur configurée par le serveur de «wait_timeout». Vous devez envisager d'expirer et / ou de tester la validité de la connexion avant de l'utiliser dans votre application, d'augmenter les valeurs configurées par le serveur pour les délais d'expiration du client ou d'utiliser la propriété de connexion Connector / J 'autoReconnect = true' pour éviter ce problème.

Je ne sais pas comment configurer ce paramètre et je ne peux pas trouver d'informations à ce sujet sur http://spring.io (un très bon site cependant). Quelles sont les idées ou les pointeurs vers des informations?

stoffer
la source
Utilisez-le pour imprimer votre DataSourceet vérifier ses propriétés. stackoverflow.com/a/36586630/148844 Spring Boot ne configurera pas automatiquement le DataSourcesi vous en avez @Beansqui définissent un DataSource. docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/…
Chloe

Réponses:

141

Je suppose que le démarrage configure le DataSourcepour vous. Dans ce cas, et puisque vous utilisez MySQL, vous pouvez ajouter ce qui suit à votre application.propertiesjusqu'à 1.3

spring.datasource.testOnBorrow=true
spring.datasource.validationQuery=SELECT 1

Comme djxak noté dans le commentaire, 1.4+ définit les espaces de noms spécifiques pour les quatre connexions pools supports Spring Boot: tomcat, hikari, dbcp, dbcp2( dbcpest OBSOLETE 1,5). Vous devez vérifier le pool de connexions que vous utilisez et vérifier si cette fonctionnalité est prise en charge. L'exemple ci-dessus était pour tomcat, vous devrez donc l'écrire comme suit dans 1.4+:

spring.datasource.tomcat.testOnBorrow=true 
spring.datasource.tomcat.validationQuery=SELECT 1

Notez que l'utilisation de autoReconnectn'est pas recommandée :

L'utilisation de cette fonctionnalité n'est pas recommandée, car elle a des effets secondaires liés à l'état de session et à la cohérence des données lorsque les applications ne gèrent pas correctement les SQLExceptions, et est uniquement conçue pour être utilisée lorsque vous ne pouvez pas configurer votre application pour gérer les SQLExceptions résultant de connexions mortes et périmées correctement.

Stéphane Nicoll
la source
8
c'est parce que nous avons harmonisé la façon dont nous écrivons les clés dans la documentation. Nous avons toujours utilisé un détendue liant de façon à la fois spring.datasource.testOnBorrowet spring.datasource.test-on-borrowfonctionnera très bien. Consultez la documentation pour plus de détails.
Stephane Nicoll
17
Puisqu'il peut en confondre les autres: SELECT 1garantit que la connexion a été testée avant d'être transmise à l'application. En utilisant testOnBorrow = true, les objets seront validés avant d'être empruntés à la piscine. Si l'objet ne parvient pas à se valider, il sera supprimé du pool et tentera d'en emprunter un autre. REMARQUE - pour qu'une valeur vraie ait un effet, le paramètre validationQuery doit être défini sur une chaîne non nulle.
Rick
14
Avertissement! Au printemps Boot 1.4+ cela a été changé : on a défini de nouveaux espaces de noms spécifiques pour les quatre connexions piscines supports à ressort: tomcat, hikari, dbcp, dbcp2. Ainsi, par exemple, pour tomcat-jdbcconnection-pool, les propriétés doivent être: spring.datasource.tomcat.testOnBorrow=trueet spring.datasource.tomcat.validationQuery=SELECT 1.
Ruslan Stelmachenko
1
Si je configure moi-même deux sources de données différentes, comment puis-je fournir ces configurations? Dois-je fournir cette configuration à la fois pour la source de données comme spring.datasource.mydatasource1.tomcat.testOnBorrow = true spring.datasource.mydatasource1.tomcat.validationQuery = SELECT 1 spring.datasource.mydatasource2.tomcat.testOnBorrow = true spring.datasource. mydatasource2.tomcat.validationQuery = SELECT 1 Ou il y a autre chose à suivre ??
Nitish Kumar
2
Avertissement! Si vous définissez un DataSource @Bean dans votre application, Spring Boot ne configurera pas le pool. docs.spring.io/spring-boot/docs/1.5.16.RELEASE/reference/... If you define your own DataSource bean, auto-configuration will not occur. J'ai suivi un guide pour OAuth2 et j'ai eu @Bean(name = "OAuth") public DataSource secondaryDataSource()...et il n'a pas été auto-configuré ni utilisé testOnBorrow.
Chloe
28

Les suggestions ci-dessus n'ont pas fonctionné pour moi. Ce qui a vraiment fonctionné, c'est l'inclusion des lignes suivantes dans l'application.

spring.datasource.testWhileIdle = true
spring.datasource.timeBetweenEvictionRunsMillis = 3600000
spring.datasource.validationQuery = SELECT 1

Vous pouvez trouver l'explication ici

Soumya
la source
5
Le lien que vous avez ajouté indique que si la connexion à la base de données est inactive pendant plus de 8 heures, elle est automatiquement fermée et l'erreur ci-dessus se produira. Donc, votre solution est de ne pas laisser la connexion rester inactive pendant des durées plus longues. Existe-t-il un moyen de me connecter au serveur SQL après son redémarrage?
Akeshwar Jha
9

La configuration spring.datasource.tomcat.testOnBorrow=truedans application.properties ne fonctionnait pas.

La configuration par programme comme ci-dessous a fonctionné sans aucun problème.

import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;    

@Bean
public DataSource dataSource() {
    PoolProperties poolProperties = new PoolProperties();
    poolProperties.setUrl(this.properties.getDatabase().getUrl());         
    poolProperties.setUsername(this.properties.getDatabase().getUsername());            
    poolProperties.setPassword(this.properties.getDatabase().getPassword());

    //here it is
    poolProperties.setTestOnBorrow(true);
    poolProperties.setValidationQuery("SELECT 1");

    return new DataSource(poolProperties);
}
qui suis je
la source
1
Si vous déclarez une source de données personnalisée, cela peut être dû au fait que vous essayez d'utiliser le .tomcat par défaut de spring. Donc, si vous créez un bean Datasource personnalisé, ajoutez le @ConfigurationProperties (prefix = "spring.datasource.tomcat") au bean DataSource et cela devrait vous permettre de les définir dans les propriétés de l'application. Mon exemple .. @Bean (name = "managementDataSource") @ConfigurationProperties (prefix = "management.datasource") public DataSource dataSource () {return DataSourceBuilder.create (). Build (); } management.datasource.test-on-borrow = true
Justin
8

Je viens de passer à Spring Boot 1.4 et j'ai trouvé que ces propriétés avaient été renommées:

spring.datasource.dbcp.test-while-idle=true
spring.datasource.dbcp.time-between-eviction-runs-millis=3600000
spring.datasource.dbcp.validation-query=SELECT 1
José Jurado
la source
2
Les noms sont équivalents. Consultez la section sur la dénomination des propriétés dans la documentation Spring Boot .
Stephen Harrison
@StephenHarrison: notez le préfixe dbcp. * Ajouté dans la version 1.4, la liaison relâchée ne s'applique pas dans ce cas.
YM
1
@Pawel: selon l'implémentation de pooling disponible dans votre projet, il se peut que ce ne soit pas les propriétés dbcp. * Pour vous, voir Spring boot avec SQL et les propriétés de la source de données
YM
4

La réponse de whoami est la bonne. En utilisant les propriétés comme suggéré, je n'ai pas pu faire fonctionner cela (en utilisant Spring Boot 1.5.3.RELEASE)

J'ajoute ma réponse car c'est une classe de configuration complète, donc cela pourrait aider quelqu'un qui utilise Spring Boot:

@Configuration
@Log4j
public class SwatDataBaseConfig {

    @Value("${swat.decrypt.location}")
    private String fileLocation;

    @Value("${swat.datasource.url}")
    private String dbURL;

    @Value("${swat.datasource.driver-class-name}")
    private String driverName;

    @Value("${swat.datasource.username}")
    private String userName;

    @Value("${swat.datasource.password}")
    private String hashedPassword;

    @Bean
    public DataSource primaryDataSource() {
        PoolProperties poolProperties = new PoolProperties();
        poolProperties.setUrl(dbURL);
        poolProperties.setUsername(userName);
        poolProperties.setPassword(password);
        poolProperties.setDriverClassName(driverName);
        poolProperties.setTestOnBorrow(true);
        poolProperties.setValidationQuery("SELECT 1");
        poolProperties.setValidationInterval(0);
        DataSource ds = new org.apache.tomcat.jdbc.pool.DataSource(poolProperties);
        return ds;
    }
}
naoru
la source
Savez-vous pourquoi ce code personnalisé est nécessaire et pourquoi Spring ne lira pas simplement ces propriétés à partir du fichier de propriétés? J'ai plusieurs propriétés de source de données dans mon fichier et il lit toutes les autres sans problème.
Uncle Long Hair
3

J'ai un problème similaire. Spring 4 et Tomcat 8. Je résous le problème avec la configuration Spring

<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource" destroy-method="close">
    <property name="initialSize" value="10" />
    <property name="maxActive" value="25" />
    <property name="maxIdle" value="20" />
    <property name="minIdle" value="10" />
     ...
    <property name="testOnBorrow" value="true" />
    <property name="validationQuery" value="SELECT 1" />
 </bean>

J'ai testé. Ça marche bien! Ces deux lignes font tout pour se reconnecter à la base de données:

<property name="testOnBorrow" value="true" />
<property name="validationQuery" value="SELECT 1" />
grep
la source
3

Au cas où quelqu'un utiliserait DataSource personnalisé

@Bean(name = "managementDataSource")
@ConfigurationProperties(prefix = "management.datasource")
public DataSource dataSource() {
    return DataSourceBuilder.create().build();
}

Les propriétés doivent ressembler à ce qui suit. Notez les @ConfigurationProperties avec le préfixe. Le préfixe est tout avant le nom réel de la propriété

management.datasource.test-on-borrow=true
management.datasource.validation-query=SELECT 1

Une référence pour Spring Version 1.4.4.RELEASE

Justin
la source
2

Comme certains l'ont déjà souligné, spring-boot 1.4+, a des espaces de noms spécifiques pour les quatre pools de connexions. Par défaut, hikaricp est utilisé dans spring-boot 2+. Vous devrez donc spécifier ici le SQL. La valeur par défaut est SELECT 1. Voici ce dont vous auriez besoin pour DB2 par exemple: spring.datasource.hikari.connection-test-query=SELECT current date FROM sysibm.sysdummy1

Attention : si votre pilote prend en charge JDBC4, nous vous recommandons vivement de ne pas définir cette propriété. Ceci concerne les pilotes «hérités» qui ne prennent pas en charge l'API JDBC4 Connection.isValid (). Il s'agit de la requête qui sera exécutée juste avant qu'une connexion ne vous soit donnée depuis le pool pour valider que la connexion à la base de données est toujours active. Encore une fois, essayez d'exécuter le pool sans cette propriété, HikariCP enregistrera une erreur si votre pilote n'est pas compatible JDBC4 pour vous en informer. Par défaut: aucun

code4kix
la source
0

Pour ceux qui veulent le faire à partir de YAML avec plusieurs sources de données, il existe un excellent article de blog à ce sujet: https://springframework.guru/how-to-configure-multiple-data-sources-in-a-spring-boot -application/

Cela dit essentiellement que vous devez tous deux configurer les propriétés de la source de données et la source de données comme ceci:

@Bean
@Primary
@ConfigurationProperties("app.datasource.member")
public DataSourceProperties memberDataSourceProperties() {
    return new DataSourceProperties();
}

@Bean
@Primary
@ConfigurationProperties("app.datasource.member.hikari")
public DataSource memberDataSource() {
    return memberDataSourceProperties().initializeDataSourceBuilder()
            .type(HikariDataSource.class).build();
}

N'oubliez pas de supprimer @Primarydes autres sources de données.

enesaltinok
la source