Categorygithub.com/sofastack/sofa-hessian-go

# README

目录

概要

sofa-hessian-gohessian 2.0/1.0 serialization protocol 的 Golang 实现,包括 1.0 协议以及 2.0 协议的 java3.xjava4.x 版本,同时还提供了 JSONhessian 类型系统互相转换的设计。

在此非常感谢 node-modules/hessian.js 提供的 golden files(测试数据), 两者底层复用了同样的测试数据集。

类型系统

hessian 的类型系统由 8 种基础类型和 3 种复合类型,以及 3 种 引用类型组成。

基础类型

  • binary
  • bool
  • string
  • int32
  • int64
  • float64
  • null
  • date (64bit)

复合类型

  • list
  • map
  • object

引用类型

  • class reference: represents the definition of class
  • type reference: represents the name of class
  • object reference: represents the instance of object or list or map.

编码规范

sofa-hessian-go 遵循以下编码规范,将 go 的类型转化为 hessian 的类型。

  • uint8/int8 => int32
  • uint16/int => int32
  • uint32/int32 => int32
  • uint64/int64 => int64
  • uint/int => int64
  • bool => bool
  • string => string
  • []byte => binary
  • nil => null
  • time.Time => date
  • map[interface{}]interface{} => map
  • []interface{} => list
  • struct => object

解码规范

sofa-hessian-go 遵循以下解码规范,将 hessian 的类型转化为 go 的类型。

  • int32 => int32

  • int64 => int64

  • bool => bool

  • string => string

  • null => nil

  • date => time.Time

  • map

    • untyped map => map[interface{}]interface{}
    • typed map => *JavaMap{class: "balaba", map[interface{}]interface{}}
  • list

    • untyped list => []interface{}
    • typed list => *JavaList{class: "balaba", []interface{}}
  • object =>

    • concrete object => go concrete struct
    • generic object => *JavaObject{class: "balaba", JavaObject{}}

json 转换

JSON 的类型系统比 HESSIAN 的类型系统更精简,从理论上是可以在一定人为约束条件下做到 JSON 和 HESSIAN 的类型转换。

JSON 到 HESSIAN 的类型转换

给定一个 HESSIAN 类型总是存在一种 JSON 类型可以等价描述出来。但是在实际实现中只有一种情况例外,即 HESSIAN 可以以较小的代价描述循环引用的数据结构,JSON 虽然可以描述,但通常导致的结果就是无限递归导致爆栈。 JSON 天然无法处理循环引用的数据结构,不过 sofa-hessian-go 在实际的实现中以较小的代价跟踪循环引用的问题,以不完整的数据结构代替了爆栈。

基础类型

NULL 类型

给定一个 hessian null 类型总是可以用 json null 类型描述即 json.null => hessian.bool

bool 类型

给定一个 hessian bool 类型总是可以用 json bool 类型描述即 json.bool => hessian.bool

int 类型

给定一个 hessian int 类型无法直接用 json number 类型描述,但是我们可以通过 json object 包装来表示即

json.object {
	"$class": "int", // or "java.lang.Integer"
	"$": number
} => hessian.int

long 类型

给定一个 hessian long 类型总是可以用 json number 类型描述即 json.number => hessian.long

double 类型

给定一个 hessian double 类型总是可以用 json object 类型描述即

json.object {
	"$class": "double", // or "java.lang.Double"
	"$": number
} => hessian.double

binary 类型

给定一个 hessian binary 类型总是可以用 json object 类型描述即

json.object {
	"$class": "bytes",
	"$": "base64 encoding string"
} => hessian.binary

date 类型

给定一个 hessian date 类型总是可以用 json object 类型描述即

json.object {
	"$class": "date", // or "java.util.Date"
	"$": number
} => hessian.date

复合类型

map 类型

给定一个 hessian map 类型总是可以用 json object 类型描述即 json.object => hessian.map

list 类型

给定一个 hessian map 类型总是可以用 json array 类型描述即 json.array => hessian.list

object 类型

给定一个 hessian object 类型总是可以用 json object 类型描述即

json.object {
	"$class": "classname",
	"$": json element
} => hessian.object

HESSIAN 到 JSON 的类型转换

给定一个 JSON 类型总是存在一种 HESSIAN 类型可以等价描述出来.

基础类型

number

给定一个 json number 类型总是可以用 hessian int/long/double 类型描述即 hessian.int|long/double => json.number

string

给定一个 json string 类型总是可以用 hessian string 类型描述即 hessian.string => json.string

bool

给定一个 json bool 类型总是可以用 hessian bool 类型描述即 hessian.bool => json.bool

null

给定一个 json null 类型总是可以用 hessian null 类型描述即 hessian.null => json.null

复合类型

object

给定一个 json object 类型总是可以用 hessian map 类型描述即 hessian.map => json.object

array

给定一个 json array 类型总是可以用 hessian array 类型描述即 hessian.array => json.array

附录

JSON 类型系统

json
    element

value
    object
    array
    string
    number
    "true"
    "false"
    "null"

HESSIAN 类型系统

#starting production
top        ::= value

#main production
value      ::= null
           ::= binary
           ::= boolean
           ::= class-def value
           ::= date
           ::= double
           ::= int
           ::= list
           ::= long
           ::= map
           ::= object
           ::= ref
           ::= string

API

encode

查看 examples/sofahessian_examples_test.go

decode

查看 examples/sofahessian_examples_test.go

json

查看 examples/sofahessian_examples_test.go

CLI

install

make hessian

decode

bin/hessian decode 4fbc636f6d2e616c697061792e736f66612e7270632e636f72652e726571756573742e536f666152657175657374950d7461726765744170704e616d650a6d6574686f644e616d651774617267657453657276696365556e697175654e616d650c7265717565737450726f70730d6d6574686f64417267536967736f904e0873617948656c6c6f1048656c6c6f536572766963653a312e304d0870726f746f636f6c04626f6c74117270635f74726163655f636f6e746578744d09736f6661527063496401300473616d700566616c73650b73797350656e4174747273000d736f666143616c6c6572496463000c736f666143616c6c65724970000b736f6661547261636549641e3061306665383633313537313034363337383735383130303138363232300c736f666150656e4174747273000e736f666143616c6c65725a6f6e65000d736f666143616c6c6572417070007a7a567400075b737472696e676e02106a6176612e6c616e672e537472696e67046c6f6e677a05776f726c64e1 --version=3

&hessian.JavaObject{
  class: "com.alipay.sofa.rpc.core.request.SofaRequest",
  names: []string{
    "targetAppName",
    "methodName",
    "targetServiceUniqueName",
    "requestProps",
    "methodArgSigs",
  },
  values: []interface {}{
    nil,
    "sayHello",
    "HelloService:1.0",
    map[interface {}]interface {}{
      "protocol":          "bolt",
      "rpc_trace_context": map[interface {}]interface {}{
        "sofaCallerZone": "",
        "sofaCallerApp":  "",
        "sofaRpcId":      "0",
        "sysPenAttrs":    "",
        "sofaCallerIdc":  "",
        "sofaPenAttrs":   "",
        "samp":           "false",
        "sofaCallerIp":   "",
        "sofaTraceId":    "0a0fe8631571046378758100186220",
      },
    },
    &hessian.JavaList{
      class: "[string",
      value: []interface {}{
        "java.lang.String",
        "long",
      },
    },
  },
}
"world"
1

fromjson

bin/hessian fromjson '{"$class": "com.alipay.sofa.rpc.core.request.SofaRequest", "$": {"1": "2"}}' --format hex

43302c636f6d2e616c697061792e736f66612e7270632e636f72652e726571756573742e536f666152657175657374910131600132

bin/hessian decode 43302c636f6d2e616c697061792e736f66612e7270632e636f72652e726571756573742e536f666152657175657374910131600132
&sofahessian.JavaObject{
  class: "com.alipay.sofa.rpc.core.request.SofaRequest",
  names: []string{
    "1",
  },
  values: []interface {}{
    "2",
  },
}

性能测试

─λ make bench
go test -benchmem -run="^$" -bench ^Benchmark ./...
?   	github.com/sofastack/sofa-hessian-go/sofahessianv1	[no test files]
goos: darwin
goarch: amd64
pkg: github.com/sofastack/sofa-hessian-go/sofahessianv2
BenchmarkDecodeBinary-8             	  558362	      2180 ns/op	15041.85 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeBool-8               	33203718	        35.8 ns/op	  27.91 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeDate-8               	14283891	        73.1 ns/op	 123.15 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeFloat64-8            	15707289	        74.1 ns/op	  40.49 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeInt32-8              	15519128	        82.9 ns/op	  60.31 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeInt64-8              	15663241	        78.2 ns/op	 115.09 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeList-8               	  751725	      2969 ns/op	   6.06 MB/s	     478 B/op	      19 allocs/op
BenchmarkDecodeMap-8                	  217478	      5185 ns/op	  14.08 MB/s	     949 B/op	      30 allocs/op
BenchmarkDecodeNil-8                	27432326	        42.6 ns/op	  23.46 MB/s	       0 B/op	       0 allocs/op
BenchmarkDecodeObject-8             	  965149	      1498 ns/op	  22.03 MB/s	     548 B/op	      13 allocs/op
BenchmarkDecodeString-8             	     818	   1333529 ns/op	 147.44 MB/s	    1370 B/op	       0 allocs/op
BenchmarkEncodeBinary-8             	  392306	      2989 ns/op	21944.34 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeBool-8               	143134738	         8.50 ns/op	 117.64 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeFloat64-8            	100000000	        11.0 ns/op	 819.48 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeInt64-8              	100000000	        10.2 ns/op	 490.08 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeInt32-8              	100000000	        10.7 ns/op	 465.65 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeList-8               	 7413168	       161 ns/op	  12.45 MB/s	      40 B/op	       2 allocs/op
BenchmarkEncodeMap-8                	13931235	        82.9 ns/op	  24.12 MB/s	       8 B/op	       1 allocs/op
BenchmarkEncodeNil-8                	197458849	         6.12 ns/op	 163.33 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeObject-8             	 3964602	       303 ns/op	  46.15 MB/s	      64 B/op	       3 allocs/op
BenchmarkEncodeRef-8                	88544038	        13.5 ns/op	 222.70 MB/s	       0 B/op	       0 allocs/op
BenchmarkEncodeString-8             	     716	   1662180 ns/op	 160.43 MB/s	      16 B/op	       1 allocs/op
BenchmarkEncodeAndDecodeJSON-8      	   51471	     22695 ns/op	  44.68 MB/s	    2913 B/op	      37 allocs/op
BenchmarkEncodeAndDecodeHessianV2-8   	  139880	      7979 ns/op	  56.77 MB/s	    3104 B/op	      57 allocs/op
PASS
ok  	github.com/sofastack/sofa-hessian-go/sofahessianv2	34.443s
?   	github.com/sofastack/sofa-hessian-go/javaobject	[no test files]

TODO

  1. hessian-generator: 通过编译时代码生成 Encode 和 Decode 方法,减少反射带来的负担。

# Packages

No description provided by the author
No description provided by the author
No description provided by the author
Package sofahessian implements the hessian 2.0 serialization protocol with golang in http://hessian.caucho.com/doc/hessian-serialization.html hessian v2 grammar top ::= value # 8-bit binary data split into 64k chunks binary ::= x41 b1 b0 <binary-data> binary # non-final chunk ::= 'B' b1 b0 <binary-data> # final chunk ::= [x20-x2f] <binary-data> # binary data of # length 0-15 ::= [x34-x37] <binary-data> # binary data of # length 0-1023 # boolean true/false boolean ::= 'T' ::= 'F' # definition for an object (compact map) class-def ::= 'C' string int string* # time in UTC encoded as 64-bit long milliseconds since # epoch date ::= x4a b7 b6 b5 b4 b3 b2 b1 b0 ::= x4b b3 b2 b1 b0 # minutes since epoch # 64-bit IEEE double double ::= 'D' b7 b6 b5 b4 b3 b2 b1 b0 ::= x5b # 0.0 ::= x5c # 1.0 ::= x5d b0 # byte cast to double # (-128.0 to 127.0) ::= x5e b1 b0 # short cast to double ::= x5f b3 b2 b1 b0 # 32-bit float cast to double # 32-bit signed integer int ::= 'I' b3 b2 b1 b0 ::= [x80-xbf] # -x10 to x3f ::= [xc0-xcf] b0 # -x800 to x7ff ::= [xd0-xd7] b1 b0 # -x40000 to x3ffff # list/vector list ::= x55 type value* 'Z' # variable-length list ::= 'V' type int value* # fixed-length list ::= x57 value* 'Z' # variable-length untyped list ::= x58 int value* # fixed-length untyped list ::= [x70-77] type value* # fixed-length typed list ::= [x78-7f] value* # fixed-length untyped list # 64-bit signed long integer long ::= 'L' b7 b6 b5 b4 b3 b2 b1 b0 ::= [xd8-xef] # -x08 to x0f ::= [xf0-xff] b0 # -x800 to x7ff ::= [x38-x3f] b1 b0 # -x40000 to x3ffff ::= x59 b3 b2 b1 b0 # 32-bit integer cast to long # map/object map ::= 'M' type (value value)* 'Z' # key, value map pairs ::= 'H' (value value)* 'Z' # untyped key, value # null value null ::= 'N' # Object instance object ::= 'O' int value* ::= [x60-x6f] value* # value reference (e.g.