WCF HttpTransport: streamed vs buffered TransferMode

j'ai un service de WCF auto-hébergé (v4 framework) qui est exposé à travers un HttpTransportpersonnalisé basé sur la liaison. La reliure utilise une coutume MessageEncoder c'est à peu près un BinaryMessageEncoder avec l'ajout de la fonctionnalité de compression gzip.

un client Silverlight et Windows utilisent le service web.

Problème: dans certains cas, le service a dû rendre de très gros objets et a parfois jeté des exceptions hors de la mémoire en répondant à plusieurs requêtes simultanées (même si le Gestionnaire des tâches a rapporté ~600 Mo pour le processus). L'exception s'est produite dans l'encodeur personnalisé, quand le message était sur le point d'être compressé, mais je crois que c'était juste un symptôme et pas la cause. L'exception a déclaré "n'a pas attribué X Mb" où x était 16, 32 ou 64, pas un montant trop énorme -pour cette raison, je crois que quelque chose d'autre déjà mis le processus près d'une certaine limite avant cela.

le point de service est défini comme suit: suit:

var transport = new HttpTransportBindingElement(); // quotas omitted for simplicity
var binaryEncoder = new BinaryMessageEncodingBindingElement(); // Readerquotas omitted for simplicity
var customBinding = new CustomBinding(new GZipMessageEncodingBindingElement(binaryEncoder), transport);

Ensuite, j'ai fait une expérience: j'ai changé TransferModeBufferedStreamedResponse (et modifié le client en conséquence). C'est la nouvelle définition de la prestation:

var transport = new HttpTransportBindingElement()
{
    TransferMode = TransferMode.StreamedResponse // <-- this is the only change
};
var binaryEncoder = new BinaryMessageEncodingBindingElement(); // Readerquotas omitted for simplicity
var customBinding = new CustomBinding(new GZipMessageEncodingBindingElement(binaryEncoder), transport);

comme par Magie, pas de dépassement de mémoire exceptions plus. Le service est un peu plus lent pour les petits messages, mais la différence devient de plus en plus petite à mesure que la taille du message augmente. Le comportement (à la fois pour la vitesse et les exceptions OutOfMemory) est reproductible, j'ai fait plusieurs tests avec les deux configurations et ces résultats sont cohérents.

Problème résolu, mais: je ne peux pas m'expliquer ce qui se passe ici. Ma surprise vient du fait que je n'ai pas modifier le contrat en quelque sorte. I. e. Je n'ai pas de créer un contrat avec un seul Stream paramètre, etc., comme vous le faites habituellement pour les messages diffusés. J'utilise toujours mes classes Complexes avec le même attribut DataContract et DataMember. je viens de modifier le point de terminaison, c'est tout.

je pensais que la mise TransferMode était juste un moyen d' activer streaming pour correctement formé des contrats, mais évidemment, il est plus que cela. Est-ce que quelqu'un peut expliquer ce qui se passe réellement sous le capot quand vous changez <!--5?

19
demandé sur Francesco De Vittori 2010-10-28 18:09:07

4 réponses

comme vous utilisez 'GZipMessageEncodingBindingElement', je suppose que vous utilisez L'échantillon MS GZIP.

regardez DecompressBuffer() in GZipMessageEncoderFactory.cs et vous comprendrez ce qui se passe en mode tampon.

Pour l'exemple, disons que vous avez un message de taille non compressée 50M, taille compressée 25M.

DecompressBuffer recevra un param de 'ArraySegment buffer' de (1) 25M taille de l'. La méthode créera alors un MemoryStream, décompressez le tampon, en utilisant (2) 50M. Ensuite, il va faire un MemoryStream.ToArray(), la copie du flux de mémoire tampon dans un nouveau (3) 50M grand tableau d'octets. Puis il faut un autre byte array du BufferManager D'au moins (4) 50M+, en réalité, il peut être beaucoup plus - dans mon cas, il était toujours 67M pour un tableau de 50M.

À la fin de DecompressBuffer, (1), seront retournés à la BufferManager (qui semble ne jamais obtenir effacé par la WCF), (2) et (3) sont soumis à la GC (qui est asynchrone, et si vous êtes plus rapide que la GC, vous pourriez obtenir OOM des exceptions, même s'il y aurait assez de mem si nettoyées). (4) sera probablement rendu au BufferManager dans votre BinaryMessageEncodingBindingElement.ReadMessage ().

pour résumer, pour votre message de 50M, votre scénario tamponné prendra Temporairement 25 + 50 + 50 + par exemple 65 = 190!--9 -- > mémoire, dont certaines sont sujettes à une GC asynchrone, d'autres à une GC asynchrone. géré par le BufferManager, ce qui - dans le pire des cas - signifie qu'il conserve en mémoire un grand nombre de tableaux inutilisés qui ne sont ni utilisables dans une requête subséquente (par exemple trop petits) ni admissibles au GC. Imaginez maintenant que vous avez plusieurs requêtes simultanées, dans ce cas BufferManager va créer des tampons séparés pour toutes les requêtes simultanées, qui seront jamais être nettoyé, sauf si vous appelez manuellement BufferManager.Clear(), et je ne sais pas de manière à le faire avec le tampon utilisé par les gestionnaires de fonds de roulement, voir aussi cette question: Comment puis-je empêcher BufferManager / PooledBufferManager dans mon application client de la FMC de gaspiller de la mémoire?]

mise à Jour: après avoir migré vers la Compression Http IIS7 ( WCF compression conditionnelle)consommation de mémoire, charge cpu et temps de démarrage diminué (ne pas avoir les numéros à portée de main) et puis la migration de tampon en streaming TransferMode ( Comment puis-je prévenir BufferManager / PooledBufferManager dans mon L'application client WCF de wasting memory?)la consommation de mémoire de mon application client WCF a chuté de 630M (pic) / 470M (continu) à 270M (pic et continu)!

17
répondu Eugene Beresovsky 2017-05-23 12:25:24

j'ai une certaine expérience avec la WCF et le streaming.

EN GROS, si vous ne mettez pas le TransferMode en streaming, puis ce sera par défaut à la mise en mémoire tampon. Donc, si vous envoyez de gros morceaux de données, il va construire les données de votre côté en mémoire et puis l'envoyer une fois que toutes les données sont chargées et prêtes à être envoyées. C'est pourquoi vous étiez sorti des erreurs de mémoire parce que les données étaient très grandes et plus que la mémoire de votre machine.

Maintenant, si vous utilisez en streaming, alors il va immédiatement commencer à envoyer des morceaux de données à l'autre extrémité au lieu de mettre en tampon, ce qui rend l'utilisation de la mémoire très minime.

mais cela ne signifie pas que le récepteur doit aussi être configuré pour le streaming. Ils peuvent être configurés pour servir de tampon et connaîtront le même problème que l'expéditeur s'ils n'ont pas suffisamment de mémoire pour vos données.

pour obtenir les meilleurs résultats, les deux paramètres devraient être configurés pour traiter la diffusion en continu (pour les données volumineuses). fichier.)

typiquement, pour le streaming, vous utilisez MessageContracts au lieu de DataContracts parce qu'il vous donne plus de contrôle sur la structure du savon.

voir ces articles du MSDN sur MessageContracts et Datacontracts pour en savoir plus. Et voici plus d'infos sur tamponné vs strié.

8
répondu Bryan Denny 2010-10-28 14:42:07

je pense (et je peux me tromper) que, de restreindre les utilisateurs à Stream paramètre dans les contrats d'exploitation qui utilisent Streamed mode Transfert, vient du fait que WCF met des données de flux dans la section de corps de message SOAP et commence à les transférer que l'utilisateur commence à lire le flux. Donc, je pense qu'il aurait été difficile pour eux de multiplexer le nombre arbitraire de flux dans un seul flux de données. E. g, supposons que vous ayez un contrat d'exploitation avec 3 paramètres de flux et 3 différents threads sur le client commencent à lire à partir de ces trois streams. Comment Pouvez-vous faire cela sans utiliser un algorithme et une programmation supplémentaire pour multiplexer ces trois flux de données (ce qui manque à la WCF en ce moment)

en ce qui concerne votre autre question, il est difficile de dire ce qui se passe réellement sans voir votre code complet, mais je pense qu'en utilisant gzip, vous compressez en fait toutes les données du message dans un tableau de Octets, le remettant à la WCF et du côté client, quand le client demande le message SOAP, le canal sous-jacent ouvre un flux pour lire le message et WCF canal pour le transfert en continu, commence à diffuser les données comme il était le corps du message.

de toute façon, vous devriez noter que le réglage MessageBodyMember attribut indique juste WCF que ce membre devrait être streamé comme le corps SOAP, mais quand vous utilisez l'encodeur personnalisé et la liaison, c'est surtout votre choix ce à quoi ressemblera le message sortant.

0
répondu Arashv 2014-06-25 07:39:18

Tampon: il doit mettre tout le fichier en mémoire avant de les télécharger/téléchargement. cette approche est très utile pour transférer de petits fichiers, en toute sécurité.

en Streaming: le fichier peut être transférée sous forme de blocs.

-1
répondu user3240440 2015-08-10 11:39:23