├── .gitattributes
├── .gitignore
├── .golangci.yml
├── .goreleaser.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── benchmark
├── README.md
├── _backup
│ ├── README.md
│ ├── micro_client.go
│ ├── micro_mclient.go
│ └── micro_server.go
├── go.mod
├── grpc_grpc
│ ├── client.go
│ └── server.go
├── grpc_rpc
│ ├── client.go
│ └── server.go
├── proto
│ ├── micro_benchmark.micro.go
│ ├── micro_benchmark.pb.go
│ └── micro_benchmark.proto
├── service
│ ├── client.go
│ ├── codec.go
│ └── server.go
├── tcp_rpc
│ ├── client.go
│ └── server.go
└── utp_rpc
│ ├── client.go
│ └── server.go
├── cmd
└── micro
│ ├── main.go
│ ├── server
│ ├── auth.go
│ ├── plugin.go
│ ├── proxy.go
│ └── server.go
│ ├── service
│ ├── api
│ │ ├── api.go
│ │ └── auth.go
│ └── service.go
│ └── web
│ ├── README.md
│ ├── html
│ ├── assets
│ │ ├── jquery.min.js
│ │ ├── mu.css
│ │ ├── mu.js
│ │ └── mu.png
│ └── template.go
│ └── web.go
├── doc
└── img
│ ├── micro-istio.png
│ └── micro.jpg
├── go.mod
├── go.sum
├── manifests
├── buildpacks
│ └── project.toml
├── charts
│ ├── micro
│ │ ├── .helmignore
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ │ ├── NOTES.txt
│ │ │ ├── _helpers.tpl
│ │ │ ├── deployment.yaml
│ │ │ ├── hpa.yaml
│ │ │ ├── ingress.yaml
│ │ │ ├── service.yaml
│ │ │ ├── serviceaccount.yaml
│ │ │ └── tests
│ │ │ │ └── test-connection.yaml
│ │ └── values.yaml
│ └── service
│ │ ├── .helmignore
│ │ ├── Chart.yaml
│ │ ├── templates
│ │ ├── _helpers.tpl
│ │ ├── deployment.yaml
│ │ ├── hpa.yaml
│ │ ├── service.yaml
│ │ ├── serviceaccount.yaml
│ │ └── tests
│ │ │ └── test-connection.yaml
│ │ └── values.yaml
├── kubevela
│ └── application.yml
└── tekton
│ ├── README.md
│ ├── buildpacks
│ ├── pipeline.yml
│ └── run.yml
│ ├── kaniko
│ ├── pipeline.yml
│ └── run.yml
│ └── trigger-template.yml
├── pkg
├── file
│ ├── file_slice.go
│ └── walk_dir.go
├── service
│ └── service.go
└── tools
│ └── proto
│ └── batch.go
├── profile
└── profile.go
└── service
├── auth
└── noop
│ └── noop.go
└── greeting
├── .goreleaser.yml
├── Dockerfile
├── LICENSE
├── Makefile
├── README.md
├── conf
├── auth_key
├── auth_key.pub
├── casbin_model.conf
├── casbin_policy.csv
└── type.go
├── domain
├── model
│ └── greeting.go
├── repository
│ └── greeting.go
└── usecase
│ └── greeting.go
├── generate.go
├── main.go
├── proto
└── greeting
│ ├── greeting.pb.go
│ ├── greeting.pb.micro.go
│ └── greeting.proto
├── registry
└── container.go
├── repo
├── gorm
│ ├── .gitkeep
│ ├── db.go
│ └── greeting_repository.go
└── memory
│ └── memory.go
├── server
└── handler.go
└── service
└── greeting.go
/.gitattributes:
--------------------------------------------------------------------------------
1 | console/web/vue-admin-template/* linguist-vendored
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.dll
4 | *.so
5 | *.dylib
6 |
7 | go.sum
8 |
9 | # Test binary, build with `go test -c`
10 | *.test
11 |
12 | # Output of the go coverage tool, specifically when used with LiteIDE
13 | *.out
14 |
15 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
16 | .glide/
17 |
18 | .idea
19 | bin
20 | build
21 | dist
22 | vendor
23 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | # options for analysis running
2 | run:
3 | deadline: 10m
4 | skip-dirs:
5 | - benchmark
6 |
7 | # default is true. Enables skipping of directories:
8 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
9 | skip-dirs-use-default: true
10 |
11 | linters:
12 | disable-all: false
13 | enable-all: false
14 | enable:
15 | - megacheck
16 | - staticcheck
17 | - deadcode
18 | - varcheck
19 | - gosimple
20 | - unused
21 | - prealloc
22 | - scopelint
23 | - goimports
24 | - unconvert
25 | - govet
26 | - nakedret
27 | - structcheck
28 | - gosec
29 | disable:
30 | - maligned
31 | - interfacer
32 | - typecheck
33 | - dupl
34 | - gocritic
35 | linters-settings:
36 | govet:
37 | check-shadowing: true
38 | issues:
39 | # Excluding configuration per-path, per-linter, per-text and per-source
40 | exclude-rules:
41 | # Exclude known linters from partially hard-vendored code,
42 | # which is impossible to exclude via "nolint" comments.
43 | - text: "weak cryptographic primitive"
44 | linters:
45 | - gosec
46 | # Independently from option `exclude` we use default exclude patterns,
47 | # it can be disabled by this option. To list all
48 | # excluded by default patterns execute `golangci-lint run --help`.
49 | # Default value for this option is true.
50 | exclude-use-default: false
51 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | # This is an example goreleaser.yaml file with some sane defaults.
2 | # Make sure to check the documentation at http://goreleaser.com
3 | project_name: "micro"
4 | before:
5 | hooks:
6 | # You may remove this if you don't use go modules.
7 | - go mod download
8 |
9 | builds:
10 | - # ID of the build.
11 | # Defaults to the project name.
12 | id: "micro"
13 |
14 | # Path to project's (sub)directory containing Go code.
15 | # This is the working directory for the Go build command(s).
16 | # Default is `.`.
17 | dir: ./cmd/micro
18 |
19 | # Path to main.go file or main package.
20 | # Default is `.`.
21 | main: .
22 |
23 | # Binary name.
24 | # Can be a path (e.g. `bin/app`) to wrap the binary in a directory.
25 | # Default is the name of the project directory.
26 | binary: bin/{{ .ProjectName }}
27 |
28 | # Custom flags templates.
29 | # Default is empty.
30 | #flags:
31 |
32 | # Custom ldflags templates.
33 | # Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`.
34 | ldflags:
35 | - -s -w -X main.version={{ .Version }} -X main.commit={{ .ShortCommit }} -X main.date={{ .Date }} -X main.builtBy=hbchen.com
36 |
37 | # Custom environment variables to be set during the builds.
38 | # Default is empty.
39 | #env:
40 |
41 | # GOOS list to build for.
42 | # For more info refer to: https://golang.org/doc/install/source#environment
43 | # Defaults are darwin and linux.
44 | goos:
45 | - linux
46 | - darwin
47 | #- windows
48 |
49 | # GOARCH to build for.
50 | # For more info refer to: https://golang.org/doc/install/source#environment
51 | # Defaults are 386 and amd64.
52 | goarch:
53 | - amd64
54 | #- 386
55 | - arm64
56 |
57 | archives:
58 | - # ID of this archive.
59 | # Defaults to `default`.
60 | id: micro
61 |
62 | #replacements:
63 | # darwin: Darwin
64 | # linux: Linux
65 | # windows: Windows
66 | # 386: i386
67 | # amd64: x86_64
68 |
69 | # Archive name template.
70 | # Defaults:
71 | # - if format is `tar.gz`, `tar.xz`, `gz` or `zip`:
72 | # - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
73 | # - if format is `binary`:
74 | # - `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}`
75 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}"
76 | format: tar.gz
77 | files:
78 | - README.md
79 | - LICENSE
80 | - doc
81 | - manifests
82 |
83 | # Set to true, if you want all files in the archive to be in a single directory.
84 | # If set to true and you extract the archive 'goreleaser_Linux_arm64.tar.gz',
85 | # you get a folder 'goreleaser_Linux_arm64'.
86 | # If set to false, all files are extracted separately.
87 | # You can also set it to a custom folder name (templating is supported).
88 | # Default is false.
89 | wrap_in_directory: true
90 |
91 | # Disables the binary count check.
92 | # Default: false
93 | allow_different_binary_count: true
94 | snapshot:
95 | # Allows you to change the name of the generated snapshot
96 | #
97 | # Note that some pipes require this to be semantic version compliant (nfpm,
98 | # for example).
99 | #
100 | # Default is `{{ .Tag }}-SNAPSHOT-{{.ShortCommit}}`.
101 | name_template: '{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}'
102 | checksum:
103 | name_template: 'checksums.txt'
104 | changelog:
105 | sort: asc
106 | filters:
107 | exclude:
108 | - '^docs:'
109 | - '^test:'
110 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.10
2 |
3 | ADD dist/micro_linux_amd64_v1/bin/micro /opt/service/micro
4 |
5 | EXPOSE 8080
6 | WORKDIR /opt/service
7 |
8 | CMD [ "./micro"]
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Hobo Go
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 |
2 | GOOS=$(shell go env GOOS)
3 | GOARCH=$(shell go env GOARCH)
4 |
5 | ifeq ($(OS),Windows_NT)
6 | uname_S := Windows
7 | else
8 | uname_S := $(shell uname -s)
9 | endif
10 |
11 | # Goreleaser config
12 | ifeq ($(uname_S), Darwin)
13 | goreleaser_config = .goreleaser.yml
14 | else
15 | goreleaser_config = .goreleaser.yml
16 | endif
17 |
18 | .PHONY: micro
19 | micro:
20 | ./dist/micro_$(GOOS)_$(GOARCH)/bin/micro --profile starter-local server
21 |
22 | .PHONY: example
23 | example:
24 | ./dist/example_$(GOOS)_$(GOARCH)/bin/example --profile starter-local
25 |
26 | .PHONY: release
27 | release:
28 | goreleaser release --config $(goreleaser_config) --skip-validate --skip-publish --rm-dist
29 |
30 | .PHONY: snapshot
31 | snapshot:
32 | goreleaser release --config $(goreleaser_config) --skip-publish --snapshot --rm-dist
33 |
34 | .PHONY: test
35 | test:
36 | go test -race -cover -v ./cmd/... ./service/... ./profile/... ./pkg/...
37 |
38 | .PHONY: lint
39 | lint:
40 | golangci-lint run ./cmd/... ./service/... ./profile/... ./pkg/...
41 |
42 | .PHONY: pack_build
43 | pack_build:
44 | pack build micro \
45 | --builder paketobuildpacks/builder:tiny \
46 | --descriptor manifests/buildpacks/project.toml \
47 | --tag registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro:latest
48 |
49 | .PHONY: run
50 | run:
51 | docker run micro --profile starter-local server
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Micro [github.com/micro](http://github.com/micro)
2 |
3 | [](https://hbchen.slack.com/messages/CE68CJ60Z)
4 |
5 | 
6 |
7 | ## Local
8 |
9 | Build micro cmd
10 | ```shell script
11 | go build -o bin/micro cmd/micro/main.go
12 | ```
13 |
14 | Start registry & api with server runtime
15 | ```shell script
16 | ./bin/micro --profile starter-local server
17 | ```
18 |
19 | Start registry & api with service command
21 | Run registry service
22 | ```shell script
23 | ./bin/micro --profile starter-local service registry
24 | ```
25 |
26 | Run API service
27 | ```shell script
28 | ./bin/micro --profile starter-local service api
29 | ```
30 |
(ms)|中位
(ms)|最大
(ms)|最小
(ms)|P90
(ms)|P99
(ms)|TPS
24 | ---|---|---|---|---|---|---|---
25 | tcp+rpc|7.236|5.629|101.506|0.177|13.338|35.880|13192
26 | grpc+rpc|8.668|7.964|101.280|0.251|12.744|21.672|11166
27 | utp+rpc|11.824|11.600|53.183|0.204|15.575|21.334|8252
28 | grpc+grpc|8.924|8.181|134.434|0.286|13.211|22.973|10845
29 |
30 | > 在开始的测试中有个误区,`grpc`服务并不使用`transport`,包括`http`服务,`transport`仅在使用`go-micro`的`rpc`服务时有效
31 |
32 | ## Codec对比
33 | `transport`和`server`分别使用`tcp`和`rpc`对比不同`codec`性能,因为并发`100`时不同`codec`的失败率差别比较大,所以使用`50`并发,完成`10W`请求进行测试
34 |
35 | ### 结果对比
36 | 对比结果:`protobuf`>`proto-rpc`>`grpc`>`json`>`grpc+json`>`json-rpc`>`bsonrpc`
37 |
38 | CODEC|平均
(ms)|中位
(ms)|最大
(ms)|最小
(ms)|P90
(ms)|P99
(ms)|TPS
39 | -----|------|------|------|------|------|------|------
40 | grpc|3.937|2.979|90.004|0.180|7.184|19.355|12310
41 | grpc+json|6.085|4.694|149.861|0.342|10.365|31.837|8000
42 | protobuf|3.661|2.707|96.636|0.156|6.542|20.261|13150
43 | json|5.402|4.122|122.360|0.225|9.186|30.474|8896
44 | json-rpc|6.380|4.878|115.141|0.288|11.150|33.395|7631
45 | proto-rpc|3.692|2.729|101.010|0.180|6.701|19.454|13041
46 | bsonrpc|7.912|5.979|132.041|0.354|14.789|40.414|6145
47 |
48 | 默认`chdec`如下
49 | ```go
50 | DefaultCodecs = map[string]codec.NewCodec{
51 | "application/grpc": grpc.NewCodec,
52 | "application/grpc+json": grpc.NewCodec,
53 | "application/grpc+proto": grpc.NewCodec,
54 | "application/json": json.NewCodec,
55 | "application/json-rpc": jsonrpc.NewCodec,
56 | "application/protobuf": proto.NewCodec,
57 | "application/proto-rpc": protorpc.NewCodec,
58 | "application/octet-stream": raw.NewCodec,
59 | }
60 | ```
61 |
62 | 另外`go-plugins`提供三个`codec`插件,在`server`和`client`初始化时自定义添加
63 | ```go
64 | server.Codec("application/msgpackrpc", msgpackrpc.NewCodec),
65 | server.Codec("application/bsonrpc", bsonrpc.NewCodec),
66 | server.Codec("application/jsonrpc2", jsonrpc2.NewCodec),
67 |
68 | client.Codec("application/msgpackrpc", msgpackrpc.NewCodec),
69 | client.Codec("application/bsonrpc", bsonrpc.NewCodec),
70 | client.Codec("application/jsonrpc2", jsonrpc2.NewCodec),
71 | ```
72 |
73 | 实际测试时由于不同原因`raw` 、`msgpackrpc`和`jsonrpc2`运行失败未测试,`grpc+proto`与`grpc`实现一致未测试
74 |
75 | >raw.NewCodec
error:{"id":"go.micro.client.codec","code":500,"detail":"failed to write: field1:……
76 | msgpackrpc.NewCodec,需要实现EncodeMsg(*Writer)
error:{"id":"go.micro.client.codec","code":500,"detail":"Not encodable","status":"Internal Server Error"}
77 | jsonrpc2.NewCodec
error:{"id":"go.micro.client.transport","code":500,"detail":"EOF","status":"Internal Server Error"}
78 |
79 | ## 详细数据
80 |
81 | ### Transport + Server
82 |
83 | #### 测试命令
84 | ```bash
85 | $ go run server.go
86 | $ go run client.go -c 100 -n 100000
87 | ```
88 |
89 | **tcp + rpc**
90 | ```bash
91 | took (ms) : 7580
92 | sent requests : 100000
93 | received requests : 100000
94 | received requests_OK : 99999
95 | throughput (TPS) : 13192
96 |
97 | concurrency mean median max min p90 p99 TPS
98 | 100 7235584ns 5629000ns 101506000ns 177000ns 13338000ns 35880000ns 13192
99 | 100 7.236ms 5.629ms 101.506ms 0.177ms 13.338ms 35.880ms 13192
100 |
101 | ```
102 |
103 | **tcp + grpc**
104 | ```bash
105 | took (ms) : 9295
106 | sent requests : 100000
107 | received requests : 100000
108 | received requests_OK : 99994
109 | throughput (TPS) : 10758
110 |
111 | concurrency mean median max min p90 p99 TPS
112 | 100 9005583ns 8171000ns 110580000ns 300000ns 13510000ns 24317000ns 10758
113 | 100 9.006ms 8.171ms 110.580ms 0.300ms 13.510ms 24.317ms 10758
114 | ```
115 |
116 | **grpc + rpc**
117 | ```bash
118 | took (ms) : 8955
119 | sent requests : 100000
120 | received requests : 100000
121 | received requests_OK : 99999
122 | throughput (TPS) : 11166
123 |
124 | concurrency mean median max min p90 p99 TPS
125 | 100 8668191ns 7964000ns 101280000ns 251000ns 12744000ns 21672000ns 11166
126 | 100 8.668ms 7.964ms 101.280ms 0.251ms 12.744ms 21.672ms 11166
127 | ```
128 |
129 |
130 | **grpc + gRPC**
131 | ```bash
132 | took (ms) : 9220
133 | sent requests : 100000
134 | received requests : 100000
135 | received requests_OK : 99995
136 | throughput (TPS) : 10845
137 |
138 | concurrency mean median max min p90 p99 TPS
139 | 100 8924043ns 8181000ns 134434000ns 286000ns 13211000ns 22973000ns 10845
140 | 100 8.924ms 8.181ms 134.434ms 0.286ms 13.211ms 22.973ms 10845
141 | ```
142 |
143 |
144 | **utp + rpc**
145 | ```bash
146 | took (ms) : 12117
147 | sent requests : 100000
148 | received requests : 100000
149 | received requests_OK : 100000
150 | throughput (TPS) : 8252
151 |
152 | concurrency mean median max min p90 p99 TPS
153 | 100 11823520ns 11600000ns 53183000ns 204000ns 15575000ns 21334000ns 8252
154 | 100 11.824ms 11.600ms 53.183ms 0.204ms 15.575ms 21.334ms 8252
155 | ```
156 |
157 | **utp + grpc**
158 | ```bash
159 | took (ms) : 9367
160 | sent requests : 100000
161 | received requests : 100000
162 | received requests_OK : 99999
163 | throughput (TPS) : 10675
164 |
165 | concurrency mean median max min p90 p99 TPS
166 | 100 9063046ns 8213000ns 102052000ns 289000ns 13553000ns 23920000ns 10675
167 | 100 9.063ms 8.213ms 102.052ms 0.289ms 13.553ms 23.920ms 10675
168 | ```
169 |
170 | ### Codec
171 |
172 | #### 测试命令
173 |
174 | ```bash
175 | $ cd benchmark/tcp_rpc
176 | $ go run server.go
177 | $ go run client.go -c 50 -n 100000 -ct grpc
178 | $ go run client.go -c 50 -n 100000 -ct grpc+json
179 | $ go run client.go -c 50 -n 100000 -ct protobuf
180 | $ go run client.go -c 50 -n 100000 -ct json
181 | $ go run client.go -c 50 -n 100000 -ct json-rpc
182 | $ go run client.go -c 50 -n 100000 -ct proto-rpc
183 | $ go run client.go -c 50 -n 100000 -ct bsonrpc
184 | ```
185 |
186 | **grpc**
187 | ```bash
188 | took (ms) : 8123
189 | sent requests : 100000
190 | received requests : 100000
191 | received requests_OK : 100000
192 | throughput (TPS) : 12310
193 |
194 | concurrency mean median max min p90 p99 TPS
195 | 50 3936652ns 2979000ns 90004000ns 180000ns 7184000ns 19355000ns 12310
196 | 50 3.937ms 2.979ms 90.004ms 0.180ms 7.184ms 19.355ms 12310
197 | ```
198 |
199 | **grpc+json**
200 | ```bash
201 | took (ms) : 12500
202 | sent requests : 100000
203 | received requests : 100000
204 | received requests_OK : 99961
205 | throughput (TPS) : 8000
206 |
207 | concurrency mean median max min p90 p99 TPS
208 | 50 6084709ns 4694000ns 149861000ns 342000ns 10365000ns 31837000ns 8000
209 | 50 6.085ms 4.694ms 149.861ms 0.342ms 10.365ms 31.837ms 8000
210 | ```
211 |
212 | **protobuf**
213 | ```bash
214 | took (ms) : 7604
215 | sent requests : 100000
216 | received requests : 100000
217 | received requests_OK : 100000
218 | throughput (TPS) : 13150
219 |
220 | concurrency mean median max min p90 p99 TPS
221 | 50 3660854ns 2707000ns 96636000ns 156000ns 6542000ns 20261000ns 13150
222 | 50 3.661ms 2.707ms 96.636ms 0.156ms 6.542ms 20.261ms 13150
223 | ```
224 |
225 | **json**
226 | ```bash
227 | took (ms) : 11241
228 | sent requests : 100000
229 | received requests : 100000
230 | received requests_OK : 99922
231 | throughput (TPS) : 8896
232 |
233 | concurrency mean median max min p90 p99 TPS
234 | 50 5401532ns 4121500ns 122360000ns 225000ns 9186000ns 30474000ns 8896
235 | 50 5.402ms 4.122ms 122.360ms 0.225ms 9.186ms 30.474ms 8896
236 | ```
237 |
238 | **json-rpc**
239 | ```bash
240 | took (ms) : 13103
241 | sent requests : 100000
242 | received requests : 100000
243 | received requests_OK : 99971
244 | throughput (TPS) : 7631
245 |
246 | concurrency mean median max min p90 p99 TPS
247 | 50 6380308ns 4878000ns 115141000ns 288000ns 11150000ns 33395000ns 7631
248 | 50 6.380ms 4.878ms 115.141ms 0.288ms 11.150ms 33.395ms 7631
249 | ```
250 |
251 | **proto-rpc**
252 | ```bash
253 | took (ms) : 7668
254 | sent requests : 100000
255 | received requests : 100000
256 | received requests_OK : 99998
257 | throughput (TPS) : 13041
258 |
259 | concurrency mean median max min p90 p99 TPS
260 | 50 3692281ns 2729000ns 101010000ns 180000ns 6701000ns 19454000ns 13041
261 | 50 3.692ms 2.729ms 101.010ms 0.180ms 6.701ms 19.454ms 13041
262 | ```
263 |
264 | **bsonrpc**
265 | ```bash
266 | took (ms) : 16272
267 | sent requests : 100000
268 | received requests : 100000
269 | received requests_OK : 99933
270 | throughput (TPS) : 6145
271 |
272 | concurrency mean median max min p90 p99 TPS
273 | 50 7911887ns 5979000ns 132041000ns 354000ns 14789000ns 40414000ns 6145
274 | 50 7.912ms 5.979ms 132.041ms 0.354ms 14.789ms 40.414ms 6145
275 | ```
--------------------------------------------------------------------------------
/benchmark/_backup/README.md:
--------------------------------------------------------------------------------
1 | # Benchmark
2 | [rpcx-benchmark](https://github.com/rpcx-ecosystem/rpcx-benchmark)
3 |
4 | - MBP开发环境,不限流&不熔断
5 | ```bash
6 | go run micro_mclient.go -c 100 -n 50000
7 | 2018/06/27 17:48:00 Servers: [127.0.0.1:8972]
8 |
9 | 2018/06/27 17:51:28 concurrency: 100
10 | requests per client: 500
11 |
12 | 2018/06/27 17:51:32 took 3885 ms for 50000 requests
13 | 2018/06/27 17:51:32 sent requests : 50000
14 | 2018/06/27 17:51:32 received requests : 50000
15 | 2018/06/27 17:51:32 received requests_OK : 50000
16 | 2018/06/27 17:51:32 throughput (TPS) : 12870
17 | 2018/06/27 17:51:32 mean: 5376750 ns, median: 4338000 ns, max: 96008000 ns, min: 156000 ns, p99: 45684000 ns
18 | 2018/06/27 17:51:32 mean: 5 ms, median: 4 ms, max: 96 ms, min: 0 ms, p99: 45 ms
19 | ```
20 |
21 | - 限流
22 | ```go
23 | helloClient := client.NewClient(
24 | client.Transport(tcp.NewTransport()),
25 | client.ContentType("application/proto-rpc"),
26 | client.Selector(cache.NewSelector(cache.TTL(time.Second*3000))),
27 | client.Retries(1),
28 | client.PoolSize(100),
29 | client.RequestTimeout(time.Millisecond*1000),
30 | client.Wrap(ratelimit.NewClientWrapper(1)),
31 | )
32 | ```
33 | ```bash
34 | # client.Wrap(ratelimit.NewClientWrapper(1))
35 |
36 | ➜ benchmark git:(master) ✗ go run micro_client.go -c 50 -n 1000
37 | 2018/07/06 21:27:13 concurrency: 50
38 | requests per client: 20
39 |
40 | 2018/07/06 21:27:37 took 24018 ms for 1000 requests
41 | 2018/07/06 21:27:37 sent requests : 1000
42 | 2018/07/06 21:27:37 received requests : 1000
43 | 2018/07/06 21:27:37 received requests_OK : 1000
44 | 2018/07/06 21:27:37 throughput (TPS) : 41
45 | 2018/07/06 21:27:37 mean: 1000159226 ns, median: 999944000 ns, max: 1009774000 ns, min: 992741000 ns, p99: 1009760500 ns
46 | 2018/07/06 21:27:37 mean: 1000 ms, median: 999 ms, max: 1009 ms, min: 992 ms, p99: 1009 ms
47 |
48 | # client.Wrap(ratelimit.NewClientWrapper(10))
49 | ➜ benchmark git:(master) ✗ go run micro_client.go -c 50 -n 1000
50 | 2018/07/06 21:27:53 concurrency: 50
51 | requests per client: 20
52 |
53 | 2018/07/06 21:27:56 took 2414 ms for 1000 requests
54 | 2018/07/06 21:27:56 sent requests : 1000
55 | 2018/07/06 21:27:56 received requests : 1000
56 | 2018/07/06 21:27:56 received requests_OK : 1000
57 | 2018/07/06 21:27:56 throughput (TPS) : 414
58 | 2018/07/06 21:27:56 mean: 100238663 ns, median: 99990000 ns, max: 109895000 ns, min: 93010000 ns, p99: 109669000 ns
59 | 2018/07/06 21:27:56 mean: 100 ms, median: 99 ms, max: 109 ms, min: 93 ms, p99: 109 ms
60 |
61 | # client.Wrap(ratelimit.NewClientWrapper(100))
62 | ➜ benchmark git:(master) ✗ go run micro_client.go -c 50 -n 1000
63 | 2018/07/06 21:28:18 concurrency: 50
64 | requests per client: 20
65 |
66 | 2018/07/06 21:28:18 took 267 ms for 1000 requests
67 | 2018/07/06 21:28:18 sent requests : 1000
68 | 2018/07/06 21:28:18 received requests : 1000
69 | 2018/07/06 21:28:18 received requests_OK : 1000
70 | 2018/07/06 21:28:18 throughput (TPS) : 3745
71 | 2018/07/06 21:28:18 mean: 8221555 ns, median: 8683500 ns, max: 29595000 ns, min: 204000 ns, p99: 29448500 ns
72 | 2018/07/06 21:28:18 mean: 8 ms, median: 8 ms, max: 29 ms, min: 0 ms, p99: 29 ms
73 |
74 | ```
--------------------------------------------------------------------------------
/benchmark/_backup/micro_client.go:
--------------------------------------------------------------------------------
1 | package _backup
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "reflect"
7 | "sync"
8 | "sync/atomic"
9 | "time"
10 |
11 | "github.com/afex/hystrix-go/hystrix"
12 | "github.com/micro/go-micro/client/selector"
13 | "github.com/montanaflynn/stats"
14 |
15 | "github.com/micro/go-micro/util/log"
16 | // micro "github.com/micro/go-micro"
17 | "github.com/micro/go-micro/client"
18 | "github.com/micro/go-plugins/transport/tcp"
19 | breaker "github.com/micro/go-plugins/wrapper/breaker/hystrix"
20 | "github.com/micro/go-plugins/wrapper/ratelimiter/uber"
21 |
22 | "github.com/hb-go/micro/benchmark/proto"
23 | )
24 |
25 | var concurrency = flag.Int("c", 1, "concurrency")
26 | var total = flag.Int("n", 1, "total requests for all clients")
27 | var host = flag.String("s", "127.0.0.1:8972", "listened ip and port")
28 |
29 | func main() {
30 | flag.Parse()
31 | n := *concurrency
32 | m := *total / n
33 |
34 | log.Logf("concurrency: %d\nrequests per client: %d\n\n", n, m)
35 |
36 | args := prepareArgs()
37 |
38 | // Hystrix配置
39 | hystrix.ConfigureCommand("hello.Hello.Say", hystrix.CommandConfig{
40 | MaxConcurrentRequests: hystrix.DefaultMaxConcurrent * 2,
41 | })
42 |
43 | // b, _ := proto.Marshal(args)
44 | // log.Logf("message size: %d bytes\n\n", len(b))
45 |
46 | var wg sync.WaitGroup
47 | wg.Add(n * m)
48 |
49 | var trans uint64
50 | var transOK uint64
51 |
52 | d := make([][]int64, n, n)
53 |
54 | // it contains warmup time but we can ignore it
55 | totalT := time.Now().UnixNano()
56 | for i := 0; i < n; i++ {
57 | dt := make([]int64, 0, m)
58 | d = append(d, dt)
59 |
60 | go func(i int) {
61 | // service := micro.NewService(
62 | // micro.Name("hello.client"),
63 | // micro.Transport(tcp.NewTransport()),
64 | // micro.Selector(cache.NewSelector(cache.TTL(time.Second*3000))),
65 | //
66 | // )
67 | // c := proto.NewHelloService("hello", service.Client())
68 |
69 | cache := selector.NewSelector(func(o *selector.Options) {
70 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Second*30)
71 | })
72 |
73 | helloClient := client.NewClient(
74 | client.Transport(tcp.NewTransport()),
75 | // client.ContentType("application/protobuf"),
76 | client.Selector(cache),
77 | client.Retries(1),
78 | client.PoolSize(10),
79 | client.RequestTimeout(time.Millisecond*100),
80 | client.Wrap(breaker.NewClientWrapper()),
81 | client.Wrap(ratelimit.NewClientWrapper(10)),
82 | )
83 | c := proto.NewHelloService("hello", helloClient)
84 |
85 | // warmup
86 | for j := 0; j < 5; j++ {
87 | c.Say(context.Background(), args)
88 | }
89 |
90 | for j := 0; j < m; j++ {
91 | t := time.Now().UnixNano()
92 | reply, err := c.Say(context.Background(), args)
93 | t = time.Now().UnixNano() - t
94 |
95 | d[i] = append(d[i], t)
96 |
97 | if err == nil && *reply.Field1 == "OK" {
98 | atomic.AddUint64(&transOK, 1)
99 | } else {
100 | log.Logf("error:%v", err)
101 | }
102 |
103 | atomic.AddUint64(&trans, 1)
104 | wg.Done()
105 | }
106 | }(i)
107 |
108 | }
109 |
110 | wg.Wait()
111 | totalT = time.Now().UnixNano() - totalT
112 | totalT = totalT / 1000000
113 | log.Logf("took %d ms for %d requests\n", totalT, n*m)
114 |
115 | totalD := make([]int64, 0, n*m)
116 | for _, k := range d {
117 | totalD = append(totalD, k...)
118 | }
119 | totalD2 := make([]float64, 0, n*m)
120 | for _, k := range totalD {
121 | totalD2 = append(totalD2, float64(k))
122 | }
123 |
124 | mean, _ := stats.Mean(totalD2)
125 | median, _ := stats.Median(totalD2)
126 | max, _ := stats.Max(totalD2)
127 | min, _ := stats.Min(totalD2)
128 | p99, _ := stats.Percentile(totalD2, 99.9)
129 |
130 | log.Logf("sent requests : %d\n", n*m)
131 | log.Logf("received requests : %d\n", atomic.LoadUint64(&trans))
132 | log.Logf("received requests_OK : %d\n", atomic.LoadUint64(&transOK))
133 | log.Logf("throughput (TPS) : %d\n", int64(n*m)*1000/totalT)
134 | log.Logf("mean: %.f ns, median: %.f ns, max: %.f ns, min: %.f ns, p99: %.f ns\n", mean, median, max, min, p99)
135 | log.Logf("mean: %d ms, median: %d ms, max: %d ms, min: %d ms, p99: %d ms\n", int64(mean/1000000), int64(median/1000000), int64(max/1000000), int64(min/1000000), int64(p99/1000000))
136 |
137 | }
138 |
139 | func prepareArgs() *proto.BenchmarkMessage {
140 | b := true
141 | var i int32 = 100000
142 | var i64 int64 = 100000
143 | var s = "许多往事在眼前一幕一幕,变的那麼模糊"
144 |
145 | var args proto.BenchmarkMessage
146 |
147 | v := reflect.ValueOf(&args).Elem()
148 | num := v.NumField()
149 | for k := 0; k < num; k++ {
150 | field := v.Field(k)
151 | if field.Type().Kind() == reflect.Ptr {
152 | switch v.Field(k).Type().Elem().Kind() {
153 | case reflect.Int, reflect.Int32:
154 | field.Set(reflect.ValueOf(&i))
155 | case reflect.Int64:
156 | field.Set(reflect.ValueOf(&i64))
157 | case reflect.Bool:
158 | field.Set(reflect.ValueOf(&b))
159 | case reflect.String:
160 | field.Set(reflect.ValueOf(&s))
161 | }
162 | } else {
163 | switch field.Kind() {
164 | case reflect.Int, reflect.Int32, reflect.Int64:
165 | field.SetInt(100000)
166 | case reflect.Bool:
167 | field.SetBool(true)
168 | case reflect.String:
169 | field.SetString(s)
170 | }
171 | }
172 |
173 | }
174 | return &args
175 | }
176 |
--------------------------------------------------------------------------------
/benchmark/_backup/micro_mclient.go:
--------------------------------------------------------------------------------
1 | package _backup
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "reflect"
7 | "strings"
8 | "sync"
9 | "sync/atomic"
10 | "time"
11 |
12 | "github.com/afex/hystrix-go/hystrix"
13 | "github.com/micro/go-micro/client/selector"
14 | "github.com/montanaflynn/stats"
15 |
16 | "github.com/micro/go-micro/util/log"
17 | // micro "github.com/micro/go-micro"
18 | "github.com/micro/go-micro/client"
19 | "github.com/micro/go-plugins/transport/tcp"
20 | breaker "github.com/micro/go-plugins/wrapper/breaker/hystrix"
21 | "github.com/micro/go-plugins/wrapper/ratelimiter/uber"
22 |
23 | "github.com/hb-go/micro/benchmark/proto"
24 | )
25 |
26 | var concurrency = flag.Int("c", 1, "concurrency")
27 | var total = flag.Int("n", 1, "total requests for all clients")
28 | var host = flag.String("s", "127.0.0.1:8972", "server ip and port")
29 |
30 | func main() {
31 | flag.Parse()
32 | n := *concurrency
33 | m := *total / n
34 |
35 | selected := -1
36 | servers := strings.Split(*host, ",")
37 | sNum := len(servers)
38 |
39 | log.Logf("Servers: %+v\n\n", servers)
40 |
41 | log.Logf("concurrency: %d\nrequests per client: %d\n\n", n, m)
42 |
43 | args := prepareArgs()
44 |
45 | // Hystrix配置
46 | hystrix.ConfigureCommand("hello.Hello.Say", hystrix.CommandConfig{
47 | MaxConcurrentRequests: hystrix.DefaultMaxConcurrent * 2,
48 | })
49 |
50 | // b, _ := proto.Marshal(args)
51 | // log.Logf("message size: %d bytes\n\n", len(b))
52 |
53 | var wg sync.WaitGroup
54 | wg.Add(n * m)
55 |
56 | var trans uint64
57 | var transOK uint64
58 |
59 | d := make([][]int64, n, n)
60 |
61 | // it contains warmup time but we can ignore it
62 | totalT := time.Now().UnixNano()
63 | for i := 0; i < n; i++ {
64 | dt := make([]int64, 0, m)
65 | d = append(d, dt)
66 | selected = (selected + 1) % sNum
67 |
68 | go func(i int, selected int) {
69 | // service := micro.NewService(
70 | // micro.Name("hello.client"),
71 | // micro.Transport(tcp.NewTransport()),
72 | // micro.Selector(cache.NewSelector(cache.TTL(time.Second*3000))),
73 | // )
74 | // c := proto.NewHelloService("hello", service.Client())
75 |
76 | cache := selector.NewSelector(func(o *selector.Options) {
77 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Second*30)
78 | })
79 |
80 | helloClient := client.NewClient(
81 | client.Transport(tcp.NewTransport()),
82 | // client.ContentType("application/protobuf"),
83 | client.Selector(cache),
84 | client.Retries(1),
85 | client.PoolSize(10),
86 | client.RequestTimeout(time.Millisecond*100),
87 | client.Wrap(breaker.NewClientWrapper()),
88 | client.Wrap(ratelimit.NewClientWrapper(10)),
89 | )
90 | c := proto.NewHelloService("hello", helloClient)
91 |
92 | // warmup
93 | for j := 0; j < 5; j++ {
94 | c.Say(context.Background(), args)
95 | }
96 |
97 | for j := 0; j < m; j++ {
98 | t := time.Now().UnixNano()
99 | reply, err := c.Say(context.Background(), args)
100 | t = time.Now().UnixNano() - t
101 |
102 | d[i] = append(d[i], t)
103 |
104 | if err == nil && *reply.Field1 == "OK" {
105 | atomic.AddUint64(&transOK, 1)
106 | } else {
107 | log.Logf("error:%v", err)
108 | }
109 |
110 | atomic.AddUint64(&trans, 1)
111 | wg.Done()
112 | }
113 |
114 | // c.Close()
115 |
116 | }(i, selected)
117 |
118 | }
119 |
120 | wg.Wait()
121 | totalT = time.Now().UnixNano() - totalT
122 | totalT = totalT / 1000000
123 | log.Logf("took %d ms for %d requests\n", totalT, n*m)
124 |
125 | totalD := make([]int64, 0, n*m)
126 | for _, k := range d {
127 | totalD = append(totalD, k...)
128 | }
129 | totalD2 := make([]float64, 0, n*m)
130 | for _, k := range totalD {
131 | totalD2 = append(totalD2, float64(k))
132 | }
133 |
134 | mean, _ := stats.Mean(totalD2)
135 | median, _ := stats.Median(totalD2)
136 | max, _ := stats.Max(totalD2)
137 | min, _ := stats.Min(totalD2)
138 | p99, _ := stats.Percentile(totalD2, 99.9)
139 |
140 | log.Logf("sent requests : %d\n", n*m)
141 | log.Logf("received requests : %d\n", atomic.LoadUint64(&trans))
142 | log.Logf("received requests_OK : %d\n", atomic.LoadUint64(&transOK))
143 | log.Logf("throughput (TPS) : %d\n", int64(n*m)*1000/totalT)
144 | log.Logf("mean: %.f ns, median: %.f ns, max: %.f ns, min: %.f ns, p99: %.f ns\n", mean, median, max, min, p99)
145 | log.Logf("mean: %d ms, median: %d ms, max: %d ms, min: %d ms, p99: %d ms\n", int64(mean/1000000), int64(median/1000000), int64(max/1000000), int64(min/1000000), int64(p99/1000000))
146 |
147 | }
148 |
149 | func prepareArgs() *proto.BenchmarkMessage {
150 | b := true
151 | var i int32 = 100000
152 | var i64 int64 = 100000
153 | var s = "许多往事在眼前一幕一幕,变的那麼模糊"
154 |
155 | var args proto.BenchmarkMessage
156 |
157 | v := reflect.ValueOf(&args).Elem()
158 | num := v.NumField()
159 | for k := 0; k < num; k++ {
160 | field := v.Field(k)
161 | if field.Type().Kind() == reflect.Ptr {
162 | switch v.Field(k).Type().Elem().Kind() {
163 | case reflect.Int, reflect.Int32:
164 | field.Set(reflect.ValueOf(&i))
165 | case reflect.Int64:
166 | field.Set(reflect.ValueOf(&i64))
167 | case reflect.Bool:
168 | field.Set(reflect.ValueOf(&b))
169 | case reflect.String:
170 | field.Set(reflect.ValueOf(&s))
171 | }
172 | } else {
173 | switch field.Kind() {
174 | case reflect.Int, reflect.Int32, reflect.Int64:
175 | field.SetInt(100000)
176 | case reflect.Bool:
177 | field.SetBool(true)
178 | case reflect.String:
179 | field.SetString(s)
180 | }
181 | }
182 |
183 | }
184 | return &args
185 | }
186 |
--------------------------------------------------------------------------------
/benchmark/_backup/micro_server.go:
--------------------------------------------------------------------------------
1 | package _backup
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "fmt"
7 | "time"
8 |
9 | "github.com/micro/go-micro"
10 | "github.com/micro/go-plugins/transport/tcp"
11 | "github.com/micro/go-plugins/wrapper/ratelimiter/uber"
12 |
13 | "github.com/hb-go/micro/benchmark/proto"
14 | )
15 |
16 | type HelloS struct{}
17 |
18 | func (t *HelloS) Say(ctx context.Context, args *proto.BenchmarkMessage, reply *proto.BenchmarkMessage) error {
19 | s := "OK"
20 | var i int32 = 100
21 | args.Field1 = &s
22 | args.Field2 = &i
23 | *reply = *args
24 | if *delay > 0 {
25 | time.Sleep(*delay)
26 | }
27 | return nil
28 | }
29 |
30 | // var host = flag.String("s", "127.0.0.1:8972", "listened ip and port")
31 |
32 | var delay = flag.Duration("delay", 0, "delay to mock business processing")
33 |
34 | func main() {
35 | flag.Parse()
36 |
37 | service := micro.NewService(
38 | micro.Name("hello"),
39 | micro.Version("latest"),
40 | micro.Transport(tcp.NewTransport()),
41 | )
42 |
43 | service.Init(
44 | // handler wrap
45 | micro.WrapHandler(
46 | ratelimit.NewHandlerWrapper(100),
47 | ),
48 | )
49 |
50 | proto.RegisterHelloHandler(service.Server(), &HelloS{})
51 |
52 | // Run the server
53 | if err := service.Run(); err != nil {
54 | fmt.Println(err)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/benchmark/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/hb-chen/micro/benchmark
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/benchmark/grpc_grpc/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "time"
7 |
8 | "github.com/micro/go-micro/client"
9 | "github.com/micro/go-micro/client/selector"
10 | "github.com/micro/go-plugins/client/grpc"
11 | trans "github.com/micro/go-plugins/transport/grpc"
12 |
13 | "github.com/hb-go/micro/benchmark/service"
14 | )
15 |
16 | var concurrency = flag.Int("c", 1, "concurrency")
17 | var total = flag.Int("n", 1, "total requests for all clients")
18 | var contentType = flag.String("ct", "application/protobuf", "content type")
19 |
20 | func main() {
21 | flag.Parse()
22 | serviceName := "hello.grpc.grpc"
23 | service.ClientWith(
24 | serviceName,
25 | func() client.Client {
26 | cache := selector.NewSelector(func(o *selector.Options) {
27 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Minute*3)
28 | })
29 | return grpc.NewClient(
30 | client.Transport(trans.NewTransport()),
31 | client.ContentType(*contentType),
32 | client.Selector(cache),
33 | client.Retries(1),
34 | client.PoolSize(*concurrency*2),
35 | client.RequestTimeout(time.Millisecond*100),
36 | // client.Wrap(breaker.NewClientWrapper()),
37 | // client.Wrap(ratelimit.NewClientWrapper(10000)),
38 | )
39 | },
40 | *concurrency,
41 | *total,
42 | )
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/benchmark/grpc_grpc/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 |
6 | "github.com/micro/go-micro"
7 | "github.com/micro/go-plugins/server/grpc"
8 | trans "github.com/micro/go-plugins/transport/grpc"
9 |
10 | "github.com/hb-go/micro/benchmark/service"
11 | )
12 |
13 | var delay = flag.Duration("delay", 0, "delay to mock business processing")
14 |
15 | func main() {
16 | flag.Parse()
17 |
18 | serviceName := "hello.grpc.grpc"
19 | service.ServeWith(
20 | serviceName,
21 | micro.Server(grpc.NewServer()),
22 | micro.Transport(trans.NewTransport()),
23 | *delay,
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/benchmark/grpc_rpc/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "time"
7 |
8 | "github.com/micro/go-micro/client"
9 | "github.com/micro/go-micro/client/selector"
10 | "github.com/micro/go-plugins/transport/grpc"
11 |
12 | "github.com/hb-go/micro/benchmark/service"
13 | )
14 |
15 | var concurrency = flag.Int("c", 1, "concurrency")
16 | var total = flag.Int("n", 1, "total requests for all clients")
17 | var contentType = flag.String("ct", "application/protobuf", "content type")
18 |
19 | func main() {
20 | flag.Parse()
21 | serviceName := "hello.grpc.rpc"
22 | service.ClientWith(
23 | serviceName,
24 | func() client.Client {
25 | cache := selector.NewSelector(func(o *selector.Options) {
26 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Minute*3)
27 | })
28 | return client.NewClient(
29 | client.Transport(grpc.NewTransport()),
30 | client.ContentType(*contentType),
31 | client.Selector(cache),
32 | client.Retries(1),
33 | client.PoolSize(*concurrency*2),
34 | client.RequestTimeout(time.Millisecond*100),
35 | // client.Wrap(breaker.NewClientWrapper()),
36 | // client.Wrap(ratelimit.NewClientWrapper(10000)),
37 | )
38 | },
39 | *concurrency,
40 | *total,
41 | )
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/benchmark/grpc_rpc/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 |
6 | "github.com/micro/go-micro"
7 | "github.com/micro/go-micro/server"
8 | "github.com/micro/go-plugins/transport/grpc"
9 |
10 | "github.com/hb-go/micro/benchmark/service"
11 | )
12 |
13 | var delay = flag.Duration("delay", 0, "delay to mock business processing")
14 |
15 | func main() {
16 | flag.Parse()
17 |
18 | serviceName := "hello.grpc.rpc"
19 | service.ServeWith(
20 | serviceName,
21 | micro.Server(server.DefaultServer),
22 | micro.Transport(grpc.NewTransport()),
23 | *delay,
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/benchmark/proto/micro_benchmark.micro.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-micro. DO NOT EDIT.
2 | // source: benchmark/proto/micro_benchmark.proto
3 |
4 | package proto
5 |
6 | import (
7 | fmt "fmt"
8 | proto "github.com/golang/protobuf/proto"
9 | math "math"
10 | )
11 |
12 | import (
13 | context "context"
14 | client "github.com/micro/go-micro/client"
15 | server "github.com/micro/go-micro/server"
16 | )
17 |
18 | // Reference imports to suppress errors if they are not otherwise used.
19 | var _ = proto.Marshal
20 | var _ = fmt.Errorf
21 | var _ = math.Inf
22 |
23 | // This is a compile-time assertion to ensure that this generated file
24 | // is compatible with the proto package it is being compiled against.
25 | // A compilation error at this line likely means your copy of the
26 | // proto package needs to be updated.
27 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
28 |
29 | // Reference imports to suppress errors if they are not otherwise used.
30 | var _ context.Context
31 | var _ client.Option
32 | var _ server.Option
33 |
34 | // Client API for Hello service
35 |
36 | type HelloService interface {
37 | // Sends a greeting
38 | Say(ctx context.Context, in *BenchmarkMessage, opts ...client.CallOption) (*BenchmarkMessage, error)
39 | }
40 |
41 | type helloService struct {
42 | c client.Client
43 | name string
44 | }
45 |
46 | func NewHelloService(name string, c client.Client) HelloService {
47 | if c == nil {
48 | c = client.NewClient()
49 | }
50 | if len(name) == 0 {
51 | name = "proto"
52 | }
53 | return &helloService{
54 | c: c,
55 | name: name,
56 | }
57 | }
58 |
59 | func (c *helloService) Say(ctx context.Context, in *BenchmarkMessage, opts ...client.CallOption) (*BenchmarkMessage, error) {
60 | req := c.c.NewRequest(c.name, "Hello.Say", in)
61 | out := new(BenchmarkMessage)
62 | err := c.c.Call(ctx, req, out, opts...)
63 | if err != nil {
64 | return nil, err
65 | }
66 | return out, nil
67 | }
68 |
69 | // Server API for Hello service
70 |
71 | type HelloHandler interface {
72 | // Sends a greeting
73 | Say(context.Context, *BenchmarkMessage, *BenchmarkMessage) error
74 | }
75 |
76 | func RegisterHelloHandler(s server.Server, hdlr HelloHandler, opts ...server.HandlerOption) error {
77 | type hello interface {
78 | Say(ctx context.Context, in *BenchmarkMessage, out *BenchmarkMessage) 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) Say(ctx context.Context, in *BenchmarkMessage, out *BenchmarkMessage) error {
92 | return h.HelloHandler.Say(ctx, in, out)
93 | }
94 |
--------------------------------------------------------------------------------
/benchmark/proto/micro_benchmark.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: benchmark/proto/micro_benchmark.proto
3 |
4 | package proto
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 BenchmarkMessage struct {
24 | Field1 *string `protobuf:"bytes,1,req,name=field1" json:"field1,omitempty"`
25 | Field9 *string `protobuf:"bytes,9,opt,name=field9" json:"field9,omitempty"`
26 | Field18 *string `protobuf:"bytes,18,opt,name=field18" json:"field18,omitempty"`
27 | Field80 *bool `protobuf:"varint,80,opt,name=field80,def=0" json:"field80,omitempty"`
28 | Field81 *bool `protobuf:"varint,81,opt,name=field81,def=1" json:"field81,omitempty"`
29 | Field2 *int32 `protobuf:"varint,2,req,name=field2" json:"field2,omitempty"`
30 | Field3 *int32 `protobuf:"varint,3,req,name=field3" json:"field3,omitempty"`
31 | Field280 *int32 `protobuf:"varint,280,opt,name=field280" json:"field280,omitempty"`
32 | Field6 *int32 `protobuf:"varint,6,opt,name=field6,def=0" json:"field6,omitempty"`
33 | Field22 *int64 `protobuf:"varint,22,opt,name=field22" json:"field22,omitempty"`
34 | Field4 *string `protobuf:"bytes,4,opt,name=field4" json:"field4,omitempty"`
35 | Field5 []uint64 `protobuf:"fixed64,5,rep,name=field5" json:"field5,omitempty"`
36 | Field59 *bool `protobuf:"varint,59,opt,name=field59,def=0" json:"field59,omitempty"`
37 | Field7 *string `protobuf:"bytes,7,opt,name=field7" json:"field7,omitempty"`
38 | Field16 *int32 `protobuf:"varint,16,opt,name=field16" json:"field16,omitempty"`
39 | Field130 *int32 `protobuf:"varint,130,opt,name=field130,def=0" json:"field130,omitempty"`
40 | Field12 *bool `protobuf:"varint,12,opt,name=field12,def=1" json:"field12,omitempty"`
41 | Field17 *bool `protobuf:"varint,17,opt,name=field17,def=1" json:"field17,omitempty"`
42 | Field13 *bool `protobuf:"varint,13,opt,name=field13,def=1" json:"field13,omitempty"`
43 | Field14 *bool `protobuf:"varint,14,opt,name=field14,def=1" json:"field14,omitempty"`
44 | Field104 *int32 `protobuf:"varint,104,opt,name=field104,def=0" json:"field104,omitempty"`
45 | Field100 *int32 `protobuf:"varint,100,opt,name=field100,def=0" json:"field100,omitempty"`
46 | Field101 *int32 `protobuf:"varint,101,opt,name=field101,def=0" json:"field101,omitempty"`
47 | Field102 *string `protobuf:"bytes,102,opt,name=field102" json:"field102,omitempty"`
48 | Field103 *string `protobuf:"bytes,103,opt,name=field103" json:"field103,omitempty"`
49 | Field29 *int32 `protobuf:"varint,29,opt,name=field29,def=0" json:"field29,omitempty"`
50 | Field30 *bool `protobuf:"varint,30,opt,name=field30,def=0" json:"field30,omitempty"`
51 | Field60 *int32 `protobuf:"varint,60,opt,name=field60,def=-1" json:"field60,omitempty"`
52 | Field271 *int32 `protobuf:"varint,271,opt,name=field271,def=-1" json:"field271,omitempty"`
53 | Field272 *int32 `protobuf:"varint,272,opt,name=field272,def=-1" json:"field272,omitempty"`
54 | Field150 *int32 `protobuf:"varint,150,opt,name=field150" json:"field150,omitempty"`
55 | Field23 *int32 `protobuf:"varint,23,opt,name=field23,def=0" json:"field23,omitempty"`
56 | Field24 *bool `protobuf:"varint,24,opt,name=field24,def=0" json:"field24,omitempty"`
57 | Field25 *int32 `protobuf:"varint,25,opt,name=field25,def=0" json:"field25,omitempty"`
58 | Field78 *bool `protobuf:"varint,78,opt,name=field78" json:"field78,omitempty"`
59 | Field67 *int32 `protobuf:"varint,67,opt,name=field67,def=0" json:"field67,omitempty"`
60 | Field68 *int32 `protobuf:"varint,68,opt,name=field68" json:"field68,omitempty"`
61 | Field128 *int32 `protobuf:"varint,128,opt,name=field128,def=0" json:"field128,omitempty"`
62 | Field129 *string `protobuf:"bytes,129,opt,name=field129,def=xxxxxxxxxxxxxxxxxxxxx" json:"field129,omitempty"`
63 | Field131 *int32 `protobuf:"varint,131,opt,name=field131,def=0" json:"field131,omitempty"`
64 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
65 | XXX_unrecognized []byte `json:"-"`
66 | XXX_sizecache int32 `json:"-"`
67 | }
68 |
69 | func (m *BenchmarkMessage) Reset() { *m = BenchmarkMessage{} }
70 | func (m *BenchmarkMessage) String() string { return proto.CompactTextString(m) }
71 | func (*BenchmarkMessage) ProtoMessage() {}
72 | func (*BenchmarkMessage) Descriptor() ([]byte, []int) {
73 | return fileDescriptor_9ec8442d33902ef2, []int{0}
74 | }
75 |
76 | func (m *BenchmarkMessage) XXX_Unmarshal(b []byte) error {
77 | return xxx_messageInfo_BenchmarkMessage.Unmarshal(m, b)
78 | }
79 | func (m *BenchmarkMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
80 | return xxx_messageInfo_BenchmarkMessage.Marshal(b, m, deterministic)
81 | }
82 | func (m *BenchmarkMessage) XXX_Merge(src proto.Message) {
83 | xxx_messageInfo_BenchmarkMessage.Merge(m, src)
84 | }
85 | func (m *BenchmarkMessage) XXX_Size() int {
86 | return xxx_messageInfo_BenchmarkMessage.Size(m)
87 | }
88 | func (m *BenchmarkMessage) XXX_DiscardUnknown() {
89 | xxx_messageInfo_BenchmarkMessage.DiscardUnknown(m)
90 | }
91 |
92 | var xxx_messageInfo_BenchmarkMessage proto.InternalMessageInfo
93 |
94 | const Default_BenchmarkMessage_Field80 bool = false
95 | const Default_BenchmarkMessage_Field81 bool = true
96 | const Default_BenchmarkMessage_Field6 int32 = 0
97 | const Default_BenchmarkMessage_Field59 bool = false
98 | const Default_BenchmarkMessage_Field130 int32 = 0
99 | const Default_BenchmarkMessage_Field12 bool = true
100 | const Default_BenchmarkMessage_Field17 bool = true
101 | const Default_BenchmarkMessage_Field13 bool = true
102 | const Default_BenchmarkMessage_Field14 bool = true
103 | const Default_BenchmarkMessage_Field104 int32 = 0
104 | const Default_BenchmarkMessage_Field100 int32 = 0
105 | const Default_BenchmarkMessage_Field101 int32 = 0
106 | const Default_BenchmarkMessage_Field29 int32 = 0
107 | const Default_BenchmarkMessage_Field30 bool = false
108 | const Default_BenchmarkMessage_Field60 int32 = -1
109 | const Default_BenchmarkMessage_Field271 int32 = -1
110 | const Default_BenchmarkMessage_Field272 int32 = -1
111 | const Default_BenchmarkMessage_Field23 int32 = 0
112 | const Default_BenchmarkMessage_Field24 bool = false
113 | const Default_BenchmarkMessage_Field25 int32 = 0
114 | const Default_BenchmarkMessage_Field67 int32 = 0
115 | const Default_BenchmarkMessage_Field128 int32 = 0
116 | const Default_BenchmarkMessage_Field129 string = "xxxxxxxxxxxxxxxxxxxxx"
117 | const Default_BenchmarkMessage_Field131 int32 = 0
118 |
119 | func (m *BenchmarkMessage) GetField1() string {
120 | if m != nil && m.Field1 != nil {
121 | return *m.Field1
122 | }
123 | return ""
124 | }
125 |
126 | func (m *BenchmarkMessage) GetField9() string {
127 | if m != nil && m.Field9 != nil {
128 | return *m.Field9
129 | }
130 | return ""
131 | }
132 |
133 | func (m *BenchmarkMessage) GetField18() string {
134 | if m != nil && m.Field18 != nil {
135 | return *m.Field18
136 | }
137 | return ""
138 | }
139 |
140 | func (m *BenchmarkMessage) GetField80() bool {
141 | if m != nil && m.Field80 != nil {
142 | return *m.Field80
143 | }
144 | return Default_BenchmarkMessage_Field80
145 | }
146 |
147 | func (m *BenchmarkMessage) GetField81() bool {
148 | if m != nil && m.Field81 != nil {
149 | return *m.Field81
150 | }
151 | return Default_BenchmarkMessage_Field81
152 | }
153 |
154 | func (m *BenchmarkMessage) GetField2() int32 {
155 | if m != nil && m.Field2 != nil {
156 | return *m.Field2
157 | }
158 | return 0
159 | }
160 |
161 | func (m *BenchmarkMessage) GetField3() int32 {
162 | if m != nil && m.Field3 != nil {
163 | return *m.Field3
164 | }
165 | return 0
166 | }
167 |
168 | func (m *BenchmarkMessage) GetField280() int32 {
169 | if m != nil && m.Field280 != nil {
170 | return *m.Field280
171 | }
172 | return 0
173 | }
174 |
175 | func (m *BenchmarkMessage) GetField6() int32 {
176 | if m != nil && m.Field6 != nil {
177 | return *m.Field6
178 | }
179 | return Default_BenchmarkMessage_Field6
180 | }
181 |
182 | func (m *BenchmarkMessage) GetField22() int64 {
183 | if m != nil && m.Field22 != nil {
184 | return *m.Field22
185 | }
186 | return 0
187 | }
188 |
189 | func (m *BenchmarkMessage) GetField4() string {
190 | if m != nil && m.Field4 != nil {
191 | return *m.Field4
192 | }
193 | return ""
194 | }
195 |
196 | func (m *BenchmarkMessage) GetField5() []uint64 {
197 | if m != nil {
198 | return m.Field5
199 | }
200 | return nil
201 | }
202 |
203 | func (m *BenchmarkMessage) GetField59() bool {
204 | if m != nil && m.Field59 != nil {
205 | return *m.Field59
206 | }
207 | return Default_BenchmarkMessage_Field59
208 | }
209 |
210 | func (m *BenchmarkMessage) GetField7() string {
211 | if m != nil && m.Field7 != nil {
212 | return *m.Field7
213 | }
214 | return ""
215 | }
216 |
217 | func (m *BenchmarkMessage) GetField16() int32 {
218 | if m != nil && m.Field16 != nil {
219 | return *m.Field16
220 | }
221 | return 0
222 | }
223 |
224 | func (m *BenchmarkMessage) GetField130() int32 {
225 | if m != nil && m.Field130 != nil {
226 | return *m.Field130
227 | }
228 | return Default_BenchmarkMessage_Field130
229 | }
230 |
231 | func (m *BenchmarkMessage) GetField12() bool {
232 | if m != nil && m.Field12 != nil {
233 | return *m.Field12
234 | }
235 | return Default_BenchmarkMessage_Field12
236 | }
237 |
238 | func (m *BenchmarkMessage) GetField17() bool {
239 | if m != nil && m.Field17 != nil {
240 | return *m.Field17
241 | }
242 | return Default_BenchmarkMessage_Field17
243 | }
244 |
245 | func (m *BenchmarkMessage) GetField13() bool {
246 | if m != nil && m.Field13 != nil {
247 | return *m.Field13
248 | }
249 | return Default_BenchmarkMessage_Field13
250 | }
251 |
252 | func (m *BenchmarkMessage) GetField14() bool {
253 | if m != nil && m.Field14 != nil {
254 | return *m.Field14
255 | }
256 | return Default_BenchmarkMessage_Field14
257 | }
258 |
259 | func (m *BenchmarkMessage) GetField104() int32 {
260 | if m != nil && m.Field104 != nil {
261 | return *m.Field104
262 | }
263 | return Default_BenchmarkMessage_Field104
264 | }
265 |
266 | func (m *BenchmarkMessage) GetField100() int32 {
267 | if m != nil && m.Field100 != nil {
268 | return *m.Field100
269 | }
270 | return Default_BenchmarkMessage_Field100
271 | }
272 |
273 | func (m *BenchmarkMessage) GetField101() int32 {
274 | if m != nil && m.Field101 != nil {
275 | return *m.Field101
276 | }
277 | return Default_BenchmarkMessage_Field101
278 | }
279 |
280 | func (m *BenchmarkMessage) GetField102() string {
281 | if m != nil && m.Field102 != nil {
282 | return *m.Field102
283 | }
284 | return ""
285 | }
286 |
287 | func (m *BenchmarkMessage) GetField103() string {
288 | if m != nil && m.Field103 != nil {
289 | return *m.Field103
290 | }
291 | return ""
292 | }
293 |
294 | func (m *BenchmarkMessage) GetField29() int32 {
295 | if m != nil && m.Field29 != nil {
296 | return *m.Field29
297 | }
298 | return Default_BenchmarkMessage_Field29
299 | }
300 |
301 | func (m *BenchmarkMessage) GetField30() bool {
302 | if m != nil && m.Field30 != nil {
303 | return *m.Field30
304 | }
305 | return Default_BenchmarkMessage_Field30
306 | }
307 |
308 | func (m *BenchmarkMessage) GetField60() int32 {
309 | if m != nil && m.Field60 != nil {
310 | return *m.Field60
311 | }
312 | return Default_BenchmarkMessage_Field60
313 | }
314 |
315 | func (m *BenchmarkMessage) GetField271() int32 {
316 | if m != nil && m.Field271 != nil {
317 | return *m.Field271
318 | }
319 | return Default_BenchmarkMessage_Field271
320 | }
321 |
322 | func (m *BenchmarkMessage) GetField272() int32 {
323 | if m != nil && m.Field272 != nil {
324 | return *m.Field272
325 | }
326 | return Default_BenchmarkMessage_Field272
327 | }
328 |
329 | func (m *BenchmarkMessage) GetField150() int32 {
330 | if m != nil && m.Field150 != nil {
331 | return *m.Field150
332 | }
333 | return 0
334 | }
335 |
336 | func (m *BenchmarkMessage) GetField23() int32 {
337 | if m != nil && m.Field23 != nil {
338 | return *m.Field23
339 | }
340 | return Default_BenchmarkMessage_Field23
341 | }
342 |
343 | func (m *BenchmarkMessage) GetField24() bool {
344 | if m != nil && m.Field24 != nil {
345 | return *m.Field24
346 | }
347 | return Default_BenchmarkMessage_Field24
348 | }
349 |
350 | func (m *BenchmarkMessage) GetField25() int32 {
351 | if m != nil && m.Field25 != nil {
352 | return *m.Field25
353 | }
354 | return Default_BenchmarkMessage_Field25
355 | }
356 |
357 | func (m *BenchmarkMessage) GetField78() bool {
358 | if m != nil && m.Field78 != nil {
359 | return *m.Field78
360 | }
361 | return false
362 | }
363 |
364 | func (m *BenchmarkMessage) GetField67() int32 {
365 | if m != nil && m.Field67 != nil {
366 | return *m.Field67
367 | }
368 | return Default_BenchmarkMessage_Field67
369 | }
370 |
371 | func (m *BenchmarkMessage) GetField68() int32 {
372 | if m != nil && m.Field68 != nil {
373 | return *m.Field68
374 | }
375 | return 0
376 | }
377 |
378 | func (m *BenchmarkMessage) GetField128() int32 {
379 | if m != nil && m.Field128 != nil {
380 | return *m.Field128
381 | }
382 | return Default_BenchmarkMessage_Field128
383 | }
384 |
385 | func (m *BenchmarkMessage) GetField129() string {
386 | if m != nil && m.Field129 != nil {
387 | return *m.Field129
388 | }
389 | return Default_BenchmarkMessage_Field129
390 | }
391 |
392 | func (m *BenchmarkMessage) GetField131() int32 {
393 | if m != nil && m.Field131 != nil {
394 | return *m.Field131
395 | }
396 | return Default_BenchmarkMessage_Field131
397 | }
398 |
399 | func init() {
400 | proto.RegisterType((*BenchmarkMessage)(nil), "proto.BenchmarkMessage")
401 | }
402 |
403 | func init() {
404 | proto.RegisterFile("benchmark/proto/micro_benchmark.proto", fileDescriptor_9ec8442d33902ef2)
405 | }
406 |
407 | var fileDescriptor_9ec8442d33902ef2 = []byte{
408 | // 515 bytes of a gzipped FileDescriptorProto
409 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x94, 0xcd, 0x6e, 0xd3, 0x40,
410 | 0x14, 0x85, 0xb1, 0x53, 0xa7, 0x8d, 0x05, 0xa8, 0x8c, 0x44, 0x7b, 0x5a, 0x68, 0x6a, 0x55, 0x42,
411 | 0xf2, 0x86, 0x76, 0x7e, 0xfc, 0x1b, 0x58, 0x05, 0x16, 0xdd, 0x80, 0xc0, 0x3c, 0x00, 0x0a, 0xad,
412 | 0xd3, 0x56, 0xa4, 0x04, 0x25, 0x45, 0x82, 0x1d, 0x3f, 0x0f, 0x00, 0x2b, 0xc4, 0x73, 0xf0, 0x84,
413 | 0xc8, 0x8e, 0xe7, 0x7a, 0xea, 0xa1, 0xd9, 0x44, 0xf7, 0x7c, 0xf7, 0xd8, 0xf7, 0xce, 0x1c, 0xd9,
414 | 0x7f, 0xf4, 0xae, 0xfc, 0x70, 0x72, 0x7e, 0x39, 0x59, 0xbc, 0x3f, 0xfa, 0xb8, 0x98, 0x5f, 0xcd,
415 | 0x8f, 0x2e, 0x2f, 0x4e, 0x16, 0xf3, 0xb7, 0xa4, 0x1e, 0xd6, 0x2a, 0xf3, 0xea, 0xbf, 0x83, 0xbf,
416 | 0x03, 0x7f, 0x73, 0xac, 0xd1, 0x8b, 0x72, 0xb9, 0x9c, 0x9c, 0x95, 0x6c, 0xcb, 0xef, 0x4f, 0x2f,
417 | 0xca, 0xd9, 0xa9, 0x80, 0x13, 0xb8, 0xe1, 0xa0, 0x68, 0x2a, 0xd2, 0x73, 0x0c, 0x02, 0x87, 0xf4,
418 | 0x9c, 0xc1, 0x5f, 0x5f, 0x75, 0x64, 0x60, 0x35, 0xd0, 0x25, 0xdb, 0x6f, 0x48, 0xc6, 0xf1, 0x2a,
419 | 0x70, 0xc2, 0x8d, 0x91, 0x37, 0x9d, 0xcc, 0x96, 0x65, 0xa1, 0x55, 0x36, 0xd4, 0x0d, 0x02, 0xaf,
420 | 0xeb, 0x86, 0xb5, 0xab, 0xc5, 0x27, 0xe2, 0xed, 0x2b, 0x25, 0xdc, 0xc0, 0x0d, 0xbd, 0xe6, 0x95,
421 | 0x92, 0x74, 0x85, 0x9e, 0xa1, 0x2b, 0xf6, 0xc0, 0xdf, 0x58, 0x75, 0x64, 0x1c, 0x7f, 0xdc, 0xc0,
422 | 0x09, 0xbd, 0x82, 0x04, 0xb6, 0xd3, 0x98, 0x12, 0xf4, 0x2b, 0x32, 0x72, 0x78, 0xe3, 0x4b, 0x68,
423 | 0x05, 0x29, 0xb1, 0x15, 0x38, 0x61, 0xaf, 0xd0, 0x25, 0xbd, 0x29, 0xc2, 0x9a, 0xb1, 0x74, 0x44,
424 | 0x7a, 0x0c, 0x2f, 0xe8, 0x85, 0xfd, 0x46, 0x8f, 0x69, 0xe5, 0x38, 0xc7, 0x13, 0x7b, 0xe5, 0x38,
425 | 0x27, 0x63, 0x8a, 0x75, 0xe3, 0x81, 0x69, 0x7b, 0x8a, 0x09, 0x36, 0xeb, 0xc1, 0x75, 0xc9, 0x86,
426 | 0xcd, 0x52, 0x42, 0x71, 0x7c, 0x77, 0xf4, 0xe8, 0xa4, 0xd1, 0x21, 0x0a, 0x89, 0xdb, 0xd6, 0x21,
427 | 0x0a, 0xd9, 0xf2, 0x14, 0xf7, 0x6c, 0x9e, 0xb6, 0x5c, 0xe1, 0x8e, 0xcd, 0x55, 0xcb, 0x23, 0xdc,
428 | 0xb5, 0x79, 0xc4, 0xf6, 0xf4, 0x7c, 0x3c, 0xc2, 0x79, 0x67, 0x3c, 0x6e, 0x62, 0x8e, 0xd3, 0x2e,
429 | 0xe6, 0x06, 0x16, 0x28, 0xbb, 0x58, 0xb0, 0x5d, 0xc2, 0x12, 0xd3, 0xfa, 0xc0, 0xa8, 0x36, 0x98,
430 | 0xc2, 0xd9, 0x35, 0x56, 0x25, 0xa1, 0xb9, 0xc2, 0x1c, 0x7b, 0xfa, 0xa9, 0x5a, 0xa1, 0x4b, 0x52,
431 | 0x1c, 0x43, 0xfb, 0x92, 0x14, 0x67, 0x0f, 0x9b, 0x86, 0x84, 0xe3, 0x69, 0xed, 0x76, 0x1f, 0x8b,
432 | 0x42, 0x4b, 0x6c, 0x5f, 0xa7, 0x2c, 0x15, 0xf8, 0xe9, 0x12, 0x27, 0xd1, 0x68, 0x90, 0xf8, 0x65,
433 | 0x37, 0x48, 0xca, 0xa9, 0x88, 0x39, 0x7e, 0x3b, 0x46, 0x4e, 0x45, 0xcc, 0xdb, 0xd1, 0x15, 0xb6,
434 | 0x3b, 0xa3, 0x2b, 0x1a, 0x5d, 0x46, 0x80, 0x3d, 0xba, 0x8c, 0x5a, 0x77, 0x8c, 0x9d, 0x8e, 0x3b,
435 | 0xa6, 0x90, 0xa5, 0x19, 0x5e, 0x56, 0xee, 0x42, 0x97, 0x64, 0x4b, 0x52, 0x3c, 0xbb, 0x6e, 0x4b,
436 | 0xda, 0x6c, 0x26, 0x19, 0x9e, 0x1b, 0xd9, 0x4c, 0xb2, 0x36, 0x9b, 0x32, 0xc3, 0xd7, 0x6e, 0x36,
437 | 0x65, 0xc6, 0x24, 0xf1, 0x1c, 0xdf, 0x2a, 0x3e, 0x18, 0xdd, 0xff, 0xfc, 0xbf, 0x1f, 0x79, 0x72,
438 | 0x23, 0xef, 0x02, 0x3f, 0xac, 0xbc, 0x0b, 0x39, 0xf6, 0xbd, 0xe3, 0x72, 0x36, 0x9b, 0xb3, 0xdc,
439 | 0xef, 0xbd, 0x99, 0x7c, 0x61, 0xdb, 0xab, 0x6f, 0xda, 0x61, 0xf7, 0x43, 0xb6, 0x7b, 0x13, 0x38,
440 | 0xb8, 0x35, 0x76, 0x8f, 0x9d, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x8d, 0xaf, 0xf5, 0x75, 0x2b,
441 | 0x05, 0x00, 0x00,
442 | }
443 |
--------------------------------------------------------------------------------
/benchmark/proto/micro_benchmark.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto2";
2 |
3 | package proto;
4 |
5 | option optimize_for = SPEED;
6 |
7 | service Hello {
8 | // Sends a greeting
9 | rpc Say (BenchmarkMessage) returns (BenchmarkMessage) {
10 | }
11 | }
12 |
13 |
14 | message BenchmarkMessage {
15 | required string field1 = 1;
16 | optional string field9 = 9;
17 | optional string field18 = 18;
18 | optional bool field80 = 80 [default = false];
19 | optional bool field81 = 81 [default = true];
20 | required int32 field2 = 2;
21 | required int32 field3 = 3;
22 | optional int32 field280 = 280;
23 | optional int32 field6 = 6 [default = 0];
24 | optional int64 field22 = 22;
25 | optional string field4 = 4;
26 | repeated fixed64 field5 = 5;
27 | optional bool field59 = 59 [default = false];
28 | optional string field7 = 7;
29 | optional int32 field16 = 16;
30 | optional int32 field130 = 130 [default = 0];
31 | optional bool field12 = 12 [default = true];
32 | optional bool field17 = 17 [default = true];
33 | optional bool field13 = 13 [default = true];
34 | optional bool field14 = 14 [default = true];
35 | optional int32 field104 = 104 [default = 0];
36 | optional int32 field100 = 100 [default = 0];
37 | optional int32 field101 = 101 [default = 0];
38 | optional string field102 = 102;
39 | optional string field103 = 103;
40 | optional int32 field29 = 29 [default = 0];
41 | optional bool field30 = 30 [default = false];
42 | optional int32 field60 = 60 [default = -1];
43 | optional int32 field271 = 271 [default = -1];
44 | optional int32 field272 = 272 [default = -1];
45 | optional int32 field150 = 150;
46 | optional int32 field23 = 23 [default = 0];
47 | optional bool field24 = 24 [default = false];
48 | optional int32 field25 = 25 [default = 0];
49 | optional bool field78 = 78;
50 | optional int32 field67 = 67 [default = 0];
51 | optional int32 field68 = 68;
52 | optional int32 field128 = 128 [default = 0];
53 | optional string field129 = 129 [default = "xxxxxxxxxxxxxxxxxxxxx"];
54 | optional int32 field131 = 131 [default = 0];
55 | }
--------------------------------------------------------------------------------
/benchmark/service/client.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "fmt"
7 | "os"
8 | "reflect"
9 | "sync"
10 | "sync/atomic"
11 | "text/tabwriter"
12 | "time"
13 |
14 | "github.com/micro/go-micro/client"
15 | "github.com/micro/go-micro/util/log"
16 | "github.com/montanaflynn/stats"
17 |
18 | "github.com/hb-go/micro/benchmark/proto"
19 | )
20 |
21 | type NewClient func() client.Client
22 |
23 | func ClientWith(serviceName string, nc NewClient, concurrency, total int) {
24 | flag.Parse()
25 | n := concurrency
26 | m := total / n
27 |
28 | log.Logf("service: %s testing", serviceName)
29 | log.Logf("concurrency: %d, requests per client: %d \n", n, m)
30 |
31 | args := prepareArgs()
32 |
33 | // Hystrix配置
34 | // hystrix.ConfigureCommand(serviceName+".Hello.Say", hystrix.CommandConfig{
35 | // MaxConcurrentRequests: hystrix.DefaultMaxConcurrent * 100,
36 | // })
37 |
38 | var wg sync.WaitGroup
39 | wg.Add(n * m)
40 |
41 | var trans uint64
42 | var transOK uint64
43 |
44 | d := make([][]int64, n, n)
45 |
46 | // it contains warmup time but we can ignore it
47 | totalT := time.Now().UnixNano()
48 | for i := 0; i < n; i++ {
49 | dt := make([]int64, 0, m)
50 | d = append(d, dt)
51 |
52 | go func(i int) {
53 | helloClient := nc()
54 | c := proto.NewHelloService(serviceName, helloClient)
55 |
56 | // warmup
57 | for j := 0; j < 5; j++ {
58 | c.Say(context.Background(), args)
59 | }
60 |
61 | for j := 0; j < m; j++ {
62 | t := time.Now().UnixNano()
63 | reply, err := c.Say(context.Background(), args)
64 | t = time.Now().UnixNano() - t
65 |
66 | d[i] = append(d[i], t)
67 |
68 | if err == nil && *reply.Field1 == "OK" {
69 | atomic.AddUint64(&transOK, 1)
70 | } else {
71 | // log.Logf("error:%v", err)
72 | }
73 |
74 | atomic.AddUint64(&trans, 1)
75 | wg.Done()
76 | }
77 | }(i)
78 |
79 | }
80 |
81 | wg.Wait()
82 | totalT = time.Now().UnixNano() - totalT
83 | totalT = totalT / 1000000
84 |
85 | totalD := make([]int64, 0, n*m)
86 | for _, k := range d {
87 | totalD = append(totalD, k...)
88 | }
89 | totalD2 := make([]float64, 0, n*m)
90 | for _, k := range totalD {
91 | totalD2 = append(totalD2, float64(k))
92 | }
93 |
94 | mean, _ := stats.Mean(totalD2)
95 | median, _ := stats.Median(totalD2)
96 | max, _ := stats.Max(totalD2)
97 | min, _ := stats.Min(totalD2)
98 | p90, _ := stats.Percentile(totalD2, 90)
99 | p99, _ := stats.Percentile(totalD2, 99)
100 | tps := int64(n*m) * 1000 / totalT
101 |
102 | w := tabwriter.NewWriter(os.Stdout, 10, 0, 1, ' ', 0)
103 |
104 | fmt.Println()
105 | fmt.Fprintf(w, "took\t(ms)\t: %d\n", totalT)
106 | fmt.Fprintf(w, "sent\trequests\t: %d\n", n*m)
107 | fmt.Fprintf(w, "received\trequests\t: %d\n", atomic.LoadUint64(&trans))
108 | fmt.Fprintf(w, "received\trequests_OK\t: %d\n", atomic.LoadUint64(&transOK))
109 | fmt.Fprintf(w, "throughput\t(TPS)\t: %d\n", tps)
110 | w.Flush()
111 |
112 | fmt.Println()
113 | fmt.Fprintf(w, "concurrency\tmean\tmedian\tmax\tmin\tp90\tp99\tTPS\n")
114 | fmt.Fprintf(w, "%d\t%.fns\t%.fns\t%.fns\t%.fns\t%.fns\t%.fns\t%d\n", concurrency, mean, median, max, min, p90, p99, tps)
115 | fmt.Fprintf(w, "%d\t%.3fms\t%.3fms\t%.3fms\t%.3fms\t%.3fms\t%.3fms\t%d\n", concurrency, mean/1000000, median/1000000, max/1000000, min/1000000, p90/1000000, p99/1000000, tps)
116 | w.Flush()
117 | }
118 |
119 | func prepareArgs() *proto.BenchmarkMessage {
120 | b := true
121 | var i int32 = 100000
122 | var i64 int64 = 100000
123 | var s = "许多往事在眼前一幕一幕,变的那麼模糊"
124 |
125 | var args proto.BenchmarkMessage
126 |
127 | v := reflect.ValueOf(&args).Elem()
128 | num := v.NumField()
129 | for k := 0; k < num; k++ {
130 | field := v.Field(k)
131 | if field.Type().Kind() == reflect.Ptr {
132 | switch v.Field(k).Type().Elem().Kind() {
133 | case reflect.Int, reflect.Int32:
134 | field.Set(reflect.ValueOf(&i))
135 | case reflect.Int64:
136 | field.Set(reflect.ValueOf(&i64))
137 | case reflect.Bool:
138 | field.Set(reflect.ValueOf(&b))
139 | case reflect.String:
140 | field.Set(reflect.ValueOf(&s))
141 | }
142 | } else {
143 | switch field.Kind() {
144 | case reflect.Int, reflect.Int32, reflect.Int64:
145 | field.SetInt(100000)
146 | case reflect.Bool:
147 | field.SetBool(true)
148 | case reflect.String:
149 | field.SetString(s)
150 | }
151 | }
152 |
153 | }
154 | return &args
155 | }
156 |
--------------------------------------------------------------------------------
/benchmark/service/codec.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/golang/protobuf/proto"
5 | )
6 |
7 | // gRPC codec options
8 | type ProtoCodec struct{}
9 |
10 | func (ProtoCodec) Marshal(v interface{}) ([]byte, error) {
11 | return proto.Marshal(v.(proto.Message))
12 | }
13 |
14 | func (ProtoCodec) Unmarshal(data []byte, v interface{}) error {
15 | return proto.Unmarshal(data, v.(proto.Message))
16 | }
17 |
18 | func (ProtoCodec) Name() string {
19 | return "proto"
20 | }
21 |
--------------------------------------------------------------------------------
/benchmark/service/server.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "context"
5 | "time"
6 |
7 | "github.com/micro/go-micro"
8 | "github.com/micro/go-micro/util/log"
9 |
10 | "github.com/hb-go/micro/benchmark/proto"
11 | )
12 |
13 | type HelloS struct{}
14 |
15 | func (t *HelloS) Say(ctx context.Context, args *proto.BenchmarkMessage, reply *proto.BenchmarkMessage) error {
16 | s := "OK"
17 | var i int32 = 100
18 | args.Field1 = &s
19 | args.Field2 = &i
20 | *reply = *args
21 | if delay > 0 {
22 | time.Sleep(delay)
23 | }
24 | return nil
25 | }
26 |
27 | var delay = time.Duration(0)
28 |
29 | func ServeWith(serviceName string, serverOpt, transOpt micro.Option, d time.Duration) {
30 | delay = d
31 | service := micro.NewService(
32 | serverOpt,
33 | micro.Name(serviceName),
34 | micro.Version("latest"),
35 | transOpt,
36 | )
37 |
38 | service.Init(
39 | // handler wrap
40 | // micro.WrapHandler(
41 | // ratelimit.NewHandlerWrapper(10000),
42 | // ),
43 | )
44 |
45 | proto.RegisterHelloHandler(service.Server(), &HelloS{})
46 |
47 | log.Logf("service: %s start", serviceName)
48 |
49 | // Run the server
50 | if err := service.Run(); err != nil {
51 | log.Log(err)
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/benchmark/tcp_rpc/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "time"
7 |
8 | "github.com/micro/go-micro/client"
9 | "github.com/micro/go-micro/client/selector"
10 | "github.com/micro/go-plugins/codec/bsonrpc"
11 | "github.com/micro/go-plugins/codec/jsonrpc2"
12 | "github.com/micro/go-plugins/codec/msgpackrpc"
13 | "github.com/micro/go-plugins/transport/tcp"
14 |
15 | "github.com/hb-go/micro/benchmark/service"
16 | )
17 |
18 | var concurrency = flag.Int("c", 1, "concurrency")
19 | var total = flag.Int("n", 1, "total requests for all clients")
20 | var contentType = flag.String("ct", "protobuf", "content type")
21 |
22 | // "application/grpc": grpc.NewCodec, // TPS:9990
23 | // "application/protobuf": proto.NewCodec, // TPS:11037
24 | // "application/json": json.NewCodec, // TPS:7782
25 | // "application/json-rpc": jsonrpc.NewCodec, // TPS:6544
26 | // "application/proto-rpc": protorpc.NewCodec, // TPS:10869
27 | // "application/octet-stream": raw.NewCodec, // error:{"id":"go.micro.client.codec","code":500,"detail":"failed to write: field1:
28 | // "application/msgpackrpc", msgpackrpc.NewCodec, // 需要实现EncodeMsg(*Writer) error,error:{"id":"go.micro.client.codec","code":500,"detail":"Not encodable","status":"Internal Server Error"}
29 | // "application/bsonrpc", bsonrpc.NewCodec, // TPS:4970
30 | // "application/jsonrpc2", jsonrpc2.NewCodec, // error:{"id":"go.micro.client.transport","code":500,"detail":"EOF","status":"Internal Server Error"}
31 |
32 | func main() {
33 | flag.Parse()
34 | serviceName := "hello.tcp.rpc"
35 | service.ClientWith(
36 | serviceName,
37 | func() client.Client {
38 | cache := selector.NewSelector(func(o *selector.Options) {
39 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Minute*3)
40 | })
41 | return client.NewClient(
42 | client.Codec("application/msgpackrpc", msgpackrpc.NewCodec),
43 | client.Codec("application/bsonrpc", bsonrpc.NewCodec),
44 | client.Codec("application/jsonrpc2", jsonrpc2.NewCodec),
45 | client.Transport(tcp.NewTransport()),
46 | client.ContentType("application/"+*contentType),
47 | client.Selector(cache),
48 | client.Retries(1),
49 | client.PoolSize(*concurrency*2),
50 | client.RequestTimeout(time.Millisecond*100),
51 | // client.Wrap(breaker.NewClientWrapper()),
52 | // client.Wrap(ratelimit.NewClientWrapper(10000)),
53 | )
54 | },
55 | *concurrency,
56 | *total,
57 | )
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/benchmark/tcp_rpc/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 |
6 | "github.com/micro/go-micro"
7 | "github.com/micro/go-micro/server"
8 | "github.com/micro/go-plugins/codec/bsonrpc"
9 | "github.com/micro/go-plugins/codec/jsonrpc2"
10 | "github.com/micro/go-plugins/codec/msgpackrpc"
11 | "github.com/micro/go-plugins/transport/tcp"
12 |
13 | "github.com/hb-go/micro/benchmark/service"
14 | )
15 |
16 | var delay = flag.Duration("delay", 0, "delay to mock business processing")
17 |
18 | func main() {
19 | flag.Parse()
20 |
21 | serviceName := "hello.tcp.rpc"
22 | service.ServeWith(
23 | serviceName,
24 | micro.Server(
25 | server.NewServer(
26 | server.Codec("application/msgpackrpc", msgpackrpc.NewCodec),
27 | server.Codec("application/bsonrpc", bsonrpc.NewCodec),
28 | server.Codec("application/jsonrpc2", jsonrpc2.NewCodec),
29 | )),
30 | micro.Transport(tcp.NewTransport()),
31 | *delay,
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/benchmark/utp_rpc/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "flag"
6 | "time"
7 |
8 | "github.com/micro/go-micro/client"
9 | "github.com/micro/go-micro/client/selector"
10 | "github.com/micro/go-plugins/transport/utp"
11 |
12 | "github.com/hb-go/micro/benchmark/service"
13 | )
14 |
15 | var concurrency = flag.Int("c", 1, "concurrency")
16 | var total = flag.Int("n", 1, "total requests for all clients")
17 | var contentType = flag.String("ct", "application/protobuf", "content type")
18 |
19 | func main() {
20 | flag.Parse()
21 | serviceName := "hello.utp.rpc"
22 | service.ClientWith(
23 | serviceName,
24 | func() client.Client {
25 | cache := selector.NewSelector(func(o *selector.Options) {
26 | o.Context = context.WithValue(o.Context, "selector_ttl", time.Minute*3)
27 | })
28 | return client.NewClient(
29 | client.Transport(utp.NewTransport()),
30 | client.ContentType(*contentType),
31 | client.Selector(cache),
32 | client.Retries(1),
33 | client.PoolSize(*concurrency*2),
34 | client.RequestTimeout(time.Millisecond*100),
35 | // client.Wrap(breaker.NewClientWrapper()),
36 | // client.Wrap(ratelimit.NewClientWrapper(10000)),
37 | )
38 | },
39 | *concurrency,
40 | *total,
41 | )
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/benchmark/utp_rpc/server.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 |
6 | "github.com/micro/go-micro"
7 | "github.com/micro/go-micro/server"
8 | "github.com/micro/go-plugins/transport/utp"
9 |
10 | "github.com/hb-go/micro/benchmark/service"
11 | )
12 |
13 | var delay = flag.Duration("delay", 0, "delay to mock business processing")
14 |
15 | func main() {
16 | flag.Parse()
17 |
18 | serviceName := "hello.utp.rpc"
19 | service.ServeWith(
20 | serviceName,
21 | micro.Server(server.DefaultServer),
22 | micro.Transport(utp.NewTransport()),
23 | *delay,
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/cmd/micro/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | //go:generate ./scripts/generate.sh
4 |
5 | import (
6 | "micro.dev/v4/cmd"
7 |
8 | _ "github.com/hb-chen/micro-starter/cmd/micro/server"
9 | _ "github.com/hb-chen/micro-starter/cmd/micro/service"
10 | _ "github.com/hb-chen/micro-starter/cmd/micro/web"
11 | _ "github.com/hb-chen/micro-starter/profile"
12 | )
13 |
14 | func main() {
15 | cmd.Run()
16 | }
17 |
--------------------------------------------------------------------------------
/cmd/micro/server/auth.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "context"
5 | "strings"
6 |
7 | "micro.dev/v4/service/auth"
8 | metadata "micro.dev/v4/service/context"
9 | "micro.dev/v4/service/errors"
10 | "micro.dev/v4/service/server"
11 | inauth "micro.dev/v4/util/auth"
12 | "micro.dev/v4/util/namespace"
13 | )
14 |
15 | // authHandler wraps a server handler to perform auth
16 | func authHandler() server.HandlerWrapper {
17 | return func(h server.HandlerFunc) server.HandlerFunc {
18 | return func(ctx context.Context, req server.Request, rsp interface{}) error {
19 | // Extract the token if the header is present. We will inspect the token regardless of if it's
20 | // present or not since noop auth will return a blank account upon Inspecting a blank token.
21 | var token string
22 | if header, ok := metadata.Get(ctx, "Authorization"); ok {
23 | // Ensure the correct scheme is being used
24 | if !strings.HasPrefix(header, inauth.BearerScheme) {
25 | return errors.Unauthorized(req.Service(), "invalid authorization header. expected Bearer schema")
26 | }
27 |
28 | // Strip the bearer scheme prefix
29 | token = strings.TrimPrefix(header, inauth.BearerScheme)
30 | }
31 |
32 | // Inspect the token and decode an account
33 | account, _ := auth.Inspect(token)
34 |
35 | // Extract the namespace header
36 | ns, ok := metadata.Get(ctx, "Micro-Namespace")
37 | if !ok && account != nil {
38 | ns = account.Issuer
39 | ctx = metadata.Set(ctx, "Micro-Namespace", ns)
40 | } else if !ok {
41 | ns = namespace.DefaultNamespace
42 | ctx = metadata.Set(ctx, "Micro-Namespace", ns)
43 | }
44 |
45 | // construct the resource
46 | res := &auth.Resource{
47 | Type: "service",
48 | Name: req.Service(),
49 | Endpoint: req.Endpoint(),
50 | }
51 |
52 | // Verify the caller has access to the resource.
53 | err := auth.Verify(account, res, auth.VerifyNamespace(ns))
54 | if err == auth.ErrForbidden && account != nil {
55 | return errors.Forbidden(req.Service(), "Forbidden call made to %v:%v by %v", req.Service(), req.Endpoint(), account.ID)
56 | } else if err == auth.ErrForbidden {
57 | return errors.Unauthorized(req.Service(), "Unauthorized call made to %v:%v", req.Service(), req.Endpoint())
58 | } else if err != nil {
59 | return errors.InternalServerError("proxy", "Error authorizing request: %v", err)
60 | }
61 |
62 | // The user is authorised, allow the call
63 | return h(ctx, req, rsp)
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/cmd/micro/server/plugin.go:
--------------------------------------------------------------------------------
1 | //go:build ignore
2 |
3 | package server
4 |
5 | import (
6 | "fmt"
7 | "plugin"
8 | // "micro.dev/v4/plugin"
9 | )
10 |
11 | var (
12 | defaultManager = plugin.NewManager()
13 | )
14 |
15 | // Plugins lists the server plugins
16 | func Plugins() []plugin.Plugin {
17 | return defaultManager.Plugins()
18 | }
19 |
20 | // Register registers an server plugin
21 | func Register(pl plugin.Plugin) error {
22 | if plugin.IsRegistered(pl) {
23 | return fmt.Errorf("%s registered globally", pl.String())
24 | }
25 | return defaultManager.Register(pl)
26 | }
27 |
--------------------------------------------------------------------------------
/cmd/micro/server/proxy.go:
--------------------------------------------------------------------------------
1 | package server
2 |
3 | import (
4 | "github.com/urfave/cli/v2"
5 | bmem "micro.dev/v4/service/broker/memory"
6 | "micro.dev/v4/service/client"
7 | log "micro.dev/v4/service/logger"
8 | "micro.dev/v4/service/registry/noop"
9 | "micro.dev/v4/service/server"
10 | sgrpc "micro.dev/v4/service/server/grpc"
11 | "micro.dev/v4/util/proxy"
12 | "micro.dev/v4/util/proxy/grpc"
13 | )
14 |
15 | func runProxy(ctx *cli.Context, wait chan bool) error {
16 | addr := ctx.String("proxy_address")
17 |
18 | // set the context
19 | popts := []proxy.Option{
20 | proxy.WithClient(client.DefaultClient),
21 | }
22 |
23 | serverOpts := []server.Option{
24 | server.Address(addr),
25 | server.Registry(noop.NewRegistry()),
26 | server.Broker(bmem.NewBroker()),
27 | }
28 |
29 | // default to the grpc proxy
30 | p := grpc.NewProxy(popts...)
31 |
32 | // wrap the proxy using the proxy's authHandler
33 | authOpt := server.WrapHandler(authHandler())
34 | serverOpts = append(serverOpts, authOpt)
35 | serverOpts = append(serverOpts, server.WithRouter(p))
36 |
37 | // create a new grpc server
38 | srv := sgrpc.NewServer(serverOpts...)
39 |
40 | // Start the proxy server
41 | if err := srv.Start(); err != nil {
42 | log.Fatal(err)
43 | }
44 |
45 | <-wait
46 |
47 | // Stop the server
48 | if err := srv.Stop(); err != nil {
49 | log.Fatal(err)
50 | }
51 |
52 | return nil
53 | }
54 |
--------------------------------------------------------------------------------
/cmd/micro/server/server.go:
--------------------------------------------------------------------------------
1 | // Package server is the micro server which runs the whole system
2 | package server
3 |
4 | import (
5 | "os"
6 | "os/signal"
7 | "strings"
8 | "syscall"
9 | "time"
10 |
11 | "github.com/hb-chen/micro-starter/cmd/micro/service/api"
12 | "github.com/urfave/cli/v2"
13 | "micro.dev/v4/cmd"
14 | "micro.dev/v4/service/client"
15 | log "micro.dev/v4/service/logger"
16 | "micro.dev/v4/service/runtime"
17 | "micro.dev/v4/service/runtime/local"
18 | )
19 |
20 | var (
21 | // list of services managed
22 | services = []string{
23 | "registry", // :8000
24 | "broker", // :8003
25 | "network", // :8443
26 | // "runtime", // :8088
27 | "config", // :8001
28 | // "store", // :8002
29 | // "events", // :8005
30 | // "auth", // :8010
31 | }
32 | )
33 |
34 | func init() {
35 | command := &cli.Command{
36 | Name: "server",
37 | Usage: "Run the micro server",
38 | Description: "Launch the micro server",
39 | Flags: []cli.Flag{
40 | &cli.StringFlag{
41 | Name: "address",
42 | Usage: "Set the micro api address",
43 | EnvVars: []string{"MICRO_API_ADDRESS"},
44 | Value: ":8080",
45 | },
46 | &cli.StringFlag{
47 | Name: "proxy_address",
48 | Usage: "Set the micro proxy address",
49 | EnvVars: []string{"MICRO_PROXY_ADDRESS"},
50 | Value: ":8081",
51 | },
52 | },
53 | Action: func(ctx *cli.Context) error {
54 | Run(ctx)
55 | return nil
56 | },
57 | }
58 |
59 | cmd.Register(command)
60 | }
61 |
62 | func setNetwork() {
63 | client.DefaultClient.Init(
64 | client.Network("127.0.0.1:8443"),
65 | )
66 | }
67 |
68 | // Run runs the entire platform
69 | func Run(context *cli.Context) error {
70 | if context.Args().Len() > 0 {
71 | cli.ShowSubcommandHelp(context)
72 | os.Exit(1)
73 | }
74 |
75 | log.Info("Starting server")
76 |
77 | // parse the env vars
78 | var envvars []string
79 | for _, val := range os.Environ() {
80 | comps := strings.Split(val, "=")
81 | if len(comps) != 2 {
82 | continue
83 | }
84 |
85 | // only process MICRO_ values
86 | if !strings.HasPrefix(comps[0], "MICRO_") {
87 | continue
88 | }
89 |
90 | // skip the profile and proxy, that's set below since it can be service specific
91 | if comps[0] == "MICRO_SERVICE_PROFILE" || comps[0] == "MICRO_SERVICE_NETWORK" {
92 | continue
93 | }
94 |
95 | envvars = append(envvars, val)
96 | }
97 |
98 | // save the runtime
99 | runtimeServer := local.NewRuntime()
100 |
101 | // start the services
102 | for _, service := range services {
103 | // all things run by the server are `micro service [name]`
104 | var cmdArgs []string
105 |
106 | profile := "server"
107 |
108 | env := envvars
109 | env = append(env, "MICRO_SERVICE_NAME="+service)
110 | env = append(env, "MICRO_SERVICE_PROFILE="+profile)
111 |
112 | // we want to pass through the global args so go up one level in the context lineage
113 | if len(context.Lineage()) > 1 {
114 | globCtx := context.Lineage()[1]
115 | for _, f := range globCtx.FlagNames() {
116 | cmdArgs = append(cmdArgs, "--"+f, context.String(f))
117 | }
118 | }
119 | cmdArgs = append(cmdArgs, "service", service)
120 |
121 | // runtime based on environment we run the service in
122 | args := []runtime.CreateOption{
123 | runtime.WithCommand(os.Args[0]),
124 | runtime.WithArgs(cmdArgs...),
125 | runtime.WithEnv(env),
126 | runtime.WithPort("0"),
127 | runtime.WithRetries(10),
128 | }
129 |
130 | log.Infof("Registering %s, args: %v", service, cmdArgs)
131 |
132 | // NOTE: we use Version right now to check for the latest release
133 | muService := &runtime.Service{Name: service, Version: "latest"}
134 | if err := runtimeServer.Create(muService, args...); err != nil {
135 | log.Errorf("Failed to create runtime environment: %v", err)
136 | return err
137 | }
138 | }
139 |
140 | log.Info("Starting runtime")
141 |
142 | // start the runtime
143 | if err := runtimeServer.Start(); err != nil {
144 | log.Fatal(err)
145 | return err
146 | }
147 |
148 | // start the proxy
149 | wait := make(chan bool)
150 |
151 | setNetwork()
152 |
153 | // run the proxy
154 | go runProxy(context, wait)
155 |
156 | // run the api
157 | apiCmd := api.NewAPI(wait)
158 | go apiCmd.Run(context)
159 |
160 | ch := make(chan os.Signal, 1)
161 | signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL)
162 | <-ch
163 |
164 | log.Info("Stopping server")
165 |
166 | // close wait chan
167 | close(wait)
168 |
169 | // stop the runtime
170 | runtimeServer.Stop()
171 |
172 | // just wait 1 sec
173 | <-time.After(time.Second)
174 |
175 | return nil
176 | }
177 |
--------------------------------------------------------------------------------
/cmd/micro/service/api/api.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "os"
7 | "os/signal"
8 | "syscall"
9 |
10 | "github.com/gorilla/mux"
11 | "github.com/urfave/cli/v2"
12 | "micro.dev/v4/service/api"
13 | "micro.dev/v4/service/api/handler"
14 | "micro.dev/v4/service/api/handler/rpc"
15 | "micro.dev/v4/service/api/router"
16 | regRouter "micro.dev/v4/service/api/router/registry"
17 | httpapi "micro.dev/v4/service/api/server/http"
18 | "micro.dev/v4/service/client"
19 | "micro.dev/v4/service/logger"
20 | "micro.dev/v4/service/registry"
21 | )
22 |
23 | type API struct {
24 | wait chan bool
25 | }
26 |
27 | func NewAPI(wait chan bool) *API {
28 | return &API{
29 | wait: wait,
30 | }
31 | }
32 |
33 | func Run(ctx *cli.Context) error {
34 | // start the proxy
35 | wait := make(chan bool)
36 |
37 | apiCmd := NewAPI(wait)
38 |
39 | go func() {
40 | if err := apiCmd.Run(ctx); err != nil {
41 | logger.Fatal(err)
42 | }
43 | }()
44 |
45 | ch := make(chan os.Signal, 1)
46 | signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGKILL)
47 | <-ch
48 |
49 | // close wait chan
50 | close(wait)
51 |
52 | return nil
53 | }
54 |
55 | func (a *API) Run(ctx *cli.Context) error {
56 | addr := ctx.String("address")
57 |
58 | // Init API
59 | var opts []api.Option
60 |
61 | opts = append(opts, api.EnableCORS(true))
62 |
63 | // create the router
64 | var h http.Handler
65 | r := mux.NewRouter()
66 | h = r
67 |
68 | // return version and list of services
69 | r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
70 | if r.Method == "OPTIONS" {
71 | return
72 | }
73 |
74 | response := fmt.Sprintf(`{"version": "%s"}`, ctx.App.Version)
75 | w.Write([]byte(response))
76 | })
77 |
78 | // strip favicon.ico
79 | r.HandleFunc("/favicon.ico", func(w http.ResponseWriter, r *http.Request) {})
80 | rt := regRouter.NewRouter(router.WithRegistry(registry.DefaultRegistry))
81 | r.PathPrefix("/").Handler(rpc.NewHandler(
82 | handler.WithClient(client.DefaultClient),
83 | handler.WithRouter(rt),
84 | ))
85 |
86 | // create a new api server with wrappers
87 | api := httpapi.NewServer(addr)
88 |
89 | // initialise
90 | api.Init(opts...)
91 | // register the http handler
92 | api.Handle("/", authWrapper()(h))
93 |
94 | // Start API
95 | if err := api.Start(); err != nil {
96 | logger.Fatal(err)
97 | }
98 |
99 | // wait to stop
100 | <-a.wait
101 |
102 | // Stop API
103 | if err := api.Stop(); err != nil {
104 | logger.Fatal(err)
105 | }
106 |
107 | return nil
108 | }
109 |
--------------------------------------------------------------------------------
/cmd/micro/service/api/auth.go:
--------------------------------------------------------------------------------
1 | package api
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "strings"
7 |
8 | "micro.dev/v4/service/api"
9 | "micro.dev/v4/service/api/router/registry"
10 | "micro.dev/v4/service/auth"
11 | "micro.dev/v4/service/logger"
12 | inauth "micro.dev/v4/util/auth"
13 | "micro.dev/v4/util/ctx"
14 | "micro.dev/v4/util/namespace"
15 | )
16 |
17 | func authWrapper() api.Wrapper {
18 | resolver := registry.NewResolver()
19 |
20 | return func(h http.Handler) http.Handler {
21 | return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
22 | // Determine the name of the service being requested
23 | endpoint := resolver.Resolve(req)
24 | ctrx := context.WithValue(req.Context(), registry.Endpoint{}, endpoint)
25 | *req = *req.Clone(ctrx)
26 |
27 | // Set the metadata so we can access it in micro api / web
28 | req = req.WithContext(ctx.FromRequest(req))
29 |
30 | // Extract the token from the request
31 | var token string
32 | if header := req.Header.Get("Authorization"); len(header) > 0 {
33 | // Extract the auth token from the request
34 | if strings.HasPrefix(header, inauth.BearerScheme) {
35 | token = header[len(inauth.BearerScheme):]
36 | }
37 | } else {
38 | // Get the token out the cookies if not provided in headers
39 | if c, err := req.Cookie("micro-token"); err == nil && c != nil {
40 | token = strings.TrimPrefix(c.Value, inauth.TokenCookieName+"=")
41 | req.Header.Set("Authorization", inauth.BearerScheme+token)
42 | }
43 | }
44 |
45 | // Get the account using the token, some are unauthenticated, so the lack of an
46 | // account doesn't necessarily mean a forbidden request
47 | acc, err := auth.Inspect(token)
48 | if err == nil {
49 | // inject into the context
50 | ctx := auth.ContextWithAccount(req.Context(), acc)
51 | *req = *req.Clone(ctx)
52 | }
53 |
54 | // Determine the namespace and set it in the header. If the user passed auth creds
55 | // on the request, use the namespace that issued the account, otherwise check for
56 | // the domain of the resolved endpoint.
57 | ns := req.Header.Get(namespace.NamespaceKey)
58 | if len(ns) == 0 && acc != nil {
59 | ns = acc.Issuer
60 | req.Header.Set(namespace.NamespaceKey, ns)
61 | } else if len(ns) == 0 {
62 | ns = endpoint.Domain
63 | req.Header.Set(namespace.NamespaceKey, ns)
64 | }
65 |
66 | // Ensure accounts only issued by the namespace are valid.
67 | if acc != nil && acc.Issuer != ns {
68 | acc = nil
69 | }
70 |
71 | // construct the resource name, e.g. home => foo.api.home
72 | resName := endpoint.Name
73 | resEndpoint := endpoint.Method
74 |
75 | // Options to use when verifying the request
76 | verifyOpts := []auth.VerifyOption{
77 | auth.VerifyContext(req.Context()),
78 | auth.VerifyNamespace(ns),
79 | }
80 |
81 | logger.Debugf("Resolving %v %v", resName, resEndpoint)
82 |
83 | // Perform the verification check to see if the account has access to
84 | // the resource they're requesting
85 | res := &auth.Resource{Type: "service", Name: resName, Endpoint: resEndpoint}
86 | if err := auth.Verify(acc, res, verifyOpts...); err == nil {
87 | // The account has the necessary permissions to access the resource
88 | h.ServeHTTP(w, req)
89 | return
90 | } else if err != auth.ErrForbidden {
91 | http.Error(w, err.Error(), http.StatusInternalServerError)
92 | return
93 | }
94 |
95 | // The account is set, but they don't have enough permissions, hence
96 | // we return a forbidden error.
97 | if acc != nil {
98 | http.Error(w, "Forbidden request", http.StatusForbidden)
99 | return
100 | }
101 |
102 | http.Error(w, "unauthorized request", http.StatusUnauthorized)
103 | return
104 | })
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/cmd/micro/service/service.go:
--------------------------------------------------------------------------------
1 | package service
2 |
3 | import (
4 | "github.com/hb-chen/micro-starter/cmd/micro/service/api"
5 | "github.com/urfave/cli/v2"
6 | "micro.dev/v4/cmd"
7 |
8 | // services
9 | auth "micro.dev/v4/service/auth/server"
10 | broker "micro.dev/v4/service/broker/server"
11 | config "micro.dev/v4/service/config/server"
12 | events "micro.dev/v4/service/events/server"
13 | network "micro.dev/v4/service/network/server"
14 | registry "micro.dev/v4/service/registry/server"
15 | runtime "micro.dev/v4/service/runtime/server"
16 | store "micro.dev/v4/service/store/server"
17 | )
18 |
19 | type srvCommand struct {
20 | Name string
21 | Command cli.ActionFunc
22 | After cli.AfterFunc
23 | Flags []cli.Flag
24 | }
25 |
26 | var apiCmd = func(*cli.Context) error {
27 | return nil
28 | }
29 | var srvCommands = []srvCommand{
30 | {
31 | Name: "api",
32 | Command: api.Run,
33 | },
34 | {
35 | Name: "auth",
36 | Command: auth.Run,
37 | },
38 | {
39 | Name: "broker",
40 | Command: broker.Run,
41 | },
42 | {
43 | Name: "config",
44 | Command: config.Run,
45 | Flags: config.Flags,
46 | },
47 | {
48 | Name: "events",
49 | Command: events.Run,
50 | },
51 | {
52 | Name: "network",
53 | Command: network.Run,
54 | Flags: network.Flags,
55 | },
56 | {
57 | Name: "registry",
58 | Command: registry.Run,
59 | },
60 | {
61 | Name: "runtime",
62 | Command: runtime.Run,
63 | },
64 | {
65 | Name: "store",
66 | Command: store.Run,
67 | },
68 | }
69 |
70 | func init() {
71 | flags := []cli.Flag{
72 | &cli.StringFlag{
73 | Name: "name",
74 | Usage: "Name of the service",
75 | EnvVars: []string{"MICRO_SERVICE_NAME"},
76 | Value: "service",
77 | },
78 | &cli.StringFlag{
79 | Name: "address",
80 | Usage: "Address of the service",
81 | EnvVars: []string{"MICRO_SERVICE_ADDRESS"},
82 | },
83 | }
84 |
85 | subcommands := make([]*cli.Command, len(srvCommands))
86 |
87 | for i, c := range srvCommands {
88 | // construct the command
89 | command := &cli.Command{
90 | Name: c.Name,
91 | Flags: c.Flags,
92 | Action: c.Command,
93 | After: c.After,
94 | }
95 |
96 | // set the command
97 | subcommands[i] = command
98 | }
99 |
100 | command := &cli.Command{
101 | Name: "service",
102 | Usage: "Run the micro service",
103 | Flags: flags,
104 | Subcommands: subcommands,
105 | }
106 |
107 | cmd.Register(command)
108 | }
109 |
--------------------------------------------------------------------------------
/cmd/micro/web/README.md:
--------------------------------------------------------------------------------
1 | # Micro Web
2 |
3 | A web app for Micro
4 |
5 | ## Usage
6 |
7 | Specify the API Address (Defaults to localhost:8080)
8 |
9 | ```
10 | MICRO_API_ADDRESS=https://api.m3o.com
11 | ```
12 |
13 | Run the dashboard
14 |
15 | ```
16 | micro web
17 | ```
18 |
19 | Go to `localhost:8082`
20 |
--------------------------------------------------------------------------------
/cmd/micro/web/html/assets/mu.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | font-size: 14px;
3 | font-family: arial, sans-serif;
4 | }
5 | #header,#container,#footer {
6 | max-width: 1400px;
7 | margin: 0 auto;
8 | padding: 2em;
9 | }
10 | #container {
11 | max-width: 1024px;
12 | }
13 | a { color: #333333; text-decoration: none; }
14 | a:hover { text-decoration: underline; }
15 | #error {
16 | color: red;
17 | }
18 | #login, #error {
19 | margin: 0 auto;
20 | max-width: 400px;
21 | }
22 | #logo {
23 | font-size: 2em;
24 | font-weight: 600;
25 | text-decoration: none;
26 | }
27 | #links {
28 | float: right;
29 | font-size: 0.8em;
30 | font-weight: normal;
31 | }
32 | #menu {
33 | float: right;
34 | list-style: none;
35 | }
36 | #menu li {
37 | display: inline-block;
38 | margin-left: 10px;
39 | }
40 | #heading {
41 | margin-bottom: 10px;
42 | font-size: 1.5em;
43 | text-transform: capitalize;
44 | }
45 | button:hover {
46 | cursor: pointer;
47 | }
48 | .response {
49 | margin-bottom: 10px;
50 | overflow-wrap: break-word;
51 | }
52 | .search {
53 | width: 100%;
54 | position: relative;
55 | margin: 0 auto;
56 | border-radius: 0;
57 | border: 0;
58 | box-shadow: none;
59 | border-bottom: 1px solid #ccc;
60 | }
61 | .search:focus {
62 | border-color: transparent;
63 | outline: 0;
64 | box-shadow: none;
65 | border-bottom: 1px solid #ccc;
66 | }
67 | #services {
68 | height: calc(100vh - 250px);
69 | width: 100%;
70 | max-width: 1024px;
71 | position: absolute;
72 | overflow: auto;
73 | background: white;
74 | z-index: 1;
75 | }
76 | .field {
77 | margin-bottom: 3px;
78 | }
79 | .key, .title {
80 | text-transform: capitalize;
81 | }
82 | .key {
83 | color: grey;
84 | }
85 | .table>tbody>tr>th, .table>tbody>tr>td {
86 | border-top: none;
87 | }
88 | .endpoint, .service, .query {
89 | margin-bottom: 5px;
90 | cursor: pointer;
91 | display: block;
92 | }
93 | .bold {
94 | font-weight: bold;
95 | }
96 | .user {
97 | padding: 15px;
98 | }
99 | form {
100 | display: flex;
101 | flex-direction: column;
102 | }
103 | .form-control {
104 | border: 1px solid #ccc;
105 | }
106 | .form-group {
107 | margin-bottom: 0px;
108 | }
109 | input {
110 | margin-bottom: 10px;
111 | outline: none;
112 | height: 25px;
113 | }
114 | label {
115 | margin-bottom: 3px;
116 | }
117 | pre {
118 | padding: 20px;
119 | border: 1px solid #ccc;
120 | overflow: auto;
121 | word-wrap: break-word;
122 | }
123 | textarea#request {
124 | margin-bottom: 10px;
125 | }
126 | @media only screen and (max-width: 500px) {
127 | }
128 |
--------------------------------------------------------------------------------
/cmd/micro/web/html/assets/mu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hb-chen/micro-starter/045d1f4f99d50c96857b34fb1eced2c419eb6507/cmd/micro/web/html/assets/mu.png
--------------------------------------------------------------------------------
/cmd/micro/web/html/template.go:
--------------------------------------------------------------------------------
1 | package html
2 |
3 | var (
4 | LoginTemplate = `
5 | {{define "title"}}Login{{end}}
6 | {{define "heading"}}{{end}}
7 | {{define "style" }}{{end}}
8 | {{define "content"}}
9 |
The requested page could not be found
{{end}}` 88 | ) 89 | -------------------------------------------------------------------------------- /cmd/micro/web/web.go: -------------------------------------------------------------------------------- 1 | // Package web is a web dashboard 2 | package web 3 | 4 | import ( 5 | "context" 6 | "embed" 7 | "html/template" 8 | "io/fs" 9 | "log" 10 | "net/http" 11 | "net/url" 12 | "os" 13 | "path/filepath" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | "github.com/gorilla/mux" 19 | "github.com/urfave/cli/v2" 20 | "micro.dev/v4/cmd" 21 | "micro.dev/v4/cmd/web/html" 22 | ) 23 | 24 | // Meta Fields of micro web 25 | var ( 26 | Name = "web" 27 | API = "http://localhost:8080" 28 | Address = ":8082" 29 | Namespace = "micro" 30 | LoginURL = "/login" 31 | // Host name the web dashboard is served on 32 | Host, _ = os.Hostname() 33 | // Token cookie name 34 | TokenCookieName = "micro-token" 35 | 36 | // create a session store 37 | mtx sync.RWMutex 38 | sessions = map[string]*session{} 39 | ) 40 | 41 | type srv struct { 42 | *mux.Router 43 | } 44 | 45 | type session struct { 46 | // token used for the session 47 | Token string 48 | } 49 | 50 | //go:embed html/* html/assets/* 51 | var content embed.FS 52 | 53 | func init() { 54 | cmd.Register( 55 | &cli.Command{ 56 | Name: "web", 57 | Usage: "Run the micro web UI", 58 | Action: Run, 59 | Flags: Flags, 60 | }, 61 | ) 62 | } 63 | 64 | // ServeHTTP serves the web dashboard and proxies where appropriate 65 | func (s *srv) ServeHTTP(w http.ResponseWriter, r *http.Request) { 66 | if strings.HasPrefix(r.URL.Path, "/assets/") { 67 | s.Router.ServeHTTP(w, r) 68 | return 69 | } 70 | 71 | // check if authenticated 72 | if r.URL.Path != LoginURL { 73 | c, err := r.Cookie(TokenCookieName) 74 | if err != nil || c == nil { 75 | http.Redirect(w, r, LoginURL, 302) 76 | return 77 | } 78 | 79 | // check the token is valid 80 | token := strings.TrimPrefix(c.Value, TokenCookieName+"=") 81 | if len(token) == 0 { 82 | http.Redirect(w, r, LoginURL, 302) 83 | return 84 | } 85 | 86 | // if we have a session retrieve it 87 | mtx.RLock() 88 | sess, ok := sessions[token] 89 | mtx.RUnlock() 90 | 91 | // no session, go get the account 92 | if !ok { 93 | // save the session 94 | mtx.Lock() 95 | sess = &session{ 96 | Token: token, 97 | } 98 | sessions[token] = sess 99 | mtx.Unlock() 100 | } 101 | 102 | // create a new context 103 | ctx := context.WithValue(r.Context(), session{}, sess) 104 | 105 | // redefine request with context 106 | r = r.Clone(ctx) 107 | } 108 | 109 | // set defaults on the request 110 | if len(r.URL.Host) == 0 { 111 | r.URL.Host = r.Host 112 | } 113 | if len(r.URL.Scheme) == 0 { 114 | r.URL.Scheme = "http" 115 | } 116 | 117 | s.Router.ServeHTTP(w, r) 118 | } 119 | 120 | func faviconHandler(w http.ResponseWriter, r *http.Request) { 121 | return 122 | } 123 | 124 | func (s *srv) notFoundHandler(w http.ResponseWriter, r *http.Request) { 125 | w.WriteHeader(http.StatusNotFound) 126 | s.render(w, r, html.NotFoundTemplate, nil) 127 | } 128 | 129 | func (s *srv) indexHandler(w http.ResponseWriter, r *http.Request) { 130 | if r.Method == "OPTIONS" { 131 | return 132 | } 133 | s.render(w, r, html.IndexTemplate, struct{}{}) 134 | } 135 | 136 | func (s *srv) loginHandler(w http.ResponseWriter, req *http.Request) { 137 | s.render(w, req, html.LoginTemplate, struct{}{}) 138 | } 139 | 140 | func (s *srv) logoutHandler(w http.ResponseWriter, req *http.Request) { 141 | http.SetCookie(w, &http.Cookie{ 142 | Name: TokenCookieName, 143 | Value: "", 144 | Expires: time.Unix(0, 0), 145 | Secure: true, 146 | }) 147 | 148 | http.Redirect(w, req, "/", 302) 149 | } 150 | 151 | type templateValue struct { 152 | Key string 153 | Value interface{} 154 | } 155 | 156 | func (s *srv) render(w http.ResponseWriter, r *http.Request, tmpl string, data interface{}, vals ...templateValue) { 157 | t, err := template.New("template").Funcs(template.FuncMap{ 158 | "Title": strings.Title, 159 | "First": func(s string) string { 160 | if len(s) == 0 { 161 | return s 162 | } 163 | return strings.Title(string(s[0])) 164 | }, 165 | "Endpoint": func(ep string) string { 166 | return strings.Replace(ep, ".", "/", -1) 167 | }, 168 | }).Parse(html.LayoutTemplate) 169 | if err != nil { 170 | http.Error(w, "Error occurred:"+err.Error(), 500) 171 | return 172 | } 173 | t, err = t.Parse(tmpl) 174 | if err != nil { 175 | http.Error(w, "Error occurred:"+err.Error(), 500) 176 | return 177 | } 178 | 179 | apiURL := API 180 | 181 | // set api from the hdear if available 182 | if v := r.Header.Get("Micro-API"); len(v) > 0 { 183 | apiURL = v 184 | } 185 | 186 | u, err := url.Parse(apiURL) 187 | if err != nil { 188 | http.Error(w, "Error occurred:"+err.Error(), 500) 189 | return 190 | } 191 | 192 | filepath.Join(u.Path, r.URL.Path) 193 | 194 | var token string 195 | 196 | sess, ok := r.Context().Value(session{}).(*session) 197 | if ok { 198 | token = sess.Token 199 | } 200 | 201 | templateData := map[string]interface{}{ 202 | "ApiURL": template.URL(apiURL), 203 | "Token": token, 204 | "Results": data, 205 | "Namespace": Namespace, 206 | } 207 | 208 | // add extra values 209 | for _, val := range vals { 210 | templateData[val.Key] = val.Value 211 | } 212 | 213 | if err := t.ExecuteTemplate(w, "layout", 214 | templateData, 215 | ); err != nil { 216 | http.Error(w, "Error occurred:"+err.Error(), 500) 217 | } 218 | } 219 | 220 | func Run(ctx *cli.Context) error { 221 | if len(ctx.String("api_address")) > 0 { 222 | API = ctx.String("api_address") 223 | } 224 | if len(ctx.String("server_name")) > 0 { 225 | Name = ctx.String("server_name") 226 | } 227 | if len(ctx.String("web_address")) > 0 { 228 | Address = ctx.String("web_address") 229 | } 230 | if len(ctx.String("web_namespace")) > 0 { 231 | Namespace = ctx.String("web_namespace") 232 | } 233 | if len(ctx.String("web_host")) > 0 { 234 | Host = ctx.String("web_host") 235 | } 236 | if len(ctx.String("namespace")) > 0 { 237 | // remove the service type from the namespace to allow for 238 | // backwards compatability 239 | Namespace = ctx.String("namespace") 240 | } 241 | // Setup auth redirect 242 | if len(ctx.String("login_url")) > 0 { 243 | LoginURL = ctx.String("login_url") 244 | } 245 | 246 | srv := &srv{ 247 | Router: mux.NewRouter(), 248 | } 249 | 250 | htmlContent, err := fs.Sub(content, "html") 251 | if err != nil { 252 | log.Fatal(err) 253 | } 254 | 255 | // the web handler itself 256 | srv.HandleFunc("/favicon.ico", faviconHandler) 257 | srv.HandleFunc("/404", srv.notFoundHandler) 258 | srv.HandleFunc("/login", srv.loginHandler) 259 | srv.HandleFunc("/logout", srv.logoutHandler) 260 | srv.PathPrefix("/assets/").Handler(http.FileServer(http.FS(htmlContent))) 261 | srv.HandleFunc("/", srv.indexHandler) 262 | srv.HandleFunc("/services", srv.indexHandler) 263 | srv.HandleFunc("/{service}", srv.indexHandler) 264 | srv.HandleFunc("/{service}/{endpoint}", srv.indexHandler) 265 | srv.HandleFunc("/{service}/{endpoint}/{method}", srv.indexHandler) 266 | 267 | // create new http server 268 | server := &http.Server{ 269 | Addr: Address, 270 | Handler: srv, 271 | } 272 | 273 | if err := server.ListenAndServe(); err != nil { 274 | log.Fatal(err) 275 | } 276 | 277 | return nil 278 | } 279 | 280 | var ( 281 | Flags = []cli.Flag{ 282 | &cli.StringFlag{ 283 | Name: "api_address", 284 | Usage: "Set the api address to call e.g http://localhost:8080", 285 | EnvVars: []string{"MICRO_API_ADDRESS"}, 286 | }, 287 | &cli.StringFlag{ 288 | Name: "web_address", 289 | Usage: "Set the web UI address e.g 0.0.0.0:8082", 290 | EnvVars: []string{"MICRO_WEB_ADDRESS"}, 291 | }, 292 | &cli.StringFlag{ 293 | Name: "namespace", 294 | Usage: "Set the namespace used by the Web proxy e.g. com.example.web", 295 | EnvVars: []string{"MICRO_WEB_NAMESPACE"}, 296 | }, 297 | &cli.StringFlag{ 298 | Name: "login_url", 299 | EnvVars: []string{"MICRO_WEB_LOGIN_URL"}, 300 | Usage: "The relative URL where a user can login", 301 | }, 302 | } 303 | ) 304 | -------------------------------------------------------------------------------- /doc/img/micro-istio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hb-chen/micro-starter/045d1f4f99d50c96857b34fb1eced2c419eb6507/doc/img/micro-istio.png -------------------------------------------------------------------------------- /doc/img/micro.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hb-chen/micro-starter/045d1f4f99d50c96857b34fb1eced2c419eb6507/doc/img/micro.jpg -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hb-chen/micro-starter 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/go-sql-driver/mysql v1.7.0 7 | github.com/golang/protobuf v1.5.3 8 | github.com/google/uuid v1.3.0 9 | github.com/gorilla/mux v1.7.3 10 | github.com/urfave/cli/v2 v2.3.0 11 | go.uber.org/dig v1.13.0 12 | google.golang.org/protobuf v1.28.1 13 | gorm.io/driver/mysql v1.4.5 14 | gorm.io/gorm v1.24.3 15 | gorm.io/plugin/dbresolver v1.4.0 16 | micro.dev/v4 v4.3.1 17 | ) 18 | 19 | require ( 20 | github.com/bitly/go-simplejson v0.5.0 // indirect 21 | github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect 22 | github.com/davecgh/go-spew v1.1.1 // indirect 23 | github.com/evanphx/json-patch/v5 v5.5.0 // indirect 24 | github.com/felixge/httpsnoop v1.0.1 // indirect 25 | github.com/golang-jwt/jwt v0.0.0-20210529014511-0f726ea0e725 // indirect 26 | github.com/gorilla/handlers v1.5.1 // indirect 27 | github.com/gorilla/websocket v1.4.2 // indirect 28 | github.com/hpcloud/tail v1.0.0 // indirect 29 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 30 | github.com/jackc/pgconn v1.13.0 // indirect 31 | github.com/jackc/pgio v1.0.0 // indirect 32 | github.com/jackc/pgpassfile v1.0.0 // indirect 33 | github.com/jackc/pgproto3/v2 v2.3.1 // indirect 34 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect 35 | github.com/jackc/pgtype v1.12.0 // indirect 36 | github.com/jackc/pgx/v4 v4.17.2 // indirect 37 | github.com/jinzhu/inflection v1.0.0 // indirect 38 | github.com/jinzhu/now v1.1.5 // indirect 39 | github.com/mattn/go-sqlite3 v1.14.15 // indirect 40 | github.com/nightlyone/lockfile v1.0.0 // indirect 41 | github.com/onsi/gomega v1.27.6 // indirect 42 | github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect 43 | github.com/patrickmn/go-cache v2.1.0+incompatible // indirect 44 | github.com/pkg/errors v0.9.1 // indirect 45 | github.com/pmezard/go-difflib v1.0.0 // indirect 46 | github.com/rogpeppe/go-internal v1.9.0 // indirect 47 | github.com/russross/blackfriday/v2 v2.0.1 // indirect 48 | github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect 49 | github.com/stretchr/objx v0.5.0 // indirect 50 | github.com/stretchr/testify v1.8.1 // indirect 51 | github.com/teris-io/shortid v0.0.0-20171029131806-771a37caa5cf // indirect 52 | go.etcd.io/bbolt v1.3.5 // indirect 53 | golang.org/x/crypto v0.4.0 // indirect 54 | golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect 55 | golang.org/x/net v0.10.0 // indirect 56 | golang.org/x/sys v0.8.0 // indirect 57 | golang.org/x/text v0.9.0 // indirect 58 | golang.org/x/tools v0.9.1 // indirect 59 | google.golang.org/genproto v0.0.0-20230306155012-7f2fa6fef1f4 // indirect 60 | google.golang.org/grpc v1.54.1 // indirect 61 | gopkg.in/fsnotify.v1 v1.4.7 // indirect 62 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 63 | gopkg.in/yaml.v3 v3.0.1 // indirect 64 | gorm.io/driver/postgres v1.4.5 // indirect 65 | gorm.io/driver/sqlite v1.4.3 // indirect 66 | ) 67 | 68 | replace micro.dev/v4 => github.com/micro/micro/v4 v4.3.1 69 | -------------------------------------------------------------------------------- /manifests/buildpacks/project.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | id = "com.hbchen.micro" 3 | name = "Micro" 4 | version = "3.8.0" 5 | 6 | [build] 7 | exclude = [ 8 | "../../README.md", 9 | "../../LICENSE" 10 | ] 11 | 12 | [[build.buildpacks]] 13 | uri = "paketobuildpacks/go:4" 14 | 15 | [[ build.env ]] 16 | name="BP_GO_VERSION" 17 | value="1.20" 18 | 19 | [[ build.env ]] 20 | name="BP_GO_TARGETS" 21 | value="./cmd/micro" 22 | 23 | [[ build.env ]] 24 | name="BP_GO_BUILD_LDFLAGS" 25 | value="-s -w" 26 | 27 | [[ build.env ]] 28 | name="GOPROXY" 29 | value="https://mirrors.aliyun.com/goproxy/,direct" -------------------------------------------------------------------------------- /manifests/charts/micro/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /manifests/charts/micro/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: micro 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.16.0" 25 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | 1. Get the application URL by running these commands: 2 | {{- if .Values.ingress.enabled }} 3 | {{- range $host := .Values.ingress.hosts }} 4 | {{- range .paths }} 5 | http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} 6 | {{- end }} 7 | {{- end }} 8 | {{- else if contains "NodePort" .Values.service.type }} 9 | export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "micro.fullname" . }}) 10 | export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") 11 | echo http://$NODE_IP:$NODE_PORT 12 | {{- else if contains "LoadBalancer" .Values.service.type }} 13 | NOTE: It may take a few minutes for the LoadBalancer IP to be available. 14 | You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "micro.fullname" . }}' 15 | export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "micro.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") 16 | echo http://$SERVICE_IP:{{ .Values.service.port }} 17 | {{- else if contains "ClusterIP" .Values.service.type }} 18 | export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "micro.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") 19 | export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") 20 | echo "Visit http://127.0.0.1:8080 to use your application" 21 | kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT 22 | {{- end }} 23 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "micro.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "micro.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "micro.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "micro.labels" -}} 37 | helm.sh/chart: {{ include "micro.chart" . }} 38 | {{ include "micro.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "micro.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "micro.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "micro.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "micro.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "micro.fullname" . }} 5 | labels: 6 | {{- include "micro.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "micro.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "micro.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "micro.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | env: 37 | - name: MICRO_SERVICE_PROFILE 38 | value: starter-kubernetes 39 | - name: MICRO_API_ADDRESS 40 | value: :8080 41 | - name: MICRO_PROXY_ADDRESS 42 | value: :8081 43 | command: 44 | - "./micro" 45 | args: 46 | - --profile 47 | - starter-kubernetes 48 | - server 49 | ports: 50 | - name: http 51 | containerPort: 8080 52 | protocol: TCP 53 | - name: registry 54 | containerPort: 8000 55 | protocol: TCP 56 | livenessProbe: 57 | httpGet: 58 | path: / 59 | port: http 60 | readinessProbe: 61 | httpGet: 62 | path: / 63 | port: http 64 | resources: 65 | {{- toYaml .Values.resources | nindent 12 }} 66 | {{- with .Values.nodeSelector }} 67 | nodeSelector: 68 | {{- toYaml . | nindent 8 }} 69 | {{- end }} 70 | {{- with .Values.affinity }} 71 | affinity: 72 | {{- toYaml . | nindent 8 }} 73 | {{- end }} 74 | {{- with .Values.tolerations }} 75 | tolerations: 76 | {{- toYaml . | nindent 8 }} 77 | {{- end }} 78 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "micro.fullname" . }} 6 | labels: 7 | {{- include "micro.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "micro.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "micro.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | {{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 5 | {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} 6 | {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} 7 | {{- end }} 8 | {{- end }} 9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 10 | apiVersion: networking.k8s.io/v1 11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 12 | apiVersion: networking.k8s.io/v1beta1 13 | {{- else -}} 14 | apiVersion: extensions/v1beta1 15 | {{- end }} 16 | kind: Ingress 17 | metadata: 18 | name: {{ $fullName }} 19 | labels: 20 | {{- include "micro.labels" . | nindent 4 }} 21 | {{- with .Values.ingress.annotations }} 22 | annotations: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | spec: 26 | {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 27 | ingressClassName: {{ .Values.ingress.className }} 28 | {{- end }} 29 | {{- if .Values.ingress.tls }} 30 | tls: 31 | {{- range .Values.ingress.tls }} 32 | - hosts: 33 | {{- range .hosts }} 34 | - {{ . | quote }} 35 | {{- end }} 36 | secretName: {{ .secretName }} 37 | {{- end }} 38 | {{- end }} 39 | rules: 40 | {{- range .Values.ingress.hosts }} 41 | - host: {{ .host | quote }} 42 | http: 43 | paths: 44 | {{- range .paths }} 45 | - path: {{ .path }} 46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 47 | pathType: {{ .pathType }} 48 | {{- end }} 49 | backend: 50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 51 | service: 52 | name: {{ $fullName }} 53 | port: 54 | number: {{ $svcPort }} 55 | {{- else }} 56 | serviceName: {{ $fullName }} 57 | servicePort: {{ $svcPort }} 58 | {{- end }} 59 | {{- end }} 60 | {{- end }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "micro.fullname" . }} 5 | labels: 6 | {{- include "micro.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | - port: 8000 15 | targetPort: registry 16 | protocol: TCP 17 | name: registry 18 | selector: 19 | {{- include "micro.selectorLabels" . | nindent 4 }} 20 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "micro.serviceAccountName" . }} 6 | labels: 7 | {{- include "micro.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /manifests/charts/micro/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.tests.enabled -}} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: "{{ include "micro.fullname" . }}-test-connection" 6 | labels: 7 | {{- include "micro.labels" . | nindent 4 }} 8 | annotations: 9 | "helm.sh/hook": test 10 | spec: 11 | containers: 12 | - name: wget 13 | image: busybox 14 | command: ['wget'] 15 | args: ['{{ include "micro.fullname" . }}:{{ .Values.service.port }}'] 16 | restartPolicy: Never 17 | {{- end }} -------------------------------------------------------------------------------- /manifests/charts/micro/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for micro. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart appVersion. 11 | tag: "latest" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Annotations to add to the service account 21 | annotations: {} 22 | # The name of the service account to use. 23 | # If not set and create is true, a name is generated using the fullname template 24 | name: "" 25 | 26 | podAnnotations: {} 27 | 28 | podSecurityContext: {} 29 | # fsGroup: 2000 30 | 31 | securityContext: {} 32 | # capabilities: 33 | # drop: 34 | # - ALL 35 | # readOnlyRootFilesystem: true 36 | # runAsNonRoot: true 37 | # runAsUser: 1000 38 | 39 | service: 40 | type: ClusterIP 41 | port: 8080 42 | 43 | ingress: 44 | enabled: false 45 | className: "nginx" 46 | annotations: {} 47 | # kubernetes.io/ingress.class: nginx 48 | # kubernetes.io/tls-acme: "true" 49 | hosts: 50 | - host: api.micro.hbchen.com 51 | paths: 52 | - path: / 53 | pathType: ImplementationSpecific 54 | tls: [] 55 | # - secretName: chart-example-tls 56 | # hosts: 57 | # - chart-example.local 58 | 59 | resources: {} 60 | # We usually recommend not to specify default resources and to leave this as a conscious 61 | # choice for the user. This also increases chances charts run on environments with little 62 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 63 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 64 | # limits: 65 | # cpu: 100m 66 | # memory: 128Mi 67 | # requests: 68 | # cpu: 100m 69 | # memory: 128Mi 70 | 71 | autoscaling: 72 | enabled: false 73 | minReplicas: 1 74 | maxReplicas: 100 75 | targetCPUUtilizationPercentage: 80 76 | # targetMemoryUtilizationPercentage: 80 77 | 78 | nodeSelector: {} 79 | 80 | tolerations: [] 81 | 82 | affinity: {} 83 | 84 | tests: 85 | enabled: false 86 | -------------------------------------------------------------------------------- /manifests/charts/service/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *.orig 18 | *~ 19 | # Various IDEs 20 | .project 21 | .idea/ 22 | *.tmproj 23 | .vscode/ 24 | -------------------------------------------------------------------------------- /manifests/charts/service/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: service 3 | description: A Helm chart for Kubernetes 4 | 5 | # A chart can be either an 'application' or a 'library' chart. 6 | # 7 | # Application charts are a collection of templates that can be packaged into versioned archives 8 | # to be deployed. 9 | # 10 | # Library charts provide useful utilities or functions for the chart developer. They're included as 11 | # a dependency of application charts to inject those utilities and functions into the rendering 12 | # pipeline. Library charts do not define any templates and therefore cannot be deployed. 13 | type: application 14 | 15 | # This is the chart version. This version number should be incremented each time you make changes 16 | # to the chart and its templates, including the app version. 17 | # Versions are expected to follow Semantic Versioning (https://semver.org/) 18 | version: 0.1.0 19 | 20 | # This is the version number of the application being deployed. This version number should be 21 | # incremented each time you make changes to the application. Versions are not expected to 22 | # follow Semantic Versioning. They should reflect the version the application is using. 23 | # It is recommended to use it with quotes. 24 | appVersion: "1.16.0" 25 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* 2 | Expand the name of the chart. 3 | */}} 4 | {{- define "service.name" -}} 5 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} 6 | {{- end }} 7 | 8 | {{/* 9 | Create a default fully qualified app name. 10 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 11 | If release name contains chart name it will be used as a full name. 12 | */}} 13 | {{- define "service.fullname" -}} 14 | {{- if .Values.fullnameOverride }} 15 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} 16 | {{- else }} 17 | {{- $name := default .Chart.Name .Values.nameOverride }} 18 | {{- if contains $name .Release.Name }} 19 | {{- .Release.Name | trunc 63 | trimSuffix "-" }} 20 | {{- else }} 21 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} 22 | {{- end }} 23 | {{- end }} 24 | {{- end }} 25 | 26 | {{/* 27 | Create chart name and version as used by the chart label. 28 | */}} 29 | {{- define "service.chart" -}} 30 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} 31 | {{- end }} 32 | 33 | {{/* 34 | Common labels 35 | */}} 36 | {{- define "service.labels" -}} 37 | helm.sh/chart: {{ include "service.chart" . }} 38 | {{ include "service.selectorLabels" . }} 39 | {{- if .Chart.AppVersion }} 40 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 41 | {{- end }} 42 | app.kubernetes.io/managed-by: {{ .Release.Service }} 43 | {{- end }} 44 | 45 | {{/* 46 | Selector labels 47 | */}} 48 | {{- define "service.selectorLabels" -}} 49 | app.kubernetes.io/name: {{ include "service.name" . }} 50 | app.kubernetes.io/instance: {{ .Release.Name }} 51 | {{- end }} 52 | 53 | {{/* 54 | Create the name of the service account to use 55 | */}} 56 | {{- define "service.serviceAccountName" -}} 57 | {{- if .Values.serviceAccount.create }} 58 | {{- default (include "service.fullname" .) .Values.serviceAccount.name }} 59 | {{- else }} 60 | {{- default "default" .Values.serviceAccount.name }} 61 | {{- end }} 62 | {{- end }} 63 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "service.fullname" . }} 5 | labels: 6 | {{- include "service.labels" . | nindent 4 }} 7 | spec: 8 | {{- if not .Values.autoscaling.enabled }} 9 | replicas: {{ .Values.replicaCount }} 10 | {{- end }} 11 | selector: 12 | matchLabels: 13 | {{- include "service.selectorLabels" . | nindent 6 }} 14 | template: 15 | metadata: 16 | {{- with .Values.podAnnotations }} 17 | annotations: 18 | {{- toYaml . | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | {{- include "service.selectorLabels" . | nindent 8 }} 22 | spec: 23 | {{- with .Values.imagePullSecrets }} 24 | imagePullSecrets: 25 | {{- toYaml . | nindent 8 }} 26 | {{- end }} 27 | serviceAccountName: {{ include "service.serviceAccountName" . }} 28 | securityContext: 29 | {{- toYaml .Values.podSecurityContext | nindent 8 }} 30 | containers: 31 | - name: {{ .Chart.Name }} 32 | securityContext: 33 | {{- toYaml .Values.securityContext | nindent 12 }} 34 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" 35 | imagePullPolicy: {{ .Values.image.pullPolicy }} 36 | env: 37 | - name: MICRO_SERVICE_PROFILE 38 | value: starter-kubernetes 39 | - name: MICRO_SERVICE_ADDRESS 40 | value: :8080 41 | ports: 42 | - name: http 43 | containerPort: 8080 44 | protocol: TCP 45 | livenessProbe: 46 | tcpSocket: 47 | port: http 48 | readinessProbe: 49 | tcpSocket: 50 | port: http 51 | resources: 52 | {{- toYaml .Values.resources | nindent 12 }} 53 | {{- with .Values.nodeSelector }} 54 | nodeSelector: 55 | {{- toYaml . | nindent 8 }} 56 | {{- end }} 57 | {{- with .Values.affinity }} 58 | affinity: 59 | {{- toYaml . | nindent 8 }} 60 | {{- end }} 61 | {{- with .Values.tolerations }} 62 | tolerations: 63 | {{- toYaml . | nindent 8 }} 64 | {{- end }} 65 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/hpa.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.autoscaling.enabled }} 2 | apiVersion: autoscaling/v2beta1 3 | kind: HorizontalPodAutoscaler 4 | metadata: 5 | name: {{ include "service.fullname" . }} 6 | labels: 7 | {{- include "service.labels" . | nindent 4 }} 8 | spec: 9 | scaleTargetRef: 10 | apiVersion: apps/v1 11 | kind: Deployment 12 | name: {{ include "service.fullname" . }} 13 | minReplicas: {{ .Values.autoscaling.minReplicas }} 14 | maxReplicas: {{ .Values.autoscaling.maxReplicas }} 15 | metrics: 16 | {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} 17 | - type: Resource 18 | resource: 19 | name: cpu 20 | targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} 21 | {{- end }} 22 | {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} 23 | - type: Resource 24 | resource: 25 | name: memory 26 | targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} 27 | {{- end }} 28 | {{- end }} 29 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "service.fullname" . }} 5 | labels: 6 | {{- include "service.labels" . | nindent 4 }} 7 | spec: 8 | type: {{ .Values.service.type }} 9 | ports: 10 | - port: {{ .Values.service.port }} 11 | targetPort: http 12 | protocol: TCP 13 | name: http 14 | selector: 15 | {{- include "service.selectorLabels" . | nindent 4 }} 16 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "service.serviceAccountName" . }} 6 | labels: 7 | {{- include "service.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /manifests/charts/service/templates/tests/test-connection.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.tests.enabled -}} 2 | apiVersion: v1 3 | kind: Pod 4 | metadata: 5 | name: "{{ include "service.fullname" . }}-test-connection" 6 | labels: 7 | {{- include "service.labels" . | nindent 4 }} 8 | annotations: 9 | "helm.sh/hook": test 10 | spec: 11 | containers: 12 | - name: wget 13 | image: busybox 14 | command: ['wget'] 15 | args: ['{{ include "service.fullname" . }}:{{ .Values.service.port }}'] 16 | restartPolicy: Never 17 | {{- end }} -------------------------------------------------------------------------------- /manifests/charts/service/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for service. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | replicaCount: 1 6 | 7 | image: 8 | repository: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-example 9 | pullPolicy: IfNotPresent 10 | # Overrides the image tag whose default is the chart appVersion. 11 | tag: "latest" 12 | 13 | imagePullSecrets: [] 14 | nameOverride: "" 15 | fullnameOverride: "" 16 | 17 | serviceAccount: 18 | # Specifies whether a service account should be created 19 | create: true 20 | # Annotations to add to the service account 21 | annotations: {} 22 | # The name of the service account to use. 23 | # If not set and create is true, a name is generated using the fullname template 24 | name: "" 25 | 26 | podAnnotations: {} 27 | 28 | podSecurityContext: {} 29 | # fsGroup: 2000 30 | 31 | securityContext: {} 32 | # capabilities: 33 | # drop: 34 | # - ALL 35 | # readOnlyRootFilesystem: true 36 | # runAsNonRoot: true 37 | # runAsUser: 1000 38 | 39 | service: 40 | type: ClusterIP 41 | port: 8080 42 | 43 | resources: {} 44 | # We usually recommend not to specify default resources and to leave this as a conscious 45 | # choice for the user. This also increases chances charts run on environments with little 46 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 47 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 48 | # limits: 49 | # cpu: 100m 50 | # memory: 128Mi 51 | # requests: 52 | # cpu: 100m 53 | # memory: 128Mi 54 | 55 | autoscaling: 56 | enabled: false 57 | minReplicas: 1 58 | maxReplicas: 100 59 | targetCPUUtilizationPercentage: 80 60 | # targetMemoryUtilizationPercentage: 80 61 | 62 | nodeSelector: {} 63 | 64 | tolerations: [] 65 | 66 | affinity: {} 67 | 68 | tests: 69 | enabled: false -------------------------------------------------------------------------------- /manifests/kubevela/application.yml: -------------------------------------------------------------------------------- 1 | apiVersion: core.oam.dev/v1beta1 2 | kind: Application 3 | metadata: 4 | name: micro-starter 5 | spec: 6 | components: 7 | - name: micro-server 8 | properties: 9 | chart: ./manifests/charts/micro 10 | git: 11 | branch: main 12 | installTimeout: 10m 13 | pullInterval: 5m 14 | releaseName: micro-server 15 | repoType: git 16 | targetNamespace: micro 17 | url: https://github.com/hb-chen/micro-starter 18 | values: 19 | image: 20 | repository: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro 21 | tag: latest 22 | ingress: 23 | annotations: 24 | kubernetes.io/ingress.class: nginx 25 | enabled: 'true' 26 | service: 27 | port: '8080' 28 | version: '*' 29 | type: helm 30 | policies: 31 | - name: env-bindings-dev 32 | properties: 33 | envs: 34 | - name: micro 35 | patch: {} 36 | placement: 37 | clusterSelector: 38 | name: local 39 | namespaceSelector: 40 | name: micro 41 | type: env-binding 42 | workflow: 43 | steps: 44 | - name: micro 45 | properties: 46 | env: micro 47 | policy: env-bindings-dev 48 | type: deploy2env -------------------------------------------------------------------------------- /manifests/tekton/README.md: -------------------------------------------------------------------------------- 1 | # Tekton CICD 2 | 3 | > `TODO` CD 使用 KubeVela Task 4 | 5 | - Tekton 环境搭建参考 [hb-chen/tekton-practice](https://github.com/hb-chen/tekton-practice) 6 | - 需要完成 `Install` `Task` 和 `Config` 三个步骤 7 | - microt-starter 提供了两种方式构建镜像 `buildpacks` 和 `kaniko` 8 | - `TriggerTemplate` 默认使用的 `buildpacks` 9 | - Trigger 的 `EventListener` 配置在 [hb-chen/tekton-practice/example/trigger.yml](https://github.com/hb-chen/tekton-practice/blob/main/example/trigger.yml) -------------------------------------------------------------------------------- /manifests/tekton/buildpacks/pipeline.yml: -------------------------------------------------------------------------------- 1 | apiVersion: tekton.dev/v1beta1 2 | kind: Pipeline 3 | metadata: 4 | name: micro-starter-buildpacks-pipeline 5 | spec: 6 | params: 7 | - name: url 8 | type: string 9 | - default: master 10 | name: revision 11 | type: string 12 | - description: image URL to push 13 | name: image 14 | type: string 15 | tasks: 16 | - name: fetch-repository 17 | params: 18 | - name: url 19 | value: $(params.url) 20 | - name: revision 21 | value: $(params.revision) 22 | - name: subdirectory 23 | value: micro-starter 24 | - name: deleteExisting 25 | value: 'true' 26 | taskRef: 27 | kind: Task 28 | name: git-clone 29 | workspaces: 30 | - name: output 31 | workspace: shared-workspace 32 | - name: buildpacks 33 | params: 34 | - name: APP_IMAGE 35 | value: $(params.image) 36 | - name: SOURCE_SUBPATH 37 | value: micro-starter 38 | - name: BUILDER_IMAGE 39 | value: 'paketobuildpacks/builder:tiny' 40 | - name: ENV_VARS 41 | value: 42 | - GOPROXY=https://mirrors.aliyun.com/goproxy/,direct 43 | runAfter: 44 | - fetch-repository 45 | taskRef: 46 | kind: Task 47 | name: buildpacks 48 | workspaces: 49 | - name: source 50 | workspace: shared-workspace 51 | - name: cache 52 | workspace: shared-workspace 53 | - name: dockerconfig 54 | workspace: dockerconfig 55 | workspaces: 56 | - name: shared-workspace 57 | - name: dockerconfig 58 | - name: kubeconfig -------------------------------------------------------------------------------- /manifests/tekton/buildpacks/run.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1beta1 3 | kind: PipelineRun 4 | metadata: 5 | # name: micro-starter-buildpacks-pipeline-run 6 | generateName: micro-starter-buildpacks-pipeline-run- 7 | spec: 8 | serviceAccountName: build-bot 9 | pipelineRef: 10 | name: micro-starter-buildpacks-pipeline 11 | params: 12 | - name: url 13 | value: https://github.com/hb-chen/micro-starter 14 | - name: revision 15 | value: main 16 | - name: image 17 | value: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro 18 | workspaces: 19 | - name: shared-workspace 20 | persistentvolumeclaim: 21 | claimName: golang-source-pvc 22 | - name: dockerconfig 23 | secret: 24 | secretName: dockerconfig 25 | - name: kubeconfig 26 | secret: 27 | secretName: k8s-kubeconfig 28 | -------------------------------------------------------------------------------- /manifests/tekton/kaniko/pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1beta1 3 | kind: Pipeline 4 | metadata: 5 | name: micro-starter-pipeline 6 | spec: 7 | workspaces: 8 | - name: shared-workspace 9 | - name: dockerconfig 10 | - name: kubeconfig 11 | params: 12 | - name: url 13 | - name: revision 14 | default: master 15 | tasks: 16 | - name: fetch-repository 17 | taskRef: 18 | name: git-clone 19 | workspaces: 20 | - name: output 21 | workspace: shared-workspace 22 | params: 23 | - name: url 24 | value: $(params.url) 25 | - name: revision 26 | value: $(params.revision) 27 | - name: subdirectory 28 | value: "micro-starter" 29 | - name: deleteExisting 30 | value: "true" 31 | # - name: run-test 32 | # taskRef: 33 | # name: golang-test 34 | # runAfter: 35 | # - fetch-repository 36 | # workspaces: 37 | # - name: source 38 | # workspace: shared-workspace 39 | # params: 40 | # - name: package 41 | # value: github.com/hb-chen/micro-starter 42 | # - name: subdirectory 43 | # value: "micro-starter" 44 | # - name: version 45 | # value: 1.14.15 46 | - name: run-build 47 | taskRef: 48 | name: golang-build 49 | runAfter: 50 | - fetch-repository 51 | workspaces: 52 | - name: source 53 | workspace: shared-workspace 54 | params: 55 | - name: package 56 | value: github.com/hb-chen/micro-starter 57 | - name: packages 58 | value: "./cmd/micro/main.go" 59 | - name: subdirectory 60 | value: "micro-starter" 61 | - name: version 62 | value: 1.15.11 63 | - name: flags 64 | value: -v -o $(workspaces.source.path)/micro-starter/dist/micro_linux_amd64/bin/micro 65 | - name: docker-build 66 | taskRef: 67 | name: kaniko 68 | runAfter: 69 | - run-build 70 | workspaces: 71 | - name: source 72 | workspace: shared-workspace 73 | - name: dockerconfig 74 | workspace: dockerconfig 75 | params: 76 | - name: IMAGE 77 | value: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro:latest 78 | - name: DOCKERFILE 79 | value: ./micro-starter/Dockerfile 80 | - name: CONTEXT 81 | value: ./micro-starter/ 82 | - name: EXTRA_ARGS 83 | value: 84 | - "--skip-tls-verify" 85 | - "--insecure-registry=registry.cn-hangzhou.aliyuncs.com" 86 | # - name: helm-kubectl-deploy 87 | # taskRef: 88 | # name: helm-kubectl-deploy 89 | # runAfter: 90 | # - docker-build 91 | # workspaces: 92 | # - name: source 93 | # workspace: shared-workspace 94 | # - name: kubeconfig 95 | # workspace: kubeconfig 96 | # params: 97 | # - name: commands 98 | # value: | 99 | # helm template \ 100 | # --release-name micro-starter \ 101 | # --no-hooks \ 102 | # --set image.repository=registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-micro \ 103 | # --set image.tag=latest \ 104 | # --set image.digest=@$(tasks.docker-build.results.IMAGE-DIGEST) \ 105 | # ./micro-starter/manifests/helm | kubectl apply -n micro-starter -f - 106 | - name: run-build-service 107 | taskRef: 108 | name: golang-build 109 | runAfter: 110 | - fetch-repository 111 | workspaces: 112 | - name: source 113 | workspace: shared-workspace 114 | params: 115 | - name: package 116 | value: github.com/hb-chen/micro-starter 117 | - name: packages 118 | value: "./service/account/main.go" 119 | - name: subdirectory 120 | value: "micro-starter" 121 | - name: version 122 | value: 1.15.11 123 | - name: flags 124 | value: -v -o $(workspaces.source.path)/micro-starter/dist/micro_linux_amd64/bin/example 125 | - name: docker-build-service 126 | taskRef: 127 | name: kaniko 128 | runAfter: 129 | - run-build 130 | workspaces: 131 | - name: source 132 | workspace: shared-workspace 133 | - name: dockerconfig 134 | workspace: dockerconfig 135 | params: 136 | - name: IMAGE 137 | value: registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-example:latest 138 | - name: DOCKERFILE 139 | value: ./micro-starter/service/account/Dockerfile 140 | - name: CONTEXT 141 | value: ./micro-starter/ 142 | - name: EXTRA_ARGS 143 | value: 144 | - "--skip-tls-verify" 145 | - "--insecure-registry=registry.cn-hangzhou.aliyuncs.com" -------------------------------------------------------------------------------- /manifests/tekton/kaniko/run.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: tekton.dev/v1beta1 3 | kind: PipelineRun 4 | metadata: 5 | # name: micro-starter-pipeline-run 6 | generateName: micro-starter-pipeline-run- 7 | spec: 8 | serviceAccountName: build-bot 9 | pipelineRef: 10 | name: micro-starter-pipeline 11 | params: 12 | - name: url 13 | value: https://github.com/hb-chen/micro-starter 14 | - name: revision 15 | value: main 16 | workspaces: 17 | - name: shared-workspace 18 | persistentvolumeclaim: 19 | claimName: golang-source-pvc 20 | - name: dockerconfig 21 | secret: 22 | secretName: dockerconfig 23 | - name: kubeconfig 24 | secret: 25 | secretName: k8s-kubeconfig 26 | -------------------------------------------------------------------------------- /manifests/tekton/trigger-template.yml: -------------------------------------------------------------------------------- 1 | apiVersion: triggers.tekton.dev/v1alpha1 2 | kind: TriggerTemplate 3 | metadata: 4 | name: micro-starter-pipeline-template 5 | spec: 6 | params: 7 | - name: gitrevision 8 | description: The git revision 9 | default: master 10 | - name: gitrepositoryurl 11 | description: The git repository url 12 | resourcetemplates: 13 | - apiVersion: tekton.dev/v1beta1 14 | kind: PipelineRun 15 | metadata: 16 | generateName: micro-starter-pipeline-run- 17 | spec: 18 | serviceAccountName: build-bot 19 | pipelineRef: 20 | name: micro-starter-buildpacks-pipeline 21 | workspaces: 22 | - name: shared-workspace 23 | persistentvolumeclaim: 24 | claimName: golang-source-pvc 25 | - name: dockerconfig 26 | secret: 27 | secretName: dockerconfig 28 | - name: kubeconfig 29 | secret: 30 | secretName: k8s-kubeconfig 31 | params: 32 | - name: url 33 | value: $(tt.params.gitrepositoryurl) 34 | - name: revision 35 | value: $(tt.params.gitrevision) 36 | -------------------------------------------------------------------------------- /pkg/file/file_slice.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | type FileInfo struct { 8 | os.FileInfo 9 | Path string 10 | } 11 | 12 | type FileSlice []FileInfo 13 | 14 | // ModTime升序 15 | // sort.Interface 16 | // Len is the number of elements in the collection. 17 | func (files FileSlice) Len() int { 18 | return len(files) 19 | } 20 | 21 | // Less reports whether the element with 22 | // index i should sort before the element with index j. 23 | func (files FileSlice) Less(i, j int) bool { 24 | a := files[i] 25 | b := files[j] 26 | if a.ModTime().UnixNano() < b.ModTime().UnixNano() { 27 | return true 28 | } 29 | return false 30 | } 31 | 32 | // Swap swaps the elements with indexes i and j. 33 | func (files FileSlice) Swap(i, j int) { 34 | files[i], files[j] = files[j], files[i] 35 | } 36 | -------------------------------------------------------------------------------- /pkg/file/walk_dir.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "path" 8 | "path/filepath" 9 | "sync" 10 | ) 11 | 12 | // sema is a counting semaphore for limiting concurrency in dirents 13 | var sema = make(chan struct{}, 20) 14 | 15 | // 读取目录dir下的文件信息 16 | func dirents(dir string) []os.FileInfo { 17 | sema <- struct{}{} 18 | defer func() { <-sema }() 19 | entries, err := ioutil.ReadDir(dir) 20 | if err != nil { 21 | fmt.Fprintf(os.Stderr, "du: %v\n", err) 22 | return nil 23 | } 24 | return entries 25 | } 26 | 27 | // 获取目录dir下的文件 28 | func walkDir(dir, suffix string, wg *sync.WaitGroup, fileInfos chan<- FileInfo) { 29 | defer wg.Done() 30 | for _, entry := range dirents(dir) { 31 | subDir := filepath.Join(dir, entry.Name()) 32 | if entry.IsDir() { // 目录 33 | wg.Add(1) 34 | go walkDir(subDir, suffix, wg, fileInfos) 35 | } else { 36 | if suffix == "" || suffix == path.Ext(entry.Name()) { 37 | fi := FileInfo{ 38 | FileInfo: entry, 39 | Path: subDir, 40 | } 41 | fileInfos <- fi 42 | } 43 | } 44 | } 45 | } 46 | 47 | func WalkDirs(roots []string, suffix string, files *FileSlice, nbytes *int64) { 48 | fileInfos := make(chan FileInfo) 49 | var wg sync.WaitGroup 50 | for _, root := range roots { 51 | wg.Add(1) 52 | go walkDir(root, suffix, &wg, fileInfos) 53 | } 54 | go func() { 55 | wg.Wait() // 等待goroutine结束 56 | close(fileInfos) 57 | }() 58 | 59 | loop: 60 | for { 61 | select { 62 | case fi, ok := <-fileInfos: 63 | if !ok { 64 | break loop 65 | } 66 | 67 | *files = append(*files, fi) 68 | *nbytes += fi.Size() 69 | } 70 | } 71 | 72 | return 73 | } 74 | -------------------------------------------------------------------------------- /pkg/service/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "github.com/urfave/cli/v2" 5 | "micro.dev/v4/cmd" 6 | "micro.dev/v4/service" 7 | "micro.dev/v4/service/profile" 8 | ) 9 | 10 | // New returns a new Micro Service 11 | func New(opts ...service.Option) *service.Service { 12 | // setup micro, this triggers the Before 13 | // function which parses CLI flags. 14 | c := cmd.New( 15 | cmd.Service(), 16 | cmd.Flags( 17 | &cli.StringFlag{ 18 | Name: "address", 19 | Usage: "Set the micro service address", 20 | EnvVars: []string{"MICRO_SERVICE_ADDRESS"}, 21 | Value: ":0", 22 | }, 23 | ), 24 | cmd.Before(func(ctx *cli.Context) error { 25 | addr := ctx.String("address") 26 | opts = append(opts, service.Address(addr)) 27 | return nil 28 | }), 29 | ) 30 | 31 | err := c.Run() 32 | if err != nil { 33 | return nil 34 | } 35 | 36 | // setup auth 37 | profile.SetupAccount(nil) 38 | 39 | // return a new service 40 | svc := &service.Service{} 41 | svc.Init(opts...) 42 | return svc 43 | } 44 | -------------------------------------------------------------------------------- /pkg/tools/proto/batch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "flag" 6 | "fmt" 7 | "os" 8 | "os/exec" 9 | "strings" 10 | 11 | "github.com/hb-chen/micro-starter/pkg/file" 12 | ) 13 | 14 | var ( 15 | cmdHelp = flag.Bool("h", false, "帮助") 16 | root = flag.String("r", "", "需要查找.proto文件的路径,多路径使用\":\"分隔") 17 | ) 18 | 19 | func init() { 20 | flag.Parse() 21 | } 22 | 23 | func main() { 24 | if *cmdHelp { 25 | flag.PrintDefaults() 26 | return 27 | } 28 | 29 | roots := strings.Split(*root, ":") 30 | if *root == "" || len(roots) <= 0 { 31 | fmt.Println("-r 请输入正确的路径") 32 | return 33 | } 34 | 35 | files := file.FileSlice{} 36 | nbytes := int64(0) 37 | file.WalkDirs(roots, ".proto", &files, &nbytes) 38 | 39 | fmt.Printf("proto files count:%d, size:%.3f kb\n", files.Len(), float64(nbytes)/1e3) 40 | 41 | arg := []string{} 42 | 43 | arg = append(arg, "-I="+os.Getenv("GOPATH")+"/src:.") 44 | arg = append(arg, "--micro_out=.", "--go_out=.") 45 | 46 | var out bytes.Buffer 47 | var stderr bytes.Buffer 48 | for _, v := range files { 49 | fmt.Println("proto file path:" + v.Path) 50 | 51 | cmd := exec.Command("protoc", append(arg, v.Path)...) 52 | cmd.Stdout = &out 53 | cmd.Stderr = &stderr 54 | 55 | err := cmd.Run() 56 | if err != nil { 57 | fmt.Printf("exec cmd error:" + fmt.Sprint(err) + "\n" + stderr.String()) 58 | continue 59 | } 60 | 61 | fmt.Println("exec cmd success\n" + out.String()) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /profile/profile.go: -------------------------------------------------------------------------------- 1 | // Package profile is for specific profiles 2 | // @todo this package is the definition of cruft and 3 | // should be rewritten in a more elegant way 4 | package profile 5 | 6 | import ( 7 | "path/filepath" 8 | 9 | "github.com/hb-chen/micro-starter/service/auth/noop" 10 | "github.com/urfave/cli/v2" 11 | microAuth "micro.dev/v4/service/auth" 12 | "micro.dev/v4/service/broker" 13 | memBroker "micro.dev/v4/service/broker/memory" 14 | "micro.dev/v4/service/config" 15 | storeConfig "micro.dev/v4/service/config/store" 16 | microEvents "micro.dev/v4/service/events" 17 | evStore "micro.dev/v4/service/events/store" 18 | memStream "micro.dev/v4/service/events/stream/memory" 19 | "micro.dev/v4/service/logger" 20 | "micro.dev/v4/service/model" 21 | "micro.dev/v4/service/model/sql" 22 | "micro.dev/v4/service/profile" 23 | "micro.dev/v4/service/registry" 24 | memRegistry "micro.dev/v4/service/registry/memory" 25 | microRuntime "micro.dev/v4/service/runtime" 26 | "micro.dev/v4/service/runtime/local" 27 | microStore "micro.dev/v4/service/store" 28 | "micro.dev/v4/service/store/file" 29 | mem "micro.dev/v4/service/store/memory" 30 | "micro.dev/v4/util/user" 31 | ) 32 | 33 | func init() { 34 | if err := profile.Register("starter-local", Local); err != nil { 35 | logger.Fatalf("Error profile register: %v", err) 36 | } 37 | 38 | if err := profile.Register("starter-kubernetes", Kubernetes); err != nil { 39 | logger.Fatalf("Error profile register: %v", err) 40 | } 41 | 42 | if err := profile.Register("starter-test", Kubernetes); err != nil { 43 | logger.Fatalf("Error profile register: %v", err) 44 | } 45 | } 46 | 47 | // Local profile to run locally 48 | var Local = &profile.Profile{ 49 | Name: "starter-local", 50 | Setup: func(ctx *cli.Context) error { 51 | // catch all 52 | profile.SetupDefaults() 53 | 54 | microAuth.DefaultAuth = noop.NewAuth() 55 | microStore.DefaultStore = file.NewStore(file.WithDir(filepath.Join(user.Dir, "server", "store"))) 56 | profile.SetupConfigSecretKey() 57 | config.DefaultConfig, _ = storeConfig.NewConfig(microStore.DefaultStore, "") 58 | profile.SetupJWT() 59 | 60 | // the registry service uses the memory registry, the other core services will use the default 61 | // rpc client and call the registry service 62 | if ctx.Args().Get(1) == "registry" { 63 | profile.SetupRegistry(memRegistry.NewRegistry()) 64 | } else { 65 | // set the registry address 66 | registry.DefaultRegistry.Init( 67 | registry.Addrs("localhost:8000"), 68 | ) 69 | 70 | profile.SetupRegistry(registry.DefaultRegistry) 71 | } 72 | 73 | // the broker service uses the memory broker, the other core services will use the default 74 | // rpc client and call the broker service 75 | if ctx.Args().Get(1) == "broker" { 76 | profile.SetupBroker(memBroker.NewBroker()) 77 | } else { 78 | broker.DefaultBroker.Init( 79 | broker.Addrs("localhost:8003"), 80 | ) 81 | profile.SetupBroker(broker.DefaultBroker) 82 | } 83 | 84 | // set the store in the model 85 | // TODO sql model 86 | model.DefaultModel = sql.NewModel() 87 | 88 | // use the local runtime, note: the local runtime is designed to run source code directly so 89 | // the runtime builder should NOT be set when using this implementation 90 | microRuntime.DefaultRuntime = local.NewRuntime() 91 | 92 | var err error 93 | microEvents.DefaultStream, err = memStream.NewStream() 94 | if err != nil { 95 | logger.Fatalf("Error configuring stream: %v", err) 96 | } 97 | microEvents.DefaultStore = evStore.NewStore( 98 | evStore.WithStore(microStore.DefaultStore), 99 | ) 100 | 101 | microStore.DefaultBlobStore, err = file.NewBlobStore() 102 | if err != nil { 103 | logger.Fatalf("Error configuring file blob store: %v", err) 104 | } 105 | 106 | // Configure tracing with Jaeger (forced tracing): 107 | tracingServiceName := ctx.Args().Get(1) 108 | if len(tracingServiceName) == 0 { 109 | tracingServiceName = "Micro" 110 | } 111 | // openTracer, _, err := jaeger.New( 112 | // opentelemetry.WithServiceName(tracingServiceName), 113 | // opentelemetry.WithSamplingRate(1), 114 | // ) 115 | // if err != nil { 116 | // logger.Fatalf("Error configuring opentracing: %v", err) 117 | // } 118 | // opentelemetry.DefaultOpenTracer = openTracer 119 | 120 | return nil 121 | }, 122 | } 123 | 124 | // Kubernetes profile to run on kubernetes with zero deps. Designed for use with the micro helm chart 125 | var Kubernetes = &profile.Profile{ 126 | Name: "starter-kubernetes", 127 | Setup: func(ctx *cli.Context) (err error) { 128 | // catch all 129 | profile.SetupDefaults() 130 | 131 | microAuth.DefaultAuth = noop.NewAuth() 132 | 133 | microRuntime.DefaultRuntime = local.NewRuntime() 134 | 135 | microEvents.DefaultStream, err = memStream.NewStream() 136 | if err != nil { 137 | logger.Fatalf("Error configuring stream: %v", err) 138 | } 139 | 140 | microStore.DefaultStore = file.NewStore(file.WithDir("/store")) 141 | microStore.DefaultBlobStore, err = file.NewBlobStore(file.WithDir("/store/blob")) 142 | if err != nil { 143 | logger.Fatalf("Error configuring file blob store: %v", err) 144 | } 145 | 146 | // set the store in the model 147 | // TODO sql model 148 | model.DefaultModel = sql.NewModel() 149 | 150 | // the registry service uses the memory registry, the other core services will use the default 151 | // rpc client and call the registry service 152 | if ctx.Args().Get(1) == "registry" { 153 | profile.SetupRegistry(memRegistry.NewRegistry()) 154 | } else { 155 | // set the registry address 156 | registry.DefaultRegistry.Init( 157 | registry.Addrs("micro-server.micro.svc.cluster.local:8000"), 158 | ) 159 | 160 | profile.SetupRegistry(registry.DefaultRegistry) 161 | } 162 | 163 | // the broker service uses the memory broker, the other core services will use the default 164 | // rpc client and call the broker service 165 | if ctx.Args().Get(1) == "broker" { 166 | profile.SetupBroker(memBroker.NewBroker()) 167 | } else { 168 | broker.DefaultBroker.Init( 169 | broker.Addrs("micro-server.micro.svc.cluster.local:8003"), 170 | ) 171 | profile.SetupBroker(broker.DefaultBroker) 172 | } 173 | 174 | config.DefaultConfig, err = storeConfig.NewConfig(microStore.DefaultStore, "") 175 | if err != nil { 176 | logger.Fatalf("Error configuring config: %v", err) 177 | } 178 | profile.SetupConfigSecretKey() 179 | 180 | // Configure tracing with Jaeger: 181 | tracingServiceName := ctx.Args().Get(1) 182 | if len(tracingServiceName) == 0 { 183 | tracingServiceName = "Micro" 184 | } 185 | // openTracer, _, err := jaeger.New( 186 | // opentelemetry.WithServiceName(tracingServiceName), 187 | // opentelemetry.WithTraceReporterAddress("localhost:6831"), 188 | // ) 189 | // if err != nil { 190 | // logger.Fatalf("Error configuring opentracing: %v", err) 191 | // } 192 | // opentelemetry.DefaultOpenTracer = openTracer 193 | 194 | return nil 195 | }, 196 | } 197 | 198 | // Test profile is used for the go test suite 199 | var Test = &profile.Profile{ 200 | Name: "starter-test", 201 | Setup: func(ctx *cli.Context) error { 202 | // catch all 203 | profile.SetupDefaults() 204 | 205 | microAuth.DefaultAuth = noop.NewAuth() 206 | microStore.DefaultStore = mem.NewStore() 207 | microStore.DefaultBlobStore, _ = file.NewBlobStore() 208 | config.DefaultConfig, _ = storeConfig.NewConfig(microStore.DefaultStore, "") 209 | profile.SetupRegistry(memRegistry.NewRegistry()) 210 | // set the store in the model 211 | // TODO sql model 212 | model.DefaultModel = sql.NewModel() 213 | return nil 214 | }, 215 | } 216 | -------------------------------------------------------------------------------- /service/auth/noop/noop.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Asim Aslam 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | // 15 | // Original source: github.com/micro/go-micro/v3/auth/noop/noop.go 16 | 17 | package noop 18 | 19 | import ( 20 | "github.com/google/uuid" 21 | "micro.dev/v4/service/auth" 22 | ) 23 | 24 | func NewAuth(opts ...auth.Option) auth.Auth { 25 | var options auth.Options 26 | for _, o := range opts { 27 | o(&options) 28 | } 29 | 30 | return &noop{ 31 | opts: options, 32 | } 33 | } 34 | 35 | type noop struct { 36 | opts auth.Options 37 | } 38 | 39 | // String returns the name of the implementation 40 | func (n *noop) String() string { 41 | return "noop" 42 | } 43 | 44 | // Init the auth 45 | func (n *noop) Init(opts ...auth.Option) { 46 | for _, o := range opts { 47 | o(&n.opts) 48 | } 49 | } 50 | 51 | // Options set for auth 52 | func (n *noop) Options() auth.Options { 53 | return n.opts 54 | } 55 | 56 | // Generate a new account 57 | func (n *noop) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { 58 | options := auth.NewGenerateOptions(opts...) 59 | name := options.Name 60 | if name == "" { 61 | name = id 62 | } 63 | return &auth.Account{ 64 | ID: id, 65 | Secret: options.Secret, 66 | Metadata: options.Metadata, 67 | Scopes: options.Scopes, 68 | Issuer: n.Options().Issuer, 69 | Name: name, 70 | }, nil 71 | } 72 | 73 | // Grant access to a resource 74 | func (n *noop) Grant(rule *auth.Rule) error { 75 | return nil 76 | } 77 | 78 | // Revoke access to a resource 79 | func (n *noop) Revoke(rule *auth.Rule) error { 80 | return nil 81 | } 82 | 83 | // Rules used to verify requests 84 | func (n *noop) Rules(opts ...auth.RulesOption) ([]*auth.Rule, error) { 85 | return []*auth.Rule{}, nil 86 | } 87 | 88 | // Verify an account has access to a resource 89 | func (n *noop) Verify(acc *auth.Account, res *auth.Resource, opts ...auth.VerifyOption) error { 90 | return nil 91 | } 92 | 93 | // Inspect a token 94 | func (n *noop) Inspect(token string) (*auth.Account, error) { 95 | return &auth.Account{ 96 | ID: uuid.New().String(), 97 | Type: "service", 98 | Issuer: n.Options().Issuer, 99 | Scopes: []string{"service"}, 100 | }, nil 101 | } 102 | 103 | // Token generation using an account id and secret 104 | func (n *noop) Token(opts ...auth.TokenOption) (*auth.AccountToken, error) { 105 | return &auth.AccountToken{}, nil 106 | } 107 | -------------------------------------------------------------------------------- /service/greeting/.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | project_name: "greeting" 4 | before: 5 | hooks: 6 | # You may remove this if you don't use go modules. 7 | - go mod download 8 | 9 | builds: 10 | - # ID of the build. 11 | # Defaults to the project name. 12 | id: "service" 13 | 14 | # Path to project's (sub)directory containing Go code. 15 | # This is the working directory for the Go build command(s). 16 | # Default is `.`. 17 | dir: . 18 | 19 | # Path to main.go file or main package. 20 | # Default is `.`. 21 | main: . 22 | 23 | # Binary name. 24 | # Can be a path (e.g. `bin/app`) to wrap the binary in a directory. 25 | # Default is the name of the project directory. 26 | binary: bin/main 27 | 28 | # Custom flags templates. 29 | # Default is empty. 30 | flags: 31 | 32 | # Custom ldflags templates. 33 | # Default is `-s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -X main.builtBy=goreleaser`. 34 | ldflags: 35 | - -s -w -X main.version={{ .Version }} -X main.commit={{ .ShortCommit }} -X main.date={{ .Date }} -X main.builtBy=hbchen.com 36 | 37 | # Custom environment variables to be set during the builds. 38 | # Default is empty. 39 | env: 40 | 41 | # GOOS list to build for. 42 | # For more info refer to: https://golang.org/doc/install/source#environment 43 | # Defaults are darwin and linux. 44 | goos: 45 | - linux 46 | - darwin 47 | #- windows 48 | 49 | # GOARCH to build for. 50 | # For more info refer to: https://golang.org/doc/install/source#environment 51 | # Defaults are 386 and amd64. 52 | goarch: 53 | - amd64 54 | #- 386 55 | - arm64 56 | 57 | archives: 58 | - # ID of this archive. 59 | # Defaults to `default`. 60 | id: service 61 | 62 | # Archive name template. 63 | # Defaults: 64 | # - if format is `tar.gz`, `tar.xz`, `gz` or `zip`: 65 | # - `{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}` 66 | # - if format is `binary`: 67 | # - `{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}` 68 | name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}{{ if .Mips }}_{{ .Mips }}{{ end }}" 69 | format: tar.gz 70 | files: 71 | - README.md 72 | - LICENSE 73 | 74 | # Set to true, if you want all files in the archive to be in a single directory. 75 | # If set to true and you extract the archive 'goreleaser_Linux_arm64.tar.gz', 76 | # you get a folder 'goreleaser_Linux_arm64'. 77 | # If set to false, all files are extracted separately. 78 | # You can also set it to a custom folder name (templating is supported). 79 | # Default is false. 80 | wrap_in_directory: true 81 | 82 | # Disables the binary count check. 83 | # Default: false 84 | allow_different_binary_count: true 85 | snapshot: 86 | # Allows you to change the name of the generated snapshot 87 | # 88 | # Note that some pipes require this to be semantic version compliant (nfpm, 89 | # for example). 90 | # 91 | # Default is `{{ .Tag }}-SNAPSHOT-{{.ShortCommit}}`. 92 | name_template: '{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}' 93 | checksum: 94 | name_template: 'checksums.txt' 95 | changelog: 96 | sort: asc 97 | filters: 98 | exclude: 99 | - '^docs:' 100 | - '^test:' 101 | -------------------------------------------------------------------------------- /service/greeting/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.10 2 | 3 | ADD dist/service_linux_amd64_v1/bin/main /opt/service/main 4 | 5 | WORKDIR /opt/service 6 | 7 | CMD [ "./main" ] 8 | -------------------------------------------------------------------------------- /service/greeting/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hobo Go 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 | -------------------------------------------------------------------------------- /service/greeting/Makefile: -------------------------------------------------------------------------------- 1 | 2 | GOPATH:=$(shell go env GOPATH) 3 | 4 | ifeq ($(OS),Windows_NT) 5 | uname_S := Windows 6 | else 7 | uname_S := $(shell uname -s) 8 | endif 9 | 10 | # Goreleaser config 11 | ifeq ($(uname_S), Darwin) 12 | goreleaser_config = .goreleaser.yml 13 | else 14 | goreleaser_config = .goreleaser.yml 15 | endif 16 | 17 | .PHONY: init 18 | init: 19 | go get -u github.com/golang/protobuf/proto 20 | go get -u github.com/golang/protobuf/protoc-gen-go 21 | go get github.com/micro/micro/v3/cmd/protoc-gen-micro 22 | go get github.com/micro/micro/v3/cmd/protoc-gen-openapi 23 | 24 | .PHONY: api 25 | api: 26 | protoc --proto_path=${GOPATH}/src/ --openapi_out=. github.com/hb-chen/micro-starter/service/greeting/proto/greeting/greeting.proto 27 | 28 | .PHONY: proto 29 | proto: 30 | protoc --proto_path=${GOPATH}/src/ --micro_out=. --go_out=. github.com/hb-chen/micro-starter/service/greeting/proto/greeting/greeting.proto 31 | 32 | .PHONY: build 33 | build: proto 34 | go build -o bin/console main.go plugin.go 35 | 36 | .PHONY: test 37 | test: 38 | go test -v ./... -cover 39 | 40 | .PHONY: docker 41 | docker: 42 | docker build . -t example-srv:latest 43 | 44 | .PHONY: release 45 | release: 46 | goreleaser release --config $(goreleaser_config) --skip-validate --skip-publish --rm-dist 47 | 48 | .PHONY: snapshot 49 | snapshot: 50 | goreleaser release --config $(goreleaser_config) --skip-publish --snapshot --rm-dist -------------------------------------------------------------------------------- /service/greeting/README.md: -------------------------------------------------------------------------------- 1 | # Greeting Service 2 | 3 | This is the Greeting service 4 | 5 | ## Usage 6 | 7 | Generate the proto code 8 | 9 | ``` 10 | make proto 11 | ``` 12 | 13 | Run the service 14 | 15 | ``` 16 | go run main.go --profile starter-local 17 | ``` 18 | 19 | Call service 20 | 21 | ```shell script 22 | curl "http://localhost:8080/greeting/call?msg=helloworld" 23 | {"id":"1","msg":"helloworld"} 24 | ``` 25 | 26 | ## Image 27 | 28 | ```shell 29 | $ make snapshot 30 | 31 | $ docker build ./ -f Dockerfile --platform=linux/amd64 -t registry.cn-hangzhou.aliyuncs.com/hb-chen/micro-starter-example:latest 32 | ``` 33 | 34 | -------------------------------------------------------------------------------- /service/greeting/conf/auth_key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEowIBAAKCAQEA4f5wg5l2hKsTeNem/V41fGnJm6gOdrj8ym3rFkEU/wT8RDtn 3 | SgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7mCpz9Er5qLaMXJwZxzHzAahlfA0i 4 | cqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBpHssPnpYGIn20ZZuNlX2BrClciHhC 5 | PUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2XrHhR+1DcKJzQBSTAGnpYVaqpsAR 6 | ap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3bODIRe1AuTyHceAbewn8b462yEWKA 7 | Rdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy7wIDAQABAoIBAQCwia1k7+2oZ2d3 8 | n6agCAbqIE1QXfCmh41ZqJHbOY3oRQG3X1wpcGH4Gk+O+zDVTV2JszdcOt7E5dAy 9 | MaomETAhRxB7hlIOnEN7WKm+dGNrKRvV0wDU5ReFMRHg31/Lnu8c+5BvGjZX+ky9 10 | POIhFFYJqwCRlopGSUIxmVj5rSgtzk3iWOQXr+ah1bjEXvlxDOWkHN6YfpV5ThdE 11 | KdBIPGEVqa63r9n2h+qazKrtiRqJqGnOrHzOECYbRFYhexsNFz7YT02xdfSHn7gM 12 | IvabDDP/Qp0PjE1jdouiMaFHYnLBbgvlnZW9yuVf/rpXTUq/njxIXMmvmEyyvSDn 13 | FcFikB8pAoGBAPF77hK4m3/rdGT7X8a/gwvZ2R121aBcdPwEaUhvj/36dx596zvY 14 | mEOjrWfZhF083/nYWE2kVquj2wjs+otCLfifEEgXcVPTnEOPO9Zg3uNSL0nNQghj 15 | FuD3iGLTUBCtM66oTe0jLSslHe8gLGEQqyMzHOzYxNqibxcOZIe8Qt0NAoGBAO+U 16 | I5+XWjWEgDmvyC3TrOSf/KCGjtu0TSv30ipv27bDLMrpvPmD/5lpptTFwcxvVhCs 17 | 2b+chCjlghFSWFbBULBrfci2FtliClOVMYrlNBdUSJhf3aYSG2Doe6Bgt1n2CpNn 18 | /iu37Y3NfemZBJA7hNl4dYe+f+uzM87cdQ214+jrAoGAXA0XxX8ll2+ToOLJsaNT 19 | OvNB9h9Uc5qK5X5w+7G7O998BN2PC/MWp8H+2fVqpXgNENpNXttkRm1hk1dych86 20 | EunfdPuqsX+as44oCyJGFHVBnWpm33eWQw9YqANRI+pCJzP08I5WK3osnPiwshd+ 21 | hR54yjgfYhBFNI7B95PmEQkCgYBzFSz7h1+s34Ycr8SvxsOBWxymG5zaCsUbPsL0 22 | 4aCgLScCHb9J+E86aVbbVFdglYa5Id7DPTL61ixhl7WZjujspeXZGSbmq0Kcnckb 23 | mDgqkLECiOJW2NHP/j0McAkDLL4tysF8TLDO8gvuvzNC+WQ6drO2ThrypLVZQ+ry 24 | eBIPmwKBgEZxhqa0gVvHQG/7Od69KWj4eJP28kq13RhKay8JOoN0vPmspXJo1HY3 25 | CKuHRG+AP579dncdUnOMvfXOtkdM4vk0+hWASBQzM9xzVcztCa+koAugjVaLS9A+ 26 | 9uQoqEeVNTckxx0S2bYevRy7hGQmUJTyQm3j1zEUR5jpdbL83Fbq 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /service/greeting/conf/auth_key.pub: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4f5wg5l2hKsTeNem/V41 3 | fGnJm6gOdrj8ym3rFkEU/wT8RDtnSgFEZOQpHEgQ7JL38xUfU0Y3g6aYw9QT0hJ7 4 | mCpz9Er5qLaMXJwZxzHzAahlfA0icqabvJOMvQtzD6uQv6wPEyZtDTWiQi9AXwBp 5 | HssPnpYGIn20ZZuNlX2BrClciHhCPUIIZOQn/MmqTD31jSyjoQoV7MhhMTATKJx2 6 | XrHhR+1DcKJzQBSTAGnpYVaqpsARap+nwRipr3nUTuxyGohBTSmjJ2usSeQXHI3b 7 | ODIRe1AuTyHceAbewn8b462yEWKARdpd9AjQW5SIVPfdsz5B6GlYQ5LdYKtznTuy 8 | 7wIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /service/greeting/conf/casbin_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | 4 | [policy_definition] 5 | p = sub, obj, act 6 | 7 | [policy_effect] 8 | e = some(where (p.eft == allow)) 9 | 10 | [matchers] 11 | m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) 12 | -------------------------------------------------------------------------------- /service/greeting/conf/casbin_policy.csv: -------------------------------------------------------------------------------- 1 | p, public, /, GET 2 | p, public, /stats, GET 3 | 4 | p, alice, /alice_data/*, GET 5 | p, alice, /alice_data/resource1, POST 6 | 7 | p, bob, /alice_data/resource2, GET 8 | p, bob, /bob_data/*, POST 9 | 10 | p, cathy, /cathy_data, (GET)|(POST) 11 | -------------------------------------------------------------------------------- /service/greeting/conf/type.go: -------------------------------------------------------------------------------- 1 | package conf 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | var BASE_PATH = "./conf/" 8 | 9 | type Database struct { 10 | Engine string 11 | Host string 12 | Port string 13 | User string 14 | Password string 15 | Name string 16 | 17 | MaxOpenConns int 18 | MaxIdleConns int 19 | ConnMaxLifetime time.Duration 20 | } 21 | -------------------------------------------------------------------------------- /service/greeting/domain/model/greeting.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Msg struct { 4 | Id int64 `xorm:"bigint pk autoincr" gorm:"primary_key"` 5 | Msg string `xorm:"varchar(128) unique" gorm:"type:varchar(128);unique_index"` 6 | } 7 | -------------------------------------------------------------------------------- /service/greeting/domain/repository/greeting.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "github.com/hb-chen/micro-starter/service/greeting/domain/model" 5 | ) 6 | 7 | type GreetingRepository interface { 8 | FindById(id int64) (*model.Msg, error) 9 | FindByMsg(msg string) (*model.Msg, error) 10 | Add(msg *model.Msg) error 11 | List(page, size int) ([]*model.Msg, error) 12 | } 13 | -------------------------------------------------------------------------------- /service/greeting/domain/usecase/greeting.go: -------------------------------------------------------------------------------- 1 | package usecase 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hb-chen/micro-starter/service/greeting/domain/model" 7 | "github.com/hb-chen/micro-starter/service/greeting/domain/repository" 8 | ) 9 | 10 | type GreetingUsecase interface { 11 | Add(msg string) (*model.Msg, error) 12 | List(page, size int) ([]*model.Msg, error) 13 | Duplicated(msg string) error 14 | } 15 | 16 | type greetingUsecase struct { 17 | repo repository.GreetingRepository 18 | } 19 | 20 | func NewGreetingUsecase(repo repository.GreetingRepository) GreetingUsecase { 21 | return &greetingUsecase{ 22 | repo: repo, 23 | } 24 | } 25 | 26 | func (s *greetingUsecase) Add(msg string) (*model.Msg, error) { 27 | item, err := s.repo.FindByMsg(msg) 28 | if err != nil { 29 | return nil, err 30 | } 31 | 32 | if item != nil { 33 | return item, nil 34 | } 35 | 36 | u := model.Msg{ 37 | Msg: msg, 38 | } 39 | err = s.repo.Add(&u) 40 | if err != nil { 41 | return nil, err 42 | } 43 | 44 | return &u, nil 45 | } 46 | 47 | func (s *greetingUsecase) List(page, size int) ([]*model.Msg, error) { 48 | items, err := s.repo.List(page, size) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | return items, nil 54 | } 55 | 56 | func (s *greetingUsecase) Duplicated(msg string) error { 57 | item, err := s.repo.FindByMsg(msg) 58 | if item != nil { 59 | return fmt.Errorf("msg duplicated: %s", item.Msg) 60 | } 61 | if err != nil { 62 | return err 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /service/greeting/generate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | //go:generate make proto 4 | -------------------------------------------------------------------------------- /service/greeting/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "micro.dev/v4/service" 5 | "micro.dev/v4/service/logger" 6 | 7 | svc "github.com/hb-chen/micro-starter/pkg/service" 8 | _ "github.com/hb-chen/micro-starter/profile" 9 | "github.com/hb-chen/micro-starter/service/greeting/registry" 10 | "github.com/hb-chen/micro-starter/service/greeting/server" 11 | ) 12 | 13 | func main() { 14 | // New Service 15 | srv := svc.New( 16 | service.Name("greeting"), 17 | service.Version("latest"), 18 | ) 19 | 20 | c, err := registry.NewContainer() 21 | if err != nil { 22 | logger.Fatal(err) 23 | } 24 | 25 | // Register Handler 26 | server.Apply(srv.Server(), c) 27 | 28 | // Run service 29 | if err := srv.Run(); err != nil { 30 | logger.Fatal(err) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /service/greeting/proto/greeting/greeting.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // versions: 3 | // protoc-gen-go v1.26.0 4 | // protoc v3.21.12 5 | // source: github.com/hb-chen/micro-starter/service/greeting/proto/greeting/greeting.proto 6 | 7 | package greeting 8 | 9 | import ( 10 | protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 | protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 | reflect "reflect" 13 | sync "sync" 14 | ) 15 | 16 | const ( 17 | // Verify that this generated code is sufficiently up-to-date. 18 | _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 19 | // Verify that runtime/protoimpl is sufficiently up-to-date. 20 | _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 21 | ) 22 | 23 | type CallRequest struct { 24 | state protoimpl.MessageState 25 | sizeCache protoimpl.SizeCache 26 | unknownFields protoimpl.UnknownFields 27 | 28 | Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"` 29 | } 30 | 31 | func (x *CallRequest) Reset() { 32 | *x = CallRequest{} 33 | if protoimpl.UnsafeEnabled { 34 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[0] 35 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 36 | ms.StoreMessageInfo(mi) 37 | } 38 | } 39 | 40 | func (x *CallRequest) String() string { 41 | return protoimpl.X.MessageStringOf(x) 42 | } 43 | 44 | func (*CallRequest) ProtoMessage() {} 45 | 46 | func (x *CallRequest) ProtoReflect() protoreflect.Message { 47 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[0] 48 | if protoimpl.UnsafeEnabled && x != nil { 49 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 50 | if ms.LoadMessageInfo() == nil { 51 | ms.StoreMessageInfo(mi) 52 | } 53 | return ms 54 | } 55 | return mi.MessageOf(x) 56 | } 57 | 58 | // Deprecated: Use CallRequest.ProtoReflect.Descriptor instead. 59 | func (*CallRequest) Descriptor() ([]byte, []int) { 60 | return file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescGZIP(), []int{0} 61 | } 62 | 63 | func (x *CallRequest) GetMsg() string { 64 | if x != nil { 65 | return x.Msg 66 | } 67 | return "" 68 | } 69 | 70 | type CallResponse struct { 71 | state protoimpl.MessageState 72 | sizeCache protoimpl.SizeCache 73 | unknownFields protoimpl.UnknownFields 74 | 75 | Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` 76 | Msg string `protobuf:"bytes,2,opt,name=msg,proto3" json:"msg,omitempty"` 77 | } 78 | 79 | func (x *CallResponse) Reset() { 80 | *x = CallResponse{} 81 | if protoimpl.UnsafeEnabled { 82 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[1] 83 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 84 | ms.StoreMessageInfo(mi) 85 | } 86 | } 87 | 88 | func (x *CallResponse) String() string { 89 | return protoimpl.X.MessageStringOf(x) 90 | } 91 | 92 | func (*CallResponse) ProtoMessage() {} 93 | 94 | func (x *CallResponse) ProtoReflect() protoreflect.Message { 95 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[1] 96 | if protoimpl.UnsafeEnabled && x != nil { 97 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 98 | if ms.LoadMessageInfo() == nil { 99 | ms.StoreMessageInfo(mi) 100 | } 101 | return ms 102 | } 103 | return mi.MessageOf(x) 104 | } 105 | 106 | // Deprecated: Use CallResponse.ProtoReflect.Descriptor instead. 107 | func (*CallResponse) Descriptor() ([]byte, []int) { 108 | return file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescGZIP(), []int{1} 109 | } 110 | 111 | func (x *CallResponse) GetId() int64 { 112 | if x != nil { 113 | return x.Id 114 | } 115 | return 0 116 | } 117 | 118 | func (x *CallResponse) GetMsg() string { 119 | if x != nil { 120 | return x.Msg 121 | } 122 | return "" 123 | } 124 | 125 | type ListResponse struct { 126 | state protoimpl.MessageState 127 | sizeCache protoimpl.SizeCache 128 | unknownFields protoimpl.UnknownFields 129 | 130 | Items []*CallResponse `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` 131 | } 132 | 133 | func (x *ListResponse) Reset() { 134 | *x = ListResponse{} 135 | if protoimpl.UnsafeEnabled { 136 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[2] 137 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 138 | ms.StoreMessageInfo(mi) 139 | } 140 | } 141 | 142 | func (x *ListResponse) String() string { 143 | return protoimpl.X.MessageStringOf(x) 144 | } 145 | 146 | func (*ListResponse) ProtoMessage() {} 147 | 148 | func (x *ListResponse) ProtoReflect() protoreflect.Message { 149 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[2] 150 | if protoimpl.UnsafeEnabled && x != nil { 151 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 152 | if ms.LoadMessageInfo() == nil { 153 | ms.StoreMessageInfo(mi) 154 | } 155 | return ms 156 | } 157 | return mi.MessageOf(x) 158 | } 159 | 160 | // Deprecated: Use ListResponse.ProtoReflect.Descriptor instead. 161 | func (*ListResponse) Descriptor() ([]byte, []int) { 162 | return file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescGZIP(), []int{2} 163 | } 164 | 165 | func (x *ListResponse) GetItems() []*CallResponse { 166 | if x != nil { 167 | return x.Items 168 | } 169 | return nil 170 | } 171 | 172 | type Page struct { 173 | state protoimpl.MessageState 174 | sizeCache protoimpl.SizeCache 175 | unknownFields protoimpl.UnknownFields 176 | 177 | Page int64 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"` 178 | Size int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` 179 | } 180 | 181 | func (x *Page) Reset() { 182 | *x = Page{} 183 | if protoimpl.UnsafeEnabled { 184 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[3] 185 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 186 | ms.StoreMessageInfo(mi) 187 | } 188 | } 189 | 190 | func (x *Page) String() string { 191 | return protoimpl.X.MessageStringOf(x) 192 | } 193 | 194 | func (*Page) ProtoMessage() {} 195 | 196 | func (x *Page) ProtoReflect() protoreflect.Message { 197 | mi := &file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[3] 198 | if protoimpl.UnsafeEnabled && x != nil { 199 | ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 200 | if ms.LoadMessageInfo() == nil { 201 | ms.StoreMessageInfo(mi) 202 | } 203 | return ms 204 | } 205 | return mi.MessageOf(x) 206 | } 207 | 208 | // Deprecated: Use Page.ProtoReflect.Descriptor instead. 209 | func (*Page) Descriptor() ([]byte, []int) { 210 | return file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescGZIP(), []int{3} 211 | } 212 | 213 | func (x *Page) GetPage() int64 { 214 | if x != nil { 215 | return x.Page 216 | } 217 | return 0 218 | } 219 | 220 | func (x *Page) GetSize() int64 { 221 | if x != nil { 222 | return x.Size 223 | } 224 | return 0 225 | } 226 | 227 | var File_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto protoreflect.FileDescriptor 228 | 229 | var file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDesc = []byte{ 230 | 0x0a, 0x4f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x68, 0x62, 0x2d, 231 | 0x63, 0x68, 0x65, 0x6e, 0x2f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2d, 0x73, 0x74, 0x61, 0x72, 0x74, 232 | 0x65, 0x72, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 233 | 0x69, 0x6e, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 234 | 0x6e, 0x67, 0x2f, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 235 | 0x6f, 0x12, 0x15, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 236 | 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x1f, 0x0a, 0x0b, 0x43, 0x61, 0x6c, 0x6c, 237 | 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x01, 238 | 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x30, 0x0a, 0x0c, 0x43, 0x61, 0x6c, 239 | 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 240 | 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x02, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 241 | 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x73, 0x67, 0x22, 0x49, 0x0a, 0x0c, 0x4c, 242 | 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x39, 0x0a, 0x05, 0x69, 243 | 0x74, 0x65, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x67, 0x6f, 0x2e, 244 | 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 245 | 0x6e, 0x67, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 246 | 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x2e, 0x0a, 0x04, 0x50, 0x61, 0x67, 0x65, 0x12, 0x12, 247 | 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x04, 0x70, 0x61, 248 | 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 249 | 0x52, 0x04, 0x73, 0x69, 0x7a, 0x65, 0x32, 0xa9, 0x01, 0x0a, 0x08, 0x47, 0x72, 0x65, 0x65, 0x74, 250 | 0x69, 0x6e, 0x67, 0x12, 0x51, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x22, 0x2e, 0x67, 0x6f, 251 | 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 252 | 0x69, 0x6e, 0x67, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 253 | 0x23, 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 0x67, 254 | 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 255 | 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x4a, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1b, 256 | 0x2e, 0x67, 0x6f, 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 0x67, 0x72, 257 | 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x1a, 0x23, 0x2e, 0x67, 0x6f, 258 | 0x2e, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x2e, 0x73, 0x72, 0x76, 0x2e, 0x67, 0x72, 0x65, 0x65, 0x74, 259 | 0x69, 0x6e, 0x67, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 260 | 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x19, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x72, 261 | 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x3b, 0x67, 0x72, 0x65, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x62, 262 | 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 263 | } 264 | 265 | var ( 266 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescOnce sync.Once 267 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescData = file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDesc 268 | ) 269 | 270 | func file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescGZIP() []byte { 271 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescOnce.Do(func() { 272 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescData = protoimpl.X.CompressGZIP(file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescData) 273 | }) 274 | return file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDescData 275 | } 276 | 277 | var file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes = make([]protoimpl.MessageInfo, 4) 278 | var file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_goTypes = []interface{}{ 279 | (*CallRequest)(nil), // 0: go.micro.srv.greeting.CallRequest 280 | (*CallResponse)(nil), // 1: go.micro.srv.greeting.CallResponse 281 | (*ListResponse)(nil), // 2: go.micro.srv.greeting.ListResponse 282 | (*Page)(nil), // 3: go.micro.srv.greeting.Page 283 | } 284 | var file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_depIdxs = []int32{ 285 | 1, // 0: go.micro.srv.greeting.ListResponse.items:type_name -> go.micro.srv.greeting.CallResponse 286 | 0, // 1: go.micro.srv.greeting.Greeting.Call:input_type -> go.micro.srv.greeting.CallRequest 287 | 3, // 2: go.micro.srv.greeting.Greeting.List:input_type -> go.micro.srv.greeting.Page 288 | 1, // 3: go.micro.srv.greeting.Greeting.Call:output_type -> go.micro.srv.greeting.CallResponse 289 | 2, // 4: go.micro.srv.greeting.Greeting.List:output_type -> go.micro.srv.greeting.ListResponse 290 | 3, // [3:5] is the sub-list for method output_type 291 | 1, // [1:3] is the sub-list for method input_type 292 | 1, // [1:1] is the sub-list for extension type_name 293 | 1, // [1:1] is the sub-list for extension extendee 294 | 0, // [0:1] is the sub-list for field type_name 295 | } 296 | 297 | func init() { 298 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_init() 299 | } 300 | func file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_init() { 301 | if File_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto != nil { 302 | return 303 | } 304 | if !protoimpl.UnsafeEnabled { 305 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 306 | switch v := v.(*CallRequest); i { 307 | case 0: 308 | return &v.state 309 | case 1: 310 | return &v.sizeCache 311 | case 2: 312 | return &v.unknownFields 313 | default: 314 | return nil 315 | } 316 | } 317 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 318 | switch v := v.(*CallResponse); i { 319 | case 0: 320 | return &v.state 321 | case 1: 322 | return &v.sizeCache 323 | case 2: 324 | return &v.unknownFields 325 | default: 326 | return nil 327 | } 328 | } 329 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 330 | switch v := v.(*ListResponse); i { 331 | case 0: 332 | return &v.state 333 | case 1: 334 | return &v.sizeCache 335 | case 2: 336 | return &v.unknownFields 337 | default: 338 | return nil 339 | } 340 | } 341 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 342 | switch v := v.(*Page); i { 343 | case 0: 344 | return &v.state 345 | case 1: 346 | return &v.sizeCache 347 | case 2: 348 | return &v.unknownFields 349 | default: 350 | return nil 351 | } 352 | } 353 | } 354 | type x struct{} 355 | out := protoimpl.TypeBuilder{ 356 | File: protoimpl.DescBuilder{ 357 | GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 358 | RawDescriptor: file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDesc, 359 | NumEnums: 0, 360 | NumMessages: 4, 361 | NumExtensions: 0, 362 | NumServices: 1, 363 | }, 364 | GoTypes: file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_goTypes, 365 | DependencyIndexes: file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_depIdxs, 366 | MessageInfos: file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_msgTypes, 367 | }.Build() 368 | File_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto = out.File 369 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_rawDesc = nil 370 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_goTypes = nil 371 | file_github_com_hb_chen_micro_starter_service_greeting_proto_greeting_greeting_proto_depIdxs = nil 372 | } 373 | -------------------------------------------------------------------------------- /service/greeting/proto/greeting/greeting.pb.micro.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-micro. DO NOT EDIT. 2 | // source: github.com/hb-chen/micro-starter/service/greeting/proto/greeting/greeting.proto 3 | 4 | package greeting 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 "micro.dev/v4/service/api" 15 | client "micro.dev/v4/service/client" 16 | server "micro.dev/v4/service/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 Greeting service 37 | 38 | func NewGreetingEndpoints() []*api.Endpoint { 39 | return []*api.Endpoint{} 40 | } 41 | 42 | // Client API for Greeting service 43 | 44 | type GreetingService interface { 45 | Call(ctx context.Context, in *CallRequest, opts ...client.CallOption) (*CallResponse, error) 46 | List(ctx context.Context, in *Page, opts ...client.CallOption) (*ListResponse, error) 47 | } 48 | 49 | type greetingService struct { 50 | c client.Client 51 | name string 52 | } 53 | 54 | func NewGreetingService(name string, c client.Client) GreetingService { 55 | return &greetingService{ 56 | c: c, 57 | name: name, 58 | } 59 | } 60 | 61 | func (c *greetingService) Call(ctx context.Context, in *CallRequest, opts ...client.CallOption) (*CallResponse, error) { 62 | req := c.c.NewRequest(c.name, "Greeting.Call", in) 63 | out := new(CallResponse) 64 | err := c.c.Call(ctx, req, out, opts...) 65 | if err != nil { 66 | return nil, err 67 | } 68 | return out, nil 69 | } 70 | 71 | func (c *greetingService) List(ctx context.Context, in *Page, opts ...client.CallOption) (*ListResponse, error) { 72 | req := c.c.NewRequest(c.name, "Greeting.List", in) 73 | out := new(ListResponse) 74 | err := c.c.Call(ctx, req, out, opts...) 75 | if err != nil { 76 | return nil, err 77 | } 78 | return out, nil 79 | } 80 | 81 | // Server API for Greeting service 82 | 83 | type GreetingHandler interface { 84 | Call(context.Context, *CallRequest, *CallResponse) error 85 | List(context.Context, *Page, *ListResponse) error 86 | } 87 | 88 | func RegisterGreetingHandler(s server.Server, hdlr GreetingHandler, opts ...server.HandlerOption) error { 89 | type greeting interface { 90 | Call(ctx context.Context, in *CallRequest, out *CallResponse) error 91 | List(ctx context.Context, in *Page, out *ListResponse) error 92 | } 93 | type Greeting struct { 94 | greeting 95 | } 96 | h := &greetingHandler{hdlr} 97 | return s.Handle(s.NewHandler(&Greeting{h}, opts...)) 98 | } 99 | 100 | type greetingHandler struct { 101 | GreetingHandler 102 | } 103 | 104 | func (h *greetingHandler) Call(ctx context.Context, in *CallRequest, out *CallResponse) error { 105 | return h.GreetingHandler.Call(ctx, in, out) 106 | } 107 | 108 | func (h *greetingHandler) List(ctx context.Context, in *Page, out *ListResponse) error { 109 | return h.GreetingHandler.List(ctx, in, out) 110 | } 111 | -------------------------------------------------------------------------------- /service/greeting/proto/greeting/greeting.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package go.micro.srv.greeting; 4 | 5 | option go_package = "./proto/greeting;greeting"; 6 | 7 | service Greeting { 8 | rpc Call (CallRequest) returns (CallResponse) { 9 | } 10 | rpc List (Page)returns(ListResponse){ 11 | } 12 | } 13 | 14 | message CallRequest { 15 | string msg = 1; 16 | } 17 | 18 | message CallResponse { 19 | int64 id = 1; 20 | string msg = 2; 21 | } 22 | 23 | message ListResponse { 24 | repeated CallResponse items = 1; 25 | } 26 | 27 | message Page { 28 | int64 page = 1; 29 | int64 size = 2; 30 | } 31 | -------------------------------------------------------------------------------- /service/greeting/registry/container.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | _ "github.com/go-sql-driver/mysql" 5 | "github.com/hb-chen/micro-starter/service/greeting/repo/gorm" 6 | "github.com/hb-chen/micro-starter/service/greeting/repo/memory" 7 | "go.uber.org/dig" 8 | "micro.dev/v4/service/config" 9 | 10 | "github.com/hb-chen/micro-starter/service/greeting/domain/usecase" 11 | "github.com/hb-chen/micro-starter/service/greeting/service" 12 | ) 13 | 14 | func NewContainer() (*dig.Container, error) { 15 | c := dig.New() 16 | 17 | err := build(c) 18 | if err != nil { 19 | return nil, err 20 | } 21 | 22 | return c, nil 23 | } 24 | 25 | func build(c *dig.Container) error { 26 | conf, _ := config.Get("persistence") 27 | persistence := conf.String("") 28 | 29 | // ORM选择,gorm、xorm... 30 | switch persistence { 31 | case "xorm": 32 | case "gorm": 33 | // DB初始化 34 | err := c.Provide(gorm.NewDB) 35 | if err != nil { 36 | return err 37 | } 38 | err = c.Provide(gorm.NewGreetingRepository) 39 | if err != nil { 40 | return err 41 | } 42 | default: 43 | // 默认memory作为mock 44 | err := c.Provide(memory.NewGreetingRepository) 45 | if err != nil { 46 | return err 47 | } 48 | } 49 | 50 | err := c.Provide(usecase.NewGreetingUsecase) 51 | if err != nil { 52 | return err 53 | } 54 | err = c.Provide(service.NewGreetingService) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /service/greeting/repo/gorm/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hb-chen/micro-starter/045d1f4f99d50c96857b34fb1eced2c419eb6507/service/greeting/repo/gorm/.gitkeep -------------------------------------------------------------------------------- /service/greeting/repo/gorm/db.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/hb-chen/micro-starter/service/greeting/domain/model" 7 | "gorm.io/driver/mysql" 8 | "gorm.io/gorm" 9 | "gorm.io/plugin/dbresolver" 10 | "micro.dev/v4/service/config" 11 | log "micro.dev/v4/service/logger" 12 | 13 | "github.com/hb-chen/micro-starter/service/greeting/conf" 14 | ) 15 | 16 | func NewDB() (*gorm.DB, error) { 17 | dbConf := conf.Database{} 18 | cv, err := config.Get("database") 19 | if err != nil { 20 | log.Fatal(err) 21 | } 22 | if err := cv.Scan(&dbConf); err != nil { 23 | log.Fatal(err) 24 | } 25 | 26 | dsn := dbConf.User + ":" + dbConf.Password + "@tcp(" + dbConf.Host + ":" + dbConf.Port + ")/" + dbConf.Name + "?charset=utf8mb4&parseTime=True&loc=Local" 27 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) 28 | if err != nil { 29 | log.Error(err) 30 | return nil, err 31 | } 32 | 33 | err = db.Use( 34 | dbresolver.Register(dbresolver.Config{}). 35 | SetConnMaxIdleTime(time.Hour). 36 | SetConnMaxLifetime(dbConf.ConnMaxLifetime). 37 | SetMaxIdleConns(dbConf.MaxIdleConns). 38 | SetMaxOpenConns(dbConf.MaxOpenConns), 39 | ) 40 | if err != nil { 41 | log.Error(err) 42 | return nil, err 43 | } 44 | 45 | return db, nil 46 | } 47 | 48 | func Migrate() error { 49 | db, err := NewDB() 50 | if err != nil { 51 | return err 52 | } 53 | 54 | err = db.AutoMigrate(&model.Msg{}) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /service/greeting/repo/gorm/greeting_repository.go: -------------------------------------------------------------------------------- 1 | package gorm 2 | 3 | import ( 4 | "errors" 5 | 6 | "gorm.io/gorm" 7 | 8 | "github.com/hb-chen/micro-starter/service/greeting/domain/model" 9 | "github.com/hb-chen/micro-starter/service/greeting/domain/repository" 10 | ) 11 | 12 | type greetingRepository struct { 13 | db *gorm.DB 14 | } 15 | 16 | func NewGreetingRepository(db *gorm.DB) repository.GreetingRepository { 17 | return &greetingRepository{ 18 | db: db, 19 | } 20 | } 21 | 22 | func (r *greetingRepository) FindById(id int64) (*model.Msg, error) { 23 | item := model.Msg{} 24 | if result := r.db.Where("id = ?", id).First(&item); result.Error != nil { 25 | if errors.Is(result.Error, gorm.ErrRecordNotFound) { 26 | return nil, nil 27 | } else { 28 | return nil, result.Error 29 | } 30 | } else { 31 | return &item, nil 32 | } 33 | } 34 | 35 | func (r *greetingRepository) FindByMsg(msg string) (*model.Msg, error) { 36 | item := model.Msg{} 37 | if result := r.db.Where("msg = ?", msg).First(&item); result.Error != nil { 38 | if errors.Is(result.Error, gorm.ErrRecordNotFound) { 39 | return nil, nil 40 | } else { 41 | return nil, result.Error 42 | } 43 | } else { 44 | return &item, nil 45 | } 46 | } 47 | 48 | func (r *greetingRepository) Add(msg *model.Msg) error { 49 | err := r.db.Create(msg).Error 50 | if err != nil { 51 | return err 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func (r *greetingRepository) List(page, size int) ([]*model.Msg, error) { 58 | list := make([]*model.Msg, 0) 59 | err := r.db.Order("id DESC").Offset((page - 1) * size).Limit(size).Find(&list).Error 60 | 61 | return list, err 62 | } 63 | -------------------------------------------------------------------------------- /service/greeting/repo/memory/memory.go: -------------------------------------------------------------------------------- 1 | package memory 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/hb-chen/micro-starter/service/greeting/domain/model" 7 | "github.com/hb-chen/micro-starter/service/greeting/domain/repository" 8 | ) 9 | 10 | type greetingRepository struct { 11 | mu *sync.Mutex 12 | items []*model.Msg 13 | } 14 | 15 | func (r *greetingRepository) FindById(id int64) (*model.Msg, error) { 16 | r.mu.Lock() 17 | defer r.mu.Unlock() 18 | 19 | for _, item := range r.items { 20 | if item.Id == id { 21 | return item, nil 22 | } 23 | } 24 | return nil, nil 25 | } 26 | 27 | func (r *greetingRepository) FindByMsg(msg string) (*model.Msg, error) { 28 | r.mu.Lock() 29 | defer r.mu.Unlock() 30 | 31 | for _, item := range r.items { 32 | if item.Msg == msg { 33 | return item, nil 34 | } 35 | } 36 | return nil, nil 37 | } 38 | 39 | func (r *greetingRepository) Add(item *model.Msg) error { 40 | r.mu.Lock() 41 | defer r.mu.Unlock() 42 | 43 | id := int64(len(r.items) + 1) 44 | item.Id = id 45 | 46 | r.items = append(r.items, item) 47 | 48 | return nil 49 | } 50 | 51 | func (r *greetingRepository) List(page, size int) ([]*model.Msg, error) { 52 | idx := (page - 1) * size 53 | offset := idx + size 54 | 55 | if offset > len(r.items) { 56 | offset = len(r.items) 57 | } 58 | 59 | if offset <= idx { 60 | return []*model.Msg{}, nil 61 | } 62 | 63 | items := make([]*model.Msg, 0, offset-idx) 64 | for _, item := range r.items[idx:offset] { 65 | items = append(items, item) 66 | } 67 | return items, nil 68 | } 69 | 70 | func NewGreetingRepository() repository.GreetingRepository { 71 | items := make([]*model.Msg, 0) 72 | 73 | return &greetingRepository{ 74 | mu: &sync.Mutex{}, 75 | items: items, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /service/greeting/server/handler.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "go.uber.org/dig" 5 | log "micro.dev/v4/service/logger" 6 | "micro.dev/v4/service/server" 7 | 8 | pb "github.com/hb-chen/micro-starter/service/greeting/proto/greeting" 9 | ) 10 | 11 | func Apply(server server.Server, c *dig.Container) { 12 | err := c.Invoke(func(svc pb.GreetingHandler) { 13 | err := pb.RegisterGreetingHandler(server, svc) 14 | if err != nil { 15 | log.Error(err) 16 | return 17 | } 18 | }) 19 | if err != nil { 20 | log.Error(err) 21 | return 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /service/greeting/service/greeting.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hb-chen/micro-starter/service/greeting/domain/repository" 7 | "github.com/hb-chen/micro-starter/service/greeting/domain/usecase" 8 | "github.com/hb-chen/micro-starter/service/greeting/proto/greeting" 9 | ) 10 | 11 | type Msg struct { 12 | Id int64 `json:"id"` 13 | Msg string `json:"msg"` 14 | } 15 | 16 | var _ greeting.GreetingHandler = &greetingService{} 17 | 18 | type greetingService struct { 19 | repo repository.GreetingRepository 20 | greetingUsecase usecase.GreetingUsecase 21 | } 22 | 23 | func (g *greetingService) Call(ctx context.Context, req *greeting.CallRequest, resp *greeting.CallResponse) error { 24 | result, err := g.greetingUsecase.Add(req.Msg) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | resp.Id = result.Id 30 | resp.Msg = result.Msg 31 | 32 | return nil 33 | } 34 | 35 | func (g *greetingService) List(ctx context.Context, page *greeting.Page, resp *greeting.ListResponse) error { 36 | items, err := g.greetingUsecase.List(int(page.Page), int(page.Size)) 37 | if err != nil { 38 | return err 39 | } 40 | 41 | for _, item := range items { 42 | 43 | resp.Items = append(resp.Items, 44 | &greeting.CallResponse{ 45 | Id: item.Id, 46 | Msg: item.Msg, 47 | }) 48 | } 49 | 50 | return nil 51 | } 52 | 53 | func NewGreetingService(repo repository.GreetingRepository, us usecase.GreetingUsecase) greeting.GreetingHandler { 54 | return &greetingService{ 55 | repo: repo, 56 | greetingUsecase: us, 57 | } 58 | } 59 | --------------------------------------------------------------------------------