2011-12-13 59 views
3

我目前能够将我创建的对象存储到HttpContext.Current.Session中,并且我遇到了protobuf-net。有没有办法通过protobuf序列化来存储我的对象?protobuf-net:如何在用户会话中存储

看起来protobuf想要将信息存储到Stream,所以我(我可以吗?)将Stream对象存储到用户会话中吗?或者我应该先将它从Stream转换为另一种对象类型?如果是这样,转换序列化的对象会绕过使用protobuf的原始目的(cpu使用情况,内存使用情况)吗?有没有人做过这个?

我的目标是使用protobuf作为压缩层将信息存储到用户会话中。是否有更好的方式(更小的尺寸,更快的压缩率,更容易维护,更小的实现开销),还是最适合这项任务的工具?


更新

我使用这个类对象

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

这个类是我的菜单列表类的包装,这是(基本上)包含内置列表的列表类型(字符串,整数)。我创建了包装器来将时间戳与我的对象相关联。

如果我有一个会话缓存未命中(会话密钥为空或session.lastUpdated大于全局存储时间),我做我的正常数据库查找(MSSQL),创建MenuList对象,并将其存储到会话中,像这样

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

当前我们的会话存储在内存中,但我们可能会在未来移动到数据库会话存储。

我们的会话使用非常繁重,因为我们存储了大量的大对象(尽管我希望在将来的某个时刻清理会话中存储的内容)。

例如,我们将每个用户的权限集存储到其会话存储中以避免数据库命中。当前有许多存储结构的权限和权限存储在会话中。

在这一点上,我只是查看可用的选项,因为我希望在将来更智能和严格地使用会话缓存。

请让我知道,如果有什么你需要的。

+0

我想这取决于状态对象的复杂性/大小,对象越大,受益于protobuf。对于简单的对象,它可能是一个有双重序列化的笔触。编辑:你可以基准memcached + protobuff实现与经典的asp.net会话状态服务器为不同的会话对象,它将interresting :) – Guillaume86 2011-12-13 17:25:06

+0

嗨JesseB - 你目前的设置是什么?您目前使用哪种会话提供程序?在记忆中? SQL?要么...?另外:你的会话使用有多广泛?另外,你在会话中存储什么类型的东西? (所以我可以给出最合适的指导) – 2011-12-13 18:01:48

回答

4

请注意,在这里使用protobuf-net 主要是如果您正在寻找在某个时间点转移到持久状态提供程序,这才有意义。

首先,因为你此刻正在使用的内存(这样的类型不序列化,据我所知),对不断变化的会话使用任何一种基于串行化提供商的一些注意事项:

  • 类型必须由提供者序列化(听起来很明显,但如果你有圆形图等,这会有特别的影响)
  • 因为数据是序列化的,语义是不同的;您每次都会收到副本,这意味着您在请求期间所做的任何更改都会丢失 - 只要您确保重新显式重新存储数据,并且可以避免一些线程问题 - 这是很好的 - 双刃
  • 内置状态机制通常检索会话为单个操作 - 如果(如您所述)存在一些大对象,则这可能是一个问题;与protobuf-net没有任何关系,但是我曾经调用过一个垂死的服务器,结果是一个处于状态的多MB对象查杀系统,因为请求(即使那些不使用该数据的服务器)而造成这个庞大的对象要通过网络(双向)运输

在许多方面,实际上我根本就不是一个标准的会话状态模式的风扇 - 这是之前我连碰关于它与protobuf-net的关系!

protobuf-net最终是一个序列化层。标准会话状态实现的另一个特性是,因为它最初是用BinaryFormatter来编写的,它假定对象可以在没有任何额外上下文的情况下被反序列化。然而,protobuf-net(就像XmlSerializer,DataContractSerializerJavaScriptSerializer)不与任何特定类型系统绑定 - 它采取的方法是“你告诉我你希望我填充什么类型,我会担心数据”。这实际上是一个非常好的的事情,因为我已经看到了发布新版本时,通过BinaryFormatter杀死网络服务器,因为有人有大胆触摸甚至略有的类型之一是发生涉及到一个对象存储在持久会话中。 BinaryFormatter不喜欢那样; 特别是如果你(气喘吁吁)重命名一个类型,或者(震动)将某个字段+属性设置为自动实现的属性。提示:这些是谷歌设计protobuf要避免的问题。

但是!这确实意味着它不是极大地方便使用标准会话状态模型。我已经实现了将类型名称编码到流中的系统(例如,我为protobuf-net编写了一个enyim/memcached代码转换器),但是...它并不漂亮。国际海事组织,更好的方法是将的负担转移到调用者手中,以了解数据是。我的意思是,真的......来电者应该知道他们期望在任何给定的密钥中的数据类型,对吧?

这样做的一种方法是存储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网是不一样的BinaryFormatter - 它们有不同期望什么是合理的,例如默认 protobuf-net预计知道数据看起来像什么(即public object Value {get;set;}将是一种痛苦),并且不处理圆形图表(尽管已有规定以支持这两种情况)。作为一般的经验法则:如果您可以使用类似XmlSerializerDataContractSerializer的数据序列化您的数据,它将轻松地使用protobuf-net进行序列化; protobuf-net支持额外的场景,但不作公开保证每序列化任意数据模型。用DTO来思考会让生活变得更轻松。在大多数情况下,这不是一个问题根本就是,因为大多数人都有合理的数据。有些人不要有合理的数据,我只是想适当地设置期望值!

个人,不过,就像我说的 - 尤其是大对象可以参与,我根本就没有内置的会话状态模式的粉丝。我的可能建议使用一个单独的按键数据存储(意思是:每个用户每个键一个记录,而不是每个用户只有一个记录) - 也许只是为了更大的对象,可能是所有的东西。这可能是SQL Server,或类似redis/memcached。如果你正在使用期望使用会话状态的第三方控件(webforms等),但是如果你在你的代码中手动使用状态,这显然有点痛苦,这很容易实现。 FWIW,BookSleeve耦合到redis适用于这样的事情,并提供对基于byte[]的存储的体面访问。如上所示,您可以从byte[]反序列化对象。

无论如何 - 我会在那里停下来,以防我离题太远;随意回ping有任何问题,但执行摘要:

  • protobuf网可以阻止很多版本问题你也许会BinaryFormatter
  • 看到,但它不一定是直接的1: 1个互换,因为protobuf网不编码“类型”信息(其中内置的会话机制预计)
  • 它可以制成与byte[]
  • 工作,最常见的,但如果你是存储大对象,你可能有其他我与会话状态想要工作的方式相关的ssues(与protobuf-net无关)
  • 对于较大的对象,我建议使用自己的机制(即,不是会话状态);关键值存储系统(redis,memcached,AppFabric缓存)对此很有效