Un Pool de Thread avec Priorité

Informatique
21 octobre 2015 par Romain
Pas de commentaires sur cet article

Chez iTK, nous devons lancer, à chaque mise à jour de la météo, plusieurs milliers de simulations agronomiques. Le temps de calcul pour chaque simulation peut varier de deux à plusieurs dizaines de secondes. Pour réaliser cette opération nous utilisons un pool de threads.

Les utilisateurs n’aiment pas attendre !

En plus des simulations déclenchées par une mise à jour de nos données météo, nos utilisateurs ont la possibilité de demander de nouvelles simulations. Si l’un de ces derniers souhaite simuler pendant que les milliers d’autres simulations sont en train de tourner, il va devoir attendre que les autres simulations finissent pour enfin obtenir son résultat.

Un rapide calcul nous montre que pour 1000 simulations (très peu !), qui ont chacune un temps de calcul de 2 secondes (dans la fourchette basse, donc), parallélisées sur 10 threads, le temps d’attente est de 200 secondes, soit 3 minutes : voilà qui ne rendra pas notre utilisateur heureux.

Nous avons donc mis en place un système de pool, où la prochaine tâche est sélectionnée en fonction de sa priorité. Pour notre exemple, l’utilisateur sera toujours prioritaire par rapport aux simulations déclenchées par la mise à jour de la météo.

Gérer des priorités dans un pool de threads

Quand nous nous sommes lancés dans ce développement, nous pensions ce problème commun, avec une solution au moins triviale, au mieux déjà existante et prête à l’emploi. Malheureusement, rien n’existait dans notre stack backend Java / Spring.

Après quelques recherches nous avons choisi d’implémenter cette solution. On utilise l’Executor de spring, qui nécessite une Future et une Callable. On va définir les nôtres avec la gestion de priorité.

L’Executor

Tout d’abord nous devons définir un ThreadPoolExecutor avec une PriorityBlockingQueue. La méthode newTaskFor(…), à redéfinir, va nous permettre d’intercepter la création de nouvelle tâche. Ainsi nous pouvons ajouter à la PriorityBlockingQueue notre tâche avec sa priorité.

 

La Future

Une PriorityFuture qui est une Future avec une priorité. En effet, les pools de threads en java utilisent des futures pour communiquer le résultat d’un traitement. Pour ranger dans le bon ordre les tâches à exécuter, PriorityBlockingQueue utilise la fonction PriorityFuture.COMP qui est une implémentation standard de la méthode compare en Java.

Nous avons implémenté COMP de sorte que la plus petite valeur passe en premier. C’est ici qu’il vous faut décider quoi faire des priorités.

La Callable

Il ne nous manque plus qu’à définir PriorityCallable, une petite interface permettant de définir la priorité d’une Callable, ainsi nous aurons couvert la totalité des fonctionnalités d’un thread de pool.

 

L’Utilisation

Rien de plus simple, puisqu’il suffit d’appeler notre executor comme avant.

Nous nous sommes juste permis de créer une Factory afin de faciliter la création de l’instance de Callable et surtout de cacher les détails d’implémentation, comme l’initialisation du contexte Spring.

Conclusion

En interne, java va transformer notre Callable en PriorityFuture. Comme cette PriorityFuture spécialise RunnableFuture, le thread va pouvoir se lancer, traiter notre demande. Nous aurons alors le choix d’attendre le résultat et bloquer le thread courant ou de ne rien faire de la future et laisser le thread courant continuer son exécution normale.

Pour la suite des aventures, nous pensons maintenant passer à une architecture micro-service, qui fera l’objet d’un autre article !

Si jamais vous avez réussi à implémenter une autre solution, nous serons ravis de la découvrir.

Romain Romain
Développeur web, en quête perpétuelle de nouvelles choses...
Commentaires
Soyez le premier à réagir sur cet article !

UP