Hessian 序列化的软肋

Hessian是一种高效的二进制Web服务协议,适用于多种编程语言。本文探讨了Hessian在字符串序列化方面的局限性,特别是针对大字符串处理时存在的性能瓶颈,并提出了改进措施。

Hessian最早是用于Java的二进制的Web服务,官方 定义:

 

The Hessian binary web service protocol makes web services usable without requiring a large framework, and without learning yet another alphabet soup of protocols. Because it is a binary protocol, it is well-suited to sending binary data without any need to extend the protocol with attachments.

 

后来被广泛用于其它的编译语言象Python,C++,C#,PHP,Ruby,Erlang等。

 

其实Hessian除了用于Web方法调用外,还有一个常用的功能就是跨语言的序列化。正是有了高效、紧凑的序列化,Hessian才广为流传。

 

Hessian序列化协议不管是1.0.2规范 还是2.0草案 都是十分完善的,但其中最大的问题就是关于字符串(或者XML,在Hessian里XML的序列化基本与字符串一样)的序列化。

下面是Hessian字符串序列化的定义:

 

1.0:

string ::= (s b16 b8 utf-8-data)* S b16 b8 utf-8-data

2.0:

# UTF-8 encoded character string split into 64k chunks
string     ::= x52 b1 b0 <utf8-data> string  # non-final chunk
           ::= 'S' b1 b0 <utf8-data>         # string of length
                                             #  0-65535
           ::= [x00-x1f] <utf8-data>         # string of length
                                             #  0-31
           ::= [x30-x34] <utf8-data>         # string of length
                                             #  0-1023
我们看到,不管是1.0还是2.0,字符串都是“分段”传输的。二个版本不同之处在于2.0只是针对0-31长度和0-1023长度的短字符串作了
特殊优化。
所谓“分段”就是单个字符串的最大长度不能大于65536(2 ^ 16),如果大于这个长度就要按65536进行分段。
举个例子,假设有个字符串长度为65537,那么Hessian序列化大致为:
s xFF xFF <UTF8-DATA> S x00 x01 <UTF8-DATA>
第一个小写的s表示是字符串的分段中的一段,大写的S表示是最后一段。在S的后面就是2个字节的长度,注意这里不是整段字节的长度,
而是字符串的长度!接下来就是UTF8编码的字符串。
照理说,按这个定义不会出现多大问题,为字符国际化以及长、短字符串都作了相关的优化处理。
但事实上,当字符串很大时(为何会很大?想想如果输出一个很大的XML结果集(2M以上)),这种方案的时间会耗费大量的资源,极为低效。
(1)字符编码问题。
Hessian字符串采用了对英文友好的UTF8编码,是为减少传输的大小。但UTF8是多字节编码,且不说对于非英文字符,它增加了传输的开销,
重要的是采用UTF8编码和解码必须进行逐字节的低效率的扫描。当字符多时,这个会成为整个序列化的瓶颈。
从效率的角度来讲,个人觉得Hessian采用UTF16最好。UTF8虽然小了一点,处理的开销可不只是大了一点点。

(2)分段问题。
分段带来传输的一点性能优势和分开检验的好处,但拆分和组合一个巨大的尤其是必须按字节扫描的字符串时,这个带来了很大的开销。
(3)字符长度问题。(重点)
Hessian保存了每个字符段的字符的个数,但没有保存整个段字节的长度。这样在反序列化时,只能逐字节扫描,没有办法进行读取优化。 逐字节的验证UTF8和读取UTF8字符串,在C++、C#或者Java这样高效率的语言来讲,本身不是太大问题, 但对于PHP、Python或者Ruby、Erlang等非Unicode的低性能语言来说,就是要命的事情了。
作一简单测试,当PHP解析Hession 3M的字符串(XML),耗时竟然超过30秒,30M得半小时了!而C#都不超过1秒!
看来,想要让PHP等语言快点,除了标明标明字符串的字符个数外还必须标明实际字节大小,尽管这样破坏了标准:
string ::= (s b16 b8 C16 C8 utf-8-data)* S b16 b8 C16 C8 utf-8-data
C16和C8就是实际字符串在该段的字节数。有了这个这个标记,PHP等语言就可高效处理Hessian大字符串了,30M 1秒钟!
如果你有更好的办法,请不吝指教!
### Hessian 序列化概述 Hessian 是一种轻量级的二进制协议,主要用于实现远程服务调用以及对象的序列化和反序列化操作。它支持多种编程语言间的互操作性,在性能上优于传统的 XML 或 JSON 格式的序列化方式。 #### 使用方法 Hessian 提供了一套 API 来完成对象的序列化与反序列化工作。以下是其主要使用方法: 1. **序列化** 在 Hessian 中,可以通过 `Hessian2Output` 类来完成对象的序列化操作。该类会将 Java 对象转换为字节流形式以便存储或传输[^2]。 下面是一个简单的代码示例展示如何通过 Hessian 进行序列化: ```java import com.caucho.hessian.io.Hessian2Output; import java.io.ByteArrayOutputStream; public class HessianSerializeExample { public static byte[] serialize(Object obj) throws Exception { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream); hessian2Output.writeObject(obj); hessian2Output.flush(); return byteArrayOutputStream.toByteArray(); } } ``` 2. **反序列化** 反序列化则是将字节流重新还原成原始的对象实例。这通常由 `Hessian2Input` 完成,具体流程如下所示: ```java import com.caucho.hessian.io.Hessian2Input; import java.io.ByteArrayInputStream; public class HessianDeserializeExample { public static Object deserialize(byte[] data) throws Exception { ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data); Hessian2Input hessian2Input = new Hessian2Input(byteArrayInputStream); return hessian2Input.readObject(); } } ``` #### 工作原理 Hessian 的核心机制在于定义了一个标准化的数据编码规则,并利用这些规则实现了高效的二进制数据交换。它的内部结构涉及以下几个重要组件及其交互逻辑: 1. **序列化过程** - 数据被写入到 `Hessian2Output` 实例中。 - 之后,`SerializerFactory` 负责查找合适的 `Serializer` 接口实现类,用于处理特定类型的对象并将其转化为二进制格式[^3]。 2. **反序列化过程** - 字节流输入至 `Hessian2Input` 实例。 - 同样借助 `SerializerFactory` 查找对应的 `Deserializer` 接口实现类,从而解析出原生对象。 这种设计使得 Hessian 不仅能够快速执行序列化/反序列化动作,而且还能很好地兼容多语言环境下的 RPC 请求响应链路。 另外需要注意的是,当涉及到复杂业务场景时(比如字段类型变更),如果目标端存在基础数据类型,则可能引发异常情况;而对于引用型变量来说,默认情况下不会出现问题[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值