Gestion des tâches en arrière-plan sur un grand site

49

Nous traitons un problème intéressant sur StackOverflow.

Nous avons toute une série de petites tâches à accomplir rapidement. Un exemple est la mise à jour des listes "Questions connexes". Ce que nous avons fait par le passé est d’associer ces tâches au chargement de pages de certains utilisateurs.

Ce n'était jamais idéal, mais ce n'était pas vraiment perceptible. Maintenant que SO a dépassé le million de points d'interrogation, ces utilisateurs malchanceux commencent à le ressentir.

La solution naturelle est de pousser ces tâches en arrière-plan. J'envisage deux grandes façons de procéder.

1. Dans IIS en tant que pool de threads / file de travail personnalisé

Fondamentalement, nous mettons en place quelques threads (non ThreadPool , afin de ne pas interférer avec IIS) et les mettons au service de certaines collections dans lesquelles nous transférons des Funcs .

Le grand pro ici est la simplicité. Nous n'avons plus à nous soucier de rien, ni à nous assurer qu'un service externe est opérationnel et répond.

Nous avons également accès à tous nos codes communs.

Le problème est, eh bien, que nous ne devrions pas utiliser de threads d'arrière-plan. Les objections que je connais sont toutes centrées autour de l'IIS affamé (si vous utilisez ThreadPool) et des threads en train de mourir de manière aléatoire (en raison du recyclage AppPool).

Nous avons l'infrastructure existante pour que la mort aléatoire des threads ne soit plus un problème (la possibilité de détecter une tâche a été abandonnée, en gros), et il n'est pas difficile de limiter le nombre de threads (et d'utiliser des threads autres que ThreadPool).

Me manque-t-il d'autres objections dans le regroupement de threads / files de travail de processus IIS?

Déplacé vers StackOverflow , car cela n'a pas vraiment été abordé ici.

2. En tant que service

Soit une solution tierce, soit une solution personnalisée.

En gros, nous regrouperions une tâche d'un service à un autre et l'oublierions. Vraisemblablement, nous lions du code dans, ou sommes limités à du SQL brut + une chaîne de connexion.

Le pro est que c'est la "bonne manière" de faire cela.

Les inconvénients sont que nous sommes soit très limités dans ce que nous pouvons faire, soit que nous allons devoir mettre au point un système pour maintenir ce service synchronisé avec notre base de code. Nous devrons également relier d’une manière ou d’une autre notre surveillance et notre enregistrement des erreurs, que nous obtenons gratuitement avec l’option "In IIS".

Y a-t-il d'autres avantages ou problèmes avec l'approche de service?

En un mot, existe-t-il des problèmes imprévus et insurmontables qui rendent l'approche n ° 1 inapplicable et, dans l'affirmative, existe-t-il de bons services tiers que nous devrions examiner pour l'approche n ° 2?

Kevin Montrose
la source
La bonne façon, c'est de dire que quand on décide de faire l'inverse, on se dit qu'on aurait dû le faire correctement. Choisis sagement. Je ne connais pas suffisamment le monde IIS pour commenter ce problème particulier.
Chris
2
Je suis curieux parce que j'ai un scénario similaire (à une échelle beaucoup plus petite) et que moi aussi je ne fais que greffer sur une connexion malchanceuse d'utilisateurs aléatoires. Je ne connais pas la meilleure solution, je vais donc suivre ici. :-)
pc1oad1etter
7
Je ne comprends pas pourquoi ce n'est pas sur StackOverflow. Ceci est un compromis d'ingénierie, pas une évaluation subjective. Vous demandez une analyse des différentes approches - tout est objectif. Ce n’est que lorsque l’analyse a clairement montré ce que sont exactement les compromis, y at-il une subjectivité à cela et, autant que je sache, votre question n’est pas: que devrais-je trouver de plus important, mon temps et les ressources du serveur, ou le temps de mon utilisateur? ' ou quelque chose de similaire.
Joren
@ Kevin Montrose - d'après vos commentaires, il semble que vous établissiez une distinction entre "doit être fait rapidement - de manière approximative" et "planifié à intervalle". Pouvez-vous expliquer pourquoi il s’agit de deux types différents de tâches d’arrière-plan nécessitant un modèle / une infrastructure différent?
Portman
@Portman - La différence fondamentale est que les tâches "à court terme" ne peuvent pas être effectuées de manière spéculative, nous devons vraiment attendre jusqu'à ce que nous sachions qu'elles doivent être accomplies. Certains calculs arrière montrent que, si nous devions déplacer les requêtes "Related Questions" (une parmi beaucoup d’autres) vers un onglet cron "idiot", il faudrait environ 20%. une semaine d'exécution solide pour répondre à toutes les questions. En règle générale, nous souhaitons également qu'ils s'exécutent le plus rapidement possible (sans que l'expérience utilisateur en soit affectée), alors que nos tâches par intervalles peuvent être exécutées en ne s'exécutant pas plus d'une fois toutes les 5 minutes (et généralement beaucoup moins fréquemment).
Kevin Montrose

Réponses:

17

Il y a quelques semaines, j'ai posé une question similaire sur SO. En résumé, mon approche consiste depuis quelque temps à développer un service Windows. J'utiliserais NServiceBus (essentiellement MSMQ sous les couvertures) pour regrouper les demandes de mon application Web à mon service. J'avais l'habitude d'utiliser WCF, mais le fait de faire fonctionner correctement une transaction distribuée sur WCF me semblait toujours être un casse-tête. NServiceBus a fait l'affaire, je pouvais valider des données et créer des tâches dans une transaction sans me soucier de savoir si mon service était opérationnel à ce moment-là. À titre d’exemple simple, si j’avais besoin d’envoyer un courrier électronique (par exemple, un courrier électronique d’enregistrement), je créerais le compte de l’utilisateur et enverrais un signal à mon service Windows (pour envoyer le courrier électronique) lors d’une transaction. Le gestionnaire de messages du côté service prendrait le message et le traiterait en conséquence.

Depuis que ASP .NET 4.0 et AppFabric ont été publiés, il existe un certain nombre d'alternatives viables au mécanisme ci-dessus. Pour revenir à la question que j'ai mentionnée ci-dessus, nous avons à présent AppInitialize d'AppFabric (via net.pipe), ainsi que la fonction de démarrage automatique d'ASP .NET 4.0, qui font du développement de Windows Services en tant qu'applications Web une alternative viable. J'ai commencé à le faire maintenant pour un certain nombre de raisons (la plus importante étant le déploiement n'est plus un problème):

  1. Vous pouvez développer une interface utilisateur Web sur votre service (car il fonctionne en tant qu'application Web). Ceci est extrêmement utile pour voir ce qui se passe au moment de l'exécution.
  2. Votre modèle de déploiement pour vos applications Web fonctionnera pour votre application de service.
  3. IIS fournit quelques fonctionnalités intéressantes pour la gestion des échecs d'application (similaires à certains égards à un service Windows).
  4. Les développeurs Web connaissent très bien le développement d’applications Web (naturellement), mais la plupart n’en savent pas beaucoup sur les pratiques recommandées lors du développement d’un service Windows.
  5. Il offre un certain nombre d'alternatives à l'exposition d'une API pour que d'autres applications l'utilisent.

Si vous choisissez cette voie (pardonnez-moi de copier et coller de mon message d'origine), je considérerais certainement d'exécuter la logique d'arrière-plan dans une application Web distincte. Il y a plusieurs raisons à cela:

  1. Sécurité . Il peut exister un modèle de sécurité différent pour l'interface utilisateur affichant des informations sur les processus d'arrière-plan en cours d'exécution. Je ne voudrais pas exposer cette interface utilisateur à d'autres personnes que l'équipe ops. En outre, l'application Web peut s'exécuter en tant qu'utilisateur différent disposant d'un ensemble d'autorisations élevé.
  2. Entretien . Il serait bon de pouvoir déployer des modifications dans l'application hébergeant les processus en arrière-plan sans que l'utilisateur ait à utiliser le site Web frontal.
  3. La performance . Si l'application est séparée des demandes des utilisateurs du site principal traitant, les threads d'arrière-plan ne diminueront pas la capacité d'IIS de gérer la file d'attente des demandes entrantes. De plus, l'application traitant les tâches en arrière-plan peut être déployée sur un serveur séparé si nécessaire.

Cela revient à l'aspect de marshaling. WCF, NServiceBus / RabbitMQ / ActiveMQ, etc., vanilla MSMQ, API RESTful (think MVC) sont toutes des options. Si vous utilisez Windows Workflow 4.0, vous pouvez exposer un point de terminaison hôte que votre application Web pourrait consommer.

L’approche de l’hébergement Web pour les services est encore relativement nouvelle pour moi, seul le temps nous dira s’il s’agissait du bon choix. Jusqu'ici tout va bien cependant. À propos, si vous ne voulez pas utiliser AppFabric (je ne pouvais pas parce que, pour une raison étrange, Windows Server Web Edition n'est pas pris en charge), la fonctionnalité de démarrage automatique mentionnée dans l'article de Gu fonctionne parfaitement. Éloignez-vous du fichier applicationhost.config, vous pouvez tout configurer dans la publication via la console IIS (Éditeur de configuration au niveau du serveur principal).

Remarque: à l'origine, j'avais posté quelques liens supplémentaires dans ce message, mais hélas, il s'agit de mon premier message à cet échange et un seul lien est pris en charge! Il y en avait essentiellement deux autres, pour les amener à Google "Mort aux services Windows ... Longue vie à AppFabric!" et "auto-start-asp-net-applications". Désolé pour ça.

Rohland
la source
L'idée de base d'utiliser un site Web distinct comme service est une idée intrigante que je n'avais pas envisagée ...
Kevin Montrose
Rohland, il se peut que quelque chose me manque ici, mais vous semblez dire que vous interagissiez avec un service Windows depuis votre gestionnaire NServiceBus, le service envoie ensuite le courrier électronique. Si j'ai raison, puis-je vous demander pourquoi vous n'envoyez simplement pas l'e-mail d'un gestionnaire de messages NServiceBus, qui serait très facile à développer, à tester et à déployer?
Sean Kearon
Le site Web envoie un message au service Windows. Le gestionnaire de messages Windows Service NServiceBus récupère le message et l'envoie. En substance, il s’agit du processus que vous décrivez.
Rohland
22

Il existe en réalité une troisième manière dans Windows d’exécuter des services d’arrière-plan et elle est très courante dans le monde UNIX. La troisième façon est un CRONtravail qui gère une partie de votre infrastructure. Sous Windows, cela s'appelle task scheduleret est très courant pour exécuter du code sur une base planifiée. Pour utiliser cela, vous créez une application de ligne de commande qui est exécutée selon un planning prédéfini. L'avantage de cela est que vous n'avez pas à vous inquiéter si le processus reste opérationnel comme un service, car s'il échouait pour une raison quelconque, il ne ferait que démarrer la prochaine fois.

En ce qui concerne le marshaling de tâches spécifiques, il vous suffit de stocker ces tâches dans un stockage binaire persistant. Jusqu'à ce que l'application en ligne de commande les sélectionne et les exécute. Dans le passé, j'ai déjà utilisé la base de données Cassandra en tant que fournisseur d'état de session pour intégrer des tâches d'arrière-plan à des utilisateurs spécifiques de la base de données Cassandra, puis la ligne de commande les sélectionne et les exécute pour l'utilisateur.

Ce n'était peut-être pas la solution typique du marshaling, mais cela a très bien fonctionné pour moi et s'est avéré être une solution très élégante, car les tâches planifiées survivaient aux arrêts, aux problèmes de réseau, et n'importe quelle machine pouvait exécuter la tâche car elle était centralisée. stockée.

Promotion sans vergogne, mais ceci est mon projet et la solution que je viens de décrire brièvement est la raison pour laquelle j'ai créé le projet: http://github.com/managedfusion/fluentcassandra/

Nick Berardi
la source
2
Je le fais avec mon service d'hébergement partagé car je n'ai pas d'accès au shell. Ecrivez une page PHP qui fait quelque chose d'important, puis effectuez un travail cron qui charge la page à l'aide de wget ou de lynx périodiquement. Cela ressemble exactement au genre de chose qui fonctionnerait dans ce cas et qui serait extrêmement simple, ne nécessitant guère de changement de la façon dont les choses se font actuellement.
Ricket
Quelle solution simple. Cela a suscité des idées pour mon propre projet que je n’envisageais pas encore. De plus, vous avez un accès complet à votre base de code existante. Ajoutez simplement un projet de console à la solution et référencez les projets existants.
Tim Murphy
10

Cron + Web App

Il s'agit d'une conception testée au combat qui s'adapte horizontalement à votre batterie de serveurs Web et garantit que vous utilisez la pile de technologies Web que vous connaissez déjà.

Voilà comment cela fonctionne:

  1. Créez un contrôleur / une action dans votre application Web pour gérer les tâches en arrière-plan planifiées. Par convention, j'appelle habituellement le mien http://mydomain.com/system/cron.
  2. Pour des raisons de sécurité, cette action doit être verrouillée uniquement aux adresses IP authentifiées sur le réseau local.
  3. Sur une machine séparée, installez Wget et configurez une tâche planifiée pour que wget récupère la ressource à partir de l'étape 1. Vous pouvez exécuter la tâche aussi souvent que vous le souhaitez (j'opte généralement pendant 30 secondes). N'oubliez pas de transmettre l'argument de cookie approprié à Wget afin qu'il s'authentifie sur votre application Web.
  4. Pour la redondance, vous pouvez également installer un deuxième wget planifié sur un deuxième ordinateur.

Hourra! Maintenant, vous avez un itinéraire qui sera appelé toutes les 30 secondes. Et si le traitement de la demande prend 5 minutes, personne ne s'en souciera, car cela ne fait pas partie de la demande de page d'un utilisateur.

L' cronaction finit par paraître très simple: il dispose d'une liste de méthodes à exécuter sur une certaine fréquence. Lorsqu'une demande arrive, il voit s'il y a une méthode à exécuter et appelle la méthode appropriée. Cela signifie que vous pouvez contrôler la planification dans votre base de données , où vous avez probablement déjà beaucoup d'autres données de configuration importantes pour votre site.

Plus important encore (pour vous), cela signifie que vos travaux ne doivent pas nécessairement être appelés selon un horaire fixe. Vous pouvez écrire n'importe quelle logique pour déterminer quand exécuter une méthode.

Avantages et inconvénients

Avantages
  • Vous êtes déjà très bon en écriture de code ASP.NET MVC. Cela vous permet donc d'écrire vos tâches d'arrière-plan sur la même plate - forme que celle dans laquelle vous écrivez le reste de votre solution.
  • Les tâches s'exécutent dans le même contexte que votre application Web. Vous pouvez donc partager le cache et utiliser les méthodes d'assistance existantes.
  • Si vous voulez que wget récupère un URI à charge équilibrée , vos tâches d'arrière-plan sont désormais également à charge équilibrée.
  • Déploiement simultané - vous n'avez pas à vous soucier de la synchronisation de votre application Web avec votre logique de tâche en arrière-plan, car elles font toutes partie du même déploiement.
Les inconvénients
  • Au fil des ans, quelques personnes m'ont dit que cette conception était "fortement couplée", mais quand on les pressait, elles n'arrivaient pas à expliquer pourquoi c'était une mauvaise chose.

Remarque: S'il y a des questions ou des préoccupations, veuillez ajouter un commentaire . Je suis heureux d'élaborer.

Portman
la source
7

J'ai essayé et utilisé à peu près tous les moyens possibles de le faire dans mon application actuelle. J'ai commencé par faire la même chose que vous faites actuellement, à la suite d'une demande d'un utilisateur de remplir les données, puis de les mettre en cache à l'avenir. J'ai aussi compris que c'était une mauvaise idée (d'autant plus que vous utilisez plusieurs serveurs Web et que plus d'utilisateurs en prennent le coup).

J'ai également eu un travail planifié qui correspond à une URL dans l'application ASP.NET. Il s'agit d'une solution décente, mais qui commence à s'effriter à la minute près où vous passez devant un serveur Web.

Actuellement, j'utilise deux méthodes différentes, toutes deux utilisant Quartz.NET, qui est une excellente petite bibliothèque. La première est Quartz.NET qui s'exécute in-process avec ASP.NET, il est configuré dans le fichier global.asax et s'exécute toutes les deux minutes. J'utilise ceci pour mettre à jour le cache ASP.NET hors bande, ce qui est la seule raison pour laquelle il est exécuté dans le cadre d'ASP.NET.

La seconde est que j'ai écrit une bibliothèque pour emballer Quartz.NET appelée DaemonMaster - il est facile de déposer une DLL dans un répertoire et de l'exécuter dans un service Windows. J'ai trouvé que cela permettait d'éviter certaines des tâches gênantes liées au travail avec un service Windows et de nettoyer certaines parties de l'api Quartz.NET. Les services exécutés par DaemonMaster sont de deux types différents. Les premiers sont les travaux devant être exécutés toutes les nuits ou toutes les X minutes. Les autres travaux sont exécutés hors d'une file d'attente en fonction des données provenant de l'application ASP.NET. L'application ASP.NET place les objets JSON sur RabbitMQ et les services interrogent RabbitMQ, puis traitent les données.

Sur cette base, je vous suggérerais d’utiliser un service Windows (et de consulter DaemonMaster) et, si nécessaire, d’utiliser une file d’attente comme RabbitMQ pour la transmission des données de l’application ASP.NET aux services. C’est le meilleur de toutes ces solutions. . Si vous chargez le cache, alors exécuter ASP.NET est logique, sinon je ne le pense pas.

James Avery
la source
6

Je le ferais de la bonne façon et j'aurais un service Windows en cours d'exécution qui surveille une "file d'attente". Je dis "file" car la programmation avec MSMQ s'apparente à coller des pokers chauds dans vos globes oculaires.

Je suis tombé amoureux de la simplicité de Delayed :: Job in Rails, et il est très facile de faire quelque chose de similaire dans .NET.

Fondamentalement, vous ajoutez une sorte de SomethingOperation(quelque chose qui a une Perform()méthode). Ensuite, il suffit de sérialiser les paramètres pertinents, de lui donner une priorité, une sorte de comportement de nouvelle tentative par défaut et de les insérer dans une base de données.

Votre service ne ferait que surveiller cela et traiter les travaux en file d'attente.

Ben Scheirman
la source
Sérialiser les paramètres pertinents n'est pas vraiment un "juste", c'est presque le "tout". C'est l'une de mes plus grandes réserves sur l'approche du processus séparé ...
Kevin Montrose
Oui, c'est un peu la même solution que j'ai utilisée, mais j'ai sérialisé l'objet entier dans la base de données en tant que fichier binaire, puis je l'ai extrait pour l'exécuter. J'ai utilisé Cassandra comme stockage persistant et le planificateur de tâches en tant que planificateur CRON pour l'application en ligne de commande qui exécuterait et exécuterait les tâches.
Nick Berardi
Nous avons commencé par inclure simplement une simple donnée dans le message et avons fini par lancer l'objet entier. Cela fonctionnait toujours très bien. Je considérerais la séparation car elle présente également d’autres avantages.
Nathan Palmer
@ Kevin - si seulement nous avions des gens avec beaucoup d'histoire de sérialisation ....
Marc Gravell
4

Nous avons été plutôt satisfaits d’une approche Service Bus / Message Queue / Service. L'architecture de base est la suivante.

Le site Web envoie un message à la file d'attente

bus.Send(new ProjectApproved()); // returns immediately

Le service Windows reçoit et traite le message en temps voulu

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Do something "offline"
   }
}

L'avantage est qu'il n'y a pas de délai pour le service frontal auquel les utilisateurs sont également connectés. Le service Windows peut être arrêté et mis à niveau sans interruption du site principal. De plus, c'est extrêmement rapide .

Si vous ne pouvez pas stocker toutes vos données dans le message, vous pouvez toujours les stocker et les récupérer ultérieurement. Je suggère d'utiliser un mécanisme de stockage de documents tel que: RavenDB ou MongoDB où il est très simple de stocker vos classes sans modification.

Le site Web envoie un message à la file d'attente

// Save your object
store.Save(completeProject);

// Send a message indicating its ready to be processed
bus.Send(new ProjectApproved() { ProjectId = completeProject.Id });

Le service Windows reçoit et traite le message en temps voulu

public class DoesSomethingAwesome : ConsumerOf<ProjectApproved>
{
   public void Consume(ProjectApproved Message)
   {
      // Retrieve your object back
      var completeProject = store.Get(Message.ProjectId);
   }
}

Pour simplifier les choses, nous utilisons Rhino ESB et Topshelf . La configuration est extrêmement simple et sa mise en place pour une application existante s’est avérée prendre très peu de temps.

Nathan Palmer
la source
Quoi qu'il en soit, utiliser un bus de service avec CQRS est toujours un bon moyen d'améliorer votre évolutivité
thinkbeforecoding
3

Je suis curieux de savoir pourquoi une combinaison des deux n'est pas une option viable. En ce moment, vous déclenchez des travaux sur les pages vues, avec un peu de chance malchanceux bloqué qui attend 10 secondes pour que la page apparaisse. Au moins c'est ce que je comprends de votre méthode actuelle.

Cependant, l'exécution de ces tâches prend de plus en plus de temps à mesure que le site se développe et vous ne voulez pas nuire à l'expérience utilisateur sur le site. Pas même pour quelques (ou peut-être beaucoup) utilisateurs malchanceux tout au long de la journée, vous envisagez donc maintenant de planifier des tâches en arrière-plan.

Je ne vois pas pourquoi un travail d'arrière-plan exécuté à intervalles réguliers ne peut pas imiter un visiteur. Maintenant, je ne suis pas un programmeur Windows, mais dans le monde Linux, je mettrais en place un travail cron qui s'exécute à un intervalle régulier et qui aurait 2 lignes de code.

#!/bin/bash
wget -O /dev/null http://stackoverflow.com/specially_crafted_url

Il combine les avantages des deux systèmes. C'est fait en arrière-plan. Cela n'affecte pas les utilisateurs. Il utilise toujours une page pour lancer le travail. J'ai déjà vu cette approche utilisée auparavant. Cela tend à être le juste milieu entre les manières simples d’ancien et les voies les plus complexes à venir.

Mise à jour

Je pense que vous pouvez contourner le problème de l'équilibrage de la charge en exécutant les coureurs de travaux sur les serveurs Web eux-mêmes. Le lanceur de travaux extrait une URL de la file d'attente et l'exécute comme suit:

wget -O /dev/null http://localhost/specially_crafted_url

En raison de la nature des files d'attente de travail / de messagerie, les travaux seront répartis de manière égale entre les coureurs de travaux, ce qui signifie que la propriété special_crafted_url sera éventuellement distribuée sur vos serveurs Web.

Mellowsoon
la source
Nous le faisons déjà pour tout ce qui fonctionne à des intervalles prévisibles, ce qui nous reste sont des choses qui ne peuvent pas être prédites trop longtemps à l’avance. Par exemple, le "bloc de questions connexes" n’est mis à jour que pour les questions consultées récemment. De même, les listes de questions étiquetées ne sont mises en cache que si quelqu'un veut vérifier ces étiquettes. Étant donné que nous avons plus d'un million de questions et que nous approchons les 25 000 balises, nous ne pouvons pas exécuter toutes les tâches associées (et ce n'est que deux exemples) "au cas où."
Kevin Montrose
Il existe également des problèmes d'équilibre de charge, car le responsable de sécurité est réparti sur plusieurs serveurs. Fondamentalement, si vous allez à stackoverflow.com, vous frapperez toujours le même serveur. L’approche wget nous obligerait à regrouper toutes les tâches sur un seul serveur (ou à retravailler notre configuration d’équilibrage de charge), ce qui serait très pénible.
Kevin Montrose
Sois sympa si les choses se passaient à intervalles réguliers, hein? Je comprends ce que vous dites, mais la méthodologie décrite ci-dessus (et mentionnée par quelques autres personnes) ne change pas. Lorsqu'une page visualisée indique "il est temps d'exécuter ce travail", vous le collez dans une file d'attente de messages. Un travail d'arrière-plan de longue durée exécute les travaux qu'il trouve. Dans ce cas, les tâches ne sont rien de plus que des URL à demander. hehe Vous pouvez probablement configurer ceci sur un serveur partagé à 20 $ par mois, car il n'a pas besoin de votre base de code pour s'exécuter. Jetez un coup d'œil à Amazon SQS pour un service de messagerie convivial.
Mellowsoon
En ce qui concerne les problèmes d'équilibre de charge. Quand on veut, on peut! Au lieu de demander à stackoverflow.com, vous pouvez frapper un serveur au hasard en utilisant son adresse IP. Si l'équilibreur de charge vérifie les cookies pour canaliser les demandes, vous pouvez les simuler. S'il vérifie l'adresse IP, vous pourriez probablement même le simuler (puisque vous ne vous souciez pas de la réponse du serveur).
Mellowsoon
Convenu que l'équilibrage de la charge ne devrait pas être une raison pour ne pas le faire. Étant donné que la demande pour specially_crafted_urlprovient d'une adresse IP connue, vous pouvez ajouter une règle à votre équilibreur de charge pour effectuer un round-robin uniquement pour les demandes provenant de cette adresse IP.
Portman
2

Je pense que le problème avec l'approche purement axée sur le service est que le code est dispersé dans le service et éloigné de l'application principale.

Voici ce que nous avons fait avec des tâches d'arrière-plan de grande taille, non sensibles au facteur temps, qui permettent de conserver le code ensemble et de simplifier le service:

  1. Créer une file d'attente de travaux (en mémoire ou en base de données, quelle que soit la persistance requise pour les types de travaux)
  2. Créer un service Web qui exécutera les travaux en file d'attente
  3. Dead simple application de service qui appelle le service Web à un intervalle spécifié, laisse tous les éléments complexes (récupération et exécution de travaux) au service Web dans votre base de code.

Encore plus simple, il suffit de faire l'appel dans une application console et d'utiliser le Planificateur de tâches ou VisualCron pour en faire un "service".

Brandon
la source
1
J'ai exactement cela dans une application importante au travail - un service Windows qui déclenche l'application Web à intervalles réguliers. L'application Web reste sans état et extrait l'état de la base de données selon les besoins. Fonctionne un régal.
Bevan
1

J'ai aimé TopShelf. Conserve la simplicité, tout en conservant le bon fonctionnement en tant que service Windows. Fondamentalement, créez une application console, ajoutez environ 15 à 20 lignes de code, puis installez-la en tant que service.

http://code.google.com/p/topshelf/

Shane
la source
1

Que diriez-vous d’un service Windows très simple qui s’exécute sur le serveur Web et affiche périodiquement une URL de maintenance qui effectue vos tâches diverses. Demandez-lui de limiter le travail qu’il accomplit dans une demande donnée.

Rob Sobers
la source
1

Je vais inverser la tendance apparente ici et suggérer de choisir le modèle in-IIS. Je l'ai utilisé moi-même et cela fonctionne vraiment bien. Il n'est vraiment pas difficile d'implémenter une classe de pool de threads décente (au fil des années, j'ai élargi ma classe de pool de threads pour prendre en charge la création et la destruction dynamiques de threads, la nouvelle tentative de travaux, etc.). Les avantages sont:

  • Aucun service externe à surveiller
  • Simplicité de mise en œuvre: pas de regroupement des processus, pas de surveillance avancée des travaux
  • Vous êtes toujours dans votre processus IIS, vous pouvez donc effectuer toute votre journalisation habituelle, etc. (inutile de disposer de plusieurs fichiers journaux).
  • Déploiement considérablement simplifié (lorsque vous mettez à jour un service, vous devez arrêter le service, copier les fichiers, démarrer le service - cela s'ajoute à vos mises à jour habituelles du code du site Web)

À mon avis, une solution in-IIS est tout simplement la "prochaine étape" en combinant le travail à des vues de page aléatoires.

Dean Harding
la source
1

Resque est sympa. Ou même Kthxbye si vous devez être informé de la valeur obtenue une fois celle-ci complétée.

Tous deux basés à Redis / Ruby.

Honnêtement, si vous utilisez une approche basée sur les services, elle n’a pas vraiment besoin d’être super intégrée à votre plateforme actuelle, ce qui, à mon avis, est un avantage. J'espère que ce sera un système paramétrable qui fonctionnerait (avec une sorte de surveillance) et achèverait des travaux. Je ne suis pas sûr qu'il doive être exécuté sur la même plate-forme, car il ne fait que mettre à jour / modifier les informations de la base de données.

Je suis presque sûr que vous pourriez obtenir beaucoup plus pour beaucoup moins si vous exploitiez ce type de travail, mais plutôt parce que vous avez affaire à des problèmes de filetage. Les deux Resque et kthxbye déplacer le traitement vers des processus séparés pour permettre le système d' exploitation pour gérer la concurrence.

Resque

Kthxbye

Lukas
la source
Je dois essayer Kthxbye si seulement à cause du grand nom!
Nathan Palmer
à peu près génial. la prochaine sera l'ORLY? bibliothèque. probablement pour la surveillance des statistiques d'une certaine sorte ...;)
Lukas
0

J'utiliserais un service WCF hébergé par WAS en écoutant une file d'attente MSMQ.

Avantages

  • Feu et oublie les messages à sens unique de l'application Web

  • MSMQ / WCF étranglement et réessayez

  • Livraison garantie; D

  • Lettre morte gestion

  • Traitement distribué

  • Activation WAS / MSMQ

Les inconvénients

  • MSMQ (ce n'est pas mort ... encore)

Les fonctionnalités MSMQ dans WCF rendent l'utilisation de MSMQ vraiment agréable. Oui, vous saurez sur la configuration, mais les avantages l'emporteront sur les sacrifices.


la source
0

Je l'ai rencontré à quelques reprises lors du développement d'applications Web. Nous avons résolu ce problème en créant une application console Windows qui exécute la tâche et en créant une tâche planifiée qui s'exécute de temps en temps pour effectuer la tâche.

John Christensen
la source
0

Vous pouvez shunter un travail sur un fil d’arrière-plan (ou sur plusieurs fils d’arrière-plan) à l’aide de Rx et de l’apparence suivante:

var scheduler = new EventLoopScheduler( SchedulerThreadName );
_workToDo = new Subject<Action>();
var queueSubscription = _workToDo.ObserveOn( scheduler ).Subscribe( work => work() );
_cleanup = new CompositeDisposable( queueSubscription, scheduler );

Utiliser:

var work = () => { ... };
_workToDo.OnNext( work ); // Can also put on error / on complete in here

Hébergez tout cela dans une classe dont il n’existe qu’un seul (alias un singleton, mais faites-le correctement - utilisez votre conteneur IoC pour déterminer le mode de vie).

Vous pouvez contrôler la taille du pool de threads, etc. en écrivant un planificateur personnalisé au lieu d'utiliser EventLoopScheduler (qui exécute un seul thread).

Neal
la source
0

J'ai implémenté ce genre de chose à quelques reprises. Sur Windows, j'ai mis en place un programme en ligne de commande Python qui fait quelque chose à différents moments. Ce programme expose également une interface xmlrpc sur un port. Ensuite, un travail de tâche planifiée est exécuté toutes les minutes et interroge les interfaces xmlrpc. S'ils ne sont pas levés, il essaie de les lancer. S'il ne peut pas, il m'envoie un email.

L'avantage est que le travail qui s'exécute n'est pas lié à la charge ou à la planification. J'ai un travail de processus qui s'exécute toutes les secondes, mais j'attendrai de plus en plus longtemps avant de commencer un nouveau travail en fonction de la tâche à accomplir. En outre, il peut être utilisé pour agir intelligemment en fonction du résultat. Vous avez une erreur 500? Vous avez un très long délai? Fais autre chose. Notifier un autre service. Etc.

Et le même système fonctionne sous unix, avec des modifications mineures.

Christopher Mahan
la source
0

Je n'ai pas de réponse pour vous moi-même, mais le problème a sonné une cloche - je me souviens que des gars au hasard en ont discuté une fois sur un podcast .

Spolsky: J'ai remarqué que l'une des questions que vous avez posées sur le blog était la suivante: comment gérer les tâches de maintenance récurrentes en général?

Atwood: Oui.

Spolsky: Est-ce une caractérisation juste? Chaque site Web contient des tâches que vous ne souhaitez pas exécuter au moment du chargement d'une page Web, mais que vous souhaitez exécuter avec une certaine récurrence.

Atwood: Oui, les tâches de fond.

Spolsky: Oui, alors qu'as-tu découvert?

Atwood: Eh bien, je l’avais initialement demandé sur Twitter, car je voulais juste quelque chose de léger. Je ne voulais vraiment pas aimer écrire un service Windows. Je me sentais comme si c'était hors du code de la bande. De plus, le code qui fait le travail est en fait une page Web, car pour moi, une unité de travail logique sur un site Web est une page Web. Donc, c’est vraiment comme si nous rappelions sur le site Web, c’était comme une autre demande sur le site Web; j’ai donc considéré que cela devait rester en ligne et que la petite approche que nous avions suggérée nous avait été recommandée sur Twitter. consistait essentiellement à ajouter quelque chose dans le cache de l'application avec une expiration fixe; vous avez alors un rappel; ainsi, lorsqu'il expire, il appelle une fonction qui effectue le travail, puis vous le rajoutez dans le cache avec la même expiration.

Bizarre
la source
1
Oui, cela fonctionne pour des sites beaucoup plus petits que ce que StackOverflow est devenu. L'échelle est un gros problème ici, malheureusement (ou heureusement, selon la façon dont vous le regardez).
Kevin Montrose
@ Kevin Montrose, je plaide ici une ignorance complète du domaine. Pourriez-vous s'il vous plaît expliquer pourquoi avoir une page Web secrète effectuer le travail (peut-être par petites unités) et être appelé par un travail rafraîchissant de page / cron quelque part n'est pas évolutif? Je ne doute pas que vous ayez raison, mais j'aimerais apprendre.
Bizarre pensées
votre suggestion particulière (l'expiration du cache) ne s'adapte pas, car toutes les expirations de cache (dans ASP.NET) exécutent un seul thread (il s'agit d'un hack astucieux pour les sites plus petits, comme le faisait SO par le passé). Une tâche périodique ne s'adapte pas car notre serveur est devenu trop grand (le nombre de SO est maintenant de 3, et continue de grandir) et toute tâche périodique toucherait un seul serveur (au moins, changer cet invariant serait vraiment pénible avec notre charge. réglage de la balance). Une tâche périodique devrait également être exécutée très fréquemment, car ces tâches sont récurrentes de l'ordre de quelques minutes.
Kevin Montrose
Il est intéressant de noter que nous utilisons la planification "à la manière de cron" pour des exécutions moins fréquentes, des intervalles fixes, des tâches déjà existantes, telles que l’octroi de badges et les notifications quotidiennes par e-mail.
Kevin Montrose
0

Vue d'ensemble de l'API Java de la file d'attente de tâches

Concepts de tâche
Dans le traitement en arrière-plan d'App Engine, une tâche est une description complète d'une petite unité de travail. Cette description comprend deux parties:

  • Une charge de données qui paramètre la tâche.
  • Code qui implémente la tâche.

Les tâches en tant que crochets Web hors
connexion Heureusement, Internet fournit déjà une telle solution, sous la forme d'une requête HTTP et de sa réponse. La charge de données correspond au contenu de la demande HTTP, tel que des variables de formulaire Web, XML, JSON ou des données binaires codées. La référence de code est l'URL elle-même; le code réel correspond à la logique que le serveur exécute pour préparer la réponse.

antony.trupe
la source
Je ne suggère pas d'utiliser l'API de file d'attente des tâches GAE, mais de suivre leur modèle. Ils y ont réfléchi pendant un moment et en ont écrit une mise en œuvre.
antony.trupe
0

Faire les deux

Ajoutez un paramètre facultatif au chemin de la question qui effectue le travail que vous suivez actuellement sur les demandes des utilisateurs:

Gestion des tâches en arrière-plan sur un grand site

Créez une application de console qui s'exécute sur chaque serveur et ouvre le binaire partagé du journal IIS et le lit à la fin du fichier. Utilisez un système de fichiers ou un intervalle chronométré pour lire en avant afin de collecter les mises à jour lorsque IIS a vidé le journal.

Utilisez ces informations pour déterminer quelles pages ont été consultées.

Utilisez les URL de page du journal analysé pour appeler la version "extrastuff" de l’URL sur localhost avec un objet webclient.

Ajoutez du code pour changer de fichier à la fin de chaque période de journal ou redémarrez le processus à chaque période de journal.

Facture
la source