У меня есть собственная WCF-служба (v4 framework), которая открывается через пользовательскую привязку HttpTransport
. Связывание использует пользовательский MessageEncoder
, который в значительной степени является BinaryMessageEncoder
с добавлением функций сжатия gzip.
Silverlight и клиент Windows потребляют веб-службу.
Проблема: в некоторых случаях службе приходилось возвращать очень большие объекты и изредка выбрасывать исключения OutOfMemory при ответе на несколько параллельных запросов (даже если диспетчер задач сообщил о ~ 600 Мб для процесса). Исключение произошло в пользовательском кодере, когда сообщение было сжато, но я считаю, что это всего лишь симптом, а не причина. Исключение указано "не удалось выделить x Мб", где x было 16, 32 или 64, а не чрезмерно огромным количеством - по этой причине я считаю, что еще что-то еще уже поставило процесс около некоторого предела до этого.
Конечная точка службы определяется следующим образом:
var transport = new HttpTransportBindingElement(); // quotas omitted for simplicity
var binaryEncoder = new BinaryMessageEncodingBindingElement(); // Readerquotas omitted for simplicity
var customBinding = new CustomBinding(new GZipMessageEncodingBindingElement(binaryEncoder), transport);
Затем я сделал эксперимент: я изменил TransferMode
с Buffered
на StreamedResponse
(и соответствующим образом изменил клиент). Это новое определение службы:
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);
Магически, никаких исключений OutOfMemory больше. Услуга немного медленнее для небольших сообщений, но разница становится все меньше и меньше по мере увеличения размера сообщения. Поведение (как для скорости, так и для исключений OutOfMemory) воспроизводимо, я провел несколько тестов с обеих конфигураций, и эти результаты были согласованы.
Проблема решена, НО: Я не могу объяснить, что здесь происходит. Мое удивление связано с тем, что я никак не менял контракт. То есть Я не создавал контракт с одним параметром Stream
и т.д., Как вы обычно делаете для потоковых сообщений. Я все еще использую свои сложные классы с тем же атрибутом DataContract и DataMember. Я только что изменил конечную точку, что все.
Я думал, что настройка TransferMode - это просто способ включения потоковой передачи для правильно сформированных контрактов, но, очевидно, есть нечто большее.
Может ли кто-нибудь объяснить, что на самом деле происходит под капотом, когда вы меняете TransferMode
?