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秒钟!

如果你有更好的办法,请不吝指教!