mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1cd1449085 | |||
| 55690f3738 | |||
| 13f6fb1929 | |||
| e8088a6563 | |||
| e8051bad9a | |||
| d0d41a63a6 | |||
| 853b038a47 | |||
| 34946f6105 | |||
| 15d88c269d | |||
| cbbfd85eeb | |||
| adf90c876f | |||
| b4f76b8448 | |||
| ed858ebd4b | |||
| 272b9c7afd | |||
| 8dc8dd9756 | |||
| a64d1001e2 |
24
.github/workflows/gf.yml
vendored
24
.github/workflows/gf.yml
vendored
@ -35,6 +35,15 @@ jobs:
|
||||
|
||||
# Service containers to run with `code-test`
|
||||
services:
|
||||
# Etcd service.
|
||||
# docker run -d --name etcd -p 2379:2379 -e ALLOW_NONE_AUTHENTICATION=yes loads/etcd:3.4.24
|
||||
etcd:
|
||||
image: loads/etcd:3.4.24
|
||||
env:
|
||||
ALLOW_NONE_AUTHENTICATION: yes
|
||||
ports:
|
||||
- 2379:2379
|
||||
|
||||
# Redis backend server.
|
||||
redis:
|
||||
image : loads/redis:7.0
|
||||
@ -54,7 +63,6 @@ jobs:
|
||||
MYSQL_DATABASE : test
|
||||
MYSQL_ROOT_PASSWORD: 12345678
|
||||
ports:
|
||||
# Maps tcp port 3306 on service container to the host
|
||||
- 3306:3306
|
||||
|
||||
# PostgreSQL backend server.
|
||||
@ -164,18 +172,8 @@ jobs:
|
||||
uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Setup Golang caches
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/.cache/go-build
|
||||
~/Library/Caches/go-build
|
||||
~\AppData\Local\go-build
|
||||
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-${{ matrix.go-version }}-
|
||||
cache: true
|
||||
cache-dependency-path: '**/go.sum'
|
||||
|
||||
- name: Before Script
|
||||
run: bash .github/workflows/before_script.sh
|
||||
|
||||
19
.github/workflows/issue-translator.yml
vendored
Normal file
19
.github/workflows/issue-translator.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
# https://github.com/usthe/issues-translate-action
|
||||
name: 'Issue Translator'
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: true
|
||||
# not require, default false. Decide whether to modify the issue title
|
||||
# if true, the robot account @Issues-translate-bot must have modification permissions,
|
||||
# invite @Issues-translate-bot to your project or use your custom bot.
|
||||
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
|
||||
@ -5,7 +5,7 @@
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/actions/workflows/gf.yml)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://codecov.io/gh/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
</div>
|
||||
|
||||
@ -2,54 +2,67 @@
|
||||
|
||||
`gf` is a powerful CLI tool for building [GoFrame](https://goframe.org) application with convenience.
|
||||
|
||||
|
||||
## 1. Install
|
||||
|
||||
## 1) PreCompiled Binary
|
||||
You can also install `gf` tool using pre-built binaries: https://github.com/gogf/gf/releases
|
||||
|
||||
You can also install `gf` tool using pre-built binaries: <https://github.com/gogf/gf/releases>
|
||||
|
||||
1. `Mac` & `Linux`
|
||||
```shell
|
||||
|
||||
```shell
|
||||
wget -O gf https://github.com/gogf/gf/releases/latest/download/gf_$(go env GOOS)_$(go env GOARCH) && chmod +x gf && ./gf install -y && rm ./gf
|
||||
```
|
||||
```
|
||||
|
||||
> If you're using `zsh`, you might need rename your alias by command `alias gf=gf` to resolve the conflicts between `gf` and `git fetch`.
|
||||
|
||||
2. `Windows`
|
||||
Manually download, execute in command line it and then follow the instruction.
|
||||
|
||||
Manually download, execute it and then follow the instruction.
|
||||
3. Database support
|
||||
|
||||
3. Database `sqlite` and `oracle` are not support in `gf gen` command in default as it needs `cgo` and `gcc`, you can manually make some changes to the source codes and do the building.
|
||||
| DB | support | remarks |
|
||||
| :------: | :------: | :------: |
|
||||
| mysql | yes | - |
|
||||
| mariadb | yes | - |
|
||||
| tidb | yes | - |
|
||||
| mssql | yes | - |
|
||||
| oracle | yes | - |
|
||||
| pgsql | yes | - |
|
||||
| sqlite | yes | - |
|
||||
| clickhouse | no | manually make some changes to the [source codes](./internal/cmd/cmd_gen_dao.go) and do the building. |
|
||||
| dm | no | manually make some changes to the [source codes](./internal/cmd/cmd_gen_dao.go) and do the building. |
|
||||
|
||||
## 2) Manually Install
|
||||
|
||||
```shell
|
||||
```shell
|
||||
git clone https://github.com/gogf/gf && cd gf/cmd/gf && go install
|
||||
```
|
||||
|
||||
```
|
||||
|
||||
## 2. Commands
|
||||
|
||||
```html
|
||||
$ gf
|
||||
USAGE
|
||||
gf COMMAND [OPTION]
|
||||
|
||||
COMMAND
|
||||
env show current Golang environment variables
|
||||
run running go codes with hot-compiled-like feature
|
||||
gen automatically generate go files for dao/do/entity/pb/pbentity
|
||||
tpl template parsing and building commands
|
||||
init create and initialize an empty GoFrame project
|
||||
pack packing any file/directory to a resource file, or a go file
|
||||
build cross-building go project for lots of platforms
|
||||
docker build docker image for current GoFrame project
|
||||
install install gf binary to system (might need root/admin permission)
|
||||
version show version information of current binary
|
||||
env show current Golang environment variables
|
||||
run running go codes with hot-compiled-like feature
|
||||
gen automatically generate go files for dao/do/entity/pb/pbentity
|
||||
tpl template parsing and building commands
|
||||
init create and initialize an empty GoFrame project
|
||||
pack packing any file/directory to a resource file, or a go file
|
||||
build cross-building go project for lots of platforms
|
||||
docker build docker image for current GoFrame project
|
||||
install install gf binary to system (might need root/admin permission)
|
||||
version show version information of current binary
|
||||
|
||||
OPTION
|
||||
-y, --yes all yes for all command without prompt ask
|
||||
-v, --version show version information of current binary
|
||||
-d, --debug show internal detailed debugging information
|
||||
-h, --help more information about this command
|
||||
-y, --yes all yes for all command without prompt ask
|
||||
-v, --version show version information of current binary
|
||||
-d, --debug show internal detailed debugging information
|
||||
-h, --help more information about this command
|
||||
|
||||
ADDITIONAL
|
||||
Use "gf COMMAND -h" for details about a command.
|
||||
@ -60,10 +73,3 @@ ADDITIONAL
|
||||
### 1). Command `gf run` returns `pipe: too many open files`
|
||||
|
||||
Please use `ulimit -n 65535` to enlarge your system configuration for max open files for current terminal shell session, and then `gf run`.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -39,9 +39,9 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.7.0 // indirect
|
||||
golang.org/x/crypto v0.1.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.1.0 // indirect
|
||||
golang.org/x/sys v0.1.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/net v0.7.0 // indirect
|
||||
golang.org/x/sys v0.5.0 // indirect
|
||||
golang.org/x/text v0.7.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.16.8 // indirect
|
||||
modernc.org/mathutil v1.4.1 // indirect
|
||||
|
||||
@ -79,8 +79,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -97,15 +97,15 @@ golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220405052023-b1e9470b6e64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
||||
@ -2,12 +2,9 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/genv"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
)
|
||||
@ -16,64 +13,53 @@ type (
|
||||
cGenPb struct{}
|
||||
cGenPbInput struct {
|
||||
g.Meta `name:"pb" brief:"parse proto files and generate protobuf go files"`
|
||||
Path string `name:"path" short:"p" dc:"protobuf file folder path" d:"manifest/protobuf"`
|
||||
Output string `name:"output" short:"o" dc:"output folder path storing generated go files" d:"api"`
|
||||
}
|
||||
cGenPbOutput struct{}
|
||||
)
|
||||
|
||||
func (c cGenPb) Pb(ctx context.Context, in cGenPbInput) (out *cGenPbOutput, err error) {
|
||||
// Necessary check.
|
||||
if gproc.SearchBinary("protoc") == "" {
|
||||
protoc := gproc.SearchBinary("protoc")
|
||||
if protoc == "" {
|
||||
mlog.Fatalf(`command "protoc" not found in your environment, please install protoc first to proceed this command`)
|
||||
}
|
||||
|
||||
// protocol fold checks.
|
||||
protoFolder := "protocol"
|
||||
if !gfile.Exists(protoFolder) {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, protoFolder)
|
||||
protoPath := gfile.RealPath(in.Path)
|
||||
if protoPath == "" {
|
||||
mlog.Fatalf(`proto files folder "%s" does not exist`, in.Path)
|
||||
}
|
||||
// output path checks.
|
||||
outputPath := gfile.RealPath(in.Output)
|
||||
if outputPath == "" {
|
||||
mlog.Fatalf(`output folder "%s" does not exist`, in.Output)
|
||||
}
|
||||
|
||||
// folder scanning.
|
||||
files, err := gfile.ScanDirFile(protoFolder, "*.proto", true)
|
||||
files, err := gfile.ScanDirFile(protoPath, "*.proto", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
if len(files) == 0 {
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, protoFolder)
|
||||
mlog.Fatalf(`no proto files found in folder "%s"`, in.Path)
|
||||
}
|
||||
|
||||
if err = gfile.Chdir(protoPath); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
dirSet := gset.NewStrSet()
|
||||
for _, file := range files {
|
||||
dirSet.Add(gfile.Dir(file))
|
||||
}
|
||||
var (
|
||||
servicePath = gfile.RealPath(".")
|
||||
goPathSrc = gfile.RealPath(gfile.Join(genv.Get("GOPATH").String(), "src"))
|
||||
)
|
||||
dirSet.Iterator(func(protoDirPath string) bool {
|
||||
parsingCommand := fmt.Sprintf(
|
||||
"protoc --gofast_out=plugins=grpc:. %s/*.proto -I%s",
|
||||
protoDirPath,
|
||||
servicePath,
|
||||
)
|
||||
if goPathSrc != "" {
|
||||
parsingCommand += " -I" + goPathSrc
|
||||
}
|
||||
mlog.Print(parsingCommand)
|
||||
if output, err := gproc.ShellExec(ctx, parsingCommand); err != nil {
|
||||
mlog.Print(output)
|
||||
var command = gproc.NewProcess(protoc, nil)
|
||||
command.Args = append(command.Args, "--proto_path="+gfile.Pwd())
|
||||
command.Args = append(command.Args, "--go_out=paths=source_relative:"+outputPath)
|
||||
command.Args = append(command.Args, "--go-grpc_out=paths=source_relative:"+outputPath)
|
||||
command.Args = append(command.Args, file)
|
||||
mlog.Print(command.String())
|
||||
if err = command.Run(ctx); err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
return true
|
||||
})
|
||||
// Custom replacement.
|
||||
//pbFolder := "protobuf"
|
||||
//_, _ = gfile.ScanDirFileFunc(pbFolder, "*.go", true, func(path string) string {
|
||||
// content := gfile.GetContents(path)
|
||||
// content = gstr.ReplaceByArray(content, g.SliceStr{
|
||||
// `gtime "gtime"`, `gtime "github.com/gogf/gf/v2/os/gtime"`,
|
||||
// })
|
||||
// _ = gfile.PutContents(path, content)
|
||||
// utils.GoFmt(path)
|
||||
// return path
|
||||
//})
|
||||
}
|
||||
mlog.Print("done!")
|
||||
return
|
||||
}
|
||||
|
||||
@ -9,13 +9,13 @@ import (
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/utils"
|
||||
)
|
||||
|
||||
func doClear(ctx context.Context, dirPath string) {
|
||||
func doClear(ctx context.Context, dirPath string, force bool) {
|
||||
files, err := gfile.ScanDirFile(dirPath, "*.go", true)
|
||||
if err != nil {
|
||||
mlog.Fatal(err)
|
||||
}
|
||||
for _, file := range files {
|
||||
if utils.IsFileDoNotEdit(file) {
|
||||
if force || utils.IsFileDoNotEdit(file) {
|
||||
if err = gfile.Remove(file); err != nil {
|
||||
mlog.Print(err)
|
||||
}
|
||||
|
||||
@ -6,12 +6,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/consts"
|
||||
"github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
|
||||
@ -24,7 +25,7 @@ func generateDao(ctx context.Context, in CGenDaoInternalInput) {
|
||||
dirPathDaoInternal = gfile.Join(dirPathDao, "internal")
|
||||
)
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathDao)
|
||||
doClear(ctx, dirPathDao, true)
|
||||
}
|
||||
for i := 0; i < len(in.TableNames); i++ {
|
||||
generateDaoSingle(ctx, generateDaoSingleInput{
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
func generateDo(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var dirPathDo = gfile.Join(in.Path, in.DoPath)
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathDo)
|
||||
doClear(ctx, dirPathDo, false)
|
||||
}
|
||||
in.NoJsonTag = true
|
||||
in.DescriptionTag = false
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
func generateEntity(ctx context.Context, in CGenDaoInternalInput) {
|
||||
var dirPathEntity = gfile.Join(in.Path, in.EntityPath)
|
||||
if in.Clear {
|
||||
doClear(ctx, dirPathEntity)
|
||||
doClear(ctx, dirPathEntity, false)
|
||||
}
|
||||
// Model content.
|
||||
for i, tableName := range in.TableNames {
|
||||
|
||||
@ -207,9 +207,11 @@ func (a *SortedArray) doRemoveWithoutLock(index int) (value interface{}, found b
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedArray) RemoveValue(value interface{}) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if i, r := a.binSearch(value, false); r == 0 {
|
||||
_, res := a.doRemoveWithoutLock(i)
|
||||
return res
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -193,9 +193,11 @@ func (a *SortedIntArray) doRemoveWithoutLock(index int) (value int, found bool)
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedIntArray) RemoveValue(value int) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
_, found := a.Remove(i)
|
||||
return found
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if i, r := a.binSearch(value, false); r == 0 {
|
||||
_, res := a.doRemoveWithoutLock(i)
|
||||
return res
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -179,9 +179,11 @@ func (a *SortedStrArray) doRemoveWithoutLock(index int) (value string, found boo
|
||||
// RemoveValue removes an item by value.
|
||||
// It returns true if value is found in the array, or else false if not found.
|
||||
func (a *SortedStrArray) RemoveValue(value string) bool {
|
||||
if i := a.Search(value); i != -1 {
|
||||
a.Remove(i)
|
||||
return true
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if i, r := a.binSearch(value, false); r == 0 {
|
||||
_, res := a.doRemoveWithoutLock(i)
|
||||
return res
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -514,3 +514,24 @@ func (m *AnyAnyMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *AnyAnyMap) IsSubOf(other *AnyAnyMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// IntAnyMap implements map[int]interface{} with RWMutex that has switch.
|
||||
type IntAnyMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]interface{}
|
||||
@ -514,3 +515,24 @@ func (m *IntAnyMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewIntAnyMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *IntAnyMap) IsSubOf(other *IntAnyMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// IntIntMap implements map[int]int with RWMutex that has switch.
|
||||
type IntIntMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]int
|
||||
@ -484,3 +485,24 @@ func (m *IntIntMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewIntIntMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *IntIntMap) IsSubOf(other *IntIntMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// IntStrMap implements map[int]string with RWMutex that has switch.
|
||||
type IntStrMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]string
|
||||
@ -484,3 +485,24 @@ func (m *IntStrMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewIntStrMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *IntStrMap) IsSubOf(other *IntStrMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -501,3 +501,24 @@ func (m *StrAnyMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewStrAnyMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *StrAnyMap) IsSubOf(other *StrAnyMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// StrIntMap implements map[string]int with RWMutex that has switch.
|
||||
type StrIntMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]int
|
||||
@ -488,3 +489,24 @@ func (m *StrIntMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewStrIntMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *StrIntMap) IsSubOf(other *StrIntMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// StrStrMap implements map[string]string with RWMutex that has switch.
|
||||
type StrStrMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]string
|
||||
@ -477,3 +478,24 @@ func (m *StrStrMap) DeepCopy() interface{} {
|
||||
}
|
||||
return NewStrStrMapFrom(data, m.mu.IsSafe())
|
||||
}
|
||||
|
||||
// IsSubOf checks whether the current map is a sub-map of `other`.
|
||||
func (m *StrStrMap) IsSubOf(other *StrStrMap) bool {
|
||||
if m == other {
|
||||
return true
|
||||
}
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key, value := range m.data {
|
||||
otherValue, ok := other.data[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if otherValue != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -391,3 +391,18 @@ func Test_AnyAnyMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get("k1"), n.Get("k1"))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m2 := gmap.NewAnyAnyMapFrom(g.MapAnyAny{
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -375,3 +375,18 @@ func Test_IntAnyMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
m2 := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
2: "v2",
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -383,3 +383,18 @@ func Test_IntIntMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
1: 1,
|
||||
2: 2,
|
||||
})
|
||||
m2 := gmap.NewIntAnyMapFrom(g.MapIntAny{
|
||||
2: 2,
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -447,3 +447,18 @@ func Test_IntStrMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get(1), n.Get(1))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
1: "v1",
|
||||
2: "v2",
|
||||
})
|
||||
m2 := gmap.NewIntStrMapFrom(g.MapIntStr{
|
||||
2: "v2",
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -381,3 +381,18 @@ func Test_StrAnyMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m2 := gmap.NewStrAnyMapFrom(g.MapStrAny{
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -389,3 +389,18 @@ func Test_StrIntMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"k1": 1,
|
||||
"k2": 2,
|
||||
})
|
||||
m2 := gmap.NewStrIntMapFrom(g.MapStrInt{
|
||||
"k2": 2,
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -388,3 +388,18 @@ func Test_StrStrMap_DeepCopy(t *testing.T) {
|
||||
t.AssertNE(m.Get("key1"), n.Get("key1"))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_IsSubOf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m2 := gmap.NewStrStrMapFrom(g.MapStrStr{
|
||||
"k2": "v2",
|
||||
})
|
||||
t.Assert(m1.IsSubOf(m2), false)
|
||||
t.Assert(m2.IsSubOf(m1), true)
|
||||
t.Assert(m2.IsSubOf(m2), true)
|
||||
})
|
||||
}
|
||||
|
||||
@ -5,6 +5,6 @@ go 1.15
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
|
||||
require (
|
||||
gitee.com/chunanyong/dm v1.8.11
|
||||
gitee.com/chunanyong/dm v1.8.10
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
gitee.com/chunanyong/dm v1.8.11 h1:JPwiS1PqHObo4QFodruLR8WOhLP+7Y/EKGGu2BJ5SJI=
|
||||
gitee.com/chunanyong/dm v1.8.11/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg=
|
||||
gitee.com/chunanyong/dm v1.8.10 h1:9S1CKUggWHIea/GI7nr7S/DNMaxIilNFgfzdzKDx2+I=
|
||||
gitee.com/chunanyong/dm v1.8.10/go.mod h1:EPRJnuPFgbyOFgJ0TRYCTGzhq+ZT4wdyaj/GW/LLcNg=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
|
||||
@ -87,6 +87,10 @@ func (d *Driver) Open(config *gdb.ConfigNode) (db *sql.DB, err error) {
|
||||
)
|
||||
}
|
||||
|
||||
if config.Namespace != "" {
|
||||
source = fmt.Sprintf("%s search_path=%s", source, config.Namespace)
|
||||
}
|
||||
|
||||
if config.Timezone != "" {
|
||||
source = fmt.Sprintf("%s timezone=%s", source, config.Timezone)
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ func (d *Driver) TableFields(
|
||||
if link, err = d.SlaveLink(usedSchema); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, err = d.DoSelect(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, table))
|
||||
result, err = d.DoSelect(ctx, link, fmt.Sprintf(`PRAGMA TABLE_INFO(%s)`, d.QuoteWord(table)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -33,15 +33,16 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
TableSize = 10
|
||||
TableName = "user"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix = "gf_"
|
||||
CreateTime = "2018-10-24 10:00:00"
|
||||
DBGroupTest = "test"
|
||||
DBGroupPrefix = "prefix"
|
||||
DBGroupInvalid = "invalid"
|
||||
TableSize = 10
|
||||
TableName = "user"
|
||||
TableNameWhichIsKeyword = "group"
|
||||
TestSchema1 = "test1"
|
||||
TestSchema2 = "test2"
|
||||
TableNamePrefix = "gf_"
|
||||
CreateTime = "2018-10-24 10:00:00"
|
||||
DBGroupTest = "test"
|
||||
DBGroupPrefix = "prefix"
|
||||
DBGroupInvalid = "invalid"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -125,7 +126,7 @@ func createTableWithDb(db gdb.DB, table ...string) (name string) {
|
||||
nickname VARCHAR(45),
|
||||
create_time DATETIME
|
||||
);
|
||||
`, name,
|
||||
`, db.GetCore().QuoteWord(name),
|
||||
)); err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
@ -1571,3 +1571,39 @@ func Test_TableFields(t *testing.T) {
|
||||
gtest.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TableNameIsKeyword(t *testing.T) {
|
||||
table := createInitTable(TableNameWhichIsKeyword)
|
||||
defer dropTable(table)
|
||||
_, err := db.Update(ctx, table, "create_time='2010-10-10 00:00:01'", "id=?", 1)
|
||||
gtest.AssertNil(err)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id := 1
|
||||
result, err := db.Model(table).Fields("*").Where("id = ?", id).All()
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
type t_user struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime string
|
||||
}
|
||||
|
||||
t_users := make([]t_user, 0)
|
||||
err = result.Structs(&t_users)
|
||||
if err != nil {
|
||||
gtest.Fatal(err)
|
||||
}
|
||||
|
||||
resultIntMap := result.MapKeyInt("id")
|
||||
t.Assert(t_users[0].Id, resultIntMap[id]["id"])
|
||||
t.Assert(t_users[0].Passport, resultIntMap[id]["passport"])
|
||||
t.Assert(t_users[0].Password, resultIntMap[id]["password"])
|
||||
t.Assert(t_users[0].NickName, resultIntMap[id]["nickname"])
|
||||
t.Assert(t_users[0].CreateTime, resultIntMap[id]["create_time"])
|
||||
})
|
||||
}
|
||||
|
||||
@ -47,6 +47,7 @@ func New(config *gredis.Config) *Redis {
|
||||
Password: config.Pass,
|
||||
DB: config.Db,
|
||||
MaxRetries: defaultMaxRetries,
|
||||
PoolSize: config.MaxActive,
|
||||
MinIdleConns: config.MinIdle,
|
||||
MaxConnAge: config.MaxConnLifetime,
|
||||
IdleTimeout: config.IdleTimeout,
|
||||
|
||||
3
contrib/registry/README.MD
Normal file
3
contrib/registry/README.MD
Normal file
@ -0,0 +1,3 @@
|
||||
# Service registrar and discovery
|
||||
|
||||
Please refer to certain sub folder.
|
||||
@ -8,16 +8,16 @@
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -45,7 +45,7 @@ const (
|
||||
)
|
||||
|
||||
// New creates and returns a new etcd registry.
|
||||
func New(address string, option ...Option) *Registry {
|
||||
func New(address string, option ...Option) gsvc.Registry {
|
||||
endpoints := gstr.SplitAndTrim(address, ",")
|
||||
if len(endpoints) == 0 {
|
||||
panic(gerror.NewCodef(gcode.CodeInvalidParameter, `invalid etcd address "%s"`, address))
|
||||
@ -84,31 +84,22 @@ func extractResponseToServices(res *etcd3.GetResponse) ([]gsvc.Service, error) {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
services []gsvc.Service
|
||||
serviceKey string
|
||||
serviceMap = make(map[string]*gsvc.LocalService)
|
||||
services []gsvc.Service
|
||||
servicePrefixMap = make(map[string]*Service)
|
||||
)
|
||||
for _, kv := range res.Kvs {
|
||||
service, err := gsvc.NewServiceWithKV(string(kv.Key), string(kv.Value))
|
||||
service, err := gsvc.NewServiceWithKV(
|
||||
string(kv.Key), string(kv.Value),
|
||||
)
|
||||
if err != nil {
|
||||
return services, err
|
||||
}
|
||||
localService, ok := service.(*gsvc.LocalService)
|
||||
if !ok {
|
||||
return nil, gerror.Newf(
|
||||
`service from "gsvc.NewServiceWithKV" is not "*gsvc.LocalService", but "%s"`,
|
||||
reflect.TypeOf(service),
|
||||
)
|
||||
}
|
||||
if localService != nil {
|
||||
serviceKey = localService.GetPrefix()
|
||||
var localServiceInMap *gsvc.LocalService
|
||||
if localServiceInMap, ok = serviceMap[serviceKey]; ok {
|
||||
localServiceInMap.Endpoints = append(localServiceInMap.Endpoints, localService.Endpoints...)
|
||||
} else {
|
||||
serviceMap[serviceKey] = localService
|
||||
services = append(services, service)
|
||||
}
|
||||
s := NewService(service)
|
||||
if v, ok := servicePrefixMap[service.GetPrefix()]; ok {
|
||||
v.Endpoints = append(v.Endpoints, service.GetEndpoints()...)
|
||||
} else {
|
||||
servicePrefixMap[s.GetPrefix()] = s
|
||||
services = append(services, s)
|
||||
}
|
||||
}
|
||||
return services, nil
|
||||
|
||||
@ -9,11 +9,14 @@ package etcd
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Search is the etcd discovery search function.
|
||||
// Search searches and returns services with specified condition.
|
||||
func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]gsvc.Service, error) {
|
||||
if in.Prefix == "" && in.Name != "" {
|
||||
in.Prefix = gsvc.NewServiceWithName(in.Name).GetPrefix()
|
||||
@ -29,20 +32,31 @@ func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]gsvc.Serv
|
||||
}
|
||||
// Service filter.
|
||||
filteredServices := make([]gsvc.Service, 0)
|
||||
for _, v := range services {
|
||||
if in.Name != "" && in.Name != v.GetName() {
|
||||
for _, service := range services {
|
||||
if in.Prefix != "" && !gstr.HasPrefix(service.GetKey(), in.Prefix) {
|
||||
continue
|
||||
}
|
||||
if in.Version != "" && in.Version != v.GetVersion() {
|
||||
if in.Name != "" && service.GetName() != in.Name {
|
||||
continue
|
||||
}
|
||||
service := v
|
||||
filteredServices = append(filteredServices, service)
|
||||
if in.Version != "" && service.GetVersion() != in.Version {
|
||||
continue
|
||||
}
|
||||
if len(in.Metadata) != 0 {
|
||||
m1 := gmap.NewStrAnyMapFrom(in.Metadata)
|
||||
m2 := gmap.NewStrAnyMapFrom(service.GetMetadata())
|
||||
if !m1.IsSubOf(m2) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
resultItem := service
|
||||
filteredServices = append(filteredServices, resultItem)
|
||||
}
|
||||
return filteredServices, nil
|
||||
}
|
||||
|
||||
// Watch is the etcd discovery watch function.
|
||||
// Watch watches specified condition changes.
|
||||
// The `key` is the prefix of service key.
|
||||
func (r *Registry) Watch(ctx context.Context, key string) (gsvc.Watcher, error) {
|
||||
return newWatcher(key, r.client)
|
||||
}
|
||||
|
||||
@ -9,13 +9,16 @@ package etcd
|
||||
import (
|
||||
"context"
|
||||
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
// Register implements the gsvc.Register interface.
|
||||
// Register registers `service` to Registry.
|
||||
// Note that it returns a new Service if it changes the input Service with custom one.
|
||||
func (r *Registry) Register(ctx context.Context, service gsvc.Service) (gsvc.Service, error) {
|
||||
service = NewService(service)
|
||||
r.lease = etcd3.NewLease(r.client)
|
||||
grant, err := r.lease.Grant(ctx, int64(r.keepaliveTTL.Seconds()))
|
||||
if err != nil {
|
||||
@ -33,7 +36,7 @@ func (r *Registry) Register(ctx context.Context, service gsvc.Service) (gsvc.Ser
|
||||
key, value, grant.ID,
|
||||
)
|
||||
}
|
||||
r.logger.Infof(
|
||||
r.logger.Debugf(
|
||||
ctx,
|
||||
`etcd put success with key "%s", value "%s", lease "%d"`,
|
||||
key, value, grant.ID,
|
||||
@ -46,7 +49,7 @@ func (r *Registry) Register(ctx context.Context, service gsvc.Service) (gsvc.Ser
|
||||
return service, nil
|
||||
}
|
||||
|
||||
// Deregister implements the gsvc.Deregister interface.
|
||||
// Deregister off-lines and removes `service` from the Registry.
|
||||
func (r *Registry) Deregister(ctx context.Context, service gsvc.Service) error {
|
||||
_, err := r.client.Delete(ctx, service.GetKey())
|
||||
if r.lease != nil {
|
||||
|
||||
64
contrib/registry/etcd/etcd_service.go
Normal file
64
contrib/registry/etcd/etcd_service.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package etcd
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Service wrapper.
|
||||
type Service struct {
|
||||
gsvc.Service
|
||||
Endpoints gsvc.Endpoints
|
||||
Metadata gsvc.Metadata
|
||||
}
|
||||
|
||||
// NewService creates and returns local Service from gsvc.Service interface object.
|
||||
func NewService(service gsvc.Service) *Service {
|
||||
s, ok := service.(*Service)
|
||||
if ok {
|
||||
if s.Endpoints == nil {
|
||||
s.Endpoints = make(gsvc.Endpoints, 0)
|
||||
}
|
||||
if s.Metadata == nil {
|
||||
s.Metadata = make(gsvc.Metadata)
|
||||
}
|
||||
return s
|
||||
}
|
||||
s = &Service{
|
||||
Service: service,
|
||||
Endpoints: make(gsvc.Endpoints, 0),
|
||||
Metadata: make(gsvc.Metadata),
|
||||
}
|
||||
if len(service.GetEndpoints()) > 0 {
|
||||
s.Endpoints = service.GetEndpoints()
|
||||
}
|
||||
if len(service.GetMetadata()) > 0 {
|
||||
s.Metadata = service.GetMetadata()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetMetadata returns the Metadata map of service.
|
||||
// The Metadata is key-value pair map specifying extra attributes of a service.
|
||||
func (s *Service) GetMetadata() gsvc.Metadata {
|
||||
return s.Metadata
|
||||
}
|
||||
|
||||
// GetEndpoints returns the Endpoints of service.
|
||||
// The Endpoints contain multiple host/port information of service.
|
||||
func (s *Service) GetEndpoints() gsvc.Endpoints {
|
||||
return s.Endpoints
|
||||
}
|
||||
|
||||
// GetValue formats and returns the value of the service.
|
||||
// The result value is commonly used for key-value registrar server.
|
||||
func (s *Service) GetValue() string {
|
||||
b, _ := gjson.Marshal(s.Metadata)
|
||||
return string(b)
|
||||
}
|
||||
@ -9,8 +9,9 @@ package etcd
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
etcd3 "go.etcd.io/etcd/client/v3"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -46,6 +47,7 @@ func (w *watcher) Proceed() ([]gsvc.Service, error) {
|
||||
case <-w.ctx.Done():
|
||||
return nil, w.ctx.Err()
|
||||
case <-w.watchChan:
|
||||
// It retrieves, merges and returns all services by prefix if any changes.
|
||||
return w.getServicesByPrefix()
|
||||
}
|
||||
}
|
||||
|
||||
145
contrib/registry/etcd/etcd_z_test.go
Normal file
145
contrib/registry/etcd/etcd_z_test.go
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package etcd_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/etcd/v2"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func TestRegistry(t *testing.T) {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
registry = etcd.New(`127.0.0.1:2379`)
|
||||
)
|
||||
svc := &gsvc.LocalService{
|
||||
Name: guid.S(),
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:8888"),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
registered, err := registry.Register(ctx, svc)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc.GetName())
|
||||
})
|
||||
|
||||
// Search by name.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.Name,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
|
||||
// Search by prefix.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Prefix: svc.GetPrefix(),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
|
||||
// Search by metadata.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.GetName(),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.GetName(),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "grpc",
|
||||
},
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := registry.Deregister(ctx, svc)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
registry = etcd.New(`127.0.0.1:2379`)
|
||||
)
|
||||
|
||||
svc1 := &gsvc.LocalService{
|
||||
Name: guid.S(),
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:8888"),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
registered, err := registry.Register(ctx, svc1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc1.GetName())
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
watcher, err := registry.Watch(ctx, svc1.GetPrefix())
|
||||
t.AssertNil(err)
|
||||
|
||||
// Register another service.
|
||||
svc2 := &gsvc.LocalService{
|
||||
Name: svc1.Name,
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9999"),
|
||||
}
|
||||
registered, err := registry.Register(ctx, svc2)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc2.GetName())
|
||||
|
||||
// Watch and retrieve the service changes:
|
||||
// svc1 and svc2 is the same service name, which has 2 endpoints.
|
||||
proceedResult, err := watcher.Proceed()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(proceedResult), 1)
|
||||
t.Assert(
|
||||
proceedResult[0].GetEndpoints(),
|
||||
gsvc.Endpoints{svc1.GetEndpoints()[0], svc2.GetEndpoints()[0]},
|
||||
)
|
||||
|
||||
// Watch and retrieve the service changes:
|
||||
// left only svc1, which means this service has only 1 endpoint.
|
||||
err = registry.Deregister(ctx, svc2)
|
||||
t.AssertNil(err)
|
||||
proceedResult, err = watcher.Proceed()
|
||||
t.AssertNil(err)
|
||||
t.Assert(
|
||||
proceedResult[0].GetEndpoints(),
|
||||
gsvc.Endpoints{svc1.GetEndpoints()[0]},
|
||||
)
|
||||
t.AssertNil(watcher.Close())
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := registry.Deregister(ctx, svc1)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
77
contrib/registry/file/README.MD
Normal file
77
contrib/registry/file/README.MD
Normal file
@ -0,0 +1,77 @@
|
||||
# GoFrame File Registry
|
||||
|
||||
|
||||
Use `file` as service registration and discovery management.
|
||||
|
||||
|
||||
## Installation
|
||||
```
|
||||
go get -u -v github.com/gogf/gf/contrib/registry/file/v2
|
||||
```
|
||||
suggested using `go.mod`:
|
||||
```
|
||||
require github.com/gogf/gf/contrib/registry/file/v2 latest
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
### Reference example
|
||||
|
||||
[server](example/registry/file/server/main.go)
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))
|
||||
|
||||
s := g.Server(`hello.svc`)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
g.Log().Info(r.Context(), `request received`)
|
||||
r.Response.Write(`Hello world`)
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
[client](example/registry/file/client/main.go)
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))
|
||||
|
||||
client := g.Client()
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Get(gctx.New(), `http://hello.svc/`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(res.ReadAllString())
|
||||
res.Close()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
42
contrib/registry/file/file.go
Normal file
42
contrib/registry/file/file.go
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package file implements service Registry and Discovery using file.
|
||||
package file
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
var (
|
||||
_ gsvc.Registry = &Registry{}
|
||||
)
|
||||
|
||||
const (
|
||||
updateAtKey = "UpdateAt"
|
||||
serviceTTL = 20 * time.Second
|
||||
serviceUpdateInterval = 10 * time.Second
|
||||
defaultSeparator = "#"
|
||||
)
|
||||
|
||||
// Registry implements interface Registry using file.
|
||||
// This implement is usually for testing only.
|
||||
type Registry struct {
|
||||
path string // Local storing folder path for Services.
|
||||
}
|
||||
|
||||
// New creates and returns a gsvc.Registry implements using file.
|
||||
func New(path string) gsvc.Registry {
|
||||
if !gfile.Exists(path) {
|
||||
_ = gfile.Mkdir(path)
|
||||
}
|
||||
return &Registry{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
134
contrib/registry/file/file_discovery.go
Normal file
134
contrib/registry/file/file_discovery.go
Normal file
@ -0,0 +1,134 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gfsnotify"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Search searches and returns services with specified condition.
|
||||
func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) (result []gsvc.Service, err error) {
|
||||
services, err := r.getServices(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, service := range services {
|
||||
if in.Prefix != "" && !gstr.HasPrefix(service.GetKey(), in.Prefix) {
|
||||
continue
|
||||
}
|
||||
if in.Name != "" && service.GetName() != in.Name {
|
||||
continue
|
||||
}
|
||||
if in.Version != "" && service.GetVersion() != in.Version {
|
||||
continue
|
||||
}
|
||||
if len(in.Metadata) != 0 {
|
||||
m1 := gmap.NewStrAnyMapFrom(in.Metadata)
|
||||
m2 := gmap.NewStrAnyMapFrom(service.GetMetadata())
|
||||
if !m1.IsSubOf(m2) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
resultItem := service
|
||||
result = append(result, resultItem)
|
||||
}
|
||||
result = r.mergeServices(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch watches specified condition changes.
|
||||
// The `key` is the prefix of service key.
|
||||
func (r *Registry) Watch(ctx context.Context, key string) (watcher gsvc.Watcher, err error) {
|
||||
fileWatcher := &Watcher{
|
||||
prefix: key,
|
||||
discovery: r,
|
||||
ch: make(chan gsvc.Service, 100),
|
||||
}
|
||||
_, err = gfsnotify.Add(r.path, func(event *gfsnotify.Event) {
|
||||
if event.IsChmod() {
|
||||
return
|
||||
}
|
||||
if !gstr.HasPrefix(gfile.Basename(event.Path), r.getServiceKeyForFile(key)) {
|
||||
return
|
||||
}
|
||||
service, err := r.getServiceByFilePath(event.Path)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileWatcher.ch <- service
|
||||
})
|
||||
return fileWatcher, err
|
||||
}
|
||||
|
||||
func (r *Registry) getServices(ctx context.Context) (services []gsvc.Service, err error) {
|
||||
filePaths, err := gfile.ScanDirFile(r.path, "*", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, filePath := range filePaths {
|
||||
s, e := r.getServiceByFilePath(filePath)
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
// Check service TTL.
|
||||
var (
|
||||
updateAt = s.GetMetadata().Get(updateAtKey).GTime()
|
||||
nowTime = gtime.Now()
|
||||
subDuration = nowTime.Sub(updateAt)
|
||||
)
|
||||
if updateAt.IsZero() || subDuration > serviceTTL {
|
||||
g.Log().Debugf(
|
||||
ctx,
|
||||
`service "%s" is expired, update at: %s, current: %s, sub duration: %s`,
|
||||
s.GetKey(), updateAt.String(), nowTime.String(), subDuration.String(),
|
||||
)
|
||||
continue
|
||||
}
|
||||
services = append(services, s)
|
||||
}
|
||||
services = r.mergeServices(services)
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Registry) getServiceByFilePath(filePath string) (gsvc.Service, error) {
|
||||
var (
|
||||
fileName = gfile.Basename(filePath)
|
||||
fileContent = gfile.GetContents(filePath)
|
||||
serviceKey = gstr.Replace(fileName, defaultSeparator, gsvc.DefaultSeparator)
|
||||
)
|
||||
serviceKey = gsvc.DefaultSeparator + serviceKey
|
||||
return gsvc.NewServiceWithKV(serviceKey, fileContent)
|
||||
}
|
||||
|
||||
func (r *Registry) mergeServices(services []gsvc.Service) []gsvc.Service {
|
||||
if len(services) == 0 {
|
||||
return services
|
||||
}
|
||||
|
||||
var (
|
||||
servicePrefixMap = make(map[string]*Service)
|
||||
mergeServices = make([]gsvc.Service, 0)
|
||||
)
|
||||
for _, service := range services {
|
||||
if v, ok := servicePrefixMap[service.GetPrefix()]; ok {
|
||||
v.Endpoints = append(v.Endpoints, service.GetEndpoints()...)
|
||||
} else {
|
||||
s := NewService(service)
|
||||
servicePrefixMap[s.GetPrefix()] = s
|
||||
mergeServices = append(mergeServices, s)
|
||||
}
|
||||
}
|
||||
return mergeServices
|
||||
}
|
||||
62
contrib/registry/file/file_registrar.go
Normal file
62
contrib/registry/file/file_registrar.go
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/os/gtimer"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Register registers `service` to Registry.
|
||||
// Note that it returns a new Service if it changes the input Service with custom one.
|
||||
func (r *Registry) Register(ctx context.Context, service gsvc.Service) (registered gsvc.Service, err error) {
|
||||
service = NewService(service)
|
||||
service.GetMetadata().Set(updateAtKey, gtime.Now())
|
||||
var (
|
||||
filePath = r.getServiceFilePath(service)
|
||||
fileContent = service.GetValue()
|
||||
)
|
||||
err = gfile.PutContents(filePath, fileContent)
|
||||
if err == nil {
|
||||
gtimer.Add(ctx, serviceUpdateInterval, func(ctx context.Context) {
|
||||
if !gfile.Exists(filePath) {
|
||||
gtimer.Exit()
|
||||
}
|
||||
// Update TTL in timer.
|
||||
service, _ = r.getServiceByFilePath(filePath)
|
||||
if service != nil {
|
||||
service.GetMetadata().Set(updateAtKey, gtime.Now())
|
||||
}
|
||||
_ = gfile.PutContents(filePath, service.GetValue())
|
||||
})
|
||||
}
|
||||
return service, err
|
||||
}
|
||||
|
||||
// Deregister off-lines and removes `service` from the Registry.
|
||||
func (r *Registry) Deregister(ctx context.Context, service gsvc.Service) error {
|
||||
return gfile.Remove(r.getServiceFilePath(service))
|
||||
}
|
||||
|
||||
func (r *Registry) getServiceFilePath(service gsvc.Service) string {
|
||||
return gfile.Join(r.path, r.getServiceFileName(service))
|
||||
}
|
||||
|
||||
func (r *Registry) getServiceFileName(service gsvc.Service) string {
|
||||
return r.getServiceKeyForFile(service.GetKey())
|
||||
}
|
||||
|
||||
func (r *Registry) getServiceKeyForFile(key string) string {
|
||||
key = gstr.Replace(key, gsvc.DefaultSeparator, defaultSeparator)
|
||||
key = gstr.Trim(key, defaultSeparator)
|
||||
return key
|
||||
}
|
||||
64
contrib/registry/file/file_service.go
Normal file
64
contrib/registry/file/file_service.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Service wrapper.
|
||||
type Service struct {
|
||||
gsvc.Service
|
||||
Endpoints gsvc.Endpoints
|
||||
Metadata gsvc.Metadata
|
||||
}
|
||||
|
||||
// NewService creates and returns local Service from gsvc.Service interface object.
|
||||
func NewService(service gsvc.Service) *Service {
|
||||
s, ok := service.(*Service)
|
||||
if ok {
|
||||
if s.Endpoints == nil {
|
||||
s.Endpoints = make(gsvc.Endpoints, 0)
|
||||
}
|
||||
if s.Metadata == nil {
|
||||
s.Metadata = make(gsvc.Metadata)
|
||||
}
|
||||
return s
|
||||
}
|
||||
s = &Service{
|
||||
Service: service,
|
||||
Endpoints: make(gsvc.Endpoints, 0),
|
||||
Metadata: make(gsvc.Metadata),
|
||||
}
|
||||
if len(service.GetEndpoints()) > 0 {
|
||||
s.Endpoints = service.GetEndpoints()
|
||||
}
|
||||
if len(service.GetMetadata()) > 0 {
|
||||
s.Metadata = service.GetMetadata()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// GetMetadata returns the Metadata map of service.
|
||||
// The Metadata is key-value pair map specifying extra attributes of a service.
|
||||
func (s *Service) GetMetadata() gsvc.Metadata {
|
||||
return s.Metadata
|
||||
}
|
||||
|
||||
// GetEndpoints returns the Endpoints of service.
|
||||
// The Endpoints contain multiple host/port information of service.
|
||||
func (s *Service) GetEndpoints() gsvc.Endpoints {
|
||||
return s.Endpoints
|
||||
}
|
||||
|
||||
// GetValue formats and returns the value of the service.
|
||||
// The result value is commonly used for key-value registrar server.
|
||||
func (s *Service) GetValue() string {
|
||||
b, _ := gjson.Marshal(s.Metadata)
|
||||
return string(b)
|
||||
}
|
||||
35
contrib/registry/file/file_watcher.go
Normal file
35
contrib/registry/file/file_watcher.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package file
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Watcher for file changes watch.
|
||||
type Watcher struct {
|
||||
prefix string // Watched prefix key, not file name prefix.
|
||||
discovery gsvc.Discovery // Service discovery.
|
||||
ch chan gsvc.Service // Changes that caused by inotify.
|
||||
}
|
||||
|
||||
// Proceed proceeds watch in blocking way.
|
||||
// It returns all complete services that watched by `key` if any change.
|
||||
func (w *Watcher) Proceed() (services []gsvc.Service, err error) {
|
||||
<-w.ch
|
||||
return w.discovery.Search(context.Background(), gsvc.SearchInput{
|
||||
Prefix: w.prefix,
|
||||
})
|
||||
}
|
||||
|
||||
// Close closes the watcher.
|
||||
func (w *Watcher) Close() error {
|
||||
close(w.ch)
|
||||
return nil
|
||||
}
|
||||
150
contrib/registry/file/file_z_test.go
Normal file
150
contrib/registry/file/file_z_test.go
Normal file
@ -0,0 +1,150 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package file_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
func TestRegistry(t *testing.T) {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
path = gfile.Temp(guid.S())
|
||||
registry = file.New(path)
|
||||
)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
svc := &gsvc.LocalService{
|
||||
Name: guid.S(),
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:8888"),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
registered, err := registry.Register(ctx, svc)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc.GetName())
|
||||
})
|
||||
|
||||
// Search by name.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.Name,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
|
||||
// Search by prefix.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Prefix: svc.GetPrefix(),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
|
||||
// Search by metadata.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.GetName(),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0].GetName(), svc.Name)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := registry.Search(ctx, gsvc.SearchInput{
|
||||
Name: svc.GetName(),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "grpc",
|
||||
},
|
||||
})
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := registry.Deregister(ctx, svc)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
path = gfile.Temp(guid.S())
|
||||
registry = file.New(path)
|
||||
)
|
||||
defer gfile.Remove(path)
|
||||
|
||||
svc1 := &gsvc.LocalService{
|
||||
Name: guid.S(),
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:8888"),
|
||||
Metadata: map[string]interface{}{
|
||||
"protocol": "https",
|
||||
},
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
registered, err := registry.Register(ctx, svc1)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc1.GetName())
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
watcher, err := registry.Watch(ctx, svc1.GetPrefix())
|
||||
t.AssertNil(err)
|
||||
|
||||
// Register another service.
|
||||
svc2 := &gsvc.LocalService{
|
||||
Name: svc1.Name,
|
||||
Endpoints: gsvc.NewEndpoints("127.0.0.1:9999"),
|
||||
}
|
||||
registered, err := registry.Register(ctx, svc2)
|
||||
t.AssertNil(err)
|
||||
t.Assert(registered.GetName(), svc2.GetName())
|
||||
|
||||
// Watch and retrieve the service changes:
|
||||
// svc1 and svc2 is the same service name, which has 2 endpoints.
|
||||
proceedResult, err := watcher.Proceed()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(proceedResult), 1)
|
||||
t.Assert(
|
||||
proceedResult[0].GetEndpoints(),
|
||||
gsvc.Endpoints{svc1.GetEndpoints()[0], svc2.GetEndpoints()[0]},
|
||||
)
|
||||
|
||||
// Watch and retrieve the service changes:
|
||||
// left only svc1, which means this service has only 1 endpoint.
|
||||
err = registry.Deregister(ctx, svc2)
|
||||
t.AssertNil(err)
|
||||
proceedResult, err = watcher.Proceed()
|
||||
t.AssertNil(err)
|
||||
t.Assert(
|
||||
proceedResult[0].GetEndpoints(),
|
||||
gsvc.Endpoints{svc1.GetEndpoints()[0]},
|
||||
)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := registry.Deregister(ctx, svc1)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
7
contrib/registry/file/go.mod
Normal file
7
contrib/registry/file/go.mod
Normal file
@ -0,0 +1,7 @@
|
||||
module github.com/gogf/gf/contrib/registry/file/v2
|
||||
|
||||
go 1.15
|
||||
|
||||
require github.com/gogf/gf/v2 v2.0.0
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
83
contrib/registry/file/go.sum
Normal file
83
contrib/registry/file/go.sum
Normal file
@ -0,0 +1,83 @@
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
|
||||
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@ -133,7 +133,7 @@ func WithLogger(logger glog.ILogger) Option {
|
||||
}
|
||||
|
||||
// New create a new registry.
|
||||
func New(provider polaris.ProviderAPI, consumer polaris.ConsumerAPI, opts ...Option) (r *Registry) {
|
||||
func New(provider polaris.ProviderAPI, consumer polaris.ConsumerAPI, opts ...Option) gsvc.Registry {
|
||||
op := options{
|
||||
Namespace: "default",
|
||||
ServiceToken: "",
|
||||
@ -160,7 +160,7 @@ func New(provider polaris.ProviderAPI, consumer polaris.ConsumerAPI, opts ...Opt
|
||||
}
|
||||
|
||||
// NewWithConfig new a registry with config.
|
||||
func NewWithConfig(conf config.Configuration, opts ...Option) (r *Registry) {
|
||||
func NewWithConfig(conf config.Configuration, opts ...Option) gsvc.Registry {
|
||||
provider, err := polaris.NewProviderAPIByConfig(conf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
@ -11,10 +11,13 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/polarismesh/polaris-go"
|
||||
"github.com/polarismesh/polaris-go/pkg/model"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// Search returns the service instances in memory according to the service name.
|
||||
@ -38,7 +41,29 @@ func (r *Registry) Search(ctx context.Context, in gsvc.SearchInput) ([]gsvc.Serv
|
||||
return nil, err
|
||||
}
|
||||
serviceInstances := instancesToServiceInstances(instancesResponse.GetInstances())
|
||||
return serviceInstances, nil
|
||||
// Service filter.
|
||||
filteredServices := make([]gsvc.Service, 0)
|
||||
for _, service := range serviceInstances {
|
||||
if in.Prefix != "" && !gstr.HasPrefix(service.GetKey(), in.Prefix) {
|
||||
continue
|
||||
}
|
||||
if in.Name != "" && service.GetName() != in.Name {
|
||||
continue
|
||||
}
|
||||
if in.Version != "" && service.GetVersion() != in.Version {
|
||||
continue
|
||||
}
|
||||
if len(in.Metadata) != 0 {
|
||||
m1 := gmap.NewStrAnyMapFrom(in.Metadata)
|
||||
m2 := gmap.NewStrAnyMapFrom(service.GetMetadata())
|
||||
if !m1.IsSubOf(m2) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
resultItem := service
|
||||
filteredServices = append(filteredServices, resultItem)
|
||||
}
|
||||
return filteredServices, nil
|
||||
}
|
||||
|
||||
// Watch creates a watcher according to the service name.
|
||||
|
||||
@ -9,14 +9,16 @@ package zookeeper
|
||||
|
||||
import (
|
||||
"github.com/go-zookeeper/zk"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
var _ gsvc.Registry = &Registry{}
|
||||
|
||||
// Content for custom service Marshal/Unmarshal.
|
||||
type Content struct {
|
||||
Key string
|
||||
Value string
|
||||
|
||||
@ -8,13 +8,16 @@ package zookeeper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Search is the etcd discovery search function.
|
||||
// Search searches and returns services with specified condition.
|
||||
func (r *Registry) Search(_ context.Context, in gsvc.SearchInput) ([]gsvc.Service, error) {
|
||||
prefix := strings.TrimPrefix(strings.ReplaceAll(in.Prefix, "/", "-"), "-")
|
||||
instances, err, _ := r.group.Do(prefix, func() (interface{}, error) {
|
||||
@ -62,10 +65,33 @@ func (r *Registry) Search(_ context.Context, in gsvc.SearchInput) ([]gsvc.Servic
|
||||
"Error with group do",
|
||||
)
|
||||
}
|
||||
return instances.([]gsvc.Service), nil
|
||||
// Service filter.
|
||||
filteredServices := make([]gsvc.Service, 0)
|
||||
for _, service := range instances.([]gsvc.Service) {
|
||||
if in.Prefix != "" && !gstr.HasPrefix(service.GetKey(), in.Prefix) {
|
||||
continue
|
||||
}
|
||||
if in.Name != "" && service.GetName() != in.Name {
|
||||
continue
|
||||
}
|
||||
if in.Version != "" && service.GetVersion() != in.Version {
|
||||
continue
|
||||
}
|
||||
if len(in.Metadata) != 0 {
|
||||
m1 := gmap.NewStrAnyMapFrom(in.Metadata)
|
||||
m2 := gmap.NewStrAnyMapFrom(service.GetMetadata())
|
||||
if !m1.IsSubOf(m2) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
resultItem := service
|
||||
filteredServices = append(filteredServices, resultItem)
|
||||
}
|
||||
return filteredServices, nil
|
||||
}
|
||||
|
||||
// Watch is the etcd discovery watch function.
|
||||
// Watch watches specified condition changes.
|
||||
// The `key` is the prefix of service key.
|
||||
func (r *Registry) Watch(ctx context.Context, key string) (gsvc.Watcher, error) {
|
||||
return newWatcher(ctx, r.opts.namespace, key, r.conn)
|
||||
}
|
||||
|
||||
@ -10,15 +10,17 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/go-zookeeper/zk"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"golang.org/x/sync/singleflight"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
var _ gsvc.Watcher = (*watcher)(nil)
|
||||
|
||||
// ErrWatcherStopped is the certain error for watcher closed.
|
||||
var ErrWatcherStopped = errors.New("watcher stopped")
|
||||
|
||||
type watcher struct {
|
||||
@ -43,6 +45,8 @@ func newWatcher(ctx context.Context, nameSpace, prefix string, conn *zk.Conn) (*
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Proceed proceeds watch in blocking way.
|
||||
// It returns all complete services that watched by `key` if any change.
|
||||
func (w *watcher) Proceed() ([]gsvc.Service, error) {
|
||||
select {
|
||||
case <-w.ctx.Done():
|
||||
@ -111,6 +115,7 @@ func (w *watcher) getServicesByPrefix() ([]gsvc.Service, error) {
|
||||
return instances.([]gsvc.Service), nil
|
||||
}
|
||||
|
||||
// Close closes the watcher.
|
||||
func (w *watcher) Close() error {
|
||||
w.cancel()
|
||||
return nil
|
||||
|
||||
18
contrib/rpc/grpcx/go.mod
Normal file
18
contrib/rpc/grpcx/go.mod
Normal file
@ -0,0 +1,18 @@
|
||||
module github.com/gogf/gf/contrib/rpc/grpcx/v2
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/registry/file/v2 v2.0.0-20230223141509-94b2eae1bec0
|
||||
github.com/gogf/gf/v2 v2.0.0
|
||||
go.opentelemetry.io/otel v1.10.0
|
||||
go.opentelemetry.io/otel/trace v1.10.0
|
||||
golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 // indirect
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/registry/file/v2 => ../../registry/file/
|
||||
github.com/gogf/gf/v2 => ../../../
|
||||
)
|
||||
199
contrib/rpc/grpcx/go.sum
Normal file
199
contrib/rpc/grpcx/go.sum
Normal file
@ -0,0 +1,199 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
|
||||
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
|
||||
github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1 h1:0fThFwLbW7P/kOiTBs03FsJSV9RM2M/Q/MOnCQxKMo0=
|
||||
github.com/grokify/html-strip-tags-go v0.0.1/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4=
|
||||
go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E=
|
||||
go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 h1:TWZxd/th7FbRSMret2MVQdlI8uT49QEtwZdvJrxjEHU=
|
||||
golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2 h1:GLw7MR8AfAG2GmGcmVgObFOHXYypgGjnGno25RDwn3Y=
|
||||
golang.org/x/text v0.3.8-0.20211105212822-18b340fc7af2/go.mod h1:EFNZuWvGYxIRUEX+K8UmCFwYmZjqcrnq15ZuVldZkZ0=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
|
||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
40
contrib/rpc/grpcx/grpcx.go
Normal file
40
contrib/rpc/grpcx/grpcx.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package grpcx provides grpc service functionalities.
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/balancer"
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/grpcctx"
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/resolver"
|
||||
)
|
||||
|
||||
type (
|
||||
modCtx = grpcctx.Ctx
|
||||
modBalancer = balancer.Balancer
|
||||
modResolver = resolver.Manager
|
||||
modClient struct{}
|
||||
modServer struct{}
|
||||
)
|
||||
|
||||
const (
|
||||
// FreePortAddress marks the server listens using random free port.
|
||||
FreePortAddress = ":0"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultServerName = `default`
|
||||
configNodeNameGrpcServer = `grpc`
|
||||
)
|
||||
|
||||
var (
|
||||
Ctx = modCtx{} // Ctx is instance of module Context, which manages the context feature.
|
||||
Balancer = modBalancer{} // Balancer is instance of module Balancer, which manages the load balancer features.
|
||||
Resolver = modResolver{} // Resolver is instance of module Resolver, which manages the DNS resolving for client.
|
||||
Client = modClient{} // Client is instance of module Client, which manages the client features.
|
||||
Server = modServer{} // Server is instance of module Server, which manages the server feature.
|
||||
)
|
||||
77
contrib/rpc/grpcx/grpcx_grpc_client.go
Normal file
77
contrib/rpc/grpcx/grpcx_grpc_client.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// DefaultGrpcDialOptions returns the default options for creating grpc client connection.
|
||||
func (c modClient) DefaultGrpcDialOptions() []grpc.DialOption {
|
||||
return []grpc.DialOption{
|
||||
Balancer.WithName(gsel.GetBuilder().Name()),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
}
|
||||
}
|
||||
|
||||
// NewGrpcClientConn creates and returns a client connection for given service `appId`.
|
||||
func (c modClient) NewGrpcClientConn(serviceName string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||
autoLoadAndRegisterFileRegistry()
|
||||
|
||||
var (
|
||||
service = gsvc.NewServiceWithName(serviceName)
|
||||
grpcClientOptions = make([]grpc.DialOption, 0)
|
||||
)
|
||||
grpcClientOptions = append(grpcClientOptions, c.DefaultGrpcDialOptions()...)
|
||||
if len(opts) > 0 {
|
||||
grpcClientOptions = append(grpcClientOptions, opts...)
|
||||
}
|
||||
grpcClientOptions = append(grpcClientOptions, c.ChainUnary(
|
||||
c.UnaryTracing,
|
||||
c.UnaryError,
|
||||
))
|
||||
grpcClientOptions = append(grpcClientOptions, c.ChainStream(
|
||||
c.StreamTracing,
|
||||
))
|
||||
conn, err := grpc.Dial(fmt.Sprintf(`%s://%s`, gsvc.Schema, service.GetKey()), grpcClientOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// MustNewGrpcClientConn creates and returns a client connection for given service `appId`.
|
||||
// It panics if any error occurs.
|
||||
func (c modClient) MustNewGrpcClientConn(serviceName string, opts ...grpc.DialOption) *grpc.ClientConn {
|
||||
conn, err := c.NewGrpcClientConn(serviceName, opts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
// ChainUnary creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainUnaryClient(one, two, three) will execute one before two before three.
|
||||
func (c modClient) ChainUnary(interceptors ...grpc.UnaryClientInterceptor) grpc.DialOption {
|
||||
return grpc.WithChainUnaryInterceptor(interceptors...)
|
||||
}
|
||||
|
||||
// ChainStream creates a single interceptor out of a chain of many interceptors.
|
||||
//
|
||||
// Execution is done in left-to-right order, including passing of context.
|
||||
// For example ChainStreamClient(one, two, three) will execute one before two before three.
|
||||
func (c modClient) ChainStream(interceptors ...grpc.StreamClientInterceptor) grpc.DialOption {
|
||||
return grpc.WithChainStreamInterceptor(interceptors...)
|
||||
}
|
||||
264
contrib/rpc/grpcx/grpcx_grpc_server.go
Normal file
264
contrib/rpc/grpcx/grpcx_grpc_server.go
Normal file
@ -0,0 +1,264 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gipv4"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/net/gtcp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/os/gproc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// GrpcServer is the server for GRPC protocol.
|
||||
type GrpcServer struct {
|
||||
Server *grpc.Server
|
||||
config *GrpcServerConfig
|
||||
listener net.Listener
|
||||
services []gsvc.Service
|
||||
waitGroup sync.WaitGroup
|
||||
registrar gsvc.Registrar
|
||||
}
|
||||
|
||||
// Service implements gsvc.Service interface.
|
||||
type Service struct {
|
||||
gsvc.Service
|
||||
Endpoints gsvc.Endpoints
|
||||
}
|
||||
|
||||
// New creates and returns a grpc server.
|
||||
func (s modServer) New(conf ...*GrpcServerConfig) *GrpcServer {
|
||||
autoLoadAndRegisterFileRegistry()
|
||||
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
config *GrpcServerConfig
|
||||
)
|
||||
if len(conf) > 0 {
|
||||
config = conf[0]
|
||||
} else {
|
||||
config = s.NewConfig()
|
||||
}
|
||||
if config.Address == "" {
|
||||
randomPort, err := gtcp.GetFreePort()
|
||||
if err != nil {
|
||||
g.Log().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
config.Address = fmt.Sprintf(`:%d`, randomPort)
|
||||
}
|
||||
if !gstr.Contains(config.Address, ":") {
|
||||
g.Log().Fatal(ctx, "invalid service address, should contain listening port")
|
||||
}
|
||||
if config.Logger == nil {
|
||||
config.Logger = glog.New()
|
||||
}
|
||||
grpcServer := &GrpcServer{
|
||||
config: config,
|
||||
registrar: gsvc.GetRegistry(),
|
||||
}
|
||||
grpcServer.config.Options = append([]grpc.ServerOption{
|
||||
s.ChainUnary(
|
||||
s.UnaryRecover,
|
||||
s.UnaryTracing,
|
||||
s.UnaryError,
|
||||
grpcServer.UnaryLogger,
|
||||
),
|
||||
s.ChainStream(
|
||||
s.StreamTracing,
|
||||
),
|
||||
}, grpcServer.config.Options...)
|
||||
grpcServer.Server = grpc.NewServer(grpcServer.config.Options...)
|
||||
return grpcServer
|
||||
}
|
||||
|
||||
// Service binds service list to current server.
|
||||
// Server will automatically register the service list after it starts.
|
||||
func (s *GrpcServer) Service(services ...gsvc.Service) {
|
||||
s.services = append(s.services, services...)
|
||||
}
|
||||
|
||||
// Run starts the server in blocking way.
|
||||
func (s *GrpcServer) Run() {
|
||||
var (
|
||||
err error
|
||||
ctx = gctx.GetInitCtx()
|
||||
)
|
||||
// Create listener to bind listening ip and port.
|
||||
s.listener, err = net.Listen("tcp", s.config.Address)
|
||||
if err != nil {
|
||||
s.config.Logger.Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
|
||||
// Start listening.
|
||||
go func() {
|
||||
if err = s.Server.Serve(s.listener); err != nil {
|
||||
s.config.Logger.Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
}()
|
||||
|
||||
// Service register.
|
||||
s.doServiceRegister()
|
||||
s.config.Logger.Infof(
|
||||
ctx,
|
||||
"pid[%d]: grpc server started listening on [%s]",
|
||||
gproc.Pid(), s.GetListenedAddress(),
|
||||
)
|
||||
s.doSignalListen()
|
||||
}
|
||||
|
||||
// doSignalListen does signal listening and handling for gracefully shutdown.
|
||||
func (s *GrpcServer) doSignalListen() {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
sigChan = make(chan os.Signal, 1)
|
||||
)
|
||||
signal.Notify(
|
||||
sigChan,
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGABRT,
|
||||
)
|
||||
for {
|
||||
sig := <-sigChan
|
||||
switch sig {
|
||||
case
|
||||
syscall.SIGINT,
|
||||
syscall.SIGQUIT,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGABRT:
|
||||
s.config.Logger.Infof(ctx, "signal received: %s, gracefully shutting down", sig.String())
|
||||
s.doServiceDeregister()
|
||||
time.Sleep(time.Second)
|
||||
s.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// doServiceRegister registers current service to Registry.
|
||||
func (s *GrpcServer) doServiceRegister() {
|
||||
if s.registrar == nil {
|
||||
return
|
||||
}
|
||||
if len(s.services) == 0 {
|
||||
s.services = []gsvc.Service{&gsvc.LocalService{
|
||||
Name: s.config.Name,
|
||||
Metadata: gsvc.Metadata{},
|
||||
}}
|
||||
}
|
||||
var (
|
||||
err error
|
||||
ctx = gctx.GetInitCtx()
|
||||
protocol = `grpc`
|
||||
)
|
||||
// Register service list after server starts.
|
||||
for i, service := range s.services {
|
||||
service = &gsvc.LocalService{
|
||||
Name: service.GetName(),
|
||||
Endpoints: s.calculateListenedEndpoints(),
|
||||
Metadata: service.GetMetadata(),
|
||||
}
|
||||
service.GetMetadata().Sets(gsvc.Metadata{
|
||||
gsvc.MDProtocol: protocol,
|
||||
})
|
||||
s.config.Logger.Debugf(ctx, `service register: %+v`, service)
|
||||
if service, err = s.registrar.Register(ctx, service); err != nil {
|
||||
s.config.Logger.Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
s.services[i] = service
|
||||
}
|
||||
}
|
||||
|
||||
// doServiceDeregister de-registers current service from Registry.
|
||||
func (s *GrpcServer) doServiceDeregister() {
|
||||
if s.registrar == nil {
|
||||
return
|
||||
}
|
||||
var ctx = gctx.GetInitCtx()
|
||||
for _, service := range s.services {
|
||||
s.config.Logger.Debugf(ctx, `service deregister: %+v`, service)
|
||||
if err := s.registrar.Deregister(ctx, service); err != nil {
|
||||
s.config.Logger.Errorf(ctx, `%+v`, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start starts the server in no-blocking way.
|
||||
func (s *GrpcServer) Start() {
|
||||
s.waitGroup.Add(1)
|
||||
go func() {
|
||||
defer s.waitGroup.Done()
|
||||
s.Run()
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait works with Start, which blocks current goroutine until the server stops.
|
||||
func (s *GrpcServer) Wait() {
|
||||
s.waitGroup.Wait()
|
||||
}
|
||||
|
||||
// Stop gracefully stops the server.
|
||||
func (s *GrpcServer) Stop() {
|
||||
s.Server.GracefulStop()
|
||||
}
|
||||
|
||||
// GetListenedAddress retrieves and returns the address string which are listened by current server.
|
||||
func (s *GrpcServer) GetListenedAddress() string {
|
||||
if !gstr.Contains(s.config.Address, FreePortAddress) {
|
||||
return s.config.Address
|
||||
}
|
||||
var (
|
||||
address = s.config.Address
|
||||
listenedPort = s.GetListenedPort()
|
||||
)
|
||||
address = gstr.Replace(address, FreePortAddress, fmt.Sprintf(`:%d`, listenedPort))
|
||||
return address
|
||||
}
|
||||
|
||||
// GetListenedPort retrieves and returns one port which is listened to by current server.
|
||||
func (s *GrpcServer) GetListenedPort() int {
|
||||
if ln := s.listener; ln != nil {
|
||||
return ln.Addr().(*net.TCPAddr).Port
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (s *GrpcServer) calculateListenedEndpoints() gsvc.Endpoints {
|
||||
var (
|
||||
address = s.config.Address
|
||||
endpoints = make(gsvc.Endpoints, 0)
|
||||
listenedPort = s.GetListenedPort()
|
||||
listenedIps []string
|
||||
)
|
||||
var addrArray = gstr.Split(address, ":")
|
||||
switch addrArray[0] {
|
||||
case "0.0.0.0", "":
|
||||
listenedIps = []string{gipv4.MustGetIntranetIp()}
|
||||
default:
|
||||
listenedIps = []string{addrArray[0]}
|
||||
}
|
||||
for _, ip := range listenedIps {
|
||||
endpoints = append(endpoints, gsvc.NewEndpoint(fmt.Sprintf(`%s:%d`, ip, listenedPort)))
|
||||
}
|
||||
return endpoints
|
||||
}
|
||||
72
contrib/rpc/grpcx/grpcx_grpc_server_config.go
Normal file
72
contrib/rpc/grpcx/grpcx_grpc_server_config.go
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// GrpcServerConfig is the configuration for server.
|
||||
type GrpcServerConfig struct {
|
||||
Address string // (optional) Address for server listening.
|
||||
Name string // (optional) Name for current service.
|
||||
Logger *glog.Logger // (optional) Logger for server.
|
||||
LogPath string // (optional) LogPath specifies the directory for storing logging files.
|
||||
LogStdout bool // (optional) LogStdout specifies whether printing logging content to stdout.
|
||||
ErrorStack bool // (optional) ErrorStack specifies whether logging stack information when error.
|
||||
ErrorLogEnabled bool // (optional) ErrorLogEnabled enables error logging content to files.
|
||||
ErrorLogPattern string // (optional) ErrorLogPattern specifies the error log file pattern like: error-{Ymd}.log
|
||||
AccessLogEnabled bool // (optional) AccessLogEnabled enables access logging content to file.
|
||||
AccessLogPattern string // (optional) AccessLogPattern specifies the error log file pattern like: access-{Ymd}.log
|
||||
Options []grpc.ServerOption // (optional) GRPC Server options.
|
||||
}
|
||||
|
||||
// NewConfig creates and returns a ServerConfig object with default configurations.
|
||||
// Note that, do not define this default configuration to local package variable, as there are
|
||||
// some pointer attributes that may be shared in different servers.
|
||||
func (s modServer) NewConfig() *GrpcServerConfig {
|
||||
var (
|
||||
err error
|
||||
ctx = context.TODO()
|
||||
config = &GrpcServerConfig{
|
||||
Name: defaultServerName,
|
||||
Logger: glog.New(),
|
||||
LogStdout: true,
|
||||
ErrorLogEnabled: true,
|
||||
ErrorLogPattern: "error-{Ymd}.log",
|
||||
AccessLogEnabled: false,
|
||||
AccessLogPattern: "access-{Ymd}.log",
|
||||
}
|
||||
)
|
||||
// Reading configuration file and updating the configured keys.
|
||||
if g.Cfg().Available(ctx) {
|
||||
if err = g.Cfg().MustGet(ctx, configNodeNameGrpcServer).Struct(&config); err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
}
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// SetWithMap changes current configuration with map.
|
||||
// This is commonly used for changing several configurations of current object.
|
||||
func (c *GrpcServerConfig) SetWithMap(m g.Map) error {
|
||||
return gconv.Struct(m, c)
|
||||
}
|
||||
|
||||
// MustSetWithMap acts as SetWithMap but panics if error occurs.
|
||||
func (c *GrpcServerConfig) MustSetWithMap(m g.Map) {
|
||||
err := c.SetWithMap(m)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
89
contrib/rpc/grpcx/grpcx_grpc_server_unary.go
Normal file
89
contrib/rpc/grpcx/grpcx_grpc_server_unary.go
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// UnaryLogger is the default unary interceptor for logging purpose.
|
||||
func (s *GrpcServer) UnaryLogger(
|
||||
ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
var (
|
||||
start = time.Now()
|
||||
res, err = handler(ctx, req)
|
||||
duration = time.Since(start)
|
||||
)
|
||||
s.handleAccessLog(ctx, err, duration, info, req, res)
|
||||
s.handleErrorLog(ctx, err, duration, info, req, res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// handleAccessLog handles the access logging for server.
|
||||
func (s *GrpcServer) handleAccessLog(
|
||||
ctx context.Context, err error, duration time.Duration, info *grpc.UnaryServerInfo, req, res interface{},
|
||||
) {
|
||||
if !s.config.AccessLogEnabled {
|
||||
return
|
||||
}
|
||||
content := fmt.Sprintf(
|
||||
"%s, %.3fms, %+v, %+v",
|
||||
info.FullMethod, float64(duration)/1e6, req, res,
|
||||
)
|
||||
s.config.Logger.Stdout(s.config.LogStdout).File(s.config.AccessLogPattern).Print(ctx, content)
|
||||
}
|
||||
|
||||
// handleErrorLog handles the error logging for server.
|
||||
func (s *GrpcServer) handleErrorLog(
|
||||
ctx context.Context, err error, duration time.Duration, info *grpc.UnaryServerInfo, req, res interface{},
|
||||
) {
|
||||
// It does nothing if error logging is custom disabled.
|
||||
if !s.config.ErrorLogEnabled || err == nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
code = gerror.Code(err)
|
||||
codeDetail = code.Detail()
|
||||
codeDetailStr string
|
||||
grpcCode codes.Code
|
||||
grpcMessage string
|
||||
)
|
||||
if grpcStatus, ok := status.FromError(err); ok {
|
||||
grpcCode = grpcStatus.Code()
|
||||
grpcMessage = grpcStatus.Message()
|
||||
}
|
||||
if codeDetail != nil {
|
||||
codeDetailStr = gstr.Replace(fmt.Sprintf(`%+v`, codeDetail), "\n", " ")
|
||||
}
|
||||
content := fmt.Sprintf(
|
||||
`%s, %.3fms, %d, "%s", %+v, %+v, %d, "%s", "%s"`,
|
||||
info.FullMethod, float64(duration)/1e6, grpcCode, grpcMessage,
|
||||
req, res, code.Code(), code.Message(), codeDetailStr,
|
||||
)
|
||||
if s.config.ErrorStack {
|
||||
if stack := gerror.Stack(err); stack != "" {
|
||||
content += "\nStack:\n" + stack
|
||||
} else {
|
||||
content += ", " + err.Error()
|
||||
}
|
||||
} else {
|
||||
content += ", " + err.Error()
|
||||
}
|
||||
s.config.Logger.Stack(false).
|
||||
Stdout(s.config.LogStdout).
|
||||
File(s.config.ErrorLogPattern).Error(ctx, content)
|
||||
}
|
||||
50
contrib/rpc/grpcx/grpcx_interceptor_client.go
Normal file
50
contrib/rpc/grpcx/grpcx_interceptor_client.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/tracing"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// UnaryError handles the error types converting between grpc and gerror.
|
||||
// Note that, the minus error code is only used locally which will not be sent to other side.
|
||||
func (c modClient) UnaryError(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
err := invoker(ctx, method, req, reply, cc, opts...)
|
||||
if err != nil {
|
||||
grpcStatus, ok := status.FromError(err)
|
||||
if ok {
|
||||
if code := grpcStatus.Code(); code != 0 {
|
||||
return gerror.NewCode(gcode.New(int(code), "", nil), grpcStatus.Message())
|
||||
}
|
||||
return gerror.New(grpcStatus.Message())
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// UnaryTracing is a unary interceptor for adding tracing feature for gRPC client using OpenTelemetry.
|
||||
func (c modClient) UnaryTracing(
|
||||
ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
|
||||
return tracing.UnaryClientInterceptor(ctx, method, req, reply, cc, invoker, opts...)
|
||||
}
|
||||
|
||||
// StreamTracing is a stream interceptor for adding tracing feature for gRPC client using OpenTelemetry.
|
||||
func (c modClient) StreamTracing(
|
||||
ctx context.Context, desc *grpc.StreamDesc,
|
||||
cc *grpc.ClientConn, method string, streamer grpc.Streamer,
|
||||
callOpts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
return tracing.StreamClientInterceptor(ctx, desc, cc, method, streamer, callOpts...)
|
||||
}
|
||||
91
contrib/rpc/grpcx/grpcx_interceptor_server.go
Normal file
91
contrib/rpc/grpcx/grpcx_interceptor_server.go
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/tracing"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gutil"
|
||||
)
|
||||
|
||||
// ChainUnary returns a ServerOption that specifies the chained interceptor
|
||||
// for unary RPCs. The first interceptor will be the outermost,
|
||||
// while the last interceptor will be the innermost wrapper around the real call.
|
||||
// All unary interceptors added by this method will be chained.
|
||||
func (s modServer) ChainUnary(interceptors ...grpc.UnaryServerInterceptor) grpc.ServerOption {
|
||||
return grpc.ChainUnaryInterceptor(interceptors...)
|
||||
}
|
||||
|
||||
// ChainStream returns a ServerOption that specifies the chained interceptor
|
||||
// for stream RPCs. The first interceptor will be the outermost,
|
||||
// while the last interceptor will be the innermost wrapper around the real call.
|
||||
// All stream interceptors added by this method will be chained.
|
||||
func (s modServer) ChainStream(interceptors ...grpc.StreamServerInterceptor) grpc.ServerOption {
|
||||
return grpc.ChainStreamInterceptor(interceptors...)
|
||||
}
|
||||
|
||||
// UnaryError is the default unary interceptor for error converting from custom error to grpc error.
|
||||
func (s modServer) UnaryError(
|
||||
ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
res, err := handler(ctx, req)
|
||||
if err != nil {
|
||||
code := gerror.Code(err)
|
||||
if code.Code() != -1 {
|
||||
err = status.Error(codes.Code(code.Code()), err.Error())
|
||||
}
|
||||
}
|
||||
return res, err
|
||||
}
|
||||
|
||||
// UnaryRecover is the first interceptor that keep server not down from panics.
|
||||
func (s modServer) UnaryRecover(
|
||||
ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
|
||||
) (res interface{}, err error) {
|
||||
gutil.TryCatch(ctx, func(ctx2 context.Context) {
|
||||
res, err = handler(ctx, req)
|
||||
}, func(ctx context.Context, exception error) {
|
||||
err = gerror.WrapCode(gcode.New(int(codes.Internal), "", nil), err, "panic recovered")
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// UnaryValidate Common validation unary interpreter.
|
||||
func (s modServer) UnaryValidate(
|
||||
ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
// It does nothing if there's no validation tag in the struct definition.
|
||||
if err := g.Validator().Data(req).Run(ctx); err != nil {
|
||||
return nil, gerror.NewCode(
|
||||
gcode.New(int(codes.InvalidArgument), "", nil),
|
||||
gerror.Current(err).Error(),
|
||||
)
|
||||
}
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
// UnaryTracing is a unary interceptor for adding tracing feature for gRPC server using OpenTelemetry.
|
||||
func (s modServer) UnaryTracing(
|
||||
ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
|
||||
) (interface{}, error) {
|
||||
return tracing.UnaryServerInterceptor(ctx, req, info, handler)
|
||||
}
|
||||
|
||||
// StreamTracing is a stream unary interceptor for adding tracing feature for gRPC server using OpenTelemetry.
|
||||
func (s modServer) StreamTracing(
|
||||
srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler,
|
||||
) error {
|
||||
return tracing.StreamServerInterceptor(srv, ss, info, handler)
|
||||
}
|
||||
34
contrib/rpc/grpcx/grpcx_registry_file.go
Normal file
34
contrib/rpc/grpcx/grpcx_registry_file.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/resolver"
|
||||
)
|
||||
|
||||
// autoLoadAndRegisterFileRegistry checks and registers ETCD service as default service registry
|
||||
// if no registry is registered previously.
|
||||
func autoLoadAndRegisterFileRegistry() {
|
||||
// It ignores etcd registry if any registry already registered.
|
||||
if gsvc.GetRegistry() != nil {
|
||||
return
|
||||
}
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
fileRegistry = file.New(gfile.Temp("gsvc"))
|
||||
)
|
||||
|
||||
g.Log().Debug(ctx, `set default registry using file registry as no custom registry set`)
|
||||
resolver.SetRegistry(fileRegistry)
|
||||
}
|
||||
63
contrib/rpc/grpcx/grpcx_unit_ctx_test.go
Normal file
63
contrib/rpc/grpcx/grpcx_unit_ctx_test.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package grpcx_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Ctx_Basic(t *testing.T) {
|
||||
ctx := metadata.NewIncomingContext(context.Background(), metadata.Pairs(
|
||||
"k1", "v1",
|
||||
"k2", "v2",
|
||||
))
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m1 := grpcx.Ctx.IncomingMap(ctx)
|
||||
t.Assert(m1.Get("k1"), "v1")
|
||||
t.Assert(m1.Get("k2"), "v2")
|
||||
m2 := grpcx.Ctx.OutgoingMap(ctx)
|
||||
t.Assert(m2.Size(), 0)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := grpcx.Ctx.IncomingToOutgoing(ctx)
|
||||
m1 := grpcx.Ctx.IncomingMap(ctx)
|
||||
t.Assert(m1.Get("k1"), "v1")
|
||||
t.Assert(m1.Get("k2"), "v2")
|
||||
m2 := grpcx.Ctx.OutgoingMap(ctx)
|
||||
t.Assert(m2.Get("k1"), "v1")
|
||||
t.Assert(m2.Get("k2"), "v2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := grpcx.Ctx.IncomingToOutgoing(ctx, "k1")
|
||||
m1 := grpcx.Ctx.IncomingMap(ctx)
|
||||
t.Assert(m1.Get("k1"), "v1")
|
||||
t.Assert(m1.Get("k2"), "v2")
|
||||
m2 := grpcx.Ctx.OutgoingMap(ctx)
|
||||
t.Assert(m2.Get("k1"), "v1")
|
||||
t.Assert(m2.Get("k2"), "")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ctx := grpcx.Ctx.NewIncoming(ctx)
|
||||
ctx = grpcx.Ctx.SetIncoming(ctx, g.Map{"k1": "v1"})
|
||||
ctx = grpcx.Ctx.SetIncoming(ctx, g.Map{"k2": "v2"})
|
||||
ctx = grpcx.Ctx.SetOutgoing(ctx, g.Map{"k3": "v3"})
|
||||
ctx = grpcx.Ctx.SetOutgoing(ctx, g.Map{"k4": "v4"})
|
||||
m1 := grpcx.Ctx.IncomingMap(ctx)
|
||||
t.Assert(m1.Get("k1"), "v1")
|
||||
t.Assert(m1.Get("k2"), "v2")
|
||||
m2 := grpcx.Ctx.OutgoingMap(ctx)
|
||||
t.Assert(m2.Get("k3"), "v3")
|
||||
t.Assert(m2.Get("k4"), "v4")
|
||||
})
|
||||
}
|
||||
77
contrib/rpc/grpcx/internal/balancer/balancer.go
Normal file
77
contrib/rpc/grpcx/internal/balancer/balancer.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package balancer defines APIs for load balancing in gRPC.
|
||||
package balancer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/balancer/base"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
)
|
||||
|
||||
type Balancer struct{}
|
||||
|
||||
const (
|
||||
rawSvcKeyInSubConnInfo = `RawService`
|
||||
)
|
||||
|
||||
var (
|
||||
Random = gsel.NewBuilderRandom()
|
||||
Weight = gsel.NewBuilderWeight()
|
||||
RoundRobin = gsel.NewBuilderRoundRobin()
|
||||
LeastConnection = gsel.NewBuilderLeastConnection()
|
||||
)
|
||||
|
||||
func init() {
|
||||
b := Balancer{}
|
||||
b.Register(Random, Weight, RoundRobin, LeastConnection)
|
||||
}
|
||||
|
||||
// Register registers the given balancer builder with the given name.
|
||||
func (Balancer) Register(builders ...gsel.Builder) {
|
||||
for _, builder := range builders {
|
||||
balancer.Register(
|
||||
base.NewBalancerBuilder(
|
||||
builder.Name(),
|
||||
&Builder{builder: builder},
|
||||
base.Config{HealthCheck: true},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// WithRandom returns a grpc.DialOption which enables random load balancing.
|
||||
func (b Balancer) WithRandom() grpc.DialOption {
|
||||
return b.WithName(Random.Name())
|
||||
}
|
||||
|
||||
// WithWeight returns a grpc.DialOption which enables weight load balancing.
|
||||
func (b Balancer) WithWeight() grpc.DialOption {
|
||||
return b.WithName(Weight.Name())
|
||||
}
|
||||
|
||||
// WithRoundRobin returns a grpc.DialOption which enables round-robin load balancing.
|
||||
func (b Balancer) WithRoundRobin() grpc.DialOption {
|
||||
return b.WithName(RoundRobin.Name())
|
||||
}
|
||||
|
||||
// WithLeastConnection returns a grpc.DialOption which enables the least connection load balancing.
|
||||
func (b Balancer) WithLeastConnection() grpc.DialOption {
|
||||
return b.WithName(LeastConnection.Name())
|
||||
}
|
||||
|
||||
// WithName returns a grpc.DialOption which enables the load balancing by name.
|
||||
func (b Balancer) WithName(name string) grpc.DialOption {
|
||||
return grpc.WithDefaultServiceConfig(fmt.Sprintf(
|
||||
`{"loadBalancingPolicy": "%s"}`,
|
||||
name,
|
||||
))
|
||||
}
|
||||
52
contrib/rpc/grpcx/internal/balancer/balancer_builder.go
Normal file
52
contrib/rpc/grpcx/internal/balancer/balancer_builder.go
Normal file
@ -0,0 +1,52 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package balancer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"google.golang.org/grpc/balancer"
|
||||
"google.golang.org/grpc/balancer/base"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Builder implements grpc balancer base.PickerBuilder,
|
||||
// which returns a picker that will be used by gRPC to pick a SubConn.
|
||||
type Builder struct {
|
||||
builder gsel.Builder
|
||||
}
|
||||
|
||||
// Build returns a picker that will be used by gRPC to pick a SubConn.
|
||||
func (b *Builder) Build(info base.PickerBuildInfo) balancer.Picker {
|
||||
if len(info.ReadySCs) == 0 {
|
||||
return base.NewErrPicker(balancer.ErrNoSubConnAvailable)
|
||||
}
|
||||
var (
|
||||
ctx = context.Background()
|
||||
nodes = make([]gsel.Node, 0)
|
||||
)
|
||||
for conn, subConnInfo := range info.ReadySCs {
|
||||
svc, _ := subConnInfo.Address.Attributes.Value(rawSvcKeyInSubConnInfo).(gsvc.Service)
|
||||
if svc == nil {
|
||||
g.Log().Noticef(ctx, `empty service read from: %+v`, subConnInfo.Address)
|
||||
continue
|
||||
}
|
||||
nodes = append(nodes, &Node{
|
||||
service: svc,
|
||||
conn: conn,
|
||||
})
|
||||
}
|
||||
p := &Picker{
|
||||
selector: b.builder.Build(),
|
||||
}
|
||||
if err := p.selector.Update(ctx, nodes); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
33
contrib/rpc/grpcx/internal/balancer/balancer_node.go
Normal file
33
contrib/rpc/grpcx/internal/balancer/balancer_node.go
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package balancer
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/balancer"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Node is the node for the balancer.
|
||||
type Node struct {
|
||||
service gsvc.Service
|
||||
conn balancer.SubConn
|
||||
}
|
||||
|
||||
// Service returns the service of the node.
|
||||
func (n *Node) Service() gsvc.Service {
|
||||
return n.service
|
||||
}
|
||||
|
||||
// Address returns the address of the node.
|
||||
func (n *Node) Address() string {
|
||||
endpoints := n.service.GetEndpoints()
|
||||
if len(endpoints) == 0 {
|
||||
return ""
|
||||
}
|
||||
return endpoints[0].String()
|
||||
}
|
||||
64
contrib/rpc/grpcx/internal/balancer/balancer_picker.go
Normal file
64
contrib/rpc/grpcx/internal/balancer/balancer_picker.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package balancer
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/balancer"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
)
|
||||
|
||||
// Picker implements grpc balancer.Picker,
|
||||
// which is used by gRPC to pick a SubConn to send an RPC.
|
||||
// Balancer is expected to generate a new picker from its snapshot every time its
|
||||
// internal state has changed.
|
||||
//
|
||||
// The pickers used by gRPC can be updated by ClientConn.UpdateState().
|
||||
type Picker struct {
|
||||
selector gsel.Selector
|
||||
}
|
||||
|
||||
// Pick returns the connection to use for this RPC and related information.
|
||||
//
|
||||
// Pick should not block. If the balancer needs to do I/O or any blocking
|
||||
// or time-consuming work to service this call, it should return
|
||||
// ErrNoSubConnAvailable, and the Pick call will be repeated by gRPC when
|
||||
// the Picker is updated (using ClientConn.UpdateState).
|
||||
//
|
||||
// If an error is returned:
|
||||
//
|
||||
// - If the error is ErrNoSubConnAvailable, gRPC will block until a new
|
||||
// Picker is provided by the balancer (using ClientConn.UpdateState).
|
||||
//
|
||||
// - If the error is a status error (implemented by the grpc/status
|
||||
// package), gRPC will terminate the RPC with the code and message
|
||||
// provided.
|
||||
//
|
||||
// - For all other errors, wait for ready RPCs will wait, but non-wait for
|
||||
// ready RPCs will be terminated with this error's Error() string and
|
||||
// status code Unavailable.
|
||||
func (p *Picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) {
|
||||
node, done, err := p.selector.Pick(info.Ctx)
|
||||
if err != nil {
|
||||
return balancer.PickResult{}, err
|
||||
}
|
||||
return balancer.PickResult{
|
||||
SubConn: node.(*Node).conn,
|
||||
Done: func(di balancer.DoneInfo) {
|
||||
if done == nil {
|
||||
return
|
||||
}
|
||||
done(info.Ctx, gsel.DoneInfo{
|
||||
Err: di.Err,
|
||||
Trailer: di.Trailer,
|
||||
BytesSent: di.BytesSent,
|
||||
BytesReceived: di.BytesReceived,
|
||||
ServerLoad: di.ServerLoad,
|
||||
})
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
117
contrib/rpc/grpcx/internal/grpcctx/grpcctx.go
Normal file
117
contrib/rpc/grpcx/internal/grpcctx/grpcctx.go
Normal file
@ -0,0 +1,117 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package grpcctx provides context feature for GRPC.
|
||||
package grpcctx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
type (
|
||||
Ctx struct{}
|
||||
)
|
||||
|
||||
func (c Ctx) NewIncoming(ctx context.Context, data ...g.Map) context.Context {
|
||||
if len(data) > 0 {
|
||||
incomingMd := make(metadata.MD)
|
||||
for key, value := range data[0] {
|
||||
incomingMd.Set(key, gconv.String(value))
|
||||
}
|
||||
return metadata.NewIncomingContext(ctx, incomingMd)
|
||||
}
|
||||
return metadata.NewIncomingContext(ctx, nil)
|
||||
}
|
||||
|
||||
func (c Ctx) NewOutgoing(ctx context.Context, data ...g.Map) context.Context {
|
||||
if len(data) > 0 {
|
||||
outgoingMd := make(metadata.MD)
|
||||
for key, value := range data[0] {
|
||||
outgoingMd.Set(key, gconv.String(value))
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, outgoingMd)
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, nil)
|
||||
}
|
||||
|
||||
func (c Ctx) IncomingToOutgoing(ctx context.Context, keys ...string) context.Context {
|
||||
incomingMd, _ := metadata.FromIncomingContext(ctx)
|
||||
if incomingMd == nil {
|
||||
return ctx
|
||||
}
|
||||
outgoingMd, _ := metadata.FromOutgoingContext(ctx)
|
||||
if outgoingMd == nil {
|
||||
outgoingMd = make(metadata.MD)
|
||||
}
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
outgoingMd[key] = append(outgoingMd[key], incomingMd.Get(key)...)
|
||||
}
|
||||
} else {
|
||||
for key, values := range incomingMd {
|
||||
outgoingMd[key] = append(outgoingMd[key], values...)
|
||||
}
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, outgoingMd)
|
||||
}
|
||||
|
||||
func (c Ctx) IncomingMap(ctx context.Context) *gmap.Map {
|
||||
var (
|
||||
data = gmap.New()
|
||||
incomingMd, _ = metadata.FromIncomingContext(ctx)
|
||||
)
|
||||
for key, values := range incomingMd {
|
||||
if len(values) == 1 {
|
||||
data.Set(key, values[0])
|
||||
} else {
|
||||
data.Set(key, values)
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c Ctx) OutgoingMap(ctx context.Context) *gmap.Map {
|
||||
var (
|
||||
data = gmap.New()
|
||||
outgoingMd, _ = metadata.FromOutgoingContext(ctx)
|
||||
)
|
||||
for key, values := range outgoingMd {
|
||||
if len(values) == 1 {
|
||||
data.Set(key, values[0])
|
||||
} else {
|
||||
data.Set(key, values)
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c Ctx) SetIncoming(ctx context.Context, data g.Map) context.Context {
|
||||
incomingMd, _ := metadata.FromIncomingContext(ctx)
|
||||
if incomingMd == nil {
|
||||
incomingMd = make(metadata.MD)
|
||||
}
|
||||
for key, value := range data {
|
||||
incomingMd.Set(key, gconv.String(value))
|
||||
}
|
||||
return metadata.NewIncomingContext(ctx, incomingMd)
|
||||
}
|
||||
|
||||
func (c Ctx) SetOutgoing(ctx context.Context, data g.Map) context.Context {
|
||||
outgoingMd, _ := metadata.FromOutgoingContext(ctx)
|
||||
if outgoingMd == nil {
|
||||
outgoingMd = make(metadata.MD)
|
||||
}
|
||||
for key, value := range data {
|
||||
outgoingMd.Set(key, gconv.String(value))
|
||||
}
|
||||
return metadata.NewOutgoingContext(ctx, outgoingMd)
|
||||
}
|
||||
35
contrib/rpc/grpcx/internal/resolver/resolver.go
Normal file
35
contrib/rpc/grpcx/internal/resolver/resolver.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package resolver defines APIs for name resolution in gRPC.
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
const (
|
||||
rawSvcKeyInSubConnInfo = `RawService`
|
||||
)
|
||||
|
||||
func init() {
|
||||
// It registers default resolver here.
|
||||
// It uses default builder handling the name resolving for grpc service requests.
|
||||
// Use `grpc.WithResolver` to custom resolver for client.
|
||||
resolver.Register(NewBuilder(gsvc.GetRegistry()))
|
||||
}
|
||||
|
||||
// SetRegistry sets the default Registry implements as your own implemented interface.
|
||||
func SetRegistry(registry gsvc.Registry) {
|
||||
if registry == nil {
|
||||
panic(gerror.New(`invalid Registry value "nil" given`))
|
||||
}
|
||||
gsvc.SetRegistry(registry)
|
||||
resolver.Register(NewBuilder(registry))
|
||||
}
|
||||
63
contrib/rpc/grpcx/internal/resolver/resolver_builder.go
Normal file
63
contrib/rpc/grpcx/internal/resolver/resolver_builder.go
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
// Builder is the builder for the etcd discovery resolver.
|
||||
type Builder struct {
|
||||
discovery gsvc.Discovery
|
||||
}
|
||||
|
||||
// NewBuilder creates and returns a Builder.
|
||||
func NewBuilder(discovery gsvc.Discovery) *Builder {
|
||||
return &Builder{
|
||||
discovery: discovery,
|
||||
}
|
||||
}
|
||||
|
||||
// Build creates a new etcd discovery resolver.
|
||||
func (b *Builder) Build(
|
||||
target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions,
|
||||
) (resolver.Resolver, error) {
|
||||
var (
|
||||
err error
|
||||
watcher gsvc.Watcher
|
||||
watchKey = target.URL.Path
|
||||
ctx, cancel = context.WithCancel(gctx.GetInitCtx())
|
||||
)
|
||||
g.Log().Debugf(ctx, `Watch key "%s"`, watchKey)
|
||||
if watcher, err = b.discovery.Watch(ctx, watchKey); err != nil {
|
||||
cancel()
|
||||
return nil, gerror.Wrap(err, `registry.Watch failed`)
|
||||
}
|
||||
r := &Resolver{
|
||||
discovery: b.discovery,
|
||||
watcher: watcher,
|
||||
watchKey: watchKey,
|
||||
cc: cc,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
logger: g.Log(),
|
||||
}
|
||||
go r.watch()
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Scheme return scheme of discovery
|
||||
func (*Builder) Scheme() string {
|
||||
return gsvc.Schema
|
||||
}
|
||||
20
contrib/rpc/grpcx/internal/resolver/resolver_manager.go
Normal file
20
contrib/rpc/grpcx/internal/resolver/resolver_manager.go
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
)
|
||||
|
||||
// Manager for Builder creating.
|
||||
type Manager struct{}
|
||||
|
||||
func (m Manager) New(discovery gsvc.Discovery) resolver.Builder {
|
||||
return NewBuilder(discovery)
|
||||
}
|
||||
123
contrib/rpc/grpcx/internal/resolver/resolver_resolver.go
Normal file
123
contrib/rpc/grpcx/internal/resolver/resolver_resolver.go
Normal file
@ -0,0 +1,123 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package resolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/attributes"
|
||||
"google.golang.org/grpc/resolver"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/glog"
|
||||
)
|
||||
|
||||
// Resolver implements grpc resolver.Resolver,
|
||||
// which watches for the updates on the specified target.
|
||||
// Updates include address updates and service config updates.
|
||||
type Resolver struct {
|
||||
discovery gsvc.Discovery // Service discovery.
|
||||
watcher gsvc.Watcher // Service watcher
|
||||
watchKey string // Watched key.
|
||||
cc resolver.ClientConn // GRPC client conn.
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
logger *glog.Logger
|
||||
}
|
||||
|
||||
func (r *Resolver) watch() {
|
||||
var (
|
||||
err error
|
||||
services []gsvc.Service
|
||||
)
|
||||
// It updates the resolver state in time.
|
||||
services, err = r.discovery.Search(r.ctx, gsvc.SearchInput{
|
||||
Prefix: r.watchKey,
|
||||
})
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
r.logger.Warningf(r.ctx, `discovery.Search error: %+v`, err)
|
||||
}
|
||||
if len(services) > 0 {
|
||||
r.update(services)
|
||||
}
|
||||
// Then watch.
|
||||
for {
|
||||
select {
|
||||
case <-r.ctx.Done():
|
||||
return
|
||||
|
||||
default:
|
||||
services, err = r.watcher.Proceed()
|
||||
if err != nil && !errors.Is(err, context.Canceled) {
|
||||
r.logger.Warningf(r.ctx, `watcher.Proceed error: %+v`, err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
if len(services) > 0 {
|
||||
r.update(services)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) update(services []gsvc.Service) {
|
||||
var (
|
||||
err error
|
||||
addresses = make([]resolver.Address, 0)
|
||||
)
|
||||
for _, service := range services {
|
||||
for _, endpoint := range service.GetEndpoints() {
|
||||
addr := resolver.Address{
|
||||
Addr: endpoint.String(),
|
||||
ServerName: service.GetName(),
|
||||
Attributes: newAttributesFromMetadata(service.GetMetadata()),
|
||||
}
|
||||
addr.Attributes = addr.Attributes.WithValue(rawSvcKeyInSubConnInfo, service)
|
||||
addresses = append(addresses, addr)
|
||||
}
|
||||
}
|
||||
if len(addresses) == 0 {
|
||||
r.logger.Noticef(r.ctx, "empty addresses parsed from: %+v", services)
|
||||
return
|
||||
}
|
||||
r.logger.Debugf(r.ctx, "client conn updated with addresses %s", gjson.MustEncodeString(addresses))
|
||||
if err = r.cc.UpdateState(resolver.State{Addresses: addresses}); err != nil {
|
||||
r.logger.Errorf(r.ctx, "UpdateState failed: %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the resolver.
|
||||
func (r *Resolver) Close() {
|
||||
r.logger.Debugf(r.ctx, `resolver closed`)
|
||||
if err := r.watcher.Close(); err != nil {
|
||||
r.logger.Errorf(r.ctx, `%+v`, err)
|
||||
}
|
||||
r.cancel()
|
||||
}
|
||||
|
||||
// ResolveNow will be called by gRPC to try to resolve the target name
|
||||
// again. It's just a hint, resolver can ignore this if it's not necessary.
|
||||
//
|
||||
// It could be called multiple times concurrently.
|
||||
func (r *Resolver) ResolveNow(options resolver.ResolveNowOptions) {
|
||||
|
||||
}
|
||||
|
||||
func newAttributesFromMetadata(metadata map[string]interface{}) *attributes.Attributes {
|
||||
var a *attributes.Attributes
|
||||
for k, v := range metadata {
|
||||
if a == nil {
|
||||
a = attributes.New(k, v)
|
||||
} else {
|
||||
a = a.WithValue(k, v)
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
85
contrib/rpc/grpcx/internal/tracing/tracing.go
Normal file
85
contrib/rpc/grpcx/internal/tracing/tracing.go
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package tracing provide tracing feature for GRPC.
|
||||
//
|
||||
// Refer to: opentelemetry-go-contrib/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
const (
|
||||
// GRPCStatusCodeKey is convention for numeric status code of a gRPC request.
|
||||
GRPCStatusCodeKey = attribute.Key("rpc.grpc.status_code")
|
||||
)
|
||||
|
||||
const (
|
||||
tracingMaxContentLogSize = 256 * 1024 // Max log size for request and response body.
|
||||
tracingInstrumentGrpcClient = "github.com/gogf/gf/contrib/rpc/grpcx/v2/krpc.GrpcClient"
|
||||
tracingInstrumentGrpcServer = "github.com/gogf/gf/contrib/rpc/grpcx/v2/krpc.GrpcServer"
|
||||
tracingEventGrpcRequest = "grpc.request"
|
||||
tracingEventGrpcRequestMessage = "grpc.request.message"
|
||||
tracingEventGrpcRequestBaggage = "grpc.request.baggage"
|
||||
tracingEventGrpcMetadataOutgoing = "grpc.metadata.outgoing"
|
||||
tracingEventGrpcMetadataIncoming = "grpc.metadata.incoming"
|
||||
tracingEventGrpcResponse = "grpc.response"
|
||||
tracingEventGrpcResponseMessage = "grpc.response.message"
|
||||
)
|
||||
|
||||
type metadataSupplier struct {
|
||||
metadata metadata.MD
|
||||
}
|
||||
|
||||
func (s *metadataSupplier) Get(key string) string {
|
||||
values := s.metadata.Get(key)
|
||||
if len(values) == 0 {
|
||||
return ""
|
||||
}
|
||||
return values[0]
|
||||
}
|
||||
|
||||
func (s *metadataSupplier) Set(key string, value string) {
|
||||
s.metadata.Set(key, value)
|
||||
}
|
||||
|
||||
func (s *metadataSupplier) Keys() []string {
|
||||
var (
|
||||
index = 0
|
||||
keys = make([]string, s.metadata.Len())
|
||||
)
|
||||
for k := range s.metadata {
|
||||
keys[index] = k
|
||||
index++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Inject injects correlation context and span context into the gRPC
|
||||
// metadata object. This function is meant to be used on outgoing
|
||||
// requests.
|
||||
func Inject(ctx context.Context, metadata metadata.MD) {
|
||||
otel.GetTextMapPropagator().Inject(ctx, &metadataSupplier{
|
||||
metadata: metadata,
|
||||
})
|
||||
}
|
||||
|
||||
// Extract returns the correlation context and span context that
|
||||
// another service encoded in the gRPC metadata object with Inject.
|
||||
// This function is meant to be used on incoming requests.
|
||||
func Extract(ctx context.Context, metadata metadata.MD) (baggage.Baggage, trace.SpanContext) {
|
||||
ctx = otel.GetTextMapPropagator().Extract(ctx, &metadataSupplier{
|
||||
metadata: metadata,
|
||||
})
|
||||
return baggage.FromContext(ctx), trace.SpanContextFromContext(ctx)
|
||||
}
|
||||
287
contrib/rpc/grpcx/internal/tracing/tracing_interceptor.go
Normal file
287
contrib/rpc/grpcx/internal/tracing/tracing_interceptor.go
Normal file
@ -0,0 +1,287 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/master/instrumentation/google.golang.org/grpc/otelgrpc/interceptor.go
|
||||
|
||||
package tracing
|
||||
|
||||
// gRPC tracing middleware
|
||||
// https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/semantic_conventions/rpc.md
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
semconv "go.opentelemetry.io/otel/semconv/v1.7.0"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
grpcCodes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/peer"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type messageType attribute.KeyValue
|
||||
|
||||
// Event adds an event of the messageType to the span associated with the
|
||||
// passed context with id and size (if message is a proto message).
|
||||
func (m messageType) Event(ctx context.Context, id int, message interface{}) {
|
||||
span := trace.SpanFromContext(ctx)
|
||||
if p, ok := message.(proto.Message); ok {
|
||||
span.AddEvent("message", trace.WithAttributes(
|
||||
attribute.KeyValue(m),
|
||||
attribute.Key("message.id").Int(id),
|
||||
attribute.Key("message.uncompressed_size").Int(proto.Size(p)),
|
||||
))
|
||||
} else {
|
||||
span.AddEvent("message", trace.WithAttributes(
|
||||
attribute.KeyValue(m),
|
||||
attribute.Key("message.id").Int(id),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
messageSent = messageType(attribute.Key("message.type").String("SENT"))
|
||||
messageReceived = messageType(attribute.Key("message.type").String("RECEIVED"))
|
||||
)
|
||||
|
||||
type streamEventType int
|
||||
|
||||
type streamEvent struct {
|
||||
Type streamEventType
|
||||
Err error
|
||||
}
|
||||
|
||||
const (
|
||||
closeEvent streamEventType = iota
|
||||
receiveEndEvent
|
||||
errorEvent
|
||||
)
|
||||
|
||||
// clientStream wraps around the embedded grpc.ClientStream, and intercepts the RecvMsg and
|
||||
// SendMsg method call.
|
||||
type clientStream struct {
|
||||
grpc.ClientStream
|
||||
|
||||
desc *grpc.StreamDesc
|
||||
events chan streamEvent
|
||||
eventsDone chan struct{}
|
||||
finished chan error
|
||||
|
||||
receivedMessageID int
|
||||
sentMessageID int
|
||||
}
|
||||
|
||||
var _ = proto.Marshal
|
||||
|
||||
func (w *clientStream) RecvMsg(m interface{}) error {
|
||||
err := w.ClientStream.RecvMsg(m)
|
||||
|
||||
if err == nil && !w.desc.ServerStreams {
|
||||
w.sendStreamEvent(receiveEndEvent, nil)
|
||||
} else if errors.Is(err, io.EOF) {
|
||||
w.sendStreamEvent(receiveEndEvent, nil)
|
||||
} else if err != nil {
|
||||
w.sendStreamEvent(errorEvent, err)
|
||||
} else {
|
||||
w.receivedMessageID++
|
||||
messageReceived.Event(w.Context(), w.receivedMessageID, m)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *clientStream) SendMsg(m interface{}) error {
|
||||
err := w.ClientStream.SendMsg(m)
|
||||
|
||||
w.sentMessageID++
|
||||
messageSent.Event(w.Context(), w.sentMessageID, m)
|
||||
|
||||
if err != nil {
|
||||
w.sendStreamEvent(errorEvent, err)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *clientStream) Header() (metadata.MD, error) {
|
||||
md, err := w.ClientStream.Header()
|
||||
|
||||
if err != nil {
|
||||
w.sendStreamEvent(errorEvent, err)
|
||||
}
|
||||
|
||||
return md, err
|
||||
}
|
||||
|
||||
func (w *clientStream) CloseSend() error {
|
||||
err := w.ClientStream.CloseSend()
|
||||
|
||||
if err != nil {
|
||||
w.sendStreamEvent(errorEvent, err)
|
||||
} else {
|
||||
w.sendStreamEvent(closeEvent, nil)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
clientClosedState byte = 1 << iota
|
||||
receiveEndedState
|
||||
)
|
||||
|
||||
func wrapClientStream(s grpc.ClientStream, desc *grpc.StreamDesc) *clientStream {
|
||||
events := make(chan streamEvent)
|
||||
eventsDone := make(chan struct{})
|
||||
finished := make(chan error)
|
||||
|
||||
go func() {
|
||||
defer close(eventsDone)
|
||||
|
||||
// Both streams have to be closed
|
||||
state := byte(0)
|
||||
|
||||
for event := range events {
|
||||
switch event.Type {
|
||||
case closeEvent:
|
||||
state |= clientClosedState
|
||||
case receiveEndEvent:
|
||||
state |= receiveEndedState
|
||||
case errorEvent:
|
||||
finished <- event.Err
|
||||
return
|
||||
}
|
||||
|
||||
if state == clientClosedState|receiveEndedState {
|
||||
finished <- nil
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return &clientStream{
|
||||
ClientStream: s,
|
||||
desc: desc,
|
||||
events: events,
|
||||
eventsDone: eventsDone,
|
||||
finished: finished,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *clientStream) sendStreamEvent(eventType streamEventType, err error) {
|
||||
select {
|
||||
case <-w.eventsDone:
|
||||
case w.events <- streamEvent{Type: eventType, Err: err}:
|
||||
}
|
||||
}
|
||||
|
||||
// serverStream wraps around the embedded grpc.ServerStream, and intercepts the RecvMsg and
|
||||
// SendMsg method call.
|
||||
type serverStream struct {
|
||||
grpc.ServerStream
|
||||
ctx context.Context
|
||||
|
||||
receivedMessageID int
|
||||
sentMessageID int
|
||||
}
|
||||
|
||||
func (w *serverStream) Context() context.Context {
|
||||
return w.ctx
|
||||
}
|
||||
|
||||
func (w *serverStream) RecvMsg(m interface{}) error {
|
||||
err := w.ServerStream.RecvMsg(m)
|
||||
|
||||
if err == nil {
|
||||
w.receivedMessageID++
|
||||
messageReceived.Event(w.Context(), w.receivedMessageID, m)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (w *serverStream) SendMsg(m interface{}) error {
|
||||
err := w.ServerStream.SendMsg(m)
|
||||
|
||||
w.sentMessageID++
|
||||
messageSent.Event(w.Context(), w.sentMessageID, m)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func wrapServerStream(ctx context.Context, ss grpc.ServerStream) *serverStream {
|
||||
return &serverStream{
|
||||
ServerStream: ss,
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// spanInfo returns a span name and all appropriate attributes from the gRPC
|
||||
// method and peer address.
|
||||
func spanInfo(fullMethod, peerAddress string) (string, []attribute.KeyValue) {
|
||||
attrs := []attribute.KeyValue{attribute.Key("rpc.system").String("grpc")}
|
||||
name, mAttrs := parseFullMethod(fullMethod)
|
||||
attrs = append(attrs, mAttrs...)
|
||||
attrs = append(attrs, peerAttr(peerAddress)...)
|
||||
return name, attrs
|
||||
}
|
||||
|
||||
// peerAttr returns attributes about the peer address.
|
||||
func peerAttr(addr string) []attribute.KeyValue {
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return []attribute.KeyValue(nil)
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
host = "127.0.0.1"
|
||||
}
|
||||
|
||||
return []attribute.KeyValue{
|
||||
semconv.NetPeerIPKey.String(host),
|
||||
semconv.NetPeerPortKey.String(port),
|
||||
}
|
||||
}
|
||||
|
||||
// peerFromCtx returns a peer address from a context, if one exists.
|
||||
func peerFromCtx(ctx context.Context) string {
|
||||
p, ok := peer.FromContext(ctx)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return p.Addr.String()
|
||||
}
|
||||
|
||||
// parseFullMethod returns a span name following the OpenTelemetry semantic
|
||||
// conventions as well as all applicable span attribute.KeyValue attributes based
|
||||
// on a gRPC's FullMethod.
|
||||
func parseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
|
||||
name := strings.TrimLeft(fullMethod, "/")
|
||||
parts := strings.SplitN(name, "/", 2)
|
||||
if len(parts) != 2 {
|
||||
// Invalid format, does not follow `/package.service/method`.
|
||||
return name, []attribute.KeyValue(nil)
|
||||
}
|
||||
|
||||
var attrs []attribute.KeyValue
|
||||
if service := parts[0]; service != "" {
|
||||
attrs = append(attrs, semconv.RPCServiceKey.String(service))
|
||||
}
|
||||
if method := parts[1]; method != "" {
|
||||
attrs = append(attrs, semconv.RPCMethodKey.String(method))
|
||||
}
|
||||
return name, attrs
|
||||
}
|
||||
|
||||
// statusCodeAttr returns status code attribute based on given gRPC code.
|
||||
func statusCodeAttr(c grpcCodes.Code) attribute.KeyValue {
|
||||
return GRPCStatusCodeKey.Int64(int64(c))
|
||||
}
|
||||
137
contrib/rpc/grpcx/internal/tracing/tracing_interceptor_client.go
Normal file
137
contrib/rpc/grpcx/internal/tracing/tracing_interceptor_client.go
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
grpcCodes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/grpcctx"
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/net/gtrace"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// UnaryClientInterceptor returns a grpc.UnaryClientInterceptor suitable
|
||||
// for use in a grpc.Dial call.
|
||||
func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{},
|
||||
cc *grpc.ClientConn, invoker grpc.UnaryInvoker, callOpts ...grpc.CallOption) error {
|
||||
tracer := otel.GetTracerProvider().Tracer(
|
||||
tracingInstrumentGrpcClient,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
requestMetadata, _ := metadata.FromOutgoingContext(ctx)
|
||||
metadataCopy := requestMetadata.Copy()
|
||||
name, attr := spanInfo(method, cc.Target())
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(
|
||||
ctx,
|
||||
name,
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(attr...),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
Inject(ctx, metadataCopy)
|
||||
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadataCopy)
|
||||
|
||||
// If it is now using default trace provider, it then does no complex tracing jobs.
|
||||
if gtrace.IsUsingDefaultProvider() {
|
||||
return invoker(ctx, method, req, reply, cc, callOpts...)
|
||||
}
|
||||
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
span.AddEvent(tracingEventGrpcRequest, trace.WithAttributes(
|
||||
attribute.String(tracingEventGrpcRequestBaggage, gconv.String(gtrace.GetBaggageMap(ctx))),
|
||||
attribute.String(tracingEventGrpcMetadataOutgoing, gconv.String(grpcctx.Ctx{}.OutgoingMap(ctx))),
|
||||
attribute.String(
|
||||
tracingEventGrpcRequestMessage,
|
||||
utils.MarshalMessageToJsonStringForTracing(
|
||||
req, "Request", tracingMaxContentLogSize,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
err := invoker(ctx, method, req, reply, cc, callOpts...)
|
||||
|
||||
span.AddEvent(tracingEventGrpcResponse, trace.WithAttributes(
|
||||
attribute.String(
|
||||
tracingEventGrpcResponseMessage,
|
||||
utils.MarshalMessageToJsonStringForTracing(
|
||||
reply, "Response", tracingMaxContentLogSize,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
s, _ := status.FromError(err)
|
||||
span.SetStatus(codes.Error, s.Message())
|
||||
span.SetAttributes(statusCodeAttr(s.Code()))
|
||||
} else {
|
||||
span.SetAttributes(statusCodeAttr(grpcCodes.OK))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// StreamClientInterceptor returns a grpc.StreamClientInterceptor suitable
|
||||
// for use in a grpc.Dial call.
|
||||
func StreamClientInterceptor(
|
||||
ctx context.Context, desc *grpc.StreamDesc,
|
||||
cc *grpc.ClientConn, method string, streamer grpc.Streamer,
|
||||
callOpts ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
tracer := otel.GetTracerProvider().Tracer(
|
||||
tracingInstrumentGrpcClient,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
requestMetadata, _ := metadata.FromOutgoingContext(ctx)
|
||||
metadataCopy := requestMetadata.Copy()
|
||||
name, attr := spanInfo(method, cc.Target())
|
||||
|
||||
var span trace.Span
|
||||
ctx, span = tracer.Start(
|
||||
ctx,
|
||||
name,
|
||||
trace.WithSpanKind(trace.SpanKindClient),
|
||||
trace.WithAttributes(attr...),
|
||||
)
|
||||
|
||||
Inject(ctx, metadataCopy)
|
||||
ctx = metadata.NewOutgoingContext(ctx, metadataCopy)
|
||||
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
s, err := streamer(ctx, desc, cc, method, callOpts...)
|
||||
stream := wrapClientStream(s, desc)
|
||||
go func() {
|
||||
if err == nil {
|
||||
err = <-stream.finished
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s, _ := status.FromError(err)
|
||||
span.SetStatus(codes.Error, s.Message())
|
||||
span.SetAttributes(statusCodeAttr(s.Code()))
|
||||
} else {
|
||||
span.SetAttributes(statusCodeAttr(grpcCodes.OK))
|
||||
}
|
||||
|
||||
span.End()
|
||||
}()
|
||||
|
||||
return stream, err
|
||||
}
|
||||
127
contrib/rpc/grpcx/internal/tracing/tracing_interceptor_server.go
Normal file
127
contrib/rpc/grpcx/internal/tracing/tracing_interceptor_server.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package tracing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/otel"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
"go.opentelemetry.io/otel/codes"
|
||||
"go.opentelemetry.io/otel/trace"
|
||||
"google.golang.org/grpc"
|
||||
grpcCodes "google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/grpc/status"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/grpcctx"
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2"
|
||||
"github.com/gogf/gf/v2/net/gtrace"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// UnaryServerInterceptor returns a grpc.UnaryServerInterceptor suitable
|
||||
// for use in a grpc.NewServer call.
|
||||
func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
|
||||
tracer := otel.GetTracerProvider().Tracer(
|
||||
tracingInstrumentGrpcServer,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
requestMetadata, _ := metadata.FromIncomingContext(ctx)
|
||||
metadataCopy := requestMetadata.Copy()
|
||||
|
||||
entries, spanCtx := Extract(ctx, metadataCopy)
|
||||
ctx = baggage.ContextWithBaggage(ctx, entries)
|
||||
ctx = trace.ContextWithRemoteSpanContext(ctx, spanCtx)
|
||||
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
|
||||
ctx, span := tracer.Start(
|
||||
ctx,
|
||||
name,
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
trace.WithAttributes(attr...),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
// If it is now using default trace provider, it then does no complex tracing jobs.
|
||||
if gtrace.IsUsingDefaultProvider() {
|
||||
return handler(ctx, req)
|
||||
}
|
||||
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
span.AddEvent(tracingEventGrpcRequest, trace.WithAttributes(
|
||||
attribute.String(tracingEventGrpcRequestBaggage, gconv.String(gtrace.GetBaggageMap(ctx))),
|
||||
attribute.String(tracingEventGrpcMetadataIncoming, gconv.String(grpcctx.Ctx{}.IncomingMap(ctx))),
|
||||
attribute.String(
|
||||
tracingEventGrpcRequestMessage,
|
||||
utils.MarshalMessageToJsonStringForTracing(
|
||||
req, "Request", tracingMaxContentLogSize,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
res, err := handler(ctx, req)
|
||||
|
||||
span.AddEvent(tracingEventGrpcResponse, trace.WithAttributes(
|
||||
attribute.String(
|
||||
tracingEventGrpcResponseMessage,
|
||||
utils.MarshalMessageToJsonStringForTracing(
|
||||
res, "Response", tracingMaxContentLogSize,
|
||||
),
|
||||
),
|
||||
))
|
||||
|
||||
if err != nil {
|
||||
s, _ := status.FromError(err)
|
||||
span.SetStatus(codes.Error, s.Message())
|
||||
span.SetAttributes(statusCodeAttr(s.Code()))
|
||||
} else {
|
||||
span.SetAttributes(statusCodeAttr(grpcCodes.OK))
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// StreamServerInterceptor returns a grpc.StreamServerInterceptor suitable
|
||||
// for use in a grpc.NewServer call.
|
||||
func StreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||
tracer := otel.GetTracerProvider().Tracer(
|
||||
tracingInstrumentGrpcServer,
|
||||
trace.WithInstrumentationVersion(gf.VERSION),
|
||||
)
|
||||
|
||||
ctx := ss.Context()
|
||||
requestMetadata, _ := metadata.FromIncomingContext(ctx)
|
||||
metadataCopy := requestMetadata.Copy()
|
||||
entries, spanCtx := Extract(ctx, metadataCopy)
|
||||
ctx = baggage.ContextWithBaggage(ctx, entries)
|
||||
ctx = trace.ContextWithRemoteSpanContext(ctx, spanCtx)
|
||||
name, attr := spanInfo(info.FullMethod, peerFromCtx(ctx))
|
||||
ctx, span := tracer.Start(
|
||||
ctx,
|
||||
name,
|
||||
trace.WithSpanKind(trace.SpanKindServer),
|
||||
trace.WithAttributes(attr...),
|
||||
)
|
||||
defer span.End()
|
||||
|
||||
span.SetAttributes(gtrace.CommonLabels()...)
|
||||
|
||||
err := handler(srv, wrapServerStream(ctx, ss))
|
||||
|
||||
if err != nil {
|
||||
s, _ := status.FromError(err)
|
||||
span.SetStatus(codes.Error, s.Message())
|
||||
span.SetAttributes(statusCodeAttr(s.Code()))
|
||||
} else {
|
||||
span.SetAttributes(statusCodeAttr(grpcCodes.OK))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
44
contrib/rpc/grpcx/internal/utils/utils.go
Normal file
44
contrib/rpc/grpcx/internal/utils/utils.go
Normal file
@ -0,0 +1,44 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package utils provides utilities for GRPC.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
var (
|
||||
protoJSONMarshaller = &protojson.MarshalOptions{
|
||||
EmitUnpopulated: true,
|
||||
}
|
||||
)
|
||||
|
||||
// MarshalPbMessageToJsonString marshals protobuf message to json string.
|
||||
func MarshalPbMessageToJsonString(msg proto.Message) string {
|
||||
return protoJSONMarshaller.Format(msg)
|
||||
}
|
||||
|
||||
func MarshalMessageToJsonStringForTracing(value interface{}, msgType string, maxBytes int) string {
|
||||
var messageContent string
|
||||
if msg, ok := value.(proto.Message); ok {
|
||||
if proto.Size(msg) <= maxBytes {
|
||||
messageContent = MarshalPbMessageToJsonString(msg)
|
||||
} else {
|
||||
messageContent = fmt.Sprintf(
|
||||
"[%s Message Too Large For Tracing, Max: %d bytes]",
|
||||
msgType,
|
||||
maxBytes,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
messageContent = fmt.Sprintf("%v", value)
|
||||
}
|
||||
return messageContent
|
||||
}
|
||||
@ -283,7 +283,7 @@ func parseConfigNodeLink(node *ConfigNode) *ConfigNode {
|
||||
} else {
|
||||
node.Name = match[5]
|
||||
}
|
||||
if len(match) > 6 {
|
||||
if len(match) > 6 && match[7] != "" {
|
||||
node.Extra = match[7]
|
||||
}
|
||||
node.Link = ""
|
||||
|
||||
@ -58,7 +58,7 @@ func New(config ...*Config) (*Redis, error) {
|
||||
}
|
||||
redis := &Redis{
|
||||
config: config[0],
|
||||
localAdapter: defaultAdapterFunc(config[0]),
|
||||
localAdapter: usedAdapter,
|
||||
}
|
||||
return redis.initGroup(), nil
|
||||
}
|
||||
|
||||
@ -7,30 +7,33 @@ require (
|
||||
github.com/gogf/gf/contrib/config/kubecm/v2 v2.0.0
|
||||
github.com/gogf/gf/contrib/config/nacos/v2 v2.2.6
|
||||
github.com/gogf/gf/contrib/config/polaris/v2 v2.0.0
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.2.1
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.0.0
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.2.6
|
||||
github.com/gogf/gf/contrib/registry/etcd/v2 v2.2.6
|
||||
github.com/gogf/gf/contrib/registry/file/v2 v2.2.6
|
||||
github.com/gogf/gf/contrib/registry/polaris/v2 v2.0.0
|
||||
github.com/gogf/gf/contrib/rpc/grpcx/v2 v2.0.0
|
||||
github.com/gogf/gf/contrib/trace/jaeger/v2 v2.0.0
|
||||
github.com/gogf/gf/v2 v2.2.1
|
||||
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/nacos-group/nacos-sdk-go v1.1.2
|
||||
github.com/polarismesh/polaris-go v1.2.0-beta.3
|
||||
google.golang.org/grpc v1.46.2
|
||||
google.golang.org/grpc v1.49.0
|
||||
google.golang.org/protobuf v1.28.1
|
||||
k8s.io/client-go v0.25.2
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/gogf/gf/contrib/config/apollo/v2 => ../contrib/config/apollo/
|
||||
github.com/gogf/gf/contrib/config/kubecm/v2 => ../contrib/config/kubecm/
|
||||
github.com/gogf/gf/contrib/config/nacos/v2 => ../contrib/config/nacos/
|
||||
github.com/gogf/gf/contrib/config/polaris/v2 => ../contrib/config/polaris/
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 => ../contrib/drivers/mysql/
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 => ../contrib/nosql/redis/
|
||||
github.com/gogf/gf/contrib/registry/etcd/v2 => ../contrib/registry/etcd/
|
||||
github.com/gogf/gf/contrib/registry/file/v2 => ../contrib/registry/file/
|
||||
github.com/gogf/gf/contrib/registry/polaris/v2 => ../contrib/registry/polaris/
|
||||
github.com/gogf/gf/contrib/rpc/grpcx/v2 => ../contrib/rpc/grpcx/
|
||||
github.com/gogf/gf/contrib/trace/jaeger/v2 => ../contrib/trace/jaeger/
|
||||
github.com/gogf/gf/v2 => ../
|
||||
)
|
||||
|
||||
@ -188,8 +188,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773 h1:YQBLawktoymYtPGs9idE9JS5Wqd3SjIzUEZOPKCdSw0=
|
||||
github.com/gogf/katyusha v0.4.1-0.20220620125113-f55d6f739773/go.mod h1:Z0GCeHXz1UI0HtA0K45c6TzEGM4DL/PLatS747/WarI=
|
||||
github.com/gogf/gf/contrib/config/nacos/v2 v2.2.6 h1:QQLN9bB9vFPFvROWM2uSchsHDaMr6ciNjrWY9xuZlIk=
|
||||
github.com/gogf/gf/contrib/config/nacos/v2 v2.2.6/go.mod h1:phuuQsfTgyMdEENfotnk22ZrEu48pO7rjho158bZsD0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@ -544,14 +544,16 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/otel v1.7.0 h1:Z2lA3Tdch0iDcrhJXDIlC94XE+bxok1F9B+4Lz/lGsM=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.10.0 h1:Y7DTJMR6zs1xkS/upamJYk0SxxN4C9AqRd77jmZnyY4=
|
||||
go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.7.0 h1:wXgjiRldljksZkZrldGVe6XrG9u3kYDyQmkZwmm5dI0=
|
||||
go.opentelemetry.io/otel/exporters/jaeger v1.7.0/go.mod h1:PwQAOqBgqbLQRKlj466DuD2qyMjbtcPpfPfj+AqbSBs=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0 h1:4OmStpcKVOfvDOgCt7UriAPtKolwIhxpnSNI/yK+1B0=
|
||||
go.opentelemetry.io/otel/sdk v1.7.0/go.mod h1:uTEOTwaqIVuTGiJN7ii13Ibp75wJmYUDe374q6cZwUU=
|
||||
go.opentelemetry.io/otel/trace v1.7.0 h1:O37Iogk1lEkMRXewVtZ1BBTVn5JEp8GrJvP92bJqC6o=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.10.0 h1:npQMbR8o7mum8uF95yFbOEJffhs1sbCOfDh8zAJiH5E=
|
||||
go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
@ -668,9 +670,9 @@ golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1 h1:TWZxd/th7FbRSMret2MVQdlI8uT49QEtwZdvJrxjEHU=
|
||||
golang.org/x/net v0.0.0-20220919232410-f2f64ebce3c1/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -776,8 +778,9 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -985,8 +988,9 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
|
||||
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ=
|
||||
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
|
||||
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
|
||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@ -1001,8 +1005,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
|
||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
27
example/registry/file/client/main.go
Normal file
27
example/registry/file/client/main.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))
|
||||
|
||||
client := g.Client()
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Get(gctx.New(), `http://hello.svc/`)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(res.ReadAllString())
|
||||
res.Close()
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
20
example/registry/file/server/main.go
Normal file
20
example/registry/file/server/main.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/registry/file/v2"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
gsvc.SetRegistry(file.New(gfile.Temp("gsvc")))
|
||||
|
||||
s := g.Server(`hello.svc`)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
g.Log().Info(r.Context(), `request received`)
|
||||
r.Response.Write(`Hello world`)
|
||||
})
|
||||
s.Run()
|
||||
}
|
||||
0
example/rpc/grpcx/basic/client/config.yaml
Normal file
0
example/rpc/grpcx/basic/client/config.yaml
Normal file
32
example/rpc/grpcx/basic/client/main.go
Normal file
32
example/rpc/grpcx/basic/client/main.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
|
||||
"github.com/gogf/gf/example/rpc/grpcx/basic/protocol"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
client = protocol.NewEchoClient(grpcx.Client.MustNewGrpcClientConn("demo"))
|
||||
)
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Say(ctx, &protocol.SayReq{Content: "Hello"})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
return
|
||||
}
|
||||
g.Log().Print(ctx, "Response:", res.Content)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
21
example/rpc/grpcx/basic/protobuf/echo.proto
Normal file
21
example/rpc/grpcx/basic/protobuf/echo.proto
Normal file
@ -0,0 +1,21 @@
|
||||
// protoc --go_out=plugins=grpc:. *.proto
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
|
||||
option go_package = "/proto";
|
||||
|
||||
service Echo{
|
||||
rpc Say(SayReq) returns (SayRes) {}
|
||||
}
|
||||
|
||||
message SayReq {
|
||||
string content = 1;
|
||||
}
|
||||
|
||||
message SayRes {
|
||||
string content = 1;
|
||||
}
|
||||
|
||||
|
||||
305
example/rpc/grpcx/basic/protocol/echo.pb.go
Normal file
305
example/rpc/grpcx/basic/protocol/echo.pb.go
Normal file
@ -0,0 +1,305 @@
|
||||
// protoc --go_out=plugins=grpc:. *.proto
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.23.0
|
||||
// protoc v3.11.4
|
||||
// source: echo.proto
|
||||
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
context "context"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type SayReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SayReq) Reset() {
|
||||
*x = SayReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_echo_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SayReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SayReq) ProtoMessage() {}
|
||||
|
||||
func (x *SayReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_echo_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SayReq.ProtoReflect.Descriptor instead.
|
||||
func (*SayReq) Descriptor() ([]byte, []int) {
|
||||
return file_echo_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SayReq) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SayRes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SayRes) Reset() {
|
||||
*x = SayRes{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_echo_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SayRes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SayRes) ProtoMessage() {}
|
||||
|
||||
func (x *SayRes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_echo_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SayRes.ProtoReflect.Descriptor instead.
|
||||
func (*SayRes) Descriptor() ([]byte, []int) {
|
||||
return file_echo_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SayRes) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_echo_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_echo_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x06, 0x53, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x22, 0x0a, 0x06, 0x53, 0x61, 0x79, 0x52, 0x65,
|
||||
0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x32, 0x2d, 0x0a, 0x04, 0x45,
|
||||
0x63, 0x68, 0x6f, 0x12, 0x25, 0x0a, 0x03, 0x53, 0x61, 0x79, 0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x2e, 0x53, 0x61, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x2e, 0x53, 0x61, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_echo_proto_rawDescOnce sync.Once
|
||||
file_echo_proto_rawDescData = file_echo_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_echo_proto_rawDescGZIP() []byte {
|
||||
file_echo_proto_rawDescOnce.Do(func() {
|
||||
file_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_echo_proto_rawDescData)
|
||||
})
|
||||
return file_echo_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_echo_proto_goTypes = []interface{}{
|
||||
(*SayReq)(nil), // 0: proto.SayReq
|
||||
(*SayRes)(nil), // 1: proto.SayRes
|
||||
}
|
||||
var file_echo_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Echo.Say:input_type -> proto.SayReq
|
||||
1, // 1: proto.Echo.Say:output_type -> proto.SayRes
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_echo_proto_init() }
|
||||
func file_echo_proto_init() {
|
||||
if File_echo_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SayReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_echo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SayRes); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_echo_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_echo_proto_goTypes,
|
||||
DependencyIndexes: file_echo_proto_depIdxs,
|
||||
MessageInfos: file_echo_proto_msgTypes,
|
||||
}.Build()
|
||||
File_echo_proto = out.File
|
||||
file_echo_proto_rawDesc = nil
|
||||
file_echo_proto_goTypes = nil
|
||||
file_echo_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// EchoClient is the client API for Echo service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type EchoClient interface {
|
||||
Say(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (*SayRes, error)
|
||||
}
|
||||
|
||||
type echoClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewEchoClient(cc grpc.ClientConnInterface) EchoClient {
|
||||
return &echoClient{cc}
|
||||
}
|
||||
|
||||
func (c *echoClient) Say(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (*SayRes, error) {
|
||||
out := new(SayRes)
|
||||
err := c.cc.Invoke(ctx, "/proto.Echo/Say", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// EchoServer is the server API for Echo service.
|
||||
type EchoServer interface {
|
||||
Say(context.Context, *SayReq) (*SayRes, error)
|
||||
}
|
||||
|
||||
// UnimplementedEchoServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedEchoServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedEchoServer) Say(context.Context, *SayReq) (*SayRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Say not implemented")
|
||||
}
|
||||
|
||||
func RegisterEchoServer(s *grpc.Server, srv EchoServer) {
|
||||
s.RegisterService(&_Echo_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Echo_Say_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SayReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(EchoServer).Say(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Echo/Say",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(EchoServer).Say(ctx, req.(*SayReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Echo_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Echo",
|
||||
HandlerType: (*EchoServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Say",
|
||||
Handler: _Echo_Say_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "echo.proto",
|
||||
}
|
||||
8
example/rpc/grpcx/basic/server/config.yaml
Normal file
8
example/rpc/grpcx/basic/server/config.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
grpc:
|
||||
name: "demo"
|
||||
logPath: "./log"
|
||||
logStdout: true
|
||||
errorLogEnabled: true
|
||||
accessLogEnabled: true
|
||||
errorStack: true
|
||||
|
||||
19
example/rpc/grpcx/basic/server/main.go
Normal file
19
example/rpc/grpcx/basic/server/main.go
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
|
||||
"github.com/gogf/gf/example/rpc/grpcx/basic/protocol"
|
||||
"github.com/gogf/gf/example/rpc/grpcx/basic/service"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := grpcx.Server.New()
|
||||
protocol.RegisterEchoServer(s.Server, new(service.Echo))
|
||||
s.Run()
|
||||
}
|
||||
27
example/rpc/grpcx/basic/service/service_echo.go
Normal file
27
example/rpc/grpcx/basic/service/service_echo.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/example/rpc/grpcx/basic/protocol"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcmd"
|
||||
)
|
||||
|
||||
// Echo is the service for echo.
|
||||
type Echo struct{}
|
||||
|
||||
// Say implements the protobuf.EchoServer interface.
|
||||
func (s *Echo) Say(ctx context.Context, r *protocol.SayReq) (*protocol.SayRes, error) {
|
||||
g.Log().Print(ctx, "Received:", r.Content)
|
||||
text := fmt.Sprintf(`%s: > %s`, gcmd.GetOpt("node", "default"), r.Content)
|
||||
return &protocol.SayRes{Content: text}, nil
|
||||
}
|
||||
0
example/rpc/grpcx/basic_with_tag/client/config.yaml
Normal file
0
example/rpc/grpcx/basic_with_tag/client/config.yaml
Normal file
34
example/rpc/grpcx/basic_with_tag/client/main.go
Normal file
34
example/rpc/grpcx/basic_with_tag/client/main.go
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/example/rpc/grpcx/basic_with_tag/protocol"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
ctx = gctx.GetInitCtx()
|
||||
client, err = protocol.NewClient()
|
||||
)
|
||||
if err != nil {
|
||||
g.Log().Fatalf(ctx, `%+v`, err)
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
res, err := client.Echo().Say(ctx, &protocol.SayReq{Content: "Hello"})
|
||||
if err != nil {
|
||||
g.Log().Error(ctx, err)
|
||||
return
|
||||
}
|
||||
g.Log().Print(ctx, "Response:", res.Content)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
19
example/rpc/grpcx/basic_with_tag/protobuf/echo.proto
Normal file
19
example/rpc/grpcx/basic_with_tag/protobuf/echo.proto
Normal file
@ -0,0 +1,19 @@
|
||||
// protoc --go_out=plugins=grpc:. *.proto
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
package proto;
|
||||
|
||||
service Echo{
|
||||
rpc Say(SayReq) returns (SayRes) {}
|
||||
}
|
||||
|
||||
message SayReq {
|
||||
string content = 1;
|
||||
}
|
||||
|
||||
message SayRes {
|
||||
string content = 1;
|
||||
}
|
||||
|
||||
|
||||
31
example/rpc/grpcx/basic_with_tag/protocol/client.go
Normal file
31
example/rpc/grpcx/basic_with_tag/protocol/client.go
Normal file
@ -0,0 +1,31 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/gogf/gf/contrib/rpc/grpcx/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
// AppID is the application ID for the protobuf service.
|
||||
AppID = "demo"
|
||||
)
|
||||
|
||||
// Client is the client for protobuf.
|
||||
type Client struct {
|
||||
conn *grpc.ClientConn
|
||||
}
|
||||
|
||||
// NewClient creates and returns a new client.
|
||||
func NewClient(options ...grpc.DialOption) (*Client, error) {
|
||||
conn, err := grpcx.Client.NewGrpcClientConn(AppID, options...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Client{conn: conn}, nil
|
||||
}
|
||||
|
||||
// Echo is the client for protobuf.Echo.
|
||||
func (c *Client) Echo() EchoClient {
|
||||
return NewEchoClient(c.conn)
|
||||
}
|
||||
305
example/rpc/grpcx/basic_with_tag/protocol/echo.pb.go
Normal file
305
example/rpc/grpcx/basic_with_tag/protocol/echo.pb.go
Normal file
@ -0,0 +1,305 @@
|
||||
// protoc --go_out=plugins=grpc:. *.proto
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.23.0
|
||||
// protoc v3.11.4
|
||||
// source: echo.proto
|
||||
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
context "context"
|
||||
proto "github.com/golang/protobuf/proto"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
// This is a compile-time assertion that a sufficiently up-to-date version
|
||||
// of the legacy proto package is being used.
|
||||
const _ = proto.ProtoPackageIsVersion4
|
||||
|
||||
type SayReq struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SayReq) Reset() {
|
||||
*x = SayReq{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_echo_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SayReq) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SayReq) ProtoMessage() {}
|
||||
|
||||
func (x *SayReq) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_echo_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SayReq.ProtoReflect.Descriptor instead.
|
||||
func (*SayReq) Descriptor() ([]byte, []int) {
|
||||
return file_echo_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SayReq) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SayRes struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Content string `protobuf:"bytes,1,opt,name=content,proto3" json:"content,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SayRes) Reset() {
|
||||
*x = SayRes{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_echo_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SayRes) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SayRes) ProtoMessage() {}
|
||||
|
||||
func (x *SayRes) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_echo_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SayRes.ProtoReflect.Descriptor instead.
|
||||
func (*SayRes) Descriptor() ([]byte, []int) {
|
||||
return file_echo_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *SayRes) GetContent() string {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_echo_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_echo_proto_rawDesc = []byte{
|
||||
0x0a, 0x0a, 0x65, 0x63, 0x68, 0x6f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x22, 0x22, 0x0a, 0x06, 0x53, 0x61, 0x79, 0x52, 0x65, 0x71, 0x12, 0x18, 0x0a,
|
||||
0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
|
||||
0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x22, 0x0a, 0x06, 0x53, 0x61, 0x79, 0x52, 0x65,
|
||||
0x73, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x32, 0x2d, 0x0a, 0x04, 0x45,
|
||||
0x63, 0x68, 0x6f, 0x12, 0x25, 0x0a, 0x03, 0x53, 0x61, 0x79, 0x12, 0x0d, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x2e, 0x53, 0x61, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x0d, 0x2e, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x2e, 0x53, 0x61, 0x79, 0x52, 0x65, 0x73, 0x22, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
|
||||
0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_echo_proto_rawDescOnce sync.Once
|
||||
file_echo_proto_rawDescData = file_echo_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_echo_proto_rawDescGZIP() []byte {
|
||||
file_echo_proto_rawDescOnce.Do(func() {
|
||||
file_echo_proto_rawDescData = protoimpl.X.CompressGZIP(file_echo_proto_rawDescData)
|
||||
})
|
||||
return file_echo_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_echo_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_echo_proto_goTypes = []interface{}{
|
||||
(*SayReq)(nil), // 0: proto.SayReq
|
||||
(*SayRes)(nil), // 1: proto.SayRes
|
||||
}
|
||||
var file_echo_proto_depIdxs = []int32{
|
||||
0, // 0: proto.Echo.Say:input_type -> proto.SayReq
|
||||
1, // 1: proto.Echo.Say:output_type -> proto.SayRes
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_echo_proto_init() }
|
||||
func file_echo_proto_init() {
|
||||
if File_echo_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_echo_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SayReq); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_echo_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SayRes); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_echo_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
GoTypes: file_echo_proto_goTypes,
|
||||
DependencyIndexes: file_echo_proto_depIdxs,
|
||||
MessageInfos: file_echo_proto_msgTypes,
|
||||
}.Build()
|
||||
File_echo_proto = out.File
|
||||
file_echo_proto_rawDesc = nil
|
||||
file_echo_proto_goTypes = nil
|
||||
file_echo_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConnInterface
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion6
|
||||
|
||||
// EchoClient is the client API for Echo service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type EchoClient interface {
|
||||
Say(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (*SayRes, error)
|
||||
}
|
||||
|
||||
type echoClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewEchoClient(cc grpc.ClientConnInterface) EchoClient {
|
||||
return &echoClient{cc}
|
||||
}
|
||||
|
||||
func (c *echoClient) Say(ctx context.Context, in *SayReq, opts ...grpc.CallOption) (*SayRes, error) {
|
||||
out := new(SayRes)
|
||||
err := c.cc.Invoke(ctx, "/proto.Echo/Say", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// EchoServer is the server API for Echo service.
|
||||
type EchoServer interface {
|
||||
Say(context.Context, *SayReq) (*SayRes, error)
|
||||
}
|
||||
|
||||
// UnimplementedEchoServer can be embedded to have forward compatible implementations.
|
||||
type UnimplementedEchoServer struct {
|
||||
}
|
||||
|
||||
func (*UnimplementedEchoServer) Say(context.Context, *SayReq) (*SayRes, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Say not implemented")
|
||||
}
|
||||
|
||||
func RegisterEchoServer(s *grpc.Server, srv EchoServer) {
|
||||
s.RegisterService(&_Echo_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _Echo_Say_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SayReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(EchoServer).Say(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/proto.Echo/Say",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(EchoServer).Say(ctx, req.(*SayReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _Echo_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "proto.Echo",
|
||||
HandlerType: (*EchoServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Say",
|
||||
Handler: _Echo_Say_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "echo.proto",
|
||||
}
|
||||
8
example/rpc/grpcx/basic_with_tag/server/config.yaml
Normal file
8
example/rpc/grpcx/basic_with_tag/server/config.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
grpc:
|
||||
name: "demo"
|
||||
logPath: "./log"
|
||||
logStdout: true
|
||||
errorLogEnabled: true
|
||||
accessLogEnabled: true
|
||||
errorStack: true
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user