Comment réchauffer les classes Java pour éviter un premier appel lent?

13

Je fais un projet où j'ai besoin de tous les appels d'API pour prendre moins de 1s mais je suis confronté à un problème avec le premier appel à chaque route qui est plus lent que les suivants.

Actuellement, le premier appel à / login prend 3,6 secondes et les suivants prennent 170 ms et même pour toutes les autres routes.

J'ai découvert -XX:+TraceClassLoadingqu'en utilisant cela lors du premier appel, les classes étaient chargées en mémoire et cela causait le problème de performances.

Cependant, je n'ai pas trouvé de moyen facile de charger toutes les classes au démarrage et pour chaque nouveau service, j'ai besoin d'ajouter un appel de préchauffage dans un ApplicationRunner.

Quelqu'un a-t-il une solution pour charger automatiquement les classes d'une application SpringBoot ou réchauffer toutes ses routes?

Ybri
la source
Pouvez-vous ajouter plus de détails? Votre application instancie-t-elle les contrôleurs? Ou appelez-vous d'autres services? Comment passez-vous les appels vers d'autres services?
Menios
Spring Boot utilise intensivement l'analyse de classe, vous n'avez donc pas besoin de vous «réchauffer» comme sur une application de bureau. Ce long chargement initial peut être le résultat d'une recherche de ressources - par exemple le chargement d'un modèle de page.
Alex Chernyshev
Une approche indirecte: si vous disposez d'une couverture de test unitaire à 100% pour les points finaux, vous pouvez les utiliser. Vous devriez toujours coder par point de terminaison mais vous gagnez quelque chose
Margé le
1
Cela peut ne pas être idéal selon le projet que vous faites, mais vous pouvez appeler vos points de terminaison en interne lorsque votre application est chargée.
omoshiroiii
@omoshiroiii il n'y a rien de mal à cela. nous le faisons. en production. la raison a à voir avec certaines bibliothèques dynamiques qui utilisent invokedynamicet nous savons que la résolution est lente au premier appel pour celles-ci (nous avons des dizaines de milliers de ces appels, qui sans ce premier appel s'accumulent à des dizaines de secondes).
Eugene

Réponses:

1

Le chargement des classes de Java est paresseux. Cela signifie qu'une classe n'est chargée par la JVM que lorsqu'elle en a besoin et si elle le doit.

Si vous souhaitez le forcer à charger les classes avec impatience, il vous suffit de les référencer. Une façon de le faire consiste à parcourir le contenu du pot ou les fichiers de classe pour obtenir les noms de classe, puis les utiliser pour appeler Class.forName(className).

De plus, si le temps de démarrage et les performances sont très importants pour votre cas d'utilisation, vous pouvez envisager des solutions de compilation à l' avance comme GraalVM , ou réduire le seuil de compilation de JIT ( -XX:CompileThreshold).

andresp
la source
aucun de ceux-ci ne résoudra le problème d'OP. le chargement est toujours paresseux dans GraalVM et JITn'a vraiment aucun sens lors des premières invocations.
Eugene
aussi, GraalVMc'est bien, mais s'il vous plaît jetez un œil au nombre de problèmes qu'il a dans github: dès que vous passez d'un projet de bac à sable à quelque chose de plus grand (je regarde votre réflexion, principalement), vous aurez mal, au moins. mon point est: le passage à GraalVM n'est pas un simple claquement de doigts.
Eugene
J'ai pensé à charger les classes dans le bocal mais je n'ai pas trouvé de moyen de le faire, auriez-vous un exemple?
Ybri
@Eugene si vous lisez ma réponse, vous verrez que je n'ai pas dit que GraalVM ou le seuil JIT changerait la paresse du chargement de classe. La réponse à la question d'OP concernant la paresse est le paragraphe précédent. Le dernier paragraphe n'est qu'un conseil supplémentaire au cas où OP devrait optimiser davantage le temps de démarrage / les performances au-delà du chargement de classe.
andresp
1
@Ybri il y a d'autres questions avec des réponses à cela ici, par exemple stackoverflow.com/questions/2370867/…
andresp
0

Pour moi, la seule option viable que vous avez est class data sharing, répartie sur JEP 310 , JEP 341 et JEP 350 , mais cela nécessite probablement Java-13. Nous testons cela en interne sur mon lieu de travail (surtout pour le plaisir, je ne vais pas mentir) et les résultats semblent bons jusqu'à présent.

L'autre option appelle vos points de terminaison au démarrage de l'application - si c'est une option. Encore une fois, il est pour nous par exemple: nous les appelons avec des données fictives quelques centaines de fois pour se réchauffer le code. Mais, en même temps, nous avons des services où cela serait impossible - c'est pourquoi l'exploration de CDStrop.

Eugène
la source
Comment gérez-vous l'authentification et les postes d'extrémité pour éviter la création de données dans votre base de données de production?
Ybri
@Ybri exactement pourquoi j'ai dit, impossible pour certains.
Eugene