0
votes

Comment puis-je exécuter du code qui n'a que ASYNC API sans aucune impasse dans un environnement où je sais que la piscine de thread sera saturée?

J'essaie d'écrire des méthodes d'assistance pour appeler de biztalk. BizTalk ne comprend pas les tâches ou async / attendre, de sorte que les méthodes d'assistant doivent renvoyer un type de retour normal .NET non soulevé dans une tâche .

Les méthodes d'assistance utilisent identitémodel.clients.ACTRÉDIRECTORY bibliothèque et ultérieurement httpclient pour appeler à un appel à un http- API basée sur la cache de ce résultat. Ces classes ne disposent que d'une API asynchrone, c'est-à-dire toutes les méthodes de retour de travail Tâche et terminez par -ASync.

La façon dont BizTalk gère sa piscine de thread. que la piscine de thread sera saturée (à une valeur par défaut de 25 fils de travail) s'il existe une charge de message élevée; Par exemple, un grand nombre de fichiers ont été supprimés à la fois - il s'agit d'un scénario réalisable dans une utilisation normale et non un problème. Je l'ai observé via le débogage.

Lorsque le code d'assistant fait un appel d'API, cela est assez coûteux car il renvoie beaucoup de données, et je veux seulement qu'un appel soit en cours à la fois. Si toute la mise en œuvre était synchrone, j'utiliserais simplement une instruction verroue autour de l'actualisation du cache, car le délai dans les messages de traitement est acceptable pour assurer la synchronisation. Le verrouillage a été démontré sur une impasse, ce qui a du sens pour moi, car l'architecture d'application garantit essentiellement qu'aucun thread ne sera disponible pour compléter les méthodes asynchrones. Il s'agit d'un cas plus extrême des conseils communément donnés de ne pas verrouiller dans le contexte du code asynchrone, en ce que ce n'est pas seulement probable mais certain à une impasse.

J'ai essayé d'utiliser Semaphoreslim .WAITASYNC Pour activer l'équivalent au verrouillage, mais de manière non bloquante, c'est toujours empêcher plus d'un fil d'entrer dans le bloc, mais en leur permettant de générer au lieu de blocage. Cela ne résout pas le problème, car les méthodes d'assistance de niveau supérieur doivent toujours bloquer pour attendre la mise à jour du cache. Mon hypothèse est que le moment où cette attente donne le fil, il est ensuite avalé de traiter un nouveau message - qui le bloque ensuite, empêchant le fil entré dans le sémaphore de continuer.

Le pseudocode suivant (c.-à-d. N'essayez pas de vous corriger et de me corriger sur des problèmes de style de codage non pertinents pour le problème. Il ne peut aussi pas être un MCVE, sauf si vous avez une installation de BizTalk Handy) illustre le problème que j'essaie de résoudre: < / p> xxx

  • est ma compréhension de ce que le problème fondamental est ici correct?
  • Comment puis-je le résoudre?

3 commentaires

"Async tout le chemin" n'est pas une option, je suppose?


@Fildor Nope, pas aussi loin que je sache. Comme je l'ai mentionné la méthode publique à laquelle on parle ne peut pas être asynchronisé parce que l'appelant ne sait pas comment déballer cette tâche dans son résultat, comme le compilateur C #, le C # ne le fera jamais.


Avez-vous réussi à résoudre ce problème?


3 Réponses :


0
votes

Je dois dire d'abord, on dirait que vous créez beaucoup de problèmes inutiles en essayant de faire tout ce genre de fantaisie dans une classe d'assistance. Parce que A) Il n'y a vraiment pas de cas pratique pour les opérations asynchrones dans une classe d'assistance, car il ne changera pas de temps de traitement des messages, b) Vous devriez pas appeler des points d'extrémités HTTP dans une classe d'assistance .

Donc, la solution correcte consiste à appeler le point d'extrémité HTTP à l'aide d'un port de livraison de livraison commandé . .

Je dis ordonnai la livraison à cause de cela: "Et je veux seulement qu'un appel soit en cours à la fois", mais ... ça ne compte pas ce que tu veux , tu devrais Faites des appels série uniquement si le service le requiert ou a des problèmes de capacité.

Si vous souhaitez mettre en cache interne les résultats, il existe de nombreux exemples tels que celui-ci ici (toutes les règles .NET s'appliquent): Mixez la mise en cache pour vos applications BizTalk à l'aide de classes et méthodes statiques


7 commentaires

Je suis d'accord avec vous et préférerais ne pas le faire de cette façon, mais ce n'est pas ma décision. Malheureusement, cela ne répond pas à la question.


@Tomw Alors comment pouvons-nous vous aider à démontrer que quiconque fait cette décision est faux? On dirait qu'ils ne comprennent pas comment Biztalk ou Async in .Net fonctionne. Et oui, cela répond à 100% à la question parce que vous ne devriez pas utiliser ce modèle dans Biztalk.


Mon point de vue est que c'est que c'est biztalk, et le fait que le travail lent est une demande d'API, est sans conséquence et la viande de la question est la pontage d'ASYNC avec des appels de méthode synchrones. Je pense qu'il devrait être possible de faire fonctionner le travail de synchronisation, le scénario exact n'est pas si important. J'ai ajouté ce détail parce que s'il était absent, quelqu'un se plaint, non pas parce que je pense que c'est du matériel au problème.


@Tomw Eh bien, oui, ce que vous voulez faire est très, très basique et très, très, très commun et biztalk, comme une plate-forme, fournit des méthodes, des modèles et des fonctionnalités à faire exactement cela. Vous choisissez de faire quelque chose de complètement inutile et, sur biztalk, mal. Pardon. C'est comme essayer d'utiliser ADO pour se connecter à Oracle dans une procédure stockée SQL Server ... Vous pouvez, mais c'est juste ... mal. Bonne chance, mais vous essayez de résoudre un problème que vous ou «architecte» créé.


Johns a raison, si vous devez appeler une classe d'assistance de Biztalk pour parler à un service Web, vous le faites mal. Vous perdrez toutes les capacités intégrées de biztalk telles que la gestion des erreurs, les tentatives automatiques, la persistance et la multi-threading.


@Dijkgraaf a accepté. Mais ce n'est pas la question que j'ai posée.


où étais-tu?! Aucune activité depuis le 1er juillet!



2
votes

Votre question est en fait "Comment puis-je synchroniser-over-async lorsque la piscine de thread est saturée" et la seule réponse réelle est "Vous ne pouvez pas". Le problème avec synchronisation-over-async est qu'il bloque un thread, puis peut nécessiter un autre thread pour débloquer celui-là.

Une chose que vous pouvez essayer est d'installer votre propre contexte (temporairement) sur votre fil. J'ai un asynccontext type dans mon bibliothèque Asyncex que peut le faire. Donc, à votre point d'entrée BizTalk, vous pouvez utiliser cela au lieu de bloquer directement: xxx

Ceci permettra attendre continuations à exécuter sur votre propre thread par défaut. Il se comporte un peu similaire à une boucle de message d'interface utilisateur (juste sans UI).

Malheureusement, vous ne pouvez pas garantir que cela fonctionnera toujours, car les continuations ne capturent que ce contexte par défaut . Et pour les bibliothèques générales telles que ActiveDirectory et httpclient , le contexte de capture est considéré comme une mauvaise pratique; La plupart des codes de bibliothèque se déplacent d'utiliser la piscine de thread en utilisant toujours configureawait (false) .

donc, le seul moyen d'éviter les blocages dans le code Sync-Overnc est de s'assurer que la piscine de thread n'est pas saturée. S'il y avait un moyen de limiter BizTalk à une certaine valeur et que la piscine de fil est plus grande que cela fonctionnerait.

une solution beaucoup plus idéale serait d'aller "synchroniser tout le chemin". Vous voudriez remplacer httpclient avec webclient , mais à partir de votre description, il ressemble à activedirectory ne prend pas en charge les API de synchronisation.

Vous pourriez vous échapper avec une solution hybride: Utilisez WebClient pour appeller l'API Synchrone et enveloppez tous les appels ActiveDirectory dans asynccontext.run . Je pense que ceci pourrait travail car l'équipe ASP.NET CORE a supprimé la plupart / tous les ConfigureAEAIT (FALSE) Appels dans leur code. Ainsi, l'API HTTP serait synchrone et le activedirectory serait asynchrone mais avec ses continuations exécutées dans le fil qui l'attend (ne nécessitant pas de filetage de piscine de thread).

Mais même si vous obtenez ce travail, cela n'est pas garanti à l'avenir. Manquant configureawait (FALSE) peut être considéré comme un "bogue", et s'ils "corrigent" le bug en ajoutant configureawait (false) retour, alors votre code serait soumis Pour des blocages à nouveau.

La seule solution vraiment garantie serait de forcer les API asynchrones à être synchrones, en écrivant votre propre webapi proxy qui enveloppe les appels ActiveDirectory . Ensuite, votre code BizTalk parlerait à cette API (et l'autre API) à l'aide de WebClient , et tout le code BizTalk serait synchrone à ce point.


1 commentaires

Merci! Cela confirme à peu près ce que je soupçonnais mais je ne voulais pas accepter. La seule idée ruinante restante pour moi est de savoir si ma compréhension de la gestion du thread de BizTalk est exacte, alors tout le monde en lisant - ne prenez pas ma parole pour cela. La nécessité d'utiliser des hacks pour faire de ce travail (de manière fiable ?!) suffit d'une justification de réécrire avec une approche différente.



0
votes

Si le nombre de threads est le problème principal, vous pouvez réduire ou augmenter le nombre de threads en ajoutant l'adresse et le paramètre MaxConnection dans la section ConnectionMangement du fichier BTSNTSVC64.EXE.CONFIG ou BTSNTSVC.EXE.CONFIG s'il est 32 bits. Ou comme le dit Johns dans sa réponse, si vous le souhaitez, cochez la livraison ordonnée sur le port d'envoi.

    <system.net>
        <connectionManagement>
            <add address="*" maxconnection="25"   />

            <add address="https://LIMITEDSERVER.com*" maxconnection="5"  />
            <add address="https://GRUNTYSERVER.com*" maxconnection="50"  />
       </connectionManagement>
    </system.net>


0 commentaires