web-dev-qa-db-fra.com

Puis-je utiliser des threads pour effectuer des travaux de longue durée sur IIS?

Dans une application ASP.Net, l'utilisateur clique sur un bouton de la page Web, ce qui instancie ensuite un objet sur le serveur via le gestionnaire d'événements et appelle une méthode sur l'objet. La méthode passe à un système externe pour faire des choses et cela peut prendre un certain temps. Donc, ce que je voudrais faire, c'est exécuter cet appel de méthode dans un autre thread afin que je puisse retourner le contrôle à l'utilisateur avec "Votre demande a été soumise". Je suis raisonnablement heureux de le faire comme un feu et d'oublier, mais ce serait encore plus agréable si l'utilisateur pouvait continuer à interroger l'objet pour obtenir son statut.

Ce que je ne sais pas, c'est si IIS permet à mon thread de continuer à fonctionner, même si la session utilisateur expire. Imaginez, l'utilisateur déclenche l'événement et nous instancions l'objet sur le serveur et tirons la méthode dans un nouveau thread. L'utilisateur est satisfait du message "Votre demande a été soumise" et ferme son navigateur. Finalement, cette session d'utilisateurs expirera sur IIS, mais le thread peut toujours être en cours d'exécution, en cours de travail. IIS permettra-t-il au thread de continuer à fonctionner ou le tuera-t-il et supprimera-t-il l'objet une fois la session utilisateur expirée?

EDIT: D'après les réponses et les commentaires, je comprends que la meilleure façon de le faire est de déplacer le traitement de longue durée en dehors d'IIS. Mis à part tout le reste, cela traite du problème de recyclage du domaine d'application. Dans la pratique, j'ai besoin de faire décoller la version 1 en un temps limité et je dois travailler dans un cadre existant, donc je voudrais éviter la couche de service, d'où le désir de simplement lancer le thread dans IIS. Dans la pratique, "long terme" ne durera que quelques minutes et la concurrence sur le site Web sera faible, donc ça devrait aller. Mais, la prochaine version devra certainement être divisée en une couche de service distincte.

81
Frans

Vous pouvez accomplir ce que vous voulez, mais c'est généralement une mauvaise idée. Plusieurs moteurs de blog et CMS ASP.NET adoptent cette approche, car ils veulent être installables sur un système d'hébergement partagé et ne pas dépendre d'un service Windows qui doit être installé. En règle générale, ils lancent un long thread dans Global.asax au démarrage de l'application et ont ce processus de thread mis en file d'attente des tâches.

En plus de réduire les ressources disponibles pour IIS/ASP.NET pour traiter les demandes, vous rencontrez également des problèmes avec le thread tué lorsque l'AppDomain est recyclé, puis vous devez gérer la persistance de la tâche pendant son vol, comme ainsi que le redémarrage du travail lorsque l'AppDomain revient.

Gardez à l'esprit que dans de nombreux cas, l'AppDomain est recyclé automatiquement à un intervalle par défaut, ainsi que si vous mettez à jour le web.config, etc.

Si vous pouvez gérer à tout moment la persistance et les aspects transactionnels de la perte de votre thread, vous pouvez contourner le recyclage AppDomain en ayant un processus externe qui fait une demande sur votre site à un certain intervalle - de sorte que si le site est recyclé, vous sont garantis qu'il redémarre automatiquement dans les X minutes.

Encore une fois, c'est généralement une mauvaise idée.

EDIT: Voici quelques exemples de cette technique en action:

Serveur de communauté: utilisation des services Windows et du thread d'arrière-plan pour exécuter du code à intervalles réguliersCréation d'un thread d'arrière-plan au démarrage du site Web

EDIT (d'un avenir lointain) - Ces jours-ci, j'utiliserais Hangfire .

63
Bryan Batchelder

Je ne suis pas d'accord avec la réponse acceptée.

Utilisation d'un fil d'arrière-plan (ou d'une tâche, commencée par Task.Factory.StartNew) est correct dans ASP.NET. Comme pour tous les environnements d'hébergement, vous souhaiterez peut-être comprendre et coopérer avec les installations régissant l'arrêt.

Dans ASP.NET, vous pouvez enregistrer le travail devant s'arrêter correctement à l'arrêt à l'aide de HostingEnvironment.RegisterObject méthode. Voir cet article et les commentaires pour une discussion.

(Comme Gerard le souligne dans son commentaire, il y a maintenant aussi HostingEnvironment.QueueBackgroundWorkItem qui appelle RegisterObject pour enregistrer un planificateur sur lequel l'élément d'arrière-plan doit travailler. Dans l'ensemble, la nouvelle méthode est plus agréable car elle est basée sur les tâches.)

En ce qui concerne le thème général que vous entendez souvent dire que c'est une mauvaise idée, envisagez l'alternative de déployer un service Windows (ou un autre type d'application extra-processus):

  • Plus de déploiement trivial avec le déploiement Web
  • Non déployable uniquement sur les sites Web Azure
  • Selon la nature de la tâche en arrière-plan, les processus devront probablement communiquer. Cela signifie soit une certaine forme de IPC ou le service devra accéder à une base de données commune.

Notez également que certains scénarios avancés peuvent même nécessiter l'exécution du thread d'arrière-plan dans le même espace d'adressage que les requêtes. Je vois le fait que ASP.NET peut le faire comme un grand avantage qui est devenu possible grâce à .NET.

34
John

Vous ne voudriez pas utiliser un thread du pool de threads IIS pour cette tâche, car il ne permettrait pas à ce thread de traiter les demandes futures. Vous pourriez examiner Pages asynchrones dans ASP .NET 2. , mais ce ne serait vraiment pas la bonne réponse non plus. Au lieu de cela, il semblerait que vous bénéficieriez de la recherche dans Microsoft Message Queuing . Essentiellement, vous ajouteriez le les détails de la tâche dans la file d'attente et un autre processus d'arrière-plan (éventuellement un service Windows) seraient chargés d'exécuter cette tâche. Mais l'essentiel est que le processus d'arrière-plan est complètement isolé d'IIS.

7
senfo

Je suggère d'utiliser HangFire pour de telles exigences. C'est un joli feu et oubliez que le moteur tourne en arrière-plan, prend en charge différentes architectures, fiable car il est soutenu par un stockage persistant.

6
Vipul

Il y a un bon fil et un exemple de code ici: http://forums.asp.net/t/1534903.aspx?PageIndex=2

J'ai même joué avec l'idée d'appeler une page Keep Alive sur mon site Web à partir du fil pour aider à maintenir le pool d'applications en vie. N'oubliez pas que si vous utilisez cette méthode, vous avez besoin d'une très bonne gestion de la récupération, car l'application peut être recyclée à tout moment. Comme beaucoup l'ont mentionné, ce n'est pas la bonne approche si vous avez accès à d'autres options de service, mais pour l'hébergement partagé, cela peut être l'une de vos seules options.

Pour aider à maintenir le pool d'applications en vie, vous pouvez faire une demande à votre propre site pendant le traitement du thread. Cela peut aider à maintenir le pool d'applications en vie si votre processus s'exécute longtemps.

string tempStr = GetUrlPageSource("http://www.mysite.com/keepalive.aspx");


    public static string GetUrlPageSource(string url)
    {
        string returnString = "";

        try
        {
            Uri uri = new Uri(url);
            if (uri.Scheme == Uri.UriSchemeHttp)
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                CookieContainer cookieJar = new CookieContainer();

                req.CookieContainer = cookieJar;

                //set the request timeout to 60 seconds
                req.Timeout = 60000;
                req.UserAgent = "MyAgent";

                //we do not want to request a persistent connection
                req.KeepAlive = false;

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
                Stream stream = resp.GetResponseStream();
                StreamReader sr = new StreamReader(stream);

                returnString = sr.ReadToEnd();

                sr.Close();
                stream.Close();
                resp.Close();
            }
        }
        catch
        {
            returnString = "";
        }

        return returnString;
    }
5
Daniel

Nous avons commencé dans cette voie, et cela a fonctionné correctement lorsque notre application était sur un serveur. Lorsque nous voulions évoluer vers plusieurs machines (ou utiliser plusieurs w3wp dans un web garen), nous avons dû réévaluer et regarder comment gérer une file d'attente de travail, la gestion des erreurs, les tentatives et le problème délicat du verrouillage correct pour en garantir une seule. le serveur récupère l'élément suivant.

... nous avons réalisé que nous ne sommes pas dans le domaine de l'écriture de moteurs de traitement en arrière-plan, nous avons donc cherché des solutions existantes et nous avons atterri en utilisant le génial projet OSS hangfire

Sergey Odinokov a créé un véritable joyau qui est très facile à démarrer et vous permet de changer le backend de la façon dont le travail est persisté et mis en file d'attente. Hangfire utilise des threads d'arrière-plan, mais persiste les travaux, gère les tentatives et vous donne une visibilité dans la file d'attente de travail. Ainsi, les travaux de pendaison sont robustes et survivent à tous les caprices des domaines d'applications recyclés, etc.

Sa configuration de base utilise le serveur SQL comme stockage, mais vous pouvez échanger pour Redis ou MSMQ quand il est temps de passer à l'échelle supérieure. Il dispose également d'une excellente interface utilisateur pour visualiser tous les travaux et leur état, ainsi que pour vous permettre de re-mettre en file d'attente les travaux.

Mon point est que bien qu'il soit tout à fait possible de faire ce que vous voulez dans un thread d'arrière-plan, il y a beaucoup de travail pour le rendre évolutif et robuste. C'est bien pour les charges de travail simples, mais lorsque les choses deviennent plus complexes, je préfère de loin utiliser une bibliothèque spécialement conçue plutôt que de passer par cet effort.

Pour plus de perspective sur les options disponibles, consultez blog de Scott Hanselman qui couvre quelques options pour gérer les tâches d'arrière-plan dans asp.net. (Il a donné à hangfire une critique élogieuse)

Également comme référencé par John, il vaut la peine de lire blog de Phil Haack sur les raisons pour lesquelles l'approche est problématique et sur la façon d'arrêter gracieusement le travail sur le thread lorsque le domaine d'application est déchargé.

4
Avner

Vous pouvez exécuter des tâches en arrière-plan et elles se termineront même après la fin de la demande. Ne laissez pas une exception non capturée être levée . Normalement, vous voulez toujours lever vos exceptions. Si une exception est levée dans un nouveau thread, elle bloquera le processus de travail IIS - w3wp.exe , car vous n'êtes plus dans le contexte de la demande. Cela va également tuer toutes les autres tâches d'arrière-plan que vous avez en cours d'exécution en plus des sessions sauvegardées en mémoire si vous les utilisez. Ce serait difficile à diagnostiquer, c'est pourquoi la pratique est découragée.

2
Timothy Gonzalez

Pouvez-vous créer un service Windows pour effectuer cette tâche? Utilisez ensuite .NET à distance depuis le serveur Web pour appeler le service Windows pour effectuer l'action? Si tel est le cas, c'est ce que je ferais.

Cela éliminerait la nécessité de relayer sur IIS et limiterait une partie de sa puissance de traitement.

Sinon, je forcerais l'utilisateur à rester assis pendant que le processus est terminé. De cette façon, vous vous assurez qu'il est terminé et non tué par IIS.

2
David Basarab

Il semble qu'il existe une façon prise en charge d'héberger des travaux de longue durée dans IIS. Workflow Services semble conçu pour cela, en particulier conjointement avec Windows Server AppFabric . La conception permet le recyclage du pool d'applications en prenant en charge la persistance et la reprise automatiques du travail de longue durée.

2
Olly

Créez simplement un processus de substitution pour exécuter les tâches asynchrones; il n'est pas nécessaire que ce soit un service Windows (bien que ce soit l'approche la plus optimale dans la plupart des cas. MSMQ est bien plus que kill.

1
Matt Davison