web-dev-qa-db-fra.com

Web Api - Feu et oublie

J'ai une action de l'API Web où je dois exécuter une tâche et oublier cette tâche . Voici comment ma méthode est organisée maintenant:

public async Task<SomeType> DoSth()
{
    await Task.Run(...);
    .....
    //Do some other work
}

Le problème, c’est qu’il est évident qu’il s’arrête à la file d’attente en attente et que c’est alors seulement que le travail se poursuit. Et j'ai besoin de "tirer et oublier" Dois-je simplement appeler Task.Run () sans aucune attente async?

21
D. Jones

Et j'ai besoin de "tirer et oublier"

J'ai un article de blog qui décrit en détail plusieurs approches différentes pour fire-and-oublier sur ASP.NET .

En résumé: essayez d'abord de ne pas faire de feu et d'oublier. C'est presque toujours une mauvaise idée. Voulez-vous vraiment «oublier»? Comme dans, ne vous souciez pas de savoir si cela se termine ou non? Ignorer les erreurs? Accepter des "travaux perdus" occasionnels sans aucune notification de journal? Presque toujours, la réponse est non, feu-et-oublier n'est pas la bonne approche.

Une solution fiable consiste à construire une architecture distribuée appropriée. Autrement dit, créez un message qui représente le travail à effectuer et mettez-le en file d'attente dans une file d'attente fiable (par exemple, file d'attente Azure, MSMQ, etc.). Créez ensuite un serveur indépendant qui traite cette file d'attente (par exemple, Azure WebJob, le service Win32, etc.).

Devrais-je simplement appeler Task.Run () sans attendre async?

C'est la pire solution possible. Si vous devez faire feu et oublier, et que vous ne voulez pas construire une architecture distribuée, alors considérez Hangfire. Si cela ne fonctionne pas pour vous, alors au moins très, vous devez enregistrer votre travail d'arrière-plan de cow-boy avec le runtime ASP.NET via HostingEnvironment.QueueBackgroundWorkItem ou ma bibliothèque ASP/NET Background Task . Notez que QBWI et AspNetBackgroundTasks sont des solutions peu fiables. ils ne font que minimiser le risque de perdre du travail, et non le prévenir.

20
Stephen Cleary

Vrai feu et oublier les tâches peuvent être difficiles dans asp.net car ils peuvent souvent mourir avec la demande à laquelle ils ont été créés. 

Si vous utilisez la version 4.5.2+, vous pouvez utiliser QueueBackgroundWorkItem pour exécuter la tâche. En enregistrant des tâches via cette méthode, AppDomain essayera de retarder la fermeture jusqu'à ce qu'ils soient tous terminés, mais il peut toujours y avoir des cas où ils seront tués avant d'être terminés. C’est probablement la chose la plus simple à faire, mais il vaut la peine d’être lu pour voir exactement quelles instances peuvent entraîner l’annulation de tâches.

HostingEnvironment.QueueBackgroundWorkItem(async cancellationToken =>
{
  await Task.Run(...);
});

Il existe un outil appelé hangfire qui utilise un magasin persistant pour s’assurer que la tâche est terminée et qu’il dispose d’une fonctionnalité intégrée de nouvelle tentative et d’enregistrement des erreurs. C’est plus pour les «tâches d’arrière-plan» mais convient au feu et oublie. Ceci est relativement facile à installer et offre une variété de magasins de sauvegarde, je ne me souviens pas des détails exacts mais certains nécessitent une licence et d'autres non (comme MSSQL).

5
Fermin

Pour le feu et oublier, utilisez cette

Task.Factory.StartNew(async () =>
{
    using (HttpClient client = new HttpClient())
    {
        await client.PostAsync("http://localhost/api/action", new StringContent(""));
    }
});
4
Catalin

J'utilise HangFire .

C'est mieux pour moi.

Un moyen facile d'effectuer le traitement en arrière-plan dans les applications .NET et .NET Core. Aucun service Windows ou processus séparé requis.

Soutenu par le stockage persistant. Ouvert et gratuit pour un usage commercial. 

3
Majid gharaei

Ne tirez jamais et oubliez, car vous ne verrez alors aucune erreur, ce qui rend le dépannage très fastidieux en cas de problème (le fait que la méthode de tâche effectue sa propre gestion des exceptions ne fonctionne pas forcément, car la tâche risque de ne pas fonctionner. commencer avec succès en premier lieu). À moins que vraiment ne vous dérange pas si la tâche fait quelque chose ou non, mais c'est assez inhabituel (puisque, si vous vous en ficheiez, pourquoi exécuter la tâche en premier lieu)? À tout le moins, créez votre tâche avec un continuation :

Task.Run(...)
  .ContinueWith(t => 
    logException(t.Exception.GetBaseException()),
    TaskContinuationOptions.OnlyOnFaulted
  )
;

Vous pouvez rendre cela plus sophistiqué selon vos besoins.

Dans le cas spécifique d'une API Web, vous souhaiterez peut-être attendre que vos tâches d'arrière-plan soient terminées avant de terminer votre demande. Si vous ne le faites pas, vous laissez des éléments en tâche de fond qui peuvent donner une idée fausse de la charge que votre service peut réellement supporter, voire même cesser de fonctionner si les clients envoient trop de demandes et que vous ne faites rien pour les limiter. Vous pouvez regrouper des tâches et émettre une await Task.WhenAll(...) à la fin pour y parvenir; De cette façon, vous pouvez continuer à faire un travail utile pendant que vos tâches d'arrière-plan se déroulent, mais vous ne revenez pas avant que tout soit terminé.

2
Jeroen Mostert

Je suis d’accord avec les autres pour dire que vous ne devez pas oublier votre appel. Toutefois, pour répondre à votre question, si vous supprimez wait de la ligne Task.Run (), l'appel ne sera pas bloqué comme indiqué ici

public async Task<SomeType> DoSth()
{
    Task.Run(...);
    .....
    //Do some other work while Task.Run() continues in parallel.
}
0
Avinash Kumar

Il y a quelques messages ici qui poussent le "Ne jamais tirer et oublier", c'est faux, etc.

La vérité est que ce n’est pas faux à certains points de vue. Plutôt mal décrit à certains égards.

Ce que cela devrait vraiment dire, c’est «l’utilisateur devrait pouvoir tirer et oublier, mais il devrait quand même en informer le développeur/propriétaire».

Un bon exemple est un formulaire de contact sur un site Web. Le client remplit le formulaire et, dans les coulisses, il envoie un courrier électronique au propriétaire du site. Certains serveurs de messagerie prennent beaucoup de temps à traiter l'envoi, aussi des paquets comme Hangfire (ce que je voudrais utiliser) l'envoient à un autre processus en dehors du "thread" du serveur Web.

Oui, cela devrait en quelque sorte informer le développeur/propriétaire (et conserver les détails du contact) en cas d'erreur. Mais cela ne doit en aucun cas informer le contact potentiel de la question. (sauf si vous voulez perdre un client potentiel)

0
William Humphreys

Pour invoquer un incendie et oublier la méthode WebApi, j'ai utilisé le code suivant pour s'assurer qu'il renvoie une réponse OK . Dans mon cas, le jeton d'autorisation au porteur créé lors de la connexion est stocké dans un cookie:

...
FireAndForget().Wait();
...

private async Task FireAndForget()
    {
        using (var httpClient = new HttpClient())
        {
            HttpCookie cookie = this.Request.Cookies["AuthCookieName"];
            var authToken = cookie["AuthTokenPropertyName"] as string;
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken);
            using (var response = await httpClient.GetAsync("http://localhost/api/FireAndForgetApiMethodName"))
            {
                //will throw an exception if not successful
                response.EnsureSuccessStatusCode();
            }
        }
    }
0
CAK2