github.com/cloudwego/frugal@v0.1.15/README_cn.md (about) 1 # Frugal 2 3 [English](README.md) | 中文 4 5 一款基于 JIT 编译的高性能动态 Thrift 编解码器。 6 7 ## 特点 8 9 ### 无需生成代码 10 11 传统的 Thrift 编解码方式,要求用户必须要先生成编解码代码,Frugal 通过 JIT 编译技术在运行时动态生成编解码机器代码,避免了这一过程。 12 13 ### 高性能 14 15 基于 JIT 技术 Frugal 可以生成比 Go 语言编译器性能更好的机器代码,在多核场景下,Frugal 的性能可以达到传统编解码方式的 5 倍左右。 16 17 ```text 18 name old time/op new time/op delta 19 MarshalAllSize_Parallel/small-16 78.8ns ± 0% 14.9ns ± 0% -81.10% 20 MarshalAllSize_Parallel/medium-16 1.34µs ± 0% 0.32µs ± 0% -76.32% 21 MarshalAllSize_Parallel/large-16 37.7µs ± 0% 9.4µs ± 0% -75.02% 22 UnmarshalAllSize_Parallel/small-16 368ns ± 0% 30ns ± 0% -91.90% 23 UnmarshalAllSize_Parallel/medium-16 11.9µs ± 0% 0.8µs ± 0% -92.98% 24 UnmarshalAllSize_Parallel/large-16 233µs ± 0% 21µs ± 0% -90.99% 25 26 name old speed new speed delta 27 MarshalAllSize_Parallel/small-16 7.31GB/s ± 0% 38.65GB/s ± 0% +428.84% 28 MarshalAllSize_Parallel/medium-16 12.9GB/s ± 0% 54.7GB/s ± 0% +322.10% 29 MarshalAllSize_Parallel/large-16 11.7GB/s ± 0% 46.8GB/s ± 0% +300.26% 30 UnmarshalAllSize_Parallel/small-16 1.56GB/s ± 0% 19.31GB/s ± 0% +1134.41% 31 UnmarshalAllSize_Parallel/medium-16 1.46GB/s ± 0% 20.80GB/s ± 0% +1324.55% 32 UnmarshalAllSize_Parallel/large-16 1.89GB/s ± 0% 20.98GB/s ± 0% +1009.73% 33 34 name old alloc/op new alloc/op delta 35 MarshalAllSize_Parallel/small-16 112B ± 0% 0B -100.00% 36 MarshalAllSize_Parallel/medium-16 112B ± 0% 0B -100.00% 37 MarshalAllSize_Parallel/large-16 779B ± 0% 57B ± 0% -92.68% 38 UnmarshalAllSize_Parallel/small-16 1.31kB ± 0% 0.10kB ± 0% -92.76% 39 UnmarshalAllSize_Parallel/medium-16 448B ± 0% 3022B ± 0% +574.55% 40 UnmarshalAllSize_Parallel/large-16 1.13MB ± 0% 0.07MB ± 0% -93.54% 41 42 name old allocs/op new allocs/op delta 43 MarshalAllSize_Parallel/small-16 1.00 ± 0% 0.00 -100.00% 44 MarshalAllSize_Parallel/medium-16 1.00 ± 0% 0.00 -100.00% 45 MarshalAllSize_Parallel/large-16 1.00 ± 0% 0.00 -100.00% 46 UnmarshalAllSize_Parallel/small-16 6.00 ± 0% 1.00 ± 0% -83.33% 47 UnmarshalAllSize_Parallel/medium-16 6.00 ± 0% 30.00 ± 0% +400.00% 48 UnmarshalAllSize_Parallel/large-16 4.80k ± 0% 0.76k ± 0% -84.10% 49 ``` 50 51 ## 用 Frugal 可以做什么? 52 53 ### 使用 Frugal 作为 [Kitex](https://github.com/cloudwego/kitex) 的编解码 54 55 可以不用再生成大量的编解码代码,使仓库变得干净整洁,review 时也不用再带上一堆无意义的 diff。然后相比于生成的编解码代码,Frugal 的性能更高。 56 57 ### 在 [Thriftgo](https://github.com/cloudwego/thriftgo) 生成的 struct 上进行编解码 58 59 如果你只需要使用 Thrift 的编解码能力,同时也定义好了 IDL,那么只需要用 Thriftgo 生成 IDL 对应的 Go 语言 struct,就可以使用 Frugal 的编解码能力了。 60 61 ### 直接定义 struct 进行编解码 62 63 如果你们连 IDL 都不想有,没问题,直接定义好 Go 语言 struct 后,给每个 Field 带上 Frugal 所需的 tag,就可以直接使用 Frugal 进行编解码了。 64 65 ## 使用手册 66 67 ### 配合 Kitex 使用 68 69 #### 1. 更新 Kitex 到 v0.4.2 以上版本 70 71 ```shell 72 go get github.com/cloudwego/kitex@latest 73 ``` 74 75 #### 2. 带上 `-thrift frugal_tag` 参数重新生成一次代码 76 77 示例: 78 79 ```shell 80 kitex -thrift frugal_tag -service a.b.c my.thrift 81 ``` 82 83 如果不需要编解码代码,可以带上 `-thrift template=slim` 参数 84 85 ```shell 86 kitex -thrift frugal_tag,template=slim -service a.b.c my.thrift 87 ``` 88 89 #### 3. 初始化 client 和 server 时使用 `WithPayloadCodec(thrift.NewThriftFrugalCodec())` option 90 91 client 示例: 92 93 ```go 94 package client 95 96 import ( 97 "context" 98 99 "example.com/kitex_test/client/kitex_gen/a/b/c/echo" 100 "github.com/cloudwego/kitex/client" 101 "github.com/cloudwego/kitex/pkg/remote/codec/thrift" 102 ) 103 104 func Echo() { 105 code := thrift.NewThriftCodecWithConfig(thrift.FastRead | thrift.FastWrite | thrift.FrugalRead | thrift.FrugalWrite) 106 cli := echo.MustNewClient("a.b.c", client.WithPayloadCodec(codec)) 107 ... 108 } 109 ``` 110 111 server 示例: 112 113 ```go 114 package main 115 116 import ( 117 "log" 118 119 "github.com/cloudwego/kitex/server" 120 c "example.com/kitex_test/kitex_gen/a/b/c/echo" 121 "github.com/cloudwego/kitex/pkg/remote/codec/thrift" 122 ) 123 124 func main() { 125 code := thrift.NewThriftCodecWithConfig(thrift.FastRead | thrift.FastWrite | thrift.FrugalRead | thrift.FrugalWrite) 126 svr := c.NewServer(new(EchoImpl), server.WithPayloadCodec(code)) 127 128 err := svr.Run() 129 if err != nil { 130 log.Println(err.Error()) 131 } 132 } 133 ``` 134 135 ### 配合 Thriftgo 做 Thrift IDL 的编解码 136 137 #### 编写 Thrift 文件 138 139 现在假设我们有如下 Thrift 文件: 140 my.thrift: 141 142 ```thrift 143 struct MyStruct { 144 1: string msg 145 2: i64 code 146 } 147 ``` 148 149 #### 使用 Thriftgo 生成代码 150 151 定义好需要的 Thrift 文件后,在使用 Thriftgo 生成 Go 语言代码时使用 `frugal_tag` 参数。 152 示例: 153 154 ```shell 155 thriftgo -r -o thrift -g go:frugal_tag,package_prefix=example.com/kitex_test/thrift my.thrift 156 ``` 157 158 如果不需要编解码代码,可以带上 `template=slim` 参数 159 160 ```shell 161 thriftgo -r -o thrift -g go:frugal_tag,template=slim,package_prefix=example.com/kitex_test/thrift my.thrift 162 ``` 163 164 #### 使用 Frugal 进行编解码 165 166 生成所需要的结构体后,直接使用 Frugal 进行编解码即可。 167 示例: 168 169 ```go 170 package main 171 172 import ( 173 "github.com/cloudwego/frugal" 174 175 "example.com/kitex_test/thrift" 176 ) 177 178 func main() { 179 ms := &thrift.MyStruct{ 180 Msg: "my message", 181 Code: 1024, 182 } 183 ... 184 buf := make([]byte, frugal.EncodedSize(ms)) 185 frugal.EncodeObject(buf, nil, ms) 186 ... 187 got := &thrift.MyStruct{} 188 frugal.DecodeObject(buf, got) 189 ... 190 } 191 ``` 192 193 ### 直接定义 struct 进行编解码 194 195 #### 定义 struct 196 197 现在假设我们需要如下 struct: 198 199 ```go 200 type MyStruct struct { 201 Msg string 202 Code int64 203 Numbers []int64 204 } 205 ``` 206 207 #### 给结构体字段添加 tag 208 209 Frugal 所需的 tag 形如 `frugal:"1,default,string"`,其中 `1` 为字段 ID, `default` 为字段的 requiredness, `string` 表示字段的类型。字段 ID 和 字段 requiredness 是必须的,但是字段类型只有当字段为 `list` 、`set` 和 `enum` 时是必须的。 210 211 上述的 `MyStruct` 可以添加如下 tag: 212 213 ```go 214 type MyStruct struct { 215 Msg string `frugal:"1,default"` 216 Code int64 `frugal:"2,default"` 217 Numbers []int64 `frugal:"3,default,list<i64>"` 218 } 219 ``` 220 221 下面是完整的类型示例: 222 223 ```go 224 type MyEnum int64 225 226 type Example struct { 227 MyOptBool *bool `frugal:"1,optional"` 228 MyReqBool bool `frugal:"2,required"` 229 MyOptByte *int8 `frugal:"3,optional"` 230 MyReqByte int8 `frugal:"4,required"` 231 MyOptI16 *int16 `frugal:"5,optional"` 232 MyReqI16 int16 `frugal:"6,required"` 233 MyOptI32 *int32 `frugal:"7,optional"` 234 MyReqI32 int32 `frugal:"8,required"` 235 MyOptI64 *int64 `frugal:"9,optional"` 236 MyReqI64 int64 `frugal:"10,required"` 237 MyOptString *string `frugal:"11,optional"` 238 MyReqString string `frugal:"12,required"` 239 MyOptBinary []byte `frugal:"13,optional"` 240 MyReqBinary []byte `frugal:"14,required"` 241 MyOptI64Set []int64 `frugal:"15,optional,set<i64>"` 242 MyReqI64Set []int64 `frugal:"16,required,set<i64>"` 243 MyOptI64List []int64 `frugal:"17,optional,list<i64>"` 244 MyReqI64List []int64 `frugal:"18,required,list<i64>"` 245 MyOptI64StringMap map[int64]string `frugal:"19,optional"` 246 MyReqI64StringMap map[int64]string `frugal:"20,required"` 247 MyOptEnum *MyEnum `frugal:"21,optional,i64"` 248 MyReqEnum *MyEnum `frugal:"22,optional,i64"` 249 } 250 ``` 251 252 #### 使用 Frugal 进行编解码 253 254 直接使用 Frugal 进行编解码即可。 255 示例: 256 257 ```go 258 package main 259 260 import ( 261 "github.com/cloudwego/frugal" 262 ) 263 264 func main() { 265 ms := &thrift.MyStruct{ 266 Msg: "my message", 267 Code: 1024, 268 Numbers: []int64{0, 1, 2, 3, 4}, 269 } 270 ... 271 buf := make([]byte, frugal.EncodedSize(ms)) 272 frugal.EncodeObject(buf, nil, ms) 273 ... 274 got := &thrift.MyStruct{} 275 frugal.DecodeObject(buf, got) 276 ... 277 } 278 ```