Передача больших полезных данных (сериализованных объектов) с использованием wsHttp в WCF с защитой сообщений

У меня есть случай, когда мне нужно передать большое количество сериализованных графов объектов (через NetDataContractSerializer), используя WCF, используя wsHttp. Я использую безопасность сообщений и хотел бы продолжать это делать. Используя эту настройку, я хотел бы перенести сериализованный граф объектов, который иногда может приближаться к 300 МБ или около того, но когда я пытаюсь это сделать, я начинаю видеть исключение типа System.InsufficientMemoryException.

После небольшого исследования выясняется, что по умолчанию в WCF результат вызова службы содержится в одном сообщении по умолчанию, которое содержит сериализованные данные, и эти данные по умолчанию буферизуются на сервере до тех пор, пока все сообщение не будет полностью записано. Таким образом, исключение памяти вызвано тем фактом, что серверу не хватает ресурсов памяти, которые ему разрешено выделять, поскольку этот буфер заполнен. Две основные рекомендации, с которыми я столкнулся, - это использование потоковой передачи или разбиения на фрагменты для решения этой проблемы, однако мне не ясно, что это включает и возможно ли какое-либо решение с моей текущей настройкой (wsHttp/NetDataContractSerializer/Message Security). До сих пор я понимаю, что использование безопасности потоковых сообщений не сработает, поскольку шифрование и дешифрование сообщений должны работать со всем набором данных, а не с частичным сообщением. Чанкинг, однако, звучит так, как будто это возможно, однако мне не ясно, как это будет сделано с другими ограничениями, которые я перечислил. Если бы кто-нибудь мог предложить какое-то руководство о том, какие решения доступны и как его реализовать, я был бы очень признателен.

Я должен добавить, что в моем случае я действительно не беспокоюсь о совместимости с другими клиентами, так как мы владеем и контролируем каждую сторону связи и используем шаблон общего интерфейса для данных, передаваемых любой стороне. Так что я открыт для любой идеи, которая вписывается в ограничения использования wsHttp с защитой сообщений для передачи графов объектов, сериализованных с использованием NetDataContractSerializer, и я предпочитаю решение, в котором мне не нужно резко менять существующие сервисы и окружающую инфраструктуру.

Связанные ресурсы:

Я также интересуюсь любым типом сжатия, которое может быть выполнено с этими данными, но, похоже, мне лучше было бы сделать это на транспортном уровне, как только я смогу перейти на.NET 4.0, чтобы клиент автоматически поддерживал gzip Заголовки, если я правильно понимаю.

Обновление (2010-06-29):

Некоторая история о том, как я узнал о том, что буферизованное сообщение было слишком большим, вызывала мою проблему. Первоначально я видел CommunicationException ниже во время тестирования.

Основное соединение было закрыто: соединение было неожиданно закрыто.

В конце концов, после выполнения этого и внесения дополнительных изменений в журналы, я обнаружил лежащее в основе исключение InsufficientMemoryException, которое вызывало проблему с указанным сообщением.

Не удалось выделить буфер управляемой памяти размером 268435456 байт. Объем доступной памяти может быть низким.

Который возник из следующего метода.

System.ServiceModel.Diagnostics.Utility.AllocateByteArray (размер Int32)

Другими словами, сбой произошел из-за выделения массива. При записи одних и тех же данных, сериализованных на диск, это занимает около 146 МБ, и если я урежу их вдвое, я перестану получать сообщение об ошибке, однако я больше не копался в конкретном пороговом значении, которое нарушает мой буфер, и в зависимости от того, относится ли оно к моей системе или не.

Обновление (2010-12-06):

Я думаю, на данный момент я ищу некоторые разъяснения для следующего. Насколько я понимаю, по умолчанию с WCF wsHttp с защитой сообщений, что целое сообщение (как правило, весь набор данных, которые я возвращаю) необходимо буферизовать на сервере, прежде чем ответ будет отправлен обратно клиенту, что вызовет мои проблемы.

Возможные решения:

  • Ограничение размера данных - с использованием некоторой формы сжатия, кодирования или ограничения фактических данных, возвращаемых с использованием некоторого метода, подобного поисковому вызову, чтобы избежать использования максимальной емкости исходящего буфера.
  • Потоковая передача - позволяет передавать большие объемы данных через WCF потоковым способом, однако это несовместимо с wsHttp или MessageSecurity, поскольку эти методы требуют буферизации всех данных.
  • Chunking Channel - позволяет разбивать данные на отдельные сообщения, но на данный момент я не уверен в ограничениях, которые это имеет для дизайна контракта на обслуживание, и могу ли я все еще использовать wsHttp с привязкой сообщений.

Ограничение данных, которые я могу вернуть, работает только до определенного момента, и, как и при использовании параметра "Потоковая передача", эти параметры требуют кодирования большей части работы более низкого уровня за пределами вызовов службы WCF. Поэтому я думаю, что мне нужно знать, может ли любая возможная реализация канала чанкинга обойти большие проблемы с сообщениями, позволяя разбивать один набор данных на отдельные сообщения на сервере и затем объединять их на клиенте в таким образом, что мне не нужно менять интерфейс / форму существующих сервисных контрактов и таким образом, что процесс в значительной степени скрыт от клиентской и серверной части каждой реализации сервиса, все еще используя защиту сообщений и wsHttp. Если канал chunking потребует, чтобы я переписал свои сервисные контракты, чтобы показать потоки, то я не вижу, как это действительно отличается от потокового решения. Если кто-то может просто ответить на эти вопросы для меня, я награжу их щедростью и отмечу это как ответ.

5 ответов

Protobuf-net обычно имеет значительную экономию пространства (например, по порядку величины) для большинства данных и может подключаться к WCF. К сожалению, на данный момент он не поддерживает полные графики, только деревья. Однако у меня есть планы, которые я просто не успел осуществить. Я ничего не могу обещать, но я мог бы попытаться поднять эту работу немного раньше.

Иначе; Могут быть способы настроить существующий код для работы с деревом вместо графа.

Если вы по-прежнему хотите использовать Message Security, я бы порекомендовал вам использовать MTOM для оптимизации пропускной способности сети, которая должна использоваться для передачи сообщений, а также канала чанкинга для использования меньших буферов памяти при применении безопасности. В противном случае WCF попытается загрузить все сообщение в память для обеспечения безопасности, и поэтому вы получите исключение Недостаточно памяти.

Я использовал для реализации своего рода передачи большого текста в / из WCF. Мой триг - преобразовать его в поток и использовать GZipStream для сжатия, а затем отправить его как byte[], к счастью, его никогда не будет превышать 10 МБ.

В вашем случае я рекомендую сделать фрагментацию. Преобразовать сериализованный объект в byte[], затем объединить и распаковать

psudo

int transferSize = 5000000; // 5MB
byte[] compressed = ...;
var mem = new System.IO.MemoryStream(compressed);

for(int i = 0; i < compressed .length; i+= transferSize )
{
    byte[] buffer = new byte[transferSize];
    mem.Read(buffer, i, compressed);
    mem.Flush();
    sendFragmentToWCF(buffer);
}

редактировать 08 декабря 2010

хорошо, насколько я понимаю, ситуация такова, что клиент загружает какой-то большой объект сериализации через WCF. Я не особо тестировал это решение, но думаю, оно должно работать. Ключ сохранить сериализованный объект в файл и использовать Response для передачи этого файла.

[WebMethod]
public void GetSerializedObject()
{
    string path = @"X:\temp.tmp";

    var serializer = new  System.Runtime.Serialization.NetDataContractSerializer();
    var file = new System.IO.FileStream(path, System.IO.FileMode.CreateNew);

    try
    {
        serializer.Serialize(file, ...);
        this.Context.Response.TransmitFile(path);
        this.Context.Response.Flush();
    }
    finally
    {
        file.Flush();
        file.Close();
        file.Dispose();
        System.IO.File.Delete(path);
    }
}

WCF должен автоматически выполнять потоковую передачу файлов, и вам не нужно беспокоиться о размере сериализованного объекта, поскольку мы используем передачу файлов. Не забудьте указать предел ответа конфигурации.

Некоторые более легкие, но не гарантированные решения, были бы

  • использовать DataContractSerializer вместо этого, поскольку у вас есть обе стороны. Это не требует информации встроенного типа, которая является значительно большой.
  • использование [DataMember(EmitDefaultValue = false)] который обсуждается в вопросе, который я задал - опять же, потому что у вас есть обе стороны; это уменьшит размер сообщения (конечно, многое зависит от того, сколько полей в графике являются значениями по умолчанию).
  • использование [DataContract(IsReference=true)]особенно если у вас много повторяющихся объектов значений или справочных данных
  • использовать какое-то регулирование на сервере, чтобы уменьшить нагрузку на память одновременных результатов

Это, конечно, компромиссы, например, с удобочитаемостью.

Поскольку никто этого не сделал, возможно, с помощью WebSockets или методов длительного опроса можно решить эту проблему. Я только кратко рассмотрел эти решения и не разработал решение вокруг них, но я хотел предложить эти концепции для записи, и я расширю свой ответ позже, если позволит время.

Основная идея заключается в том, чтобы достичь чего-то похожего на идею работы примера ChunkingChannel, но при этом не требуется полнодуплексный канал, который обычно нарушает модель запросов / ответов на основе порта 80, которая нежелательна для создания брандмауэра и других устройств. связанные конфигурации для клиентов.

Другие связанные материалы:

Обновление: после более подробного изучения этого вопроса выясняется, что при использовании WebSockets, который известен как NetHttpBinding, я по своей сути не буду решать исходный запрос, который заключается в использовании wsHttp в WCF с безопасностью сообщений. Однако я собираюсь оставить здесь свой ответ как информацию для тех, кто может искать альтернативу.

Другие вопросы по тегам