protobuf-net: как хранить в пользовательской сессии - PullRequest
3 голосов
/ 13 декабря 2011

В настоящее время я могу сохранить созданный мной объект в HttpContext.Current.Session, и я столкнулся с protobuf-net . Есть ли способ сохранить мой объект, сериализовав его с protobuf?

Похоже, protobuf хочет сохранить информацию в Stream, поэтому я должен (могу ли я?) Сохранить объект Stream в сеансе пользователей? Или я должен сначала преобразовать его из Stream в другой тип объекта? Если да, будет ли преобразование сериализованного объекта обойти первоначальную цель использования protobuf (использование процессора, использование памяти)? Кто-нибудь делал это раньше?

Моя цель - использовать protobuf в качестве слоя сжатия для хранения информации в пользовательской сессии. Есть ли лучший способ сделать это (меньшие размеры, более быстрое сжатие, более простое в обслуживании, меньшие накладные расходы на реализацию), или является protobuf подходящим инструментом для этой задачи?


Обновление

Я использую этот объект класса

[Serializable]
public class DynamicMenuCache
{
    public System.DateTime lastUpdated { get; set; }
    public MenuList menu { get; set; }
}

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

Если у меня пропущен кэш сеанса (ключ сеанса имеет значение null или значение session.lastUpdated превышает глобально хранимое время), я делаю свой обычный поиск в базе данных (MSSQL), создаю объект MenuList и сохраняю его в сеанс, вот так

HttpContext.Current.Session.Add("DynamicMenu" + MenuType, new DynamicMenuCache()
{
    lastUpdated = System.DateTime.Now,
    menu = Menu
});

В настоящее время наш сеанс хранится в памяти, но в будущем мы можем перейти в хранилище сеансов БД.

Наше использование сессии довольно интенсивно, так как мы храним в нем много больших объектов (хотя я надеюсь очистить то, что мы сохраним в сессии в будущем).

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

На данный момент я просто просматриваю доступные варианты, поскольку в будущем я хотел бы более разумно и неукоснительно использовать кэш сессий.

Пожалуйста, дайте мне знать, если вам нужно что-то еще.

1 Ответ

4 голосов
/ 14 декабря 2011

Обратите внимание, что использование здесь protobuf-net в основном имеет смысл только в том случае, если вы хотите перейти к постоянному поставщику состояний в какой-то момент.

Во-первых, поскольку вы используете in-memoryна данный момент (чтобы типы не сериализовались, AFAIK), некоторые примечания об изменении сеанса для использования любого типа поставщика на основе сериализации:

  • типы должны быть сериализуемыпровайдер (звучит очевидно, но это имеет особое значение, если у вас есть круговые диаграммы и т. д.)
  • , поскольку данные сериализуются, семантика отличается;вы получаете копию каждый раз, что означает, что любые изменения, сделанные вами во время запроса, будут потеряны - это нормально, если вы убедитесь, что вы снова явно сохраняете данные заново, и можете избежать некоторых проблем с потоками -обоюдоострые
  • встроенные механизмы состояния обычно извлекают сеанс как одиночную операцию - что может быть проблемой, если (как вы упомянули) у вас есть большие объекты;не имеет ничего общего с protobuf-net, но однажды меня вызвали для исследования умирающего сервера, который оказался состоящим из нескольких МБ объектов в состоянии, убивающем систему, как каждый запрос (даже те, которые не используютэти данные) привели к транспортировке этого огромного объекта (в обоих направлениях) по сети

Во многих отношениях я на самом деле просто не фанат стандартной сессиимодель - и это еще до того, как я коснусь того, как это связано с protobuf-net!

protobuf-net, в конечном счете, является уровнем сериализации.Другая особенность стандартной реализации состояния сеанса состоит в том, что , поскольку изначально был написан с учетом BinaryFormatter, предполагается, что объекты могут быть десериализованы без какого-либо дополнительного контекста.Однако protobuf-net (точно так же как XmlSerializer, DataContractSerializer и JavaScriptSerializer) не привязан к какой-либо конкретной системе типов - он использует подход "вы говорите мне, какой тип вы хотите, чтобы я заполнял, я буду беспокоитьсяо данных ".На самом деле это невероятно хорошо , поскольку я видел, как веб-серверы убивали BinaryFormatter при выпуске новых версий, потому что у кого-то была отвага , чтобы даже слегка прикоснуться к один из типов, который имел отношение к объекту, сохраненному в постоянном сеансе.BinaryFormatter не нравится это; особенно , если вы (вздыхаете) переименовываете тип или ( shock ) создаете что-то из свойства field + в автоматически реализуемое свойство.Подсказка: это те проблемы, которые Google Protobuf разработал, чтобы избежать.

Однако!Это означает, что это не очень удобно для использования со стандартной моделью состояния сеанса.Я реализовал системы для кодирования имени типа в поток раньше (например, я написал транскодер enyim / memcached для protobuf-net), но ... это не красиво.ИМО, лучший способ сделать это - передать бремя , зная, что это за данные , на вызывающую сторону.Я имею в виду, действительно ... вызывающий должен знать, какой тип данных они ожидают в любом данном ключе, верно?

Один из способов сделать это - сохранить byte[].Практически любая реализация состояния может обрабатывать BLOB.Если это не сработает, просто используйте Convert.ToBase64String / Convert.FromBase64String для хранения string - любая реализация, не обрабатывающая string, нуждается в стрельбе!Чтобы использовать с потоком, вы можете сделать что-то вроде (псевдокод здесь):

public static T GetFromState<T>(string key) {
    byte[] blob = {standard state provider get by key}
    using(var ms = new MemoryStream(blob)) {
        return Serializer.Deserialize<T>(ms);
    }
}

(и аналогично для добавления)

Обратите внимание, что protobuf-net отличается от BinaryFormatter - у них разные ожидания того, что разумно, например, по умолчанию protobuf-net предполагает знать заранее как выглядят данные (т. е. public object Value {get;set;} было бы больно), и не обрабатывает круговые графики (хотя существуют положения, поддерживающие оба этих сценария). Общее практическое правило: если вы можете сериализовать ваши данные с помощью чего-то вроде XmlSerializer или DataContractSerializer, они легко сериализуются с protobuf-net; Protobuf-net также поддерживает дополнительные сценарии, но не дает открытой гарантии сериализации каждой произвольной модели данных. Мышление с точки зрения DTO облегчит жизнь. В большинстве случаев это не проблема вообще , так как большинство людей имеют разумные данные. Некоторые люди не имеют разумные данные, и я просто хочу правильно рассчитать ожидание!

Лично, как я уже сказал - особенно , когда большие объекты могут быть вовлечены, я просто не фанат встроенного шаблона состояния сеанса. Вместо этого я мог бы предложить использовать отдельное хранилище данных для каждого ключа (то есть одну запись на пользователя на ключ, а не одну запись на пользователя) - возможно, только для более крупных объектов, может быть для всего. Это может быть SQL Server или что-то вроде redis / memcached. Очевидно, это немного болезненно, если вы используете сторонние элементы управления (веб-формы и т. Д.), Которые ожидают использовать состояние сеанса, но если вы используете состояние в своем коде вручную, это довольно просто реализовать. FWIW, BookSleeve в сочетании с Redis хорошо работает для таких вещей и обеспечивает достойный доступ к хранилищу на основе byte[]. С byte[] вы можете десериализовать объект, как показано выше.

В любом случае - я остановлюсь там, на случай, если зайду слишком далеко от темы; Не стесняйтесь отвечать на любые вопросы, но резюме:

  • protobuf-net может остановить многие проблемы с версиями , которые могут возникнуть при BinaryFormatter
  • но это не обязательно прямой обмен 1: 1, так как protobuf-net не кодирует информацию «type» (которую ожидает встроенный механизм сеанса)
  • его можно заставить работать, чаще всего с byte[]
  • но если вы храните большие объекты, у вас могут возникнуть другие проблемы (не связанные с protobuf-net), связанные с тем, как сессионное состояние хочет работать
  • для больших объектов, в частности, я рекомендую использовать собственный механизм (т. Е. Не состояние сеанса); системы хранения значений ключей (redis, memcached, AppFabric cache) хорошо работают для этого
...