mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-13 18:00:00 +08:00
Merge branches 'goctl-sql-fix' and 'master' of github.com:anqiansong/go-zero into goctl-sql-fix
Conflicts: tools/modelctl/model/modelgen/gen.go
This commit is contained in:
3
.codecov.yml
Normal file
3
.codecov.yml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ignore:
|
||||||
|
- "example/*"
|
||||||
|
- "tools/*"
|
||||||
11
.github/workflows/go.yml
vendored
11
.github/workflows/go.yml
vendored
@@ -25,10 +25,11 @@ jobs:
|
|||||||
- name: Get dependencies
|
- name: Get dependencies
|
||||||
run: |
|
run: |
|
||||||
go get -v -t -d ./...
|
go get -v -t -d ./...
|
||||||
if [ -f Gopkg.toml ]; then
|
|
||||||
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
|
|
||||||
dep ensure
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v -race ./...
|
run: go test -race -coverprofile=coverage.txt -covermode=atomic ./...
|
||||||
|
|
||||||
|
- name: Codecov
|
||||||
|
uses: codecov/codecov-action@v1.0.6
|
||||||
|
with:
|
||||||
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
stages:
|
|
||||||
- analysis
|
|
||||||
|
|
||||||
variables:
|
|
||||||
GOPATH: '/runner-cache/zero'
|
|
||||||
GOCACHE: '/runner-cache/zero'
|
|
||||||
GOPROXY: 'https://goproxy.cn,direct'
|
|
||||||
|
|
||||||
analysis:
|
|
||||||
stage: analysis
|
|
||||||
image: golang
|
|
||||||
script:
|
|
||||||
- go version && go env
|
|
||||||
- go test -short $(go list ./...) | grep -v "no test"
|
|
||||||
only:
|
|
||||||
- merge_requests
|
|
||||||
tags:
|
|
||||||
- common
|
|
||||||
@@ -2,7 +2,7 @@ package discov
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tal-tech/go-zero/core/discov/internal"
|
"github.com/tal-tech/go-zero/core/discov/internal"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
@@ -26,7 +26,7 @@ func NewFacade(endpoints []string) Facade {
|
|||||||
|
|
||||||
func (f Facade) Client() internal.EtcdClient {
|
func (f Facade) Client() internal.EtcdClient {
|
||||||
conn, err := f.registry.GetConn(f.endpoints)
|
conn, err := f.registry.GetConn(f.endpoints)
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
return conn
|
return conn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/lang"
|
||||||
"github.com/tal-tech/go-zero/core/proc"
|
"github.com/tal-tech/go-zero/core/proc"
|
||||||
"github.com/tal-tech/go-zero/core/syncx"
|
"github.com/tal-tech/go-zero/core/syncx"
|
||||||
"github.com/tal-tech/go-zero/core/threading"
|
"github.com/tal-tech/go-zero/core/threading"
|
||||||
@@ -32,19 +33,21 @@ type (
|
|||||||
container TaskContainer
|
container TaskContainer
|
||||||
waitGroup sync.WaitGroup
|
waitGroup sync.WaitGroup
|
||||||
// avoid race condition on waitGroup when calling wg.Add/Done/Wait(...)
|
// avoid race condition on waitGroup when calling wg.Add/Done/Wait(...)
|
||||||
wgBarrier syncx.Barrier
|
wgBarrier syncx.Barrier
|
||||||
guarded bool
|
confirmChan chan lang.PlaceholderType
|
||||||
newTicker func(duration time.Duration) timex.Ticker
|
guarded bool
|
||||||
lock sync.Mutex
|
newTicker func(duration time.Duration) timex.Ticker
|
||||||
|
lock sync.Mutex
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor {
|
func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *PeriodicalExecutor {
|
||||||
executor := &PeriodicalExecutor{
|
executor := &PeriodicalExecutor{
|
||||||
// buffer 1 to let the caller go quickly
|
// buffer 1 to let the caller go quickly
|
||||||
commander: make(chan interface{}, 1),
|
commander: make(chan interface{}, 1),
|
||||||
interval: interval,
|
interval: interval,
|
||||||
container: container,
|
container: container,
|
||||||
|
confirmChan: make(chan lang.PlaceholderType),
|
||||||
newTicker: func(d time.Duration) timex.Ticker {
|
newTicker: func(d time.Duration) timex.Ticker {
|
||||||
return timex.NewTicker(interval)
|
return timex.NewTicker(interval)
|
||||||
},
|
},
|
||||||
@@ -59,10 +62,12 @@ func NewPeriodicalExecutor(interval time.Duration, container TaskContainer) *Per
|
|||||||
func (pe *PeriodicalExecutor) Add(task interface{}) {
|
func (pe *PeriodicalExecutor) Add(task interface{}) {
|
||||||
if vals, ok := pe.addAndCheck(task); ok {
|
if vals, ok := pe.addAndCheck(task); ok {
|
||||||
pe.commander <- vals
|
pe.commander <- vals
|
||||||
|
<-pe.confirmChan
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PeriodicalExecutor) Flush() bool {
|
func (pe *PeriodicalExecutor) Flush() bool {
|
||||||
|
pe.enterExecution()
|
||||||
return pe.executeTasks(func() interface{} {
|
return pe.executeTasks(func() interface{} {
|
||||||
pe.lock.Lock()
|
pe.lock.Lock()
|
||||||
defer pe.lock.Unlock()
|
defer pe.lock.Unlock()
|
||||||
@@ -114,6 +119,8 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
|
|||||||
select {
|
select {
|
||||||
case vals := <-pe.commander:
|
case vals := <-pe.commander:
|
||||||
commanded = true
|
commanded = true
|
||||||
|
pe.enterExecution()
|
||||||
|
pe.confirmChan <- lang.Placeholder
|
||||||
pe.executeTasks(vals)
|
pe.executeTasks(vals)
|
||||||
last = timex.Now()
|
last = timex.Now()
|
||||||
case <-ticker.Chan():
|
case <-ticker.Chan():
|
||||||
@@ -135,13 +142,18 @@ func (pe *PeriodicalExecutor) backgroundFlush() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PeriodicalExecutor) executeTasks(tasks interface{}) bool {
|
func (pe *PeriodicalExecutor) doneExecution() {
|
||||||
|
pe.waitGroup.Done()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pe *PeriodicalExecutor) enterExecution() {
|
||||||
pe.wgBarrier.Guard(func() {
|
pe.wgBarrier.Guard(func() {
|
||||||
pe.waitGroup.Add(1)
|
pe.waitGroup.Add(1)
|
||||||
})
|
})
|
||||||
defer pe.wgBarrier.Guard(func() {
|
}
|
||||||
pe.waitGroup.Done()
|
|
||||||
})
|
func (pe *PeriodicalExecutor) executeTasks(tasks interface{}) bool {
|
||||||
|
defer pe.doneExecution()
|
||||||
|
|
||||||
ok := pe.hasTasks(tasks)
|
ok := pe.hasTasks(tasks)
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -106,6 +106,40 @@ func TestPeriodicalExecutor_Bulk(t *testing.T) {
|
|||||||
lock.Unlock()
|
lock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPeriodicalExecutor_Wait(t *testing.T) {
|
||||||
|
var lock sync.Mutex
|
||||||
|
executer := NewBulkExecutor(func(tasks []interface{}) {
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}, WithBulkTasks(1), WithBulkInterval(time.Second))
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
executer.Add(1)
|
||||||
|
}
|
||||||
|
executer.Flush()
|
||||||
|
executer.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeriodicalExecutor_WaitFast(t *testing.T) {
|
||||||
|
const total = 3
|
||||||
|
var cnt int
|
||||||
|
var lock sync.Mutex
|
||||||
|
executer := NewBulkExecutor(func(tasks []interface{}) {
|
||||||
|
defer func() {
|
||||||
|
cnt++
|
||||||
|
}()
|
||||||
|
lock.Lock()
|
||||||
|
defer lock.Unlock()
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}, WithBulkTasks(1), WithBulkInterval(10*time.Millisecond))
|
||||||
|
for i := 0; i < total; i++ {
|
||||||
|
executer.Add(2)
|
||||||
|
}
|
||||||
|
executer.Flush()
|
||||||
|
executer.Wait()
|
||||||
|
assert.Equal(t, total, cnt)
|
||||||
|
}
|
||||||
|
|
||||||
// go test -benchtime 10s -bench .
|
// go test -benchtime 10s -bench .
|
||||||
func BenchmarkExecutor(b *testing.B) {
|
func BenchmarkExecutor(b *testing.B) {
|
||||||
b.ReportAllocs()
|
b.ReportAllocs()
|
||||||
|
|||||||
@@ -1,16 +1,8 @@
|
|||||||
package lang
|
package lang
|
||||||
|
|
||||||
import "log"
|
|
||||||
|
|
||||||
var Placeholder PlaceholderType
|
var Placeholder PlaceholderType
|
||||||
|
|
||||||
type (
|
type (
|
||||||
GenericType = interface{}
|
GenericType = interface{}
|
||||||
PlaceholderType = struct{}
|
PlaceholderType = struct{}
|
||||||
)
|
)
|
||||||
|
|
||||||
func Must(err error) {
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package lang
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestMust(t *testing.T) {
|
|
||||||
Must(nil)
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,6 @@ import (
|
|||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/iox"
|
"github.com/tal-tech/go-zero/core/iox"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
|
||||||
"github.com/tal-tech/go-zero/core/sysx"
|
"github.com/tal-tech/go-zero/core/sysx"
|
||||||
"github.com/tal-tech/go-zero/core/timex"
|
"github.com/tal-tech/go-zero/core/timex"
|
||||||
)
|
)
|
||||||
@@ -46,6 +45,7 @@ const (
|
|||||||
levelInfo = "info"
|
levelInfo = "info"
|
||||||
levelError = "error"
|
levelError = "error"
|
||||||
levelSevere = "severe"
|
levelSevere = "severe"
|
||||||
|
levelFatal = "fatal"
|
||||||
levelSlow = "slow"
|
levelSlow = "slow"
|
||||||
levelStat = "stat"
|
levelStat = "stat"
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ type (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func MustSetup(c LogConf) {
|
func MustSetup(c LogConf) {
|
||||||
lang.Must(SetUp(c))
|
Must(SetUp(c))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUp sets up the logx. If already set up, just return nil.
|
// SetUp sets up the logx. If already set up, just return nil.
|
||||||
@@ -210,6 +210,14 @@ func Infof(format string, v ...interface{}) {
|
|||||||
infoSync(fmt.Sprintf(format, v...))
|
infoSync(fmt.Sprintf(format, v...))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Must(err error) {
|
||||||
|
if err != nil {
|
||||||
|
msg := formatWithCaller(err.Error(), 3)
|
||||||
|
output(severeLog, levelFatal, msg)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SetLevel(level uint32) {
|
func SetLevel(level uint32) {
|
||||||
atomic.StoreUint32(&logLevel, level)
|
atomic.StoreUint32(&logLevel, level)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ func (mw *mockWriter) Reset() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mw *mockWriter) Contains(text string) bool {
|
func (mw *mockWriter) Contains(text string) bool {
|
||||||
return strings.Index(mw.builder.String(), text) > -1
|
return strings.Contains(mw.builder.String(), text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFileLineFileMode(t *testing.T) {
|
func TestFileLineFileMode(t *testing.T) {
|
||||||
@@ -131,6 +131,10 @@ func TestSetLevelWithDuration(t *testing.T) {
|
|||||||
assert.Equal(t, 0, writer.builder.Len())
|
assert.Equal(t, 0, writer.builder.Len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMustNil(t *testing.T) {
|
||||||
|
Must(nil)
|
||||||
|
}
|
||||||
|
|
||||||
func BenchmarkCopyByteSliceAppend(b *testing.B) {
|
func BenchmarkCopyByteSliceAppend(b *testing.B) {
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
var buf []byte
|
var buf []byte
|
||||||
|
|||||||
44
core/queue/balancedqueuepusher.go
Normal file
44
core/queue/balancedqueuepusher.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrNoAvailablePusher = errors.New("no available pusher")
|
||||||
|
|
||||||
|
type BalancedQueuePusher struct {
|
||||||
|
name string
|
||||||
|
pushers []Pusher
|
||||||
|
index uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewBalancedQueuePusher(pushers []Pusher) Pusher {
|
||||||
|
return &BalancedQueuePusher{
|
||||||
|
name: generateName(pushers),
|
||||||
|
pushers: pushers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pusher *BalancedQueuePusher) Name() string {
|
||||||
|
return pusher.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pusher *BalancedQueuePusher) Push(message string) error {
|
||||||
|
size := len(pusher.pushers)
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
index := atomic.AddUint64(&pusher.index, 1) % uint64(size)
|
||||||
|
target := pusher.pushers[index]
|
||||||
|
|
||||||
|
if err := target.Push(message); err != nil {
|
||||||
|
logx.Error(err)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrNoAvailablePusher
|
||||||
|
}
|
||||||
43
core/queue/balancedqueuepusher_test.go
Normal file
43
core/queue/balancedqueuepusher_test.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBalancedQueuePusher(t *testing.T) {
|
||||||
|
const numPushers = 100
|
||||||
|
var pushers []Pusher
|
||||||
|
var mockedPushers []*mockedPusher
|
||||||
|
for i := 0; i < numPushers; i++ {
|
||||||
|
p := &mockedPusher{
|
||||||
|
name: "pusher:" + strconv.Itoa(i),
|
||||||
|
}
|
||||||
|
pushers = append(pushers, p)
|
||||||
|
mockedPushers = append(mockedPushers, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pusher := NewBalancedQueuePusher(pushers)
|
||||||
|
assert.True(t, len(pusher.Name()) > 0)
|
||||||
|
|
||||||
|
for i := 0; i < numPushers*1000; i++ {
|
||||||
|
assert.Nil(t, pusher.Push("item"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var counts []int
|
||||||
|
for _, p := range mockedPushers {
|
||||||
|
counts = append(counts, p.count)
|
||||||
|
}
|
||||||
|
mean := calcMean(counts)
|
||||||
|
variance := calcVariance(mean, counts)
|
||||||
|
assert.True(t, variance < 100, fmt.Sprintf("too big variance - %.2f", variance))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBalancedQueuePusher_NoAvailable(t *testing.T) {
|
||||||
|
pusher := NewBalancedQueuePusher(nil)
|
||||||
|
assert.True(t, len(pusher.Name()) == 0)
|
||||||
|
assert.Equal(t, ErrNoAvailablePusher, pusher.Push("item"))
|
||||||
|
}
|
||||||
10
core/queue/consumer.go
Normal file
10
core/queue/consumer.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
type (
|
||||||
|
Consumer interface {
|
||||||
|
Consume(string) error
|
||||||
|
OnEvent(event interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
ConsumerFactory func() (Consumer, error)
|
||||||
|
)
|
||||||
6
core/queue/messagequeue.go
Normal file
6
core/queue/messagequeue.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
type MessageQueue interface {
|
||||||
|
Start()
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
31
core/queue/multiqueuepusher.go
Normal file
31
core/queue/multiqueuepusher.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import "github.com/tal-tech/go-zero/core/errorx"
|
||||||
|
|
||||||
|
type MultiQueuePusher struct {
|
||||||
|
name string
|
||||||
|
pushers []Pusher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMultiQueuePusher(pushers []Pusher) Pusher {
|
||||||
|
return &MultiQueuePusher{
|
||||||
|
name: generateName(pushers),
|
||||||
|
pushers: pushers,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pusher *MultiQueuePusher) Name() string {
|
||||||
|
return pusher.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pusher *MultiQueuePusher) Push(message string) error {
|
||||||
|
var batchError errorx.BatchError
|
||||||
|
|
||||||
|
for _, each := range pusher.pushers {
|
||||||
|
if err := each.Push(message); err != nil {
|
||||||
|
batchError.Add(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return batchError.Err()
|
||||||
|
}
|
||||||
39
core/queue/multiqueuepusher_test.go
Normal file
39
core/queue/multiqueuepusher_test.go
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMultiQueuePusher(t *testing.T) {
|
||||||
|
const numPushers = 100
|
||||||
|
var pushers []Pusher
|
||||||
|
var mockedPushers []*mockedPusher
|
||||||
|
for i := 0; i < numPushers; i++ {
|
||||||
|
p := &mockedPusher{
|
||||||
|
name: "pusher:" + strconv.Itoa(i),
|
||||||
|
}
|
||||||
|
pushers = append(pushers, p)
|
||||||
|
mockedPushers = append(mockedPushers, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
pusher := NewMultiQueuePusher(pushers)
|
||||||
|
assert.True(t, len(pusher.Name()) > 0)
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
_ = pusher.Push("item")
|
||||||
|
}
|
||||||
|
|
||||||
|
var counts []int
|
||||||
|
for _, p := range mockedPushers {
|
||||||
|
counts = append(counts, p.count)
|
||||||
|
}
|
||||||
|
mean := calcMean(counts)
|
||||||
|
variance := calcVariance(mean, counts)
|
||||||
|
assert.True(t, math.Abs(mean-1000*(1-failProba)) < 10)
|
||||||
|
assert.True(t, variance < 100, fmt.Sprintf("too big variance - %.2f", variance))
|
||||||
|
}
|
||||||
15
core/queue/producer.go
Normal file
15
core/queue/producer.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
type (
|
||||||
|
Producer interface {
|
||||||
|
AddListener(listener ProduceListener)
|
||||||
|
Produce() (string, bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
ProduceListener interface {
|
||||||
|
OnProducerPause()
|
||||||
|
OnProducerResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
ProducerFactory func() (Producer, error)
|
||||||
|
)
|
||||||
239
core/queue/queue.go
Normal file
239
core/queue/queue.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
"github.com/tal-tech/go-zero/core/rescue"
|
||||||
|
"github.com/tal-tech/go-zero/core/stat"
|
||||||
|
"github.com/tal-tech/go-zero/core/threading"
|
||||||
|
"github.com/tal-tech/go-zero/core/timex"
|
||||||
|
)
|
||||||
|
|
||||||
|
const queueName = "queue"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Queue struct {
|
||||||
|
name string
|
||||||
|
metrics *stat.Metrics
|
||||||
|
producerFactory ProducerFactory
|
||||||
|
producerRoutineGroup *threading.RoutineGroup
|
||||||
|
consumerFactory ConsumerFactory
|
||||||
|
consumerRoutineGroup *threading.RoutineGroup
|
||||||
|
producerCount int
|
||||||
|
consumerCount int
|
||||||
|
active int32
|
||||||
|
channel chan string
|
||||||
|
quit chan struct{}
|
||||||
|
listeners []Listener
|
||||||
|
eventLock sync.Mutex
|
||||||
|
eventChannels []chan interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
Listener interface {
|
||||||
|
OnPause()
|
||||||
|
OnResume()
|
||||||
|
}
|
||||||
|
|
||||||
|
Poller interface {
|
||||||
|
Name() string
|
||||||
|
Poll() string
|
||||||
|
}
|
||||||
|
|
||||||
|
Pusher interface {
|
||||||
|
Name() string
|
||||||
|
Push(string) error
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewQueue(producerFactory ProducerFactory, consumerFactory ConsumerFactory) *Queue {
|
||||||
|
queue := &Queue{
|
||||||
|
metrics: stat.NewMetrics(queueName),
|
||||||
|
producerFactory: producerFactory,
|
||||||
|
producerRoutineGroup: threading.NewRoutineGroup(),
|
||||||
|
consumerFactory: consumerFactory,
|
||||||
|
consumerRoutineGroup: threading.NewRoutineGroup(),
|
||||||
|
producerCount: runtime.NumCPU(),
|
||||||
|
consumerCount: runtime.NumCPU() << 1,
|
||||||
|
channel: make(chan string),
|
||||||
|
quit: make(chan struct{}),
|
||||||
|
}
|
||||||
|
queue.SetName(queueName)
|
||||||
|
|
||||||
|
return queue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) AddListener(listener Listener) {
|
||||||
|
queue.listeners = append(queue.listeners, listener)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) Broadcast(message interface{}) {
|
||||||
|
go func() {
|
||||||
|
queue.eventLock.Lock()
|
||||||
|
defer queue.eventLock.Unlock()
|
||||||
|
|
||||||
|
for _, channel := range queue.eventChannels {
|
||||||
|
channel <- message
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) SetName(name string) {
|
||||||
|
queue.name = name
|
||||||
|
queue.metrics.SetName(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) SetNumConsumer(count int) {
|
||||||
|
queue.consumerCount = count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) SetNumProducer(count int) {
|
||||||
|
queue.producerCount = count
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) Start() {
|
||||||
|
queue.startProducers(queue.producerCount)
|
||||||
|
queue.startConsumers(queue.consumerCount)
|
||||||
|
|
||||||
|
queue.producerRoutineGroup.Wait()
|
||||||
|
close(queue.channel)
|
||||||
|
queue.consumerRoutineGroup.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) Stop() {
|
||||||
|
close(queue.quit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) consume(eventChan chan interface{}) {
|
||||||
|
var consumer Consumer
|
||||||
|
|
||||||
|
for {
|
||||||
|
var err error
|
||||||
|
if consumer, err = queue.consumerFactory(); err != nil {
|
||||||
|
logx.Errorf("Error on creating consumer: %v", err)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message, ok := <-queue.channel:
|
||||||
|
if ok {
|
||||||
|
queue.consumeOne(consumer, message)
|
||||||
|
} else {
|
||||||
|
logx.Info("Task channel was closed, quitting consumer...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case event := <-eventChan:
|
||||||
|
consumer.OnEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) consumeOne(consumer Consumer, message string) {
|
||||||
|
threading.RunSafe(func() {
|
||||||
|
startTime := timex.Now()
|
||||||
|
defer func() {
|
||||||
|
duration := timex.Since(startTime)
|
||||||
|
queue.metrics.Add(stat.Task{
|
||||||
|
Duration: duration,
|
||||||
|
})
|
||||||
|
logx.WithDuration(duration).Infof("%s", message)
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := consumer.Consume(message); err != nil {
|
||||||
|
logx.Errorf("Error occurred while consuming %v: %v", message, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) pause() {
|
||||||
|
for _, listener := range queue.listeners {
|
||||||
|
listener.OnPause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) produce() {
|
||||||
|
var producer Producer
|
||||||
|
|
||||||
|
for {
|
||||||
|
var err error
|
||||||
|
if producer, err = queue.producerFactory(); err != nil {
|
||||||
|
logx.Errorf("Error on creating producer: %v", err)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddInt32(&queue.active, 1)
|
||||||
|
producer.AddListener(routineListener{
|
||||||
|
queue: queue,
|
||||||
|
})
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-queue.quit:
|
||||||
|
logx.Info("Quitting producer")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if v, ok := queue.produceOne(producer); ok {
|
||||||
|
queue.channel <- v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) produceOne(producer Producer) (string, bool) {
|
||||||
|
// avoid panic quit the producer, just log it and continue
|
||||||
|
defer rescue.Recover()
|
||||||
|
|
||||||
|
return producer.Produce()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) resume() {
|
||||||
|
for _, listener := range queue.listeners {
|
||||||
|
listener.OnResume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) startConsumers(number int) {
|
||||||
|
for i := 0; i < number; i++ {
|
||||||
|
eventChan := make(chan interface{})
|
||||||
|
queue.eventLock.Lock()
|
||||||
|
queue.eventChannels = append(queue.eventChannels, eventChan)
|
||||||
|
queue.eventLock.Unlock()
|
||||||
|
queue.consumerRoutineGroup.Run(func() {
|
||||||
|
queue.consume(eventChan)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (queue *Queue) startProducers(number int) {
|
||||||
|
for i := 0; i < number; i++ {
|
||||||
|
queue.producerRoutineGroup.Run(func() {
|
||||||
|
queue.produce()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type routineListener struct {
|
||||||
|
queue *Queue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl routineListener) OnProducerPause() {
|
||||||
|
if atomic.AddInt32(&rl.queue.active, -1) <= 0 {
|
||||||
|
rl.queue.pause()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rl routineListener) OnProducerResume() {
|
||||||
|
if atomic.AddInt32(&rl.queue.active, 1) == 1 {
|
||||||
|
rl.queue.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
94
core/queue/queue_test.go
Normal file
94
core/queue/queue_test.go
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
consumers = 4
|
||||||
|
rounds = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestQueue(t *testing.T) {
|
||||||
|
producer := newMockedProducer(rounds)
|
||||||
|
consumer := newMockedConsumer()
|
||||||
|
consumer.wait.Add(consumers)
|
||||||
|
q := NewQueue(func() (Producer, error) {
|
||||||
|
return producer, nil
|
||||||
|
}, func() (Consumer, error) {
|
||||||
|
return consumer, nil
|
||||||
|
})
|
||||||
|
q.AddListener(new(mockedListener))
|
||||||
|
q.SetName("mockqueue")
|
||||||
|
q.SetNumConsumer(consumers)
|
||||||
|
q.SetNumProducer(1)
|
||||||
|
q.pause()
|
||||||
|
q.resume()
|
||||||
|
go func() {
|
||||||
|
producer.wait.Wait()
|
||||||
|
q.Stop()
|
||||||
|
}()
|
||||||
|
q.Start()
|
||||||
|
assert.Equal(t, int32(rounds), atomic.LoadInt32(&consumer.count))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedConsumer struct {
|
||||||
|
count int32
|
||||||
|
events int32
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockedConsumer() *mockedConsumer {
|
||||||
|
return new(mockedConsumer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConsumer) Consume(string) error {
|
||||||
|
atomic.AddInt32(&c.count, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *mockedConsumer) OnEvent(interface{}) {
|
||||||
|
if atomic.AddInt32(&c.events, 1) <= consumers {
|
||||||
|
c.wait.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedProducer struct {
|
||||||
|
total int32
|
||||||
|
count int32
|
||||||
|
wait sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockedProducer(total int32) *mockedProducer {
|
||||||
|
p := new(mockedProducer)
|
||||||
|
p.total = total
|
||||||
|
p.wait.Add(int(total))
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockedProducer) AddListener(listener ProduceListener) {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockedProducer) Produce() (string, bool) {
|
||||||
|
if atomic.AddInt32(&p.count, 1) <= p.total {
|
||||||
|
p.wait.Done()
|
||||||
|
return "item", true
|
||||||
|
} else {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedListener struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *mockedListener) OnPause() {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *mockedListener) OnResume() {
|
||||||
|
}
|
||||||
12
core/queue/util.go
Normal file
12
core/queue/util.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
func generateName(pushers []Pusher) string {
|
||||||
|
names := make([]string, len(pushers))
|
||||||
|
for i, pusher := range pushers {
|
||||||
|
names[i] = pusher.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(names, ",")
|
||||||
|
}
|
||||||
77
core/queue/util_test.go
Normal file
77
core/queue/util_test.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package queue
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
"github.com/tal-tech/go-zero/core/mathx"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
proba = mathx.NewProba()
|
||||||
|
failProba = 0.01
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logx.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateName(t *testing.T) {
|
||||||
|
pushers := []Pusher{
|
||||||
|
&mockedPusher{name: "first"},
|
||||||
|
&mockedPusher{name: "second"},
|
||||||
|
&mockedPusher{name: "third"},
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, "first,second,third", generateName(pushers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGenerateNameNil(t *testing.T) {
|
||||||
|
var pushers []Pusher
|
||||||
|
assert.Equal(t, "", generateName(pushers))
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcMean(vals []int) float64 {
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var result float64
|
||||||
|
for _, val := range vals {
|
||||||
|
result += float64(val)
|
||||||
|
}
|
||||||
|
return result / float64(len(vals))
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcVariance(mean float64, vals []int) float64 {
|
||||||
|
if len(vals) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var result float64
|
||||||
|
for _, val := range vals {
|
||||||
|
result += math.Pow(float64(val)-mean, 2)
|
||||||
|
}
|
||||||
|
return result / float64(len(vals))
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockedPusher struct {
|
||||||
|
name string
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockedPusher) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *mockedPusher) Push(s string) error {
|
||||||
|
if proba.TrueOnProba(failProba) {
|
||||||
|
return errors.New("dummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
p.count++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/iox"
|
"github.com/tal-tech/go-zero/core/iox"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -24,17 +24,17 @@ var (
|
|||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cpus, err := perCpuUsage()
|
cpus, err := perCpuUsage()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
cores = uint64(len(cpus))
|
cores = uint64(len(cpus))
|
||||||
|
|
||||||
sets, err := cpuSets()
|
sets, err := cpuSets()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
quota = float64(len(sets))
|
quota = float64(len(sets))
|
||||||
cq, err := cpuQuota()
|
cq, err := cpuQuota()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
if cq != -1 {
|
if cq != -1 {
|
||||||
period, err := cpuPeriod()
|
period, err := cpuPeriod()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
|
|
||||||
limit := float64(cq) / float64(period)
|
limit := float64(cq) / float64(period)
|
||||||
if limit < quota {
|
if limit < quota {
|
||||||
@@ -44,10 +44,10 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
preSystem, err = systemCpuUsage()
|
preSystem, err = systemCpuUsage()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
|
|
||||||
preTotal, err = totalCpuUsage()
|
preTotal, err = totalCpuUsage()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RefreshCpu() uint64 {
|
func RefreshCpu() uint64 {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/collection"
|
"github.com/tal-tech/go-zero/core/collection"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/proc"
|
"github.com/tal-tech/go-zero/core/proc"
|
||||||
"github.com/tal-tech/go-zero/core/stat"
|
"github.com/tal-tech/go-zero/core/stat"
|
||||||
@@ -33,7 +32,7 @@ type delayTask struct {
|
|||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
timingWheel, err = collection.NewTimingWheel(time.Second, timingWheelSlots, clean)
|
timingWheel, err = collection.NewTimingWheel(time.Second, timingWheelSlots, clean)
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
|
|
||||||
proc.AddShutdownListener(func() {
|
proc.AddShutdownListener(func() {
|
||||||
timingWheel.Drain(clean)
|
timingWheel.Drain(clean)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package sysx
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/stringx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var hostname string
|
var hostname string
|
||||||
@@ -11,7 +11,9 @@ var hostname string
|
|||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
hostname, err = os.Hostname()
|
hostname, err = os.Hostname()
|
||||||
lang.Must(err)
|
if err != nil {
|
||||||
|
hostname = stringx.RandId()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Hostname() string {
|
func Hostname() string {
|
||||||
|
|||||||
Binary file not shown.
31
doc/goctl.md
31
doc/goctl.md
@@ -3,8 +3,8 @@
|
|||||||
## goctl用途
|
## goctl用途
|
||||||
* 定义api请求
|
* 定义api请求
|
||||||
* 根据定义的api自动生成golang(后端), java(iOS & Android), typescript(web & 晓程序),dart(flutter)
|
* 根据定义的api自动生成golang(后端), java(iOS & Android), typescript(web & 晓程序),dart(flutter)
|
||||||
* 生成MySQL CURD (https://goctl.xiaoheiban.cn)
|
* 生成MySQL CURD+Cache
|
||||||
* 生成MongoDB CURD (https://goctl.xiaoheiban.cn)
|
* 生成MongoDB CURD+Cache
|
||||||
|
|
||||||
## goctl使用说明
|
## goctl使用说明
|
||||||
#### goctl参数说明
|
#### goctl参数说明
|
||||||
@@ -179,23 +179,31 @@ service user-api {
|
|||||||
* 在定义的get/post/put/delete等请求的handler和logic里增加处理业务逻辑的代码
|
* 在定义的get/post/put/delete等请求的handler和logic里增加处理业务逻辑的代码
|
||||||
|
|
||||||
#### 根据定义好的api文件生成java代码
|
#### 根据定义好的api文件生成java代码
|
||||||
`goctl api java -api user/user.api -dir ./src`
|
```shell
|
||||||
|
goctl api java -api user/user.api -dir ./src
|
||||||
|
```
|
||||||
|
|
||||||
#### 根据定义好的api文件生成typescript代码
|
#### 根据定义好的api文件生成typescript代码
|
||||||
`goctl api ts -api user/user.api -dir ./src -webapi ***`
|
```shell
|
||||||
|
goctl api ts -api user/user.api -dir ./src -webapi ***
|
||||||
|
|
||||||
ts需要指定webapi所在目录
|
ts需要指定webapi所在目录
|
||||||
|
```
|
||||||
|
|
||||||
#### 根据定义好的api文件生成Dart代码
|
#### 根据定义好的api文件生成Dart代码
|
||||||
`goctl api dart -api user/user.api -dir ./src`
|
```shell
|
||||||
|
goctl api dart -api user/user.api -dir ./src
|
||||||
|
```
|
||||||
|
|
||||||
## 根据定义好的简单go文件生成mongo代码文件(仅限golang使用)
|
## 根据定义好的简单go文件生成mongo代码文件(仅限golang使用)
|
||||||
`goctl model mongo -src {{yourDir}}/xiao/service/xhb/user/model/usermodel.go -cache yes`
|
```shell
|
||||||
|
goctl model mongo -src {{yourDir}}/xiao/service/xhb/user/model/usermodel.go -cache yes
|
||||||
|
|
||||||
-src需要提供简单的usermodel.go文件,里面只需要提供一个结构体即可
|
-src需要提供简单的usermodel.go文件,里面只需要提供一个结构体即可
|
||||||
-cache 控制是否需要缓存 yes=需要 no=不需要
|
-cache 控制是否需要缓存 yes=需要 no=不需要
|
||||||
src 示例代码如下
|
src 示例代码如下
|
||||||
```
|
```
|
||||||
|
```go
|
||||||
package model
|
package model
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
@@ -262,4 +270,3 @@ type User struct {
|
|||||||
└── test.proto
|
└── test.proto
|
||||||
```
|
```
|
||||||
- 注意 :目前rpc目录生成的proto文件暂不支持import外部proto文件
|
- 注意 :目前rpc目录生成的proto文件暂不支持import外部proto文件
|
||||||
* 如有不理解的地方,随时问Kim/Kevin
|
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/breaker"
|
"github.com/tal-tech/go-zero/core/breaker"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/lang"
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"gopkg.in/cheggaaa/pb.v1"
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ func main() {
|
|||||||
|
|
||||||
gb := breaker.NewBreaker()
|
gb := breaker.NewBreaker()
|
||||||
fp, err := os.Create("result.csv")
|
fp, err := os.Create("result.csv")
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
fmt.Fprintln(fp, "seconds,state,googleCalls,netflixCalls")
|
fmt.Fprintln(fp, "seconds,state,googleCalls,netflixCalls")
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/discov"
|
"github.com/tal-tech/go-zero/core/discov"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
sub, err := discov.NewSubscriber([]string{"etcd.discovery:2379"}, "028F2C35852D", discov.Exclusive())
|
sub, err := discov.NewSubscriber([]string{"etcd.discovery:2379"}, "028F2C35852D", discov.Exclusive())
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
|
|
||||||
ticker := time.NewTicker(time.Second * 3)
|
ticker := time.NewTicker(time.Second * 3)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package svc
|
|||||||
import "github.com/tal-tech/go-zero/rpcx"
|
import "github.com/tal-tech/go-zero/rpcx"
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Client *rpcx.RpcClient
|
Client rpcx.Client
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,5 +3,5 @@ package svc
|
|||||||
import "github.com/tal-tech/go-zero/rpcx"
|
import "github.com/tal-tech/go-zero/rpcx"
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Client *rpcx.RpcClient
|
Client rpcx.Client
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/lang"
|
||||||
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/threading"
|
"github.com/tal-tech/go-zero/core/threading"
|
||||||
"gopkg.in/cheggaaa/pb.v1"
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
)
|
)
|
||||||
@@ -119,14 +120,14 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
fp, err := os.Create("result.csv")
|
fp, err := os.Create("result.csv")
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
fmt.Fprintln(fp, "seconds,goodOk,goodFail,goodReject,goodErrs,goodUnknowns,goodDropRatio,"+
|
fmt.Fprintln(fp, "seconds,goodOk,goodFail,goodReject,goodErrs,goodUnknowns,goodDropRatio,"+
|
||||||
"heavyOk,heavyFail,heavyReject,heavyErrs,heavyUnknowns,heavyDropRatio")
|
"heavyOk,heavyFail,heavyReject,heavyErrs,heavyUnknowns,heavyDropRatio")
|
||||||
|
|
||||||
var gm, hm metric
|
var gm, hm metric
|
||||||
dur, err := time.ParseDuration(*duration)
|
dur, err := time.ParseDuration(*duration)
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
done := make(chan lang.PlaceholderType)
|
done := make(chan lang.PlaceholderType)
|
||||||
group := threading.NewRoutineGroup()
|
group := threading.NewRoutineGroup()
|
||||||
group.RunSafe(func() {
|
group.RunSafe(func() {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/collection"
|
"github.com/tal-tech/go-zero/core/collection"
|
||||||
"github.com/tal-tech/go-zero/core/executors"
|
"github.com/tal-tech/go-zero/core/executors"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/syncx"
|
"github.com/tal-tech/go-zero/core/syncx"
|
||||||
"gopkg.in/cheggaaa/pb.v1"
|
"gopkg.in/cheggaaa/pb.v1"
|
||||||
)
|
)
|
||||||
@@ -47,7 +47,7 @@ func main() {
|
|||||||
lessWriter = executors.NewLessExecutor(interval * total / 100)
|
lessWriter = executors.NewLessExecutor(interval * total / 100)
|
||||||
|
|
||||||
fp, err := os.Create("result.csv")
|
fp, err := os.Create("result.csv")
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
fmt.Fprintln(fp, "second,maxFlight,flying,agressiveAvgFlying,lazyAvgFlying,bothAvgFlying")
|
fmt.Fprintln(fp, "second,maxFlight,flying,agressiveAvgFlying,lazyAvgFlying,bothAvgFlying")
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/fx"
|
"github.com/tal-tech/go-zero/core/fx"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -27,7 +27,7 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
fp, err := os.Create("result.csv")
|
fp, err := os.Create("result.csv")
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
defer fp.Close()
|
defer fp.Close()
|
||||||
fmt.Fprintln(fp, "seconds,total,pass,fail,drop")
|
fmt.Fprintln(fp, "seconds,total,pass,fail,drop")
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/executors"
|
"github.com/tal-tech/go-zero/core/executors"
|
||||||
@@ -8,7 +9,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
exeutor := executors.NewBulkExecutor(func(items []interface{}) {
|
exeutor := executors.NewBulkExecutor(func(items []interface{}) {
|
||||||
println(len(items))
|
fmt.Println(len(items))
|
||||||
}, executors.WithBulkTasks(10))
|
}, executors.WithBulkTasks(10))
|
||||||
for {
|
for {
|
||||||
exeutor.Add(1)
|
exeutor.Add(1)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
configFile = flag.String("f", "config.json", "the config file")
|
configFile = flag.String("f", "config.json", "the config file")
|
||||||
client *rpcx.RpcClient
|
client rpcx.Client
|
||||||
)
|
)
|
||||||
|
|
||||||
func handle(w http.ResponseWriter, r *http.Request) {
|
func handle(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
PortalServer struct {
|
PortalServer struct {
|
||||||
userRpc *rpcx.RpcClient
|
userRpc rpcx.Client
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewPortalServer(client *rpcx.RpcClient) *PortalServer {
|
func NewPortalServer(client rpcx.Client) *PortalServer {
|
||||||
return &PortalServer{
|
return &PortalServer{
|
||||||
userRpc: client,
|
userRpc: client,
|
||||||
}
|
}
|
||||||
|
|||||||
23
readme.md
23
readme.md
@@ -1,6 +1,8 @@
|
|||||||
# go-zero项目介绍
|
# go-zero项目介绍
|
||||||
|
|
||||||

|
[](https://github.com/tal-tech/go-zero/actions)
|
||||||
|
[](https://codecov.io/gh/tal-tech/go-zero)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
## 1. go-zero框架背景
|
## 1. go-zero框架背景
|
||||||
|
|
||||||
@@ -144,25 +146,24 @@ go-zero是一个集成了各种工程实践的包含web和rpc框架,有如下
|
|||||||
|
|
||||||
8 directories, 9 files
|
8 directories, 9 files
|
||||||
```
|
```
|
||||||
|
|
||||||
生成的代码可以直接运行:
|
生成的代码可以直接运行:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
cd greet
|
cd greet
|
||||||
go run greet.go -f etc/greet-api.json
|
go run greet.go -f etc/greet-api.json
|
||||||
```
|
```
|
||||||
|
|
||||||
默认侦听在8888端口(可以在配置文件里修改),可以通过curl请求:
|
默认侦听在8888端口(可以在配置文件里修改),可以通过curl请求:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
➜ go-zero git:(master) curl -w "\ncode: %{http_code}\n" http://localhost:8888/greet/from/kevin
|
➜ go-zero git:(master) curl -w "\ncode: %{http_code}\n" http://localhost:8888/greet/from/kevin
|
||||||
{"code":0}
|
{"code":0}
|
||||||
code: 200
|
code: 200
|
||||||
```
|
```
|
||||||
|
|
||||||
编写业务代码:
|
编写业务代码:
|
||||||
|
|
||||||
* 可以在servicecontext.go里面传递依赖给logic,比如mysql, redis等
|
* 可以在servicecontext.go里面传递依赖给logic,比如mysql, redis等
|
||||||
* 在api定义的get/post/put/delete等请求对应的logic里增加业务处理逻辑
|
* 在api定义的get/post/put/delete等请求对应的logic里增加业务处理逻辑
|
||||||
|
|
||||||
4. 可以根据api文件生成前端需要的Java, TypeScript, Dart, JavaScript代码
|
4. 可以根据api文件生成前端需要的Java, TypeScript, Dart, JavaScript代码
|
||||||
@@ -173,6 +174,10 @@ go-zero是一个集成了各种工程实践的包含web和rpc框架,有如下
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 8. 文档
|
||||||
|
|
||||||
|
* [goctl使用帮助](doc/goctl.md)
|
||||||
|
|
||||||
### 微信交流群
|
### 微信交流群
|
||||||
|
|
||||||
添加我的微信:kevwan,请注明go-zero,我拉进go-zero社区群🤝
|
添加我的微信:kevwan,请注明go-zero,我拉进go-zero社区群🤝
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal"
|
"github.com/tal-tech/go-zero/rest/token"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -43,7 +43,7 @@ func Authorize(secret string, opts ...AuthorizeOption) func(http.Handler) http.H
|
|||||||
opt(&authOpts)
|
opt(&authOpts)
|
||||||
}
|
}
|
||||||
|
|
||||||
parser := internal.NewTokenParser()
|
parser := token.NewTokenParser()
|
||||||
return func(next http.Handler) http.Handler {
|
return func(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
token, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
|
token, err := parser.ParseToken(r, secret, authOpts.PrevSecret)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/tal-tech/go-zero/core/breaker"
|
"github.com/tal-tech/go-zero/core/breaker"
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/stat"
|
"github.com/tal-tech/go-zero/core/stat"
|
||||||
"github.com/tal-tech/go-zero/rest/internal"
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal/security"
|
"github.com/tal-tech/go-zero/rest/internal/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ func BreakerHandler(method, path string, metrics *stat.Metrics) func(http.Handle
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
metrics.AddDrop()
|
metrics.AddDrop()
|
||||||
logx.Errorf("[http] dropped, %s - %s - %s",
|
logx.Errorf("[http] dropped, %s - %s - %s",
|
||||||
r.RequestURI, internal.GetRemoteAddr(r), r.UserAgent())
|
r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent())
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/timex"
|
"github.com/tal-tech/go-zero/core/timex"
|
||||||
"github.com/tal-tech/go-zero/core/utils"
|
"github.com/tal-tech/go-zero/core/utils"
|
||||||
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal"
|
"github.com/tal-tech/go-zero/rest/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -112,10 +113,10 @@ func logBrief(r *http.Request, code int, timer *utils.ElapsedTimer, logs *intern
|
|||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
duration := timer.Duration()
|
duration := timer.Duration()
|
||||||
buf.WriteString(fmt.Sprintf("%d - %s - %s - %s - %s",
|
buf.WriteString(fmt.Sprintf("%d - %s - %s - %s - %s",
|
||||||
code, r.RequestURI, internal.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration)))
|
code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration)))
|
||||||
if duration > slowThreshold {
|
if duration > slowThreshold {
|
||||||
logx.Slowf("[HTTP] %d - %s - %s - %s - slowcall(%s)",
|
logx.Slowf("[HTTP] %d - %s - %s - %s - slowcall(%s)",
|
||||||
code, r.RequestURI, internal.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration))
|
code, r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent(), timex.ReprOfDuration(duration))
|
||||||
}
|
}
|
||||||
|
|
||||||
ok := isOkResponse(code)
|
ok := isOkResponse(code)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"github.com/tal-tech/go-zero/core/load"
|
"github.com/tal-tech/go-zero/core/load"
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/core/stat"
|
"github.com/tal-tech/go-zero/core/stat"
|
||||||
"github.com/tal-tech/go-zero/rest/internal"
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal/security"
|
"github.com/tal-tech/go-zero/rest/internal/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ func SheddingHandler(shedder load.Shedder, metrics *stat.Metrics) func(http.Hand
|
|||||||
metrics.AddDrop()
|
metrics.AddDrop()
|
||||||
sheddingStat.IncrementDrop()
|
sheddingStat.IncrementDrop()
|
||||||
logx.Errorf("[http] dropped, %s - %s - %s",
|
logx.Errorf("[http] dropped, %s - %s - %s",
|
||||||
r.RequestURI, internal.GetRemoteAddr(r), r.UserAgent())
|
r.RequestURI, httpx.GetRemoteAddr(r), r.UserAgent())
|
||||||
w.WriteHeader(http.StatusServiceUnavailable)
|
w.WriteHeader(http.StatusServiceUnavailable)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ func Parse(r *http.Request, v interface{}) error {
|
|||||||
|
|
||||||
// Parses the form request.
|
// Parses the form request.
|
||||||
func ParseForm(r *http.Request, v interface{}) error {
|
func ParseForm(r *http.Request, v interface{}) error {
|
||||||
if strings.Index(r.Header.Get(ContentType), multipartFormData) != -1 {
|
if strings.Contains(r.Header.Get(ContentType), multipartFormData) {
|
||||||
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
if err := r.ParseMultipartForm(maxMemory); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -84,7 +84,6 @@ func ParseHeader(headerValue string) map[string]string {
|
|||||||
// Parses the post request which contains json in body.
|
// Parses the post request which contains json in body.
|
||||||
func ParseJsonBody(r *http.Request, v interface{}) error {
|
func ParseJsonBody(r *http.Request, v interface{}) error {
|
||||||
var reader io.Reader
|
var reader io.Reader
|
||||||
|
|
||||||
if withJsonBody(r) {
|
if withJsonBody(r) {
|
||||||
reader = io.LimitReader(r.Body, maxBodyLen)
|
reader = io.LimitReader(r.Body, maxBodyLen)
|
||||||
} else {
|
} else {
|
||||||
@@ -107,5 +106,5 @@ func ParsePath(r *http.Request, v interface{}) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withJsonBody(r *http.Request) bool {
|
func withJsonBody(r *http.Request) bool {
|
||||||
return r.ContentLength > 0 && strings.Index(r.Header.Get(ContentType), ApplicationJson) != -1
|
return r.ContentLength > 0 && strings.Contains(r.Header.Get(ContentType), ApplicationJson)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package internal
|
package httpx
|
||||||
|
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package internal
|
package httpx
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -16,4 +16,3 @@ func TestGetRemoteAddr(t *testing.T) {
|
|||||||
r.Header.Set(xForwardFor, host)
|
r.Header.Set(xForwardFor, host)
|
||||||
assert.Equal(t, host, GetRemoteAddr(r))
|
assert.Equal(t, host, GetRemoteAddr(r))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
)
|
)
|
||||||
|
|
||||||
const LogContext = "request_logs"
|
const LogContext = "request_logs"
|
||||||
@@ -79,5 +80,5 @@ func formatf(r *http.Request, format string, v ...interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func formatWithReq(r *http.Request, v string) string {
|
func formatWithReq(r *http.Request, v string) string {
|
||||||
return fmt.Sprintf("(%s - %s) %s", r.RequestURI, GetRemoteAddr(r), v)
|
return fmt.Sprintf("(%s - %s) %s", r.RequestURI, httpx.GetRemoteAddr(r), v)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/tal-tech/go-zero/rest/httpx"
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal/router"
|
"github.com/tal-tech/go-zero/rest/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestWithMiddleware(t *testing.T) {
|
func TestWithMiddleware(t *testing.T) {
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
"github.com/tal-tech/go-zero/rest/handler"
|
"github.com/tal-tech/go-zero/rest/handler"
|
||||||
"github.com/tal-tech/go-zero/rest/httpx"
|
"github.com/tal-tech/go-zero/rest/httpx"
|
||||||
"github.com/tal-tech/go-zero/rest/internal"
|
"github.com/tal-tech/go-zero/rest/internal"
|
||||||
"github.com/tal-tech/go-zero/rest/internal/router"
|
"github.com/tal-tech/go-zero/rest/router"
|
||||||
)
|
)
|
||||||
|
|
||||||
// use 1000m to represent 100%
|
// use 1000m to represent 100%
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package internal
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package internal
|
package token
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -10,11 +10,22 @@ import (
|
|||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RpcClient struct {
|
var (
|
||||||
client internal.Client
|
WithDialOption = internal.WithDialOption
|
||||||
}
|
WithTimeout = internal.WithTimeout
|
||||||
|
)
|
||||||
|
|
||||||
func MustNewClient(c RpcClientConf, options ...internal.ClientOption) *RpcClient {
|
type (
|
||||||
|
Client interface {
|
||||||
|
Conn() *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
RpcClient struct {
|
||||||
|
client Client
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func MustNewClient(c RpcClientConf, options ...internal.ClientOption) Client {
|
||||||
cli, err := NewClient(c, options...)
|
cli, err := NewClient(c, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
@@ -23,20 +34,20 @@ func MustNewClient(c RpcClientConf, options ...internal.ClientOption) *RpcClient
|
|||||||
return cli
|
return cli
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(c RpcClientConf, options ...internal.ClientOption) (*RpcClient, error) {
|
func NewClient(c RpcClientConf, options ...internal.ClientOption) (Client, error) {
|
||||||
var opts []internal.ClientOption
|
var opts []internal.ClientOption
|
||||||
if c.HasCredential() {
|
if c.HasCredential() {
|
||||||
opts = append(opts, internal.WithDialOption(grpc.WithPerRPCCredentials(&auth.Credential{
|
opts = append(opts, WithDialOption(grpc.WithPerRPCCredentials(&auth.Credential{
|
||||||
App: c.App,
|
App: c.App,
|
||||||
Token: c.Token,
|
Token: c.Token,
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
if c.Timeout > 0 {
|
if c.Timeout > 0 {
|
||||||
opts = append(opts, internal.WithTimeout(time.Duration(c.Timeout)*time.Millisecond))
|
opts = append(opts, WithTimeout(time.Duration(c.Timeout)*time.Millisecond))
|
||||||
}
|
}
|
||||||
opts = append(opts, options...)
|
opts = append(opts, options...)
|
||||||
|
|
||||||
var client internal.Client
|
var client Client
|
||||||
var err error
|
var err error
|
||||||
if len(c.Server) > 0 {
|
if len(c.Server) > 0 {
|
||||||
client, err = internal.NewDirectClient(c.Server, opts...)
|
client, err = internal.NewDirectClient(c.Server, opts...)
|
||||||
@@ -52,7 +63,7 @@ func NewClient(c RpcClientConf, options ...internal.ClientOption) (*RpcClient, e
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientNoAuth(c discov.EtcdConf) (*RpcClient, error) {
|
func NewClientNoAuth(c discov.EtcdConf) (Client, error) {
|
||||||
client, err := internal.NewDiscovClient(c.Hosts, c.Key)
|
client, err := internal.NewDiscovClient(c.Hosts, c.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
Name = "p2c_ewma"
|
Name = "p2c_ewma"
|
||||||
decayTime = int64(time.Millisecond * 600)
|
decayTime = int64(time.Second * 10) // default value from finagle
|
||||||
forcePick = int64(time.Second)
|
forcePick = int64(time.Second)
|
||||||
initSuccess = 1000
|
initSuccess = 1000
|
||||||
throttleSuccess = initSuccess / 2
|
throttleSuccess = initSuccess / 2
|
||||||
|
|||||||
@@ -18,10 +18,6 @@ type (
|
|||||||
}
|
}
|
||||||
|
|
||||||
ClientOption func(options *ClientOptions)
|
ClientOption func(options *ClientOptions)
|
||||||
|
|
||||||
Client interface {
|
|
||||||
Conn() *grpc.ClientConn
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func WithDialOption(opt grpc.DialOption) ClientOption {
|
func WithDialOption(opt grpc.DialOption) ClientOption {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package internal
|
package serverinterceptors
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
type RpcProxy struct {
|
type RpcProxy struct {
|
||||||
backend string
|
backend string
|
||||||
clients map[string]*RpcClient
|
clients map[string]Client
|
||||||
options []internal.ClientOption
|
options []internal.ClientOption
|
||||||
sharedCalls syncx.SharedCalls
|
sharedCalls syncx.SharedCalls
|
||||||
lock sync.Mutex
|
lock sync.Mutex
|
||||||
@@ -21,7 +21,7 @@ type RpcProxy struct {
|
|||||||
func NewRpcProxy(backend string, opts ...internal.ClientOption) *RpcProxy {
|
func NewRpcProxy(backend string, opts ...internal.ClientOption) *RpcProxy {
|
||||||
return &RpcProxy{
|
return &RpcProxy{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
clients: make(map[string]*RpcClient),
|
clients: make(map[string]Client),
|
||||||
options: opts,
|
options: opts,
|
||||||
sharedCalls: syncx.NewSharedCalls(),
|
sharedCalls: syncx.NewSharedCalls(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,8 +117,8 @@ func setupInterceptors(server internal.Server, c RpcServerConf, metrics *stat.Me
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
server.AddStreamInterceptors(internal.StreamAuthorizeInterceptor(authenticator))
|
server.AddStreamInterceptors(serverinterceptors.StreamAuthorizeInterceptor(authenticator))
|
||||||
server.AddUnaryInterceptors(internal.UnaryAuthorizeInterceptor(authenticator))
|
server.AddUnaryInterceptors(serverinterceptors.UnaryAuthorizeInterceptor(authenticator))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
@@ -32,8 +32,8 @@ func DartCommand(c *cli.Context) error {
|
|||||||
dir = dir + "/"
|
dir = dir + "/"
|
||||||
}
|
}
|
||||||
api.Info.Title = strings.Replace(apiFile, ".api", "", -1)
|
api.Info.Title = strings.Replace(apiFile, ".api", "", -1)
|
||||||
lang.Must(genData(dir+"data/", api))
|
logx.Must(genData(dir+"data/", api))
|
||||||
lang.Must(genApi(dir+"api/", api))
|
logx.Must(genApi(dir+"api/", api))
|
||||||
lang.Must(genVars(dir + "vars/"))
|
logx.Must(genVars(dir + "vars/"))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package gogen
|
package gogen
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@@ -13,7 +14,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
|
apiformat "github.com/tal-tech/go-zero/tools/goctl/api/format"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
apiutil "github.com/tal-tech/go-zero/tools/goctl/api/util"
|
||||||
@@ -44,17 +45,18 @@ func GoCommand(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lang.Must(util.MkdirIfNotExist(dir))
|
logx.Must(util.MkdirIfNotExist(dir))
|
||||||
lang.Must(genEtc(dir, api))
|
logx.Must(genEtc(dir, api))
|
||||||
lang.Must(genConfig(dir))
|
logx.Must(genConfig(dir))
|
||||||
lang.Must(genMain(dir, api))
|
logx.Must(genMain(dir, api))
|
||||||
lang.Must(genServiceContext(dir, api))
|
logx.Must(genServiceContext(dir, api))
|
||||||
lang.Must(genTypes(dir, api))
|
logx.Must(genTypes(dir, api))
|
||||||
lang.Must(genHandlers(dir, api))
|
logx.Must(genHandlers(dir, api))
|
||||||
lang.Must(genRoutes(dir, api))
|
logx.Must(genRoutes(dir, api))
|
||||||
lang.Must(genLogic(dir, api))
|
logx.Must(genLogic(dir, api))
|
||||||
// it does not work
|
// it does not work
|
||||||
format(dir)
|
format(dir)
|
||||||
|
createGoModFileIfNeed(dir)
|
||||||
|
|
||||||
if err := backupAndSweep(apiFile); err != nil {
|
if err := backupAndSweep(apiFile); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -98,7 +100,7 @@ func format(dir string) {
|
|||||||
cmd := exec.Command("go", "fmt", "./"+dir+"...")
|
cmd := exec.Command("go", "fmt", "./"+dir+"...")
|
||||||
_, err := cmd.CombinedOutput()
|
_, err := cmd.CombinedOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
print(err.Error())
|
fmt.Println(err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,3 +133,43 @@ func sweep() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createGoModFileIfNeed(dir string) {
|
||||||
|
absDir, err := filepath.Abs(dir)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempPath = absDir
|
||||||
|
var hasGoMod = false
|
||||||
|
for {
|
||||||
|
if tempPath == filepath.Dir(tempPath) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tempPath = filepath.Dir(tempPath)
|
||||||
|
if util.FileExists(filepath.Join(tempPath, goModeIdentifier)) {
|
||||||
|
tempPath = filepath.Dir(tempPath)
|
||||||
|
hasGoMod = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !hasGoMod {
|
||||||
|
gopath := os.Getenv("GOPATH")
|
||||||
|
parent := path.Join(gopath, "src")
|
||||||
|
pos := strings.Index(absDir, parent)
|
||||||
|
if pos < 0 {
|
||||||
|
moduleName := absDir[len(filepath.Dir(absDir))+1:]
|
||||||
|
cmd := exec.Command("go", "mod", "init", moduleName)
|
||||||
|
cmd.Dir = dir
|
||||||
|
var stdout, stderr bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
err := cmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
outStr, errStr := string(stdout.Bytes()), string(stderr.Bytes())
|
||||||
|
fmt.Printf(outStr + "\n" + errStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,10 +23,14 @@ func getParentPackage(dir string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
absDir = strings.ReplaceAll(absDir, `\`, `/`)
|
||||||
var rootPath string
|
var rootPath string
|
||||||
var tempPath = absDir
|
var tempPath = absDir
|
||||||
var hasGoMod = false
|
var hasGoMod = false
|
||||||
for {
|
for {
|
||||||
|
if tempPath == filepath.Dir(tempPath) {
|
||||||
|
break
|
||||||
|
}
|
||||||
tempPath = filepath.Dir(tempPath)
|
tempPath = filepath.Dir(tempPath)
|
||||||
if goctlutil.FileExists(filepath.Join(tempPath, goModeIdentifier)) {
|
if goctlutil.FileExists(filepath.Join(tempPath, goModeIdentifier)) {
|
||||||
tempPath = filepath.Dir(tempPath)
|
tempPath = filepath.Dir(tempPath)
|
||||||
@@ -43,8 +47,7 @@ func getParentPackage(dir string) (string, error) {
|
|||||||
parent := path.Join(gopath, "src")
|
parent := path.Join(gopath, "src")
|
||||||
pos := strings.Index(absDir, parent)
|
pos := strings.Index(absDir, parent)
|
||||||
if pos < 0 {
|
if pos < 0 {
|
||||||
message := fmt.Sprintf("%s not in gomod project path, or not in GOPATH of %s directory", absDir, gopath)
|
fmt.Printf("%s not in gomod project path, or not in GOPATH of %s directory\n", absDir, gopath)
|
||||||
println(message)
|
|
||||||
tempPath = filepath.Dir(absDir)
|
tempPath = filepath.Dir(absDir)
|
||||||
rootPath = absDir[len(tempPath)+1:]
|
rootPath = absDir[len(tempPath)+1:]
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@@ -36,9 +36,9 @@ func JavaCommand(c *cli.Context) error {
|
|||||||
packetName = packetName[:len(packetName)-4]
|
packetName = packetName[:len(packetName)-4]
|
||||||
}
|
}
|
||||||
|
|
||||||
lang.Must(util.MkdirIfNotExist(dir))
|
logx.Must(util.MkdirIfNotExist(dir))
|
||||||
lang.Must(genPacket(dir, packetName, api))
|
logx.Must(genPacket(dir, packetName, api))
|
||||||
lang.Must(genComponents(dir, packetName, api))
|
logx.Must(genComponents(dir, packetName, api))
|
||||||
|
|
||||||
fmt.Println(aurora.Green("Done."))
|
fmt.Println(aurora.Green("Done."))
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ func formatFile(tmplBytes *bytes.Buffer, file *os.File) {
|
|||||||
builder.WriteString(scanner.Text() + "\n")
|
builder.WriteString(scanner.Text() + "\n")
|
||||||
}
|
}
|
||||||
if err := scanner.Err(); err != nil {
|
if err := scanner.Err(); err != nil {
|
||||||
println(err)
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,10 +268,12 @@ func genType(writer io.Writer, tp spec.Type) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writeBreakline(writer)
|
writeBreakline(writer)
|
||||||
writeIndent(writer, 1)
|
writeIndent(writer, 1)
|
||||||
genGetSet(writer, tp, 2)
|
genGetSet(writer, tp, 2)
|
||||||
writeIndent(writer, 1)
|
writeIndent(writer, 1)
|
||||||
fmt.Fprintln(writer, "}")
|
fmt.Fprintln(writer, "}")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -14,8 +14,8 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
p, err := parser.NewParser(os.Args[1])
|
p, err := parser.NewParser(os.Args[1])
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
api, err := p.Parse()
|
api, err := p.Parse()
|
||||||
lang.Must(err)
|
logx.Must(err)
|
||||||
fmt.Println(api)
|
fmt.Println(api)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/logrusorgru/aurora"
|
"github.com/logrusorgru/aurora"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
"github.com/tal-tech/go-zero/tools/goctl/api/parser"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@@ -34,9 +34,9 @@ func TsCommand(c *cli.Context) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
lang.Must(util.MkdirIfNotExist(dir))
|
logx.Must(util.MkdirIfNotExist(dir))
|
||||||
lang.Must(genHandler(dir, webApi, caller, api, unwrapApi))
|
logx.Must(genHandler(dir, webApi, caller, api, unwrapApi))
|
||||||
lang.Must(genComponents(dir, api))
|
logx.Must(genComponents(dir, api))
|
||||||
|
|
||||||
fmt.Println(aurora.Green("Done."))
|
fmt.Println(aurora.Green("Done."))
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ func paramsForRoute(route spec.Route, prefixForType func(string) string) string
|
|||||||
hasBody := hasRequestBody(route)
|
hasBody := hasRequestBody(route)
|
||||||
rt, err := goTypeToTs(route.RequestType.Name, prefixForType)
|
rt, err := goTypeToTs(route.RequestType.Name, prefixForType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
println(err.Error())
|
fmt.Println(err.Error())
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
if hasParams && hasBody {
|
if hasParams && hasBody {
|
||||||
|
|||||||
@@ -8,13 +8,13 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
"github.com/tal-tech/go-zero/tools/goctl/api/spec"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err error) {
|
func MaybeCreateFile(dir, subdir, file string) (fp *os.File, created bool, err error) {
|
||||||
lang.Must(util.MkdirIfNotExist(path.Join(dir, subdir)))
|
logx.Must(util.MkdirIfNotExist(path.Join(dir, subdir)))
|
||||||
fpath := path.Join(dir, subdir, file)
|
fpath := path.Join(dir, subdir, file)
|
||||||
if util.FileExists(fpath) {
|
if util.FileExists(fpath) {
|
||||||
fmt.Printf("%s exists, ignored generation\n", fpath)
|
fmt.Printf("%s exists, ignored generation\n", fpath)
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
"github.com/tal-tech/go-zero/core/conf"
|
"github.com/tal-tech/go-zero/core/conf"
|
||||||
"github.com/tal-tech/go-zero/core/hash"
|
"github.com/tal-tech/go-zero/core/hash"
|
||||||
"github.com/tal-tech/go-zero/core/lang"
|
|
||||||
"github.com/tal-tech/go-zero/core/logx"
|
"github.com/tal-tech/go-zero/core/logx"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/update/config"
|
"github.com/tal-tech/go-zero/tools/goctl/update/config"
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/util"
|
"github.com/tal-tech/go-zero/tools/goctl/util"
|
||||||
@@ -56,5 +55,5 @@ func main() {
|
|||||||
|
|
||||||
fs := http.FileServer(http.Dir(c.FileDir))
|
fs := http.FileServer(http.Dir(c.FileDir))
|
||||||
http.Handle(c.FilePath, http.StripPrefix(c.FilePath, forChksumHandler(path.Join(c.FileDir, filename), fs)))
|
http.Handle(c.FilePath, http.StripPrefix(c.FilePath, forChksumHandler(path.Join(c.FileDir, filename), fs)))
|
||||||
lang.Must(http.ListenAndServe(c.ListenOn, nil))
|
logx.Must(http.ListenAndServe(c.ListenOn, nil))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
"github.com/tal-tech/go-zero/tools/goctl/vars"
|
||||||
@@ -44,16 +43,3 @@ func PathFromGoSrc() (string, error) {
|
|||||||
// skip slash
|
// skip slash
|
||||||
return dir[len(parent)+1:], nil
|
return dir[len(parent)+1:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetParentPackage(dir string) (string, error) {
|
|
||||||
absDir, err := filepath.Abs(dir)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
pos := strings.Index(absDir, vars.ProjectName)
|
|
||||||
if pos < 0 {
|
|
||||||
return "", fmt.Errorf("error dir:[%s],please make sure that your project is in the %s directory", vars.ProjectName, dir)
|
|
||||||
}
|
|
||||||
|
|
||||||
return absDir[pos:], nil
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user