Exécution de tâches différées avec Redis
J'ai besoin de concevoir un Redis-driven évolutive de la planification des tâches du système.
Exigences:
- Plusieurs processus de travail.
- De nombreuses tâches, mais de longues périodes d'inactivité sont possibles.
- Raisonnable précision de la synchronisation.
- Minimum le gaspillage de ressources en cas d'inactivité.
- Devrait synchrone Redis API.
- Devrait travailler pour le Redis 2.4 (c'est à dire pas de fonctionnalités à venir de la 2.6).
- Ne devrait pas utiliser d'autres moyens de RPC que le Redis.
Pseudo-API: schedule_task(timestamp, task_data)
. Timestamp est un entier de secondes.
Idée de base:
- Écouter pour les tâches à venir sur la liste.
- Mettre tâches à seaux par horodateur.
- Sommeil jusqu'à l'horodateur le plus proche.
- Si une nouvelle tâche s'affiche avec horodatage de moins que le plus proche, se réveiller.
- Traiter toutes les tâches à venir avec horodatage ≤ maintenant, dans les lots (en supposant que
que l'exécution de la tâche est rapide). - Assurez-vous que le travail simultanées ne traitent pas les mêmes tâches. Dans le même temps, assurez-vous qu'aucune tâche n'est perdu si nous crash lors du traitement.
Jusqu'à présent, je n'arrive pas à comprendre comment intégrer le Redis primitives...
Toute indices?
Remarque qu'il est à la même vieille question: Retards dans l'exécution /la planification avec le Redis? Dans cette nouvelle question, je introduire plus de détails (le plus important, de nombreux travailleurs). Jusqu'à présent je n'étais pas en mesure de comprendre comment l'appliquer vieux réponses ici —ainsi, une nouvelle question.
source d'informationauteur Alexander Gladysh
Vous devez vous connecter pour publier un commentaire.
Voici une autre solution qui s'appuie sur un couple d'autres [1]. Il utilise le redis REGARDER la commande de suppression de la condition de la course sans utiliser lua dans le redis 2.6.
Le schéma de base est:
Je n'ai pas testé 🙂
Les foo créateur d'emplois:
Le répartiteur(s):
Les foo travailleur ressemblerait à:
[1] Cette solution est basée sur not_a_golfer, l'un à http://www.saltycrane.com/blog/2011/11/unique-python-redis-based-queue-delay/et le redis docs pour les transactions.
Vous n'avez pas à spécifier la langue que vous utilisez. Vous avez au moins 3 variantes de faire cela sans écrire une seule ligne de code en Python au moins.
Le céleri a une option redis courtier.
http://celeryproject.org/
resque est extrêmement populaire redis tâche de la file d'attente à l'aide de redis.
https://github.com/defunkt/resque
RQ est un simple et petit redis en fonction de la file d'attente qui vise à "prendre les bonnes choses de céleri et resque" et être beaucoup plus simple de travailler avec.
http://python-rq.org/
Vous pouvez au moins regarder leur design si vous ne pouvez pas les utiliser.
Mais pour répondre à votre question - ce que vous voulez peut être fait avec le redis. En fait, j'ai écrit plus ou moins que dans le passé.
EDIT:
Comme pour la modélisation de ce que vous voulez sur le redis, c'est ce que je ferais:
de files d'attente d'une tâche avec un horodatage sera effectuée directement par le client, - vous mettez la tâche en un ensemble trié avec le timestamp comme le score et la tâche de la valeur (voir ZADD).
Un répartiteur central se réveille toutes les N secondes, vérifie la première date et l'heure sur ce jeu, et si il y a des tâches prêtes à être exécutées, il pousse la tâche à un "être exécutée MAINTENANT" de la liste. Cela peut être fait avec ZREVRANGEBYSCORE sur le "en attente" ensemble trié, obtenir tous les articles avec le timestamp<=now, de sorte que vous obtenez tous les éléments prêts à la fois. pousser est fait par RPUSH.
les travailleurs utilisent BLPOP sur le "pour être exécuté MAINTENANT" liste de le réveiller quand il y a quelque chose à travailler, et faire leur chose. C'est sûr, depuis le redis est mono-thread, et pas de 2 travailleurs prendra jamais la même tâche.
une fois terminé, les travailleurs de mettre le résultat dans une file d'attente de réponse, ce qui est vérifié par le répartiteur ou un autre thread. Vous pouvez ajouter un "en attente" seau pour éviter les échecs ou quelque chose comme ça.
de sorte que le code devrait ressembler à quelque chose comme ceci (c'est juste le pseudo-code):
client:
répartiteur:
Je n'ai pas ajouter à la file d'attente de réponse de la manipulation, mais c'est plus ou moins comme le travailleur:
travailleur:
EDit: apatrides atomique répartiteur :
Si vous êtes à la recherche de solution prête à l'emploi sur l'île de Java. Redisson est bon pour vous. Il permet de planifier et d'exécuter des tâches (avec cron-expression de soutien) de manière distribuée sur Redisson nœuds utilisation familière ScheduledExecutorService api et basé sur le Redis file d'attente.
Ici est un exemple. D'abord définir une tâche à l'aide de
java.lang.Runnable
interface. Chaque tâche peut accéder à Redis exemple via injectéRedissonClient
objet.Il est maintenant prêt à remplir en
ScheduledExecutorService
:Exemples avec cron expressions:
Toutes les tâches sont en cours d'exécution sur Redisson nœud.
Une approche combinée semble plausible:
Aucune nouvelle tâche d'horodatage peut être moins de temps courant (borne moins). En supposant fiable NTP synchronisation.
Toutes les tâches de seau-listes sur les touches, suffixé à la tâche d'horodatage.
En outre, toutes les tâches horodateurs aller à la zset (clé et le score — timestamp lui-même).
De nouvelles tâches sont acceptés à partir de clients par l'intermédiaire de séparer Redis liste.
Boucle: aller Chercher le plus ancien N expiré horodateurs via zrangebyscore ... la limite.
BLPOP avec délai sur la nouvelle liste des tâches et des listes extraites des horodateurs.
Si j'ai sorti un vieux de la tâche, de la traiter. Si la nouvelle — ajouter dans le seau et zset.
Vérifier si le traitement des seaux sont vides. Si oui — supprimer la liste et entrt de zset. Sans doute ne pas vérifier très récemment expiré seaux, pour se protéger contre le temps, les problèmes de synchronisation. Fin de la boucle.
Critique? Des commentaires? Alternatives?