From 278a4e60fdd44b116890d32fbf14edfd47573c14 Mon Sep 17 00:00:00 2001 From: Laurence <45508533+LaurenceLiZhixin@users.noreply.github.com> Date: Wed, 27 Jul 2022 14:51:26 +0800 Subject: [PATCH] fix: add stress test for aop (#109) * fix: add stress test for aop * fix: ut * Fix: ut * fix: Add release makefile and git ignore for iocli. * fix: ut * Fix: add release script * Fix: fix tar --- .gitignore | 4 +- Makefile | 8 +- aop/common/runtime.go | 7 +- aop/proxy.go | 4 +- autowire/monkey_test.go | 51 ----- autowire/wrapper.go | 13 +- example/aop/observability/main_test.go | 20 ++ example/go.mod | 2 +- extension/aop/trace/goroutine.go | 2 +- extension/aop/trace/server.go | 6 +- extension/aop/trace/transport/collector.go | 9 +- extension/aop/transaction/interceptor.go | 6 +- extension/aop/watch/aop.go | 10 +- extension/aop/watch/interceptor.go | 100 +++++----- extension/aop/watch/interceptor_test.go | 40 ++-- extension/aop/watch/server.go | 28 ++- extension/aop/watch/zz_generated.ioc.go | 198 ++++++++++++++++++++ extension/go.mod | 2 +- iocli/Makefile | 10 +- iocli/go.mod | 5 +- iocli/go.sum | 9 +- autowire/monkey.go => logger/logger_test.go | 18 +- test/stress/aop/proxy_test.go | 32 ++++ test/stress/aop/recursive_app.go | 54 ++++++ test/stress/aop/zz_generated.ioc.go | 63 ++++++- 25 files changed, 530 insertions(+), 171 deletions(-) delete mode 100644 autowire/monkey_test.go create mode 100644 extension/aop/watch/zz_generated.ioc.go rename autowire/monkey.go => logger/logger_test.go (74%) create mode 100644 test/stress/aop/recursive_app.go diff --git a/.gitignore b/.gitignore index a02f5c6..6c30e5a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.DS_Store -.idea \ No newline at end of file +.idea +iocli/.release +.release \ No newline at end of file diff --git a/Makefile b/Makefile index 90b9458..6b7b2ee 100644 --- a/Makefile +++ b/Makefile @@ -30,4 +30,10 @@ test-all: go test ./... -cover -p 1 cd extension && go test ./... -cover -p 1 cd example && go test ./... -cover -p 1 - cd iocli && go test ./... -cover -p 1 \ No newline at end of file + cd iocli && go test ./... -cover -p 1 + +release-all: gen-all test-all + mkdir -p .release/ioc-golang + cd iocli && make build-all-platform && mv ./.release ../.release/iocli + cp -r `ls` ./.release/ioc-golang + tar -czvf ./.release/ioc-golang.tar.gz ./.release/ioc-golang \ No newline at end of file diff --git a/aop/common/runtime.go b/aop/common/runtime.go index ecfbc3d..3dce44d 100644 --- a/aop/common/runtime.go +++ b/aop/common/runtime.go @@ -30,7 +30,7 @@ func CurrentCallingMethodName(skip int) string { return runtime.FuncForPC(pc[0]).Name() } -func TraceLevel(entranceName string) int64 { +func IsTraceEntrance(entranceName string) bool { pc := make([]uintptr, 500) n := runtime.Callers(0, pc) foundEntrance := false @@ -42,6 +42,9 @@ func TraceLevel(entranceName string) int64 { if strings.HasPrefix(fName, ProxyMethodPrefix) { level++ } + if level == 2 { + return false + } continue } if fName == entranceName { @@ -49,5 +52,5 @@ func TraceLevel(entranceName string) int64 { } } - return level - 1 + return level-1 == 0 } diff --git a/aop/proxy.go b/aop/proxy.go index 42e8dca..c4ea7a9 100644 --- a/aop/proxy.go +++ b/aop/proxy.go @@ -91,11 +91,11 @@ func makeProxyFunction(proxyPtr interface{}, rf reflect.Value, sdid, methodName proxyFunc := func(in []reflect.Value) []reflect.Value { defer func() { if r := recover(); r != nil { - logger.Red("[AOP Proxy] Cache error = %s", r) + logger.Red("[AOP Proxy] Catch error = %s", r) } }() - invocationCtx := NewInvocationContext(proxyPtr, sdid, methodName, common.CurrentCallingMethodName(4), in) + invocationCtx := NewInvocationContext(proxyPtr, sdid, methodName, common.CurrentCallingMethodName(3), in) for _, i := range interceptorImpls { i.BeforeInvoke(invocationCtx) diff --git a/autowire/monkey_test.go b/autowire/monkey_test.go deleted file mode 100644 index 8bcf0c5..0000000 --- a/autowire/monkey_test.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022, Alibaba Group; - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package autowire - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" -) - -var mockMonkeyFunction = func(i interface{}, s string) { - -} - -func TestRegisterMonkeyFunction(t *testing.T) { - type args struct { - f func(interface{}, string) - } - tests := []struct { - name string - args args - }{ - { - name: "test register mock monkey function", - args: args{ - f: mockMonkeyFunction, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - RegisterMonkeyFunction(tt.args.f) - defer RegisterMonkeyFunction(nil) - assert.Equal(t, fmt.Sprintf("%p", mockMonkeyFunction), fmt.Sprintf("%p", mf)) - }) - } -} diff --git a/autowire/wrapper.go b/autowire/wrapper.go index 9dd4183..c43453b 100644 --- a/autowire/wrapper.go +++ b/autowire/wrapper.go @@ -16,9 +16,8 @@ package autowire import ( - "os" + "fmt" "reflect" - "runtime" perrors "github.com/pkg/errors" @@ -168,9 +167,10 @@ func (w *WrapperAutowireImpl) inject(impledPtr interface{}, sdId string) error { tagKey = aw.TagKey() tagValue = val if !(subService.IsValid() && subService.CanSet()) { - err := perrors.Errorf("Failed to autowire struct %s's impl %s service. It's field %s with tag '%s:\"%s\"', please check if the field is exported", + errMsg := fmt.Sprintf("Failed to autowire struct %s's impl %s service. It's field type %s with tag '%s:\"%s\"', please check if the field name is exported", sd.ID(), util.GetStructName(impledPtr), field.Type.Name(), tagKey, tagValue) - return err + logger.Red("[Autowire Wrapper] Inject field failed with error: %s", errMsg) + return perrors.New(errMsg) } fieldType := buildFiledTypeFullName(field.Type) @@ -198,11 +198,6 @@ func (w *WrapperAutowireImpl) inject(impledPtr interface{}, sdId string) error { // set field subService.Set(reflect.ValueOf(subImpledPtr)) } - // 3. monkey - if monkeyFunction := GetMonkeyFunction(); (os.Getenv("GOARCH") == "amd64" || runtime.GOARCH == "amd64") && monkeyFunction != nil { - // only amd64-os/amd64-go-arch-env with build flags '-gcflags="-N -l" -tags iocdebug' can inject monkey function - monkeyFunction(impledPtr, sd.ID()) - } return nil } diff --git a/example/aop/observability/main_test.go b/example/aop/observability/main_test.go index 66dd7cd..dbf85c8 100644 --- a/example/aop/observability/main_test.go +++ b/example/aop/observability/main_test.go @@ -66,4 +66,24 @@ Param 1: (string) (len=8) "laurence" github.com/alibaba/ioc-golang/example/aop/observability.ServiceImpl1.GetHelloString() Response 1: (string) (len=36) "This is ServiceImpl2, hello laurence"`)) + output, err = iocli_command.Run([]string{"monitor", "-i", "3"}, time.Second*4) + assert.Nil(t, err) + assert.True(t, strings.Contains(output, `github.com/alibaba/ioc-golang/example/aop/observability.ServiceImpl1.GetHelloString() +Total: 1, Success: 1, Fail: 0, AvgRT: `)) + assert.True(t, strings.Contains(output, `us, FailRate: 0.00% +github.com/alibaba/ioc-golang/example/aop/observability.ServiceImpl2.GetHelloString() +Total: 1, Success: 1, Fail: 0, AvgRT: `)) + + output, err = iocli_command.Run([]string{"trace", "github.com/alibaba/ioc-golang/example/aop/observability.ServiceImpl1", "GetHelloString"}, time.Second*6) + assert.Nil(t, err) + assert.True(t, strings.Contains(output, `OperationName: github.com/alibaba/ioc-golang/example/aop/observability.(*serviceImpl2_).GetHelloString, StartTime: `)) + assert.True(t, strings.Contains(output, `OperationName: github.com/alibaba/ioc-golang/example/aop/observability.(*serviceImpl1_).GetHelloString, StartTime: `)) + assert.True(t, strings.Contains(output, `ReferenceSpans: [{TraceID:`)) + + output, err = iocli_command.Run([]string{"trace", "github.com/alibaba/ioc-golang/example/aop/observability.ServiceImpl2", "GetHelloString"}, time.Second*6) + assert.Nil(t, err) + assert.True(t, strings.Contains(output, `OperationName: github.com/alibaba/ioc-golang/example/aop/observability.(*serviceImpl2_).GetHelloString, StartTime: `)) + assert.True(t, !strings.Contains(output, `OperationName: github.com/alibaba/ioc-golang/example/aop/observability.(*serviceImpl1_).GetHelloString, StartTime: `)) + assert.True(t, strings.Contains(output, `ReferenceSpans: [{TraceID:`)) + } diff --git a/example/go.mod b/example/go.mod index f265ac1..6438da2 100644 --- a/example/go.mod +++ b/example/go.mod @@ -3,7 +3,7 @@ module github.com/alibaba/ioc-golang/example go 1.17 require ( - github.com/alibaba/ioc-golang v0.0.0-20220726064435-a135d55b94e9 + github.com/alibaba/ioc-golang v0.0.0-20220726180817-a8481c622579 github.com/alibaba/ioc-golang/extension v0.0.0-20220707071909-d19576eea84e github.com/apache/rocketmq-client-go/v2 v2.1.0 github.com/go-redis/redis v6.15.9+incompatible diff --git a/extension/aop/trace/goroutine.go b/extension/aop/trace/goroutine.go index 0bcbedf..b40a583 100644 --- a/extension/aop/trace/goroutine.go +++ b/extension/aop/trace/goroutine.go @@ -51,7 +51,7 @@ func (g *goRoutineTraceInterceptor) AfterInvoke(ctx *aop.InvocationContext) { currentSpan.LogFields(log.String(traceCommon.SpanReturnValuesKey, common.ReflectValues2String(ctx.ReturnValues, int(traceCtx.maxDepth), int(traceCtx.maxLength)))) // calculate level - if common.TraceLevel(traceCtx.getTrace().entranceMethod) == 0 { + if common.IsTraceEntrance(traceCtx.getTrace().entranceMethod) { // tracing finished g.tracingGrIDMap.Delete(ctx.GrID) } diff --git a/extension/aop/trace/server.go b/extension/aop/trace/server.go index ccdb62a..727a018 100644 --- a/extension/aop/trace/server.go +++ b/extension/aop/trace/server.go @@ -26,6 +26,8 @@ import ( "github.com/alibaba/ioc-golang/logger" ) +const outBatchBufferAndChSize = 1000 + type traceServiceImpl struct { tracePB.UnimplementedTraceServiceServer traceInterceptor *methodTraceInterceptor @@ -57,7 +59,7 @@ func (d *traceServiceImpl) Trace(req *tracePB.TraceRequest, traceServer tracePB. if req.GetPushToCollectorAddress() != "" { // start subscribing batch buffer - outBatchBuffer := make(chan *bytes.Buffer, 100) + outBatchBuffer := make(chan *bytes.Buffer, outBatchBufferAndChSize) getGlobalTracer().subscribeBatchBuffer(outBatchBuffer) go func() { for { @@ -74,7 +76,7 @@ func (d *traceServiceImpl) Trace(req *tracePB.TraceRequest, traceServer tracePB. }() } - outTraceCh := make(chan []*model.Trace, 100) + outTraceCh := make(chan []*model.Trace, outBatchBufferAndChSize) // start subscribing traces info getGlobalTracer().subscribeTrace(outTraceCh) go func() { diff --git a/extension/aop/trace/transport/collector.go b/extension/aop/trace/transport/collector.go index 3e18cbe..8519f71 100644 --- a/extension/aop/trace/transport/collector.go +++ b/extension/aop/trace/transport/collector.go @@ -33,6 +33,9 @@ import ( ) const memoryStorageType = "memory" +const memoryMaxTraces = 1000 +const dynamicQueueMaxMemory = 1 * 1024 * 1024 // 1MB +const queueSize = 1000 type collector struct { spanHandlers *app.SpanHandlers @@ -45,7 +48,7 @@ type collector struct { func newCollector(appName string, interval int, out chan []*model.Trace) (*collector, error) { logger := zap.NewNop() v := viper.New() - v.Set("memory.max-traces", 100) + v.Set("memory.max-traces", memoryMaxTraces) storageFactory, err := storage.NewFactory(storage.FactoryConfig{ SpanWriterTypes: []string{memoryStorageType}, @@ -69,8 +72,8 @@ func newCollector(appName string, interval int, out chan []*model.Trace) (*colle if err != nil { return nil, err } - collectorOpts.DynQueueSizeMemory = 1 * 1024 * 1024 // 1MB - collectorOpts.QueueSize = 10 + collectorOpts.DynQueueSizeMemory = dynamicQueueMaxMemory + collectorOpts.QueueSize = queueSize handlerBuilder := &app.SpanHandlerBuilder{ SpanWriter: spanWriter, diff --git a/extension/aop/transaction/interceptor.go b/extension/aop/transaction/interceptor.go index a9680f6..cecbd49 100644 --- a/extension/aop/transaction/interceptor.go +++ b/extension/aop/transaction/interceptor.go @@ -66,9 +66,9 @@ func (t *interceptorImpl) AfterInvoke(ctx *aop.InvocationContext) { // if invocation failed invocationFailed, err := common.IsInvocationFailed(ctx.ReturnValues) - // 1.1 if current invocation is the entrance of transaction ? - // calculate level - if common.TraceLevel(txCtx.getEntranceMethod()) == 0 { + // if current invocation is the entrance of transaction ? + // check if is entrance + if common.IsTraceEntrance(txCtx.getEntranceMethod()) { // current invocation is the entrance of transaction t.transactionGrIDMap.Delete(ctx.GrID) // if the transaction failed ? diff --git a/extension/aop/watch/aop.go b/extension/aop/watch/aop.go index 8c15d59..6a6fb69 100644 --- a/extension/aop/watch/aop.go +++ b/extension/aop/watch/aop.go @@ -22,14 +22,18 @@ import ( watchPB "github.com/alibaba/ioc-golang/extension/aop/watch/api/ioc_golang/aop/watch" ) +const Name = "watch" + func init() { aop.RegisterAOP(aop.AOP{ - Name: "watch", + Name: Name, InterceptorFactory: func() aop.Interceptor { - return getWatchInterceptorSingleton() + impl, _ := GetinterceptorImplIOCInterfaceSingleton() + return impl }, GRPCServiceRegister: func(server *grpc.Server) { - watchPB.RegisterWatchServiceServer(server, getWatchService()) + watchInterface, _ := GetwatchServiceSingleton() + watchPB.RegisterWatchServiceServer(server, watchInterface) }, }) } diff --git a/extension/aop/watch/interceptor.go b/extension/aop/watch/interceptor.go index 5d857b3..650f503 100644 --- a/extension/aop/watch/interceptor.go +++ b/extension/aop/watch/interceptor.go @@ -19,10 +19,9 @@ import ( "reflect" "sync" - "github.com/alibaba/ioc-golang/aop" - "github.com/petermattis/goid" + "github.com/alibaba/ioc-golang/aop" "github.com/alibaba/ioc-golang/aop/common" watchPB "github.com/alibaba/ioc-golang/extension/aop/watch/api/ioc_golang/aop/watch" ) @@ -32,86 +31,93 @@ const ( defaultRecordValuesLength = 1000 ) +// +ioc:autowire=true +// +ioc:autowire:type=singleton +// +ioc:autowire:proxy:autoInjection=false + type interceptorImpl struct { watch sync.Map } func (w *interceptorImpl) BeforeInvoke(ctx *aop.InvocationContext) { if watchCtxInterface, ok := w.watch.Load(common.GetMethodUniqueKey(ctx.SDID, ctx.MethodName)); ok { - watchCtxInterface.(*context).beforeInvoke(ctx) + watchCtxInterface.(contextIOCInterface).beforeInvoke(ctx) } } func (w *interceptorImpl) AfterInvoke(ctx *aop.InvocationContext) { if watchCtxInterface, ok := w.watch.Load(common.GetMethodUniqueKey(ctx.SDID, ctx.MethodName)); ok { - watchCtxInterface.(*context).afterInvoke(ctx) + watchCtxInterface.(contextIOCInterface).afterInvoke(ctx) } } -func (w *interceptorImpl) Watch(watchCtx *context) { - w.watch.Store(common.GetMethodUniqueKey(watchCtx.SDID, watchCtx.MethodName), watchCtx) +func (w *interceptorImpl) Watch(watchCtx contextIOCInterface) { + w.watch.Store(common.GetMethodUniqueKey(watchCtx.getSDID(), watchCtx.getMethod()), watchCtx) } -func (w *interceptorImpl) UnWatch(watchCtx *context) { - w.watch.Delete(common.GetMethodUniqueKey(watchCtx.SDID, watchCtx.MethodName)) +func (w *interceptorImpl) UnWatch(watchCtx contextIOCInterface) { + w.watch.Delete(common.GetMethodUniqueKey(watchCtx.getSDID(), watchCtx.getMethod())) } +// +ioc:autowire=true +// +ioc:autowire:type=normal +// +ioc:autowire:proxy:autoInjection=false +// +ioc:autowire:paramType=contextParam +// +ioc:autowire:constructFunc=new + type context struct { - SDID string - MethodName string - Ch chan *watchPB.WatchResponse - FieldMatcher *common.FieldMatcher watchGRRequestMap sync.Map - maxDepth int - maxLength int + contextParam } -func newContext(sdid, method string, maxDepth, maxLength int, sendCh chan *watchPB.WatchResponse, fieldMatcher *common.FieldMatcher) *context { - if maxLength == 0 { - maxLength = defaultRecordValuesLength - } - if maxDepth == 0 { - maxDepth = defaultRecordValuesDepth +type contextParam struct { + SDID string + MethodName string + MaxDepth int + MaxLength int + Ch chan *watchPB.WatchResponse + FieldMatcher *common.FieldMatcher +} + +func (p *contextParam) new(c *context) (*context, error) { + if p.MaxDepth == 0 { + p.MaxDepth = defaultRecordValuesLength } - return &context{ - SDID: sdid, - MethodName: method, - Ch: sendCh, - FieldMatcher: fieldMatcher, - maxDepth: maxDepth, - maxLength: maxLength, + if p.MaxDepth == 0 { + p.MaxDepth = defaultRecordValuesDepth } + c.contextParam = *p + return c, nil } -func (w *context) beforeInvoke(ctx *aop.InvocationContext) { - if w.FieldMatcher != nil && !w.FieldMatcher.Match(ctx.Params) { +func (c *context) getSDID() string { + return c.SDID +} + +func (c *context) getMethod() string { + return c.MethodName +} + +func (c *context) beforeInvoke(ctx *aop.InvocationContext) { + if c.FieldMatcher != nil && !c.FieldMatcher.Match(ctx.Params) { // doesn't match return } grID := goid.Get() - w.watchGRRequestMap.Store(grID, ctx.Params) + c.watchGRRequestMap.Store(grID, ctx.Params) } -func (w *context) afterInvoke(ctx *aop.InvocationContext) { - paramValues, ok := w.watchGRRequestMap.Load(ctx.GrID) +func (c *context) afterInvoke(ctx *aop.InvocationContext) { + paramValues, ok := c.watchGRRequestMap.Load(ctx.GrID) if !ok { return } invokeDetail := &watchPB.WatchResponse{ - Sdid: w.SDID, - MethodName: w.MethodName, - Params: common.ReflectValues2Strings(paramValues.([]reflect.Value), w.maxDepth, w.maxLength), - ReturnValues: common.ReflectValues2Strings(ctx.ReturnValues, w.maxDepth, w.maxLength), - } - w.Ch <- invokeDetail - w.watchGRRequestMap.Delete(ctx.GrID) -} - -var watchInterceptorSingleton *interceptorImpl - -func getWatchInterceptorSingleton() *interceptorImpl { - if watchInterceptorSingleton == nil { - watchInterceptorSingleton = &interceptorImpl{} + Sdid: c.SDID, + MethodName: c.MethodName, + Params: common.ReflectValues2Strings(paramValues.([]reflect.Value), c.MaxDepth, c.MaxLength), + ReturnValues: common.ReflectValues2Strings(ctx.ReturnValues, c.MaxDepth, c.MaxLength), } - return watchInterceptorSingleton + c.Ch <- invokeDetail + c.watchGRRequestMap.Delete(ctx.GrID) } diff --git a/extension/aop/watch/interceptor_test.go b/extension/aop/watch/interceptor_test.go index 3dd0c9e..2192c36 100644 --- a/extension/aop/watch/interceptor_test.go +++ b/extension/aop/watch/interceptor_test.go @@ -32,7 +32,8 @@ import ( ) func TestWatchInterceptor(t *testing.T) { - watchInterceptor := getWatchInterceptorSingleton() + watchInterceptor, err := GetinterceptorImplIOCInterfaceSingleton() + assert.Nil(t, err) sdid := util.GetSDIDByStructPtr(&common.ServiceFoo{}) methodName := "Invoke" methodFullName := sdid + "." + methodName @@ -42,7 +43,13 @@ func TestWatchInterceptor(t *testing.T) { info := <-sendCh controlCh <- info }() - watchInterceptor.Watch(newContext(sdid, methodName, 0, 0, sendCh, nil)) + watchCtx, err := GetcontextIOCInterface(&contextParam{ + SDID: sdid, + MethodName: methodName, + Ch: sendCh, + }) + assert.Nil(t, err) + watchInterceptor.Watch(watchCtx) service := &common.ServiceFoo{} ctx := oriCtx.Background() @@ -60,26 +67,27 @@ func TestWatchInterceptor(t *testing.T) { info := <-controlCh assert.Equal(t, sdid, info.Sdid) assert.Equal(t, "Invoke", info.MethodName) + watchInterceptor.UnWatch(watchCtx) } func TestWatchInterceptorWithCondition(t *testing.T) { - watchInterceptor := getWatchInterceptorSingleton() + watchInterceptor, err := GetinterceptorImplIOCInterfaceSingleton() + assert.Nil(t, err) sdid := util.GetSDIDByStructPtr(&common.ServiceFoo{}) methodName := "Invoke" methodFullName := sdid + "." + methodName sendCh := make(chan *watch.WatchResponse, 10) - controlCh := make(chan *watch.WatchResponse, 10) - go func() { - for { - info := <-sendCh - controlCh <- info - } - }() - watchCtx := newContext(sdid, methodName, 0, 0, sendCh, - &common.FieldMatcher{ + watchCtx, err := GetcontextIOCInterface(&contextParam{ + SDID: sdid, + MethodName: methodName, + Ch: sendCh, + FieldMatcher: &common.FieldMatcher{ FieldIndex: 1, MatchRule: "User.Name=lizhixin", - }) + }, + }) + assert.Nil(t, err) + watchInterceptor.Watch(watchCtx) service := &common.ServiceFoo{} @@ -100,7 +108,7 @@ func TestWatchInterceptorWithCondition(t *testing.T) { watchInterceptor.AfterInvoke(invocationCtx) time.Sleep(time.Millisecond * 500) select { - case info = <-controlCh: + case info = <-sendCh: default: } assert.Equal(t, "", info.Sdid) @@ -115,7 +123,7 @@ func TestWatchInterceptorWithCondition(t *testing.T) { watchInterceptor.AfterInvoke(invocationCtx) time.Sleep(time.Millisecond * 500) select { - case info = <-controlCh: + case info = <-sendCh: default: } assert.Equal(t, util.GetSDIDByStructPtr(&common.ServiceFoo{}), info.Sdid) @@ -131,7 +139,7 @@ func TestWatchInterceptorWithCondition(t *testing.T) { watchInterceptor.AfterInvoke(invocationCtx) info = &watch.WatchResponse{} select { - case info = <-controlCh: + case info = <-sendCh: default: } assert.Equal(t, "", info.Sdid) diff --git a/extension/aop/watch/server.go b/extension/aop/watch/server.go index e065ead..1066bdf 100644 --- a/extension/aop/watch/server.go +++ b/extension/aop/watch/server.go @@ -21,15 +21,13 @@ import ( "github.com/alibaba/ioc-golang/logger" ) +// +ioc:autowire=true +// +ioc:autowire:type=singleton +// +ioc:autowire:proxy=false + type watchService struct { watch.UnimplementedWatchServiceServer - watchInterceptor *interceptorImpl -} - -func getWatchService() *watchService { - return &watchService{ - watchInterceptor: getWatchInterceptorSingleton(), - } + WatchInterceptor interceptorImplIOCInterface `singleton:""` } func (w *watchService) Watch(req *watch.WatchRequest, svr watch.WatchService_WatchServer) error { @@ -57,15 +55,25 @@ func (w *watchService) Watch(req *watch.WatchRequest, svr watch.WatchService_Wat } } - watchCtx := newContext(sdid, method, maxDepth, maxLength, sendCh, fieldMatcher) - w.watchInterceptor.Watch(watchCtx) + watchCtx, err := GetcontextIOCInterface(&contextParam{ + SDID: sdid, + MethodName: method, + MaxLength: maxLength, + MaxDepth: maxDepth, + Ch: sendCh, + FieldMatcher: fieldMatcher, + }) + if err != nil { + return err + } + w.WatchInterceptor.Watch(watchCtx) done := svr.Context().Done() for { select { case <-done: // watch stop - w.watchInterceptor.UnWatch(watchCtx) + w.WatchInterceptor.UnWatch(watchCtx) return nil case watchRsp := <-sendCh: if err := svr.Send(watchRsp); err != nil { diff --git a/extension/aop/watch/zz_generated.ioc.go b/extension/aop/watch/zz_generated.ioc.go new file mode 100644 index 0000000..2f8111e --- /dev/null +++ b/extension/aop/watch/zz_generated.ioc.go @@ -0,0 +1,198 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// Code generated by iocli, run 'iocli gen' to re-generate + +package watch + +import ( + "github.com/alibaba/ioc-golang/aop" + autowire "github.com/alibaba/ioc-golang/autowire" + normal "github.com/alibaba/ioc-golang/autowire/normal" + singleton "github.com/alibaba/ioc-golang/autowire/singleton" + util "github.com/alibaba/ioc-golang/autowire/util" +) + +func init() { + normal.RegisterStructDescriptor(&autowire.StructDescriptor{ + Factory: func() interface{} { + return &interceptorImpl_{} + }, + }) + interceptorImplStructDescriptor := &autowire.StructDescriptor{ + Factory: func() interface{} { + return &interceptorImpl{} + }, + Metadata: map[string]interface{}{ + "aop": map[string]interface{}{}, + "autowire": map[string]interface{}{}, + }, + DisableProxy: true, + } + singleton.RegisterStructDescriptor(interceptorImplStructDescriptor) + normal.RegisterStructDescriptor(&autowire.StructDescriptor{ + Factory: func() interface{} { + return &context_{} + }, + }) + contextStructDescriptor := &autowire.StructDescriptor{ + Factory: func() interface{} { + return &context{} + }, + ParamFactory: func() interface{} { + var _ contextParamInterface = &contextParam{} + return &contextParam{} + }, + ConstructFunc: func(i interface{}, p interface{}) (interface{}, error) { + param := p.(contextParamInterface) + impl := i.(*context) + return param.new(impl) + }, + Metadata: map[string]interface{}{ + "aop": map[string]interface{}{}, + "autowire": map[string]interface{}{}, + }, + DisableProxy: true, + } + normal.RegisterStructDescriptor(contextStructDescriptor) + watchServiceStructDescriptor := &autowire.StructDescriptor{ + Factory: func() interface{} { + return &watchService{} + }, + Metadata: map[string]interface{}{ + "aop": map[string]interface{}{}, + "autowire": map[string]interface{}{}, + }, + DisableProxy: true, + } + singleton.RegisterStructDescriptor(watchServiceStructDescriptor) +} + +type contextParamInterface interface { + new(impl *context) (*context, error) +} +type interceptorImpl_ struct { + BeforeInvoke_ func(ctx *aop.InvocationContext) + AfterInvoke_ func(ctx *aop.InvocationContext) + Watch_ func(watchCtx contextIOCInterface) + UnWatch_ func(watchCtx contextIOCInterface) +} + +func (i *interceptorImpl_) BeforeInvoke(ctx *aop.InvocationContext) { + i.BeforeInvoke_(ctx) +} + +func (i *interceptorImpl_) AfterInvoke(ctx *aop.InvocationContext) { + i.AfterInvoke_(ctx) +} + +func (i *interceptorImpl_) Watch(watchCtx contextIOCInterface) { + i.Watch_(watchCtx) +} + +func (i *interceptorImpl_) UnWatch(watchCtx contextIOCInterface) { + i.UnWatch_(watchCtx) +} + +type context_ struct { + getSDID_ func() string + getMethod_ func() string + beforeInvoke_ func(ctx *aop.InvocationContext) + afterInvoke_ func(ctx *aop.InvocationContext) +} + +func (c *context_) getSDID() string { + return c.getSDID_() +} + +func (c *context_) getMethod() string { + return c.getMethod_() +} + +func (c *context_) beforeInvoke(ctx *aop.InvocationContext) { + c.beforeInvoke_(ctx) +} + +func (c *context_) afterInvoke(ctx *aop.InvocationContext) { + c.afterInvoke_(ctx) +} + +type interceptorImplIOCInterface interface { + BeforeInvoke(ctx *aop.InvocationContext) + AfterInvoke(ctx *aop.InvocationContext) + Watch(watchCtx contextIOCInterface) + UnWatch(watchCtx contextIOCInterface) +} + +type contextIOCInterface interface { + getSDID() string + getMethod() string + beforeInvoke(ctx *aop.InvocationContext) + afterInvoke(ctx *aop.InvocationContext) +} + +var _interceptorImplSDID string + +func GetinterceptorImplSingleton() (*interceptorImpl, error) { + if _interceptorImplSDID == "" { + _interceptorImplSDID = util.GetSDIDByStructPtr(new(interceptorImpl)) + } + i, err := singleton.GetImpl(_interceptorImplSDID, nil) + if err != nil { + return nil, err + } + impl := i.(*interceptorImpl) + return impl, nil +} + +func GetinterceptorImplIOCInterfaceSingleton() (interceptorImplIOCInterface, error) { + if _interceptorImplSDID == "" { + _interceptorImplSDID = util.GetSDIDByStructPtr(new(interceptorImpl)) + } + i, err := singleton.GetImplWithProxy(_interceptorImplSDID, nil) + if err != nil { + return nil, err + } + impl := i.(interceptorImplIOCInterface) + return impl, nil +} + +var _contextSDID string + +func Getcontext(p *contextParam) (*context, error) { + if _contextSDID == "" { + _contextSDID = util.GetSDIDByStructPtr(new(context)) + } + i, err := normal.GetImpl(_contextSDID, p) + if err != nil { + return nil, err + } + impl := i.(*context) + return impl, nil +} + +func GetcontextIOCInterface(p *contextParam) (contextIOCInterface, error) { + if _contextSDID == "" { + _contextSDID = util.GetSDIDByStructPtr(new(context)) + } + i, err := normal.GetImplWithProxy(_contextSDID, p) + if err != nil { + return nil, err + } + impl := i.(contextIOCInterface) + return impl, nil +} + +var _watchServiceSDID string + +func GetwatchServiceSingleton() (*watchService, error) { + if _watchServiceSDID == "" { + _watchServiceSDID = util.GetSDIDByStructPtr(new(watchService)) + } + i, err := singleton.GetImpl(_watchServiceSDID, nil) + if err != nil { + return nil, err + } + impl := i.(*watchService) + return impl, nil +} diff --git a/extension/go.mod b/extension/go.mod index ee07935..8dae295 100644 --- a/extension/go.mod +++ b/extension/go.mod @@ -4,7 +4,7 @@ go 1.17 require ( dubbo.apache.org/dubbo-go/v3 v3.0.2 - github.com/alibaba/ioc-golang v0.0.0-20220726064435-a135d55b94e9 + github.com/alibaba/ioc-golang v0.0.0-20220726180817-a8481c622579 github.com/alibaba/ioc-golang/iocli v0.0.0-20220705164359-95be52984ef8 github.com/apache/rocketmq-client-go/v2 v2.1.0 github.com/apache/thrift v0.16.0 diff --git a/iocli/Makefile b/iocli/Makefile index 6235c21..eaae8f9 100644 --- a/iocli/Makefile +++ b/iocli/Makefile @@ -5,4 +5,12 @@ test: go test ./... lint: tidy - golangci-lint run \ No newline at end of file + golangci-lint run + +build-all-platform: + mkdir -p ./.release/linux-amd64 && GOOS=linux GOARCH=amd64 go build -o ./.release/linux-amd64 && tar -czvf ./.release/iocli-linux-amd64.tar.gz ./.release/linux-amd64 + mkdir -p ./.release/linux-arm64 && GOOS=linux GOARCH=arm64 go build -o ./.release/linux-arm64 && tar -czvf ./.release/iocli-linux-arm64.tar.gz ./.release/linux-arm64 + mkdir -p ./.release/darwin-amd64 && GOOS=darwin GOARCH=amd64 go build -o ./.release/darwin-amd64 && tar -czvf ./.release/iocli-darwin-amd64.tar.gz ./.release/darwin-amd64 + mkdir -p ./.release/darwin-arm64 && GOOS=darwin GOARCH=arm64 go build -o ./.release/darwin-arm64 && tar -czvf ./.release/iocli-darwin-arm64.tar.gz ./.release/darwin-arm64 + mkdir -p ./.release/windows-amd64 && GOOS=windows GOARCH=amd64 go build -o ./.release/windows-amd64 && tar -czvf ./.release/iocli-windows-amd64.tar.gz ./.release/windows-amd64 + mkdir -p ./.release/windows-arm64 && GOOS=windows GOARCH=arm64 go build -o ./.release/windows-arm64 && tar -czvf ./.release/iocli-windows-arm64.tar.gz ./.release/windows-arm64 diff --git a/iocli/go.mod b/iocli/go.mod index c44b938..2f6383b 100644 --- a/iocli/go.mod +++ b/iocli/go.mod @@ -3,8 +3,8 @@ module github.com/alibaba/ioc-golang/iocli go 1.17 require ( - github.com/alibaba/ioc-golang v0.0.0-20220726064435-a135d55b94e9 - github.com/alibaba/ioc-golang/extension v0.0.0-20220726064435-a135d55b94e9 + github.com/alibaba/ioc-golang v0.0.0-20220726180817-a8481c622579 + github.com/alibaba/ioc-golang/extension v0.0.0-20220726180817-a8481c622579 github.com/gobuffalo/packr/v2 v2.8.3 github.com/spf13/cobra v1.5.0 sigs.k8s.io/controller-tools v0.9.0 @@ -59,6 +59,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/flatbuffers v1.12.1 // indirect + github.com/google/uuid v1.3.0 // indirect github.com/gorilla/handlers v1.5.1 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect diff --git a/iocli/go.sum b/iocli/go.sum index 7d03491..3e7d927 100644 --- a/iocli/go.sum +++ b/iocli/go.sum @@ -62,10 +62,10 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alibaba/ioc-golang v0.0.0-20220726064435-a135d55b94e9 h1:0IKwfhWUjtutwwJNcCum/9F1Erit4LHpeGwaPwIbKok= -github.com/alibaba/ioc-golang v0.0.0-20220726064435-a135d55b94e9/go.mod h1:CbkGnD1t/oVYYoOzxS/arJ8hE6xAX4+Z9BYkrnZoWcM= -github.com/alibaba/ioc-golang/extension v0.0.0-20220726064435-a135d55b94e9 h1:5Ab6OPtxviSP8NafqMASntzi4+wqez6qih/wuPALrec= -github.com/alibaba/ioc-golang/extension v0.0.0-20220726064435-a135d55b94e9/go.mod h1:GQgCGWmuDdhha0A1dSGma4WpJhj3MUP2RdJCBFb0B8w= +github.com/alibaba/ioc-golang v0.0.0-20220726180817-a8481c622579 h1:yU4kpw5+TqHf7RIQjL6sIj/JEHZa9YJX/FDwUCftxi4= +github.com/alibaba/ioc-golang v0.0.0-20220726180817-a8481c622579/go.mod h1:jOGOivOSn3nZ6YlzbU5mDTULzYaTYuZRMYAfE7T9LuQ= +github.com/alibaba/ioc-golang/extension v0.0.0-20220726180817-a8481c622579 h1:PB2YDUT15ddudzP5XgdtLbVYAk+KDgx1fdsYY3RDpnM= +github.com/alibaba/ioc-golang/extension v0.0.0-20220726180817-a8481c622579/go.mod h1:k47ZBXFQpBmwMunDID+Iz3ViLttNJFbE5lWwoPbxLQA= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= @@ -368,6 +368,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4 github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= diff --git a/autowire/monkey.go b/logger/logger_test.go similarity index 74% rename from autowire/monkey.go rename to logger/logger_test.go index 26b592d..326391c 100644 --- a/autowire/monkey.go +++ b/logger/logger_test.go @@ -13,16 +13,16 @@ * limitations under the License. */ -package autowire +package logger -// monkey function +import ( + "testing" -var mf func(interface{}, string) + "github.com/stretchr/testify/assert" +) -func RegisterMonkeyFunction(f func(interface{}, string)) { - mf = f -} - -func GetMonkeyFunction() func(interface{}, string) { - return mf +func TestLoggerDisable(t *testing.T) { + assert.True(t, !disableLogs) + Disable() + assert.True(t, disableLogs) } diff --git a/test/stress/aop/proxy_test.go b/test/stress/aop/proxy_test.go index 4d39941..9e3dc50 100644 --- a/test/stress/aop/proxy_test.go +++ b/test/stress/aop/proxy_test.go @@ -56,3 +56,35 @@ Total: 100000, Success: 100000, Fail: 0, AvgRT: `)) wg.Wait() <-closeCh } + +func TestAOPRecursive(t *testing.T) { + assert.Nil(t, ioc.Load()) + closeCh := make(chan struct{}) + go func() { + output, err := iocli_command.Run([]string{"monitor"}, time.Second*6) + assert.Nil(t, err) + assert.True(t, strings.Contains(output, `github.com/alibaba/ioc-golang/test/stress/aop.RecursiveApp.RunTest() +Total: 901, Success: 901, Fail: 0, AvgRT: `)) + assert.True(t, strings.Contains(output, `us, FailRate: 0.00% +github.com/alibaba/ioc-golang/test/stress/aop.ServiceImpl1.GetHelloString() +Total: 2, Success: 2, Fail: 0, AvgRT: `)) + close(closeCh) + }() + time.Sleep(time.Second * 1) + recApp, err := GetRecursiveAppIOCInterfaceSingleton() + assert.Nil(t, err) + recApp.RunTest(t) + <-closeCh + + recApp.Reset() + closeCh = make(chan struct{}) + go func() { + output, err := iocli_command.Run([]string{"trace"}, time.Second*6) + assert.Nil(t, err) + assert.Equal(t, 901, strings.Count(output, ", OperationName: github.com/alibaba/ioc-golang/test/stress/aop.(*recursiveApp_).RunTest, StartTime: ")) + close(closeCh) + }() + time.Sleep(time.Second * 1) + recApp.RunTest(t) + <-closeCh +} diff --git a/test/stress/aop/recursive_app.go b/test/stress/aop/recursive_app.go new file mode 100644 index 0000000..ccc4614 --- /dev/null +++ b/test/stress/aop/recursive_app.go @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2022, Alibaba Group; + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package aop + +import ( + "testing" + + "github.com/alibaba/ioc-golang/autowire/singleton" + "github.com/alibaba/ioc-golang/autowire/util" + + "github.com/stretchr/testify/assert" +) + +// +ioc:autowire=true +// +ioc:autowire:type=singleton + +type RecursiveApp struct { + // inject main.ServiceImpl1 pointer to Service interface with proxy wrapper + ServiceImpl1 Service `normal:"github.com/alibaba/ioc-golang/test/stress/aop.ServiceImpl1"` + counter int +} + +func (s *RecursiveApp) Reset() { + s.counter = 0 +} + +func (s *RecursiveApp) RunTest(t *testing.T) { + if s.counter < 900 { + s.counter++ + s, err := singleton.GetImplWithProxy(util.GetSDIDByStructPtr(s), nil) + assert.Nil(t, err) + s.(RecursiveAppIOCInterface).RunTest(t) + return + } + assert.Equal(t, expectString, s.ServiceImpl1.GetHelloString(reqString)) + + // test creat by API + createByAPIService1, err := GetServiceImpl1IOCInterface() + assert.Nil(t, err) + assert.Equal(t, expectString, createByAPIService1.GetHelloString(reqString)) +} diff --git a/test/stress/aop/zz_generated.ioc.go b/test/stress/aop/zz_generated.ioc.go index 9c59078..0abd501 100644 --- a/test/stress/aop/zz_generated.ioc.go +++ b/test/stress/aop/zz_generated.ioc.go @@ -10,8 +10,8 @@ import ( autowire "github.com/alibaba/ioc-golang/autowire" normal "github.com/alibaba/ioc-golang/autowire/normal" - singleton "github.com/alibaba/ioc-golang/autowire/singleton" - util "github.com/alibaba/ioc-golang/autowire/util" + "github.com/alibaba/ioc-golang/autowire/singleton" + "github.com/alibaba/ioc-golang/autowire/util" ) func init() { @@ -30,6 +30,21 @@ func init() { }, } normal.RegisterStructDescriptor(normalAppStructDescriptor) + normal.RegisterStructDescriptor(&autowire.StructDescriptor{ + Factory: func() interface{} { + return &recursiveApp_{} + }, + }) + recursiveAppStructDescriptor := &autowire.StructDescriptor{ + Factory: func() interface{} { + return &RecursiveApp{} + }, + Metadata: map[string]interface{}{ + "aop": map[string]interface{}{}, + "autowire": map[string]interface{}{}, + }, + } + singleton.RegisterStructDescriptor(recursiveAppStructDescriptor) normal.RegisterStructDescriptor(&autowire.StructDescriptor{ Factory: func() interface{} { return &serviceImpl1_{} @@ -88,6 +103,19 @@ func (n *normalApp_) RunTest(t *testingx.T) { n.RunTest_(t) } +type recursiveApp_ struct { + Reset_ func() + RunTest_ func(t *testingx.T) +} + +func (r *recursiveApp_) Reset() { + r.Reset_() +} + +func (r *recursiveApp_) RunTest(t *testingx.T) { + r.RunTest_(t) +} + type serviceImpl1_ struct { GetHelloString_ func(name string) string } @@ -116,6 +144,11 @@ type NormalAppIOCInterface interface { RunTest(t *testingx.T) } +type RecursiveAppIOCInterface interface { + Reset() + RunTest(t *testingx.T) +} + type ServiceImpl1IOCInterface interface { GetHelloString(name string) string } @@ -154,6 +187,32 @@ func GetNormalAppIOCInterface() (NormalAppIOCInterface, error) { return impl, nil } +var _recursiveAppSDID string + +func GetRecursiveAppSingleton() (*RecursiveApp, error) { + if _recursiveAppSDID == "" { + _recursiveAppSDID = util.GetSDIDByStructPtr(new(RecursiveApp)) + } + i, err := singleton.GetImpl(_recursiveAppSDID, nil) + if err != nil { + return nil, err + } + impl := i.(*RecursiveApp) + return impl, nil +} + +func GetRecursiveAppIOCInterfaceSingleton() (RecursiveAppIOCInterface, error) { + if _recursiveAppSDID == "" { + _recursiveAppSDID = util.GetSDIDByStructPtr(new(RecursiveApp)) + } + i, err := singleton.GetImplWithProxy(_recursiveAppSDID, nil) + if err != nil { + return nil, err + } + impl := i.(RecursiveAppIOCInterface) + return impl, nil +} + var _serviceImpl1SDID string func GetServiceImpl1Singleton() (*ServiceImpl1, error) {