├── .editorconfig
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── arpc
├── client
│ └── main.go
├── codec
│ ├── codec.go
│ ├── jsoniter.go
│ └── protobuf.go
├── go.mod
├── go.sum
└── server
│ └── main.go
├── docs
├── images
│ ├── result_20201014_012332.png
│ └── result_20201014_222559.png
└── index.html
├── erpc-v6
├── client
│ └── main.go
├── go.mod
├── go.sum
└── server
│ └── main.go
├── gin
├── client
│ └── main.go
├── go.mod
├── go.sum
└── server
│ └── main.go
├── go-micro-v2
├── client
│ └── main.go
├── go.mod
├── go.sum
└── server
│ └── main.go
├── grpc
├── client
│ └── main.go
├── go.mod
├── go.sum
└── server
│ └── main.go
├── model
├── go.mod
├── go.sum
├── grpc
│ └── model
│ │ ├── model.go
│ │ ├── model.pb.go
│ │ └── model_grpc.pb.go
├── micro-v2
│ └── model
│ │ ├── model.go
│ │ ├── model.pb.go
│ │ └── model.pb.micro.go
├── model.proto
├── standard
│ └── model
│ │ ├── model.go
│ │ └── model.pb.go
└── twirp-v7
│ └── model
│ ├── model.go
│ ├── model.pb.go
│ └── model.twirp.go
├── rpcx-v5
├── client
│ └── main.go
├── codec
│ ├── codec.go
│ └── jsoniter.go
├── go.mod
├── go.sum
├── server
│ └── main.go
└── tls
│ └── tls.go
├── scripts
├── benchmark.list
├── benchmark.sh
├── build.list
├── build.sh
└── gen-proto.sh
├── tools
├── gen
│ ├── docs.tmpl
│ ├── main.go
│ └── readme.tmpl
├── go.mod
└── go.sum
└── twirp-v7
├── client
└── main.go
├── go.mod
├── go.sum
└── server
└── main.go
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 |
3 | root = true
4 |
5 | [*]
6 | charset = utf-8
7 | indent_style = space
8 | indent_size = 2
9 | tab_width = 4
10 | end_of_line = lf
11 | insert_final_newline = true
12 | trim_trailing_whitespace = true
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/go,code,jetbrains
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go,code,jetbrains
3 |
4 | ### Code ###
5 | .vscode/*
6 | !.vscode/settings.json
7 | !.vscode/tasks.json
8 | !.vscode/launch.json
9 | !.vscode/extensions.json
10 | *.code-workspace
11 |
12 | ### Go ###
13 | # Binaries for programs and plugins
14 | *.exe
15 | *.exe~
16 | *.dll
17 | *.so
18 | *.dylib
19 |
20 | # Test binary, built with `go test -c`
21 | *.test
22 |
23 | # Output of the go coverage tool, specifically when used with LiteIDE
24 | *.out
25 |
26 | # Dependency directories (remove the comment below to include it)
27 | # vendor/
28 |
29 | ### Go Patch ###
30 | /vendor/
31 | /Godeps/
32 |
33 | ### JetBrains ###
34 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
35 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
36 |
37 | # User-specific stuff
38 | .idea/**/workspace.xml
39 | .idea/**/tasks.xml
40 | .idea/**/usage.statistics.xml
41 | .idea/**/dictionaries
42 | .idea/**/shelf
43 |
44 | # Generated files
45 | .idea/**/contentModel.xml
46 |
47 | # Sensitive or high-churn files
48 | .idea/**/dataSources/
49 | .idea/**/dataSources.ids
50 | .idea/**/dataSources.local.xml
51 | .idea/**/sqlDataSources.xml
52 | .idea/**/dynamic.xml
53 | .idea/**/uiDesigner.xml
54 | .idea/**/dbnavigator.xml
55 |
56 | # Gradle
57 | .idea/**/gradle.xml
58 | .idea/**/libraries
59 |
60 | # Gradle and Maven with auto-import
61 | # When using Gradle or Maven with auto-import, you should exclude module files,
62 | # since they will be recreated, and may cause churn. Uncomment if using
63 | # auto-import.
64 | # .idea/artifacts
65 | # .idea/compiler.xml
66 | # .idea/jarRepositories.xml
67 | # .idea/modules.xml
68 | # .idea/*.iml
69 | # .idea/modules
70 | # *.iml
71 | # *.ipr
72 |
73 | # CMake
74 | cmake-build-*/
75 |
76 | # Mongo Explorer plugin
77 | .idea/**/mongoSettings.xml
78 |
79 | # File-based project format
80 | *.iws
81 |
82 | # IntelliJ
83 | out/
84 |
85 | # mpeltonen/sbt-idea plugin
86 | .idea_modules/
87 |
88 | # JIRA plugin
89 | atlassian-ide-plugin.xml
90 |
91 | # Cursive Clojure plugin
92 | .idea/replstate.xml
93 |
94 | # Crashlytics plugin (for Android Studio and IntelliJ)
95 | com_crashlytics_export_strings.xml
96 | crashlytics.properties
97 | crashlytics-build.properties
98 | fabric.properties
99 |
100 | # Editor-based Rest Client
101 | .idea/httpRequests
102 |
103 | # Android studio 3.1+ serialized cache file
104 | .idea/caches/build_file_checksums.ser
105 |
106 | ### JetBrains Patch ###
107 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
108 |
109 | # *.iml
110 | # modules.xml
111 | # .idea/misc.xml
112 | # *.ipr
113 |
114 | # Sonarlint plugin
115 | # https://plugins.jetbrains.com/plugin/7973-sonarlint
116 | .idea/**/sonarlint/
117 |
118 | # SonarQube Plugin
119 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
120 | .idea/**/sonarIssues.xml
121 |
122 | # Markdown Navigator plugin
123 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
124 | .idea/**/markdown-navigator.xml
125 | .idea/**/markdown-navigator-enh.xml
126 | .idea/**/markdown-navigator/
127 |
128 | # Cache file creation bug
129 | # See https://youtrack.jetbrains.com/issue/JBR-2257
130 | .idea/$CACHE_FILE$
131 |
132 | # CodeStream plugin
133 | # https://plugins.jetbrains.com/plugin/12206-codestream
134 | .idea/codestream.xml
135 |
136 | # End of https://www.toptal.com/developers/gitignore/api/go,code,jetbrains
137 |
138 | bin/
139 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 micro-svc
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | GOBUILD = go build -ldflags "-w -s"
2 |
3 | ALL: lint build
4 |
5 | .PHONY: gomod-tidy
6 | gomod-tidy:
7 | ./scripts/build.sh gomod-tidy
8 |
9 | .PHONY: lint
10 | lint:
11 | ./scripts/build.sh lint
12 |
13 | .PHONY: build
14 | build:
15 | ./scripts/build.sh build
16 |
17 | .PHONY: benchmark
18 | benchmark:
19 | ./scripts/benchmark.sh
20 |
21 | .PHONY: gen-docs
22 | gen-docs:
23 | cd tools && go run gen/main.go --gen=docs >../docs/index.html 2>&1
24 |
25 | .PHONY: gen-readme
26 | gen-readme:
27 | cd tools && go run gen/main.go --gen=readme >../README.md 2>&1
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://micro-svc.github.io/go-rpc-framework-benchmark/)
2 |
3 | ## 测试结果
4 |
5 | | name | package | transport | codec | tps |
6 | | ----------- | ----------- | --------- | ---------- | ----: |
7 | | gin | gin | `http` | `json` | 29241 |
8 | | grpc | grpc | `grpc` | `protobuf` | 49967 |
9 | | rpcx-01 | rpcx-v5 | `tcp` | `json` | 38435 |
10 | | rpcx-02 | rpcx-v5 | `tcp` | `protobuf` | 73303 |
11 | | rpcx-03 | rpcx-v5 | `tcp` | `msgpack` | 40637 |
12 | | rpcx-04 | rpcx-v5 | `tcp` | `jsoniter` | 53163 |
13 | | go-micro-01 | go-micro-v2 | `grpc` | `json` | 6879 |
14 | | go-micro-02 | go-micro-v2 | `grpc` | `protobuf` | 24923 |
15 | | go-micro-03 | go-micro-v2 | `http` | `json` | 4297 |
16 | | go-micro-04 | go-micro-v2 | `http` | `protobuf` | 4016 |
17 | | twirp-01 | twirp-v7 | `http` | `json` | 8344 |
18 | | twirp-02 | twirp-v7 | `http` | `protobuf` | 41862 |
19 | | erpc-01 | erpc-v6 | `tcp` | `json` | 38183 |
20 | | erpc-02 | erpc-v6 | `tcp` | `protobuf` | 62062 |
21 | | arpc-01 | arpc | `tcp` | `json` | 44339 |
22 | | arpc-02 | arpc | `tcp` | `protobuf` | 89693 |
23 | | arpc-03 | arpc | `tcp` | `jsoniter` | 63259 |
24 |
25 | > 机器配置:4 Cores 16G Memory
26 |
27 | > 更新时间:2020-10-14
28 |
29 | ## 测试列表
30 |
31 | - [gin](https://github.com/gin-gonic/gin):WEB 框架,用来和 RPC 做对比
32 | - [grpc](https://github.com/grpc/grpc-go):跨语言 RPC 框架
33 | - [rpcx/v5](https://github.com/smallnest/rpcx):RPC 框架,自带微服务组件
34 | - [go-micro/v2](https://github.com/micro/go-micro):RPC 框架,自带微服务组件
35 | - [twirp/v7](https://github.com/twitchtv/twirp):RPC 框架
36 | - [erpc-v6](https://github.com/henrylee2cn/erpc):RPC 框架
37 | - [arpc](https://github.com/lesismal/arpc):RPC 框架
38 |
39 | ## 详细结果
40 |
41 | ### gin
42 |
43 | ```sh
44 | $ bin/gin-cli --transport=http --codec=json
45 | {"time":"2020-10-14T22:12:59.380+08:00","level":"info","caller":"main.go:52","goid":1,"clients":100,"requests":1000,"total":100000}
46 | {"time":"2020-10-14T22:13:02.834+08:00","level":"info","caller":"main.go:75","goid":1,"tps":29241,"min":"133.706µs","max":"33.977848ms","mean":"3.361119ms","median":"2.349353ms"}
47 | ```
48 |
49 | ### grpc
50 |
51 | ```sh
52 | $ bin/grpc-cli --transport=grpc --codec=protobuf
53 | {"time":"2020-10-14T22:13:22.086+08:00","level":"info","caller":"main.go:48","goid":1,"clients":100,"requests":1000,"total":100000}
54 | {"time":"2020-10-14T22:13:24.118+08:00","level":"info","caller":"main.go:71","goid":1,"tps":49967,"min":"149.954µs","max":"19.60794ms","mean":"1.98342ms","median":"1.767637ms"}
55 | ```
56 |
57 | ### rpcx-01
58 |
59 | ```sh
60 | $ bin/rpcx-v5-cli --transport=tcp --codec=json
61 | {"time":"2020-10-14T22:13:43.265+08:00","level":"info","caller":"main.go:65","goid":1,"clients":100,"requests":1000,"total":100000}
62 | {"time":"2020-10-14T22:13:45.901+08:00","level":"info","caller":"main.go:88","goid":1,"tps":38435,"min":"128.622µs","max":"29.78092ms","mean":"2.54391ms","median":"1.879683ms"}
63 | ```
64 |
65 | ### rpcx-02
66 |
67 | ```sh
68 | $ bin/rpcx-v5-cli --transport=tcp --codec=protobuf
69 | {"time":"2020-10-14T22:14:04.532+08:00","level":"info","caller":"main.go:65","goid":1,"clients":100,"requests":1000,"total":100000}
70 | {"time":"2020-10-14T22:14:05.928+08:00","level":"info","caller":"main.go:88","goid":1,"tps":73303,"min":"74.261µs","max":"20.752848ms","mean":"1.330593ms","median":"1.080378ms"}
71 | ```
72 |
73 | ### rpcx-03
74 |
75 | ```sh
76 | $ bin/rpcx-v5-cli --transport=tcp --codec=msgpack
77 | {"time":"2020-10-14T22:14:24.565+08:00","level":"info","caller":"main.go:65","goid":1,"clients":100,"requests":1000,"total":100000}
78 | {"time":"2020-10-14T22:14:27.057+08:00","level":"info","caller":"main.go:88","goid":1,"tps":40637,"min":"115.936µs","max":"27.667247ms","mean":"2.409238ms","median":"1.826393ms"}
79 | ```
80 |
81 | ### rpcx-04
82 |
83 | ```sh
84 | $ bin/rpcx-v5-cli --transport=tcp --codec=jsoniter
85 | {"time":"2020-10-14T22:14:45.688+08:00","level":"info","caller":"main.go:65","goid":1,"clients":100,"requests":1000,"total":100000}
86 | {"time":"2020-10-14T22:14:47.601+08:00","level":"info","caller":"main.go:88","goid":1,"tps":53163,"min":"85.394µs","max":"21.437595ms","mean":"1.844519ms","median":"1.456187ms"}
87 | ```
88 |
89 | ### go-micro-01
90 |
91 | ```sh
92 | $ bin/go-micro-v2-cli --transport=grpc --codec=json
93 | {"time":"2020-10-14T22:15:06.756+08:00","level":"info","caller":"main.go:79","goid":1,"clients":100,"requests":1000,"total":100000}
94 | {"time":"2020-10-14T22:15:21.323+08:00","level":"info","caller":"main.go:102","goid":1,"tps":6879,"min":"581.034µs","max":"130.350326ms","mean":"14.278674ms","median":"9.94277ms"}
95 | ```
96 |
97 | ### go-micro-02
98 |
99 | ```sh
100 | $ bin/go-micro-v2-cli --transport=grpc --codec=protobuf
101 | {"time":"2020-10-14T22:15:40.075+08:00","level":"info","caller":"main.go:79","goid":1,"clients":100,"requests":1000,"total":100000}
102 | {"time":"2020-10-14T22:15:44.120+08:00","level":"info","caller":"main.go:102","goid":1,"tps":24923,"min":"218.682µs","max":"22.308351ms","mean":"3.958798ms","median":"3.332323ms"}
103 | ```
104 |
105 | ### go-micro-03
106 |
107 | ```sh
108 | $ bin/go-micro-v2-cli --transport=http --codec=json
109 | {"time":"2020-10-14T22:16:02.906+08:00","level":"info","caller":"main.go:79","goid":1,"clients":100,"requests":1000,"total":100000}
110 | {"time":"2020-10-14T22:16:26.206+08:00","level":"info","caller":"main.go:102","goid":1,"tps":4297,"min":"724.543µs","max":"386.259566ms","mean":"23.175511ms","median":"22.880496ms"}
111 | ```
112 |
113 | ### go-micro-04
114 |
115 | ```sh
116 | $ bin/go-micro-v2-cli --transport=http --codec=protobuf
117 | {"time":"2020-10-14T22:16:44.996+08:00","level":"info","caller":"main.go:79","goid":1,"clients":100,"requests":1000,"total":100000}
118 | {"time":"2020-10-14T22:17:09.924+08:00","level":"info","caller":"main.go:102","goid":1,"tps":4016,"min":"1.222163ms","max":"172.771424ms","mean":"24.871255ms","median":"24.670879ms"}
119 | ```
120 |
121 | ### twirp-01
122 |
123 | ```sh
124 | $ bin/twirp-v7-cli --transport=http --codec=json
125 | {"time":"2020-10-14T22:17:28.989+08:00","level":"info","caller":"main.go:56","goid":1,"clients":100,"requests":1000,"total":100000}
126 | {"time":"2020-10-14T22:17:41.004+08:00","level":"info","caller":"main.go:79","goid":1,"tps":8344,"min":"449.595µs","max":"121.748109ms","mean":"11.760852ms","median":"7.731058ms"}
127 | ```
128 |
129 | ### twirp-02
130 |
131 | ```sh
132 | $ bin/twirp-v7-cli --transport=http --codec=protobuf
133 | {"time":"2020-10-14T22:17:59.633+08:00","level":"info","caller":"main.go:56","goid":1,"clients":100,"requests":1000,"total":100000}
134 | {"time":"2020-10-14T22:18:02.055+08:00","level":"info","caller":"main.go:79","goid":1,"tps":41862,"min":"97.612µs","max":"31.231029ms","mean":"2.331445ms","median":"1.578338ms"}
135 | ```
136 |
137 | ### erpc-01
138 |
139 | ```sh
140 | $ bin/erpc-v6-cli --transport=tcp --codec=json
141 | {"time":"2020-10-14T22:18:21.267+08:00","level":"info","caller":"main.go:54","goid":1,"clients":100,"requests":1000,"total":100000}
142 | {"time":"2020-10-14T22:18:23.918+08:00","level":"info","caller":"main.go:77","goid":1,"tps":38183,"min":"113.675µs","max":"33.545517ms","mean":"2.536947ms","median":"1.621098ms"}
143 | ```
144 |
145 | ### erpc-02
146 |
147 | ```sh
148 | $ bin/erpc-v6-cli --transport=tcp --codec=protobuf
149 | {"time":"2020-10-14T22:18:42.577+08:00","level":"info","caller":"main.go:54","goid":1,"clients":100,"requests":1000,"total":100000}
150 | {"time":"2020-10-14T22:18:44.222+08:00","level":"info","caller":"main.go:77","goid":1,"tps":62062,"min":"74.662µs","max":"19.501517ms","mean":"1.576802ms","median":"1.037877ms"}
151 | ```
152 |
153 | ### arpc-01
154 |
155 | ```sh
156 | $ bin/arpc-cli --transport=tcp --codec=json
157 | {"time":"2020-10-14T22:19:03.154+08:00","level":"info","caller":"main.go:58","goid":1,"clients":100,"requests":1000,"total":100000}
158 | {"time":"2020-10-14T22:19:05.440+08:00","level":"info","caller":"main.go:81","goid":1,"tps":44339,"min":"90.944µs","max":"63.282135ms","mean":"2.140956ms","median":"1.043528ms"}
159 | ```
160 |
161 | ### arpc-02
162 |
163 | ```sh
164 | $ bin/arpc-cli --transport=tcp --codec=protobuf
165 | {"time":"2020-10-14T22:19:24.070+08:00","level":"info","caller":"main.go:58","goid":1,"clients":100,"requests":1000,"total":100000}
166 | {"time":"2020-10-14T22:19:25.215+08:00","level":"info","caller":"main.go:81","goid":1,"tps":89693,"min":"53.244µs","max":"21.649969ms","mean":"1.055977ms","median":"557.315µs"}
167 | ```
168 |
169 | ### arpc-03
170 |
171 | ```sh
172 | $ bin/arpc-cli --transport=tcp --codec=jsoniter
173 | {"time":"2020-10-14T22:19:43.857+08:00","level":"info","caller":"main.go:58","goid":1,"clients":100,"requests":1000,"total":100000}
174 | {"time":"2020-10-14T22:19:45.470+08:00","level":"info","caller":"main.go:81","goid":1,"tps":63259,"min":"93.395µs","max":"28.08575ms","mean":"1.499947ms","median":"790.141µs"}
175 | ```
176 |
177 | ## TODO
178 |
179 | - [go-chassis](https://github.com/go-chassis/go-chassis)
180 | - [TarsGo](https://github.com/TarsCloud/TarsGo)
181 | - [go-zero](https://github.com/tal-tech/go-zero)
182 |
--------------------------------------------------------------------------------
/arpc/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "net"
6 | "sync"
7 | "time"
8 |
9 | "github.com/micro-svc/go-rpc-framework-benchmark/arpc/codec"
10 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
11 |
12 | ulog "github.com/gxxgle/go-utils/log"
13 | "github.com/lesismal/arpc"
14 | alog "github.com/lesismal/arpc/log"
15 | "github.com/montanaflynn/stats"
16 | "github.com/phuslu/log"
17 | )
18 |
19 | var (
20 | // flags
21 | clients = flag.Int("clients", 100, "concurrency client amount")
22 | requests = flag.Int("requests", 1000, "request amount per client")
23 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
24 | transport = flag.String("transport", "tcp", "server transport [tcp]")
25 | xcodec = flag.String("codec", "json", "server codec [json, jsoniter, protobuf]")
26 |
27 | acodec codec.Codec
28 | )
29 |
30 | func main() {
31 | flag.Parse()
32 | alog.SetLogLevel(alog.LogLevelNone)
33 |
34 | switch *transport {
35 | case "tcp":
36 | default:
37 | log.Fatal().Msg("flag transport not support")
38 | }
39 |
40 | switch *xcodec {
41 | case "json":
42 | acodec = codec.Json
43 | case "jsoniter":
44 | acodec = codec.Jsoniter
45 | case "protobuf":
46 | acodec = codec.Protobuf
47 | default:
48 | log.Fatal().Msg("flag codec not support")
49 | }
50 |
51 | var (
52 | wg sync.WaitGroup
53 | total = *clients * *requests
54 | clis = newClients(*clients)
55 | durations = make([]time.Duration, total)
56 | )
57 |
58 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
59 |
60 | start := time.Now()
61 | wg.Add(total)
62 | for i := range clis {
63 | go func(i int) {
64 | cli := clis[i]
65 | for j := 0; j < *requests; j++ {
66 | start := time.Now()
67 | call(cli)
68 | durations[i**requests+j] = time.Since(start)
69 | wg.Done()
70 | }
71 | }(i)
72 | }
73 | wg.Wait()
74 |
75 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
76 | s := stats.LoadRawData(durations)
77 | min, _ := s.Min()
78 | max, _ := s.Max()
79 | mean, _ := s.Mean()
80 | median, _ := s.Median()
81 | log.Info().
82 | Int64("tps", tps).
83 | Dur("min", time.Duration(min)).
84 | Dur("max", time.Duration(max)).
85 | Dur("mean", time.Duration(mean)).
86 | Dur("median", time.Duration(median)).
87 | Msg("")
88 | }
89 |
90 | func newClients(amount int) []*arpc.Client {
91 | clis := make([]*arpc.Client, amount)
92 | for i := 0; i < amount; i++ {
93 | cli, err := arpc.NewClient(func() (net.Conn, error) {
94 | return net.DialTimeout(*transport, *address, time.Second*3)
95 | })
96 | ulog.FatalIfError(err)
97 | cli.Codec = acodec
98 | cli.Run()
99 | clis[i] = cli
100 | call(clis[i]) // warmup
101 | }
102 | return clis
103 | }
104 |
105 | func call(cli *arpc.Client) {
106 | rsp := &model.Message{}
107 | err := cli.Call("Hello", model.Example, rsp, time.Second*30)
108 | ulog.FatalIfError(err)
109 | if !model.CheckExample(rsp) {
110 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/arpc/codec/codec.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "errors"
5 |
6 | "github.com/lesismal/arpc/codec"
7 | )
8 |
9 | type Codec = codec.Codec
10 |
11 | var (
12 | Json Codec = &codec.JSONCodec{}
13 | Jsoniter Codec = jsoniterCodec{}
14 | Protobuf Codec = protoCodec{}
15 |
16 | ErrInvalidMessage = errors.New("invalid message")
17 | )
18 |
--------------------------------------------------------------------------------
/arpc/codec/jsoniter.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | jsoniter "github.com/json-iterator/go"
5 | )
6 |
7 | var json = jsoniter.Config{
8 | EscapeHTML: true,
9 | SortMapKeys: true,
10 | ValidateJsonRawMessage: true,
11 | UseNumber: true,
12 | }.Froze()
13 |
14 | type jsoniterCodec struct{}
15 |
16 | func (c jsoniterCodec) Marshal(v interface{}) ([]byte, error) {
17 | return json.Marshal(v)
18 | }
19 |
20 | func (c jsoniterCodec) Unmarshal(data []byte, v interface{}) error {
21 | return json.Unmarshal(data, v)
22 | }
23 |
--------------------------------------------------------------------------------
/arpc/codec/protobuf.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | // nolint
5 | "github.com/golang/protobuf/proto"
6 | )
7 |
8 | type protoCodec struct{}
9 |
10 | func (c protoCodec) Marshal(v interface{}) ([]byte, error) {
11 | p, ok := v.(proto.Message)
12 | if !ok {
13 | return nil, ErrInvalidMessage
14 | }
15 | return proto.Marshal(p)
16 | }
17 |
18 | func (c protoCodec) Unmarshal(data []byte, v interface{}) error {
19 | p, ok := v.(proto.Message)
20 | if !ok {
21 | return ErrInvalidMessage
22 | }
23 | return proto.Unmarshal(data, p)
24 | }
25 |
--------------------------------------------------------------------------------
/arpc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/arpc
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/golang/protobuf v1.4.2
7 | github.com/gxxgle/go-utils v1.2.4
8 | github.com/json-iterator/go v1.1.10
9 | github.com/lesismal/arpc v0.0.0-20201013181910-f4499e7405f3
10 | github.com/lesismal/arpcext v0.0.0-20201012120830-1c9688695667 // indirect
11 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
12 | github.com/montanaflynn/stats v0.6.3
13 | github.com/phuslu/log v1.0.44
14 | )
15 |
16 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
17 |
18 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
19 |
--------------------------------------------------------------------------------
/arpc/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "net"
6 |
7 | "github.com/micro-svc/go-rpc-framework-benchmark/arpc/codec"
8 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
9 |
10 | ulog "github.com/gxxgle/go-utils/log"
11 | "github.com/lesismal/arpc"
12 | alog "github.com/lesismal/arpc/log"
13 | "github.com/phuslu/log"
14 | )
15 |
16 | var (
17 | // flags
18 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
19 | transport = flag.String("transport", "tcp", "server transport [tcp]")
20 | xcodec = flag.String("codec", "json", "server codec [json, jsoniter, protobuf]")
21 |
22 | acodec codec.Codec
23 | )
24 |
25 | type Server struct{}
26 |
27 | func (s *Server) Hello(ctx *arpc.Context) {
28 | req := &model.Message{}
29 | ulog.FatalIfError(ctx.Bind(req))
30 | ulog.FatalIfError(ctx.Write(req))
31 | }
32 |
33 | func main() {
34 | flag.Parse()
35 | alog.SetLogLevel(alog.LogLevelNone)
36 |
37 | switch *transport {
38 | case "tcp":
39 | default:
40 | log.Fatal().Msg("flag transport not support")
41 | }
42 |
43 | switch *xcodec {
44 | case "json":
45 | acodec = codec.Json
46 | case "jsoniter":
47 | acodec = codec.Jsoniter
48 | case "protobuf":
49 | acodec = codec.Protobuf
50 | default:
51 | log.Fatal().Msg("flag codec not support")
52 | }
53 |
54 | lis, err := net.Listen(*transport, *address)
55 | ulog.FatalIfError(err)
56 |
57 | srv := arpc.NewServer()
58 | srv.Codec = acodec
59 | srv.Handler.Handle("Hello", new(Server).Hello)
60 | log.Info().Str("address", *address).Msg("server running")
61 | ulog.FatalIfError(srv.Serve(lis))
62 | }
63 |
--------------------------------------------------------------------------------
/docs/images/result_20201014_012332.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/micro-svc/go-rpc-framework-benchmark/1b6ef8eb5c2819c968a16bc8153cdb94ff3859c2/docs/images/result_20201014_012332.png
--------------------------------------------------------------------------------
/docs/images/result_20201014_222559.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/micro-svc/go-rpc-framework-benchmark/1b6ef8eb5c2819c968a16bc8153cdb94ff3859c2/docs/images/result_20201014_222559.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Go RPC framework benchmark
8 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/erpc-v6/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "sync"
6 | "time"
7 |
8 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
9 |
10 | _ "github.com/gxxgle/go-utils/log"
11 | "github.com/henrylee2cn/erpc/v6"
12 | "github.com/henrylee2cn/erpc/v6/codec"
13 | "github.com/montanaflynn/stats"
14 | "github.com/phuslu/log"
15 | )
16 |
17 | var (
18 | // flags
19 | clients = flag.Int("clients", 100, "concurrency client amount")
20 | requests = flag.Int("requests", 1000, "request amount per client")
21 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
22 | transport = flag.String("transport", "tcp", "server transport [tcp]")
23 | xcodec = flag.String("codec", "json", "server codec [json, protobuf]")
24 |
25 | cfgCodec string
26 | )
27 |
28 | func main() {
29 | flag.Parse()
30 | erpc.SetLoggerLevel("OFF")
31 |
32 | switch *transport {
33 | case "tcp":
34 | default:
35 | log.Fatal().Msg("flag transport not support")
36 | }
37 |
38 | switch *xcodec {
39 | case "json":
40 | cfgCodec = new(codec.JSONCodec).Name()
41 | case "protobuf":
42 | cfgCodec = new(codec.ProtoCodec).Name()
43 | default:
44 | log.Fatal().Msg("flag codec not support")
45 | }
46 |
47 | var (
48 | wg sync.WaitGroup
49 | total = *clients * *requests
50 | clis = newClients(*clients)
51 | durations = make([]time.Duration, total)
52 | )
53 |
54 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
55 |
56 | start := time.Now()
57 | wg.Add(total)
58 | for i := range clis {
59 | go func(i int) {
60 | cli := clis[i]
61 | for j := 0; j < *requests; j++ {
62 | start := time.Now()
63 | call(cli)
64 | durations[i**requests+j] = time.Since(start)
65 | wg.Done()
66 | }
67 | }(i)
68 | }
69 | wg.Wait()
70 |
71 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
72 | s := stats.LoadRawData(durations)
73 | min, _ := s.Min()
74 | max, _ := s.Max()
75 | mean, _ := s.Mean()
76 | median, _ := s.Median()
77 | log.Info().
78 | Int64("tps", tps).
79 | Dur("min", time.Duration(min)).
80 | Dur("max", time.Duration(max)).
81 | Dur("mean", time.Duration(mean)).
82 | Dur("median", time.Duration(median)).
83 | Msg("")
84 | }
85 |
86 | func newClients(amount int) []erpc.Session {
87 | clis := make([]erpc.Session, amount)
88 | for i := 0; i < amount; i++ {
89 | peer := erpc.NewPeer(erpc.PeerConfig{
90 | Network: *transport,
91 | DefaultBodyCodec: cfgCodec,
92 | })
93 | cli, stat := peer.Dial(*address)
94 | if !stat.OK() {
95 | log.Fatal().Msg(stat.String())
96 | }
97 | clis[i] = cli
98 | call(clis[i]) // warmup
99 | }
100 | return clis
101 | }
102 |
103 | func call(cli erpc.Session) {
104 | rsp := &model.Message{}
105 | stat := cli.Call("/server/hello", model.Example, rsp).Status()
106 | if !stat.OK() {
107 | log.Fatal().Msg(stat.String())
108 | }
109 | if !model.CheckExample(rsp) {
110 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/erpc-v6/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/erpc-v6
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gxxgle/go-utils v1.2.4
7 | github.com/henrylee2cn/erpc/v6 v6.3.4
8 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
9 | github.com/montanaflynn/stats v0.6.3
10 | github.com/phuslu/log v1.0.44
11 | )
12 |
13 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
14 |
15 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
16 |
--------------------------------------------------------------------------------
/erpc-v6/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "strings"
6 |
7 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
8 |
9 | "github.com/gxxgle/go-utils/conver"
10 | ulog "github.com/gxxgle/go-utils/log"
11 | "github.com/henrylee2cn/erpc/v6"
12 | "github.com/henrylee2cn/erpc/v6/codec"
13 | "github.com/phuslu/log"
14 | )
15 |
16 | var (
17 | // flags
18 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
19 | transport = flag.String("transport", "tcp", "server transport [tcp]")
20 | xcodec = flag.String("codec", "json", "server codec [json, protobuf]")
21 |
22 | cfgCodec string
23 | )
24 |
25 | type Server struct {
26 | erpc.CallCtx
27 | }
28 |
29 | func (s *Server) Hello(req *model.Message) (*model.Message, *erpc.Status) {
30 | rsp := &model.Message{}
31 | *rsp = *req
32 | return rsp, nil
33 | }
34 |
35 | func main() {
36 | flag.Parse()
37 | erpc.SetLoggerLevel("OFF")
38 |
39 | switch *transport {
40 | case "tcp":
41 | default:
42 | log.Fatal().Msg("flag transport not support")
43 | }
44 |
45 | switch *xcodec {
46 | case "json":
47 | cfgCodec = new(codec.JSONCodec).Name()
48 | case "protobuf":
49 | cfgCodec = new(codec.ProtoCodec).Name()
50 | default:
51 | log.Fatal().Msg("flag codec not support")
52 | }
53 |
54 | addres := strings.Split(*address, ":")
55 | if len(addres) != 2 {
56 | log.Fatal().Msg("flag addr not support")
57 | }
58 |
59 | srv := erpc.NewPeer(erpc.PeerConfig{
60 | Network: *transport,
61 | LocalIP: addres[0],
62 | ListenPort: uint16(conver.IntMust(addres[1])),
63 | DefaultBodyCodec: cfgCodec,
64 | })
65 |
66 | srv.RouteCall(new(Server))
67 | log.Info().Str("address", *address).Msg("server running")
68 | ulog.FatalIfError(srv.ListenAndServe())
69 | }
70 |
--------------------------------------------------------------------------------
/gin/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "flag"
7 | "io/ioutil"
8 | "net"
9 | "net/http"
10 | "net/http/cookiejar"
11 | "sync"
12 | "time"
13 |
14 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
15 |
16 | ulog "github.com/gxxgle/go-utils/log"
17 | "github.com/montanaflynn/stats"
18 | "github.com/phuslu/log"
19 | )
20 |
21 | var (
22 | // flags
23 | clients = flag.Int("clients", 100, "concurrency client amount")
24 | requests = flag.Int("requests", 1000, "request amount per client")
25 | address = flag.String("addr", "127.0.0.1:5678", "server address")
26 | transport = flag.String("transport", "http", "server transport [http]")
27 | codec = flag.String("codec", "json", "server codec [json]")
28 | )
29 |
30 | func main() {
31 | flag.Parse()
32 |
33 | switch *transport {
34 | case "http":
35 | default:
36 | log.Fatal().Msg("flag transport not support")
37 | }
38 |
39 | switch *codec {
40 | case "json":
41 | default:
42 | log.Fatal().Msg("flag codec not support")
43 | }
44 |
45 | var (
46 | wg sync.WaitGroup
47 | total = *clients * *requests
48 | clis = newClients(*clients)
49 | durations = make([]time.Duration, total)
50 | )
51 |
52 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
53 |
54 | start := time.Now()
55 | wg.Add(total)
56 | for i := range clis {
57 | go func(i int) {
58 | cli := clis[i]
59 | for j := 0; j < *requests; j++ {
60 | start := time.Now()
61 | call(cli)
62 | durations[i**requests+j] = time.Since(start)
63 | wg.Done()
64 | }
65 | }(i)
66 | }
67 | wg.Wait()
68 |
69 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
70 | s := stats.LoadRawData(durations)
71 | min, _ := s.Min()
72 | max, _ := s.Max()
73 | mean, _ := s.Mean()
74 | median, _ := s.Median()
75 | log.Info().
76 | Int64("tps", tps).
77 | Dur("min", time.Duration(min)).
78 | Dur("max", time.Duration(max)).
79 | Dur("mean", time.Duration(mean)).
80 | Dur("median", time.Duration(median)).
81 | Msg("")
82 | }
83 |
84 | func newClients(amount int) []*http.Client {
85 | clis := make([]*http.Client, amount)
86 | for i := 0; i < amount; i++ {
87 | clis[i] = newHTTPClient()
88 | call(clis[i]) // warmup
89 | }
90 | return clis
91 | }
92 |
93 | func call(cli *http.Client) {
94 | bs, _ := json.Marshal(model.Example)
95 | httprsp, err := cli.Post("http://"+*address, "application/json", bytes.NewBuffer(bs))
96 | ulog.FatalIfError(err)
97 | defer httprsp.Body.Close()
98 | body, _ := ioutil.ReadAll(httprsp.Body)
99 | rsp := &model.Message{}
100 | ulog.FatalIfError(json.Unmarshal(body, rsp))
101 | if !model.CheckExample(rsp) {
102 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
103 | }
104 | }
105 |
106 | func newHTTPClient() *http.Client {
107 | jar, _ := cookiejar.New(nil)
108 | transport := &http.Transport{
109 | Proxy: http.ProxyFromEnvironment,
110 | DialContext: (&net.Dialer{
111 | Timeout: 30 * time.Second,
112 | KeepAlive: 30 * time.Second,
113 | DualStack: true,
114 | }).DialContext,
115 | MaxIdleConns: 100,
116 | IdleConnTimeout: 90 * time.Second,
117 | TLSHandshakeTimeout: 10 * time.Second,
118 | ExpectContinueTimeout: 1 * time.Second,
119 | }
120 | return &http.Client{
121 | Jar: jar,
122 | Transport: transport,
123 | Timeout: 2 * time.Minute,
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/gin/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/gin
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gin-gonic/gin v1.6.3
7 | github.com/gxxgle/go-utils v1.2.4
8 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
9 | github.com/montanaflynn/stats v0.6.3
10 | github.com/phuslu/log v1.0.44
11 | )
12 |
13 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
14 |
15 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
16 |
--------------------------------------------------------------------------------
/gin/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "net/http"
6 |
7 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
8 |
9 | "github.com/gin-gonic/gin"
10 | ulog "github.com/gxxgle/go-utils/log"
11 | "github.com/phuslu/log"
12 | )
13 |
14 | var (
15 | // flags
16 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
17 | )
18 |
19 | type Server struct{}
20 |
21 | func (s *Server) Hello(ctx *gin.Context) {
22 | msg := model.Message{}
23 | ulog.FatalIfError(ctx.BindJSON(&msg))
24 | ctx.JSON(http.StatusOK, msg)
25 | }
26 |
27 | func main() {
28 | flag.Parse()
29 | gin.SetMode(gin.ReleaseMode)
30 |
31 | srv := &Server{}
32 | app := gin.New()
33 | app.POST("/", srv.Hello)
34 | log.Info().Str("address", *address).Msg("server running")
35 | ulog.FatalIfError(app.Run(*address))
36 | }
37 |
--------------------------------------------------------------------------------
/go-micro-v2/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "sync"
7 | "time"
8 |
9 | "github.com/micro-svc/go-rpc-framework-benchmark/model/micro-v2/model"
10 |
11 | ulog "github.com/gxxgle/go-utils/log"
12 | "github.com/micro/go-micro/v2"
13 | "github.com/micro/go-micro/v2/client"
14 | cligrpc "github.com/micro/go-micro/v2/client/grpc"
15 | "github.com/micro/go-micro/v2/codec/json"
16 | "github.com/micro/go-micro/v2/codec/proto"
17 | "github.com/micro/go-micro/v2/server"
18 | srvgrpc "github.com/micro/go-micro/v2/server/grpc"
19 | ts "github.com/micro/go-micro/v2/transport"
20 | tsgrpc "github.com/micro/go-micro/v2/transport/grpc"
21 | tshttp "github.com/micro/go-micro/v2/transport/http"
22 | "github.com/montanaflynn/stats"
23 | "github.com/phuslu/log"
24 | )
25 |
26 | var (
27 | // flags
28 | clients = flag.Int("clients", 100, "concurrency client amount")
29 | requests = flag.Int("requests", 1000, "request amount per client")
30 | transport = flag.String("transport", "grpc", "server transport [grpc, http]")
31 | codec = flag.String("codec", "json", "server codec [json, protobuf]")
32 |
33 | newServer = server.NewServer
34 | newClient = client.NewClient
35 | )
36 |
37 | func main() {
38 | flag.Parse()
39 |
40 | switch *transport {
41 | case "grpc":
42 | newServer = srvgrpc.NewServer
43 | newClient = cligrpc.NewClient
44 | ts.DefaultTransport = tsgrpc.NewTransport()
45 | case "http":
46 | ts.DefaultTransport = tshttp.NewTransport()
47 | default:
48 | log.Fatal().Msg("flag transport not support")
49 | }
50 |
51 | switch *codec {
52 | case "protobuf":
53 | server.DefaultServer = newServer(
54 | server.Codec("application/protobuf", proto.NewCodec),
55 | )
56 | client.DefaultClient = newClient(
57 | client.Codec("application/protobuf", proto.NewCodec),
58 | client.ContentType("application/protobuf"),
59 | )
60 | case "json":
61 | server.DefaultServer = newServer(
62 | server.Codec("application/json", json.NewCodec),
63 | )
64 | client.DefaultClient = newClient(
65 | client.Codec("application/json", json.NewCodec),
66 | client.ContentType("application/json"),
67 | )
68 | default:
69 | log.Fatal().Msg("flag codec not support")
70 | }
71 |
72 | var (
73 | wg sync.WaitGroup
74 | total = *clients * *requests
75 | clis = newClients(*clients)
76 | durations = make([]time.Duration, total)
77 | )
78 |
79 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
80 |
81 | start := time.Now()
82 | wg.Add(total)
83 | for i := range clis {
84 | go func(i int) {
85 | cli := clis[i]
86 | for j := 0; j < *requests; j++ {
87 | start := time.Now()
88 | call(cli)
89 | durations[i**requests+j] = time.Since(start)
90 | wg.Done()
91 | }
92 | }(i)
93 | }
94 | wg.Wait()
95 |
96 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
97 | s := stats.LoadRawData(durations)
98 | min, _ := s.Min()
99 | max, _ := s.Max()
100 | mean, _ := s.Mean()
101 | median, _ := s.Median()
102 | log.Info().
103 | Int64("tps", tps).
104 | Dur("min", time.Duration(min)).
105 | Dur("max", time.Duration(max)).
106 | Dur("mean", time.Duration(mean)).
107 | Dur("median", time.Duration(median)).
108 | Msg("")
109 | }
110 |
111 | func newClients(amount int) []model.HelloService {
112 | clis := make([]model.HelloService, amount)
113 | for i := 0; i < amount; i++ {
114 | service := micro.NewService()
115 | mcli := service.Client()
116 | ulog.FatalIfError(mcli.Init(client.RequestTimeout(time.Second * 30)))
117 | clis[i] = model.NewHelloService("hello", mcli)
118 | call(clis[i]) // warmup
119 | }
120 | return clis
121 | }
122 |
123 | func call(cli model.HelloService) {
124 | rsp, err := cli.Hello(context.Background(), model.Example)
125 | ulog.FatalIfError(err)
126 | if !model.CheckExample(rsp) {
127 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/go-micro-v2/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/go-micro-v2
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gxxgle/go-utils v1.2.4
7 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
8 | github.com/micro/go-micro/v2 v2.9.1
9 | github.com/montanaflynn/stats v0.6.3
10 | github.com/phuslu/log v1.0.44
11 | )
12 |
13 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
14 |
15 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
16 |
--------------------------------------------------------------------------------
/go-micro-v2/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 |
7 | "github.com/micro-svc/go-rpc-framework-benchmark/model/micro-v2/model"
8 |
9 | ulog "github.com/gxxgle/go-utils/log"
10 | "github.com/micro/go-micro/v2"
11 | "github.com/micro/go-micro/v2/client"
12 | cligrpc "github.com/micro/go-micro/v2/client/grpc"
13 | "github.com/micro/go-micro/v2/codec/json"
14 | "github.com/micro/go-micro/v2/codec/proto"
15 | "github.com/micro/go-micro/v2/server"
16 | srvgrpc "github.com/micro/go-micro/v2/server/grpc"
17 | ts "github.com/micro/go-micro/v2/transport"
18 | "github.com/phuslu/log"
19 | )
20 |
21 | var (
22 | // flags
23 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
24 | transport = flag.String("transport", "grpc", "server transport [grpc, http]")
25 |
26 | newServer = server.NewServer
27 | newClient = client.NewClient
28 | )
29 |
30 | type Server struct{}
31 |
32 | func (s *Server) Hello(ctx context.Context, req *model.Message, rsp *model.Message) error {
33 | *rsp = *req
34 | return nil
35 | }
36 |
37 | func main() {
38 | flag.Parse()
39 |
40 | switch *transport {
41 | case "grpc":
42 | newServer = srvgrpc.NewServer
43 | newClient = cligrpc.NewClient
44 | case "http":
45 | ts.DefaultTransport = ts.NewTransport()
46 | default:
47 | log.Fatal().Msg("flag transport not support")
48 | }
49 |
50 | server.DefaultServer = newServer(
51 | server.Codec("application/json", json.NewCodec),
52 | server.Codec("application/protobuf", proto.NewCodec),
53 | )
54 | client.DefaultClient = newClient(
55 | client.Codec("application/json", json.NewCodec),
56 | client.Codec("application/protobuf", proto.NewCodec),
57 | )
58 |
59 | svc := micro.NewService(
60 | micro.Name("hello"),
61 | micro.Address(*address),
62 | )
63 |
64 | ulog.FatalIfError(model.RegisterHelloHandler(svc.Server(), new(Server)))
65 | log.Info().Str("address", *address).Msg("server running")
66 | ulog.FatalIfError(svc.Run())
67 | }
68 |
--------------------------------------------------------------------------------
/grpc/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "sync"
7 | "time"
8 |
9 | "github.com/micro-svc/go-rpc-framework-benchmark/model/grpc/model"
10 |
11 | ulog "github.com/gxxgle/go-utils/log"
12 | "github.com/montanaflynn/stats"
13 | "github.com/phuslu/log"
14 | "google.golang.org/grpc"
15 | )
16 |
17 | var (
18 | // flags
19 | clients = flag.Int("clients", 100, "concurrency client amount")
20 | requests = flag.Int("requests", 1000, "request amount per client")
21 | address = flag.String("addr", "127.0.0.1:5678", "server address")
22 | transport = flag.String("transport", "grpc", "server transport [grpc]")
23 | codec = flag.String("codec", "protobuf", "server codec [protobuf]")
24 | )
25 |
26 | func main() {
27 | flag.Parse()
28 |
29 | switch *transport {
30 | case "grpc":
31 | default:
32 | log.Fatal().Msg("flag transport not support")
33 | }
34 |
35 | switch *codec {
36 | case "protobuf":
37 | default:
38 | log.Fatal().Msg("flag codec not support")
39 | }
40 |
41 | var (
42 | wg sync.WaitGroup
43 | total = *clients * *requests
44 | clis = newClients(*clients)
45 | durations = make([]time.Duration, total)
46 | )
47 |
48 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
49 |
50 | start := time.Now()
51 | wg.Add(total)
52 | for i := range clis {
53 | go func(i int) {
54 | cli := clis[i]
55 | for j := 0; j < *requests; j++ {
56 | start := time.Now()
57 | call(cli)
58 | durations[i**requests+j] = time.Since(start)
59 | wg.Done()
60 | }
61 | }(i)
62 | }
63 | wg.Wait()
64 |
65 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
66 | s := stats.LoadRawData(durations)
67 | min, _ := s.Min()
68 | max, _ := s.Max()
69 | mean, _ := s.Mean()
70 | median, _ := s.Median()
71 | log.Info().
72 | Int64("tps", tps).
73 | Dur("min", time.Duration(min)).
74 | Dur("max", time.Duration(max)).
75 | Dur("mean", time.Duration(mean)).
76 | Dur("median", time.Duration(median)).
77 | Msg("")
78 | }
79 |
80 | func newClients(amount int) []model.HelloClient {
81 | clis := make([]model.HelloClient, amount)
82 | for i := 0; i < amount; i++ {
83 | conn, err := grpc.Dial(*address, grpc.WithInsecure(), grpc.WithBlock())
84 | if err != nil {
85 | ulog.FatalIfError(err)
86 | }
87 |
88 | clis[i] = model.NewHelloClient(conn)
89 | call(clis[i]) // warmup
90 | }
91 |
92 | return clis
93 | }
94 |
95 | func call(cli model.HelloClient) {
96 | rsp, err := cli.Hello(context.Background(), model.Example)
97 | ulog.FatalIfError(err)
98 | if !model.CheckExample(rsp) {
99 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/grpc/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/grpc
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gxxgle/go-utils v1.2.4
7 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
8 | github.com/montanaflynn/stats v0.6.3
9 | github.com/phuslu/log v1.0.44
10 | google.golang.org/grpc v1.33.0
11 | )
12 |
13 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
14 |
--------------------------------------------------------------------------------
/grpc/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "net"
7 |
8 | "github.com/micro-svc/go-rpc-framework-benchmark/model/grpc/model"
9 |
10 | ulog "github.com/gxxgle/go-utils/log"
11 | "github.com/phuslu/log"
12 | "google.golang.org/grpc"
13 | )
14 |
15 | var (
16 | // flags
17 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
18 | )
19 |
20 | type Server struct {
21 | model.UnimplementedHelloServer
22 | }
23 |
24 | func (s *Server) Hello(ctx context.Context, req *model.Message) (*model.Message, error) {
25 | rsp := &model.Message{}
26 | *rsp = *req
27 | return rsp, nil
28 | }
29 |
30 | func main() {
31 | flag.Parse()
32 |
33 | lis, err := net.Listen("tcp", *address)
34 | if err != nil {
35 | ulog.FatalIfError(err)
36 | }
37 |
38 | srv := grpc.NewServer()
39 | model.RegisterHelloServer(srv, new(Server))
40 | log.Info().Str("address", *address).Msg("server running")
41 | ulog.FatalIfError(srv.Serve(lis))
42 | }
43 |
--------------------------------------------------------------------------------
/model/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/model
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/golang/protobuf v1.4.2
7 | github.com/micro/go-micro/v2 v2.9.1
8 | github.com/twitchtv/twirp v7.1.0+incompatible
9 | google.golang.org/grpc v1.33.0
10 | )
11 |
--------------------------------------------------------------------------------
/model/grpc/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | Example = example()
5 | )
6 |
7 | func example() *Message {
8 | sub1 := SubMessage{
9 | Field01: 123456.789,
10 | Field02: 1234567890,
11 | Field03: true,
12 | Field04: "string example",
13 | Field05: []byte("bytes example"),
14 | Field06: Enum_EnumC,
15 | Field07: []float64{123456.789, 223456.789, 323456.789},
16 | Field08: []int64{1234567890, 2234567890, 3234567890},
17 | Field09: []bool{true, false, true},
18 | Field10: []string{"string example 1", "string example 2", "string example 3"},
19 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
20 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
21 | }
22 |
23 | msg := &Message{
24 | Field01: 123456.789,
25 | Field02: 1234567890,
26 | Field03: true,
27 | Field04: "string example",
28 | Field05: []byte("bytes example"),
29 | Field06: Enum_EnumC,
30 | Field07: []float64{123456.789, 223456.789, 323456.789},
31 | Field08: []int64{1234567890, 2234567890, 3234567890},
32 | Field09: []bool{true, false, true},
33 | Field10: []string{"string example 1", "string example 2", "string example 3"},
34 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
35 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
36 | Field13: &sub1,
37 | }
38 |
39 | return msg
40 | }
41 |
42 | func CheckExample(msg *Message) bool {
43 | return msg.Field01 > 0 &&
44 | msg.Field01 == Example.Field01 &&
45 | msg.Field02 == Example.Field02 &&
46 | msg.Field03 == Example.Field03 &&
47 | msg.Field04 == Example.Field04
48 | }
49 |
--------------------------------------------------------------------------------
/model/grpc/model/model.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | package model
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 |
23 | type Enum int32
24 |
25 | const (
26 | Enum_EnumA Enum = 0
27 | Enum_EnumB Enum = 1
28 | Enum_EnumC Enum = 2
29 | )
30 |
31 | var Enum_name = map[int32]string{
32 | 0: "EnumA",
33 | 1: "EnumB",
34 | 2: "EnumC",
35 | }
36 |
37 | var Enum_value = map[string]int32{
38 | "EnumA": 0,
39 | "EnumB": 1,
40 | "EnumC": 2,
41 | }
42 |
43 | func (x Enum) String() string {
44 | return proto.EnumName(Enum_name, int32(x))
45 | }
46 |
47 | func (Enum) EnumDescriptor() ([]byte, []int) {
48 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
49 | }
50 |
51 | type SubMessage struct {
52 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
53 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
54 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
55 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
56 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
57 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
58 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
59 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
60 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
61 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
62 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
63 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
64 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
65 | XXX_unrecognized []byte `json:"-"`
66 | XXX_sizecache int32 `json:"-"`
67 | }
68 |
69 | func (m *SubMessage) Reset() { *m = SubMessage{} }
70 | func (m *SubMessage) String() string { return proto.CompactTextString(m) }
71 | func (*SubMessage) ProtoMessage() {}
72 | func (*SubMessage) Descriptor() ([]byte, []int) {
73 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
74 | }
75 |
76 | func (m *SubMessage) XXX_Unmarshal(b []byte) error {
77 | return xxx_messageInfo_SubMessage.Unmarshal(m, b)
78 | }
79 | func (m *SubMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
80 | return xxx_messageInfo_SubMessage.Marshal(b, m, deterministic)
81 | }
82 | func (m *SubMessage) XXX_Merge(src proto.Message) {
83 | xxx_messageInfo_SubMessage.Merge(m, src)
84 | }
85 | func (m *SubMessage) XXX_Size() int {
86 | return xxx_messageInfo_SubMessage.Size(m)
87 | }
88 | func (m *SubMessage) XXX_DiscardUnknown() {
89 | xxx_messageInfo_SubMessage.DiscardUnknown(m)
90 | }
91 |
92 | var xxx_messageInfo_SubMessage proto.InternalMessageInfo
93 |
94 | func (m *SubMessage) GetField01() float64 {
95 | if m != nil {
96 | return m.Field01
97 | }
98 | return 0
99 | }
100 |
101 | func (m *SubMessage) GetField02() int64 {
102 | if m != nil {
103 | return m.Field02
104 | }
105 | return 0
106 | }
107 |
108 | func (m *SubMessage) GetField03() bool {
109 | if m != nil {
110 | return m.Field03
111 | }
112 | return false
113 | }
114 |
115 | func (m *SubMessage) GetField04() string {
116 | if m != nil {
117 | return m.Field04
118 | }
119 | return ""
120 | }
121 |
122 | func (m *SubMessage) GetField05() []byte {
123 | if m != nil {
124 | return m.Field05
125 | }
126 | return nil
127 | }
128 |
129 | func (m *SubMessage) GetField06() Enum {
130 | if m != nil {
131 | return m.Field06
132 | }
133 | return Enum_EnumA
134 | }
135 |
136 | func (m *SubMessage) GetField07() []float64 {
137 | if m != nil {
138 | return m.Field07
139 | }
140 | return nil
141 | }
142 |
143 | func (m *SubMessage) GetField08() []int64 {
144 | if m != nil {
145 | return m.Field08
146 | }
147 | return nil
148 | }
149 |
150 | func (m *SubMessage) GetField09() []bool {
151 | if m != nil {
152 | return m.Field09
153 | }
154 | return nil
155 | }
156 |
157 | func (m *SubMessage) GetField10() []string {
158 | if m != nil {
159 | return m.Field10
160 | }
161 | return nil
162 | }
163 |
164 | func (m *SubMessage) GetField11() [][]byte {
165 | if m != nil {
166 | return m.Field11
167 | }
168 | return nil
169 | }
170 |
171 | func (m *SubMessage) GetField12() []Enum {
172 | if m != nil {
173 | return m.Field12
174 | }
175 | return nil
176 | }
177 |
178 | type Message struct {
179 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
180 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
181 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
182 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
183 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
184 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
185 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
186 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
187 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
188 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
189 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
190 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
191 | Field13 *SubMessage `protobuf:"bytes,13,opt,name=field13,proto3" json:"field13,omitempty"`
192 | Field14 []*SubMessage `protobuf:"bytes,14,rep,name=field14,proto3" json:"field14,omitempty"`
193 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
194 | XXX_unrecognized []byte `json:"-"`
195 | XXX_sizecache int32 `json:"-"`
196 | }
197 |
198 | func (m *Message) Reset() { *m = Message{} }
199 | func (m *Message) String() string { return proto.CompactTextString(m) }
200 | func (*Message) ProtoMessage() {}
201 | func (*Message) Descriptor() ([]byte, []int) {
202 | return fileDescriptor_312ac5bcab6cbb43, []int{1}
203 | }
204 |
205 | func (m *Message) XXX_Unmarshal(b []byte) error {
206 | return xxx_messageInfo_Message.Unmarshal(m, b)
207 | }
208 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
209 | return xxx_messageInfo_Message.Marshal(b, m, deterministic)
210 | }
211 | func (m *Message) XXX_Merge(src proto.Message) {
212 | xxx_messageInfo_Message.Merge(m, src)
213 | }
214 | func (m *Message) XXX_Size() int {
215 | return xxx_messageInfo_Message.Size(m)
216 | }
217 | func (m *Message) XXX_DiscardUnknown() {
218 | xxx_messageInfo_Message.DiscardUnknown(m)
219 | }
220 |
221 | var xxx_messageInfo_Message proto.InternalMessageInfo
222 |
223 | func (m *Message) GetField01() float64 {
224 | if m != nil {
225 | return m.Field01
226 | }
227 | return 0
228 | }
229 |
230 | func (m *Message) GetField02() int64 {
231 | if m != nil {
232 | return m.Field02
233 | }
234 | return 0
235 | }
236 |
237 | func (m *Message) GetField03() bool {
238 | if m != nil {
239 | return m.Field03
240 | }
241 | return false
242 | }
243 |
244 | func (m *Message) GetField04() string {
245 | if m != nil {
246 | return m.Field04
247 | }
248 | return ""
249 | }
250 |
251 | func (m *Message) GetField05() []byte {
252 | if m != nil {
253 | return m.Field05
254 | }
255 | return nil
256 | }
257 |
258 | func (m *Message) GetField06() Enum {
259 | if m != nil {
260 | return m.Field06
261 | }
262 | return Enum_EnumA
263 | }
264 |
265 | func (m *Message) GetField07() []float64 {
266 | if m != nil {
267 | return m.Field07
268 | }
269 | return nil
270 | }
271 |
272 | func (m *Message) GetField08() []int64 {
273 | if m != nil {
274 | return m.Field08
275 | }
276 | return nil
277 | }
278 |
279 | func (m *Message) GetField09() []bool {
280 | if m != nil {
281 | return m.Field09
282 | }
283 | return nil
284 | }
285 |
286 | func (m *Message) GetField10() []string {
287 | if m != nil {
288 | return m.Field10
289 | }
290 | return nil
291 | }
292 |
293 | func (m *Message) GetField11() [][]byte {
294 | if m != nil {
295 | return m.Field11
296 | }
297 | return nil
298 | }
299 |
300 | func (m *Message) GetField12() []Enum {
301 | if m != nil {
302 | return m.Field12
303 | }
304 | return nil
305 | }
306 |
307 | func (m *Message) GetField13() *SubMessage {
308 | if m != nil {
309 | return m.Field13
310 | }
311 | return nil
312 | }
313 |
314 | func (m *Message) GetField14() []*SubMessage {
315 | if m != nil {
316 | return m.Field14
317 | }
318 | return nil
319 | }
320 |
321 | func init() {
322 | proto.RegisterEnum("model.Enum", Enum_name, Enum_value)
323 | proto.RegisterType((*SubMessage)(nil), "model.SubMessage")
324 | proto.RegisterType((*Message)(nil), "model.Message")
325 | }
326 |
327 | func init() { proto.RegisterFile("model/model.proto", fileDescriptor_312ac5bcab6cbb43) }
328 |
329 | var fileDescriptor_312ac5bcab6cbb43 = []byte{
330 | // 339 bytes of a gzipped FileDescriptorProto
331 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x93, 0xcb, 0x4e, 0xc2, 0x40,
332 | 0x14, 0x86, 0x19, 0x86, 0x72, 0x19, 0x90, 0x40, 0x57, 0x27, 0xae, 0x4e, 0x48, 0x8c, 0x55, 0x03,
333 | 0x65, 0xa6, 0xe5, 0xb6, 0x14, 0x63, 0xe2, 0xc6, 0x8d, 0xee, 0xdc, 0xd1, 0x52, 0x0b, 0xb1, 0x65,
334 | 0x48, 0x01, 0xdf, 0xcc, 0xd7, 0xf1, 0x59, 0x0c, 0x97, 0xe9, 0x49, 0x8c, 0x71, 0xe7, 0xce, 0x4d,
335 | 0xfb, 0x9f, 0x7c, 0x7f, 0x4e, 0xbf, 0x4c, 0x3a, 0xa2, 0x9d, 0xea, 0x79, 0x94, 0xb8, 0x87, 0x67,
336 | 0x6f, 0x9d, 0xe9, 0xad, 0xb6, 0xad, 0xc3, 0xd0, 0xf9, 0x2c, 0x0a, 0xf1, 0xbc, 0x0b, 0x1e, 0xa3,
337 | 0xcd, 0x66, 0x16, 0x47, 0x36, 0x88, 0xca, 0xeb, 0x32, 0x4a, 0xe6, 0x7d, 0x09, 0x0c, 0x99, 0xc3,
338 | 0x9e, 0xcc, 0x48, 0x44, 0x41, 0x11, 0x99, 0xc3, 0x0d, 0x51, 0x44, 0x3c, 0xe0, 0xc8, 0x9c, 0xaa,
339 | 0x21, 0x1e, 0x11, 0x1f, 0x4a, 0xc8, 0x9c, 0x9a, 0x21, 0x3e, 0x91, 0x01, 0x58, 0xc8, 0x9c, 0x86,
340 | 0x21, 0x03, 0xfb, 0xc2, 0x90, 0x21, 0x94, 0x91, 0x39, 0x4d, 0x55, 0xef, 0x1d, 0xb5, 0xef, 0x57,
341 | 0xbb, 0xd4, 0xd4, 0x86, 0xb4, 0x60, 0x04, 0x15, 0xe4, 0x24, 0x3a, 0x22, 0x32, 0x86, 0x2a, 0x72,
342 | 0x12, 0x1d, 0x13, 0x99, 0x40, 0x0d, 0x39, 0x89, 0x4e, 0x72, 0x22, 0xfb, 0x20, 0x90, 0xe7, 0xa2,
343 | 0xb2, 0x4f, 0x44, 0x42, 0x1d, 0x79, 0x2e, 0x2a, 0x65, 0x2e, 0x2a, 0x15, 0x34, 0x90, 0xff, 0x2c,
344 | 0x2a, 0x55, 0xe7, 0x83, 0x8b, 0xca, 0xff, 0xe9, 0xfe, 0xd1, 0xe9, 0xda, 0x37, 0xa6, 0xe6, 0xc1,
345 | 0x19, 0x32, 0xa7, 0xae, 0xda, 0xa7, 0x1a, 0xfd, 0xd3, 0xa6, 0xec, 0x51, 0xd9, 0x87, 0x26, 0xf2,
346 | 0x5f, 0xcb, 0xfe, 0xf5, 0xa5, 0x28, 0xed, 0x3f, 0x65, 0xd7, 0x84, 0xb5, 0x7f, 0xdf, 0xb6, 0x0a,
347 | 0x26, 0x4e, 0x5b, 0xcc, 0xc4, 0xbb, 0x56, 0x51, 0x29, 0x61, 0x3d, 0x44, 0x49, 0xa2, 0xed, 0x2b,
348 | 0x13, 0x9a, 0xa7, 0xb5, 0xa7, 0x9d, 0xe7, 0xdf, 0xe6, 0x4e, 0x61, 0xea, 0xbf, 0xa8, 0x78, 0xb9,
349 | 0x5d, 0xec, 0x82, 0x5e, 0xa8, 0x53, 0x37, 0x5d, 0x86, 0x99, 0xee, 0x6e, 0xde, 0x43, 0x37, 0xd6,
350 | 0xdd, 0x20, 0x5a, 0x85, 0x8b, 0x74, 0x96, 0xbd, 0x1d, 0x6f, 0xab, 0x1b, 0x67, 0xeb, 0xf0, 0x18,
351 | 0x83, 0xf2, 0xe1, 0xe6, 0x7a, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x24, 0x04, 0x6d, 0xce,
352 | 0x03, 0x00, 0x00,
353 | }
354 |
--------------------------------------------------------------------------------
/model/grpc/model/model_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 |
3 | package model
4 |
5 | import (
6 | context "context"
7 | grpc "google.golang.org/grpc"
8 | codes "google.golang.org/grpc/codes"
9 | status "google.golang.org/grpc/status"
10 | )
11 |
12 | // This is a compile-time assertion to ensure that this generated file
13 | // is compatible with the grpc package it is being compiled against.
14 | const _ = grpc.SupportPackageIsVersion7
15 |
16 | // HelloClient is the client API for Hello service.
17 | //
18 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
19 | type HelloClient interface {
20 | Hello(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error)
21 | }
22 |
23 | type helloClient struct {
24 | cc grpc.ClientConnInterface
25 | }
26 |
27 | func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
28 | return &helloClient{cc}
29 | }
30 |
31 | func (c *helloClient) Hello(ctx context.Context, in *Message, opts ...grpc.CallOption) (*Message, error) {
32 | out := new(Message)
33 | err := c.cc.Invoke(ctx, "/model.Hello/Hello", in, out, opts...)
34 | if err != nil {
35 | return nil, err
36 | }
37 | return out, nil
38 | }
39 |
40 | // HelloServer is the server API for Hello service.
41 | // All implementations must embed UnimplementedHelloServer
42 | // for forward compatibility
43 | type HelloServer interface {
44 | Hello(context.Context, *Message) (*Message, error)
45 | mustEmbedUnimplementedHelloServer()
46 | }
47 |
48 | // UnimplementedHelloServer must be embedded to have forward compatible implementations.
49 | type UnimplementedHelloServer struct {
50 | }
51 |
52 | func (UnimplementedHelloServer) Hello(context.Context, *Message) (*Message, error) {
53 | return nil, status.Errorf(codes.Unimplemented, "method Hello not implemented")
54 | }
55 | func (UnimplementedHelloServer) mustEmbedUnimplementedHelloServer() {}
56 |
57 | // UnsafeHelloServer may be embedded to opt out of forward compatibility for this service.
58 | // Use of this interface is not recommended, as added methods to HelloServer will
59 | // result in compilation errors.
60 | type UnsafeHelloServer interface {
61 | mustEmbedUnimplementedHelloServer()
62 | }
63 |
64 | func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
65 | s.RegisterService(&_Hello_serviceDesc, srv)
66 | }
67 |
68 | func _Hello_Hello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
69 | in := new(Message)
70 | if err := dec(in); err != nil {
71 | return nil, err
72 | }
73 | if interceptor == nil {
74 | return srv.(HelloServer).Hello(ctx, in)
75 | }
76 | info := &grpc.UnaryServerInfo{
77 | Server: srv,
78 | FullMethod: "/model.Hello/Hello",
79 | }
80 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
81 | return srv.(HelloServer).Hello(ctx, req.(*Message))
82 | }
83 | return interceptor(ctx, in, info, handler)
84 | }
85 |
86 | var _Hello_serviceDesc = grpc.ServiceDesc{
87 | ServiceName: "model.Hello",
88 | HandlerType: (*HelloServer)(nil),
89 | Methods: []grpc.MethodDesc{
90 | {
91 | MethodName: "Hello",
92 | Handler: _Hello_Hello_Handler,
93 | },
94 | },
95 | Streams: []grpc.StreamDesc{},
96 | Metadata: "model/model.proto",
97 | }
98 |
--------------------------------------------------------------------------------
/model/micro-v2/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | Example = example()
5 | )
6 |
7 | func example() *Message {
8 | sub1 := SubMessage{
9 | Field01: 123456.789,
10 | Field02: 1234567890,
11 | Field03: true,
12 | Field04: "string example",
13 | Field05: []byte("bytes example"),
14 | Field06: Enum_EnumC,
15 | Field07: []float64{123456.789, 223456.789, 323456.789},
16 | Field08: []int64{1234567890, 2234567890, 3234567890},
17 | Field09: []bool{true, false, true},
18 | Field10: []string{"string example 1", "string example 2", "string example 3"},
19 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
20 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
21 | }
22 |
23 | msg := &Message{
24 | Field01: 123456.789,
25 | Field02: 1234567890,
26 | Field03: true,
27 | Field04: "string example",
28 | Field05: []byte("bytes example"),
29 | Field06: Enum_EnumC,
30 | Field07: []float64{123456.789, 223456.789, 323456.789},
31 | Field08: []int64{1234567890, 2234567890, 3234567890},
32 | Field09: []bool{true, false, true},
33 | Field10: []string{"string example 1", "string example 2", "string example 3"},
34 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
35 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
36 | Field13: &sub1,
37 | }
38 |
39 | return msg
40 | }
41 |
42 | func CheckExample(msg *Message) bool {
43 | return msg.Field01 > 0 &&
44 | msg.Field01 == Example.Field01 &&
45 | msg.Field02 == Example.Field02 &&
46 | msg.Field03 == Example.Field03 &&
47 | msg.Field04 == Example.Field04
48 | }
49 |
--------------------------------------------------------------------------------
/model/micro-v2/model/model.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | package model
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 |
23 | type Enum int32
24 |
25 | const (
26 | Enum_EnumA Enum = 0
27 | Enum_EnumB Enum = 1
28 | Enum_EnumC Enum = 2
29 | )
30 |
31 | var Enum_name = map[int32]string{
32 | 0: "EnumA",
33 | 1: "EnumB",
34 | 2: "EnumC",
35 | }
36 |
37 | var Enum_value = map[string]int32{
38 | "EnumA": 0,
39 | "EnumB": 1,
40 | "EnumC": 2,
41 | }
42 |
43 | func (x Enum) String() string {
44 | return proto.EnumName(Enum_name, int32(x))
45 | }
46 |
47 | func (Enum) EnumDescriptor() ([]byte, []int) {
48 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
49 | }
50 |
51 | type SubMessage struct {
52 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
53 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
54 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
55 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
56 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
57 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
58 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
59 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
60 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
61 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
62 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
63 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
64 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
65 | XXX_unrecognized []byte `json:"-"`
66 | XXX_sizecache int32 `json:"-"`
67 | }
68 |
69 | func (m *SubMessage) Reset() { *m = SubMessage{} }
70 | func (m *SubMessage) String() string { return proto.CompactTextString(m) }
71 | func (*SubMessage) ProtoMessage() {}
72 | func (*SubMessage) Descriptor() ([]byte, []int) {
73 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
74 | }
75 |
76 | func (m *SubMessage) XXX_Unmarshal(b []byte) error {
77 | return xxx_messageInfo_SubMessage.Unmarshal(m, b)
78 | }
79 | func (m *SubMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
80 | return xxx_messageInfo_SubMessage.Marshal(b, m, deterministic)
81 | }
82 | func (m *SubMessage) XXX_Merge(src proto.Message) {
83 | xxx_messageInfo_SubMessage.Merge(m, src)
84 | }
85 | func (m *SubMessage) XXX_Size() int {
86 | return xxx_messageInfo_SubMessage.Size(m)
87 | }
88 | func (m *SubMessage) XXX_DiscardUnknown() {
89 | xxx_messageInfo_SubMessage.DiscardUnknown(m)
90 | }
91 |
92 | var xxx_messageInfo_SubMessage proto.InternalMessageInfo
93 |
94 | func (m *SubMessage) GetField01() float64 {
95 | if m != nil {
96 | return m.Field01
97 | }
98 | return 0
99 | }
100 |
101 | func (m *SubMessage) GetField02() int64 {
102 | if m != nil {
103 | return m.Field02
104 | }
105 | return 0
106 | }
107 |
108 | func (m *SubMessage) GetField03() bool {
109 | if m != nil {
110 | return m.Field03
111 | }
112 | return false
113 | }
114 |
115 | func (m *SubMessage) GetField04() string {
116 | if m != nil {
117 | return m.Field04
118 | }
119 | return ""
120 | }
121 |
122 | func (m *SubMessage) GetField05() []byte {
123 | if m != nil {
124 | return m.Field05
125 | }
126 | return nil
127 | }
128 |
129 | func (m *SubMessage) GetField06() Enum {
130 | if m != nil {
131 | return m.Field06
132 | }
133 | return Enum_EnumA
134 | }
135 |
136 | func (m *SubMessage) GetField07() []float64 {
137 | if m != nil {
138 | return m.Field07
139 | }
140 | return nil
141 | }
142 |
143 | func (m *SubMessage) GetField08() []int64 {
144 | if m != nil {
145 | return m.Field08
146 | }
147 | return nil
148 | }
149 |
150 | func (m *SubMessage) GetField09() []bool {
151 | if m != nil {
152 | return m.Field09
153 | }
154 | return nil
155 | }
156 |
157 | func (m *SubMessage) GetField10() []string {
158 | if m != nil {
159 | return m.Field10
160 | }
161 | return nil
162 | }
163 |
164 | func (m *SubMessage) GetField11() [][]byte {
165 | if m != nil {
166 | return m.Field11
167 | }
168 | return nil
169 | }
170 |
171 | func (m *SubMessage) GetField12() []Enum {
172 | if m != nil {
173 | return m.Field12
174 | }
175 | return nil
176 | }
177 |
178 | type Message struct {
179 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
180 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
181 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
182 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
183 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
184 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
185 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
186 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
187 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
188 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
189 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
190 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
191 | Field13 *SubMessage `protobuf:"bytes,13,opt,name=field13,proto3" json:"field13,omitempty"`
192 | Field14 []*SubMessage `protobuf:"bytes,14,rep,name=field14,proto3" json:"field14,omitempty"`
193 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
194 | XXX_unrecognized []byte `json:"-"`
195 | XXX_sizecache int32 `json:"-"`
196 | }
197 |
198 | func (m *Message) Reset() { *m = Message{} }
199 | func (m *Message) String() string { return proto.CompactTextString(m) }
200 | func (*Message) ProtoMessage() {}
201 | func (*Message) Descriptor() ([]byte, []int) {
202 | return fileDescriptor_312ac5bcab6cbb43, []int{1}
203 | }
204 |
205 | func (m *Message) XXX_Unmarshal(b []byte) error {
206 | return xxx_messageInfo_Message.Unmarshal(m, b)
207 | }
208 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
209 | return xxx_messageInfo_Message.Marshal(b, m, deterministic)
210 | }
211 | func (m *Message) XXX_Merge(src proto.Message) {
212 | xxx_messageInfo_Message.Merge(m, src)
213 | }
214 | func (m *Message) XXX_Size() int {
215 | return xxx_messageInfo_Message.Size(m)
216 | }
217 | func (m *Message) XXX_DiscardUnknown() {
218 | xxx_messageInfo_Message.DiscardUnknown(m)
219 | }
220 |
221 | var xxx_messageInfo_Message proto.InternalMessageInfo
222 |
223 | func (m *Message) GetField01() float64 {
224 | if m != nil {
225 | return m.Field01
226 | }
227 | return 0
228 | }
229 |
230 | func (m *Message) GetField02() int64 {
231 | if m != nil {
232 | return m.Field02
233 | }
234 | return 0
235 | }
236 |
237 | func (m *Message) GetField03() bool {
238 | if m != nil {
239 | return m.Field03
240 | }
241 | return false
242 | }
243 |
244 | func (m *Message) GetField04() string {
245 | if m != nil {
246 | return m.Field04
247 | }
248 | return ""
249 | }
250 |
251 | func (m *Message) GetField05() []byte {
252 | if m != nil {
253 | return m.Field05
254 | }
255 | return nil
256 | }
257 |
258 | func (m *Message) GetField06() Enum {
259 | if m != nil {
260 | return m.Field06
261 | }
262 | return Enum_EnumA
263 | }
264 |
265 | func (m *Message) GetField07() []float64 {
266 | if m != nil {
267 | return m.Field07
268 | }
269 | return nil
270 | }
271 |
272 | func (m *Message) GetField08() []int64 {
273 | if m != nil {
274 | return m.Field08
275 | }
276 | return nil
277 | }
278 |
279 | func (m *Message) GetField09() []bool {
280 | if m != nil {
281 | return m.Field09
282 | }
283 | return nil
284 | }
285 |
286 | func (m *Message) GetField10() []string {
287 | if m != nil {
288 | return m.Field10
289 | }
290 | return nil
291 | }
292 |
293 | func (m *Message) GetField11() [][]byte {
294 | if m != nil {
295 | return m.Field11
296 | }
297 | return nil
298 | }
299 |
300 | func (m *Message) GetField12() []Enum {
301 | if m != nil {
302 | return m.Field12
303 | }
304 | return nil
305 | }
306 |
307 | func (m *Message) GetField13() *SubMessage {
308 | if m != nil {
309 | return m.Field13
310 | }
311 | return nil
312 | }
313 |
314 | func (m *Message) GetField14() []*SubMessage {
315 | if m != nil {
316 | return m.Field14
317 | }
318 | return nil
319 | }
320 |
321 | func init() {
322 | proto.RegisterEnum("model.Enum", Enum_name, Enum_value)
323 | proto.RegisterType((*SubMessage)(nil), "model.SubMessage")
324 | proto.RegisterType((*Message)(nil), "model.Message")
325 | }
326 |
327 | func init() { proto.RegisterFile("model/model.proto", fileDescriptor_312ac5bcab6cbb43) }
328 |
329 | var fileDescriptor_312ac5bcab6cbb43 = []byte{
330 | // 339 bytes of a gzipped FileDescriptorProto
331 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x93, 0xcb, 0x4e, 0xc2, 0x40,
332 | 0x14, 0x86, 0x19, 0x86, 0x72, 0x19, 0x90, 0x40, 0x57, 0x27, 0xae, 0x4e, 0x48, 0x8c, 0x55, 0x03,
333 | 0x65, 0xa6, 0xe5, 0xb6, 0x14, 0x63, 0xe2, 0xc6, 0x8d, 0xee, 0xdc, 0xd1, 0x52, 0x0b, 0xb1, 0x65,
334 | 0x48, 0x01, 0xdf, 0xcc, 0xd7, 0xf1, 0x59, 0x0c, 0x97, 0xe9, 0x49, 0x8c, 0x71, 0xe7, 0xce, 0x4d,
335 | 0xfb, 0x9f, 0x7c, 0x7f, 0x4e, 0xbf, 0x4c, 0x3a, 0xa2, 0x9d, 0xea, 0x79, 0x94, 0xb8, 0x87, 0x67,
336 | 0x6f, 0x9d, 0xe9, 0xad, 0xb6, 0xad, 0xc3, 0xd0, 0xf9, 0x2c, 0x0a, 0xf1, 0xbc, 0x0b, 0x1e, 0xa3,
337 | 0xcd, 0x66, 0x16, 0x47, 0x36, 0x88, 0xca, 0xeb, 0x32, 0x4a, 0xe6, 0x7d, 0x09, 0x0c, 0x99, 0xc3,
338 | 0x9e, 0xcc, 0x48, 0x44, 0x41, 0x11, 0x99, 0xc3, 0x0d, 0x51, 0x44, 0x3c, 0xe0, 0xc8, 0x9c, 0xaa,
339 | 0x21, 0x1e, 0x11, 0x1f, 0x4a, 0xc8, 0x9c, 0x9a, 0x21, 0x3e, 0x91, 0x01, 0x58, 0xc8, 0x9c, 0x86,
340 | 0x21, 0x03, 0xfb, 0xc2, 0x90, 0x21, 0x94, 0x91, 0x39, 0x4d, 0x55, 0xef, 0x1d, 0xb5, 0xef, 0x57,
341 | 0xbb, 0xd4, 0xd4, 0x86, 0xb4, 0x60, 0x04, 0x15, 0xe4, 0x24, 0x3a, 0x22, 0x32, 0x86, 0x2a, 0x72,
342 | 0x12, 0x1d, 0x13, 0x99, 0x40, 0x0d, 0x39, 0x89, 0x4e, 0x72, 0x22, 0xfb, 0x20, 0x90, 0xe7, 0xa2,
343 | 0xb2, 0x4f, 0x44, 0x42, 0x1d, 0x79, 0x2e, 0x2a, 0x65, 0x2e, 0x2a, 0x15, 0x34, 0x90, 0xff, 0x2c,
344 | 0x2a, 0x55, 0xe7, 0x83, 0x8b, 0xca, 0xff, 0xe9, 0xfe, 0xd1, 0xe9, 0xda, 0x37, 0xa6, 0xe6, 0xc1,
345 | 0x19, 0x32, 0xa7, 0xae, 0xda, 0xa7, 0x1a, 0xfd, 0xd3, 0xa6, 0xec, 0x51, 0xd9, 0x87, 0x26, 0xf2,
346 | 0x5f, 0xcb, 0xfe, 0xf5, 0xa5, 0x28, 0xed, 0x3f, 0x65, 0xd7, 0x84, 0xb5, 0x7f, 0xdf, 0xb6, 0x0a,
347 | 0x26, 0x4e, 0x5b, 0xcc, 0xc4, 0xbb, 0x56, 0x51, 0x29, 0x61, 0x3d, 0x44, 0x49, 0xa2, 0xed, 0x2b,
348 | 0x13, 0x9a, 0xa7, 0xb5, 0xa7, 0x9d, 0xe7, 0xdf, 0xe6, 0x4e, 0x61, 0xea, 0xbf, 0xa8, 0x78, 0xb9,
349 | 0x5d, 0xec, 0x82, 0x5e, 0xa8, 0x53, 0x37, 0x5d, 0x86, 0x99, 0xee, 0x6e, 0xde, 0x43, 0x37, 0xd6,
350 | 0xdd, 0x20, 0x5a, 0x85, 0x8b, 0x74, 0x96, 0xbd, 0x1d, 0x6f, 0xab, 0x1b, 0x67, 0xeb, 0xf0, 0x18,
351 | 0x83, 0xf2, 0xe1, 0xe6, 0x7a, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x24, 0x04, 0x6d, 0xce,
352 | 0x03, 0x00, 0x00,
353 | }
354 |
--------------------------------------------------------------------------------
/model/micro-v2/model/model.pb.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | package model
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | api "github.com/micro/go-micro/v2/api"
15 | client "github.com/micro/go-micro/v2/client"
16 | server "github.com/micro/go-micro/v2/server"
17 | )
18 |
19 | // Reference imports to suppress errors if they are not otherwise used.
20 | var _ = proto.Marshal
21 | var _ = fmt.Errorf
22 | var _ = math.Inf
23 |
24 | // This is a compile-time assertion to ensure that this generated file
25 | // is compatible with the proto package it is being compiled against.
26 | // A compilation error at this line likely means your copy of the
27 | // proto package needs to be updated.
28 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
29 |
30 | // Reference imports to suppress errors if they are not otherwise used.
31 | var _ api.Endpoint
32 | var _ context.Context
33 | var _ client.Option
34 | var _ server.Option
35 |
36 | // Api Endpoints for Hello service
37 |
38 | func NewHelloEndpoints() []*api.Endpoint {
39 | return []*api.Endpoint{}
40 | }
41 |
42 | // Client API for Hello service
43 |
44 | type HelloService interface {
45 | Hello(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error)
46 | }
47 |
48 | type helloService struct {
49 | c client.Client
50 | name string
51 | }
52 |
53 | func NewHelloService(name string, c client.Client) HelloService {
54 | return &helloService{
55 | c: c,
56 | name: name,
57 | }
58 | }
59 |
60 | func (c *helloService) Hello(ctx context.Context, in *Message, opts ...client.CallOption) (*Message, error) {
61 | req := c.c.NewRequest(c.name, "Hello.Hello", in)
62 | out := new(Message)
63 | err := c.c.Call(ctx, req, out, opts...)
64 | if err != nil {
65 | return nil, err
66 | }
67 | return out, nil
68 | }
69 |
70 | // Server API for Hello service
71 |
72 | type HelloHandler interface {
73 | Hello(context.Context, *Message, *Message) error
74 | }
75 |
76 | func RegisterHelloHandler(s server.Server, hdlr HelloHandler, opts ...server.HandlerOption) error {
77 | type hello interface {
78 | Hello(ctx context.Context, in *Message, out *Message) error
79 | }
80 | type Hello struct {
81 | hello
82 | }
83 | h := &helloHandler{hdlr}
84 | return s.Handle(s.NewHandler(&Hello{h}, opts...))
85 | }
86 |
87 | type helloHandler struct {
88 | HelloHandler
89 | }
90 |
91 | func (h *helloHandler) Hello(ctx context.Context, in *Message, out *Message) error {
92 | return h.HelloHandler.Hello(ctx, in, out)
93 | }
94 |
--------------------------------------------------------------------------------
/model/model.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 |
3 | package model;
4 | option go_package = "github.com/micro-svc/go-rpc-framework-benchmark/model/grpc/model";
5 |
6 | enum Enum {
7 | EnumA = 0;
8 | EnumB = 1;
9 | EnumC = 2;
10 | }
11 |
12 | message SubMessage {
13 | double field01 = 1;
14 | int64 field02 = 2;
15 | bool field03 = 3;
16 | string field04 = 4;
17 | bytes field05 = 5;
18 | Enum field06 = 6;
19 | repeated double field07 = 7;
20 | repeated int64 field08 = 8;
21 | repeated bool field09 = 9;
22 | repeated string field10 = 10;
23 | repeated bytes field11 = 11;
24 | repeated Enum field12 = 12;
25 | }
26 |
27 | message Message {
28 | double field01 = 1;
29 | int64 field02 = 2;
30 | bool field03 = 3;
31 | string field04 = 4;
32 | bytes field05 = 5;
33 | Enum field06 = 6;
34 | repeated double field07 = 7;
35 | repeated int64 field08 = 8;
36 | repeated bool field09 = 9;
37 | repeated string field10 = 10;
38 | repeated bytes field11 = 11;
39 | repeated Enum field12 = 12;
40 | SubMessage field13 = 13;
41 | repeated SubMessage field14 = 14;
42 | }
43 |
44 | service Hello {
45 | rpc Hello(Message) returns (Message) {};
46 | }
47 |
--------------------------------------------------------------------------------
/model/standard/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | Example = example()
5 | )
6 |
7 | func example() *Message {
8 | sub1 := SubMessage{
9 | Field01: 123456.789,
10 | Field02: 1234567890,
11 | Field03: true,
12 | Field04: "string example",
13 | Field05: []byte("bytes example"),
14 | Field06: Enum_EnumC,
15 | Field07: []float64{123456.789, 223456.789, 323456.789},
16 | Field08: []int64{1234567890, 2234567890, 3234567890},
17 | Field09: []bool{true, false, true},
18 | Field10: []string{"string example 1", "string example 2", "string example 3"},
19 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
20 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
21 | }
22 |
23 | msg := &Message{
24 | Field01: 123456.789,
25 | Field02: 1234567890,
26 | Field03: true,
27 | Field04: "string example",
28 | Field05: []byte("bytes example"),
29 | Field06: Enum_EnumC,
30 | Field07: []float64{123456.789, 223456.789, 323456.789},
31 | Field08: []int64{1234567890, 2234567890, 3234567890},
32 | Field09: []bool{true, false, true},
33 | Field10: []string{"string example 1", "string example 2", "string example 3"},
34 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
35 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
36 | Field13: &sub1,
37 | }
38 |
39 | return msg
40 | }
41 |
42 | func CheckExample(msg *Message) bool {
43 | return msg.Field01 > 0 &&
44 | msg.Field01 == Example.Field01 &&
45 | msg.Field02 == Example.Field02 &&
46 | msg.Field03 == Example.Field03 &&
47 | msg.Field04 == Example.Field04
48 | }
49 |
--------------------------------------------------------------------------------
/model/standard/model/model.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | package model
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 |
23 | type Enum int32
24 |
25 | const (
26 | Enum_EnumA Enum = 0
27 | Enum_EnumB Enum = 1
28 | Enum_EnumC Enum = 2
29 | )
30 |
31 | var Enum_name = map[int32]string{
32 | 0: "EnumA",
33 | 1: "EnumB",
34 | 2: "EnumC",
35 | }
36 |
37 | var Enum_value = map[string]int32{
38 | "EnumA": 0,
39 | "EnumB": 1,
40 | "EnumC": 2,
41 | }
42 |
43 | func (x Enum) String() string {
44 | return proto.EnumName(Enum_name, int32(x))
45 | }
46 |
47 | func (Enum) EnumDescriptor() ([]byte, []int) {
48 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
49 | }
50 |
51 | type SubMessage struct {
52 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
53 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
54 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
55 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
56 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
57 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
58 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
59 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
60 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
61 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
62 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
63 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
64 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
65 | XXX_unrecognized []byte `json:"-"`
66 | XXX_sizecache int32 `json:"-"`
67 | }
68 |
69 | func (m *SubMessage) Reset() { *m = SubMessage{} }
70 | func (m *SubMessage) String() string { return proto.CompactTextString(m) }
71 | func (*SubMessage) ProtoMessage() {}
72 | func (*SubMessage) Descriptor() ([]byte, []int) {
73 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
74 | }
75 |
76 | func (m *SubMessage) XXX_Unmarshal(b []byte) error {
77 | return xxx_messageInfo_SubMessage.Unmarshal(m, b)
78 | }
79 | func (m *SubMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
80 | return xxx_messageInfo_SubMessage.Marshal(b, m, deterministic)
81 | }
82 | func (m *SubMessage) XXX_Merge(src proto.Message) {
83 | xxx_messageInfo_SubMessage.Merge(m, src)
84 | }
85 | func (m *SubMessage) XXX_Size() int {
86 | return xxx_messageInfo_SubMessage.Size(m)
87 | }
88 | func (m *SubMessage) XXX_DiscardUnknown() {
89 | xxx_messageInfo_SubMessage.DiscardUnknown(m)
90 | }
91 |
92 | var xxx_messageInfo_SubMessage proto.InternalMessageInfo
93 |
94 | func (m *SubMessage) GetField01() float64 {
95 | if m != nil {
96 | return m.Field01
97 | }
98 | return 0
99 | }
100 |
101 | func (m *SubMessage) GetField02() int64 {
102 | if m != nil {
103 | return m.Field02
104 | }
105 | return 0
106 | }
107 |
108 | func (m *SubMessage) GetField03() bool {
109 | if m != nil {
110 | return m.Field03
111 | }
112 | return false
113 | }
114 |
115 | func (m *SubMessage) GetField04() string {
116 | if m != nil {
117 | return m.Field04
118 | }
119 | return ""
120 | }
121 |
122 | func (m *SubMessage) GetField05() []byte {
123 | if m != nil {
124 | return m.Field05
125 | }
126 | return nil
127 | }
128 |
129 | func (m *SubMessage) GetField06() Enum {
130 | if m != nil {
131 | return m.Field06
132 | }
133 | return Enum_EnumA
134 | }
135 |
136 | func (m *SubMessage) GetField07() []float64 {
137 | if m != nil {
138 | return m.Field07
139 | }
140 | return nil
141 | }
142 |
143 | func (m *SubMessage) GetField08() []int64 {
144 | if m != nil {
145 | return m.Field08
146 | }
147 | return nil
148 | }
149 |
150 | func (m *SubMessage) GetField09() []bool {
151 | if m != nil {
152 | return m.Field09
153 | }
154 | return nil
155 | }
156 |
157 | func (m *SubMessage) GetField10() []string {
158 | if m != nil {
159 | return m.Field10
160 | }
161 | return nil
162 | }
163 |
164 | func (m *SubMessage) GetField11() [][]byte {
165 | if m != nil {
166 | return m.Field11
167 | }
168 | return nil
169 | }
170 |
171 | func (m *SubMessage) GetField12() []Enum {
172 | if m != nil {
173 | return m.Field12
174 | }
175 | return nil
176 | }
177 |
178 | type Message struct {
179 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
180 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
181 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
182 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
183 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
184 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
185 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
186 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
187 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
188 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
189 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
190 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
191 | Field13 *SubMessage `protobuf:"bytes,13,opt,name=field13,proto3" json:"field13,omitempty"`
192 | Field14 []*SubMessage `protobuf:"bytes,14,rep,name=field14,proto3" json:"field14,omitempty"`
193 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
194 | XXX_unrecognized []byte `json:"-"`
195 | XXX_sizecache int32 `json:"-"`
196 | }
197 |
198 | func (m *Message) Reset() { *m = Message{} }
199 | func (m *Message) String() string { return proto.CompactTextString(m) }
200 | func (*Message) ProtoMessage() {}
201 | func (*Message) Descriptor() ([]byte, []int) {
202 | return fileDescriptor_312ac5bcab6cbb43, []int{1}
203 | }
204 |
205 | func (m *Message) XXX_Unmarshal(b []byte) error {
206 | return xxx_messageInfo_Message.Unmarshal(m, b)
207 | }
208 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
209 | return xxx_messageInfo_Message.Marshal(b, m, deterministic)
210 | }
211 | func (m *Message) XXX_Merge(src proto.Message) {
212 | xxx_messageInfo_Message.Merge(m, src)
213 | }
214 | func (m *Message) XXX_Size() int {
215 | return xxx_messageInfo_Message.Size(m)
216 | }
217 | func (m *Message) XXX_DiscardUnknown() {
218 | xxx_messageInfo_Message.DiscardUnknown(m)
219 | }
220 |
221 | var xxx_messageInfo_Message proto.InternalMessageInfo
222 |
223 | func (m *Message) GetField01() float64 {
224 | if m != nil {
225 | return m.Field01
226 | }
227 | return 0
228 | }
229 |
230 | func (m *Message) GetField02() int64 {
231 | if m != nil {
232 | return m.Field02
233 | }
234 | return 0
235 | }
236 |
237 | func (m *Message) GetField03() bool {
238 | if m != nil {
239 | return m.Field03
240 | }
241 | return false
242 | }
243 |
244 | func (m *Message) GetField04() string {
245 | if m != nil {
246 | return m.Field04
247 | }
248 | return ""
249 | }
250 |
251 | func (m *Message) GetField05() []byte {
252 | if m != nil {
253 | return m.Field05
254 | }
255 | return nil
256 | }
257 |
258 | func (m *Message) GetField06() Enum {
259 | if m != nil {
260 | return m.Field06
261 | }
262 | return Enum_EnumA
263 | }
264 |
265 | func (m *Message) GetField07() []float64 {
266 | if m != nil {
267 | return m.Field07
268 | }
269 | return nil
270 | }
271 |
272 | func (m *Message) GetField08() []int64 {
273 | if m != nil {
274 | return m.Field08
275 | }
276 | return nil
277 | }
278 |
279 | func (m *Message) GetField09() []bool {
280 | if m != nil {
281 | return m.Field09
282 | }
283 | return nil
284 | }
285 |
286 | func (m *Message) GetField10() []string {
287 | if m != nil {
288 | return m.Field10
289 | }
290 | return nil
291 | }
292 |
293 | func (m *Message) GetField11() [][]byte {
294 | if m != nil {
295 | return m.Field11
296 | }
297 | return nil
298 | }
299 |
300 | func (m *Message) GetField12() []Enum {
301 | if m != nil {
302 | return m.Field12
303 | }
304 | return nil
305 | }
306 |
307 | func (m *Message) GetField13() *SubMessage {
308 | if m != nil {
309 | return m.Field13
310 | }
311 | return nil
312 | }
313 |
314 | func (m *Message) GetField14() []*SubMessage {
315 | if m != nil {
316 | return m.Field14
317 | }
318 | return nil
319 | }
320 |
321 | func init() {
322 | proto.RegisterEnum("model.Enum", Enum_name, Enum_value)
323 | proto.RegisterType((*SubMessage)(nil), "model.SubMessage")
324 | proto.RegisterType((*Message)(nil), "model.Message")
325 | }
326 |
327 | func init() { proto.RegisterFile("model/model.proto", fileDescriptor_312ac5bcab6cbb43) }
328 |
329 | var fileDescriptor_312ac5bcab6cbb43 = []byte{
330 | // 339 bytes of a gzipped FileDescriptorProto
331 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x93, 0xcb, 0x4e, 0xc2, 0x40,
332 | 0x14, 0x86, 0x19, 0x86, 0x72, 0x19, 0x90, 0x40, 0x57, 0x27, 0xae, 0x4e, 0x48, 0x8c, 0x55, 0x03,
333 | 0x65, 0xa6, 0xe5, 0xb6, 0x14, 0x63, 0xe2, 0xc6, 0x8d, 0xee, 0xdc, 0xd1, 0x52, 0x0b, 0xb1, 0x65,
334 | 0x48, 0x01, 0xdf, 0xcc, 0xd7, 0xf1, 0x59, 0x0c, 0x97, 0xe9, 0x49, 0x8c, 0x71, 0xe7, 0xce, 0x4d,
335 | 0xfb, 0x9f, 0x7c, 0x7f, 0x4e, 0xbf, 0x4c, 0x3a, 0xa2, 0x9d, 0xea, 0x79, 0x94, 0xb8, 0x87, 0x67,
336 | 0x6f, 0x9d, 0xe9, 0xad, 0xb6, 0xad, 0xc3, 0xd0, 0xf9, 0x2c, 0x0a, 0xf1, 0xbc, 0x0b, 0x1e, 0xa3,
337 | 0xcd, 0x66, 0x16, 0x47, 0x36, 0x88, 0xca, 0xeb, 0x32, 0x4a, 0xe6, 0x7d, 0x09, 0x0c, 0x99, 0xc3,
338 | 0x9e, 0xcc, 0x48, 0x44, 0x41, 0x11, 0x99, 0xc3, 0x0d, 0x51, 0x44, 0x3c, 0xe0, 0xc8, 0x9c, 0xaa,
339 | 0x21, 0x1e, 0x11, 0x1f, 0x4a, 0xc8, 0x9c, 0x9a, 0x21, 0x3e, 0x91, 0x01, 0x58, 0xc8, 0x9c, 0x86,
340 | 0x21, 0x03, 0xfb, 0xc2, 0x90, 0x21, 0x94, 0x91, 0x39, 0x4d, 0x55, 0xef, 0x1d, 0xb5, 0xef, 0x57,
341 | 0xbb, 0xd4, 0xd4, 0x86, 0xb4, 0x60, 0x04, 0x15, 0xe4, 0x24, 0x3a, 0x22, 0x32, 0x86, 0x2a, 0x72,
342 | 0x12, 0x1d, 0x13, 0x99, 0x40, 0x0d, 0x39, 0x89, 0x4e, 0x72, 0x22, 0xfb, 0x20, 0x90, 0xe7, 0xa2,
343 | 0xb2, 0x4f, 0x44, 0x42, 0x1d, 0x79, 0x2e, 0x2a, 0x65, 0x2e, 0x2a, 0x15, 0x34, 0x90, 0xff, 0x2c,
344 | 0x2a, 0x55, 0xe7, 0x83, 0x8b, 0xca, 0xff, 0xe9, 0xfe, 0xd1, 0xe9, 0xda, 0x37, 0xa6, 0xe6, 0xc1,
345 | 0x19, 0x32, 0xa7, 0xae, 0xda, 0xa7, 0x1a, 0xfd, 0xd3, 0xa6, 0xec, 0x51, 0xd9, 0x87, 0x26, 0xf2,
346 | 0x5f, 0xcb, 0xfe, 0xf5, 0xa5, 0x28, 0xed, 0x3f, 0x65, 0xd7, 0x84, 0xb5, 0x7f, 0xdf, 0xb6, 0x0a,
347 | 0x26, 0x4e, 0x5b, 0xcc, 0xc4, 0xbb, 0x56, 0x51, 0x29, 0x61, 0x3d, 0x44, 0x49, 0xa2, 0xed, 0x2b,
348 | 0x13, 0x9a, 0xa7, 0xb5, 0xa7, 0x9d, 0xe7, 0xdf, 0xe6, 0x4e, 0x61, 0xea, 0xbf, 0xa8, 0x78, 0xb9,
349 | 0x5d, 0xec, 0x82, 0x5e, 0xa8, 0x53, 0x37, 0x5d, 0x86, 0x99, 0xee, 0x6e, 0xde, 0x43, 0x37, 0xd6,
350 | 0xdd, 0x20, 0x5a, 0x85, 0x8b, 0x74, 0x96, 0xbd, 0x1d, 0x6f, 0xab, 0x1b, 0x67, 0xeb, 0xf0, 0x18,
351 | 0x83, 0xf2, 0xe1, 0xe6, 0x7a, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x24, 0x04, 0x6d, 0xce,
352 | 0x03, 0x00, 0x00,
353 | }
354 |
--------------------------------------------------------------------------------
/model/twirp-v7/model/model.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | Example = example()
5 | )
6 |
7 | func example() *Message {
8 | sub1 := SubMessage{
9 | Field01: 123456.789,
10 | Field02: 1234567890,
11 | Field03: true,
12 | Field04: "string example",
13 | Field05: []byte("bytes example"),
14 | Field06: Enum_EnumC,
15 | Field07: []float64{123456.789, 223456.789, 323456.789},
16 | Field08: []int64{1234567890, 2234567890, 3234567890},
17 | Field09: []bool{true, false, true},
18 | Field10: []string{"string example 1", "string example 2", "string example 3"},
19 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
20 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
21 | }
22 |
23 | msg := &Message{
24 | Field01: 123456.789,
25 | Field02: 1234567890,
26 | Field03: true,
27 | Field04: "string example",
28 | Field05: []byte("bytes example"),
29 | Field06: Enum_EnumC,
30 | Field07: []float64{123456.789, 223456.789, 323456.789},
31 | Field08: []int64{1234567890, 2234567890, 3234567890},
32 | Field09: []bool{true, false, true},
33 | Field10: []string{"string example 1", "string example 2", "string example 3"},
34 | Field11: [][]byte{[]byte("bytes example 1"), []byte("bytes example 2"), []byte("bytes example 3")},
35 | Field12: []Enum{Enum_EnumA, Enum_EnumB, Enum_EnumC},
36 | Field13: &sub1,
37 | }
38 |
39 | return msg
40 | }
41 |
42 | func CheckExample(msg *Message) bool {
43 | return msg.Field01 > 0 &&
44 | msg.Field01 == Example.Field01 &&
45 | msg.Field02 == Example.Field02 &&
46 | msg.Field03 == Example.Field03 &&
47 | msg.Field04 == Example.Field04
48 | }
49 |
--------------------------------------------------------------------------------
/model/twirp-v7/model/model.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | package model
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | // Reference imports to suppress errors if they are not otherwise used.
13 | var _ = proto.Marshal
14 | var _ = fmt.Errorf
15 | var _ = math.Inf
16 |
17 | // This is a compile-time assertion to ensure that this generated file
18 | // is compatible with the proto package it is being compiled against.
19 | // A compilation error at this line likely means your copy of the
20 | // proto package needs to be updated.
21 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
22 |
23 | type Enum int32
24 |
25 | const (
26 | Enum_EnumA Enum = 0
27 | Enum_EnumB Enum = 1
28 | Enum_EnumC Enum = 2
29 | )
30 |
31 | var Enum_name = map[int32]string{
32 | 0: "EnumA",
33 | 1: "EnumB",
34 | 2: "EnumC",
35 | }
36 |
37 | var Enum_value = map[string]int32{
38 | "EnumA": 0,
39 | "EnumB": 1,
40 | "EnumC": 2,
41 | }
42 |
43 | func (x Enum) String() string {
44 | return proto.EnumName(Enum_name, int32(x))
45 | }
46 |
47 | func (Enum) EnumDescriptor() ([]byte, []int) {
48 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
49 | }
50 |
51 | type SubMessage struct {
52 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
53 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
54 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
55 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
56 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
57 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
58 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
59 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
60 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
61 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
62 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
63 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
64 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
65 | XXX_unrecognized []byte `json:"-"`
66 | XXX_sizecache int32 `json:"-"`
67 | }
68 |
69 | func (m *SubMessage) Reset() { *m = SubMessage{} }
70 | func (m *SubMessage) String() string { return proto.CompactTextString(m) }
71 | func (*SubMessage) ProtoMessage() {}
72 | func (*SubMessage) Descriptor() ([]byte, []int) {
73 | return fileDescriptor_312ac5bcab6cbb43, []int{0}
74 | }
75 |
76 | func (m *SubMessage) XXX_Unmarshal(b []byte) error {
77 | return xxx_messageInfo_SubMessage.Unmarshal(m, b)
78 | }
79 | func (m *SubMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
80 | return xxx_messageInfo_SubMessage.Marshal(b, m, deterministic)
81 | }
82 | func (m *SubMessage) XXX_Merge(src proto.Message) {
83 | xxx_messageInfo_SubMessage.Merge(m, src)
84 | }
85 | func (m *SubMessage) XXX_Size() int {
86 | return xxx_messageInfo_SubMessage.Size(m)
87 | }
88 | func (m *SubMessage) XXX_DiscardUnknown() {
89 | xxx_messageInfo_SubMessage.DiscardUnknown(m)
90 | }
91 |
92 | var xxx_messageInfo_SubMessage proto.InternalMessageInfo
93 |
94 | func (m *SubMessage) GetField01() float64 {
95 | if m != nil {
96 | return m.Field01
97 | }
98 | return 0
99 | }
100 |
101 | func (m *SubMessage) GetField02() int64 {
102 | if m != nil {
103 | return m.Field02
104 | }
105 | return 0
106 | }
107 |
108 | func (m *SubMessage) GetField03() bool {
109 | if m != nil {
110 | return m.Field03
111 | }
112 | return false
113 | }
114 |
115 | func (m *SubMessage) GetField04() string {
116 | if m != nil {
117 | return m.Field04
118 | }
119 | return ""
120 | }
121 |
122 | func (m *SubMessage) GetField05() []byte {
123 | if m != nil {
124 | return m.Field05
125 | }
126 | return nil
127 | }
128 |
129 | func (m *SubMessage) GetField06() Enum {
130 | if m != nil {
131 | return m.Field06
132 | }
133 | return Enum_EnumA
134 | }
135 |
136 | func (m *SubMessage) GetField07() []float64 {
137 | if m != nil {
138 | return m.Field07
139 | }
140 | return nil
141 | }
142 |
143 | func (m *SubMessage) GetField08() []int64 {
144 | if m != nil {
145 | return m.Field08
146 | }
147 | return nil
148 | }
149 |
150 | func (m *SubMessage) GetField09() []bool {
151 | if m != nil {
152 | return m.Field09
153 | }
154 | return nil
155 | }
156 |
157 | func (m *SubMessage) GetField10() []string {
158 | if m != nil {
159 | return m.Field10
160 | }
161 | return nil
162 | }
163 |
164 | func (m *SubMessage) GetField11() [][]byte {
165 | if m != nil {
166 | return m.Field11
167 | }
168 | return nil
169 | }
170 |
171 | func (m *SubMessage) GetField12() []Enum {
172 | if m != nil {
173 | return m.Field12
174 | }
175 | return nil
176 | }
177 |
178 | type Message struct {
179 | Field01 float64 `protobuf:"fixed64,1,opt,name=field01,proto3" json:"field01,omitempty"`
180 | Field02 int64 `protobuf:"varint,2,opt,name=field02,proto3" json:"field02,omitempty"`
181 | Field03 bool `protobuf:"varint,3,opt,name=field03,proto3" json:"field03,omitempty"`
182 | Field04 string `protobuf:"bytes,4,opt,name=field04,proto3" json:"field04,omitempty"`
183 | Field05 []byte `protobuf:"bytes,5,opt,name=field05,proto3" json:"field05,omitempty"`
184 | Field06 Enum `protobuf:"varint,6,opt,name=field06,proto3,enum=model.Enum" json:"field06,omitempty"`
185 | Field07 []float64 `protobuf:"fixed64,7,rep,packed,name=field07,proto3" json:"field07,omitempty"`
186 | Field08 []int64 `protobuf:"varint,8,rep,packed,name=field08,proto3" json:"field08,omitempty"`
187 | Field09 []bool `protobuf:"varint,9,rep,packed,name=field09,proto3" json:"field09,omitempty"`
188 | Field10 []string `protobuf:"bytes,10,rep,name=field10,proto3" json:"field10,omitempty"`
189 | Field11 [][]byte `protobuf:"bytes,11,rep,name=field11,proto3" json:"field11,omitempty"`
190 | Field12 []Enum `protobuf:"varint,12,rep,packed,name=field12,proto3,enum=model.Enum" json:"field12,omitempty"`
191 | Field13 *SubMessage `protobuf:"bytes,13,opt,name=field13,proto3" json:"field13,omitempty"`
192 | Field14 []*SubMessage `protobuf:"bytes,14,rep,name=field14,proto3" json:"field14,omitempty"`
193 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
194 | XXX_unrecognized []byte `json:"-"`
195 | XXX_sizecache int32 `json:"-"`
196 | }
197 |
198 | func (m *Message) Reset() { *m = Message{} }
199 | func (m *Message) String() string { return proto.CompactTextString(m) }
200 | func (*Message) ProtoMessage() {}
201 | func (*Message) Descriptor() ([]byte, []int) {
202 | return fileDescriptor_312ac5bcab6cbb43, []int{1}
203 | }
204 |
205 | func (m *Message) XXX_Unmarshal(b []byte) error {
206 | return xxx_messageInfo_Message.Unmarshal(m, b)
207 | }
208 | func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
209 | return xxx_messageInfo_Message.Marshal(b, m, deterministic)
210 | }
211 | func (m *Message) XXX_Merge(src proto.Message) {
212 | xxx_messageInfo_Message.Merge(m, src)
213 | }
214 | func (m *Message) XXX_Size() int {
215 | return xxx_messageInfo_Message.Size(m)
216 | }
217 | func (m *Message) XXX_DiscardUnknown() {
218 | xxx_messageInfo_Message.DiscardUnknown(m)
219 | }
220 |
221 | var xxx_messageInfo_Message proto.InternalMessageInfo
222 |
223 | func (m *Message) GetField01() float64 {
224 | if m != nil {
225 | return m.Field01
226 | }
227 | return 0
228 | }
229 |
230 | func (m *Message) GetField02() int64 {
231 | if m != nil {
232 | return m.Field02
233 | }
234 | return 0
235 | }
236 |
237 | func (m *Message) GetField03() bool {
238 | if m != nil {
239 | return m.Field03
240 | }
241 | return false
242 | }
243 |
244 | func (m *Message) GetField04() string {
245 | if m != nil {
246 | return m.Field04
247 | }
248 | return ""
249 | }
250 |
251 | func (m *Message) GetField05() []byte {
252 | if m != nil {
253 | return m.Field05
254 | }
255 | return nil
256 | }
257 |
258 | func (m *Message) GetField06() Enum {
259 | if m != nil {
260 | return m.Field06
261 | }
262 | return Enum_EnumA
263 | }
264 |
265 | func (m *Message) GetField07() []float64 {
266 | if m != nil {
267 | return m.Field07
268 | }
269 | return nil
270 | }
271 |
272 | func (m *Message) GetField08() []int64 {
273 | if m != nil {
274 | return m.Field08
275 | }
276 | return nil
277 | }
278 |
279 | func (m *Message) GetField09() []bool {
280 | if m != nil {
281 | return m.Field09
282 | }
283 | return nil
284 | }
285 |
286 | func (m *Message) GetField10() []string {
287 | if m != nil {
288 | return m.Field10
289 | }
290 | return nil
291 | }
292 |
293 | func (m *Message) GetField11() [][]byte {
294 | if m != nil {
295 | return m.Field11
296 | }
297 | return nil
298 | }
299 |
300 | func (m *Message) GetField12() []Enum {
301 | if m != nil {
302 | return m.Field12
303 | }
304 | return nil
305 | }
306 |
307 | func (m *Message) GetField13() *SubMessage {
308 | if m != nil {
309 | return m.Field13
310 | }
311 | return nil
312 | }
313 |
314 | func (m *Message) GetField14() []*SubMessage {
315 | if m != nil {
316 | return m.Field14
317 | }
318 | return nil
319 | }
320 |
321 | func init() {
322 | proto.RegisterEnum("model.Enum", Enum_name, Enum_value)
323 | proto.RegisterType((*SubMessage)(nil), "model.SubMessage")
324 | proto.RegisterType((*Message)(nil), "model.Message")
325 | }
326 |
327 | func init() { proto.RegisterFile("model/model.proto", fileDescriptor_312ac5bcab6cbb43) }
328 |
329 | var fileDescriptor_312ac5bcab6cbb43 = []byte{
330 | // 339 bytes of a gzipped FileDescriptorProto
331 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x93, 0xcb, 0x4e, 0xc2, 0x40,
332 | 0x14, 0x86, 0x19, 0x86, 0x72, 0x19, 0x90, 0x40, 0x57, 0x27, 0xae, 0x4e, 0x48, 0x8c, 0x55, 0x03,
333 | 0x65, 0xa6, 0xe5, 0xb6, 0x14, 0x63, 0xe2, 0xc6, 0x8d, 0xee, 0xdc, 0xd1, 0x52, 0x0b, 0xb1, 0x65,
334 | 0x48, 0x01, 0xdf, 0xcc, 0xd7, 0xf1, 0x59, 0x0c, 0x97, 0xe9, 0x49, 0x8c, 0x71, 0xe7, 0xce, 0x4d,
335 | 0xfb, 0x9f, 0x7c, 0x7f, 0x4e, 0xbf, 0x4c, 0x3a, 0xa2, 0x9d, 0xea, 0x79, 0x94, 0xb8, 0x87, 0x67,
336 | 0x6f, 0x9d, 0xe9, 0xad, 0xb6, 0xad, 0xc3, 0xd0, 0xf9, 0x2c, 0x0a, 0xf1, 0xbc, 0x0b, 0x1e, 0xa3,
337 | 0xcd, 0x66, 0x16, 0x47, 0x36, 0x88, 0xca, 0xeb, 0x32, 0x4a, 0xe6, 0x7d, 0x09, 0x0c, 0x99, 0xc3,
338 | 0x9e, 0xcc, 0x48, 0x44, 0x41, 0x11, 0x99, 0xc3, 0x0d, 0x51, 0x44, 0x3c, 0xe0, 0xc8, 0x9c, 0xaa,
339 | 0x21, 0x1e, 0x11, 0x1f, 0x4a, 0xc8, 0x9c, 0x9a, 0x21, 0x3e, 0x91, 0x01, 0x58, 0xc8, 0x9c, 0x86,
340 | 0x21, 0x03, 0xfb, 0xc2, 0x90, 0x21, 0x94, 0x91, 0x39, 0x4d, 0x55, 0xef, 0x1d, 0xb5, 0xef, 0x57,
341 | 0xbb, 0xd4, 0xd4, 0x86, 0xb4, 0x60, 0x04, 0x15, 0xe4, 0x24, 0x3a, 0x22, 0x32, 0x86, 0x2a, 0x72,
342 | 0x12, 0x1d, 0x13, 0x99, 0x40, 0x0d, 0x39, 0x89, 0x4e, 0x72, 0x22, 0xfb, 0x20, 0x90, 0xe7, 0xa2,
343 | 0xb2, 0x4f, 0x44, 0x42, 0x1d, 0x79, 0x2e, 0x2a, 0x65, 0x2e, 0x2a, 0x15, 0x34, 0x90, 0xff, 0x2c,
344 | 0x2a, 0x55, 0xe7, 0x83, 0x8b, 0xca, 0xff, 0xe9, 0xfe, 0xd1, 0xe9, 0xda, 0x37, 0xa6, 0xe6, 0xc1,
345 | 0x19, 0x32, 0xa7, 0xae, 0xda, 0xa7, 0x1a, 0xfd, 0xd3, 0xa6, 0xec, 0x51, 0xd9, 0x87, 0x26, 0xf2,
346 | 0x5f, 0xcb, 0xfe, 0xf5, 0xa5, 0x28, 0xed, 0x3f, 0x65, 0xd7, 0x84, 0xb5, 0x7f, 0xdf, 0xb6, 0x0a,
347 | 0x26, 0x4e, 0x5b, 0xcc, 0xc4, 0xbb, 0x56, 0x51, 0x29, 0x61, 0x3d, 0x44, 0x49, 0xa2, 0xed, 0x2b,
348 | 0x13, 0x9a, 0xa7, 0xb5, 0xa7, 0x9d, 0xe7, 0xdf, 0xe6, 0x4e, 0x61, 0xea, 0xbf, 0xa8, 0x78, 0xb9,
349 | 0x5d, 0xec, 0x82, 0x5e, 0xa8, 0x53, 0x37, 0x5d, 0x86, 0x99, 0xee, 0x6e, 0xde, 0x43, 0x37, 0xd6,
350 | 0xdd, 0x20, 0x5a, 0x85, 0x8b, 0x74, 0x96, 0xbd, 0x1d, 0x6f, 0xab, 0x1b, 0x67, 0xeb, 0xf0, 0x18,
351 | 0x83, 0xf2, 0xe1, 0xe6, 0x7a, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x24, 0x04, 0x6d, 0xce,
352 | 0x03, 0x00, 0x00,
353 | }
354 |
--------------------------------------------------------------------------------
/model/twirp-v7/model/model.twirp.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-twirp v7.1.0, DO NOT EDIT.
2 | // source: model/model.proto
3 |
4 | /*
5 | Package model is a generated twirp stub package.
6 | This code was generated with github.com/twitchtv/twirp/protoc-gen-twirp v7.1.0.
7 |
8 | It is generated from these files:
9 | model/model.proto
10 | */
11 | package model
12 |
13 | import bytes "bytes"
14 | import strings "strings"
15 | import context "context"
16 | import fmt "fmt"
17 | import ioutil "io/ioutil"
18 | import http "net/http"
19 | import strconv "strconv"
20 |
21 | import jsonpb "github.com/golang/protobuf/jsonpb"
22 | import proto "github.com/golang/protobuf/proto"
23 | import twirp "github.com/twitchtv/twirp"
24 | import ctxsetters "github.com/twitchtv/twirp/ctxsetters"
25 |
26 | // Imports only used by utility functions:
27 | import io "io"
28 | import json "encoding/json"
29 | import path "path"
30 | import url "net/url"
31 |
32 | // This is a compile-time assertion to ensure that this generated file
33 | // is compatible with the twirp package used in your project.
34 | // A compilation error at this line likely means your copy of the
35 | // twirp package needs to be updated.
36 | const _ = twirp.TwirpPackageIsVersion7
37 |
38 | // ===============
39 | // Hello Interface
40 | // ===============
41 |
42 | type Hello interface {
43 | Hello(context.Context, *Message) (*Message, error)
44 | }
45 |
46 | // =====================
47 | // Hello Protobuf Client
48 | // =====================
49 |
50 | type helloProtobufClient struct {
51 | client HTTPClient
52 | urls [1]string
53 | interceptor twirp.Interceptor
54 | opts twirp.ClientOptions
55 | }
56 |
57 | // NewHelloProtobufClient creates a Protobuf client that implements the Hello interface.
58 | // It communicates using Protobuf and can be configured with a custom HTTPClient.
59 | func NewHelloProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Hello {
60 | if c, ok := client.(*http.Client); ok {
61 | client = withoutRedirects(c)
62 | }
63 |
64 | clientOpts := twirp.ClientOptions{}
65 | for _, o := range opts {
66 | o(&clientOpts)
67 | }
68 |
69 | // Build method URLs: []/./
70 | serviceURL := sanitizeBaseURL(baseURL)
71 | serviceURL += baseServicePath(clientOpts.PathPrefix(), "model", "Hello")
72 | urls := [1]string{
73 | serviceURL + "Hello",
74 | }
75 |
76 | return &helloProtobufClient{
77 | client: client,
78 | urls: urls,
79 | interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...),
80 | opts: clientOpts,
81 | }
82 | }
83 |
84 | func (c *helloProtobufClient) Hello(ctx context.Context, in *Message) (*Message, error) {
85 | ctx = ctxsetters.WithPackageName(ctx, "model")
86 | ctx = ctxsetters.WithServiceName(ctx, "Hello")
87 | ctx = ctxsetters.WithMethodName(ctx, "Hello")
88 | caller := c.callHello
89 | if c.interceptor != nil {
90 | caller = func(ctx context.Context, req *Message) (*Message, error) {
91 | resp, err := c.interceptor(
92 | func(ctx context.Context, req interface{}) (interface{}, error) {
93 | typedReq, ok := req.(*Message)
94 | if !ok {
95 | return nil, twirp.InternalError("failed type assertion req.(*Message) when calling interceptor")
96 | }
97 | return c.callHello(ctx, typedReq)
98 | },
99 | )(ctx, req)
100 | if resp != nil {
101 | typedResp, ok := resp.(*Message)
102 | if !ok {
103 | return nil, twirp.InternalError("failed type assertion resp.(*Message) when calling interceptor")
104 | }
105 | return typedResp, err
106 | }
107 | return nil, err
108 | }
109 | }
110 | return caller(ctx, in)
111 | }
112 |
113 | func (c *helloProtobufClient) callHello(ctx context.Context, in *Message) (*Message, error) {
114 | out := new(Message)
115 | ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
116 | if err != nil {
117 | twerr, ok := err.(twirp.Error)
118 | if !ok {
119 | twerr = twirp.InternalErrorWith(err)
120 | }
121 | callClientError(ctx, c.opts.Hooks, twerr)
122 | return nil, err
123 | }
124 |
125 | callClientResponseReceived(ctx, c.opts.Hooks)
126 |
127 | return out, nil
128 | }
129 |
130 | // =================
131 | // Hello JSON Client
132 | // =================
133 |
134 | type helloJSONClient struct {
135 | client HTTPClient
136 | urls [1]string
137 | interceptor twirp.Interceptor
138 | opts twirp.ClientOptions
139 | }
140 |
141 | // NewHelloJSONClient creates a JSON client that implements the Hello interface.
142 | // It communicates using JSON and can be configured with a custom HTTPClient.
143 | func NewHelloJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Hello {
144 | if c, ok := client.(*http.Client); ok {
145 | client = withoutRedirects(c)
146 | }
147 |
148 | clientOpts := twirp.ClientOptions{}
149 | for _, o := range opts {
150 | o(&clientOpts)
151 | }
152 |
153 | // Build method URLs: []/./
154 | serviceURL := sanitizeBaseURL(baseURL)
155 | serviceURL += baseServicePath(clientOpts.PathPrefix(), "model", "Hello")
156 | urls := [1]string{
157 | serviceURL + "Hello",
158 | }
159 |
160 | return &helloJSONClient{
161 | client: client,
162 | urls: urls,
163 | interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...),
164 | opts: clientOpts,
165 | }
166 | }
167 |
168 | func (c *helloJSONClient) Hello(ctx context.Context, in *Message) (*Message, error) {
169 | ctx = ctxsetters.WithPackageName(ctx, "model")
170 | ctx = ctxsetters.WithServiceName(ctx, "Hello")
171 | ctx = ctxsetters.WithMethodName(ctx, "Hello")
172 | caller := c.callHello
173 | if c.interceptor != nil {
174 | caller = func(ctx context.Context, req *Message) (*Message, error) {
175 | resp, err := c.interceptor(
176 | func(ctx context.Context, req interface{}) (interface{}, error) {
177 | typedReq, ok := req.(*Message)
178 | if !ok {
179 | return nil, twirp.InternalError("failed type assertion req.(*Message) when calling interceptor")
180 | }
181 | return c.callHello(ctx, typedReq)
182 | },
183 | )(ctx, req)
184 | if resp != nil {
185 | typedResp, ok := resp.(*Message)
186 | if !ok {
187 | return nil, twirp.InternalError("failed type assertion resp.(*Message) when calling interceptor")
188 | }
189 | return typedResp, err
190 | }
191 | return nil, err
192 | }
193 | }
194 | return caller(ctx, in)
195 | }
196 |
197 | func (c *helloJSONClient) callHello(ctx context.Context, in *Message) (*Message, error) {
198 | out := new(Message)
199 | ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out)
200 | if err != nil {
201 | twerr, ok := err.(twirp.Error)
202 | if !ok {
203 | twerr = twirp.InternalErrorWith(err)
204 | }
205 | callClientError(ctx, c.opts.Hooks, twerr)
206 | return nil, err
207 | }
208 |
209 | callClientResponseReceived(ctx, c.opts.Hooks)
210 |
211 | return out, nil
212 | }
213 |
214 | // ====================
215 | // Hello Server Handler
216 | // ====================
217 |
218 | type helloServer struct {
219 | Hello
220 | interceptor twirp.Interceptor
221 | hooks *twirp.ServerHooks
222 | pathPrefix string // prefix for routing
223 | jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response
224 | }
225 |
226 | // NewHelloServer builds a TwirpServer that can be used as an http.Handler to handle
227 | // HTTP requests that are routed to the right method in the provided svc implementation.
228 | // The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks).
229 | func NewHelloServer(svc Hello, opts ...interface{}) TwirpServer {
230 | serverOpts := twirp.ServerOptions{}
231 | for _, opt := range opts {
232 | switch o := opt.(type) {
233 | case twirp.ServerOption:
234 | o(&serverOpts)
235 | case *twirp.ServerHooks: // backwards compatibility, allow to specify hooks as an argument
236 | twirp.WithServerHooks(o)(&serverOpts)
237 | case nil: // backwards compatibility, allow nil value for the argument
238 | continue
239 | default:
240 | panic(fmt.Sprintf("Invalid option type %T on NewHelloServer", o))
241 | }
242 | }
243 |
244 | return &helloServer{
245 | Hello: svc,
246 | pathPrefix: serverOpts.PathPrefix(),
247 | interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...),
248 | hooks: serverOpts.Hooks,
249 | jsonSkipDefaults: serverOpts.JSONSkipDefaults,
250 | }
251 | }
252 |
253 | // writeError writes an HTTP response with a valid Twirp error format, and triggers hooks.
254 | // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
255 | func (s *helloServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) {
256 | writeError(ctx, resp, err, s.hooks)
257 | }
258 |
259 | // HelloPathPrefix is a convenience constant that could used to identify URL paths.
260 | // Should be used with caution, it only matches routes generated by Twirp Go clients,
261 | // that add a "/twirp" prefix by default, and use CamelCase service and method names.
262 | // More info: https://twitchtv.github.io/twirp/docs/routing.html
263 | const HelloPathPrefix = "/twirp/model.Hello/"
264 |
265 | func (s *helloServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) {
266 | ctx := req.Context()
267 | ctx = ctxsetters.WithPackageName(ctx, "model")
268 | ctx = ctxsetters.WithServiceName(ctx, "Hello")
269 | ctx = ctxsetters.WithResponseWriter(ctx, resp)
270 |
271 | var err error
272 | ctx, err = callRequestReceived(ctx, s.hooks)
273 | if err != nil {
274 | s.writeError(ctx, resp, err)
275 | return
276 | }
277 |
278 | if req.Method != "POST" {
279 | msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method)
280 | s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path))
281 | return
282 | }
283 |
284 | // Verify path format: []/./
285 | prefix, pkgService, method := parseTwirpPath(req.URL.Path)
286 | if pkgService != "model.Hello" {
287 | msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
288 | s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path))
289 | return
290 | }
291 | if prefix != s.pathPrefix {
292 | msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path)
293 | s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path))
294 | return
295 | }
296 |
297 | switch method {
298 | case "Hello":
299 | s.serveHello(ctx, resp, req)
300 | return
301 | default:
302 | msg := fmt.Sprintf("no handler for path %q", req.URL.Path)
303 | s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path))
304 | return
305 | }
306 | }
307 |
308 | func (s *helloServer) serveHello(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
309 | header := req.Header.Get("Content-Type")
310 | i := strings.Index(header, ";")
311 | if i == -1 {
312 | i = len(header)
313 | }
314 | switch strings.TrimSpace(strings.ToLower(header[:i])) {
315 | case "application/json":
316 | s.serveHelloJSON(ctx, resp, req)
317 | case "application/protobuf":
318 | s.serveHelloProtobuf(ctx, resp, req)
319 | default:
320 | msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type"))
321 | twerr := badRouteError(msg, req.Method, req.URL.Path)
322 | s.writeError(ctx, resp, twerr)
323 | }
324 | }
325 |
326 | func (s *helloServer) serveHelloJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
327 | var err error
328 | ctx = ctxsetters.WithMethodName(ctx, "Hello")
329 | ctx, err = callRequestRouted(ctx, s.hooks)
330 | if err != nil {
331 | s.writeError(ctx, resp, err)
332 | return
333 | }
334 |
335 | reqContent := new(Message)
336 | unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
337 | if err = unmarshaler.Unmarshal(req.Body, reqContent); err != nil {
338 | s.writeError(ctx, resp, malformedRequestError("the json request could not be decoded"))
339 | return
340 | }
341 |
342 | handler := s.Hello.Hello
343 | if s.interceptor != nil {
344 | handler = func(ctx context.Context, req *Message) (*Message, error) {
345 | resp, err := s.interceptor(
346 | func(ctx context.Context, req interface{}) (interface{}, error) {
347 | typedReq, ok := req.(*Message)
348 | if !ok {
349 | return nil, twirp.InternalError("failed type assertion req.(*Message) when calling interceptor")
350 | }
351 | return s.Hello.Hello(ctx, typedReq)
352 | },
353 | )(ctx, req)
354 | if resp != nil {
355 | typedResp, ok := resp.(*Message)
356 | if !ok {
357 | return nil, twirp.InternalError("failed type assertion resp.(*Message) when calling interceptor")
358 | }
359 | return typedResp, err
360 | }
361 | return nil, err
362 | }
363 | }
364 |
365 | // Call service method
366 | var respContent *Message
367 | func() {
368 | defer ensurePanicResponses(ctx, resp, s.hooks)
369 | respContent, err = handler(ctx, reqContent)
370 | }()
371 |
372 | if err != nil {
373 | s.writeError(ctx, resp, err)
374 | return
375 | }
376 | if respContent == nil {
377 | s.writeError(ctx, resp, twirp.InternalError("received a nil *Message and nil error while calling Hello. nil responses are not supported"))
378 | return
379 | }
380 |
381 | ctx = callResponsePrepared(ctx, s.hooks)
382 |
383 | var buf bytes.Buffer
384 | marshaler := &jsonpb.Marshaler{OrigName: true, EmitDefaults: !s.jsonSkipDefaults}
385 | if err = marshaler.Marshal(&buf, respContent); err != nil {
386 | s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response"))
387 | return
388 | }
389 |
390 | ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
391 | respBytes := buf.Bytes()
392 | resp.Header().Set("Content-Type", "application/json")
393 | resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
394 | resp.WriteHeader(http.StatusOK)
395 |
396 | if n, err := resp.Write(respBytes); err != nil {
397 | msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
398 | twerr := twirp.NewError(twirp.Unknown, msg)
399 | ctx = callError(ctx, s.hooks, twerr)
400 | }
401 | callResponseSent(ctx, s.hooks)
402 | }
403 |
404 | func (s *helloServer) serveHelloProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) {
405 | var err error
406 | ctx = ctxsetters.WithMethodName(ctx, "Hello")
407 | ctx, err = callRequestRouted(ctx, s.hooks)
408 | if err != nil {
409 | s.writeError(ctx, resp, err)
410 | return
411 | }
412 |
413 | buf, err := ioutil.ReadAll(req.Body)
414 | if err != nil {
415 | s.writeError(ctx, resp, wrapInternal(err, "failed to read request body"))
416 | return
417 | }
418 | reqContent := new(Message)
419 | if err = proto.Unmarshal(buf, reqContent); err != nil {
420 | s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded"))
421 | return
422 | }
423 |
424 | handler := s.Hello.Hello
425 | if s.interceptor != nil {
426 | handler = func(ctx context.Context, req *Message) (*Message, error) {
427 | resp, err := s.interceptor(
428 | func(ctx context.Context, req interface{}) (interface{}, error) {
429 | typedReq, ok := req.(*Message)
430 | if !ok {
431 | return nil, twirp.InternalError("failed type assertion req.(*Message) when calling interceptor")
432 | }
433 | return s.Hello.Hello(ctx, typedReq)
434 | },
435 | )(ctx, req)
436 | if resp != nil {
437 | typedResp, ok := resp.(*Message)
438 | if !ok {
439 | return nil, twirp.InternalError("failed type assertion resp.(*Message) when calling interceptor")
440 | }
441 | return typedResp, err
442 | }
443 | return nil, err
444 | }
445 | }
446 |
447 | // Call service method
448 | var respContent *Message
449 | func() {
450 | defer ensurePanicResponses(ctx, resp, s.hooks)
451 | respContent, err = handler(ctx, reqContent)
452 | }()
453 |
454 | if err != nil {
455 | s.writeError(ctx, resp, err)
456 | return
457 | }
458 | if respContent == nil {
459 | s.writeError(ctx, resp, twirp.InternalError("received a nil *Message and nil error while calling Hello. nil responses are not supported"))
460 | return
461 | }
462 |
463 | ctx = callResponsePrepared(ctx, s.hooks)
464 |
465 | respBytes, err := proto.Marshal(respContent)
466 | if err != nil {
467 | s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response"))
468 | return
469 | }
470 |
471 | ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK)
472 | resp.Header().Set("Content-Type", "application/protobuf")
473 | resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes)))
474 | resp.WriteHeader(http.StatusOK)
475 | if n, err := resp.Write(respBytes); err != nil {
476 | msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error())
477 | twerr := twirp.NewError(twirp.Unknown, msg)
478 | ctx = callError(ctx, s.hooks, twerr)
479 | }
480 | callResponseSent(ctx, s.hooks)
481 | }
482 |
483 | func (s *helloServer) ServiceDescriptor() ([]byte, int) {
484 | return twirpFileDescriptor0, 0
485 | }
486 |
487 | func (s *helloServer) ProtocGenTwirpVersion() string {
488 | return "v7.1.0"
489 | }
490 |
491 | // PathPrefix returns the base service path, in the form: "//./"
492 | // that is everything in a Twirp route except for the . This can be used for routing,
493 | // for example to identify the requests that are targeted to this service in a mux.
494 | func (s *helloServer) PathPrefix() string {
495 | return baseServicePath(s.pathPrefix, "model", "Hello")
496 | }
497 |
498 | // =====
499 | // Utils
500 | // =====
501 |
502 | // HTTPClient is the interface used by generated clients to send HTTP requests.
503 | // It is fulfilled by *(net/http).Client, which is sufficient for most users.
504 | // Users can provide their own implementation for special retry policies.
505 | //
506 | // HTTPClient implementations should not follow redirects. Redirects are
507 | // automatically disabled if *(net/http).Client is passed to client
508 | // constructors. See the withoutRedirects function in this file for more
509 | // details.
510 | type HTTPClient interface {
511 | Do(req *http.Request) (*http.Response, error)
512 | }
513 |
514 | // TwirpServer is the interface generated server structs will support: they're
515 | // HTTP handlers with additional methods for accessing metadata about the
516 | // service. Those accessors are a low-level API for building reflection tools.
517 | // Most people can think of TwirpServers as just http.Handlers.
518 | type TwirpServer interface {
519 | http.Handler
520 |
521 | // ServiceDescriptor returns gzipped bytes describing the .proto file that
522 | // this service was generated from. Once unzipped, the bytes can be
523 | // unmarshalled as a
524 | // github.com/golang/protobuf/protoc-gen-go/descriptor.FileDescriptorProto.
525 | //
526 | // The returned integer is the index of this particular service within that
527 | // FileDescriptorProto's 'Service' slice of ServiceDescriptorProtos. This is a
528 | // low-level field, expected to be used for reflection.
529 | ServiceDescriptor() ([]byte, int)
530 |
531 | // ProtocGenTwirpVersion is the semantic version string of the version of
532 | // twirp used to generate this file.
533 | ProtocGenTwirpVersion() string
534 |
535 | // PathPrefix returns the HTTP URL path prefix for all methods handled by this
536 | // service. This can be used with an HTTP mux to route Twirp requests.
537 | // The path prefix is in the form: "//./"
538 | // that is, everything in a Twirp route except for the at the end.
539 | PathPrefix() string
540 | }
541 |
542 | // WriteError writes an HTTP response with a valid Twirp error format (code, msg, meta).
543 | // Useful outside of the Twirp server (e.g. http middleware), but does not trigger hooks.
544 | // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err)
545 | func WriteError(resp http.ResponseWriter, err error) {
546 | writeError(context.Background(), resp, err, nil)
547 | }
548 |
549 | // writeError writes Twirp errors in the response and triggers hooks.
550 | func writeError(ctx context.Context, resp http.ResponseWriter, err error, hooks *twirp.ServerHooks) {
551 | // Non-twirp errors are wrapped as Internal (default)
552 | twerr, ok := err.(twirp.Error)
553 | if !ok {
554 | twerr = twirp.InternalErrorWith(err)
555 | }
556 |
557 | statusCode := twirp.ServerHTTPStatusFromErrorCode(twerr.Code())
558 | ctx = ctxsetters.WithStatusCode(ctx, statusCode)
559 | ctx = callError(ctx, hooks, twerr)
560 |
561 | respBody := marshalErrorToJSON(twerr)
562 |
563 | resp.Header().Set("Content-Type", "application/json") // Error responses are always JSON
564 | resp.Header().Set("Content-Length", strconv.Itoa(len(respBody)))
565 | resp.WriteHeader(statusCode) // set HTTP status code and send response
566 |
567 | _, writeErr := resp.Write(respBody)
568 | if writeErr != nil {
569 | // We have three options here. We could log the error, call the Error
570 | // hook, or just silently ignore the error.
571 | //
572 | // Logging is unacceptable because we don't have a user-controlled
573 | // logger; writing out to stderr without permission is too rude.
574 | //
575 | // Calling the Error hook would confuse users: it would mean the Error
576 | // hook got called twice for one request, which is likely to lead to
577 | // duplicated log messages and metrics, no matter how well we document
578 | // the behavior.
579 | //
580 | // Silently ignoring the error is our least-bad option. It's highly
581 | // likely that the connection is broken and the original 'err' says
582 | // so anyway.
583 | _ = writeErr
584 | }
585 |
586 | callResponseSent(ctx, hooks)
587 | }
588 |
589 | // sanitizeBaseURL parses the the baseURL, and adds the "http" scheme if needed.
590 | // If the URL is unparsable, the baseURL is returned unchaged.
591 | func sanitizeBaseURL(baseURL string) string {
592 | u, err := url.Parse(baseURL)
593 | if err != nil {
594 | return baseURL // invalid URL will fail later when making requests
595 | }
596 | if u.Scheme == "" {
597 | u.Scheme = "http"
598 | }
599 | return u.String()
600 | }
601 |
602 | // baseServicePath composes the path prefix for the service (without ).
603 | // e.g.: baseServicePath("/twirp", "my.pkg", "MyService")
604 | // returns => "/twirp/my.pkg.MyService/"
605 | // e.g.: baseServicePath("", "", "MyService")
606 | // returns => "/MyService/"
607 | func baseServicePath(prefix, pkg, service string) string {
608 | fullServiceName := service
609 | if pkg != "" {
610 | fullServiceName = pkg + "." + service
611 | }
612 | return path.Join("/", prefix, fullServiceName) + "/"
613 | }
614 |
615 | // parseTwirpPath extracts path components form a valid Twirp route.
616 | // Expected format: "[]/./"
617 | // e.g.: prefix, pkgService, method := parseTwirpPath("/twirp/pkg.Svc/MakeHat")
618 | func parseTwirpPath(path string) (string, string, string) {
619 | parts := strings.Split(path, "/")
620 | if len(parts) < 2 {
621 | return "", "", ""
622 | }
623 | method := parts[len(parts)-1]
624 | pkgService := parts[len(parts)-2]
625 | prefix := strings.Join(parts[0:len(parts)-2], "/")
626 | return prefix, pkgService, method
627 | }
628 |
629 | // getCustomHTTPReqHeaders retrieves a copy of any headers that are set in
630 | // a context through the twirp.WithHTTPRequestHeaders function.
631 | // If there are no headers set, or if they have the wrong type, nil is returned.
632 | func getCustomHTTPReqHeaders(ctx context.Context) http.Header {
633 | header, ok := twirp.HTTPRequestHeaders(ctx)
634 | if !ok || header == nil {
635 | return nil
636 | }
637 | copied := make(http.Header)
638 | for k, vv := range header {
639 | if vv == nil {
640 | copied[k] = nil
641 | continue
642 | }
643 | copied[k] = make([]string, len(vv))
644 | copy(copied[k], vv)
645 | }
646 | return copied
647 | }
648 |
649 | // newRequest makes an http.Request from a client, adding common headers.
650 | func newRequest(ctx context.Context, url string, reqBody io.Reader, contentType string) (*http.Request, error) {
651 | req, err := http.NewRequest("POST", url, reqBody)
652 | if err != nil {
653 | return nil, err
654 | }
655 | req = req.WithContext(ctx)
656 | if customHeader := getCustomHTTPReqHeaders(ctx); customHeader != nil {
657 | req.Header = customHeader
658 | }
659 | req.Header.Set("Accept", contentType)
660 | req.Header.Set("Content-Type", contentType)
661 | req.Header.Set("Twirp-Version", "v7.1.0")
662 | return req, nil
663 | }
664 |
665 | // JSON serialization for errors
666 | type twerrJSON struct {
667 | Code string `json:"code"`
668 | Msg string `json:"msg"`
669 | Meta map[string]string `json:"meta,omitempty"`
670 | }
671 |
672 | // marshalErrorToJSON returns JSON from a twirp.Error, that can be used as HTTP error response body.
673 | // If serialization fails, it will use a descriptive Internal error instead.
674 | func marshalErrorToJSON(twerr twirp.Error) []byte {
675 | // make sure that msg is not too large
676 | msg := twerr.Msg()
677 | if len(msg) > 1e6 {
678 | msg = msg[:1e6]
679 | }
680 |
681 | tj := twerrJSON{
682 | Code: string(twerr.Code()),
683 | Msg: msg,
684 | Meta: twerr.MetaMap(),
685 | }
686 |
687 | buf, err := json.Marshal(&tj)
688 | if err != nil {
689 | buf = []byte("{\"type\": \"" + twirp.Internal + "\", \"msg\": \"There was an error but it could not be serialized into JSON\"}") // fallback
690 | }
691 |
692 | return buf
693 | }
694 |
695 | // errorFromResponse builds a twirp.Error from a non-200 HTTP response.
696 | // If the response has a valid serialized Twirp error, then it's returned.
697 | // If not, the response status code is used to generate a similar twirp
698 | // error. See twirpErrorFromIntermediary for more info on intermediary errors.
699 | func errorFromResponse(resp *http.Response) twirp.Error {
700 | statusCode := resp.StatusCode
701 | statusText := http.StatusText(statusCode)
702 |
703 | if isHTTPRedirect(statusCode) {
704 | // Unexpected redirect: it must be an error from an intermediary.
705 | // Twirp clients don't follow redirects automatically, Twirp only handles
706 | // POST requests, redirects should only happen on GET and HEAD requests.
707 | location := resp.Header.Get("Location")
708 | msg := fmt.Sprintf("unexpected HTTP status code %d %q received, Location=%q", statusCode, statusText, location)
709 | return twirpErrorFromIntermediary(statusCode, msg, location)
710 | }
711 |
712 | respBodyBytes, err := ioutil.ReadAll(resp.Body)
713 | if err != nil {
714 | return wrapInternal(err, "failed to read server error response body")
715 | }
716 |
717 | var tj twerrJSON
718 | dec := json.NewDecoder(bytes.NewReader(respBodyBytes))
719 | dec.DisallowUnknownFields()
720 | if err := dec.Decode(&tj); err != nil || tj.Code == "" {
721 | // Invalid JSON response; it must be an error from an intermediary.
722 | msg := fmt.Sprintf("Error from intermediary with HTTP status code %d %q", statusCode, statusText)
723 | return twirpErrorFromIntermediary(statusCode, msg, string(respBodyBytes))
724 | }
725 |
726 | errorCode := twirp.ErrorCode(tj.Code)
727 | if !twirp.IsValidErrorCode(errorCode) {
728 | msg := "invalid type returned from server error response: " + tj.Code
729 | return twirp.InternalError(msg).WithMeta("body", string(respBodyBytes))
730 | }
731 |
732 | twerr := twirp.NewError(errorCode, tj.Msg)
733 | for k, v := range tj.Meta {
734 | twerr = twerr.WithMeta(k, v)
735 | }
736 | return twerr
737 | }
738 |
739 | // twirpErrorFromIntermediary maps HTTP errors from non-twirp sources to twirp errors.
740 | // The mapping is similar to gRPC: https://github.com/grpc/grpc/blob/master/doc/http-grpc-status-mapping.md.
741 | // Returned twirp Errors have some additional metadata for inspection.
742 | func twirpErrorFromIntermediary(status int, msg string, bodyOrLocation string) twirp.Error {
743 | var code twirp.ErrorCode
744 | if isHTTPRedirect(status) { // 3xx
745 | code = twirp.Internal
746 | } else {
747 | switch status {
748 | case 400: // Bad Request
749 | code = twirp.Internal
750 | case 401: // Unauthorized
751 | code = twirp.Unauthenticated
752 | case 403: // Forbidden
753 | code = twirp.PermissionDenied
754 | case 404: // Not Found
755 | code = twirp.BadRoute
756 | case 429: // Too Many Requests
757 | code = twirp.ResourceExhausted
758 | case 502, 503, 504: // Bad Gateway, Service Unavailable, Gateway Timeout
759 | code = twirp.Unavailable
760 | default: // All other codes
761 | code = twirp.Unknown
762 | }
763 | }
764 |
765 | twerr := twirp.NewError(code, msg)
766 | twerr = twerr.WithMeta("http_error_from_intermediary", "true") // to easily know if this error was from intermediary
767 | twerr = twerr.WithMeta("status_code", strconv.Itoa(status))
768 | if isHTTPRedirect(status) {
769 | twerr = twerr.WithMeta("location", bodyOrLocation)
770 | } else {
771 | twerr = twerr.WithMeta("body", bodyOrLocation)
772 | }
773 | return twerr
774 | }
775 |
776 | func isHTTPRedirect(status int) bool {
777 | return status >= 300 && status <= 399
778 | }
779 |
780 | // wrapInternal wraps an error with a prefix as an Internal error.
781 | // The original error cause is accessible by github.com/pkg/errors.Cause.
782 | func wrapInternal(err error, prefix string) twirp.Error {
783 | return twirp.InternalErrorWith(&wrappedError{prefix: prefix, cause: err})
784 | }
785 |
786 | type wrappedError struct {
787 | prefix string
788 | cause error
789 | }
790 |
791 | func (e *wrappedError) Error() string { return e.prefix + ": " + e.cause.Error() }
792 | func (e *wrappedError) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As
793 | func (e *wrappedError) Cause() error { return e.cause } // for github.com/pkg/errors
794 |
795 | // ensurePanicResponses makes sure that rpc methods causing a panic still result in a Twirp Internal
796 | // error response (status 500), and error hooks are properly called with the panic wrapped as an error.
797 | // The panic is re-raised so it can be handled normally with middleware.
798 | func ensurePanicResponses(ctx context.Context, resp http.ResponseWriter, hooks *twirp.ServerHooks) {
799 | if r := recover(); r != nil {
800 | // Wrap the panic as an error so it can be passed to error hooks.
801 | // The original error is accessible from error hooks, but not visible in the response.
802 | err := errFromPanic(r)
803 | twerr := &internalWithCause{msg: "Internal service panic", cause: err}
804 | // Actually write the error
805 | writeError(ctx, resp, twerr, hooks)
806 | // If possible, flush the error to the wire.
807 | f, ok := resp.(http.Flusher)
808 | if ok {
809 | f.Flush()
810 | }
811 |
812 | panic(r)
813 | }
814 | }
815 |
816 | // errFromPanic returns the typed error if the recovered panic is an error, otherwise formats as error.
817 | func errFromPanic(p interface{}) error {
818 | if err, ok := p.(error); ok {
819 | return err
820 | }
821 | return fmt.Errorf("panic: %v", p)
822 | }
823 |
824 | // internalWithCause is a Twirp Internal error wrapping an original error cause,
825 | // but the original error message is not exposed on Msg(). The original error
826 | // can be checked with go1.13+ errors.Is/As, and also by (github.com/pkg/errors).Unwrap
827 | type internalWithCause struct {
828 | msg string
829 | cause error
830 | }
831 |
832 | func (e *internalWithCause) Unwrap() error { return e.cause } // for go1.13 + errors.Is/As
833 | func (e *internalWithCause) Cause() error { return e.cause } // for github.com/pkg/errors
834 | func (e *internalWithCause) Error() string { return e.msg + ": " + e.cause.Error() }
835 | func (e *internalWithCause) Code() twirp.ErrorCode { return twirp.Internal }
836 | func (e *internalWithCause) Msg() string { return e.msg }
837 | func (e *internalWithCause) Meta(key string) string { return "" }
838 | func (e *internalWithCause) MetaMap() map[string]string { return nil }
839 | func (e *internalWithCause) WithMeta(key string, val string) twirp.Error { return e }
840 |
841 | // malformedRequestError is used when the twirp server cannot unmarshal a request
842 | func malformedRequestError(msg string) twirp.Error {
843 | return twirp.NewError(twirp.Malformed, msg)
844 | }
845 |
846 | // badRouteError is used when the twirp server cannot route a request
847 | func badRouteError(msg string, method, url string) twirp.Error {
848 | err := twirp.NewError(twirp.BadRoute, msg)
849 | err = err.WithMeta("twirp_invalid_route", method+" "+url)
850 | return err
851 | }
852 |
853 | // withoutRedirects makes sure that the POST request can not be redirected.
854 | // The standard library will, by default, redirect requests (including POSTs) if it gets a 302 or
855 | // 303 response, and also 301s in go1.8. It redirects by making a second request, changing the
856 | // method to GET and removing the body. This produces very confusing error messages, so instead we
857 | // set a redirect policy that always errors. This stops Go from executing the redirect.
858 | //
859 | // We have to be a little careful in case the user-provided http.Client has its own CheckRedirect
860 | // policy - if so, we'll run through that policy first.
861 | //
862 | // Because this requires modifying the http.Client, we make a new copy of the client and return it.
863 | func withoutRedirects(in *http.Client) *http.Client {
864 | copy := *in
865 | copy.CheckRedirect = func(req *http.Request, via []*http.Request) error {
866 | if in.CheckRedirect != nil {
867 | // Run the input's redirect if it exists, in case it has side effects, but ignore any error it
868 | // returns, since we want to use ErrUseLastResponse.
869 | err := in.CheckRedirect(req, via)
870 | _ = err // Silly, but this makes sure generated code passes errcheck -blank, which some people use.
871 | }
872 | return http.ErrUseLastResponse
873 | }
874 | return ©
875 | }
876 |
877 | // doProtobufRequest makes a Protobuf request to the remote Twirp service.
878 | func doProtobufRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) {
879 | reqBodyBytes, err := proto.Marshal(in)
880 | if err != nil {
881 | return ctx, wrapInternal(err, "failed to marshal proto request")
882 | }
883 | reqBody := bytes.NewBuffer(reqBodyBytes)
884 | if err = ctx.Err(); err != nil {
885 | return ctx, wrapInternal(err, "aborted because context was done")
886 | }
887 |
888 | req, err := newRequest(ctx, url, reqBody, "application/protobuf")
889 | if err != nil {
890 | return ctx, wrapInternal(err, "could not build request")
891 | }
892 | ctx, err = callClientRequestPrepared(ctx, hooks, req)
893 | if err != nil {
894 | return ctx, err
895 | }
896 |
897 | req = req.WithContext(ctx)
898 | resp, err := client.Do(req)
899 | if err != nil {
900 | return ctx, wrapInternal(err, "failed to do request")
901 | }
902 |
903 | defer func() {
904 | cerr := resp.Body.Close()
905 | if err == nil && cerr != nil {
906 | err = wrapInternal(cerr, "failed to close response body")
907 | }
908 | }()
909 |
910 | if err = ctx.Err(); err != nil {
911 | return ctx, wrapInternal(err, "aborted because context was done")
912 | }
913 |
914 | if resp.StatusCode != 200 {
915 | return ctx, errorFromResponse(resp)
916 | }
917 |
918 | respBodyBytes, err := ioutil.ReadAll(resp.Body)
919 | if err != nil {
920 | return ctx, wrapInternal(err, "failed to read response body")
921 | }
922 | if err = ctx.Err(); err != nil {
923 | return ctx, wrapInternal(err, "aborted because context was done")
924 | }
925 |
926 | if err = proto.Unmarshal(respBodyBytes, out); err != nil {
927 | return ctx, wrapInternal(err, "failed to unmarshal proto response")
928 | }
929 | return ctx, nil
930 | }
931 |
932 | // doJSONRequest makes a JSON request to the remote Twirp service.
933 | func doJSONRequest(ctx context.Context, client HTTPClient, hooks *twirp.ClientHooks, url string, in, out proto.Message) (_ context.Context, err error) {
934 | reqBody := bytes.NewBuffer(nil)
935 | marshaler := &jsonpb.Marshaler{OrigName: true}
936 | if err = marshaler.Marshal(reqBody, in); err != nil {
937 | return ctx, wrapInternal(err, "failed to marshal json request")
938 | }
939 | if err = ctx.Err(); err != nil {
940 | return ctx, wrapInternal(err, "aborted because context was done")
941 | }
942 |
943 | req, err := newRequest(ctx, url, reqBody, "application/json")
944 | if err != nil {
945 | return ctx, wrapInternal(err, "could not build request")
946 | }
947 | ctx, err = callClientRequestPrepared(ctx, hooks, req)
948 | if err != nil {
949 | return ctx, err
950 | }
951 |
952 | req = req.WithContext(ctx)
953 | resp, err := client.Do(req)
954 | if err != nil {
955 | return ctx, wrapInternal(err, "failed to do request")
956 | }
957 |
958 | defer func() {
959 | cerr := resp.Body.Close()
960 | if err == nil && cerr != nil {
961 | err = wrapInternal(cerr, "failed to close response body")
962 | }
963 | }()
964 |
965 | if err = ctx.Err(); err != nil {
966 | return ctx, wrapInternal(err, "aborted because context was done")
967 | }
968 |
969 | if resp.StatusCode != 200 {
970 | return ctx, errorFromResponse(resp)
971 | }
972 |
973 | unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: true}
974 | if err = unmarshaler.Unmarshal(resp.Body, out); err != nil {
975 | return ctx, wrapInternal(err, "failed to unmarshal json response")
976 | }
977 | if err = ctx.Err(); err != nil {
978 | return ctx, wrapInternal(err, "aborted because context was done")
979 | }
980 | return ctx, nil
981 | }
982 |
983 | // Call twirp.ServerHooks.RequestReceived if the hook is available
984 | func callRequestReceived(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
985 | if h == nil || h.RequestReceived == nil {
986 | return ctx, nil
987 | }
988 | return h.RequestReceived(ctx)
989 | }
990 |
991 | // Call twirp.ServerHooks.RequestRouted if the hook is available
992 | func callRequestRouted(ctx context.Context, h *twirp.ServerHooks) (context.Context, error) {
993 | if h == nil || h.RequestRouted == nil {
994 | return ctx, nil
995 | }
996 | return h.RequestRouted(ctx)
997 | }
998 |
999 | // Call twirp.ServerHooks.ResponsePrepared if the hook is available
1000 | func callResponsePrepared(ctx context.Context, h *twirp.ServerHooks) context.Context {
1001 | if h == nil || h.ResponsePrepared == nil {
1002 | return ctx
1003 | }
1004 | return h.ResponsePrepared(ctx)
1005 | }
1006 |
1007 | // Call twirp.ServerHooks.ResponseSent if the hook is available
1008 | func callResponseSent(ctx context.Context, h *twirp.ServerHooks) {
1009 | if h == nil || h.ResponseSent == nil {
1010 | return
1011 | }
1012 | h.ResponseSent(ctx)
1013 | }
1014 |
1015 | // Call twirp.ServerHooks.Error if the hook is available
1016 | func callError(ctx context.Context, h *twirp.ServerHooks, err twirp.Error) context.Context {
1017 | if h == nil || h.Error == nil {
1018 | return ctx
1019 | }
1020 | return h.Error(ctx, err)
1021 | }
1022 |
1023 | func callClientResponseReceived(ctx context.Context, h *twirp.ClientHooks) {
1024 | if h == nil || h.ResponseReceived == nil {
1025 | return
1026 | }
1027 | h.ResponseReceived(ctx)
1028 | }
1029 |
1030 | func callClientRequestPrepared(ctx context.Context, h *twirp.ClientHooks, req *http.Request) (context.Context, error) {
1031 | if h == nil || h.RequestPrepared == nil {
1032 | return ctx, nil
1033 | }
1034 | return h.RequestPrepared(ctx, req)
1035 | }
1036 |
1037 | func callClientError(ctx context.Context, h *twirp.ClientHooks, err twirp.Error) {
1038 | if h == nil || h.Error == nil {
1039 | return
1040 | }
1041 | h.Error(ctx, err)
1042 | }
1043 |
1044 | var twirpFileDescriptor0 = []byte{
1045 | // 339 bytes of a gzipped FileDescriptorProto
1046 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x93, 0xcb, 0x4e, 0xc2, 0x40,
1047 | 0x14, 0x86, 0x19, 0x86, 0x72, 0x19, 0x90, 0x40, 0x57, 0x27, 0xae, 0x4e, 0x48, 0x8c, 0x55, 0x03,
1048 | 0x65, 0xa6, 0xe5, 0xb6, 0x14, 0x63, 0xe2, 0xc6, 0x8d, 0xee, 0xdc, 0xd1, 0x52, 0x0b, 0xb1, 0x65,
1049 | 0x48, 0x01, 0xdf, 0xcc, 0xd7, 0xf1, 0x59, 0x0c, 0x97, 0xe9, 0x49, 0x8c, 0x71, 0xe7, 0xce, 0x4d,
1050 | 0xfb, 0x9f, 0x7c, 0x7f, 0x4e, 0xbf, 0x4c, 0x3a, 0xa2, 0x9d, 0xea, 0x79, 0x94, 0xb8, 0x87, 0x67,
1051 | 0x6f, 0x9d, 0xe9, 0xad, 0xb6, 0xad, 0xc3, 0xd0, 0xf9, 0x2c, 0x0a, 0xf1, 0xbc, 0x0b, 0x1e, 0xa3,
1052 | 0xcd, 0x66, 0x16, 0x47, 0x36, 0x88, 0xca, 0xeb, 0x32, 0x4a, 0xe6, 0x7d, 0x09, 0x0c, 0x99, 0xc3,
1053 | 0x9e, 0xcc, 0x48, 0x44, 0x41, 0x11, 0x99, 0xc3, 0x0d, 0x51, 0x44, 0x3c, 0xe0, 0xc8, 0x9c, 0xaa,
1054 | 0x21, 0x1e, 0x11, 0x1f, 0x4a, 0xc8, 0x9c, 0x9a, 0x21, 0x3e, 0x91, 0x01, 0x58, 0xc8, 0x9c, 0x86,
1055 | 0x21, 0x03, 0xfb, 0xc2, 0x90, 0x21, 0x94, 0x91, 0x39, 0x4d, 0x55, 0xef, 0x1d, 0xb5, 0xef, 0x57,
1056 | 0xbb, 0xd4, 0xd4, 0x86, 0xb4, 0x60, 0x04, 0x15, 0xe4, 0x24, 0x3a, 0x22, 0x32, 0x86, 0x2a, 0x72,
1057 | 0x12, 0x1d, 0x13, 0x99, 0x40, 0x0d, 0x39, 0x89, 0x4e, 0x72, 0x22, 0xfb, 0x20, 0x90, 0xe7, 0xa2,
1058 | 0xb2, 0x4f, 0x44, 0x42, 0x1d, 0x79, 0x2e, 0x2a, 0x65, 0x2e, 0x2a, 0x15, 0x34, 0x90, 0xff, 0x2c,
1059 | 0x2a, 0x55, 0xe7, 0x83, 0x8b, 0xca, 0xff, 0xe9, 0xfe, 0xd1, 0xe9, 0xda, 0x37, 0xa6, 0xe6, 0xc1,
1060 | 0x19, 0x32, 0xa7, 0xae, 0xda, 0xa7, 0x1a, 0xfd, 0xd3, 0xa6, 0xec, 0x51, 0xd9, 0x87, 0x26, 0xf2,
1061 | 0x5f, 0xcb, 0xfe, 0xf5, 0xa5, 0x28, 0xed, 0x3f, 0x65, 0xd7, 0x84, 0xb5, 0x7f, 0xdf, 0xb6, 0x0a,
1062 | 0x26, 0x4e, 0x5b, 0xcc, 0xc4, 0xbb, 0x56, 0x51, 0x29, 0x61, 0x3d, 0x44, 0x49, 0xa2, 0xed, 0x2b,
1063 | 0x13, 0x9a, 0xa7, 0xb5, 0xa7, 0x9d, 0xe7, 0xdf, 0xe6, 0x4e, 0x61, 0xea, 0xbf, 0xa8, 0x78, 0xb9,
1064 | 0x5d, 0xec, 0x82, 0x5e, 0xa8, 0x53, 0x37, 0x5d, 0x86, 0x99, 0xee, 0x6e, 0xde, 0x43, 0x37, 0xd6,
1065 | 0xdd, 0x20, 0x5a, 0x85, 0x8b, 0x74, 0x96, 0xbd, 0x1d, 0x6f, 0xab, 0x1b, 0x67, 0xeb, 0xf0, 0x18,
1066 | 0x83, 0xf2, 0xe1, 0xe6, 0x7a, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x24, 0x04, 0x6d, 0xce,
1067 | 0x03, 0x00, 0x00,
1068 | }
1069 |
--------------------------------------------------------------------------------
/rpcx-v5/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "crypto/tls"
6 | "flag"
7 | "sync"
8 | "time"
9 |
10 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
11 | "github.com/micro-svc/go-rpc-framework-benchmark/rpcx-v5/codec"
12 |
13 | ulog "github.com/gxxgle/go-utils/log"
14 | "github.com/montanaflynn/stats"
15 | "github.com/phuslu/log"
16 | "github.com/smallnest/rpcx/v5/client"
17 | xlog "github.com/smallnest/rpcx/v5/log"
18 | "github.com/smallnest/rpcx/v5/protocol"
19 | )
20 |
21 | var (
22 | // flags
23 | clients = flag.Int("clients", 100, "concurrency client amount")
24 | requests = flag.Int("requests", 1000, "request amount per client")
25 | address = flag.String("addr", "127.0.0.1:5678", "server address")
26 | transport = flag.String("transport", "tcp", "server transport [tcp, quic, utp]")
27 | xcodec = flag.String("codec", "json", "server codec [json, protobuf, msgpack, jsoniter]")
28 |
29 | option = client.DefaultOption
30 | )
31 |
32 | func main() {
33 | flag.Parse()
34 | xlog.SetDummyLogger()
35 | codec.Register()
36 |
37 | switch *transport {
38 | case "tcp":
39 | case "quic", "utp":
40 | option.TLSConfig = &tls.Config{InsecureSkipVerify: true}
41 | default:
42 | log.Fatal().Msg("flag transport not support")
43 | }
44 |
45 | switch *xcodec {
46 | case "json":
47 | option.SerializeType = protocol.JSON
48 | case "protobuf":
49 | option.SerializeType = protocol.ProtoBuffer
50 | case "msgpack":
51 | option.SerializeType = protocol.MsgPack
52 | case "jsoniter":
53 | option.SerializeType = codec.JSONiter
54 | default:
55 | log.Fatal().Msg("flag codec not support")
56 | }
57 |
58 | var (
59 | wg sync.WaitGroup
60 | total = *clients * *requests
61 | clis = newClients(*clients)
62 | durations = make([]time.Duration, total)
63 | )
64 |
65 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
66 |
67 | start := time.Now()
68 | wg.Add(total)
69 | for i := range clis {
70 | go func(i int) {
71 | cli := clis[i]
72 | for j := 0; j < *requests; j++ {
73 | start := time.Now()
74 | call(cli)
75 | durations[i**requests+j] = time.Since(start)
76 | wg.Done()
77 | }
78 | }(i)
79 | }
80 | wg.Wait()
81 |
82 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
83 | s := stats.LoadRawData(durations)
84 | min, _ := s.Min()
85 | max, _ := s.Max()
86 | mean, _ := s.Mean()
87 | median, _ := s.Median()
88 | log.Info().
89 | Int64("tps", tps).
90 | Dur("min", time.Duration(min)).
91 | Dur("max", time.Duration(max)).
92 | Dur("mean", time.Duration(mean)).
93 | Dur("median", time.Duration(median)).
94 | Msg("")
95 | }
96 |
97 | func newClients(amount int) []client.XClient {
98 | clis := make([]client.XClient, amount)
99 | discovery := client.NewPeer2PeerDiscovery(*transport+"@"+*address, "")
100 | for i := 0; i < amount; i++ {
101 | clis[i] = client.NewXClient("hello", client.Failfast, client.RoundRobin, discovery, option)
102 | call(clis[i]) // warmup
103 | }
104 | return clis
105 | }
106 |
107 | func call(cli client.XClient) {
108 | rsp := &model.Message{}
109 | err := cli.Call(context.Background(), "Hello", model.Example, rsp)
110 | ulog.FatalIfError(err)
111 | if !model.CheckExample(rsp) {
112 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/rpcx-v5/codec/codec.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "github.com/smallnest/rpcx/v5/protocol"
5 | "github.com/smallnest/rpcx/v5/share"
6 | )
7 |
8 | var (
9 | JSONiter protocol.SerializeType = 11
10 | )
11 |
12 | func Register() {
13 | share.RegisterCodec(JSONiter, &jsoniterCodec{})
14 | }
15 |
--------------------------------------------------------------------------------
/rpcx-v5/codec/jsoniter.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | jsoniter "github.com/json-iterator/go"
5 | )
6 |
7 | var json = jsoniter.Config{
8 | EscapeHTML: true,
9 | SortMapKeys: true,
10 | ValidateJsonRawMessage: true,
11 | UseNumber: true,
12 | }.Froze()
13 |
14 | type jsoniterCodec struct{}
15 |
16 | func (c jsoniterCodec) Encode(i interface{}) ([]byte, error) {
17 | return json.Marshal(i)
18 | }
19 |
20 | func (c jsoniterCodec) Decode(data []byte, i interface{}) error {
21 | return json.Unmarshal(data, i)
22 | }
23 |
--------------------------------------------------------------------------------
/rpcx-v5/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/rpcx-v5
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gxxgle/go-utils v1.2.4
7 | github.com/json-iterator/go v1.1.10
8 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
9 | github.com/montanaflynn/stats v0.6.3
10 | github.com/phuslu/log v1.0.44
11 | github.com/smallnest/rpcx/v5 v5.7.8
12 | github.com/tjfoc/gmsm v1.3.2 // indirect
13 | )
14 |
15 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
16 |
17 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
18 |
--------------------------------------------------------------------------------
/rpcx-v5/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 |
7 | "github.com/micro-svc/go-rpc-framework-benchmark/model/standard/model"
8 | "github.com/micro-svc/go-rpc-framework-benchmark/rpcx-v5/codec"
9 | "github.com/micro-svc/go-rpc-framework-benchmark/rpcx-v5/tls"
10 |
11 | ulog "github.com/gxxgle/go-utils/log"
12 | "github.com/phuslu/log"
13 | xlog "github.com/smallnest/rpcx/v5/log"
14 | "github.com/smallnest/rpcx/v5/server"
15 | )
16 |
17 | var (
18 | // flags
19 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
20 | transport = flag.String("transport", "tcp", "server transport [tcp, quic, utp]")
21 |
22 | options []server.OptionFn
23 | )
24 |
25 | type Server struct{}
26 |
27 | func (s *Server) Hello(ctx context.Context, req *model.Message, rsp *model.Message) error {
28 | *rsp = *req
29 | return nil
30 | }
31 |
32 | func main() {
33 | flag.Parse()
34 | xlog.SetDummyLogger()
35 | codec.Register()
36 |
37 | switch *transport {
38 | case "tcp":
39 | case "quic", "utp":
40 | cfg, err := tls.NewConfig()
41 | ulog.FatalIfError(err)
42 | options = append(options, server.WithTLSConfig(cfg))
43 | default:
44 | log.Fatal().Msg("flag transport not support")
45 | }
46 |
47 | server.UsePool = true
48 | srv := server.NewServer(options...)
49 | ulog.FatalIfError(srv.RegisterName("hello", new(Server), ""))
50 | log.Info().Str("address", *address).Msg("server running")
51 | ulog.FatalIfError(srv.Serve(*transport, *address))
52 | }
53 |
--------------------------------------------------------------------------------
/rpcx-v5/tls/tls.go:
--------------------------------------------------------------------------------
1 | package tls
2 |
3 | import (
4 | "crypto/tls"
5 | )
6 |
7 | var (
8 | cert = []byte(`-----BEGIN CERTIFICATE-----
9 | MIIDnzCCAoegAwIBAgIJAL6kbc7aqvDlMA0GCSqGSIb3DQEBCwUAMGYxCzAJBgNV
10 | BAYTAmNuMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRMwEQYD
11 | VQQKDApjb2xvYnUuY29tMR4wHAYJKoZIhvcNAQkBFg9kZW1vQGNvbG9idS5jb20w
12 | HhcNMTYwNjA3MDMwMDUzWhcNMjYwNjA1MDMwMDUzWjBmMQswCQYDVQQGEwJjbjEQ
13 | MA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzETMBEGA1UECgwKY29s
14 | b2J1LmNvbTEeMBwGCSqGSIb3DQEJARYPZGVtb0Bjb2xvYnUuY29tMIIBIjANBgkq
15 | hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwlweEl1KTHoZNgKjUEHquBmcZbYiJpXL
16 | Ak0jHzIC6m6n27ZbK/nKeqcjdFfWkSmgk9j0U0Z5QEQ94VhkMmKd2d17X4PXb6P2
17 | w2nWm5GqeqtJ/uvLkHEXqKjcZ1egDHmb9mcn5KElDF+p5XikBJ3C59E6q7OHnC2Q
18 | rN5clXwUkeM2I32k9g93AMvio70gtB3t+FnjjwSMwj/1vPxe9MCQA0S8tBYLhDNb
19 | Cgr5YlXeLdv8ByumDMpyNHCOwMjkHYcm8TiL4xr9liIOFR2Ebf2qOZT5+WtrfDwC
20 | Y/13Hw/pV3YXNqwLdIjZj5U1KNU/vwV2jbasLVonBODaDIVAlq6VTwIDAQABo1Aw
21 | TjAdBgNVHQ4EFgQUxnjZC69k3kWntD67RyLbFgXeiCkwHwYDVR0jBBgwFoAUxnjZ
22 | C69k3kWntD67RyLbFgXeiCkwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOC
23 | AQEAORdZmEXLkZdq3V+r1gej+u10xaHL8yN5cYqeYhzd2/yvd9AdvZV3RxP8m9o9
24 | 7kFu//0NV0lV/aMQ5LSSeDdvqLBNF9m0Fz9ucZkBkIxipcQJ3jy7fmeWnehf2dsA
25 | gkdtyDZK5B+AdCaaiyvxvSFjYQ36XDRW+mqZX76DtSsQIZKh5USk8T8ah0Z2SLaU
26 | x5x8iO6AQGpOVao8BydjRno6yVbNJ60VQmyV/XMxCbodHoO0soqaJVwMIt59yzCK
27 | VTNrTgTYk1LJM4842z7GC7eZi5VfJf4H7c2sdhulfixAnInxnKnrqioCyleleLYq
28 | JA9QMjTswvxpVDc/7djr7ZH5Iw==
29 | -----END CERTIFICATE-----`)
30 | key = []byte(`-----BEGIN RSA PRIVATE KEY-----
31 | MIIEpAIBAAKCAQEAwlweEl1KTHoZNgKjUEHquBmcZbYiJpXLAk0jHzIC6m6n27Zb
32 | K/nKeqcjdFfWkSmgk9j0U0Z5QEQ94VhkMmKd2d17X4PXb6P2w2nWm5GqeqtJ/uvL
33 | kHEXqKjcZ1egDHmb9mcn5KElDF+p5XikBJ3C59E6q7OHnC2QrN5clXwUkeM2I32k
34 | 9g93AMvio70gtB3t+FnjjwSMwj/1vPxe9MCQA0S8tBYLhDNbCgr5YlXeLdv8Byum
35 | DMpyNHCOwMjkHYcm8TiL4xr9liIOFR2Ebf2qOZT5+WtrfDwCY/13Hw/pV3YXNqwL
36 | dIjZj5U1KNU/vwV2jbasLVonBODaDIVAlq6VTwIDAQABAoIBAQCccmveWmuhDbNX
37 | b14oDzcxg6QJl0b8Ch1EN/UqeiYwsGu7hpjy1/mtKvZ2AvJ7HKfd+ogItDSLj6LD
38 | zlOw3wKyyP4zDzwN6EyMVZhi1ykD3vcMFJ9iIDZsE6h+IuF4yTbKa5SIC1J4GFT4
39 | mlhkQt0NnrR8W2GwTFklXUPnK96e4n9gzSOuYmPTRHc+5i/Y7QcmmexgmTGC9ZDd
40 | LgpZF6vjocCJk71ALWOOtfMRGZ4qy3t8LM+5S7yrto0WN3tOH49j4EhAUHQ1QKk1
41 | NLlawgOwMSiGJo9OIvnnQrIrWpIXOTjNnJf2t67RjhjKlz2BsbdDL2XsxYE6fXy7
42 | yO26g3txAoGBAOgj/8GXYeeqXhBhRP1gnvleJGV6GjaWPORrbDikjX/dw3hn12zP
43 | FMNGCcaveRUHay69x/2dTdGa91XM8+Nkxp9l94PwX8vrQtLXv0pdHg/WvgNm2/NL
44 | ocRDYFcBInvVU9OurDTeysHp15pCfzbzL9Xqu11io49oYrGTbnDFqdy3AoGBANZW
45 | CzqGDuHDoBlHVuNHPkHshZkNEgquUu+bfvuULqvfHKXd4Xsc0iIiF98VqYd0q2cr
46 | t8HufgA3jDZkLTMz1ZuJD+kMJtJjkMYc8mKUYzRAwlrNAxXZZ+x7j+eCOfqFqkht
47 | tHdt/Dhg4TfXSZEpDQl6JGGibFWpZdWyiIygk6QpAoGADIg0/GyBupH19ghqQwt+
48 | SZQNfvTyiVPfAa3S79PiV67PKKbieFtNkaUsTe+XJ814udNBAq4FT3WAOL+Rwwrj
49 | tiM5E4Rch5OxzdQb3ZXdU5mSNHjPEvlYSFIi10+WXv0LQ3hAj7IfXAyVuRwx5fOG
50 | 95NufgIfNlS6Tz/YTHo/S00CgYB7eehQF8jdAPDrfzCsEG/qVJMGgAQjyReYV9FI
51 | p65Xb1o2p+NtJEZeSZvM9dLLaUgRUJs1Curlvxp0fzOW6salKsnSrSB0Vc2ihCyS
52 | 4/9eq+wLmU2sualoECrv6RLzoytdPG4Z22/RHubAX7NPAAI1snrhEEwu8T7sZETM
53 | FvnXOQKBgQCE7tOk429mRvEM0qQTK7fFrI0uYTguebAiiOr3zApyabg0yaUHHTub
54 | BeX+dKhU/MajSydY5KBuLGECk5R+n3eKMXoQ1dLKWRFS4qMZf35mKTM3IHiJNLJs
55 | 2MAWfitpkt9Aanuw/Iqisw0M1EcmhXokTFzue2KVej5P8npLaSIxaA==
56 | -----END RSA PRIVATE KEY-----`)
57 | )
58 |
59 | func NewConfig() (*tls.Config, error) {
60 | x509, err := tls.X509KeyPair(cert, key)
61 | if err != nil {
62 | return nil, err
63 | }
64 |
65 | return &tls.Config{Certificates: []tls.Certificate{x509}}, nil
66 | }
67 |
--------------------------------------------------------------------------------
/scripts/benchmark.list:
--------------------------------------------------------------------------------
1 | gin|gin|bin/gin-srv|bin/gin-cli --transport=http --codec=json
2 | grpc|grpc|bin/grpc-srv|bin/grpc-cli --transport=grpc --codec=protobuf
3 | rpcx-01|rpcx-v5|bin/rpcx-v5-srv --transport=tcp|bin/rpcx-v5-cli --transport=tcp --codec=json
4 | rpcx-02|rpcx-v5|bin/rpcx-v5-srv --transport=tcp|bin/rpcx-v5-cli --transport=tcp --codec=protobuf
5 | rpcx-03|rpcx-v5|bin/rpcx-v5-srv --transport=tcp|bin/rpcx-v5-cli --transport=tcp --codec=msgpack
6 | rpcx-04|rpcx-v5|bin/rpcx-v5-srv --transport=tcp|bin/rpcx-v5-cli --transport=tcp --codec=jsoniter
7 | go-micro-01|go-micro-v2|bin/go-micro-v2-srv --transport=grpc|bin/go-micro-v2-cli --transport=grpc --codec=json
8 | go-micro-02|go-micro-v2|bin/go-micro-v2-srv --transport=grpc|bin/go-micro-v2-cli --transport=grpc --codec=protobuf
9 | go-micro-03|go-micro-v2|bin/go-micro-v2-srv --transport=http|bin/go-micro-v2-cli --transport=http --codec=json
10 | go-micro-04|go-micro-v2|bin/go-micro-v2-srv --transport=http|bin/go-micro-v2-cli --transport=http --codec=protobuf
11 | twirp-01|twirp-v7|bin/twirp-v7-srv|bin/twirp-v7-cli --transport=http --codec=json
12 | twirp-02|twirp-v7|bin/twirp-v7-srv|bin/twirp-v7-cli --transport=http --codec=protobuf
13 | erpc-01|erpc-v6|bin/erpc-v6-srv --codec=json|bin/erpc-v6-cli --transport=tcp --codec=json
14 | erpc-02|erpc-v6|bin/erpc-v6-srv --codec=protobuf|bin/erpc-v6-cli --transport=tcp --codec=protobuf
15 | arpc-01|arpc|bin/arpc-srv --codec=json|bin/arpc-cli --transport=tcp --codec=json
16 | arpc-02|arpc|bin/arpc-srv --codec=protobuf|bin/arpc-cli --transport=tcp --codec=protobuf
17 | arpc-03|arpc|bin/arpc-srv --codec=jsoniter|bin/arpc-cli --transport=tcp --codec=jsoniter
18 |
--------------------------------------------------------------------------------
/scripts/benchmark.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | file="./scripts/benchmark.list"
6 |
7 | if [ ! -f "$file" ]; then
8 | echo "$file not found"
9 | exit 1
10 | fi
11 |
12 | touch go-rpc-framework-benchmark.result
13 | echo "" >go-rpc-framework-benchmark.result
14 |
15 | while IFS="|" read -r name package server client; do
16 | pm2 start "${server}" --name ${name}-benchmark
17 | sleep 3s
18 | echo "\n### ${name}\n\n\`\`\`sh\n$ ${client}" >>go-rpc-framework-benchmark.result
19 | ${client} >>go-rpc-framework-benchmark.result 2>&1
20 | echo "\`\`\`" >>go-rpc-framework-benchmark.result
21 | pm2 del ${name}-benchmark
22 | sleep 15s # wait resource release
23 | done <"$file"
24 |
--------------------------------------------------------------------------------
/scripts/build.list:
--------------------------------------------------------------------------------
1 | arpc
2 | erpc-v6
3 | gin
4 | go-micro-v2
5 | grpc
6 | rpcx-v5
7 | twirp-v7
8 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | file="./scripts/build.list"
6 |
7 | if [ ! -f "$file" ]; then
8 | echo "$file not found"
9 | exit 1
10 | fi
11 |
12 | _gomod_tidy() {
13 | while IFS="|" read -r name; do
14 | echo "go mod tidy $name"
15 | cd $name
16 | rm -f go.sum && go mod tidy
17 | cd ..
18 | done <"$file"
19 | }
20 |
21 | _lint() {
22 | while IFS="|" read -r name; do
23 | echo "lint $name"
24 | cd $name
25 | golangci-lint run
26 | cd ..
27 | done <"$file"
28 | }
29 |
30 | _build() {
31 | while IFS="|" read -r name; do
32 | echo "build $name"
33 | cd $name
34 | go build -tags "quic utp" -ldflags "-w -s" -o ../bin/$name-srv server/*.go
35 | go build -tags "quic utp" -ldflags "-w -s" -o ../bin/$name-cli client/*.go
36 | cd ..
37 | done <"$file"
38 | }
39 |
40 | case "$1" in
41 | gomod-tidy)
42 | _gomod_tidy
43 | ;;
44 | lint)
45 | _lint
46 | ;;
47 | build)
48 | _build
49 | ;;
50 | *)
51 | echo "illegal command: $1"
52 | exit 1
53 | ;;
54 | esac
55 |
--------------------------------------------------------------------------------
/scripts/gen-proto.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | set -e
4 |
5 | # standard
6 | protoc --go_out=paths=source_relative:model/standard model/model.proto
7 |
8 | # grpc
9 | protoc --go_out=paths=source_relative:model/grpc --go-grpc_out=paths=source_relative:model/grpc model/model.proto
10 |
11 | # go-micro/v2
12 | protoc --go_out=paths=source_relative:model/micro-v2 --micro_out=paths=source_relative:model/micro-v2 model/model.proto
13 |
14 | # twirp/v7
15 | protoc --go_out=paths=source_relative:model/twirp-v7 --twirp_out=paths=source_relative:model/twirp-v7 model/model.proto
16 |
--------------------------------------------------------------------------------
/tools/gen/docs.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Go RPC framework benchmark
8 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/tools/gen/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "io/ioutil"
6 | "strings"
7 | "time"
8 |
9 | "github.com/flosch/pongo2/v4"
10 | "github.com/gxxgle/go-utils/json"
11 | ulog "github.com/gxxgle/go-utils/log"
12 | )
13 |
14 | var (
15 | resultFilename = "../go-rpc-framework-benchmark.result"
16 | docsTmplFile = "./gen/docs.tmpl"
17 | readmeTmplFile = "./gen/readme.tmpl"
18 | colors = []string{"#264653", "#2a9d8f", "#e9c46a", "#f4a261", "#e76f51"}
19 | gen = flag.String("gen", "docs", "gen result type. docs, readme")
20 | )
21 |
22 | type Result struct {
23 | Name string
24 | Package string
25 | Transport string
26 | Codec string
27 | TPS int64
28 | Color string
29 | Detail string
30 | }
31 |
32 | func (r Result) OK() bool {
33 | return len(r.Name) > 0 && len(r.Package) > 0 && r.TPS > 0
34 | }
35 |
36 | func main() {
37 | flag.Parse()
38 | ulog.ColorConsole()
39 | data, err := ioutil.ReadFile(resultFilename)
40 | ulog.FatalIfError(err)
41 |
42 | var (
43 | current Result
44 | results = []Result{}
45 | )
46 |
47 | for _, line := range strings.Split(string(data), "\n") {
48 | line = strings.TrimSpace(line)
49 | if len(line) == 0 {
50 | if current.OK() {
51 | results = append(results, current)
52 | current = Result{}
53 | }
54 |
55 | continue
56 | }
57 |
58 | // name
59 | if strings.HasPrefix(line, "###") {
60 | current.Name = strings.TrimPrefix(line, "###")
61 | current.Name = strings.TrimSpace(current.Name)
62 | continue
63 | }
64 |
65 | if strings.HasPrefix(line, "$ bin/") {
66 | line = strings.TrimPrefix(line, "$ bin/")
67 | for _, str := range strings.Split(line, " ") {
68 | str = strings.TrimSpace(str)
69 | // package name
70 | if strings.HasSuffix(str, "-cli") {
71 | current.Package = strings.TrimSuffix(str, "-cli")
72 | }
73 | // transport
74 | if strings.HasPrefix(str, "--transport=") {
75 | current.Transport = strings.TrimPrefix(str, "--transport=")
76 | }
77 | // codec
78 | if strings.HasPrefix(str, "--codec=") {
79 | current.Codec = strings.TrimPrefix(str, "--codec=")
80 | }
81 | }
82 | continue
83 | }
84 |
85 | // tps
86 | if strings.HasPrefix(line, "{") {
87 | current.Detail += line + "\n"
88 | current.TPS = json.GetFromString(line, "tps").Int()
89 | continue
90 | }
91 | }
92 |
93 | switch *gen {
94 | case "docs":
95 | genDocs(results)
96 | case "readme":
97 | genReadme(results)
98 | }
99 | }
100 |
101 | func setColor(rsts []Result) {
102 | idx := 0
103 | seted := make(map[string]string)
104 | for i, r := range rsts {
105 | if clr := seted[r.Package]; len(clr) > 0 {
106 | rsts[i].Color = clr
107 | continue
108 | }
109 |
110 | clr := colors[idx%len(colors)]
111 | rsts[i].Color = clr
112 | seted[r.Package] = clr
113 | idx++
114 | }
115 | }
116 |
117 | func genDocs(rsts []Result) {
118 | setColor(rsts)
119 | // sort.Slice(rsts, func(i, j int) bool { return rsts[i].TPS > rsts[j].TPS })
120 | tmpl, err := pongo2.FromFile(docsTmplFile)
121 | ulog.FatalIfError(err)
122 | data, err := tmpl.Execute(pongo2.Context{
123 | "UpdatedAt": time.Now().Format("2006-01-02"),
124 | "Results": rsts,
125 | })
126 | ulog.FatalIfError(err)
127 | println(data)
128 | }
129 |
130 | func genReadme(rsts []Result) {
131 | tmpl, err := pongo2.FromFile(readmeTmplFile)
132 | ulog.FatalIfError(err)
133 | data, err := tmpl.Execute(pongo2.Context{
134 | "UpdatedAt": time.Now().Format("2006-01-02"),
135 | "Results": rsts,
136 | })
137 | ulog.FatalIfError(err)
138 | println(data)
139 | }
140 |
--------------------------------------------------------------------------------
/tools/gen/readme.tmpl:
--------------------------------------------------------------------------------
1 | [](https://micro-svc.github.io/go-rpc-framework-benchmark/)
2 |
3 | ## 测试结果
4 |
5 | | name | package | transport | codec | tps |
6 | | ---- | ------- | --------- | ----- | --: |
7 | {% for Result in Results %}| {{ Result.Name }} | {{ Result.Package }} | `{{ Result.Transport }}` | `{{ Result.Codec }}` | {{ Result.TPS }} |
8 | {% endfor %}
9 |
10 | > 机器配置:4 Cores 16G Memory
11 |
12 | > 更新时间:{{ UpdatedAt }}
13 |
14 | ## 测试列表
15 |
16 | - [gin](https://github.com/gin-gonic/gin):WEB 框架,用来和 RPC 做对比
17 | - [grpc](https://github.com/grpc/grpc-go):跨语言 RPC 框架
18 | - [rpcx/v5](https://github.com/smallnest/rpcx):RPC 框架,自带微服务组件
19 | - [go-micro/v2](https://github.com/micro/go-micro):RPC 框架,自带微服务组件
20 | - [twirp/v7](https://github.com/twitchtv/twirp):RPC 框架
21 | - [erpc-v6](https://github.com/henrylee2cn/erpc):RPC 框架
22 | - [arpc](https://github.com/lesismal/arpc):RPC 框架
23 |
24 | ## 详细结果
25 |
26 | {% for Result in Results %}### {{ Result.Name }}
27 |
28 | ```sh
29 | $ bin/{{ Result.Package }}-cli --transport={{ Result.Transport }} --codec={{ Result.Codec }}
30 | {{ Result.Detail|safe }}```
31 |
32 | {% endfor %}
33 |
34 | ## TODO
35 |
36 | - [go-chassis](https://github.com/go-chassis/go-chassis)
37 | - [TarsGo](https://github.com/TarsCloud/TarsGo)
38 | - [go-zero](https://github.com/tal-tech/go-zero)
39 |
--------------------------------------------------------------------------------
/tools/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/model
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/flosch/pongo2/v4 v4.0.0
7 | github.com/gxxgle/go-utils v1.2.4
8 | )
9 |
--------------------------------------------------------------------------------
/tools/go.sum:
--------------------------------------------------------------------------------
1 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
2 | gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
5 | github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
6 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
7 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
8 | github.com/bsm/redislock v0.6.0/go.mod h1:3Kgu+cXw0JrkZ5pmY/JbcFpixGZ5M9v9G2PGWYqku+k=
9 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
10 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
11 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
12 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
14 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15 | github.com/denisenkom/go-mssqldb v0.0.0-20200206145737-bbfc9a55622e/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
16 | github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
17 | github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
18 | github.com/doug-martin/goqu/v9 v9.10.0/go.mod h1:zx5/YoiHux3wn7477GnI3PXzKyKpLKu32Teo9U4yCFE=
19 | github.com/flosch/pongo2/v4 v4.0.0 h1:6eZe8NSNxtTTGwXgJqqXiiLEDAj7CvkwiYrZFQRW6cQ=
20 | github.com/flosch/pongo2/v4 v4.0.0/go.mod h1:B5ObFANs/36VwxxlgKpdchIJHMvHB562PW+BWPhwZD8=
21 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
22 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
23 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
24 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
25 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
26 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
27 | github.com/go-playground/validator/v10 v10.3.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
28 | github.com/go-redis/redis/v8 v8.1.0/go.mod h1:isLoQT/NFSP7V67lyvM9GmdvLdyZ7pEhsXvvyQtnQTo=
29 | github.com/go-redis/redis/v8 v8.2.2/go.mod h1:ysgGY09J/QeDYbu3HikWEIPCwaeOkuNoTgKayTEaEOw=
30 | github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU=
31 | github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
32 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
33 | github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
34 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
35 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
36 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
37 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
38 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
39 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
40 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
41 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
42 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
43 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
44 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
45 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
46 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
47 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
48 | github.com/gxxgle/go-utils v1.2.4 h1:dSnAtiljAwia9NicpkAos0dGm2zCdZ1ynshdAQX2G0g=
49 | github.com/gxxgle/go-utils v1.2.4/go.mod h1:iTlqDy8BwLq/cfIXBRobf8Oa+c+4hMuG1Uq5GanxIAk=
50 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
51 | github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
52 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
53 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
54 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
55 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
56 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
57 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
58 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
59 | github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
60 | github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
61 | github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
62 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
63 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
64 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
65 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
66 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
67 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
68 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
69 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
70 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
71 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
72 | github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
73 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
74 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
75 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
76 | github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
77 | github.com/patrickmn/go-cache v0.0.0-20191004192108-46f407853014/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
78 | github.com/phuslu/log v1.0.44 h1:Qv/8fAyKqKTAq7D21NLLee+OlVW4jFx96Du5qzjXEmc=
79 | github.com/phuslu/log v1.0.44/go.mod h1:BSFbUpGk1VoydawCb63LBAGwv/q6qbaItgrBpehbPsk=
80 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
81 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
82 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
83 | github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
84 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
85 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
86 | github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
87 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
88 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
89 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
90 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
91 | github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
92 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
93 | github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
94 | github.com/tidwall/gjson v1.6.1 h1:LRbvNuNuvAiISWg6gxLEFuCe72UKy5hDqhxW/8183ws=
95 | github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
96 | github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
97 | github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
98 | github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
99 | github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
100 | github.com/urfave/cli/v2 v2.2.0 h1:JTTnM6wKzdA0Jqodd966MVj4vWbbquZykeX1sKbe2C4=
101 | github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
102 | github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
103 | go.opentelemetry.io/otel v0.11.0/go.mod h1:G8UCk+KooF2HLkgo8RHX9epABH/aRGYET7gQOqBVdB0=
104 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
105 | golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
106 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
107 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
108 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
109 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
110 | golang.org/x/exp v0.0.0-20200908183739-ae8ad444f925/go.mod h1:1phAWC201xIgDyaFpmDeZkgf70Q4Pd/CNqfRtVPtxNw=
111 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
112 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
113 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
114 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
115 | golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
116 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
117 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
118 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
119 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
120 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
121 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
122 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
123 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
124 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
125 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
126 | golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
127 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
128 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
129 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
130 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
131 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
132 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
133 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
134 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
135 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
136 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
137 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
138 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
139 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
140 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
141 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
142 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
143 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
144 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
145 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
146 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
147 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
148 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
149 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
150 | google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
151 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
152 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
153 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
154 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
155 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
156 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
157 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
158 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
159 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
160 | gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
161 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
162 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
163 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
164 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
165 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
166 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
167 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
168 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
169 | xorm.io/builder v0.3.7/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
170 | xorm.io/xorm v1.0.5/go.mod h1:uF9EtbhODq5kNWxMbnBEj8hRRZnlcNSz2t2N7HW/+A4=
171 |
--------------------------------------------------------------------------------
/twirp-v7/client/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "net"
7 | "net/http"
8 | "net/http/cookiejar"
9 | "sync"
10 | "time"
11 |
12 | "github.com/micro-svc/go-rpc-framework-benchmark/model/twirp-v7/model"
13 |
14 | ulog "github.com/gxxgle/go-utils/log"
15 | "github.com/montanaflynn/stats"
16 | "github.com/phuslu/log"
17 | "github.com/twitchtv/twirp"
18 | )
19 |
20 | var (
21 | // flags
22 | clients = flag.Int("clients", 100, "concurrency client amount")
23 | requests = flag.Int("requests", 1000, "request amount per client")
24 | address = flag.String("addr", "127.0.0.1:5678", "server address")
25 | transport = flag.String("transport", "http", "server transport [http]")
26 | codec = flag.String("codec", "json", "server codec [json, protobuf]")
27 |
28 | newClient func(string, model.HTTPClient, ...twirp.ClientOption) model.Hello
29 | )
30 |
31 | func main() {
32 | flag.Parse()
33 |
34 | switch *transport {
35 | case "http":
36 | default:
37 | log.Fatal().Msg("flag transport not support")
38 | }
39 |
40 | switch *codec {
41 | case "json":
42 | newClient = model.NewHelloJSONClient
43 | case "protobuf":
44 | newClient = model.NewHelloProtobufClient
45 | default:
46 | log.Fatal().Msg("flag codec not support")
47 | }
48 |
49 | var (
50 | wg sync.WaitGroup
51 | total = *clients * *requests
52 | clis = newClients(*clients)
53 | durations = make([]time.Duration, total)
54 | )
55 |
56 | log.Info().Int("clients", *clients).Int("requests", *requests).Int("total", total).Msg("")
57 |
58 | start := time.Now()
59 | wg.Add(total)
60 | for i := range clis {
61 | go func(i int) {
62 | cli := clis[i]
63 | for j := 0; j < *requests; j++ {
64 | start := time.Now()
65 | call(cli)
66 | durations[i**requests+j] = time.Since(start)
67 | wg.Done()
68 | }
69 | }(i)
70 | }
71 | wg.Wait()
72 |
73 | tps := int64(time.Duration(total) * time.Second / time.Since(start))
74 | s := stats.LoadRawData(durations)
75 | min, _ := s.Min()
76 | max, _ := s.Max()
77 | mean, _ := s.Mean()
78 | median, _ := s.Median()
79 | log.Info().
80 | Int64("tps", tps).
81 | Dur("min", time.Duration(min)).
82 | Dur("max", time.Duration(max)).
83 | Dur("mean", time.Duration(mean)).
84 | Dur("median", time.Duration(median)).
85 | Msg("")
86 | }
87 |
88 | func newClients(amount int) []model.Hello {
89 | clis := make([]model.Hello, amount)
90 | for i := 0; i < amount; i++ {
91 | clis[i] = newClient("http://"+*address, newHTTPClient())
92 | call(clis[i]) // warmup
93 | }
94 | return clis
95 | }
96 |
97 | func call(cli model.Hello) {
98 | rsp, err := cli.Hello(context.Background(), model.Example)
99 | ulog.FatalIfError(err)
100 | if !model.CheckExample(rsp) {
101 | log.Fatal().Str("response", rsp.String()).Msg("response not match")
102 | }
103 | }
104 |
105 | func newHTTPClient() *http.Client {
106 | jar, _ := cookiejar.New(nil)
107 | transport := &http.Transport{
108 | Proxy: http.ProxyFromEnvironment,
109 | DialContext: (&net.Dialer{
110 | Timeout: 30 * time.Second,
111 | KeepAlive: 30 * time.Second,
112 | DualStack: true,
113 | }).DialContext,
114 | MaxIdleConns: 100,
115 | IdleConnTimeout: 90 * time.Second,
116 | TLSHandshakeTimeout: 10 * time.Second,
117 | ExpectContinueTimeout: 1 * time.Second,
118 | }
119 | return &http.Client{
120 | Jar: jar,
121 | Transport: transport,
122 | Timeout: 2 * time.Minute,
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/twirp-v7/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/micro-svc/go-rpc-framework-benchmark/twirp-v7
2 |
3 | go 1.14
4 |
5 | require (
6 | github.com/gxxgle/go-utils v1.2.4
7 | github.com/micro-svc/go-rpc-framework-benchmark/model v0.0.0
8 | github.com/montanaflynn/stats v0.6.3
9 | github.com/phuslu/log v1.0.44
10 | github.com/twitchtv/twirp v7.1.0+incompatible
11 | )
12 |
13 | replace github.com/micro-svc/go-rpc-framework-benchmark/model => ../model
14 |
15 | replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
16 |
--------------------------------------------------------------------------------
/twirp-v7/server/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "net/http"
7 |
8 | "github.com/micro-svc/go-rpc-framework-benchmark/model/twirp-v7/model"
9 |
10 | ulog "github.com/gxxgle/go-utils/log"
11 | "github.com/phuslu/log"
12 | )
13 |
14 | var (
15 | // flags
16 | address = flag.String("addr", "127.0.0.1:5678", "server listen address")
17 | )
18 |
19 | type Server struct{}
20 |
21 | func (s *Server) Hello(ctx context.Context, req *model.Message) (*model.Message, error) {
22 | rsp := &model.Message{}
23 | *rsp = *req
24 | return rsp, nil
25 | }
26 |
27 | func main() {
28 | flag.Parse()
29 |
30 | log.Info().Str("address", *address).Msg("server running")
31 | ulog.FatalIfError(http.ListenAndServe(*address, model.NewHelloServer(new(Server))))
32 | }
33 |
--------------------------------------------------------------------------------