听过 PHPRPC 吗?试试我的 Hign!
·〉前言
在企业级应用程序中,常常需要将某个类(可能复杂、组合、等等)进行本地化。当然,个人感觉微软所提供的 Binary 序列化是最“保险”的方式。可惜这是一个略有遗憾的序列化器。常见问题如程序集版本的问题(虽然有 Binder 可以解决),以及致命的序列化的效率和用时令人不敢恭维。而 XML 序列化仅仅适用于简单对象的本地化。
无意之中,在网络中寻找到了PHPRPC。这是一个非常轻型,非常棒的序列化器。
PHPRPC,它的商业版本是Hprose。 PHPRPC 采用的序列化方式是 PHP 序列化。你可以认为它是一种半文本格式,因为文本和二进制字节流的特征它都具有。 比如表示数字时,它采用的是数字的文本格式,而不是二进制的机器存储格式,这样它就可以在不同的平台上传递而无须担心字节序引起的解析问题。而它又像二进制字节流那样不允许在数据当中插入多余的空白(空格、回车、换行等字符),并且对于字符串、数组、字典、对象还有长度描述信息。这些特征决定了它既有纯文本格式的通用性,又具有二进制字节流的快速解析能力。
PHPRPC For .NET 无论是效率,还是大小,都是非常棒的。如果读者对 PHPRPC 还不是很了解,或者对他的性能表示怀疑,可以阅读这篇文章(http://www.cnblogs.com/xlai/archive/2010/10/19/1855736.html),这篇文章详细的测试 PHPRPC 和其他序列化器的性能。毋庸置疑,在没有找到其他序列化器,这是一个非常好的选择。据闻,PHPRPC 有一个商业版,性能和效率更胜一筹,可惜无缘尝试。
但是,PHPRPC也不是全能的(至少 For .NET 是这样的)。例如对于十分复杂的组合对象,PHPRPC的序列化是抛出异常的。更要命的是,对于object类型的解析,PHPRPC显得过于2B了。例如对于List<object>、Dictionary<object,object>的处理更是一塌糊涂。虽然官方扯蛋的说:支持复杂对象传输的。
幸好,PHPRPC是一个开源的项目。翻开源码后,有一种热血立即涌上胸口!
——自己写一个。
·〉PHPRPC的不足
PHPRP的思路是非常美妙的,比如对字符串的长度描述,复杂对象的“Reference”特性等,在自己写序列化器的过程中,PHPRPC的源码令人受益匪浅。现在主要列举几个PHPRPC亟待解决的问题(可能不影响正常的使用),再一一列出解决方案。
1、对ICollection、IList和IDictionary的类型支持不够,我想应当是PHPRPC为了缩短序列化后的大小,特意对这三个常用的类型进行特别处理,无奈的是,PHPRPC处理的方式并不是最佳的,比如前文所提到的“List<object>”,如果在“List<object>”中存有一个字符串,那么反序列化,就会成了一个 byte[]数组。通过观赏代码,BUG是在【PHPReader文件的private AssocArray ReadAssocArray(Stream stream, ArrayList objectContainer)函数】。
2、也正因为PHPRPC对IDictionary、ICollection、ILIst的特殊处理,导致了对于实现三个接口任意对象,不序列化其自定义属性,而只序列化集合所包含的元素。
3、PHPRPC并不支持多维数组。如果你的维数大于1,那么就会抛出【throw new RankException("Only single dimension arrays are supported here.")】的异常。虽然本人对多维数组用的也是相当的少,但二维数组,还是有用到的。
4、性能。对,没错,PHPRPC的性能仍然无法令我满意。最明显的是反射,这个竟然没有做任何优化。
5、不支持值类型的对象。显然,PHPRPC并没有考虑这么多。
相对第3条、第4条和第5条,第1、2条显得十分致命,在许多应用程序的开发中,对Dictionary`2或List`1的继承与扩展是十分频繁的。综合PHPRPC的诸多显著性的缺点,我即将展示我的方案。
·〉我的方案
十分感谢您可以阅读到这里,虽然我前面不断的批评PHPRPC,但它的强大和奇妙,是无法褪去的。尤其是随着自己的序列化器第一版的落幕,充分体会到PHPRPC其“牺牲”与“得到”(为什么PHPRPC要这样做,而不要那样做)。
只不过——它可以更好。
通过一周左右的钻研,我发现:任意对象若想要序列化,他无限分解后,只会剩下:基础数据类型(int、string、DateTime等)和数组,而其余的,皆为浮云。
不过这样的方案是有代价的,尤其是对集合(如Dictionary)的序列化,哪怕集合为空,也无法潇洒的简短。我曾常识对Dictionary进行分解,分成两个部分,一部分是元素部分,一部分是派生类的属性部分,但最终否决了这样的方案(考虑集合有可能仅仅是对IDictionary的实现,还有待再次尝试)。
支持集合的object类型。强大的功能的背后总是性能的牺牲。为了支持一切对象,不得不再次牺牲序列化的大小,在每一个集合的元素前都写入其类型。这样的好处是:List<BaseClass>里含有 DerivedClass,在序列化与反序列化后不存在任何差异。
在牺牲序列化后的大小,得到的是——任意对象的复制!
为了减少序列化后的大小,我特意增加了一个不影响性能的特性(实际上性能反而令我吃惊的提升了),就是“简化限定名”(包含对程序集描述进行简化),例如对于int类型的则为“i”,System.Collections.Generic.List`1则为“scg.List”,支持限定名的命名空间并不多,但是基本覆盖了常用的,可以大大的简短序列化后的字符串长度。
此次公开Sofire的核心模块之一:Utils源码(Sofire.Utils,基于.NET 3.5),这个核心框架已经编写了将近两年,它包含了几个核心的功能:Result特性,Emit’s Dynamic Obect、Serialization、Security。如果您有兴趣,可以对它进行研究。此次开源仅仅是为了分享,希望可以帮助朋友,也希望路过的朋友留下宝贵的意见。接下来附上一张测试图(PS:测试结果是针对 100000 个对象进行内存流序列化,并且 PHPRPC已经是个人进行优化的后的版本(抛弃传统反射带来的性能损失)。
Sofire是一套基于.NET的开发辅助框架。在此段任务忙完以后,可能会针对个人所研发的Sofire Platfrom(信息化综合平台)进行讲解。
附图:
源码(VS2010):
Sofire.Utils框架源码(包含注释与说明,开源,没有任何限制,仅仅有一个小小的请求——请保留原名:Sofire)
最后,如果您喜欢我的文章,或者我的文章可以给您带来一丝帮助,请推荐……