├── .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 | [![result](./docs/images/result_20201014_222559.png)](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 | [![result](./docs/images/result_xxx.png)](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 | --------------------------------------------------------------------------------