使用 protobuf-net 的 ASP.NET SessionState 模式 SQLServer 序列化

Posted

技术标签:

【中文标题】使用 protobuf-net 的 ASP.NET SessionState 模式 SQLServer 序列化【英文标题】:ASP.NET SessionState mode SQLServer serialization with protobuf-net 【发布时间】:2011-02-12 05:52:10 【问题描述】:

问题背景

我一直在考虑如何优化 SQL Server 中会话的状态外存储,我遇到的一些方法是:

在不需要会话的页面上禁用会话状态。此外,在不写入会话的页面上使用只读。 在 ASP.NET 4.0 中使用 gzip 压缩选项。 尽量将会话中存储的数据量保持在最低限度。 等

现在,我在会话中存储了一个对象(一个名为 SessionObject 的类)。好消息是,它是完全可序列化的。

使用 protobuf-net 进行优化

我认为另一种可能是优化会话存储的好方法是使用协议缓冲区(protobuf-net)序列化/反序列化而不是标准 BinaryFormatter。我知道我可以让我的所有对象都继承 ISerializable,但我不想创建 DTO 或使用序列化/反序列化逻辑弄乱我的域层。

任何使用带有会话状态 SQL 服务器模式的 protobuf-net 的建议都会很棒!

【问题讨论】:

仅供参考,Gzip 压缩与会话和会话存储无关。如果您将 SQL 服务器用于会话状态,那么几乎没有理由将会话用于状态,因为您在会话中存储的大部分信息都在您的数据库中。因此,如果您仍然有要保存到会话的状态并且您正在遵循您的第 3 点,那么您要保存到会话中的是什么? SessionObject 的属性也是如此吗? 另外,我会在应用程序级别(在 web.config 文件中)设置会话状态,然后为特定页面打开或只读,这样您就不必记住打开关掉它。 @Shiv - 我非常不同意你在第一条评论中的观点;在过去,我见过很多使用会话状态来处理瞬态数据的例子个人 写了一个基于 gzip 的会话状态提供程序,以帮助减少网络 IO。它工作得很好,并且显着提高了性能。 @Marc,从他谈到 gzip 的方式来看(至少对我而言)似乎并不意味着使用 gzip 来压缩会话状态,而是使用 gzip 来压缩响应,所以我说它有与会话状态无关(他试图优化的东西)。当然压缩会话状态应该有助于 I/O 带宽的使用。至于你的另一点。我确实说过,“大部分情况下”,我确实要求澄清。 就我个人而言,我尽可能远离会话状态。当我在非常繁忙的站点(300 个请求/秒及更多)上查看 IIS 中的挂起请求时,我发现所有挂起的请求都处于“获取会话状态”状态。取消会话状态(并使用 cookie 和/或隐藏字段)不会留下挂起的请求。就个人而言,我从来不需要保存(在会话状态下)任何无法从数据库中获取的东西。再说一次,我不是说没有必要,但也许重新思考可能会导致没有必要?这是我最初评论的重点。 【参考方案1】:

如果现有的会话状态代码使用BinaryFormatter,那么您可以通过让protobuf-net 充当BinaryFormatter 的内部代理来作弊,方法是在您的根对象上实现ISerializable强>:

[ProtoContract]
class SessionObject : ISerializable 
    public SessionObject()  
    protected SessionObject(SerializationInfo info, StreamingContext context) 
        Serializer.Merge(info, this);
    
    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
        Serializer.Serialize(info, this);
    

    [ProtoMember(1)]
    public string Foo  get; set; 
    ...

注意事项:

只有根对象需要这样做;任何封装的对象都将由 protobuf-net 自动处理 它仍然会为最外层的对象添加 little 类型的元数据,但不会太多 这将打破现有状态;改变序列化机制本质上是一个突破性的改变

如果您想从 root 对象中删除类型元数据,则必须实现自己的状态提供程序(我认为 MSDN 上有一个示例);

优点:输出更小 优点:无需在根对象上实现ISerializable 缺点:您需要维护自己的状态提供程序;p

(以上提出的所有其他点仍然适用)

还请注意,此处 protobuf-net 的有效性将在一定程度上取决于您存储的数据是什么。它应该更小,但如果你有很多非常大的字符串,它不会很多小,因为 protobuf 仍然使用 UTF-8 作为字符串。

如果您确实有很多字符串,您可能会考虑另外使用 gzip - 我为我上一个尝试 gzip 的雇主编写了一个状态提供程序,并存储了最小的(原始或 gzip) -显然需要进行一些检查,例如:

如果小于 [某个值],不要 gzip 如果 gzip 超过原始压缩率,请尽早短路 gzip 压缩

以上内容可以非常愉快地结合与 protobuf-net 一起使用 - 如果您正在编写 state-provider 无论如何,您可以放弃 ISerializable 等最高性能。

最后一个选项,如果你真的想要,我先给[ProtoContract(..., CompressionMode = ...)] 添加一个“压缩模式”属性;其中:

仅适用于ISerializable 用法(出于技术原因,更改主布局没有意义,但这种情况可以) 在上述序列化/反序列化期间自动应用 gzip [也许与我上面提到的相同检查] 这意味着您不需要添加自己的状态提供程序

但是,这只是我真正想申请“v2”的东西(我对仅在 v1 中的错误修复非常残酷,这样我就可以保持理智)。

如果感兴趣,请告诉我。

【讨论】:

我认为这将是 v2 的一个很棒的功能! 在传输旁边使用 ProtoBuf 时,CompressionMode 会派上用场,因为它可以更好地控制哪些对象会在树中进行压缩,而不是盲目地执行许多较小的值类型并获得更少的净值-谷物。

以上是关于使用 protobuf-net 的 ASP.NET SessionState 模式 SQLServer 序列化的主要内容,如果未能解决你的问题,请参考以下文章

asp.net core 使用protobuf

使用protobuf-net继承时如何选择字段号?

如何使用 protobuf-net 读回附加的对象?

如何使用 protobuf-net 处理 .proto 文件

protobuf-net:日期时间的编码

使用 protobuf-net 序列化具有接口类型成员的类