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  ```