Question

Sujet:

J'essaie d'implémenter une planification de travail de base en Java pour gérer les tâches planifiées récurrentes et persistantes (pour un projet d'apprentissage personnel).Je ne souhaite utiliser aucune bibliothèque (prête à l'emploi) comme Quartz/Obsidian/Cron4J/etc.

Objectif:

  • Le travail doit être persistant (pour gérer l'arrêt du serveur)
  • Le temps d'exécution de la tâche peut prendre jusqu'à environ 2 à 5 minutes.
  • Gérer une grande quantité de travail
  • Multithread
  • Léger et rapide ;)

Tout mon travail est dans une base de données MySQL.

JOB_TABLE (id, name, nextExecution,lastExecution, status(IDLE,PENDING,RUNNING))

Pas à pas:

  1. Récupérez chaque tâche de «JOB_TABLE" où “nextExecution > now” AND “status = IDLE“.Cette étape est exécutée toutes les 10mn par un seul thread.

  2. Pour chaque travail récupéré, je mets un nouveau fil dans un ThreadPoolExecutor puis je mets à jour le statut du travail en "PENDING" dans mon "JOB_TABLE”.

  3. Lorsque le thread de travail est en cours d'exécution, je mets à jour le statut du travail en "RUNNING”.

  4. Une fois le travail terminé, je mets à jour le lastExecution avec l'heure actuelle, j'ai défini un nouveau nextExecution temps et je change le statut du travail en "IDLE”.

Lorsque le serveur démarre, je mets chaque travail en attente/en cours d'exécution dans le ThreadPoolExecutor.

Question/Observation :

  • Étape 2 :Le ThreadPoolExecutor gérera-t-il une grande quantité de threads (~ 20 000) ?
  • Dois-je utiliser une solution NoSQL au lieu de MySQL ?
  • Est-ce la meilleure solution pour faire face à un tel cas d’utilisation ?

Ceci est un brouillon, il n’y a aucun code derrière.Je suis ouvert aux suggestions, commentaires et critiques !

Était-ce utile?

La solution

J'ai fait la même chose que votre tâche sur un projet réel, mais en .NET.Voici ce dont je me souviens concernant vos questions :

Étape 2 :Le ThreadPoolExecutor gérera-t-il une grande quantité de threads (~ 20 000) ?

Nous avons découvert que le pool de threads intégré de .NET était la pire approche, car le projet était une application Web.Raison:l'application Web s'appuie sur le pool de threads intégré (qui est statique et donc partagé pour toutes les utilisations au sein du processus en cours d'exécution) pour exécuter chaque requête dans un thread séparé, tout en maintenant un recyclage efficace des threads.Utiliser le même pool de threads pour notre traitement interne allait l'épuiser et ne laisser aucun thread libre pour les demandes des utilisateurs, ou gâcher leurs performances, ce qui était inacceptable.

Comme vous semblez exécuter beaucoup de tâches (20 000, c'est beaucoup pour une seule machine), vous devriez absolument rechercher un pool de threads personnalisé.Pas besoin d'écrire la vôtre, je parie qu'il existe des solutions toutes faites et en écrire une va bien au-delà de ce qu'exigerait votre projet d'étude* voir les commentaires (si je comprends bien vous faites un projet scolaire ou universitaire).

Dois-je utiliser une solution NoSQL au lieu de MySQL ?

Dépend.Vous devez évidemment mettre à jour le statut du travail simultanément, vous aurez ainsi un accès simultané à une seule table à partir de plusieurs threads.Les bases de données peuvent assez bien s'adapter à cela, en supposant que vous ayez bien fait votre travail.Voici ce à quoi je fais référence en faisant cela correctement :

  • Concevez votre code de manière à ce que chaque tâche n'affecte que son propre sous-ensemble de lignes dans la base de données (cela inclut les autres tables).Si vous y parvenez, vous n'aurez pas besoin de verrous explicites au niveau de la base de données (sous la forme de niveaux de sérialisation des transactions).Vous pouvez même appliquer un niveau de sérialisation libéral qui peut autoriser des lectures sales ou fantômes – qui fonctionneront plus rapidement.Mais méfiez-vous, vous devez soigneusement vous assurer qu'aucune tâche ne sera simultanée sur les mêmes lignes.Ceci est difficile à réaliser dans des projets réels, vous devriez donc probablement rechercher des approches alternatives en matière de verrouillage de base de données.

  • Utilisez le mode de sérialisation des transactions approprié. Le mode de sérialisation des transactions définit le comportement du verrouillage au niveau de la base de données.Vous pouvez le configurer pour verrouiller la table entière, uniquement les lignes que vous affectez, ou rien du tout.Utilisez-le à bon escient, car toute utilisation abusive pourrait affecter la cohérence, l’intégrité et la stabilité des données de l’ensemble de l’application ou du serveur de base de données.

  • Je ne suis pas familier avec la base de données NoSQL, je ne peux donc que vous conseiller de rechercher les capacités de concurrence et de les adapter à votre scénario.Vous pourriez aboutir à une solution vraiment adaptée, mais il faut vérifier en fonction de vos besoins.D'après votre description, vous devrez prendre en charge des opérations de données simultanées sur le même type d'objets (quel est l'analogue d'une table).

Est-ce la meilleure solution pour faire face à un tel cas d’utilisation ?

Oui et non.

  • Oui, car vous rencontrerez l'une des tâches difficiles auxquelles les développeurs sont confrontés dans le monde réel.J'ai travaillé avec des collègues ayant plus de 3 fois ma propre expérience et ils étaient plus réticents que moi à faire des tâches multi-threading, ils détestaient vraiment ça.Si vous pensez que ce domaine vous intéresse, jouez avec, apprenez et améliorez-vous autant que nécessaire.

  • Non, car si vous travaillez sur un projet réel, vous avez besoin de quelque chose de fiable.Si vous avez autant de questions, vous aurez évidemment besoin de temps pour mûrir et être capable de produire une solution stable pour une telle tâche.Le multithreading est un sujet difficile pour de nombreuses raisons :

    • Il est difficile de déboguer
    • Cela introduit de nombreux points d’échec, vous devez tous en être conscient
    • Il pourrait être difficile pour d'autres développeurs de vous aider ou de travailler avec votre code, à moins que vous ne respectiez les règles communément acceptées.
    • La gestion des erreurs peut être délicate
    • Le comportement est imprévisible/indéterministe.

    Il existe des solutions existantes avec un haut niveau de maturité et de fiabilité qui constituent l’approche privilégiée pour les projets réels.L'inconvénient est que vous devrez les apprendre et examiner dans quelle mesure ils sont personnalisables pour vos besoins.

Quoi qu'il en soit, si vous avez besoin de le faire à votre manière, puis de porter votre réalisation sur un projet réel, ou sur votre propre projet, je peux vous conseiller de le faire de manière enfichable.Utiliser l'abstraction, programmation aux interfaces et d'autres pratiques pour dissocier votre propre implémentation spécifique de la logique qui définira les tâches planifiées.De cette façon, vous pouvez adapter votre API à une solution existante si cela devient un problème.


Enfin, mais pas des moindres, je n'ai vu aucune prédiction de gestion des erreurs de votre côté.Réfléchissez et recherchez ce qu'il faut faire si un travail échoue.Ajoutez au moins un statut « ÉCHEC » ou quelque chose pour persister dans un tel cas.La gestion des erreurs est délicate lorsqu'il s'agit de threads, alors soyez minutieux dans vos recherches et vos pratiques.

Bonne chance

Autres conseils

Vous pouvez déclarer la taille maximale du pool avec ThreadPoolExecutor#setMaximumPoolSize(int).Comme Integer.MAX est plus grand 20000 alors techniquement oui, c'est possible.

L'autre question est de savoir si votre machine prend en charge autant de threads à exécuter.Vous devrez fournir suffisamment de RAM pour que chaque bande de roulement soit allouée sur la pile.

Cela ne devrait pas poser de problème pour toi adresser environ 20 000 threads sur un ordinateur de bureau ou un ordinateur portable moderne, mais sur un appareil mobile, cela pourrait poser un problème.

Extrait du document :

Tailles de base et maximales des pools

Un threadpoolExecutor ajustera automatiquement la taille du pool (voir getPoolSize ()) en fonction des limites définies par CorePoolSize (voir getcorepoolSize ()) et maximumpoolSize (voir getMaxiMumpoHSize ()).Lorsqu'une nouvelle tâche est soumise dans Method Execute (java.lang.runnable), et moins que les threads corepoolSize fonctionnent, un nouveau thread est créé pour gérer la demande, même si d'autres threads de travailleur sont inactifs.S'il y a plus que CorePoolSize mais moins que MaximumpoolSize Threads en cours d'exécution, un nouveau thread ne sera créé que si la file d'attente est pleine.En définissant CorePoolSize et MaximumpoolSize, vous créez un pool de threads de taille fixe.En définissant MaximumpoolSize sur une valeur essentiellement illimitée telle que Integer.max_value, vous permettez à la piscine d'accueillir un nombre arbitraire de tâches simultanées.Le plus généralement, les tailles de pool noyau et maximum sont définies uniquement sur la construction, mais elles peuvent également être modifiées dynamiquement à l'aide de setCorePoolSize (int) et de setMaxiMumpoolSize (int).

Plus

À propos de la base de données.Créez une solution qui ne dépend pas de la structure de la base de données.Ensuite, vous pouvez configurer deux environnements et les mesurer.Commencez par la technologie que vous connaissez.Mais restez ouvert à d’autres solutions.Au début, la base de données des relations doit suivre les performances.Et si vous le gérez correctement, cela ne devrait pas poser de problème plus tard.Les NoSQL sont utilisés pour travailler avec du très Big Data.Mais le mieux pour vous est de créer les deux et d’exécuter des tests de performances.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top