Compare commits

...

16 Commits

Author SHA1 Message Date
1cd1449085 add package contrib/rpc/grpcx (#2169) 2023-03-08 14:12:51 +08:00
55690f3738 improve unit testing cases coverage to 80%+ (#2480) 2023-03-07 21:27:23 +08:00
13f6fb1929 fix issue struct attribute converting when has no json name but with omitempty tag (#2486) 2023-03-07 21:26:32 +08:00
e8088a6563 improve command gen dao by removing all dao files if clear option is true (#2502)
* improve command  by removing all dao files if clear option is true

* version updates
2023-03-07 19:32:13 +08:00
e8051bad9a fix issue in empty json name along with omitempty tag in package oai (#2500) 2023-03-07 14:17:14 +08:00
d0d41a63a6 robot translator updates (#2491) 2023-03-07 11:01:01 +08:00
853b038a47 add issue translation robot (#2490) 2023-03-06 09:50:03 +08:00
34946f6105 fix configuration of Namespace for pgsql (#2481) 2023-03-01 19:30:40 +08:00
Gin
15d88c269d fix: gredis maxActive config and duplicate connection bug (#2479) 2023-02-27 22:08:37 +08:00
cbbfd85eeb Protect RemoveValue from race condition (#2472) 2023-02-23 19:47:57 +08:00
adf90c876f improve unit testing cases (#2468) 2023-02-23 10:07:40 +08:00
b4f76b8448 fix: use keyword(like: group) as table name in sqlite (#2461) 2023-02-20 22:03:26 +08:00
ed858ebd4b Bump golang.org/x/net from 0.1.0 to 0.7.0 in /cmd/gf (#2460) 2023-02-20 22:02:31 +08:00
272b9c7afd README update for cmd/gf (#2459) 2023-02-20 22:01:36 +08:00
8dc8dd9756 fix issue #2261 (#2458) 2023-02-20 21:57:49 +08:00
a64d1001e2 improve ci by using cache feature of setup-go (#2463) 2023-02-20 21:51:54 +08:00
154 changed files with 6029 additions and 362 deletions

View File

@ -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
View 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. 👯👭🏻🧑‍🤝‍🧑👫🧑🏿‍🤝‍🧑🏻👩🏾‍🤝‍👨🏿👬🏿

View File

@ -5,7 +5,7 @@
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![GoFrame CI](https://github.com/gogf/gf/actions/workflows/gf.yml/badge.svg)](https://github.com/gogf/gf/actions/workflows/gf.yml)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf?v=1)](https://goreportcard.com/report/github.com/gogf/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf/branch/master)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf)
[![Production Ready](https://img.shields.io/badge/production-ready-blue.svg)](https://github.com/gogf/gf)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
</div>

View File

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

View File

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

View File

@ -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=

View File

@ -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
}

View File

@ -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)
}

View File

@ -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{

View File

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

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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)
})
}

View File

@ -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
)

View File

@ -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=

View File

@ -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)
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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"])
})
}

View File

@ -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,

View File

@ -0,0 +1,3 @@
# Service registrar and discovery
Please refer to certain sub folder.

View File

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

View File

@ -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)
}

View File

@ -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 {

View 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)
}

View File

@ -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()
}
}

View 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)
})
}

View 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)
}
}
```

View 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,
}
}

View 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
}

View 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
}

View 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)
}

View 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
}

View 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)
})
}

View 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 => ../../../

View 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=

View File

@ -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)

View File

@ -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.

View File

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

View File

@ -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)
}

View File

@ -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
View 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
View 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=

View 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.
)

View 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...)
}

View 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
}

View 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)
}
}

View 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)
}

View 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...)
}

View 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)
}

View 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)
}

View 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")
})
}

View 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,
))
}

View 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
}

View 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()
}

View 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
}

View 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)
}

View 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))
}

View 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
}

View 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)
}

View 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
}

View 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)
}

View 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))
}

View 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
}

View 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
}

View 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
}

View File

@ -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 = ""

View File

@ -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
}

View File

@ -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 => ../
)

View File

@ -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=

View 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)
}
}

View 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()
}

View 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)
}
}

View 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;
}

View 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",
}

View File

@ -0,0 +1,8 @@
grpc:
name: "demo"
logPath: "./log"
logStdout: true
errorLogEnabled: true
accessLogEnabled: true
errorStack: true

View 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()
}

View 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
}

View 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)
}
}

View 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;
}

View 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)
}

View 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",
}

View 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