diff --git a/.asf.yaml b/.asf.yaml new file mode 100644 index 0000000000000000000000000000000000000000..8d84e695a59e8064cf06a2f8879564efd5588d44 --- /dev/null +++ b/.asf.yaml @@ -0,0 +1,5 @@ +notifications: + commits: commits@dubbo.apache.org + issues: notifications@dubbo.apache.org + pullrequests: notifications@dubbo.apache.org + jira_options: link label link label diff --git a/.travis.yml b/.travis.yml index 1b46f5d872932b7ed307ceaf802c95997b2800e6..566c88ece05bd80175eea2d1de8fd061a279e273 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,32 @@ -language: go +dist: trusty +sudo: required +# define the dependence env +language: go os: - linux - go: - "1.13" - +services: + - docker env: - GO111MODULE=on - install: true +# define ci-stage script: + # license-check + - echo 'start license check' - go fmt ./... && [[ -z `git status -s` ]] - sh before_validate_license.sh - chmod u+x /tmp/tools/license/license-header-checker - /tmp/tools/license/license-header-checker -v -a -r -i vendor /tmp/tools/license/license.txt . go && [[ -z `git status -s` ]] + # unit-test + - echo 'start unit-test' - chmod u+x before_ut.sh && ./before_ut.sh - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic + # integrate-test + - chmod +x integrate_test.sh && ./integrate_test.sh after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/README.md b/README.md index 5c4de0559d534e03b23d136da5f5f23ff6768d55..3f8394536f944518f8d969289147272c32f169da 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Apache License, Version 2.0 ## Release note ## -[v1.4.0-rc1 - Mar 12, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0-rc1) +[v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0) [v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0) @@ -176,5 +176,5 @@ About dubbo-go benchmarking report, please refer to [dubbo benchmarking report]( If you are using [apache/dubbo-go](github.com/apache/dubbo-go) and think that it helps you or want do some contributions to it, please add your company to to [the user list](https://github.com/apache/dubbo-go/issues/2) to let us know your needs.  - +  diff --git a/README_CN.md b/README_CN.md index 5dec68fd6134fc58bf265ea382473d724332ecc6..582c5cf04cba08d4167c87b40fd0e86a3aa2ceb0 100644 --- a/README_CN.md +++ b/README_CN.md @@ -15,7 +15,7 @@ Apache License, Version 2.0 ## 鍙戝竷鏃ュ織 ## -[v1.4.0-rc1 - 2020骞�3鏈�12鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.0-rc1) +[v1.4.0 - 2020骞�3鏈�17鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.0) [v1.3.0 - 2020骞�3鏈�1鏃(https://github.com/apache/dubbo-go/releases/tag/v1.3.0) @@ -175,5 +175,5 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic 鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呭悜瀵瑰叾鍋氭敼杩涳紝璇峰繚鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撲箣銆�  - +  diff --git a/before_ut.bat b/before_ut.bat index 5e1c877af229b2b30bffc8b802cc35b6aab6c80a..dc51008dadaad21af6fcb6021863ff4102b0afa2 100644 --- a/before_ut.bat +++ b/before_ut.bat @@ -20,7 +20,7 @@ set zkJarPath=remoting/zookeeper/zookeeper-4unittest/contrib/fatjar set zkJar=%zkJarPath%/%zkJarName% if not exist "%zkJar%" ( - md %zkJarPath% + md "%zkJarPath%" curl -L %remoteJarUrl% -o %zkJar% ) diff --git a/cluster/cluster.go b/cluster/cluster.go index 617ce5ebf0fa7b5dc7f6047caacec9865aa6960f..7a0df4c0a24faf45d6d3d3da8651600481f782ba 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -21,7 +21,8 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Cluster ... +// Cluster +// Extension - Cluster type Cluster interface { Join(Directory) protocol.Invoker } diff --git a/cluster/cluster_impl/available_cluster.go b/cluster/cluster_impl/available_cluster.go index e041d91edb969c8a9b6f0309d1146f1ea874b42a..b70a97fad2de1b267ac1c6a5f0672ff445fadcc3 100644 --- a/cluster/cluster_impl/available_cluster.go +++ b/cluster/cluster_impl/available_cluster.go @@ -31,7 +31,9 @@ func init() { extension.SetCluster(available, NewAvailableCluster) } -// NewAvailableCluster ... +// NewAvailableCluster returns a cluster instance +// +// Obtain available service providers func NewAvailableCluster() cluster.Cluster { return &availableCluster{} } diff --git a/cluster/cluster_impl/available_cluster_invoker.go b/cluster/cluster_impl/available_cluster_invoker.go index e69f8b9f471d2aa6241b34bd01990d5d003feb3e..39a892379d3871565ec977aac874bb6509515ee2 100644 --- a/cluster/cluster_impl/available_cluster_invoker.go +++ b/cluster/cluster_impl/available_cluster_invoker.go @@ -35,7 +35,7 @@ type availableClusterInvoker struct { baseClusterInvoker } -// NewAvailableClusterInvoker ... +// NewAvailableClusterInvoker returns a cluster invoker instance func NewAvailableClusterInvoker(directory cluster.Directory) protocol.Invoker { return &availableClusterInvoker{ baseClusterInvoker: newBaseClusterInvoker(directory), diff --git a/cluster/cluster_impl/available_cluster_invoker_test.go b/cluster/cluster_impl/available_cluster_invoker_test.go index c2cebd3843d453a2d46d031e711e0efebd240fda..063100020ad36192a051d1e736af7264cd8df42d 100644 --- a/cluster/cluster_impl/available_cluster_invoker_test.go +++ b/cluster/cluster_impl/available_cluster_invoker_test.go @@ -19,6 +19,7 @@ package cluster_impl import ( "context" + "fmt" "strings" "testing" ) @@ -32,6 +33,7 @@ import ( "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -39,10 +41,11 @@ import ( ) var ( - availableUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + availableUrl, _ = common.NewURL(fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", + constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) -func registerAvailable(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +func registerAvailable(invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) availableCluster := NewAvailableCluster() @@ -60,7 +63,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerAvailable(t, invoker) + clusterInvoker := registerAvailable(invoker) mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().IsAvailable().Return(true) @@ -76,7 +79,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerAvailable(t, invoker) + clusterInvoker := registerAvailable(invoker) invoker.EXPECT().IsAvailable().Return(false) diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go index 12799994125c4bf5d968dfc811cda374effbf85c..bbdfa715d7cdc461689e60a5a41171ad5c9770e1 100644 --- a/cluster/cluster_impl/base_cluster_invoker.go +++ b/cluster/cluster_impl/base_cluster_invoker.go @@ -87,8 +87,11 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error { } func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { - var selectedInvoker protocol.Invoker + if len(invokers) <= 0 { + return selectedInvoker + } + url := invokers[0].GetUrl() sticky := url.GetParamBool(constant.STICKY_KEY, false) //Get the service method sticky config if have @@ -98,19 +101,17 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p invoker.stickyInvoker = nil } - if sticky && invoker.stickyInvoker != nil && (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) { - if invoker.availablecheck && invoker.stickyInvoker.IsAvailable() { - return invoker.stickyInvoker - } + if sticky && invoker.availablecheck && + invoker.stickyInvoker != nil && invoker.stickyInvoker.IsAvailable() && + (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) { + return invoker.stickyInvoker } selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked) - if sticky { invoker.stickyInvoker = selectedInvoker } return selectedInvoker - } func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { diff --git a/cluster/cluster_impl/base_cluster_invoker_test.go b/cluster/cluster_impl/base_cluster_invoker_test.go index d074697b85a3cf5b770de90da4847043d98c9df1..8121e5c0eab16b92b323fbc0e6e944231d1ed1b9 100644 --- a/cluster/cluster_impl/base_cluster_invoker_test.go +++ b/cluster/cluster_impl/base_cluster_invoker_test.go @@ -33,25 +33,33 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_StickyNormal(t *testing.T) { +const ( + baseClusterInvokerMethodName = "getUser" + baseClusterInvokerFormat = "dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider" +) + +func TestStickyNormal(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf(baseClusterInvokerFormat, i)) url.SetParam("sticky", "true") invokers = append(invokers, NewMockInvoker(url, 1)) } base := &baseClusterInvoker{} base.availablecheck = true invoked := []protocol.Invoker{} - result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked) - result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked) + + tmpRandomBalance := loadbalance.NewRandomLoadBalance() + tmpInvocation := invocation.NewRPCInvocation(baseClusterInvokerMethodName, nil, nil) + result := base.doSelect(tmpRandomBalance, tmpInvocation, invokers, invoked) + result1 := base.doSelect(tmpRandomBalance, tmpInvocation, invokers, invoked) assert.Equal(t, result, result1) } -func Test_StickyNormalWhenError(t *testing.T) { +func TestStickyNormalWhenError(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf(baseClusterInvokerFormat, i)) url.SetParam("sticky", "true") invokers = append(invokers, NewMockInvoker(url, 1)) } @@ -59,8 +67,8 @@ func Test_StickyNormalWhenError(t *testing.T) { base.availablecheck = true invoked := []protocol.Invoker{} - result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked) + result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation(baseClusterInvokerMethodName, nil, nil), invokers, invoked) invoked = append(invoked, result) - result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked) + result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation(baseClusterInvokerMethodName, nil, nil), invokers, invoked) assert.NotEqual(t, result, result1) } diff --git a/cluster/cluster_impl/broadcast_cluster.go b/cluster/cluster_impl/broadcast_cluster.go index a1692e96c5b68bc01adeab6600e99dff16a1bea1..ba454af6a8553f31b72b1d30ef5f44ec7a8278d2 100644 --- a/cluster/cluster_impl/broadcast_cluster.go +++ b/cluster/cluster_impl/broadcast_cluster.go @@ -31,7 +31,10 @@ func init() { extension.SetCluster(broadcast, NewBroadcastCluster) } -// NewBroadcastCluster ... +// NewBroadcastCluster returns a broadcast cluster instance. +// +// Calling all providers' broadcast one by one. All errors will be reported. +// It is usually used to notify all providers to update local resource information such as caches or logs. func NewBroadcastCluster() cluster.Cluster { return &broadcastCluster{} } diff --git a/cluster/cluster_impl/broadcast_cluster_invoker_test.go b/cluster/cluster_impl/broadcast_cluster_invoker_test.go index 9b5733e98b142759c3317f9cb3e3d3f08eea81e4..08d0002ee79b2f3fda5a50ce90747c0aaad91932 100644 --- a/cluster/cluster_impl/broadcast_cluster_invoker_test.go +++ b/cluster/cluster_impl/broadcast_cluster_invoker_test.go @@ -20,6 +20,7 @@ package cluster_impl import ( "context" "errors" + "fmt" "testing" ) @@ -32,6 +33,7 @@ import ( "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -39,10 +41,11 @@ import ( ) var ( - broadcastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + broadcastUrl, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) -func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker { +func registerBroadcast(mockInvokers ...*mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) invokers := []protocol.Invoker{} @@ -59,7 +62,7 @@ func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol return clusterInvoker } -func Test_BroadcastInvokeSuccess(t *testing.T) { +func TestBroadcastInvokeSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -72,13 +75,13 @@ func Test_BroadcastInvokeSuccess(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) } - clusterInvoker := registerBroadcast(t, invokers...) + clusterInvoker := registerBroadcast(invokers...) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) } -func Test_BroadcastInvokeFailed(t *testing.T) { +func TestBroadcastInvokeFailed(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -102,7 +105,7 @@ func Test_BroadcastInvokeFailed(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) } - clusterInvoker := registerBroadcast(t, invokers...) + clusterInvoker := registerBroadcast(invokers...) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockFailedResult.Err, result.Error()) diff --git a/cluster/cluster_impl/failback_cluster.go b/cluster/cluster_impl/failback_cluster.go index 76573571684c07f63609009f59ab0ac881ae1b50..432e33122c2ee599bc848ca9ab1842084da5ef68 100644 --- a/cluster/cluster_impl/failback_cluster.go +++ b/cluster/cluster_impl/failback_cluster.go @@ -31,7 +31,10 @@ func init() { extension.SetCluster(failback, NewFailbackCluster) } -// NewFailbackCluster ... +// NewFailbackCluster returns a failback cluster instance +// +// Failure automatically restored, failed to record the background request, +// regular retransmission. Usually used for message notification operations. func NewFailbackCluster() cluster.Cluster { return &failbackCluster{} } diff --git a/cluster/cluster_impl/failback_cluster_invoker.go b/cluster/cluster_impl/failback_cluster_invoker.go index 46b0ff634e56c45223a5aeb5566b9b1401518960..af17a93756a6f558c7da063eec9d8052b83cbe69 100644 --- a/cluster/cluster_impl/failback_cluster_invoker.go +++ b/cluster/cluster_impl/failback_cluster_invoker.go @@ -72,6 +72,19 @@ func newFailbackClusterInvoker(directory cluster.Directory) protocol.Invoker { return invoker } +func (invoker *failbackClusterInvoker) tryTimerTaskProc(ctx context.Context, retryTask *retryTimerTask) { + invoked := make([]protocol.Invoker, 0) + invoked = append(invoked, retryTask.lastInvoker) + + retryInvoker := invoker.doSelect(retryTask.loadbalance, retryTask.invocation, retryTask.invokers, invoked) + var result protocol.Result + result = retryInvoker.Invoke(ctx, retryTask.invocation) + if result.Error() != nil { + retryTask.lastInvoker = retryInvoker + invoker.checkRetry(retryTask, result.Error()) + } +} + func (invoker *failbackClusterInvoker) process(ctx context.Context) { invoker.ticker = time.NewTicker(time.Second * 1) for range invoker.ticker.C { @@ -91,25 +104,11 @@ func (invoker *failbackClusterInvoker) process(ctx context.Context) { } // ignore return. the get must success. - _, err = invoker.taskList.Get(1) - if err != nil { + if _, err = invoker.taskList.Get(1); err != nil { logger.Warnf("get task found err: %v\n", err) break } - - go func(retryTask *retryTimerTask) { - invoked := make([]protocol.Invoker, 0) - invoked = append(invoked, retryTask.lastInvoker) - - retryInvoker := invoker.doSelect(retryTask.loadbalance, retryTask.invocation, retryTask.invokers, invoked) - var result protocol.Result - result = retryInvoker.Invoke(ctx, retryTask.invocation) - if result.Error() != nil { - retryTask.lastInvoker = retryInvoker - invoker.checkRetry(retryTask, result.Error()) - } - }(retryTask) - + go invoker.tryTimerTaskProc(ctx, retryTask) } } } @@ -129,29 +128,26 @@ func (invoker *failbackClusterInvoker) checkRetry(retryTask *retryTimerTask, err func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) - err := invoker.checkInvokers(invokers, invocation) - if err != nil { + if err := invoker.checkInvokers(invokers, invocation); err != nil { logger.Errorf("Failed to invoke the method %v in the service %v, wait for retry in background. Ignored exception: %v.\n", invocation.MethodName(), invoker.GetUrl().Service(), err) return &protocol.RPCResult{} } - url := invokers[0].GetUrl() - methodName := invocation.MethodName() + //Get the service loadbalance config + url := invokers[0].GetUrl() lb := url.GetParam(constant.LOADBALANCE_KEY, constant.DEFAULT_LOADBALANCE) - //Get the service method loadbalance config if have + methodName := invocation.MethodName() if v := url.GetMethodParam(methodName, constant.LOADBALANCE_KEY, ""); v != "" { lb = v } - loadbalance := extension.GetLoadbalance(lb) + loadBalance := extension.GetLoadbalance(lb) invoked := make([]protocol.Invoker, 0, len(invokers)) - var result protocol.Result - - ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked) + ivk := invoker.doSelect(loadBalance, invocation, invokers, invoked) //DO INVOKE - result = ivk.Invoke(ctx, invocation) + result := ivk.Invoke(ctx, invocation) if result.Error() != nil { invoker.once.Do(func() { invoker.taskList = queue.New(invoker.failbackTasks) @@ -164,7 +160,7 @@ func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation pr return &protocol.RPCResult{} } - timerTask := newRetryTimerTask(loadbalance, invocation, invokers, ivk) + timerTask := newRetryTimerTask(loadBalance, invocation, invokers, ivk) invoker.taskList.Put(timerTask) logger.Errorf("Failback to invoke the method %v in the service %v, wait for retry in background. Ignored exception: %v.\n", @@ -172,7 +168,6 @@ func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation pr // ignore return &protocol.RPCResult{} } - return result } diff --git a/cluster/cluster_impl/failback_cluster_test.go b/cluster/cluster_impl/failback_cluster_test.go index 69418bc3b876f7c9375a2164d78bac2fcbb05043..0edb81d4285fa68ceefd96100b541ba334f95bda 100644 --- a/cluster/cluster_impl/failback_cluster_test.go +++ b/cluster/cluster_impl/failback_cluster_test.go @@ -19,6 +19,7 @@ package cluster_impl import ( "context" + "fmt" "sync" "testing" "time" @@ -34,6 +35,7 @@ import ( "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -41,11 +43,12 @@ import ( ) var ( - failbackUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failbackUrl, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) // registerFailback register failbackCluster to cluster extension. -func registerFailback(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +func registerFailback(invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failbackCluster := NewFailbackCluster() @@ -60,12 +63,12 @@ func registerFailback(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker } // success firstly, failback should return origin invoke result. -func Test_FailbackSuceess(t *testing.T) { +func TestFailbackSuceess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) + clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() @@ -77,12 +80,12 @@ func Test_FailbackSuceess(t *testing.T) { } // failed firstly, success later after one retry. -func Test_FailbackRetryOneSuccess(t *testing.T) { +func TestFailbackRetryOneSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) + clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() @@ -95,7 +98,7 @@ func Test_FailbackRetryOneSuccess(t *testing.T) { wg.Add(1) now := time.Now() mockSuccResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} - invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { + invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result { delta := time.Since(now).Nanoseconds() / int64(time.Second) assert.True(t, delta >= 5) wg.Done() @@ -120,12 +123,12 @@ func Test_FailbackRetryOneSuccess(t *testing.T) { } // failed firstly, and failed again after ech retry time. -func Test_FailbackRetryFailed(t *testing.T) { +func TestFailbackRetryFailed(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) + clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() @@ -141,7 +144,7 @@ func Test_FailbackRetryFailed(t *testing.T) { // add retry call that eventually failed. for i := 0; i < retries; i++ { j := i + 1 - invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { + invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result { delta := time.Since(now).Nanoseconds() / int64(time.Second) assert.True(t, delta >= int64(5*j)) wg.Done() @@ -166,12 +169,12 @@ func Test_FailbackRetryFailed(t *testing.T) { } // add 10 tasks but all failed firstly, and failed again with one retry. -func Test_FailbackRetryFailed10Times(t *testing.T) { +func TestFailbackRetryFailed10Times(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) + clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) clusterInvoker.maxRetries = 10 invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() @@ -184,7 +187,7 @@ func Test_FailbackRetryFailed10Times(t *testing.T) { var wg sync.WaitGroup wg.Add(10) now := time.Now() - invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { + invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result { delta := time.Since(now).Nanoseconds() / int64(time.Second) assert.True(t, delta >= 5) wg.Done() @@ -208,12 +211,12 @@ func Test_FailbackRetryFailed10Times(t *testing.T) { assert.Equal(t, int64(0), clusterInvoker.taskList.Len()) } -func Test_FailbackOutOfLimit(t *testing.T) { +func TestFailbackOutOfLimit(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) + clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) clusterInvoker.failbackTasks = 1 invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() diff --git a/cluster/cluster_impl/failfast_cluster.go b/cluster/cluster_impl/failfast_cluster.go index 1e85485f7144f27a1994b18ba9419d9537d93ca1..ac9ec6b821c1d0333c73fae56169d5bc8256ec5b 100644 --- a/cluster/cluster_impl/failfast_cluster.go +++ b/cluster/cluster_impl/failfast_cluster.go @@ -31,7 +31,10 @@ func init() { extension.SetCluster(failfast, NewFailFastCluster) } -// NewFailFastCluster ... +// NewFailFastCluster returns a failfast cluster instance. +// +// Fast failure, only made a call, failure immediately error. Usually used for non-idempotent write operations, +// such as adding records. func NewFailFastCluster() cluster.Cluster { return &failfastCluster{} } diff --git a/cluster/cluster_impl/failfast_cluster_test.go b/cluster/cluster_impl/failfast_cluster_test.go index c5ab7cd5410ea312e082f8064c13b2356c9b4bb4..77e8e9c5da73bfc8bcf08dbd90351bfd23d7e651 100644 --- a/cluster/cluster_impl/failfast_cluster_test.go +++ b/cluster/cluster_impl/failfast_cluster_test.go @@ -19,6 +19,7 @@ package cluster_impl import ( "context" + "fmt" "testing" ) @@ -32,6 +33,7 @@ import ( "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -39,11 +41,12 @@ import ( ) var ( - failfastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failfastUrl, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) // registerFailfast register failfastCluster to cluster extension. -func registerFailfast(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +func registerFailfast(invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failfastCluster := NewFailFastCluster() @@ -57,12 +60,12 @@ func registerFailfast(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker return clusterInvoker } -func Test_FailfastInvokeSuccess(t *testing.T) { +func TestFailfastInvokeSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailfast(t, invoker) + clusterInvoker := registerFailfast(invoker) invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes() @@ -77,12 +80,12 @@ func Test_FailfastInvokeSuccess(t *testing.T) { assert.Equal(t, 0, res.tried) } -func Test_FailfastInvokeFail(t *testing.T) { +func TestFailfastInvokeFail(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailfast(t, invoker) + clusterInvoker := registerFailfast(invoker) invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes() diff --git a/cluster/cluster_impl/failover_cluster.go b/cluster/cluster_impl/failover_cluster.go index b16be3bafd43c7de8e2fadd109a73a3ea710e225..d30a743e034dafabad87381cdaa356e7603b74d1 100644 --- a/cluster/cluster_impl/failover_cluster.go +++ b/cluster/cluster_impl/failover_cluster.go @@ -31,7 +31,11 @@ func init() { extension.SetCluster(name, NewFailoverCluster) } -// NewFailoverCluster ... +// NewFailoverCluster returns a failover cluster instance +// +// Failure automatically switch, when there is a failure, +// retry the other server (default). Usually used for read operations, +// but retries can result in longer delays. func NewFailoverCluster() cluster.Cluster { return &failoverCluster{} } diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go index 6178a05a1226ba629d2456ad6886b02a26288e45..66adabd1043d6e5d770704774dda22ba9e6faebe 100644 --- a/cluster/cluster_impl/failover_cluster_invoker.go +++ b/cluster/cluster_impl/failover_cluster_invoker.go @@ -45,52 +45,35 @@ func newFailoverClusterInvoker(directory cluster.Directory) protocol.Invoker { } func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { + var ( + result protocol.Result + invoked []protocol.Invoker + providers []string + ) invokers := invoker.directory.List(invocation) - err := invoker.checkInvokers(invokers, invocation) - - if err != nil { + if err := invoker.checkInvokers(invokers, invocation); err != nil { return &protocol.RPCResult{Err: err} } - loadbalance := getLoadBalance(invokers[0], invocation) - methodName := invocation.MethodName() - url := invokers[0].GetUrl() - - //get reties - retriesConfig := url.GetParam(constant.RETRIES_KEY, constant.DEFAULT_RETRIES) + retries := getRetries(invokers, methodName) + loadBalance := getLoadBalance(invokers[0], invocation) - //Get the service method loadbalance config if have - if v := url.GetMethodParam(methodName, constant.RETRIES_KEY, ""); len(v) != 0 { - retriesConfig = v - } - retries, err := strconv.Atoi(retriesConfig) - if err != nil || retries < 0 { - logger.Error("Your retries config is invalid,pls do a check. And will use the default retries configuration instead.") - retries = constant.DEFAULT_RETRIES_INT - } - invoked := []protocol.Invoker{} - providers := []string{} - var result protocol.Result - if retries > len(invokers) { - retries = len(invokers) - } for i := 0; i <= retries; i++ { //Reselect before retry to avoid a change of candidate `invokers`. //NOTE: if `invokers` changed, then `invoked` also lose accuracy. if i > 0 { - err := invoker.checkWhetherDestroyed() - if err != nil { + if err := invoker.checkWhetherDestroyed(); err != nil { return &protocol.RPCResult{Err: err} } + invokers = invoker.directory.List(invocation) - err = invoker.checkInvokers(invokers, invocation) - if err != nil { + if err := invoker.checkInvokers(invokers, invocation); err != nil { return &protocol.RPCResult{Err: err} } } - ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked) + ivk := invoker.doSelect(loadBalance, invocation, invokers, invoked) if ivk == nil { continue } @@ -100,13 +83,40 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr if result.Error() != nil { providers = append(providers, ivk.GetUrl().Key()) continue - } else { - return result } + return result } + ip, _ := gxnet.GetLocalIP() - return &protocol.RPCResult{Err: perrors.Errorf("Failed to invoke the method %v in the service %v. Tried %v times of "+ - "the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. Last error is %v.", - methodName, invoker.GetUrl().Service(), retries, providers, len(providers), len(invokers), invoker.directory.GetUrl(), ip, constant.Version, result.Error().Error(), - )} + invokerSvc := invoker.GetUrl().Service() + invokerUrl := invoker.directory.GetUrl() + return &protocol.RPCResult{ + Err: perrors.Errorf("Failed to invoke the method %v in the service %v. Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. Last error is %v.", + methodName, invokerSvc, retries, providers, len(providers), len(invokers), invokerUrl, ip, constant.Version, result.Error().Error(), + )} +} + +func getRetries(invokers []protocol.Invoker, methodName string) int { + if len(invokers) <= 0 { + return constant.DEFAULT_RETRIES_INT + } + + url := invokers[0].GetUrl() + //get reties + retriesConfig := url.GetParam(constant.RETRIES_KEY, constant.DEFAULT_RETRIES) + //Get the service method loadbalance config if have + if v := url.GetMethodParam(methodName, constant.RETRIES_KEY, ""); len(v) != 0 { + retriesConfig = v + } + + retries, err := strconv.Atoi(retriesConfig) + if err != nil || retries < 0 { + logger.Error("Your retries config is invalid,pls do a check. And will use the default retries configuration instead.") + retries = constant.DEFAULT_RETRIES_INT + } + + if retries > len(invokers) { + retries = len(invokers) + } + return retries } diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go index 1be21067a6a9045cb6ae6f84655d516fea1f844b..e05b79202cd202334db1c19421e3163ee28bac26 100644 --- a/cluster/cluster_impl/failover_cluster_test.go +++ b/cluster/cluster_impl/failover_cluster_test.go @@ -101,14 +101,14 @@ func (bi *MockInvoker) Destroy() { var count int -func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocations ...*invocation.RPCInvocation) protocol.Result { +func normalInvoke(successCount int, urlParam url.Values, invocations ...*invocation.RPCInvocation) protocol.Result { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failoverCluster := NewFailoverCluster() invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) - invokers = append(invokers, NewMockInvoker(url, successCount)) + newUrl, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) + invokers = append(invokers, NewMockInvoker(newUrl, successCount)) } staticDir := directory.NewStaticDirectory(invokers) @@ -119,40 +119,40 @@ func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocatio return clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) } -func Test_FailoverInvokeSuccess(t *testing.T) { +func TestFailoverInvokeSuccess(t *testing.T) { urlParams := url.Values{} - result := normalInvoke(t, 3, urlParams) + result := normalInvoke(3, urlParams) assert.NoError(t, result.Error()) count = 0 } -func Test_FailoverInvokeFail(t *testing.T) { +func TestFailoverInvokeFail(t *testing.T) { urlParams := url.Values{} - result := normalInvoke(t, 4, urlParams) + result := normalInvoke(4, urlParams) assert.Errorf(t, result.Error(), "error") count = 0 } -func Test_FailoverInvoke1(t *testing.T) { +func TestFailoverInvoke1(t *testing.T) { urlParams := url.Values{} urlParams.Set(constant.RETRIES_KEY, "3") - result := normalInvoke(t, 4, urlParams) + result := normalInvoke(4, urlParams) assert.NoError(t, result.Error()) count = 0 } -func Test_FailoverInvoke2(t *testing.T) { +func TestFailoverInvoke2(t *testing.T) { urlParams := url.Values{} urlParams.Set(constant.RETRIES_KEY, "2") urlParams.Set("methods.test."+constant.RETRIES_KEY, "3") ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test")) - result := normalInvoke(t, 4, urlParams, ivc) + result := normalInvoke(4, urlParams, ivc) assert.NoError(t, result.Error()) count = 0 } -func Test_FailoverDestroy(t *testing.T) { +func TestFailoverDestroy(t *testing.T) { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failoverCluster := NewFailoverCluster() @@ -170,5 +170,4 @@ func Test_FailoverDestroy(t *testing.T) { count = 0 clusterInvoker.Destroy() assert.Equal(t, false, clusterInvoker.IsAvailable()) - } diff --git a/cluster/cluster_impl/failsafe_cluster.go b/cluster/cluster_impl/failsafe_cluster.go index 177d24a585b5f72fb0667215beb8d11147cc2922..f708b7fb9108bdd17fec5dc68dc1e4249c8199d4 100644 --- a/cluster/cluster_impl/failsafe_cluster.go +++ b/cluster/cluster_impl/failsafe_cluster.go @@ -31,7 +31,10 @@ func init() { extension.SetCluster(failsafe, NewFailsafeCluster) } -// NewFailsafeCluster ... +// NewFailsafeCluster returns a failsafe cluster instance. +// +// Failure of security, anomalies, directly ignored. Usually it is +// used to write audit logs and other operations. func NewFailsafeCluster() cluster.Cluster { return &failsafeCluster{} } diff --git a/cluster/cluster_impl/failsafe_cluster_test.go b/cluster/cluster_impl/failsafe_cluster_test.go index 0bfeb576bd095508ef122c55c1345208c50eb339..d9a716e1ae65a84b605b4b7af1872b3a85dc9369 100644 --- a/cluster/cluster_impl/failsafe_cluster_test.go +++ b/cluster/cluster_impl/failsafe_cluster_test.go @@ -19,6 +19,7 @@ package cluster_impl import ( "context" + "fmt" "testing" ) @@ -32,6 +33,7 @@ import ( "github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -39,11 +41,12 @@ import ( ) var ( - failsafeUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failsafeUrl, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) // registerFailsafe register failsafeCluster to cluster extension. -func registerFailsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +func registerFailsafe(invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failsafeCluster := NewFailsafeCluster() @@ -57,12 +60,12 @@ func registerFailsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker return clusterInvoker } -func Test_FailSafeInvokeSuccess(t *testing.T) { +func TestFailSafeInvokeSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailsafe(t, invoker) + clusterInvoker := registerFailsafe(invoker) invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() @@ -76,12 +79,12 @@ func Test_FailSafeInvokeSuccess(t *testing.T) { assert.True(t, res.success) } -func Test_FailSafeInvokeFail(t *testing.T) { +func TestFailSafeInvokeFail(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := registerFailsafe(t, invoker) + clusterInvoker := registerFailsafe(invoker) invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() diff --git a/cluster/cluster_impl/forking_cluster.go b/cluster/cluster_impl/forking_cluster.go index a6f7a7b45475f8a26b9b2027a8b4a2fa4f95d509..0e6cd26882788a1f897d0d4dc8e0d4eb0a9d4218 100644 --- a/cluster/cluster_impl/forking_cluster.go +++ b/cluster/cluster_impl/forking_cluster.go @@ -31,7 +31,10 @@ func init() { extension.SetCluster(forking, NewForkingCluster) } -// NewForkingCluster ... +// NewForkingCluster returns a forking cluster instance. +// +// Multiple servers are invoked in parallel, returning as soon as one succeeds. +// Usually it is used for real-time demanding read operations while wasting more service resources. func NewForkingCluster() cluster.Cluster { return &forkingCluster{} } diff --git a/cluster/cluster_impl/forking_cluster_invoker.go b/cluster/cluster_impl/forking_cluster_invoker.go index 732569416daea8f878569db143271139b791ceca..a5a3f2ec6605dfb843fab09dff0a53000bbc3298 100644 --- a/cluster/cluster_impl/forking_cluster_invoker.go +++ b/cluster/cluster_impl/forking_cluster_invoker.go @@ -46,14 +46,12 @@ func newForkingClusterInvoker(directory cluster.Directory) protocol.Invoker { // Invoke ... func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { - err := invoker.checkWhetherDestroyed() - if err != nil { + if err := invoker.checkWhetherDestroyed(); err != nil { return &protocol.RPCResult{Err: err} } invokers := invoker.directory.List(invocation) - err = invoker.checkInvokers(invokers, invocation) - if err != nil { + if err := invoker.checkInvokers(invokers, invocation); err != nil { return &protocol.RPCResult{Err: err} } @@ -63,11 +61,9 @@ func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation pro if forks < 0 || forks > len(invokers) { selected = invokers } else { - selected = make([]protocol.Invoker, 0) - loadbalance := getLoadBalance(invokers[0], invocation) + loadBalance := getLoadBalance(invokers[0], invocation) for i := 0; i < forks; i++ { - ivk := invoker.doSelect(loadbalance, invocation, invokers, selected) - if ivk != nil { + if ivk := invoker.doSelect(loadBalance, invocation, invokers, selected); ivk != nil { selected = append(selected, ivk) } } @@ -77,8 +73,7 @@ func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation pro for _, ivk := range selected { go func(k protocol.Invoker) { result := k.Invoke(ctx, invocation) - err := resultQ.Put(result) - if err != nil { + if err := resultQ.Put(result); err != nil { logger.Errorf("resultQ put failed with exception: %v.\n", err) } }(ivk) @@ -99,6 +94,5 @@ func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation pro if !ok { return &protocol.RPCResult{Err: fmt.Errorf("failed to forking invoke provider %v, but not legal resp", selected)} } - return result } diff --git a/cluster/cluster_impl/forking_cluster_test.go b/cluster/cluster_impl/forking_cluster_test.go index 526b137d71c46c166367ac3b3308f9ad5b941538..a2fa136d312db900f45449c92a59009c6661571c 100644 --- a/cluster/cluster_impl/forking_cluster_test.go +++ b/cluster/cluster_impl/forking_cluster_test.go @@ -19,6 +19,7 @@ package cluster_impl import ( "context" + "fmt" "strconv" "sync" "testing" @@ -42,10 +43,11 @@ import ( ) var ( - forkingUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + forkingUrl, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) ) -func registerForking(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker { +func registerForking(mockInvokers ...*mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance(loadbalance.RoundRobin, loadbalance.NewRoundRobinLoadBalance) invokers := []protocol.Invoker{} @@ -62,7 +64,7 @@ func registerForking(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.I return clusterInvoker } -func Test_ForkingInvokeSuccess(t *testing.T) { +func TestForkingInvokeSuccess(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -79,20 +81,20 @@ func Test_ForkingInvokeSuccess(t *testing.T) { invokers = append(invokers, invoker) invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( - func(invocation protocol.Invocation) protocol.Result { + func(protocol.Invocation) protocol.Result { wg.Done() return mockResult }) } - clusterInvoker := registerForking(t, invokers...) + clusterInvoker := registerForking(invokers...) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) wg.Wait() } -func Test_ForkingInvokeTimeout(t *testing.T) { +func TestForkingInvokeTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -108,14 +110,14 @@ func Test_ForkingInvokeTimeout(t *testing.T) { invokers = append(invokers, invoker) invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( - func(invocation protocol.Invocation) protocol.Result { + func(protocol.Invocation) protocol.Result { time.Sleep(2 * time.Second) wg.Done() return mockResult }) } - clusterInvoker := registerForking(t, invokers...) + clusterInvoker := registerForking(invokers...) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NotNil(t, result) @@ -123,7 +125,7 @@ func Test_ForkingInvokeTimeout(t *testing.T) { wg.Wait() } -func Test_ForkingInvokeHalfTimeout(t *testing.T) { +func TestForkingInvokeHalfTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -140,13 +142,13 @@ func Test_ForkingInvokeHalfTimeout(t *testing.T) { invoker.EXPECT().IsAvailable().Return(true).AnyTimes() if i == 1 { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( - func(invocation protocol.Invocation) protocol.Result { + func(protocol.Invocation) protocol.Result { wg.Done() return mockResult }) } else { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( - func(invocation protocol.Invocation) protocol.Result { + func(protocol.Invocation) protocol.Result { time.Sleep(2 * time.Second) wg.Done() return mockResult @@ -154,7 +156,7 @@ func Test_ForkingInvokeHalfTimeout(t *testing.T) { } } - clusterInvoker := registerForking(t, invokers...) + clusterInvoker := registerForking(invokers...) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) diff --git a/cluster/cluster_impl/mock_cluster.go b/cluster/cluster_impl/mock_cluster.go index 943c2add68281d01e320252d07b7d58e27b51283..d887cfb45b9c92c859b1396046c1c1c73d46b295 100644 --- a/cluster/cluster_impl/mock_cluster.go +++ b/cluster/cluster_impl/mock_cluster.go @@ -24,7 +24,11 @@ import ( type mockCluster struct{} -// NewMockCluster ... +// NewMockCluster returns a mock cluster instance. +// +// Mock cluster is usually used for service degradation, such as an authentication service. +// When the service provider is completely hung up, the client does not throw an exception, +// return an authorization failure through the Mock data instead. func NewMockCluster() cluster.Cluster { return &mockCluster{} } diff --git a/cluster/cluster_impl/registry_aware_cluster.go b/cluster/cluster_impl/registry_aware_cluster.go index 079b688da65b3e6f6595212ad6e93c3b6ecc6504..fcefa52862a39eece98dca8660e62d9ca144e955 100644 --- a/cluster/cluster_impl/registry_aware_cluster.go +++ b/cluster/cluster_impl/registry_aware_cluster.go @@ -29,7 +29,7 @@ func init() { extension.SetCluster("registryAware", NewRegistryAwareCluster) } -// NewRegistryAwareCluster ... +// NewRegistryAwareCluster returns a registry aware cluster instance func NewRegistryAwareCluster() cluster.Cluster { return ®istryAwareCluster{} } diff --git a/cluster/cluster_impl/registry_aware_cluster_test.go b/cluster/cluster_impl/registry_aware_cluster_test.go index 3d0dcc0159839eb0a08aed842ee084449458c645..74584b44800fce3342956f4237a63ffbbabf5544 100644 --- a/cluster/cluster_impl/registry_aware_cluster_test.go +++ b/cluster/cluster_impl/registry_aware_cluster_test.go @@ -33,7 +33,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_RegAwareInvokeSuccess(t *testing.T) { +func TestRegAwareInvokeSuccess(t *testing.T) { regAwareCluster := NewRegistryAwareCluster() diff --git a/cluster/directory.go b/cluster/directory.go index 5a03b3a4490ce0b3aadece8a9ef43395f845dd12..37f0c3282935bac430d0ae676abc72d60d711c85 100644 --- a/cluster/directory.go +++ b/cluster/directory.go @@ -23,7 +23,7 @@ import ( ) // Directory -//Extension - Directory +// Extension - Directory type Directory interface { common.Node List(invocation protocol.Invocation) []protocol.Invoker diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go index 75d9ef26567df0fbd83f5d9f94c8548d1e8e633d..7865b807ad4bedfd045aadffbfdbf2f19186e293 100644 --- a/cluster/directory/base_directory.go +++ b/cluster/directory/base_directory.go @@ -83,16 +83,16 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { return } - routers := make([]router.Router, 0, len(urls)) + routers := make([]router.PriorityRouter, 0, len(urls)) for _, url := range urls { routerKey := url.GetParam(constant.ROUTER_KEY, "") if len(routerKey) > 0 { factory := extension.GetRouterFactory(url.Protocol) - r, err := factory.NewRouter(url) + r, err := factory.NewPriorityRouter(url) if err != nil { - logger.Errorf("Create router fail. router key: %s, error: %v", routerKey, url.Service(), err) + logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err) return } routers = append(routers, r) diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go index d5993959f1d37f343a612e2bee305461d49535d0..8b60163b79b7120829e51f69238474a127133fb4 100644 --- a/cluster/directory/base_directory_test.go +++ b/cluster/directory/base_directory_test.go @@ -34,19 +34,20 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +var ( + url, _ = common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) + anyUrl, _ = common.NewURL(fmt.Sprintf("condition://%s/com.foo.BarService", constant.ANYHOST_VALUE)) +) + func TestNewBaseDirectory(t *testing.T) { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")) directory := NewBaseDirectory(&url) - assert.NotNil(t, directory) - assert.Equal(t, url, directory.GetUrl()) assert.Equal(t, &url, directory.GetDirectoryUrl()) - } func TestBuildRouterChain(t *testing.T) { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider")) directory := NewBaseDirectory(&url) assert.NotNil(t, directory) @@ -63,9 +64,8 @@ func TestBuildRouterChain(t *testing.T) { } func getRouteUrl(rule string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") - url.AddParam("rule", rule) - url.AddParam("force", "true") - url.AddParam(constant.ROUTER_KEY, "router") + anyUrl.AddParam("rule", rule) + anyUrl.AddParam("force", "true") + anyUrl.AddParam(constant.ROUTER_KEY, "router") return &url } diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go index 9f600fedc40cf29a40abca6c11652935f20473b4..87f51356495dbd0a956c42bf4f34022b4d21ad4d 100644 --- a/cluster/directory/static_directory.go +++ b/cluster/directory/static_directory.go @@ -61,7 +61,7 @@ func (dir *staticDirectory) IsAvailable() bool { // List List invokers func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invoker { l := len(dir.invokers) - invokers := make([]protocol.Invoker, l, l) + invokers := make([]protocol.Invoker, l) copy(invokers, dir.invokers) routerChain := dir.RouterChain() diff --git a/cluster/directory/static_directory_test.go b/cluster/directory/static_directory_test.go index c50c9a4063bd1a372c27e47687cbf63850f76cef..8e75a2c2535058f605c3e9bb6d6a01f9ff91032a 100644 --- a/cluster/directory/static_directory_test.go +++ b/cluster/directory/static_directory_test.go @@ -32,7 +32,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_StaticDirList(t *testing.T) { +func TestStaticDirList(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) @@ -45,7 +45,7 @@ func Test_StaticDirList(t *testing.T) { assert.Len(t, list, 10) } -func Test_StaticDirDestroy(t *testing.T) { +func TestStaticDirDestroy(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) diff --git a/cluster/loadbalance.go b/cluster/loadbalance.go index fb3641a77377eabbd692729a32e2c0c096282f18..a5b344a4952d338e0f481028b3835116f1743773 100644 --- a/cluster/loadbalance.go +++ b/cluster/loadbalance.go @@ -22,7 +22,7 @@ import ( ) // LoadBalance -//Extension - LoadBalance +// Extension - LoadBalance type LoadBalance interface { Select([]protocol.Invoker, protocol.Invocation) protocol.Invoker } diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go index 957c110663d6c56ada15543d372e210fa83bf74b..84fbb268c7a8ec32f007a734e2d6da56ef3c6d25 100644 --- a/cluster/loadbalance/consistent_hash.go +++ b/cluster/loadbalance/consistent_hash.go @@ -27,7 +27,9 @@ import ( "strconv" "strings" ) - +import ( + gxsort "github.com/dubbogo/gost/sort" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/common/constant" @@ -40,7 +42,7 @@ const ( ConsistentHash = "consistenthash" // HashNodes ... HashNodes = "hash.nodes" - // HashArguments ... + // HashArguments key of hash arguments in url HashArguments = "hash.arguments" ) @@ -53,16 +55,18 @@ func init() { extension.SetLoadbalance(ConsistentHash, NewConsistentHashLoadBalance) } -// ConsistentHashLoadBalance ... +// ConsistentHashLoadBalance implementation of load balancing: using consistent hashing type ConsistentHashLoadBalance struct { } -// NewConsistentHashLoadBalance ... +// NewConsistentHashLoadBalance creates NewConsistentHashLoadBalance +// +// The same parameters of the request is always sent to the same provider. func NewConsistentHashLoadBalance() cluster.LoadBalance { return &ConsistentHashLoadBalance{} } -// Select ... +// Select gets invoker based on load balancing strategy func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker { methodName := invocation.MethodName() key := invokers[0].GetUrl().ServiceKey() + "." + methodName @@ -85,27 +89,12 @@ func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocat return selector.Select(invocation) } -// Uint32Slice ... -type Uint32Slice []uint32 - -func (s Uint32Slice) Len() int { - return len(s) -} - -func (s Uint32Slice) Less(i, j int) bool { - return s[i] < s[j] -} - -func (s Uint32Slice) Swap(i, j int) { - s[i], s[j] = s[j], s[i] -} - -// ConsistentHashSelector ... +// ConsistentHashSelector implementation of Selector:get invoker based on load balancing strategy type ConsistentHashSelector struct { hashCode uint32 replicaNum int virtualInvokers map[uint32]protocol.Invoker - keys Uint32Slice + keys gxsort.Uint32Slice argumentIndex []int } @@ -141,7 +130,7 @@ func newConsistentHashSelector(invokers []protocol.Invoker, methodName string, return selector } -// Select ... +// Select gets invoker based on load balancing strategy func (c *ConsistentHashSelector) Select(invocation protocol.Invocation) protocol.Invoker { key := c.toKey(invocation.Arguments()) digest := md5.Sum([]byte(key)) diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go index a44293172c6e2c96bd098a19306f69260b713689..9f22d39dc46243dddda89151e07dbea39ab933fb 100644 --- a/cluster/loadbalance/consistent_hash_test.go +++ b/cluster/loadbalance/consistent_hash_test.go @@ -18,6 +18,7 @@ package loadbalance import ( + "fmt" "testing" ) @@ -32,6 +33,19 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) +const ( + ip = "192.168.1.0" + port8080 = 8080 + port8082 = 8082 + + url8080Short = "dubbo://192.168.1.0:8080" + url8081Short = "dubbo://192.168.1.0:8081" + url20000 = "dubbo://192.168.1.0:20000/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1" + url8080 = "dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1" + url8081 = "dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1" + url8082 = "dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1" +) + func TestConsistentHashSelectorSuite(t *testing.T) { suite.Run(t, new(consistentHashSelectorSuite)) } @@ -43,8 +57,7 @@ type consistentHashSelectorSuite struct { func (s *consistentHashSelectorSuite) SetupTest() { var invokers []protocol.Invoker - url, _ := common.NewURL( - "dubbo://192.168.1.0:20000/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + url, _ := common.NewURL(url20000) invokers = append(invokers, protocol.NewBaseInvoker(url)) s.selector = newConsistentHashSelector(invokers, "echo", 999944) } @@ -55,14 +68,14 @@ func (s *consistentHashSelectorSuite) TestToKey() { } func (s *consistentHashSelectorSuite) TestSelectForKey() { - url1, _ := common.NewURL("dubbo://192.168.1.0:8080") - url2, _ := common.NewURL("dubbo://192.168.1.0:8081") + url1, _ := common.NewURL(url8080Short) + url2, _ := common.NewURL(url8081Short) s.selector.virtualInvokers = make(map[uint32]protocol.Invoker) s.selector.virtualInvokers[99874] = protocol.NewBaseInvoker(url1) s.selector.virtualInvokers[9999945] = protocol.NewBaseInvoker(url2) s.selector.keys = []uint32{99874, 9999945} result := s.selector.selectForKey(9999944) - s.Equal(result.GetUrl().String(), "dubbo://192.168.1.0:8081?") + s.Equal(result.GetUrl().String(), url8081Short+"?") } func TestConsistentHashLoadBalanceSuite(t *testing.T) { @@ -83,11 +96,11 @@ type consistentHashLoadBalanceSuite struct { func (s *consistentHashLoadBalanceSuite) SetupTest() { var err error - s.url1, err = common.NewURL("dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url1, err = common.NewURL(url8080) s.NoError(err) - s.url2, err = common.NewURL("dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url2, err = common.NewURL(url8081) s.NoError(err) - s.url3, err = common.NewURL("dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url3, err = common.NewURL(url8082) s.NoError(err) s.invoker1 = protocol.NewBaseInvoker(s.url1) @@ -101,9 +114,9 @@ func (s *consistentHashLoadBalanceSuite) SetupTest() { func (s *consistentHashLoadBalanceSuite) TestSelect() { args := []interface{}{"name", "password", "age"} invoker := s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil)) - s.Equal(invoker.GetUrl().Location, "192.168.1.0:8080") + s.Equal(invoker.GetUrl().Location, fmt.Sprintf("%s:%d", ip, port8080)) args = []interface{}{"ok", "abc"} invoker = s.lb.Select(s.invokers, invocation.NewRPCInvocation("echo", args, nil)) - s.Equal(invoker.GetUrl().Location, "192.168.1.0:8082") + s.Equal(invoker.GetUrl().Location, fmt.Sprintf("%s:%d", ip, port8082)) } diff --git a/cluster/loadbalance/least_active.go b/cluster/loadbalance/least_active.go index e7c41aac93e8d3dfcef5d49fa486483bd045f569..37ad91c3ed6b44370820a989b7af8ccaa82c48a2 100644 --- a/cluster/loadbalance/least_active.go +++ b/cluster/loadbalance/least_active.go @@ -28,7 +28,7 @@ import ( ) const ( - // LeastActive ... + // LeastActive is used to set the load balance extension LeastActive = "leastactive" ) @@ -39,7 +39,9 @@ func init() { type leastActiveLoadBalance struct { } -// NewLeastActiveLoadBalance ... +// NewLeastActiveLoadBalance returns a least active load balance. +// +// A random mechanism based on actives, actives means the number of a consumer's requests have been sent to provider but not yet got response. func NewLeastActiveLoadBalance() cluster.LoadBalance { return &leastActiveLoadBalance{} } diff --git a/cluster/loadbalance/least_active_test.go b/cluster/loadbalance/least_active_test.go index 54e57e930f17008cf6d767ef47c0e754ac85d8f7..34be17a4f311a374eefc56ba76885eef2a23645a 100644 --- a/cluster/loadbalance/least_active_test.go +++ b/cluster/loadbalance/least_active_test.go @@ -28,6 +28,7 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -37,7 +38,7 @@ func TestLeastActiveSelect(t *testing.T) { var invokers []protocol.Invoker - url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService") + url, _ := common.NewURL(fmt.Sprintf("dubbo://%s:%d/org.apache.demo.HelloService", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) invokers = append(invokers, protocol.NewBaseInvoker(url)) i := loadBalance.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) diff --git a/cluster/loadbalance/random.go b/cluster/loadbalance/random.go index 56f13631b653ed070dae7def5bea97d924141209..cdde1b41fb8e986d9923681dc4ab075496ac810e 100644 --- a/cluster/loadbalance/random.go +++ b/cluster/loadbalance/random.go @@ -38,7 +38,9 @@ func init() { type randomLoadBalance struct { } -// NewRandomLoadBalance ... +// NewRandomLoadBalance returns a random load balance instance. +// +// Set random probabilities by weight, and the request will be sent to provider randomly. func NewRandomLoadBalance() cluster.LoadBalance { return &randomLoadBalance{} } diff --git a/cluster/loadbalance/random_test.go b/cluster/loadbalance/random_test.go index ff876f4aef8d229e8041594aaaa096f3ad5b1834..b94d7da43d5bd42e6798fca750c8616830a8df8f 100644 --- a/cluster/loadbalance/random_test.go +++ b/cluster/loadbalance/random_test.go @@ -36,35 +36,41 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_RandomlbSelect(t *testing.T) { +const ( + tmpUrl = "dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider" + tmpUrlFormat = "dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider" + tmpIp = "192.168.1.100" +) + +func TestRandomlbSelect(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0)) + url, _ := common.NewURL(fmt.Sprintf(tmpUrlFormat, 0)) invokers = append(invokers, protocol.NewBaseInvoker(url)) i := randomlb.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) for i := 1; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf(tmpUrlFormat, i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } randomlb.Select(invokers, &invocation.RPCInvocation{}) } -func Test_RandomlbSelectWeight(t *testing.T) { +func TestRandomlbSelectWeight(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf(tmpUrlFormat, i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } urlParams := url.Values{} urlParams.Set("methods.test."+constant.WEIGHT_KEY, "10000000000000") - urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) + urll, _ := common.NewURL(tmpUrl, common.WithParams(urlParams)) invokers = append(invokers, protocol.NewBaseInvoker(urll)) ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test")) @@ -72,7 +78,7 @@ func Test_RandomlbSelectWeight(t *testing.T) { var selected float64 for i := 0; i < 10000; i++ { s := randomlb.Select(invokers, ivc) - if s.GetUrl().Ip == "192.168.1.100" { + if s.GetUrl().Ip == tmpIp { selected++ } selectedInvoker = append(selectedInvoker, s) @@ -84,18 +90,18 @@ func Test_RandomlbSelectWeight(t *testing.T) { }) } -func Test_RandomlbSelectWarmup(t *testing.T) { +func TestRandomlbSelectWarmup(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf(tmpUrlFormat, i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } urlParams := url.Values{} urlParams.Set(constant.REMOTE_TIMESTAMP_KEY, strconv.FormatInt(time.Now().Add(time.Minute*(-9)).Unix(), 10)) - urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) + urll, _ := common.NewURL(tmpUrl, common.WithParams(urlParams)) invokers = append(invokers, protocol.NewBaseInvoker(urll)) ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test")) @@ -103,7 +109,7 @@ func Test_RandomlbSelectWarmup(t *testing.T) { var selected float64 for i := 0; i < 10000; i++ { s := randomlb.Select(invokers, ivc) - if s.GetUrl().Ip == "192.168.1.100" { + if s.GetUrl().Ip == tmpIp { selected++ } selectedInvoker = append(selectedInvoker, s) diff --git a/cluster/loadbalance/round_robin.go b/cluster/loadbalance/round_robin.go index 4d039999677aefb1093071666a845279dc357ce9..c44b239dbcbcc744f47ca3c97128f92567e32a78 100644 --- a/cluster/loadbalance/round_robin.go +++ b/cluster/loadbalance/round_robin.go @@ -52,7 +52,9 @@ func init() { type roundRobinLoadBalance struct{} -// NewRoundRobinLoadBalance ... +// NewRoundRobinLoadBalance returns a round robin load balance +// +// Use the weight's common advisory to determine round robin ratio func NewRoundRobinLoadBalance() cluster.LoadBalance { return &roundRobinLoadBalance{} } diff --git a/cluster/loadbalance/round_robin_test.go b/cluster/loadbalance/round_robin_test.go index 1517f2a20b473af57cc23e61b988aa5a6a04de31..5354bae458605ff56ec8a9b35d36730ecdc0babb 100644 --- a/cluster/loadbalance/round_robin_test.go +++ b/cluster/loadbalance/round_robin_test.go @@ -29,6 +29,7 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -38,7 +39,8 @@ func TestRoundRobinSelect(t *testing.T) { var invokers []protocol.Invoker - url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService") + url, _ := common.NewURL(fmt.Sprintf("dubbo://%s:%d/org.apache.demo.HelloService", + constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) invokers = append(invokers, protocol.NewBaseInvoker(url)) i := loadBalance.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go index 9f36ad9379a3a09a4a058f6179e3e537b9e105bc..b6c013852bf55ce7eb67e4fa18802a938141d283 100644 --- a/cluster/loadbalance/util.go +++ b/cluster/loadbalance/util.go @@ -26,7 +26,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// GetWeight ... +// GetWeight gets weight for load balance strategy func GetWeight(invoker protocol.Invoker, invocation protocol.Invocation) int64 { url := invoker.GetUrl() weight := url.GetMethodParamInt64(invocation.MethodName(), constant.WEIGHT_KEY, constant.DEFAULT_WEIGHT) diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go index d48a837eba2080867f370b0cd90e38a0bdf5e417..97d20ac5fca0688d3f1f48c6ffc12314f5dc3904 100644 --- a/cluster/router/chain/chain.go +++ b/cluster/router/chain/chain.go @@ -40,19 +40,21 @@ type RouterChain struct { // Full list of addresses from registry, classified by method name. invokers []protocol.Invoker // Containing all routers, reconstruct every time 'route://' urls change. - routers []router.Router + routers []router.PriorityRouter // Fixed router instances: ConfigConditionRouter, TagRouter, e.g., the rule for each instance may change but the // instance will never delete or recreate. - builtinRouters []router.Router + builtinRouters []router.PriorityRouter mutex sync.RWMutex + + url common.URL } // Route Loop routers in RouterChain and call Route method to determine the target invokers list. func (c *RouterChain) Route(invoker []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { finalInvokers := invoker l := len(c.routers) - rs := make([]router.Router, l, int(math.Ceil(float64(l)*1.2))) + rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2))) c.mutex.RLock() copy(rs, c.routers) c.mutex.RUnlock() @@ -67,8 +69,8 @@ func (c *RouterChain) Route(invoker []protocol.Invoker, url *common.URL, invocat // New a array add builtinRouters which is not sorted in RouterChain and routers // Sort the array // Replace router array in RouterChain -func (c *RouterChain) AddRouters(routers []router.Router) { - newRouters := make([]router.Router, 0, len(c.builtinRouters)+len(routers)) +func (c *RouterChain) AddRouters(routers []router.PriorityRouter) { + newRouters := make([]router.PriorityRouter, 0, len(c.builtinRouters)+len(routers)) newRouters = append(newRouters, c.builtinRouters...) newRouters = append(newRouters, routers...) sortRouter(newRouters) @@ -77,6 +79,11 @@ func (c *RouterChain) AddRouters(routers []router.Router) { c.routers = newRouters } +// URL Return URL in RouterChain +func (c *RouterChain) URL() common.URL { + return c.url +} + // NewRouterChain Use url to init router chain // Loop routerFactories and call NewRouter method func NewRouterChain(url *common.URL) (*RouterChain, error) { @@ -84,9 +91,9 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { if len(routerFactories) == 0 { return nil, perrors.Errorf("No routerFactory exits , create one please") } - routers := make([]router.Router, 0, len(routerFactories)) + routers := make([]router.PriorityRouter, 0, len(routerFactories)) for key, routerFactory := range routerFactories { - r, err := routerFactory().NewRouter(url) + r, err := routerFactory().NewPriorityRouter(url) if r == nil || err != nil { logger.Errorf("router chain build router fail! routerFactories key:%s error:%s", key, err.Error()) continue @@ -94,7 +101,7 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { routers = append(routers, r) } - newRouters := make([]router.Router, len(routers)) + newRouters := make([]router.PriorityRouter, len(routers)) copy(newRouters, routers) sortRouter(newRouters) @@ -103,17 +110,20 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { builtinRouters: routers, routers: newRouters, } + if url != nil { + chain.url = *url + } return chain, nil } // sortRouter Sort router instance by priority with stable algorithm -func sortRouter(routers []router.Router) { +func sortRouter(routers []router.PriorityRouter) { sort.Stable(byPriority(routers)) } // byPriority Sort by priority -type byPriority []router.Router +type byPriority []router.PriorityRouter func (a byPriority) Len() int { return len(a) } func (a byPriority) Swap(i, j int) { a[i], a[j] = a[j], a[i] } diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go index 0cb47c4a185fe19b5f70ea4db2b80aab2f1aada5..c1f723525f5307e7732f0ea1ecc27eca7ba09c8d 100644 --- a/cluster/router/chain/chain_test.go +++ b/cluster/router/chain/chain_test.go @@ -20,7 +20,6 @@ package chain import ( "encoding/base64" "fmt" - "strconv" "testing" "time" ) @@ -42,10 +41,29 @@ import ( "github.com/apache/dubbo-go/remoting/zookeeper" ) +const ( + localIP = "127.0.0.1" + test1234IP = "1.2.3.4" + test1111IP = "1.1.1.1" + test0000IP = "0.0.0.0" + port20000 = 20000 + + path = "/dubbo/config/dubbo/test-condition.condition-router" + zkFormat = "zookeeper://%s:%d" + consumerFormat = "consumer://%s/com.foo.BarService" + dubboForamt = "dubbo://%s:%d/com.foo.BarService" + anyUrlFormat = "condition://%s/com.foo.BarService" + zk = "zookeeper" + applicationKey = "test-condition" + applicationField = "application" + forceField = "force" + forceValue = "true" +) + func TestNewRouterChain(t *testing.T) { ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(path) assert.NoError(t, err) testyml := `enabled: true @@ -55,19 +73,19 @@ conditions: - => host != 172.22.3.91 ` - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0) + _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) - chain, err := NewRouterChain(getRouteUrl("test-condition")) + chain, err := NewRouterChain(getRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 1, len(chain.routers)) appRouter := chain.routers[0].(*condition.AppRouter) @@ -92,10 +110,10 @@ func TestNewRouterChainURLNil(t *testing.T) { assert.NotNil(t, chain) } -func TestRouterChain_AddRouters(t *testing.T) { +func TestRouterChainAddRouters(t *testing.T) { ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(path) assert.NoError(t, err) testyml := `enabled: true @@ -105,63 +123,63 @@ conditions: - => host != 172.22.3.91 ` - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0) + _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) - chain, err := NewRouterChain(getConditionRouteUrl("test-condition")) + chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 2, len(chain.routers)) - url := getConditionRouteUrl("test-condition") + url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) factory := extension.GetRouterFactory(url.Protocol) - r, err := factory.NewRouter(url) + r, err := factory.NewPriorityRouter(url) assert.Nil(t, err) assert.NotNil(t, r) - routers := make([]router.Router, 0) + routers := make([]router.PriorityRouter, 0) routers = append(routers, r) chain.AddRouters(routers) assert.Equal(t, 3, len(chain.routers)) } -func TestRouterChain_Route(t *testing.T) { +func TestRouterChainRoute(t *testing.T) { ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) - chain, err := NewRouterChain(getConditionRouteUrl("test-condition")) + chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 1, len(chain.routers)) - url := getConditionRouteUrl("test-condition") + url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) invokers := []protocol.Invoker{} - dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService")) + dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) - targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService")) + targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} finalInvokers := chain.Route(invokers, &targetURL, inv) assert.Equal(t, 1, len(finalInvokers)) } -func TestRouterChain_Route_AppRouter(t *testing.T) { +func TestRouterChainRouteAppRouter(t *testing.T) { ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(path) assert.NoError(t, err) testyml := `enabled: true @@ -171,51 +189,51 @@ conditions: - => host = 1.1.1.1 => host != 1.2.3.4 ` - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testyml), 0) + _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) - chain, err := NewRouterChain(getConditionRouteUrl("test-condition")) + chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 2, len(chain.routers)) invokers := []protocol.Invoker{} - dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService")) + dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) - targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService")) + targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} finalInvokers := chain.Route(invokers, &targetURL, inv) assert.Equal(t, 0, len(finalInvokers)) } -func TestRouterChain_Route_NoRoute(t *testing.T) { +func TestRouterChainRouteNoRoute(t *testing.T) { ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) - chain, err := NewRouterChain(getConditionNoRouteUrl("test-condition")) + chain, err := NewRouterChain(getConditionNoRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 1, len(chain.routers)) - url := getConditionRouteUrl("test-condition") + url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) invokers := []protocol.Invoker{} - dubboURL, _ := common.NewURL(fmt.Sprintf("dubbo://1.2.3.4:20000/com.foo.BarService")) + dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) - targetURL, _ := common.NewURL(fmt.Sprintf("consumer://1.1.1.1/com.foo.BarService")) + targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} finalInvokers := chain.Route(invokers, &targetURL, inv) @@ -223,26 +241,26 @@ func TestRouterChain_Route_NoRoute(t *testing.T) { } func getConditionNoRouteUrl(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") - url.AddParam("application", applicationKey) - url.AddParam("force", "true") + url, _ := common.NewURL(fmt.Sprintf(anyUrlFormat, test0000IP)) + url.AddParam(applicationField, applicationKey) + url.AddParam(forceField, forceValue) rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host != 1.2.3.4")) url.AddParam(constant.RULE_KEY, rule) return &url } func getConditionRouteUrl(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") - url.AddParam("application", applicationKey) - url.AddParam("force", "true") + url, _ := common.NewURL(fmt.Sprintf(anyUrlFormat, test0000IP)) + url.AddParam(applicationField, applicationKey) + url.AddParam(forceField, forceValue) rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host = 1.2.3.4")) url.AddParam(constant.RULE_KEY, rule) return &url } func getRouteUrl(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") - url.AddParam("application", applicationKey) - url.AddParam("force", "true") + url, _ := common.NewURL(fmt.Sprintf(anyUrlFormat, test0000IP)) + url.AddParam(applicationField, applicationKey) + url.AddParam(forceField, forceValue) return &url } diff --git a/cluster/router/chan.go b/cluster/router/chan.go new file mode 100644 index 0000000000000000000000000000000000000000..6904e1734a7cbdaa00afa1b30797d19ca502453c --- /dev/null +++ b/cluster/router/chan.go @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 router + +// Chain +type Chain interface { + router + // AddRouters Add routers + AddRouters([]PriorityRouter) +} diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go index e99307625baf34fa6b744f168ff4e6cb8e042502..8b38f2dd6136b4d31f46e7214c0ad1359537b198 100644 --- a/cluster/router/condition/app_router_test.go +++ b/cluster/router/condition/app_router_test.go @@ -18,7 +18,7 @@ package condition import ( - "strconv" + "fmt" "testing" "time" ) @@ -31,12 +31,25 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/remoting" "github.com/apache/dubbo-go/remoting/zookeeper" ) +const ( + routerPath = "/dubbo/config/dubbo/test-condition.condition-router" + routerLocalIP = "127.0.0.1" + routerZk = "zookeeper" + routerKey = "test-condition" +) + +var ( + zkFormat = "zookeeper://%s:%d" + conditionFormat = "condition://%s/com.foo.BarService" +) + func TestNewAppRouter(t *testing.T) { testYML := `enabled: true @@ -47,22 +60,22 @@ conditions: ` ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(routerPath) assert.NoError(t, err) - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0) + _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) - appRouteURL := getAppRouteURL("test-condition") + appRouteURL := getAppRouteURL(routerKey) appRouter, err := NewAppRouter(appRouteURL) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -93,22 +106,22 @@ conditions: ` ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(routerPath) assert.NoError(t, err) - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0) + _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) - appRouteURL := getAppRouteURL("test-condition") + appRouteURL := getAppRouteURL(routerKey) appRouter, err := NewAppRouter(appRouteURL) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -130,22 +143,22 @@ conditions: ` ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - err = z.Create("/dubbo/config/dubbo/test-condition.condition-router") + err = z.Create(routerPath) assert.NoError(t, err) - _, err = z.Conn.Set("/dubbo/config/dubbo/test-condition.condition-router", []byte(testYML), 0) + _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) - appRouteURL := getAppRouteURL("test-condition") + appRouteURL := getAppRouteURL(routerKey) appRouter, err := NewAppRouter(appRouteURL) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -171,7 +184,7 @@ conditions: } func getAppRouteURL(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(fmt.Sprintf(conditionFormat, constant.ANYHOST_VALUE)) url.AddParam("application", applicationKey) url.AddParam("force", "true") return &url diff --git a/cluster/router/condition/factory.go b/cluster/router/condition/factory.go index 66512a138706e9b9ab565f7537a15c37a75deefd..f8d3e130102d4311f8b1ddb1055aece8a0633296 100644 --- a/cluster/router/condition/factory.go +++ b/cluster/router/condition/factory.go @@ -32,28 +32,28 @@ func init() { // ConditionRouterFactory Condition router factory type ConditionRouterFactory struct{} -func newConditionRouterFactory() router.RouterFactory { +func newConditionRouterFactory() router.PriorityRouterFactory { return &ConditionRouterFactory{} } -// NewRouter Create ConditionRouterFactory by URL -func (c *ConditionRouterFactory) NewRouter(url *common.URL) (router.Router, error) { +// NewPriorityRouter creates ConditionRouterFactory by URL +func (c *ConditionRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { return NewConditionRouter(url) } // NewRouter Create FileRouterFactory by Content -func (c *ConditionRouterFactory) NewFileRouter(content []byte) (router.Router, error) { +func (c *ConditionRouterFactory) NewFileRouter(content []byte) (router.PriorityRouter, error) { return NewFileConditionRouter(content) } // AppRouterFactory Application router factory type AppRouterFactory struct{} -func newAppRouterFactory() router.RouterFactory { +func newAppRouterFactory() router.PriorityRouterFactory { return &AppRouterFactory{} } -// NewRouter Create AppRouterFactory by URL -func (c *AppRouterFactory) NewRouter(url *common.URL) (router.Router, error) { +// NewPriorityRouter creates AppRouterFactory by URL +func (c *AppRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { return NewAppRouter(url) } diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go index 99cec34096a55d3c2a967b63afdf5f6d0a77279a..0f61b39fc71af3aaeffc731974a0fa997503693e 100644 --- a/cluster/router/condition/factory_test.go +++ b/cluster/router/condition/factory_test.go @@ -33,11 +33,22 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) +const ( + factory1111Ip = "1.1.1.1" + factoryUrlFormat = "condition://%s/com.foo.BarService" + factoryDubboFormat = "dubbo://%s:20880/com.foo.BarService" + factoryConsumerMethodFormat = "consumer://%s/com.foo.BarService?methods=getFoo" + factory333URL = "dubbo://10.20.3.3:20880/com.foo.BarService" + factoryConsumerFormat = "consumer://%s/com.foo.BarService" + factoryHostIp1234Format = "host = %s => host = 1.2.3.4" +) + type MockInvoker struct { url common.URL available bool @@ -59,21 +70,21 @@ func (bi *MockInvoker) GetUrl() common.URL { } func getRouteUrl(rule string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) url.AddParam("force", "true") return &url } func getRouteUrlWithForce(rule, force string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) url.AddParam("force", force) return &url } func getRouteUrlWithNoForce(rule string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) return &url } @@ -119,32 +130,32 @@ func (bi *MockInvoker) Destroy() { func TestRoute_matchWhen(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4")) - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) - cUrl, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService") + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + cUrl, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, factory1111Ip)) matchWhen := router.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, true, matchWhen) rule1 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router1, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule1)) + router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1)) matchWhen1 := router1.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, true, matchWhen1) rule2 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4")) - router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2)) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) matchWhen2 := router2.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, false, matchWhen2) rule3 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3)) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) matchWhen3 := router3.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, true, matchWhen3) rule4 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router4, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule4)) + router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4)) matchWhen4 := router4.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, true, matchWhen4) rule5 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4")) - router5, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule5)) + router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5)) matchWhen5 := router5.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, false, matchWhen5) rule6 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4")) - router6, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule6)) + router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6)) matchWhen6 := router6.(*ConditionRouter).MatchWhen(&cUrl, inv) assert.Equal(t, true, matchWhen6) } @@ -153,8 +164,8 @@ func TestRoute_matchFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() t.Logf("The local ip is %s", localIP) url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invokers := []protocol.Invoker{NewMockInvoker(url1, 1), NewMockInvoker(url2, 2), NewMockInvoker(url3, 3)} rule1 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.3")) rule2 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.* & host != 10.20.3.3")) @@ -162,13 +173,13 @@ func TestRoute_matchFilter(t *testing.T) { rule4 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4")) rule5 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host != 10.20.3.3")) rule6 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " serialization = fastjson")) - router1, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule1)) - router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2)) - router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3)) - router4, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule4)) - router5, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule5)) - router6, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule6)) - cUrl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") + router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1)) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) + router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4)) + router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5)) + router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6)) + cUrl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{}) fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{}) fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{}) @@ -187,21 +198,21 @@ func TestRoute_matchFilter(t *testing.T) { func TestRoute_methodRoute(t *testing.T) { inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{})) rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo") matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv) assert.Equal(t, true, matchWhen) - url1, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url1, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv) assert.Equal(t, true, matchWhen) - url2, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url2, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4")) - router2, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule2)) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) matchWhen = router2.(*ConditionRouter).MatchWhen(&url2, inv) assert.Equal(t, false, matchWhen) - url3, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url3, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) rule3 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4")) - router3, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule3)) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) matchWhen = router3.(*ConditionRouter).MatchWhen(&url3, inv) assert.Equal(t, true, matchWhen) @@ -213,8 +224,8 @@ func TestRoute_ReturnFalse(t *testing.T) { invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false")) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 0, len(fileredInvokers)) } @@ -225,8 +236,8 @@ func TestRoute_ReturnEmpty(t *testing.T) { invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => ")) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 0, len(fileredInvokers)) } @@ -241,25 +252,25 @@ func TestRoute_ReturnAll(t *testing.T) { invokers := []protocol.Invoker{mockInvoker1, mockInvoker2, mockInvoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, invokers, fileredInvokers) } func TestRoute_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 2, len(fileredInvokers)) assert.Equal(t, invoker2, fileredInvokers[0]) @@ -268,17 +279,17 @@ func TestRoute_HostFilter(t *testing.T) { func TestRoute_Empty_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP)) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 2, len(fileredInvokers)) assert.Equal(t, invoker2, fileredInvokers[0]) @@ -287,17 +298,17 @@ func TestRoute_Empty_HostFilter(t *testing.T) { func TestRoute_False_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 2, len(fileredInvokers)) assert.Equal(t, invoker2, fileredInvokers[0]) @@ -306,17 +317,17 @@ func TestRoute_False_HostFilter(t *testing.T) { func TestRoute_Placeholder(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host")) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 2, len(fileredInvokers)) assert.Equal(t, invoker2, fileredInvokers[0]) @@ -325,34 +336,34 @@ func TestRoute_Placeholder(t *testing.T) { func TestRoute_NoForce(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} - rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4")) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrlWithNoForce(rule)) + rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, invokers, fileredInvokers) } func TestRoute_Force(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL(factory333URL) + url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} - rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4")) - curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") - router, _ := newConditionRouterFactory().NewRouter(getRouteUrlWithForce(rule, "true")) + rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) + curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true")) fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) assert.Equal(t, 0, len(fileredInvokers)) } diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go index b2c876690043d18a1a9e746fee13f06c77a0de03..eabdf1c263446140b359b3e791238b020cecb50c 100644 --- a/cluster/router/condition/file.go +++ b/cluster/router/condition/file.go @@ -77,10 +77,7 @@ func (f *FileConditionRouter) URL() common.URL { } func parseCondition(conditions []string) string { - var ( - when string - then string - ) + var when, then string for _, condition := range conditions { condition = strings.Trim(condition, " ") if strings.Contains(condition, "=>") { @@ -101,10 +98,7 @@ func parseCondition(conditions []string) string { then = provider } } - } - } - return strings.Join([]string{when, then}, " => ") } diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go index 0267a3c7a462acb43f84ccb4701247147699804a..40a251573f5e73d40032972313565d98b288b1b1 100644 --- a/cluster/router/condition/router.go +++ b/cluster/router/condition/router.go @@ -181,9 +181,7 @@ func parseRule(rule string) (map[string]MatchPair, error) { return condition, nil } - var ( - pair MatchPair - ) + var pair MatchPair values := gxset.NewSet() matches := routerPatternReg.FindAllSubmatch([]byte(rule), -1) for _, groups := range matches { diff --git a/cluster/router/healthcheck/default_health_check_test.go b/cluster/router/healthcheck/default_health_check_test.go index 74aa3940743a012f907cfe3d8811a618f07ff800..5d35ae8e486e3f7b29b2a68a3864ef806a1053c7 100644 --- a/cluster/router/healthcheck/default_health_check_test.go +++ b/cluster/router/healthcheck/default_health_check_test.go @@ -18,6 +18,7 @@ package healthcheck import ( + "fmt" "math" "testing" ) @@ -32,12 +33,18 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func TestDefaultHealthChecker_IsHealthy(t *testing.T) { +const ( + healthCheckDubbo1010IP = "192.168.10.10" + healthCheckDubbo1011IP = "192.168.10.11" + healthCheckMethodTest = "test" + healthCheckDubboUrlFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider" +) +func TestDefaultHealthCheckerIsHealthy(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) hc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) - invoker := NewMockInvoker(url, 1) + invoker := NewMockInvoker(url) healthy := hc.IsHealthy(invoker) assert.True(t, healthy) @@ -45,7 +52,7 @@ func TestDefaultHealthChecker_IsHealthy(t *testing.T) { url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "100") // fake the outgoing request for i := 0; i < 11; i++ { - request(url, "test", 0, true, false) + request(url, healthCheckMethodTest, 0, true, false) } hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker) healthy = hc.IsHealthy(invoker) @@ -54,7 +61,7 @@ func TestDefaultHealthChecker_IsHealthy(t *testing.T) { // successive failed count is more than constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, go to unhealthy for i := 0; i < 11; i++ { - request(url, "test", 0, false, false) + request(url, healthCheckMethodTest, 0, false, false) } url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10") url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000") @@ -63,18 +70,18 @@ func TestDefaultHealthChecker_IsHealthy(t *testing.T) { assert.False(t, hc.IsHealthy(invoker)) // reset successive failed count and go to healthy - request(url, "test", 0, false, true) + request(url, healthCheckMethodTest, 0, false, true) healthy = hc.IsHealthy(invoker) assert.True(t, hc.IsHealthy(invoker)) } -func TestDefaultHealthChecker_getCircuitBreakerSleepWindowTime(t *testing.T) { +func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) // Increase the number of failed requests for i := 0; i < 100; i++ { - request(url, "test", 1, false, false) + request(url, healthCheckMethodTest, 1, false, false) } sleepWindowTime := defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url)) assert.True(t, sleepWindowTime == constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS) @@ -84,48 +91,48 @@ func TestDefaultHealthChecker_getCircuitBreakerSleepWindowTime(t *testing.T) { sleepWindowTime = NewDefaultHealthChecker(&url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url)) assert.True(t, sleepWindowTime == 0) - url1, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider") + url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP)) sleepWindowTime = defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url1)) assert.True(t, sleepWindowTime == 0) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) sleepWindowTime = defaultHc.getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url1)) assert.True(t, sleepWindowTime > 0 && sleepWindowTime < constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS) } -func TestDefaultHealthChecker_getCircuitBreakerTimeout(t *testing.T) { +func TestDefaultHealthCheckerGetCircuitBreakerTimeout(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) timeout := defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url)) assert.True(t, timeout == 0) - url1, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider") - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) - request(url1, "test", 1, false, false) + url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP)) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) + request(url1, healthCheckMethodTest, 1, false, false) timeout = defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url1)) // timeout must after the current time assert.True(t, timeout > protocol.CurrentTimeMillis()) } -func TestDefaultHealthChecker_isCircuitBreakerTripped(t *testing.T) { +func TestDefaultHealthCheckerIsCircuitBreakerTripped(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) status := protocol.GetURLStatus(url) tripped := defaultHc.isCircuitBreakerTripped(status) assert.False(t, tripped) // Increase the number of failed requests for i := 0; i < 100; i++ { - request(url, "test", 1, false, false) + request(url, healthCheckMethodTest, 1, false, false) } tripped = defaultHc.isCircuitBreakerTripped(protocol.GetURLStatus(url)) assert.True(t, tripped) @@ -134,13 +141,13 @@ func TestDefaultHealthChecker_isCircuitBreakerTripped(t *testing.T) { func TestNewDefaultHealthChecker(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) assert.NotNil(t, defaultHc) assert.Equal(t, defaultHc.outStandingRequestConutLimit, int32(math.MaxInt32)) assert.Equal(t, defaultHc.requestSuccessiveFailureThreshold, int32(constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF)) - url1, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) url1.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "10") url1.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10") nondefaultHc := NewDefaultHealthChecker(&url1).(*DefaultHealthChecker) diff --git a/cluster/router/healthcheck/factory.go b/cluster/router/healthcheck/factory.go index 32d84d145ceb2aa05f5a75de352e52d13dd9d6b3..40c9dd7ab9e431a16833507ee4093ff7fbff8c95 100644 --- a/cluster/router/healthcheck/factory.go +++ b/cluster/router/healthcheck/factory.go @@ -33,11 +33,11 @@ type HealthCheckRouteFactory struct { } // newHealthCheckRouteFactory construct a new HealthCheckRouteFactory -func newHealthCheckRouteFactory() router.RouterFactory { +func newHealthCheckRouteFactory() router.PriorityRouterFactory { return &HealthCheckRouteFactory{} } -// NewRouter construct a new NewHealthCheckRouter via url -func (f *HealthCheckRouteFactory) NewRouter(url *common.URL) (router.Router, error) { +// NewPriorityRouter construct a new NewHealthCheckRouter via url +func (f *HealthCheckRouteFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { return NewHealthCheckRouter(url) } diff --git a/cluster/router/healthcheck/factory_test.go b/cluster/router/healthcheck/factory_test.go index a9d94da7c37f0e0c9640de1386998a85823e80a6..c3a26a93896e185f0dea3732ca5afcf7687ad5ea 100644 --- a/cluster/router/healthcheck/factory_test.go +++ b/cluster/router/healthcheck/factory_test.go @@ -35,7 +35,7 @@ type MockInvoker struct { url common.URL } -func NewMockInvoker(url common.URL, successCount int) *MockInvoker { +func NewMockInvoker(url common.URL) *MockInvoker { return &MockInvoker{ url: url, } diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go index 1ddc9ccb173881a87bc5351711326f02ab2da3f6..ee42e47e3b26c9a1976b4599d3464d752b615e0a 100644 --- a/cluster/router/healthcheck/health_check_route.go +++ b/cluster/router/healthcheck/health_check_route.go @@ -38,7 +38,7 @@ type HealthCheckRouter struct { } // NewHealthCheckRouter construct an HealthCheckRouter via url -func NewHealthCheckRouter(url *common.URL) (router.Router, error) { +func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) { r := &HealthCheckRouter{ url: url, enabled: url.GetParamBool(HEALTH_ROUTE_ENABLED_KEY, false), diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go index 759ef93dbeb8d91a82eefd59060afbe8a10a4440..d5862fb884114bac0ea2ec9ee8926baac57d5ba6 100644 --- a/cluster/router/healthcheck/health_check_route_test.go +++ b/cluster/router/healthcheck/health_check_route_test.go @@ -18,6 +18,7 @@ package healthcheck import ( + "fmt" "math" "testing" "time" @@ -34,35 +35,44 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func TestHealthCheckRouter_Route(t *testing.T) { +const ( + healthCheckRoute1010IP = "192.168.10.10" + healthCheckRoute1011IP = "192.168.10.11" + healthCheckRoute1012IP = "192.168.10.12" + healthCheckRouteMethodNameTest = "test" + healthCheck1001URL = "dubbo://192.168.10.1/com.ikurento.user.UserProvider" + healthCheckRouteUrlFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider" +) + +func TestHealthCheckRouterRoute(t *testing.T) { defer protocol.CleanAllStatus() - consumerURL, _ := common.NewURL("dubbo://192.168.10.1/com.ikurento.user.UserProvider") + consumerURL, _ := common.NewURL(healthCheck1001URL) consumerURL.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true") - url1, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") - url2, _ := common.NewURL("dubbo://192.168.10.11:20000/com.ikurento.user.UserProvider") - url3, _ := common.NewURL("dubbo://192.168.10.12:20000/com.ikurento.user.UserProvider") + url1, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1010IP)) + url2, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1011IP)) + url3, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1012IP)) hcr, _ := NewHealthCheckRouter(&consumerURL) var invokers []protocol.Invoker - invoker1 := NewMockInvoker(url1, 1) - invoker2 := NewMockInvoker(url2, 1) - invoker3 := NewMockInvoker(url3, 1) + invoker1 := NewMockInvoker(url1) + invoker2 := NewMockInvoker(url2) + invoker3 := NewMockInvoker(url3) invokers = append(invokers, invoker1, invoker2, invoker3) - inv := invocation.NewRPCInvocation("test", nil, nil) + inv := invocation.NewRPCInvocation(healthCheckRouteMethodNameTest, nil, nil) res := hcr.Route(invokers, &consumerURL, inv) // now all invokers are healthy assert.True(t, len(res) == len(invokers)) for i := 0; i < 10; i++ { - request(url1, "test", 0, false, false) + request(url1, healthCheckRouteMethodNameTest, 0, false, false) } res = hcr.Route(invokers, &consumerURL, inv) // invokers1 is unhealthy now assert.True(t, len(res) == 2 && !contains(res, invoker1)) for i := 0; i < 10; i++ { - request(url1, "test", 0, false, false) - request(url2, "test", 0, false, false) + request(url1, healthCheckRouteMethodNameTest, 0, false, false) + request(url2, healthCheckRouteMethodNameTest, 0, false, false) } res = hcr.Route(invokers, &consumerURL, inv) @@ -70,9 +80,9 @@ func TestHealthCheckRouter_Route(t *testing.T) { assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2)) for i := 0; i < 10; i++ { - request(url1, "test", 0, false, false) - request(url2, "test", 0, false, false) - request(url3, "test", 0, false, false) + request(url1, healthCheckRouteMethodNameTest, 0, false, false) + request(url2, healthCheckRouteMethodNameTest, 0, false, false) + request(url3, healthCheckRouteMethodNameTest, 0, false, false) } res = hcr.Route(invokers, &consumerURL, inv) @@ -80,12 +90,12 @@ func TestHealthCheckRouter_Route(t *testing.T) { assert.True(t, len(res) == 3) // reset the invoker1 successive failed count, so invoker1 go to healthy - request(url1, "test", 0, false, true) + request(url1, healthCheckRouteMethodNameTest, 0, false, true) res = hcr.Route(invokers, &consumerURL, inv) assert.True(t, contains(res, invoker1)) for i := 0; i < 6; i++ { - request(url1, "test", 0, false, false) + request(url1, healthCheckRouteMethodNameTest, 0, false, false) } // now all invokers are unhealthy, so downgraded to all again res = hcr.Route(invokers, &consumerURL, inv) @@ -108,7 +118,7 @@ func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool { func TestNewHealthCheckRouter(t *testing.T) { defer protocol.CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) hcr, _ := NewHealthCheckRouter(&url) h := hcr.(*HealthCheckRouter) assert.Nil(t, h.checker) diff --git a/cluster/router/router.go b/cluster/router/router.go index 9ee1154437e6fd205f08098deabb1ca260c3c040..1d1f79d277860abf34bebb9deab8a0f0a67c7b5d 100644 --- a/cluster/router/router.go +++ b/cluster/router/router.go @@ -23,34 +23,30 @@ import ( ) // Extension - Router - -// RouterFactory Router create factory -type RouterFactory interface { - // NewRouter Create router instance with URL - NewRouter(*common.URL) (Router, error) +// PriorityRouterFactory creates creates priority router with url +type PriorityRouterFactory interface { + // NewPriorityRouter creates router instance with URL + NewPriorityRouter(*common.URL) (PriorityRouter, error) } -// RouterFactory Router create factory use for parse config file -type FileRouterFactory interface { +// FilePriorityRouterFactory creates priority router with parse config file +type FilePriorityRouterFactory interface { // NewFileRouters Create file router with config file - NewFileRouter([]byte) (Router, error) + NewFileRouter([]byte) (PriorityRouter, error) } // Router -type Router interface { +type router interface { // Route Determine the target invokers list. Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker - // Priority Return Priority in router - // 0 to ^int(0) is better - Priority() int64 // URL Return URL in router URL() common.URL } -// Chain -type Chain interface { - // Route Determine the target invokers list with chain. - Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker - // AddRouters Add routers - AddRouters([]Router) +// Router +type PriorityRouter interface { + router + // Priority Return Priority in router + // 0 to ^int(0) is better + Priority() int64 } diff --git a/cluster/router/tag/factory.go b/cluster/router/tag/factory.go index d74924c89862ae4f4cd85b59c7008880298c0c99..a5d989cd31453f6d02eee9c5902dc3666defe4fe 100644 --- a/cluster/router/tag/factory.go +++ b/cluster/router/tag/factory.go @@ -31,17 +31,17 @@ func init() { type tagRouterFactory struct{} // NewTagRouterFactory create a tagRouterFactory -func NewTagRouterFactory() router.RouterFactory { +func NewTagRouterFactory() router.PriorityRouterFactory { return &tagRouterFactory{} } -// NewRouter create a tagRouter by tagRouterFactory with a url +// NewPriorityRouter create a tagRouter by tagRouterFactory with a url // The url contains router configuration information -func (c *tagRouterFactory) NewRouter(url *common.URL) (router.Router, error) { +func (c *tagRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { return NewTagRouter(url) } // NewFileRouter create a tagRouter by profile content -func (c *tagRouterFactory) NewFileRouter(content []byte) (router.Router, error) { +func (c *tagRouterFactory) NewFileRouter(content []byte) (router.PriorityRouter, error) { return NewFileTagRouter(content) } diff --git a/cluster/router/tag/factory_test.go b/cluster/router/tag/factory_test.go index 58bff5b18113d69f97ec513e393aa6759a3cf050..ee195820c123e1fc67a2c27cd12aaa544650b615 100644 --- a/cluster/router/tag/factory_test.go +++ b/cluster/router/tag/factory_test.go @@ -18,6 +18,7 @@ package tag import ( + "fmt" "testing" ) @@ -29,11 +30,16 @@ import ( "github.com/apache/dubbo-go/common" ) -func TestTagRouterFactory_NewRouter(t *testing.T) { - u1, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true") +const ( + factoryLocalIP = "127.0.0.1" + factoryFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true" +) + +func TestTagRouterFactoryNewRouter(t *testing.T) { + u1, err := common.NewURL(fmt.Sprintf(factoryFormat, factoryLocalIP)) assert.Nil(t, err) factory := NewTagRouterFactory() - tagRouter, e := factory.NewRouter(&u1) + tagRouter, e := factory.NewPriorityRouter(&u1) assert.Nil(t, e) assert.NotNil(t, tagRouter) } diff --git a/cluster/router/tag/file_test.go b/cluster/router/tag/file_test.go index 94fcf9e0e0fabed2445417d14b711f91b65b9e5e..513ba0c0b6c622d6a52fad35a24824121eb71b76 100644 --- a/cluster/router/tag/file_test.go +++ b/cluster/router/tag/file_test.go @@ -29,18 +29,21 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +const ( + fileTestTag = `priority: 100 +force: true` +) + func TestNewFileTagRouter(t *testing.T) { - router, e := NewFileTagRouter([]byte(`priority: 100 -force: true`)) + router, e := NewFileTagRouter([]byte(fileTestTag)) assert.Nil(t, e) assert.NotNil(t, router) assert.Equal(t, 100, router.routerRule.Priority) assert.Equal(t, true, router.routerRule.Force) } -func TestFileTagRouter_URL(t *testing.T) { - router, e := NewFileTagRouter([]byte(`priority: 100 -force: true`)) +func TestFileTagRouterURL(t *testing.T) { + router, e := NewFileTagRouter([]byte(fileTestTag)) assert.Nil(t, e) assert.NotNil(t, router) url := router.URL() @@ -52,9 +55,8 @@ force: true`)) } -func TestFileTagRouter_Priority(t *testing.T) { - router, e := NewFileTagRouter([]byte(`priority: 100 -force: true`)) +func TestFileTagRouterPriority(t *testing.T) { + router, e := NewFileTagRouter([]byte(fileTestTag)) assert.Nil(t, e) assert.NotNil(t, router) priority := router.Priority() diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go index 280b56c8ccb69eb5d32dae2369bdc862adb8e6fd..000b3ec6724d85590c86456a009d5194c4e71e03 100644 --- a/cluster/router/tag/tag_router_test.go +++ b/cluster/router/tag/tag_router_test.go @@ -32,6 +32,21 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) +const ( + tagRouterTestHangZhouUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou" + tagRouterTestShangHaiUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai" + tagRouterTestBeijingUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing" + tagRouterTestUserConsumer = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true" + tagRouterTestUserConsumerTag = "dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true" + + tagRouterTestDubboTag = "dubbo.tag" + tagRouterTestDubboForceTag = "dubbo.force.tag" + tagRouterTestHangZhou = "hangzhou" + tagRouterTestGuangZhou = "guangzhou" + tagRouterTestFalse = "false" + tagRouterTestTrue = "true" +) + // MockInvoker is only mock the Invoker to support test tagRouter type MockInvoker struct { url common.URL @@ -73,8 +88,8 @@ func (bi *MockInvoker) Destroy() { bi.available = false } -func TestTagRouter_Priority(t *testing.T) { - u1, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true") +func TestTagRouterPriority(t *testing.T) { + u1, err := common.NewURL(tagRouterTestUserConsumerTag) assert.Nil(t, err) tagRouter, e := NewTagRouter(&u1) assert.Nil(t, e) @@ -82,15 +97,15 @@ func TestTagRouter_Priority(t *testing.T) { assert.Equal(t, int64(0), p) } -func TestTagRouter_Route_force(t *testing.T) { - u1, e1 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true&dubbo.force.tag=true") +func TestTagRouterRouteForce(t *testing.T) { + u1, e1 := common.NewURL(tagRouterTestUserConsumerTag) assert.Nil(t, e1) tagRouter, e := NewTagRouter(&u1) assert.Nil(t, e) - u2, e2 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou") - u3, e3 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai") - u4, e4 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing") + u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) + u3, e3 := common.NewURL(tagRouterTestShangHaiUrl) + u4, e4 := common.NewURL(tagRouterTestBeijingUrl) assert.Nil(t, e2) assert.Nil(t, e3) assert.Nil(t, e4) @@ -100,29 +115,29 @@ func TestTagRouter_Route_force(t *testing.T) { var invokers []protocol.Invoker invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} - inv.SetAttachments("dubbo.tag", "hangzhou") + inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) invRst1 := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 1, len(invRst1)) - assert.Equal(t, "hangzhou", invRst1[0].GetUrl().GetParam("dubbo.tag", "")) + assert.Equal(t, tagRouterTestHangZhou, invRst1[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) - inv.SetAttachments("dubbo.tag", "guangzhou") + inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) invRst2 := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 0, len(invRst2)) - inv.SetAttachments("dubbo.force.tag", "false") - inv.SetAttachments("dubbo.tag", "guangzhou") + inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) + inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) invRst3 := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 3, len(invRst3)) } -func TestTagRouter_Route_noForce(t *testing.T) { - u1, e1 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserConsumer?interface=com.ikurento.user.UserConsumer&group=&version=2.6.0&enabled=true") +func TestTagRouterRouteNoForce(t *testing.T) { + u1, e1 := common.NewURL(tagRouterTestUserConsumer) assert.Nil(t, e1) tagRouter, e := NewTagRouter(&u1) assert.Nil(t, e) - u2, e2 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=hangzhou") - u3, e3 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=shanghai") - u4, e4 := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&enabled=true&dubbo.tag=beijing") + u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) + u3, e3 := common.NewURL(tagRouterTestShangHaiUrl) + u4, e4 := common.NewURL(tagRouterTestShangHaiUrl) assert.Nil(t, e2) assert.Nil(t, e3) assert.Nil(t, e4) @@ -132,16 +147,16 @@ func TestTagRouter_Route_noForce(t *testing.T) { var invokers []protocol.Invoker invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} - inv.SetAttachments("dubbo.tag", "hangzhou") + inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) invRst := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 1, len(invRst)) - assert.Equal(t, "hangzhou", invRst[0].GetUrl().GetParam("dubbo.tag", "")) + assert.Equal(t, tagRouterTestHangZhou, invRst[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) - inv.SetAttachments("dubbo.tag", "guangzhou") - inv.SetAttachments("dubbo.force.tag", "true") + inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) + inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestTrue) invRst1 := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 0, len(invRst1)) - inv.SetAttachments("dubbo.force.tag", "false") + inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) invRst2 := tagRouter.Route(invokers, &u1, inv) assert.Equal(t, 3, len(invRst2)) } diff --git a/common/config/environment.go b/common/config/environment.go index 071af31152ba4ce3c579f70aa23df59d718ce506..44cdd1fca18bfed306b135fe38ef536779e148aa 100644 --- a/common/config/environment.go +++ b/common/config/environment.go @@ -46,7 +46,7 @@ var ( once sync.Once ) -// GetEnvInstance ... +// GetEnvInstance gets env instance by singleton func GetEnvInstance() *Environment { once.Do(func() { instance = &Environment{configCenterFirst: true} @@ -54,7 +54,7 @@ func GetEnvInstance() *Environment { return instance } -// NewEnvInstance ... +// NewEnvInstance creates Environment instance func NewEnvInstance() { instance = &Environment{configCenterFirst: true} } @@ -67,21 +67,22 @@ func NewEnvInstance() { // return env.configCenterFirst //} -// UpdateExternalConfigMap ... +// UpdateExternalConfigMap updates env externalConfigMap field func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.externalConfigMap.Store(k, v) } } -// UpdateAppExternalConfigMap ... +// UpdateAppExternalConfigMap updates env appExternalConfigMap field func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.appExternalConfigMap.Store(k, v) } } -// Configuration ... +// Configuration puts externalConfigMap and appExternalConfigMap into list +// List represents a doubly linked list. func (env *Environment) Configuration() *list.List { cfgList := list.New() // The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration @@ -90,17 +91,17 @@ func (env *Environment) Configuration() *list.List { return cfgList } -// SetDynamicConfiguration ... +// SetDynamicConfiguration sets value for dynamicConfiguration func (env *Environment) SetDynamicConfiguration(dc config_center.DynamicConfiguration) { env.dynamicConfiguration = dc } -// GetDynamicConfiguration ... +// GetDynamicConfiguration gets dynamicConfiguration func (env *Environment) GetDynamicConfiguration() config_center.DynamicConfiguration { return env.dynamicConfiguration } -// InmemoryConfiguration ... +// InmemoryConfiguration stores config in memory type InmemoryConfiguration struct { store *sync.Map } @@ -109,7 +110,7 @@ func newInmemoryConfiguration(p *sync.Map) *InmemoryConfiguration { return &InmemoryConfiguration{store: p} } -// GetProperty ... +// GetProperty gets value from InmemoryConfiguration instance by @key func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { if conf.store == nil { return false, "" @@ -123,14 +124,14 @@ func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { return false, "" } -// GetSubProperty ... +// GetSubProperty gets sub property from InmemoryConfiguration instance by @subkey func (conf *InmemoryConfiguration) GetSubProperty(subKey string) map[string]struct{} { if conf.store == nil { return nil } properties := make(map[string]struct{}) - conf.store.Range(func(key, value interface{}) bool { + conf.store.Range(func(key, _ interface{}) bool { if idx := strings.Index(key.(string), subKey); idx >= 0 { after := key.(string)[idx+len(subKey):] if i := strings.Index(after, "."); i >= 0 { diff --git a/common/config/environment_test.go b/common/config/environment_test.go index 2d84dc4ae31ef74fdcf2a37d7acb4a3e4cf36a09..183071b0ef0d10e9ce010965668b46d30b008a15 100644 --- a/common/config/environment_test.go +++ b/common/config/environment_test.go @@ -29,14 +29,14 @@ func TestGetEnvInstance(t *testing.T) { assert.NotNil(t, instance) } -func TestEnvironment_UpdateExternalConfigMap(t *testing.T) { +func TestEnvironmentUpdateExternalConfigMap(t *testing.T) { GetEnvInstance().UpdateExternalConfigMap(map[string]string{"1": "2"}) v, ok := GetEnvInstance().externalConfigMap.Load("1") assert.True(t, ok) assert.Equal(t, "2", v) } -func TestEnvironment_ConfigurationAndGetProperty(t *testing.T) { +func TestEnvironmentConfigurationAndGetProperty(t *testing.T) { GetEnvInstance().UpdateExternalConfigMap(map[string]string{"1": "2"}) list := GetEnvInstance().Configuration() ok, v := list.Back().Value.(*InmemoryConfiguration).GetProperty("1") @@ -44,7 +44,7 @@ func TestEnvironment_ConfigurationAndGetProperty(t *testing.T) { assert.Equal(t, "2", v) } -func TestInmemoryConfiguration_GetSubProperty(t *testing.T) { +func TestInmemoryConfigurationGetSubProperty(t *testing.T) { GetEnvInstance().UpdateExternalConfigMap(map[string]string{"123": "2"}) list := GetEnvInstance().Configuration() m := list.Front().Value.(*InmemoryConfiguration).GetSubProperty("1") diff --git a/common/constant/default.go b/common/constant/default.go index 3c889158e460031f06b9401008c80f55200a46e4..c69989b4fbc3e95cb42c7f5e403989b9cff9215b 100644 --- a/common/constant/default.go +++ b/common/constant/default.go @@ -43,6 +43,7 @@ const ( DEFAULT_FAILBACK_TASKS = 100 DEFAULT_REST_CLIENT = "resty" DEFAULT_REST_SERVER = "go-restful" + DEFAULT_PORT = 20000 ) const ( @@ -58,6 +59,7 @@ const ( const ( ANY_VALUE = "*" ANYHOST_VALUE = "0.0.0.0" + LOCAL_HOST_VALUE = "192.168.1.1" REMOVE_VALUE_PREFIX = "-" ) diff --git a/common/constant/key.go b/common/constant/key.go index d9413fcc9e24a857cdb398cc6fb96074ffef31b4..8da0e89caf7b5a184bdd98293f04557afe173ecf 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -119,6 +119,7 @@ const ( CONFIG_CLUSTER_KEY = "config.cluster" CONFIG_CHECK_KEY = "config.check" CONFIG_TIMEOUT_KET = "config.timeout" + CONFIG_LOG_DIR_KEY = "config.logDir" CONFIG_VERSION_KEY = "configVersion" COMPATIBLE_CONFIG_KEY = "compatible_config" ) @@ -149,6 +150,7 @@ const ( NACOS_CATEGORY_KEY = "category" NACOS_PROTOCOL_KEY = "protocol" NACOS_PATH_KEY = "path" + NACOS_NAMESPACE_ID = "namespaceId" ) const ( @@ -180,6 +182,9 @@ const ( // ForceUseTag is the tag in attachment ForceUseTag = "dubbo.force.tag" Tagkey = "dubbo.tag" + + // Attachment key in context in invoker + AttachmentKey = "attachment" ) const ( @@ -210,9 +215,9 @@ const ( // consumer CONSUMER = "consumer" // key of access key id - ACCESS_KEY_ID_KEY = "accessKeyId" + ACCESS_KEY_ID_KEY = ".accessKeyId" // key of secret access key - SECRET_ACCESS_KEY_KEY = "secretAccessKey" + SECRET_ACCESS_KEY_KEY = ".secretAccessKey" ) // metadata report diff --git a/common/extension/auth.go b/common/extension/auth.go index d7900045d3f7db9e2587e4e92e377325c74971b3..4fca0a8e8c255456720df9e4fd9852295715b160 100644 --- a/common/extension/auth.go +++ b/common/extension/auth.go @@ -26,13 +26,13 @@ var ( accesskeyStorages = make(map[string]func() filter.AccessKeyStorage) ) -// SetAuthenticator put the fcn into map with name +// SetAuthenticator puts the @fcn into map with name func SetAuthenticator(name string, fcn func() filter.Authenticator) { authenticators[name] = fcn } -// GetAuthenticator find the Authenticator with name -// if not found, it will panic +// GetAuthenticator finds the Authenticator with @name +// Panic if not found func GetAuthenticator(name string) filter.Authenticator { if authenticators[name] == nil { panic("authenticator for " + name + " is not existing, make sure you have import the package.") @@ -40,13 +40,13 @@ func GetAuthenticator(name string) filter.Authenticator { return authenticators[name]() } -// SetAccesskeyStorages will set the fcn into map with this name +// SetAccesskeyStorages will set the @fcn into map with this name func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) { accesskeyStorages[name] = fcn } -// GetAccesskeyStorages find the storage with the name. -// If not found, it will panic. +// GetAccesskeyStorages finds the storage with the @name. +// Panic if not found func GetAccesskeyStorages(name string) filter.AccessKeyStorage { if accesskeyStorages[name] == nil { panic("accesskeyStorages for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/cluster.go b/common/extension/cluster.go index b2d81f6b1e56bb487b1d408b878308f6dfe042e4..8be27a1ca3aaf93dd54201c4ff7081478c746f0f 100644 --- a/common/extension/cluster.go +++ b/common/extension/cluster.go @@ -25,12 +25,13 @@ var ( clusters = make(map[string]func() cluster.Cluster) ) -// SetCluster ... +// SetCluster sets the cluster fault-tolerant mode with @name +// For example: available/failfast/broadcast/failfast/failsafe/... func SetCluster(name string, fcn func() cluster.Cluster) { clusters[name] = fcn } -// GetCluster ... +// GetCluster finds the cluster fault-tolerant mode with @name func GetCluster(name string) cluster.Cluster { if clusters[name] == nil { panic("cluster for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center.go b/common/extension/config_center.go index 03d27db46c94b0ea0e212646077d97f948a8e328..5a2c52f32d070f5ec03bdae0b3cd47f869c28171 100644 --- a/common/extension/config_center.go +++ b/common/extension/config_center.go @@ -26,12 +26,12 @@ var ( configCenters = make(map[string]func(config *common.URL) (config_center.DynamicConfiguration, error)) ) -// SetConfigCenter ... -func SetConfigCenter(name string, v func(config *common.URL) (config_center.DynamicConfiguration, error)) { +// SetConfigCenter sets the DynamicConfiguration with @name +func SetConfigCenter(name string, v func(*common.URL) (config_center.DynamicConfiguration, error)) { configCenters[name] = v } -// GetConfigCenter ... +// GetConfigCenter finds the DynamicConfiguration with @name func GetConfigCenter(name string, config *common.URL) (config_center.DynamicConfiguration, error) { if configCenters[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center_factory.go b/common/extension/config_center_factory.go index 85913fdce1ed3472c2bd9eb4aadbb0f631481dbd..dff89752296c6d2441d043ec628aa13ad219e698 100644 --- a/common/extension/config_center_factory.go +++ b/common/extension/config_center_factory.go @@ -25,12 +25,12 @@ var ( configCenterFactories = make(map[string]func() config_center.DynamicConfigurationFactory) ) -// SetConfigCenterFactory ... +// SetConfigCenterFactory sets the DynamicConfigurationFactory with @name func SetConfigCenterFactory(name string, v func() config_center.DynamicConfigurationFactory) { configCenterFactories[name] = v } -// GetConfigCenterFactory ... +// GetConfigCenterFactory finds the DynamicConfigurationFactory with @name func GetConfigCenterFactory(name string) config_center.DynamicConfigurationFactory { if configCenterFactories[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_reader.go b/common/extension/config_reader.go index aced5b0281ff9313461425e5ec6d70d562c6c947..5e13d8629fd145dac680619a427c68b29226b051 100644 --- a/common/extension/config_reader.go +++ b/common/extension/config_reader.go @@ -26,12 +26,12 @@ var ( defaults = make(map[string]string) ) -// SetConfigReaders set a creator of config reader. +// SetConfigReaders sets a creator of config reader with @name func SetConfigReaders(name string, v func() interfaces.ConfigReader) { configReaders[name] = v } -// GetConfigReaders get a config reader by name. +// GetConfigReaders gets a config reader with @name func GetConfigReaders(name string) interfaces.ConfigReader { if configReaders[name] == nil { panic("config reader for " + name + " is not existing, make sure you have imported the package.") @@ -39,12 +39,12 @@ func GetConfigReaders(name string) interfaces.ConfigReader { return configReaders[name]() } -// SetDefaultConfigReader set {name} to default config reader for {module} +// SetDefaultConfigReader sets @name for @module in default config reader func SetDefaultConfigReader(module, name string) { defaults[module] = name } -// GetDefaultConfigReader +// GetDefaultConfigReader gets default config reader func GetDefaultConfigReader() map[string]string { return defaults } diff --git a/common/extension/configurator.go b/common/extension/configurator.go index de98f8a260ea1f3a2e2a1f32c82dc869585e2789..dc2bea73afb79aaab36e2ce7cc9675169a446eb7 100644 --- a/common/extension/configurator.go +++ b/common/extension/configurator.go @@ -23,7 +23,7 @@ import ( ) const ( - // DefaultKey ... + // DefaultKey for default Configurator DefaultKey = "default" ) @@ -33,12 +33,12 @@ var ( configurator = make(map[string]getConfiguratorFunc) ) -// SetConfigurator ... +// SetConfigurator sets the getConfiguratorFunc with @name func SetConfigurator(name string, v getConfiguratorFunc) { configurator[name] = v } -// GetConfigurator ... +// GetConfigurator finds the Configurator with @name func GetConfigurator(name string, url *common.URL) config_center.Configurator { if configurator[name] == nil { panic("configurator for " + name + " is not existing, make sure you have import the package.") @@ -47,12 +47,12 @@ func GetConfigurator(name string, url *common.URL) config_center.Configurator { } -// SetDefaultConfigurator ... +// SetDefaultConfigurator sets the default Configurator func SetDefaultConfigurator(v getConfiguratorFunc) { configurator[DefaultKey] = v } -// GetDefaultConfigurator ... +// GetDefaultConfigurator gets default configurator func GetDefaultConfigurator(url *common.URL) config_center.Configurator { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") @@ -61,7 +61,7 @@ func GetDefaultConfigurator(url *common.URL) config_center.Configurator { } -// GetDefaultConfiguratorFunc ... +// GetDefaultConfiguratorFunc gets default configurator function func GetDefaultConfiguratorFunc() getConfiguratorFunc { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") diff --git a/common/extension/filter.go b/common/extension/filter.go index deea2d908bc2741e0f15ecc36e9d4fc5975e531e..96059c4363060c41f14ececb466ca62bdaefb1a9 100644 --- a/common/extension/filter.go +++ b/common/extension/filter.go @@ -26,12 +26,13 @@ var ( rejectedExecutionHandler = make(map[string]func() filter.RejectedExecutionHandler) ) -// SetFilter ... +// SetFilter sets the filter extension with @name +// For example: hystrix/metrics/token/tracing/limit/... func SetFilter(name string, v func() filter.Filter) { filters[name] = v } -// GetFilter ... +// GetFilter finds the filter extension with @name func GetFilter(name string) filter.Filter { if filters[name] == nil { panic("filter for " + name + " is not existing, make sure you have imported the package.") @@ -39,12 +40,12 @@ func GetFilter(name string) filter.Filter { return filters[name]() } -// SetRejectedExecutionHandler ... +// SetRejectedExecutionHandler sets the RejectedExecutionHandler with @name func SetRejectedExecutionHandler(name string, creator func() filter.RejectedExecutionHandler) { rejectedExecutionHandler[name] = creator } -// GetRejectedExecutionHandler ... +// GetRejectedExecutionHandler finds the RejectedExecutionHandler with @name func GetRejectedExecutionHandler(name string) filter.RejectedExecutionHandler { creator, ok := rejectedExecutionHandler[name] if !ok { diff --git a/common/extension/graceful_shutdown.go b/common/extension/graceful_shutdown.go index 3abd75c0aa328f3553c3d83340ae440b8dfe3356..cb55419aabbce26b41e5b10f49268f6b3ace516d 100644 --- a/common/extension/graceful_shutdown.go +++ b/common/extension/graceful_shutdown.go @@ -49,7 +49,7 @@ func AddCustomShutdownCallback(callback func()) { customShutdownCallbacks.PushBack(callback) } -// GetAllCustomShutdownCallbacks ... +// GetAllCustomShutdownCallbacks gets all custom shutdown callbacks func GetAllCustomShutdownCallbacks() *list.List { return customShutdownCallbacks } diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go index 365c5d0910812efb00eb94408bb226115b037c02..8def727614dad8393eeef9ced5e30a056fa65461 100644 --- a/common/extension/health_checker.go +++ b/common/extension/health_checker.go @@ -26,12 +26,12 @@ var ( healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker) ) -// SethealthChecker set the HealthChecker with name -func SethealthChecker(name string, fcn func(url *common.URL) router.HealthChecker) { +// SethealthChecker sets the HealthChecker with @name +func SethealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) { healthCheckers[name] = fcn } -// GetHealthChecker get the HealthChecker with name +// GetHealthChecker gets the HealthChecker with @name func GetHealthChecker(name string, url *common.URL) router.HealthChecker { if healthCheckers[name] == nil { panic("healthCheckers for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/health_checker_test.go b/common/extension/health_checker_test.go index ec934e6e9cedc5acbef350f17b87b0b2e37bc844..4e83a6f6e1ed8a57b6e6374377d08eabfb56c604 100644 --- a/common/extension/health_checker_test.go +++ b/common/extension/health_checker_test.go @@ -44,6 +44,6 @@ func (m mockHealthChecker) IsHealthy(invoker protocol.Invoker) bool { return true } -func newMockhealthCheck(url *common.URL) router.HealthChecker { +func newMockhealthCheck(_ *common.URL) router.HealthChecker { return &mockHealthChecker{} } diff --git a/common/extension/loadbalance.go b/common/extension/loadbalance.go index 0d557a4640ed892a18ad59a3247763ab5807a593..aa19141014a6c42df0c17dad05301997f67fbd79 100644 --- a/common/extension/loadbalance.go +++ b/common/extension/loadbalance.go @@ -25,12 +25,13 @@ var ( loadbalances = make(map[string]func() cluster.LoadBalance) ) -// SetLoadbalance ... +// SetLoadbalance sets the loadbalance extension with @name +// For example: random/round_robin/consistent_hash/least_active/... func SetLoadbalance(name string, fcn func() cluster.LoadBalance) { loadbalances[name] = fcn } -// GetLoadbalance ... +// GetLoadbalance finds the loadbalance extension with @name func GetLoadbalance(name string) cluster.LoadBalance { if loadbalances[name] == nil { panic("loadbalance for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/metadata_report_factory.go b/common/extension/metadata_report_factory.go index 0ae0793bb4459767cb42fb1860fc484388aae1a3..c55f8617fadd9d09d68547b05341d127716ce73c 100644 --- a/common/extension/metadata_report_factory.go +++ b/common/extension/metadata_report_factory.go @@ -25,12 +25,12 @@ var ( metaDataReportFactories = make(map[string]func() metadata.MetadataReportFactory, 8) ) -// SetMetadataReportFactory ... +// SetMetadataReportFactory sets the MetadataReportFactory with @name func SetMetadataReportFactory(name string, v func() metadata.MetadataReportFactory) { metaDataReportFactories[name] = v } -// GetMetadataReportFactory ... +// GetMetadataReportFactory finds the MetadataReportFactory with @name func GetMetadataReportFactory(name string) metadata.MetadataReportFactory { if metaDataReportFactories[name] == nil { panic("metadata report for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/metrics.go b/common/extension/metrics.go index 42fca7a2db36614fcef31dd5ba7324a156164d4f..60cf6bac2384c7367094adad83e01f7dcf64a33d 100644 --- a/common/extension/metrics.go +++ b/common/extension/metrics.go @@ -27,12 +27,12 @@ var ( metricReporterMap = make(map[string]func() metrics.Reporter, 4) ) -// SetMetricReporter set a reporter with the name +// SetMetricReporter sets a reporter with the @name func SetMetricReporter(name string, reporterFunc func() metrics.Reporter) { metricReporterMap[name] = reporterFunc } -// GetMetricReporter find the reporter with name. +// GetMetricReporter finds the reporter with @name. // if not found, it will panic. // we should know that this method usually is called when system starts, so we should panic func GetMetricReporter(name string) metrics.Reporter { diff --git a/common/extension/metrics_test.go b/common/extension/metrics_test.go index 6a8a3fe538a9cd68c57c91592a88ec257ae4a267..2aaae75f0ccf7929754582aa42aa1bff4ece7c22 100644 --- a/common/extension/metrics_test.go +++ b/common/extension/metrics_test.go @@ -45,5 +45,6 @@ func TestGetMetricReporter(t *testing.T) { type mockReporter struct { } +// Report method for feature expansion func (m mockReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) { } diff --git a/common/extension/protocol.go b/common/extension/protocol.go index 009687a17ace8cea567248af655e04604d09d9b8..c89dd08fae5d12b384d6ca4e797343fe79897bbd 100644 --- a/common/extension/protocol.go +++ b/common/extension/protocol.go @@ -25,12 +25,12 @@ var ( protocols = make(map[string]func() protocol.Protocol) ) -// SetProtocol ... +// SetProtocol sets the protocol extension with @name func SetProtocol(name string, v func() protocol.Protocol) { protocols[name] = v } -// GetProtocol ... +// GetProtocol finds the protocol extension with @name func GetProtocol(name string) protocol.Protocol { if protocols[name] == nil { panic("protocol for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/proxy_factory.go b/common/extension/proxy_factory.go index 19826bb0560ea0d3fa471c04873b20a6878f57d8..1e326d884b5dd37925c38ffdf0a87e69bf6a865c 100644 --- a/common/extension/proxy_factory.go +++ b/common/extension/proxy_factory.go @@ -25,12 +25,12 @@ var ( proxyFactories = make(map[string]func(...proxy.Option) proxy.ProxyFactory) ) -// SetProxyFactory ... +// SetProxyFactory sets the ProxyFactory extension with @name func SetProxyFactory(name string, f func(...proxy.Option) proxy.ProxyFactory) { proxyFactories[name] = f } -// GetProxyFactory ... +// GetProxyFactory finds the ProxyFactory extension with @name func GetProxyFactory(name string) proxy.ProxyFactory { if name == "" { name = "default" diff --git a/common/extension/registry.go b/common/extension/registry.go index 6ba746dc47382927d12ce39b7936212c5d75153d..187c8fecf4e27c87d6feff8749730c77a83b2f32 100644 --- a/common/extension/registry.go +++ b/common/extension/registry.go @@ -26,15 +26,15 @@ var ( registrys = make(map[string]func(config *common.URL) (registry.Registry, error)) ) -// SetRegistry ... -func SetRegistry(name string, v func(config *common.URL) (registry.Registry, error)) { +// SetRegistry sets the registry extension with @name +func SetRegistry(name string, v func(_ *common.URL) (registry.Registry, error)) { registrys[name] = v } -// GetRegistry ... +// GetRegistry finds the registry extension with @name func GetRegistry(name string, config *common.URL) (registry.Registry, error) { if registrys[name] == nil { - panic("registry for " + name + " is not existing, make sure you have import the package.") + panic("registry for " + name + " does not exist. please make sure that you have imported the package `github.com/apache/dubbo-go/registry/" + name + "`.") } return registrys[name](config) diff --git a/common/extension/registry_directory.go b/common/extension/registry_directory.go index 6b92189c4e98b391a90e6e71a68d51a252eede2a..330fc46400daf81047e5c24c1634249e355d74b7 100644 --- a/common/extension/registry_directory.go +++ b/common/extension/registry_directory.go @@ -27,12 +27,12 @@ type registryDirectory func(url *common.URL, registry registry.Registry) (cluste var defaultRegistry registryDirectory -// SetDefaultRegistryDirectory ... +// SetDefaultRegistryDirectory sets the default registryDirectory func SetDefaultRegistryDirectory(v registryDirectory) { defaultRegistry = v } -// GetDefaultRegistryDirectory ... +// GetDefaultRegistryDirectory finds the registryDirectory with url and registry func GetDefaultRegistryDirectory(config *common.URL, registry registry.Registry) (cluster.Directory, error) { if defaultRegistry == nil { panic("registry directory is not existing, make sure you have import the package.") diff --git a/common/extension/rest_client.go b/common/extension/rest_client.go index 514d1fdfd2efb5c291fdb47df4dd69da26fa90b1..0c2f4ddf95d2226cfadc3ab6492c629ba15c4063 100644 --- a/common/extension/rest_client.go +++ b/common/extension/rest_client.go @@ -25,10 +25,12 @@ var ( restClients = make(map[string]func(restOptions *client.RestOptions) client.RestClient, 8) ) -func SetRestClient(name string, fun func(restOptions *client.RestOptions) client.RestClient) { +// SetRestClient sets the RestClient with @name +func SetRestClient(name string, fun func(_ *client.RestOptions) client.RestClient) { restClients[name] = fun } +// GetNewRestClient finds the RestClient with @name func GetNewRestClient(name string, restOptions *client.RestOptions) client.RestClient { if restClients[name] == nil { panic("restClient for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/rest_server.go b/common/extension/rest_server.go index fa8d435a5c976a4c95b036810fa2916a327a73b9..37a231a57c861ae49aab244eb9fa8b611ae63f6d 100644 --- a/common/extension/rest_server.go +++ b/common/extension/rest_server.go @@ -25,10 +25,12 @@ var ( restServers = make(map[string]func() server.RestServer, 8) ) +// SetRestServer sets the RestServer with @name func SetRestServer(name string, fun func() server.RestServer) { restServers[name] = fun } +// GetNewRestServer finds the RestServer with @name func GetNewRestServer(name string) server.RestServer { if restServers[name] == nil { panic("restServer for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go index 1339228618def41ccebc8d54cdebb5a623e605fa..b61a05668a0cd24e28f6de46754c00ffbcb766d8 100644 --- a/common/extension/router_factory.go +++ b/common/extension/router_factory.go @@ -26,31 +26,31 @@ import ( ) var ( - routers = make(map[string]func() router.RouterFactory) + routers = make(map[string]func() router.PriorityRouterFactory) fileRouterFactoryOnce sync.Once - fileRouterFactories = make(map[string]router.FileRouterFactory) + fileRouterFactories = make(map[string]router.FilePriorityRouterFactory) ) -// SetRouterFactory Set create router factory function by name -func SetRouterFactory(name string, fun func() router.RouterFactory) { +// SetRouterFactory sets create router factory function with @name +func SetRouterFactory(name string, fun func() router.PriorityRouterFactory) { routers[name] = fun } -// GetRouterFactory Get create router factory function by name -func GetRouterFactory(name string) router.RouterFactory { +// GetRouterFactory gets create router factory function by @name +func GetRouterFactory(name string) router.PriorityRouterFactory { if routers[name] == nil { panic("router_factory for " + name + " is not existing, make sure you have import the package.") } return routers[name]() } -// GetRouterFactories Get all create router factory function -func GetRouterFactories() map[string]func() router.RouterFactory { +// GetRouterFactories gets all create router factory function +func GetRouterFactories() map[string]func() router.PriorityRouterFactory { return routers } -// GetFileRouterFactories Get all create file router factory instance -func GetFileRouterFactories() map[string]router.FileRouterFactory { +// GetFileRouterFactories gets all create file router factory instance +func GetFileRouterFactories() map[string]router.FilePriorityRouterFactory { l := len(routers) if l == 0 { return nil @@ -58,7 +58,7 @@ func GetFileRouterFactories() map[string]router.FileRouterFactory { fileRouterFactoryOnce.Do(func() { for k := range routers { factory := GetRouterFactory(k) - if fr, ok := factory.(router.FileRouterFactory); ok { + if fr, ok := factory.(router.FilePriorityRouterFactory); ok { fileRouterFactories[k] = fr } } diff --git a/common/extension/service_discovery.go b/common/extension/service_discovery.go index 25b80cf3353505c058bea40cc4c80712ad923d2d..1d891c2189e55fbd6fa03e4c6e5ea6ec5b654e46 100644 --- a/common/extension/service_discovery.go +++ b/common/extension/service_discovery.go @@ -29,8 +29,8 @@ var ( discoveryCreatorMap = make(map[string]func(url *common.URL) (registry.ServiceDiscovery, error), 4) ) -// SetServiceDiscovery will store the creator and name -func SetServiceDiscovery(name string, creator func(url *common.URL) (registry.ServiceDiscovery, error)) { +// SetServiceDiscovery will store the @creator and @name +func SetServiceDiscovery(name string, creator func(_ *common.URL) (registry.ServiceDiscovery, error)) { discoveryCreatorMap[name] = creator } diff --git a/common/extension/tps_limit.go b/common/extension/tps_limit.go index c72c2b030fc0f391362189bfe18a65582543693a..d25821deee626cb75c94af2257f877c9983023de 100644 --- a/common/extension/tps_limit.go +++ b/common/extension/tps_limit.go @@ -26,12 +26,12 @@ var ( tpsLimiter = make(map[string]func() filter.TpsLimiter) ) -// SetTpsLimiter ... +// SetTpsLimiter sets the TpsLimiter with @name func SetTpsLimiter(name string, creator func() filter.TpsLimiter) { tpsLimiter[name] = creator } -// GetTpsLimiter ... +// GetTpsLimiter finds the TpsLimiter with @name func GetTpsLimiter(name string) filter.TpsLimiter { creator, ok := tpsLimiter[name] if !ok { @@ -41,12 +41,12 @@ func GetTpsLimiter(name string) filter.TpsLimiter { return creator() } -// SetTpsLimitStrategy ... +// SetTpsLimitStrategy sets the TpsLimitStrategyCreator with @name func SetTpsLimitStrategy(name string, creator filter.TpsLimitStrategyCreator) { tpsLimitStrategy[name] = creator } -// GetTpsLimitStrategyCreator ... +// GetTpsLimitStrategyCreator finds the TpsLimitStrategyCreator with @name func GetTpsLimitStrategyCreator(name string) filter.TpsLimitStrategyCreator { creator, ok := tpsLimitStrategy[name] if !ok { diff --git a/common/logger/logger.go b/common/logger/logger.go index 016afe69808f2007541c617f406db64beb511f1c..9bc6a461003d086e8951ebac3d6997774ac69b90 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -40,13 +40,13 @@ var ( logger Logger ) -// DubboLogger ... +// nolint type DubboLogger struct { Logger dynamicLevel zap.AtomicLevel } -// Logger ... +// Logger is the interface for Logger types type Logger interface { Info(args ...interface{}) Warn(args ...interface{}) @@ -67,7 +67,7 @@ func init() { } } -// InitLog ... +// InitLog use for init logger by call InitLogger func InitLog(logConfFile string) error { if logConfFile == "" { InitLogger(nil) @@ -96,7 +96,7 @@ func InitLog(logConfFile string) error { return nil } -// InitLogger ... +// InitLogger use for init logger by @conf func InitLogger(conf *zap.Config) { var zapLoggerConfig zap.Config if conf == nil { @@ -125,18 +125,18 @@ func InitLogger(conf *zap.Config) { getty.SetLogger(logger) } -// SetLogger ... +// SetLogger sets logger for dubbo and getty func SetLogger(log Logger) { logger = log getty.SetLogger(logger) } -// GetLogger ... +// GetLogger gets the logger func GetLogger() Logger { return logger } -// SetLoggerLevel ... +// SetLoggerLevel use for set logger level func SetLoggerLevel(level string) bool { if l, ok := logger.(OpsLogger); ok { l.SetLoggerLevel(level) @@ -145,13 +145,13 @@ func SetLoggerLevel(level string) bool { return false } -// OpsLogger ... +// OpsLogger use for the SetLoggerLevel type OpsLogger interface { Logger SetLoggerLevel(level string) } -// SetLoggerLevel ... +// SetLoggerLevel use for set logger level func (dl *DubboLogger) SetLoggerLevel(level string) { l := new(zapcore.Level) l.Set(level) diff --git a/common/logger/logging.go b/common/logger/logging.go index 36d48ee61e8a4a986abfbaa79f3d361cd81494f4..7a31ece203815287384ade282b2a4f12e11abc2a 100644 --- a/common/logger/logging.go +++ b/common/logger/logging.go @@ -17,42 +17,42 @@ package logger -// Info ... +// Info is info level func Info(args ...interface{}) { logger.Info(args...) } -// Warn ... +// Warn is warning level func Warn(args ...interface{}) { logger.Warn(args...) } -// Error ... +// Error is error level func Error(args ...interface{}) { logger.Error(args...) } -// Debug ... +// Debug is debug level func Debug(args ...interface{}) { logger.Debug(args...) } -// Infof ... +// Infof is format info level func Infof(fmt string, args ...interface{}) { logger.Infof(fmt, args...) } -// Warnf ... +// Warnf is format warning level func Warnf(fmt string, args ...interface{}) { logger.Warnf(fmt, args...) } -// Errorf ... +// Errorf is format error level func Errorf(fmt string, args ...interface{}) { logger.Errorf(fmt, args...) } -// Debugf ... +// Debugf is format debug level func Debugf(fmt string, args ...interface{}) { logger.Debugf(fmt, args...) } diff --git a/common/node.go b/common/node.go index 979eee31ef3a63eb21af6c9045aee7f6d784f2ba..4febd78536126c67bdc65fc09d4be47fb869ef5e 100644 --- a/common/node.go +++ b/common/node.go @@ -17,7 +17,7 @@ package common -// Node ... +// Node use for process dubbo node type Node interface { GetUrl() URL IsAvailable() bool diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go index 68ba3ff7882837a9419c5e47228461af11fd79ba..abcf87cd9d297769bf8aff6fa07d6a4659091eb6 100644 --- a/common/proxy/proxy.go +++ b/common/proxy/proxy.go @@ -25,12 +25,13 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) -// Proxy struct +// nolint type Proxy struct { rpc common.RPCService invoke protocol.Invoker @@ -44,7 +45,7 @@ var ( typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type() ) -// NewProxy ... +// NewProxy create service proxy. func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy { return &Proxy{ invoke: invoke, @@ -59,7 +60,6 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str // type XxxProvider struct { // Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error // } - func (p *Proxy) Implement(v common.RPCService) { // check parameters, incoming interface must be a elem's pointer. @@ -141,7 +141,7 @@ func (p *Proxy) Implement(v common.RPCService) { } // add user setAttachment - atm := invCtx.Value("attachment") + atm := invCtx.Value(constant.AttachmentKey) if m, ok := atm.(map[string]string); ok { for k, value := range m { inv.SetAttachments(k, value) @@ -149,6 +149,9 @@ func (p *Proxy) Implement(v common.RPCService) { } result := p.invoke.Invoke(invCtx, inv) + if len(result.Attachments()) > 0 { + invCtx = context.WithValue(invCtx, constant.AttachmentKey, result.Attachments()) + } err = result.Error() logger.Debugf("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err) @@ -202,12 +205,12 @@ func (p *Proxy) Implement(v common.RPCService) { } -// Get ... +// Get gets rpc service instance. func (p *Proxy) Get() common.RPCService { return p.rpc } -// GetCallback ... +// GetCallback gets callback. func (p *Proxy) GetCallback() interface{} { return p.callBack } diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go index 7b249a3e9754b097130a80bf3819d282dad6b6e8..117428cb253e1ad4a4ceee59aa620d7097b41a75 100644 --- a/common/proxy/proxy_factory.go +++ b/common/proxy/proxy_factory.go @@ -22,12 +22,12 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ProxyFactory ... +// ProxyFactory interface. type ProxyFactory interface { GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy GetInvoker(url common.URL) protocol.Invoker } -// Option ... +// Option will define a function of handling ProxyFactory type Option func(ProxyFactory) diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go index 114cfee2363022da5f7957a825a16fc42b8c928f..1b8ca222011292040c57c3e86df0438943a5b464 100644 --- a/common/proxy/proxy_factory/default.go +++ b/common/proxy/proxy_factory/default.go @@ -40,7 +40,7 @@ func init() { extension.SetProxyFactory("default", NewDefaultProxyFactory) } -// DefaultProxyFactory ... +// DefaultProxyFactory is the default proxy factory type DefaultProxyFactory struct { //delegate ProxyFactory } @@ -53,17 +53,17 @@ type DefaultProxyFactory struct { // } //} -// NewDefaultProxyFactory ... -func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory { +// NewDefaultProxyFactory returns a proxy factory instance +func NewDefaultProxyFactory(_ ...proxy.Option) proxy.ProxyFactory { return &DefaultProxyFactory{} } -// GetProxy ... +// GetProxy gets a proxy func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy { return factory.GetAsyncProxy(invoker, nil, url) } -// GetAsyncProxy ... +// GetAsyncProxy gets a async proxy func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy { //create proxy attachments := map[string]string{} @@ -71,19 +71,19 @@ func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, call return proxy.NewProxy(invoker, callBack, attachments) } -// GetInvoker ... +// GetInvoker gets a invoker func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker { return &ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), } } -// ProxyInvoker ... +// ProxyInvoker is a invoker struct type ProxyInvoker struct { protocol.BaseInvoker } -// Invoke ... +// Invoke is used to call service method by invocation func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { result := &protocol.RPCResult{} result.SetAttachments(invocation.Attachments()) @@ -113,6 +113,7 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati in := []reflect.Value{svc.Rcvr()} if method.CtxType() != nil { + ctx = context.WithValue(ctx, constant.AttachmentKey, invocation.Attachments()) in = append(in, method.SuiteContext(ctx)) } diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go index 7159b4b00eb2fcddb0f20f701f56b3179e57c4a0..99d5c020f4bfd0254ddddc65e78bcae2e252b6be 100644 --- a/common/proxy/proxy_factory/default_test.go +++ b/common/proxy/proxy_factory/default_test.go @@ -31,7 +31,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func Test_GetProxy(t *testing.T) { +func TestGetProxy(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(*url), url) @@ -45,7 +45,7 @@ func (u *TestAsync) CallBack(res common.CallbackResponse) { fmt.Println("CallBack res:", res) } -func Test_GetAsyncProxy(t *testing.T) { +func TestGetAsyncProxy(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() async := &TestAsync{} @@ -53,7 +53,7 @@ func Test_GetAsyncProxy(t *testing.T) { assert.NotNil(t, proxy) } -func Test_GetInvoker(t *testing.T) { +func TestGetInvoker(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() invoker := proxyFactory.GetInvoker(*url) diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go index 8c1c0295d05135095b5be35b5b2b16428691d7f2..14b2befbc47242d9cc9a2f88e9070b84828062c0 100644 --- a/common/proxy/proxy_test.go +++ b/common/proxy/proxy_test.go @@ -53,7 +53,7 @@ func (s *TestServiceInt) Reference() string { return "com.test.TestServiceInt" } -func TestProxy_Implement(t *testing.T) { +func TestProxyImplement(t *testing.T) { invoker := protocol.NewBaseInvoker(common.URL{}) p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"}) @@ -84,7 +84,7 @@ func TestProxy_Implement(t *testing.T) { TestService methodOne func(context.Context, interface{}, *struct{}) error } - s1 := &S1{TestService: *s, methodOne: func(i context.Context, i2 interface{}, i3 *struct{}) error { + s1 := &S1{TestService: *s, methodOne: func(_ context.Context, _ interface{}, _ *struct{}) error { return perrors.New("errors") }} p.Implement(s1) diff --git a/common/rpc_service.go b/common/rpc_service.go index ebd1d02f843bc339c3a37d977e2138798307475d..05ca3721d98124ccbdebedb47b5ae4edd8b84add 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -35,23 +35,23 @@ import ( ) // RPCService -//rpc service interface +// rpc service interface type RPCService interface { // Reference: // rpc service id or reference id Reference() string } -//AsyncCallbackService callback interface for async +// AsyncCallbackService callback interface for async type AsyncCallbackService interface { // Callback: callback CallBack(response CallbackResponse) } -//CallbackResponse for different protocol +// CallbackResponse for different protocol type CallbackResponse interface{} -//AsyncCallback async callback method +// AsyncCallback async callback method type AsyncCallback func(response CallbackResponse) // for lowercase func @@ -59,7 +59,6 @@ type AsyncCallback func(response CallbackResponse) // return map[string][string]{} // } const ( - // METHOD_MAPPER ... METHOD_MAPPER = "MethodMapper" ) @@ -68,8 +67,7 @@ var ( // because Typeof takes an empty interface value. This is annoying. typeOfError = reflect.TypeOf((*error)(nil)).Elem() - // ServiceMap ... - // todo: lowerecas? + // ServiceMap store description of service. ServiceMap = &serviceMap{ serviceMap: make(map[string]map[string]*Service), interfaceMap: make(map[string][]*Service), @@ -80,7 +78,7 @@ var ( // info of method ////////////////////////// -// MethodType ... +// MethodType is description of service method. type MethodType struct { method reflect.Method ctxType reflect.Type // request context @@ -88,27 +86,27 @@ type MethodType struct { replyType reflect.Type // return value, otherwise it is nil } -// Method ... +// Method gets @m.method. func (m *MethodType) Method() reflect.Method { return m.method } -// CtxType ... +// CtxType gets @m.ctxType. func (m *MethodType) CtxType() reflect.Type { return m.ctxType } -// ArgsType ... +// ArgsType gets @m.argsType. func (m *MethodType) ArgsType() []reflect.Type { return m.argsType } -// ReplyType ... +// ReplyType gets @m.replyType. func (m *MethodType) ReplyType() reflect.Type { return m.replyType } -// SuiteContext ... +// SuiteContext tranfers @ctx to reflect.Value type or get it from @m.ctxType. func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { if contextv := reflect.ValueOf(ctx); contextv.IsValid() { return contextv @@ -120,7 +118,7 @@ func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { // info of service interface ////////////////////////// -// Service ... +// Service is description of service type Service struct { name string rcvr reflect.Value @@ -128,17 +126,17 @@ type Service struct { methods map[string]*MethodType } -// Method ... +// Method gets @s.methods. func (s *Service) Method() map[string]*MethodType { return s.methods } -// RcvrType ... +// RcvrType gets @s.rcvrType. func (s *Service) RcvrType() reflect.Type { return s.rcvrType } -// Rcvr ... +// Rcvr gets @s.rcvr. func (s *Service) Rcvr() reflect.Value { return s.rcvr } @@ -153,7 +151,7 @@ type serviceMap struct { interfaceMap map[string][]*Service // interface -> service } -// GetService get a service defination by protocol and name +// GetService gets a service defination by protocol and name func (sm *serviceMap) GetService(protocol, name string) *Service { sm.mutex.RLock() defer sm.mutex.RUnlock() @@ -166,7 +164,7 @@ func (sm *serviceMap) GetService(protocol, name string) *Service { return nil } -// GetInterface get an interface defination by interface name +// GetInterface gets an interface defination by interface name func (sm *serviceMap) GetInterface(interfaceName string) []*Service { sm.mutex.RLock() defer sm.mutex.RUnlock() @@ -176,7 +174,7 @@ func (sm *serviceMap) GetInterface(interfaceName string) []*Service { return nil } -// Register register a service by @interfaceName and @protocol +// Register registers a service by @interfaceName and @protocol func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) (string, error) { if sm.serviceMap[protocol] == nil { sm.serviceMap[protocol] = make(map[string]*Service) @@ -224,7 +222,7 @@ func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) return strings.TrimSuffix(methods, ","), nil } -// UnRegister cancel a service by @interfaceName, @protocol and @serviceId +// UnRegister cancels a service by @interfaceName, @protocol and @serviceId func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) error { if protocol == "" || serviceId == "" { return perrors.New("protocol or serviceName is nil") diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go index 2311205d0ec0c2fd4642a4d8639c0bf871fe1d17..7c4eb421d7ed0e5e0306eb1a1a2ff5ac31c29b6d 100644 --- a/common/rpc_service_test.go +++ b/common/rpc_service_test.go @@ -27,6 +27,14 @@ import ( "github.com/stretchr/testify/assert" ) +const ( + referenceTestPath = "com.test.Path" + referenceTestPathDistinct = "com.test.Path1" + testInterfaceName = "testService" + testProtocol = "testprotocol" + testSuiteMethodExpectedString = "interface {}" +) + type TestService struct { } @@ -40,7 +48,7 @@ func (s *TestService) MethodThree() error { return nil } func (s *TestService) Reference() string { - return "com.test.Path" + return referenceTestPath } func (s *TestService) MethodMapper() map[string]string { return map[string]string{ @@ -63,36 +71,36 @@ func (s *testService) Method4(ctx context.Context, args []interface{}, rsp *stru return nil } func (s *testService) Reference() string { - return "com.test.Path" + return referenceTestPath } type TestService1 struct { } func (s *TestService1) Reference() string { - return "com.test.Path1" + return referenceTestPathDistinct } -func TestServiceMap_Register(t *testing.T) { +func TestServiceMapRegister(t *testing.T) { // lowercase s0 := &testService{} // methods, err := ServiceMap.Register("testporotocol", s0) - _, err := ServiceMap.Register("testService", "testporotocol", s0) + _, err := ServiceMap.Register(testInterfaceName, "testporotocol", s0) assert.EqualError(t, err, "type testService is not exported") // succ s := &TestService{} - methods, err := ServiceMap.Register("testService", "testporotocol", s) + methods, err := ServiceMap.Register(testInterfaceName, "testporotocol", s) assert.NoError(t, err) assert.Equal(t, "MethodOne,MethodThree,methodTwo", methods) // repeat - _, err = ServiceMap.Register("testService", "testporotocol", s) + _, err = ServiceMap.Register(testInterfaceName, "testporotocol", s) assert.EqualError(t, err, "service already defined: com.test.Path") // no method s1 := &TestService1{} - _, err = ServiceMap.Register("testService", "testporotocol", s1) + _, err = ServiceMap.Register(testInterfaceName, "testporotocol", s1) assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type") ServiceMap = &serviceMap{ @@ -101,28 +109,28 @@ func TestServiceMap_Register(t *testing.T) { } } -func TestServiceMap_UnRegister(t *testing.T) { +func TestServiceMapUnRegister(t *testing.T) { s := &TestService{} - _, err := ServiceMap.Register("TestService", "testprotocol", s) + _, err := ServiceMap.Register("TestService", testProtocol, s) assert.NoError(t, err) - assert.NotNil(t, ServiceMap.GetService("testprotocol", "com.test.Path")) + assert.NotNil(t, ServiceMap.GetService(testProtocol, referenceTestPath)) assert.Equal(t, 1, len(ServiceMap.GetInterface("TestService"))) - err = ServiceMap.UnRegister("", "", "com.test.Path") + err = ServiceMap.UnRegister("", "", referenceTestPath) assert.EqualError(t, err, "protocol or serviceName is nil") - err = ServiceMap.UnRegister("", "protocol", "com.test.Path") + err = ServiceMap.UnRegister("", "protocol", referenceTestPath) assert.EqualError(t, err, "no services for protocol") - err = ServiceMap.UnRegister("", "testprotocol", "com.test.Path1") + err = ServiceMap.UnRegister("", testProtocol, referenceTestPathDistinct) assert.EqualError(t, err, "no service for com.test.Path1") // succ - err = ServiceMap.UnRegister("TestService", "testprotocol", "com.test.Path") + err = ServiceMap.UnRegister("TestService", testProtocol, referenceTestPath) assert.NoError(t, err) } -func TestMethodType_SuiteContext(t *testing.T) { +func TestMethodTypeSuiteContext(t *testing.T) { mt := &MethodType{ctxType: reflect.TypeOf(context.TODO())} ctx := context.WithValue(context.Background(), "key", "value") assert.Equal(t, reflect.ValueOf(ctx), mt.SuiteContext(ctx)) @@ -139,9 +147,9 @@ func TestSuiteMethod(t *testing.T) { method = methodType.Method() assert.Equal(t, "func(*common.TestService, context.Context, interface {}, interface {}, interface {}) error", method.Type.String()) at := methodType.ArgsType() - assert.Equal(t, "interface {}", at[0].String()) - assert.Equal(t, "interface {}", at[1].String()) - assert.Equal(t, "interface {}", at[2].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[0].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[1].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[2].String()) ct := methodType.CtxType() assert.Equal(t, "context.Context", ct.String()) rt := methodType.ReplyType() @@ -153,12 +161,12 @@ func TestSuiteMethod(t *testing.T) { method = methodType.Method() assert.Equal(t, "func(*common.TestService, interface {}, interface {}, interface {}) (interface {}, error)", method.Type.String()) at = methodType.ArgsType() - assert.Equal(t, "interface {}", at[0].String()) - assert.Equal(t, "interface {}", at[1].String()) - assert.Equal(t, "interface {}", at[2].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[0].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[1].String()) + assert.Equal(t, testSuiteMethodExpectedString, at[2].String()) assert.Nil(t, methodType.CtxType()) rt = methodType.ReplyType() - assert.Equal(t, "interface {}", rt.String()) + assert.Equal(t, testSuiteMethodExpectedString, rt.String()) method, ok = reflect.TypeOf(s).MethodByName("MethodThree") assert.True(t, ok) diff --git a/common/url.go b/common/url.go index ebb648db27c3efff534f0d0a545f2211f335aa89..983d1d798d048e33b18f68aee16b9b38febcf16b 100644 --- a/common/url.go +++ b/common/url.go @@ -18,7 +18,6 @@ package common import ( - "bytes" "encoding/base64" "fmt" "math" @@ -26,7 +25,6 @@ import ( "net/url" "strconv" "strings" - "sync" ) import ( @@ -40,136 +38,141 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -///////////////////////////////// +// /////////////////////////////// // dubbo role type -///////////////////////////////// +// /////////////////////////////// // role constant const ( - // CONSUMER ... + // CONSUMER is consumer role CONSUMER = iota - // CONFIGURATOR ... + // CONFIGURATOR is configurator role CONFIGURATOR - // ROUTER ... + // ROUTER is router role ROUTER - // PROVIDER ... + // PROVIDER is provider role PROVIDER + PROTOCOL = "protocol" ) var ( - // DubboNodes ... + // DubboNodes Dubbo service node DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"} // DubboRole Dubbo service role DubboRole = [...]string{"consumer", "", "routers", "provider"} ) -// RoleType ... +// nolint type RoleType int func (t RoleType) String() string { return DubboNodes[t] } -// Role ... +// Role returns role by @RoleType func (t RoleType) Role() string { return DubboRole[t] } type baseUrl struct { - Protocol string - Location string // ip+port - Ip string - Port string - //url.Values is not safe map, add to avoid concurrent map read and map write error - paramsLock sync.RWMutex + Protocol string + Location string // ip+port + Ip string + Port string params url.Values PrimitiveURL string } -// URL ... +// URL is not thread-safe. +// we fail to define this struct to be immutable object. +// but, those method which will update the URL, including SetParam, SetParams +// are only allowed to be invoked in creating URL instance +// Please keep in mind that this struct is immutable after it has been created and initialized. type URL struct { baseUrl Path string // like /com.ikurento.dubbo.UserProvider3 Username string Password string Methods []string - //special for registry + // special for registry SubURL *URL } +// Option accepts url +// Option will define a function of handling URL type option func(*URL) -// WithUsername ... +// WithUsername sets username for url func WithUsername(username string) option { return func(url *URL) { url.Username = username } } -// WithPassword ... +// WithPassword sets password for url func WithPassword(pwd string) option { return func(url *URL) { url.Password = pwd } } -// WithMethods ... +// WithMethods sets methods for url func WithMethods(methods []string) option { return func(url *URL) { url.Methods = methods } } -// WithParams ... +// WithParams sets params for url func WithParams(params url.Values) option { return func(url *URL) { url.params = params } } -// WithParamsValue ... +// WithParamsValue sets params field for url func WithParamsValue(key, val string) option { return func(url *URL) { url.SetParam(key, val) } } -// WithProtocol ... +// WithProtocol sets protocol for url func WithProtocol(proto string) option { return func(url *URL) { url.Protocol = proto } } -// WithIp ... +// WithIp sets ip for url func WithIp(ip string) option { return func(url *URL) { url.Ip = ip } } -// WithPort ... +// WithPort sets port for url func WithPort(port string) option { return func(url *URL) { url.Port = port } } -// WithPath ... +// WithPath sets path for url func WithPath(path string) option { return func(url *URL) { url.Path = "/" + strings.TrimPrefix(path, "/") } } -// WithLocation ... +// WithLocation sets location for url func WithLocation(location string) option { return func(url *URL) { url.Location = location } } -// WithToken ... +// WithToken sets token for url func WithToken(token string) option { return func(url *URL) { if len(token) > 0 { @@ -182,7 +185,7 @@ func WithToken(token string) option { } } -// NewURLWithOptions ... +// NewURLWithOptions will create a new url with options func NewURLWithOptions(opts ...option) *URL { url := &URL{} for _, opt := range opts { @@ -195,7 +198,6 @@ func NewURLWithOptions(opts ...option) *URL { // NewURL will create a new url // the urlString should not be empty func NewURL(urlString string, opts ...option) (URL, error) { - var ( err error rawUrlString string @@ -213,7 +215,7 @@ func NewURL(urlString string, opts ...option) (URL, error) { return s, perrors.Errorf("url.QueryUnescape(%s), error{%v}", urlString, err) } - //rawUrlString = "//" + rawUrlString + // rawUrlString = "//" + rawUrlString if strings.Index(rawUrlString, "//") < 0 { t := URL{baseUrl: baseUrl{}} for _, opt := range opts { @@ -249,7 +251,7 @@ func NewURL(urlString string, opts ...option) (URL, error) { return s, nil } -// URLEqual ... +// URLEqual judge @url and @c is equal or not. func (c URL) URLEqual(url URL) bool { c.Ip = "" c.Port = "" @@ -265,17 +267,19 @@ func (c URL) URLEqual(url URL) bool { } else if urlGroup == constant.ANY_VALUE { urlKey = strings.Replace(urlKey, "group=*", "group="+cGroup, 1) } + + // 1. protocol, username, password, ip, port, service name, group, version should be equal if cKey != urlKey { return false } + + // 2. if url contains enabled key, should be true, or * if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE { return false } - //TODO :may need add interface key any value condition - if !isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) { - return false - } - return true + + // TODO :may need add interface key any value condition + return isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) } func isMatchCategory(category1 string, category2 string) bool { @@ -291,38 +295,35 @@ func isMatchCategory(category1 string, category2 string) bool { } func (c URL) String() string { - var buildString string + var buf strings.Builder if len(c.Username) == 0 && len(c.Password) == 0 { - buildString = fmt.Sprintf( + buf.WriteString(fmt.Sprintf( "%s://%s:%s%s?", - c.Protocol, c.Ip, c.Port, c.Path) + c.Protocol, c.Ip, c.Port, c.Path)) } else { - buildString = fmt.Sprintf( + buf.WriteString(fmt.Sprintf( "%s://%s:%s@%s:%s%s?", - c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path) + c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)) } - c.paramsLock.RLock() - buildString += c.params.Encode() - c.paramsLock.RUnlock() - return buildString + buf.WriteString(c.params.Encode()) + return buf.String() } -// Key ... +// Key gets key func (c URL) Key() string { buildString := fmt.Sprintf( "%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, "")) return buildString - //return c.ServiceKey() } -// ServiceKey ... +// ServiceKey gets a unique key of a service. func (c URL) ServiceKey() string { intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if intf == "" { return "" } - buf := &bytes.Buffer{} + var buf strings.Builder group := c.GetParam(constant.GROUP_KEY, "") if group != "" { buf.WriteString(group) @@ -347,7 +348,7 @@ func (c *URL) ColonSeparatedKey() string { if intf == "" { return "" } - buf := &bytes.Buffer{} + var buf strings.Builder buf.WriteString(intf) buf.WriteString(":") version := c.GetParam(constant.VERSION_KEY, "") @@ -362,44 +363,44 @@ func (c *URL) ColonSeparatedKey() string { return buf.String() } -// EncodedServiceKey ... +// EncodedServiceKey encode the service key func (c *URL) EncodedServiceKey() string { serviceKey := c.ServiceKey() return strings.Replace(serviceKey, "/", "*", 1) } -// Service ... +// Service gets service func (c URL) Service() string { service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if service != "" { return service } else if c.SubURL != nil { service = c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) - if service != "" { //if url.path is "" then return suburl's path, special for registry url + if service != "" { // if url.path is "" then return suburl's path, special for registry url return service } } return "" } -// AddParam ... +// AddParam will add the key-value pair +// Not thread-safe +// think twice before using it. func (c *URL) AddParam(key string, value string) { - c.paramsLock.Lock() c.params.Add(key, value) - c.paramsLock.Unlock() } -// SetParam ... +// SetParam will put the key-value pair into url +// it's not thread safe. +// think twice before you want to use this method +// usually it should only be invoked when you want to initialized an url func (c *URL) SetParam(key string, value string) { - c.paramsLock.Lock() c.params.Set(key, value) - c.paramsLock.Unlock() } -// RangeParams ... +// RangeParams will iterate the params +// it's not thread-safe func (c *URL) RangeParams(f func(key, value string) bool) { - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() for k, v := range c.params { if !f(k, v[0]) { break @@ -407,35 +408,31 @@ func (c *URL) RangeParams(f func(key, value string) bool) { } } -// GetParam ... +// GetParam gets value by key func (c URL) GetParam(s string, d string) string { - var r string - c.paramsLock.RLock() - if r = c.params.Get(s); len(r) == 0 { + r := c.params.Get(s) + if len(r) == 0 { r = d } - c.paramsLock.RUnlock() return r } -// GetParams ... +// GetParams gets values func (c URL) GetParams() url.Values { return c.params } -// GetParamAndDecoded ... +// GetParamAndDecoded gets values and decode func (c URL) GetParamAndDecoded(key string) (string, error) { - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, "")) value := string(ruleDec) return value, err } -// GetRawParam ... +// GetRawParam gets raw param func (c URL) GetRawParam(key string) string { switch key { - case "protocol": + case PROTOCOL: return c.Protocol case "username": return c.Username @@ -452,76 +449,61 @@ func (c URL) GetRawParam(key string) string { } } -// GetParamBool ... -func (c URL) GetParamBool(s string, d bool) bool { - - var r bool - var err error - if r, err = strconv.ParseBool(c.GetParam(s, "")); err != nil { +// GetParamBool judge whether @key exists or not +func (c URL) GetParamBool(key string, d bool) bool { + r, err := strconv.ParseBool(c.GetParam(key, "")) + if err != nil { return d } return r } -// GetParamInt ... -func (c URL) GetParamInt(s string, d int64) int64 { - var r int - var err error - - if r, err = strconv.Atoi(c.GetParam(s, "")); r == 0 || err != nil { +// GetParamInt gets int value by @key +func (c URL) GetParamInt(key string, d int64) int64 { + r, err := strconv.Atoi(c.GetParam(key, "")) + if r == 0 || err != nil { return d } return int64(r) } -// GetMethodParamInt ... +// GetMethodParamInt gets int method param func (c URL) GetMethodParamInt(method string, key string, d int64) int64 { - var r int - var err error - c.paramsLock.RLock() - defer c.paramsLock.RUnlock() - if r, err = strconv.Atoi(c.GetParam("methods."+method+"."+key, "")); r == 0 || err != nil { + r, err := strconv.Atoi(c.GetParam("methods."+method+"."+key, "")) + if r == 0 || err != nil { return d } return int64(r) } -// GetMethodParamInt64 ... +// GetMethodParamInt64 gets int64 method param func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { r := c.GetMethodParamInt(method, key, math.MinInt64) if r == math.MinInt64 { return c.GetParamInt(key, d) } - return r } -// GetMethodParam ... +// GetMethodParam gets method param func (c URL) GetMethodParam(method string, key string, d string) string { - var r string - if r = c.GetParam("methods."+method+"."+key, ""); r == "" { + r := c.GetParam("methods."+method+"."+key, "") + if r == "" { r = d } return r } -// GetMethodParamBool ... +// GetMethodParamBool judge whether @method param exists or not func (c URL) GetMethodParamBool(method string, key string, d bool) bool { r := c.GetParamBool("methods."+method+"."+key, d) return r } -// RemoveParams ... -func (c *URL) RemoveParams(set *gxset.HashSet) { - c.paramsLock.Lock() - defer c.paramsLock.Unlock() - for k := range set.Items { - s := k.(string) - delete(c.params, s) - } -} - -// SetParams ... +// SetParams will put all key-value pair into url. +// 1. if there already has same key, the value will be override +// 2. it's not thread safe +// 3. think twice when you want to invoke this method func (c *URL) SetParams(m url.Values) { for k := range m { c.SetParam(k, m.Get(k)) @@ -530,7 +512,6 @@ func (c *URL) SetParams(m url.Values) { // ToMap transfer URL to Map func (c URL) ToMap() map[string]string { - paramsMap := make(map[string]string) c.RangeParams(func(key, value string) bool { @@ -539,7 +520,7 @@ func (c URL) ToMap() map[string]string { }) if c.Protocol != "" { - paramsMap["protocol"] = c.Protocol + paramsMap[PROTOCOL] = c.Protocol } if c.Username != "" { paramsMap["username"] = c.Username @@ -558,7 +539,7 @@ func (c URL) ToMap() map[string]string { paramsMap["port"] = port } if c.Protocol != "" { - paramsMap["protocol"] = c.Protocol + paramsMap[PROTOCOL] = c.Protocol } if c.Path != "" { paramsMap["path"] = c.Path @@ -571,29 +552,35 @@ func (c URL) ToMap() map[string]string { // configuration > reference config >service config // in this function we should merge the reference local url config into the service url from registry. -//TODO configuration merge, in the future , the configuration center's config should merge too. - -// MergeUrl ... +// TODO configuration merge, in the future , the configuration center's config should merge too. + +// MergeUrl will merge those two url +// the result is based on serviceUrl, and the key which si only contained in referenceUrl +// will be added into result. +// for example, if serviceUrl contains params (a1->v1, b1->v2) and referenceUrl contains params(a2->v3, b1 -> v4) +// the params of result will be (a1->v1, b1->v2, a2->v3). +// You should notice that the value of b1 is v2, not v4. +// due to URL is not thread-safe, so this method is not thread-safe func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { mergedUrl := serviceUrl.Clone() - //iterator the referenceUrl if serviceUrl not have the key ,merge in + // iterator the referenceUrl if serviceUrl not have the key ,merge in referenceUrl.RangeParams(func(key, value string) bool { if v := mergedUrl.GetParam(key, ""); len(v) == 0 { mergedUrl.SetParam(key, value) } return true }) - //loadBalance,cluster,retries strategy config + // loadBalance,cluster,retries strategy config methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY}) - //remote timestamp + // remote timestamp if v := serviceUrl.GetParam(constant.TIMESTAMP_KEY, ""); len(v) > 0 { mergedUrl.SetParam(constant.REMOTE_TIMESTAMP_KEY, v) mergedUrl.SetParam(constant.TIMESTAMP_KEY, referenceUrl.GetParam(constant.TIMESTAMP_KEY, "")) } - //finally execute methodConfigMergeFcn + // finally execute methodConfigMergeFcn for _, method := range referenceUrl.Methods { for _, fcn := range methodConfigMergeFcn { fcn("methods." + method) @@ -603,7 +590,7 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { return mergedUrl } -// Clone ... +// Clone will copy the url func (c *URL) Clone() *URL { newUrl := &URL{} copier.Copy(newUrl, c) @@ -615,8 +602,43 @@ func (c *URL) Clone() *URL { return newUrl } +func (c *URL) CloneExceptParams(excludeParams *gxset.HashSet) *URL { + newUrl := &URL{} + copier.Copy(newUrl, c) + newUrl.params = url.Values{} + c.RangeParams(func(key, value string) bool { + if !excludeParams.Contains(key) { + newUrl.SetParam(key, value) + } + return true + }) + return newUrl +} + +// Copy url based on the reserved parameter's keys. +func (c *URL) CloneWithParams(reserveParams []string) *URL { + params := url.Values{} + for _, reserveParam := range reserveParams { + v := c.GetParam(reserveParam, "") + if len(v) != 0 { + params.Set(reserveParam, v) + } + } + + return NewURLWithOptions( + WithProtocol(c.Protocol), + WithUsername(c.Username), + WithPassword(c.Password), + WithIp(c.Ip), + WithPort(c.Port), + WithPath(c.Path), + WithMethods(c.Methods), + WithParams(params), + ) +} + func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) { - var methodConfigMergeFcn = []func(method string){} + methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys)) for _, paramKey := range paramKeys { if v := referenceUrl.GetParam(paramKey, ""); len(v) > 0 { mergedUrl.SetParam(paramKey, v) diff --git a/common/url_test.go b/common/url_test.go index 2372de520e88b0949023e88cec64871736dd6aa0..6845190a7362571ebbd4738bd146c94f6d644253 100644 --- a/common/url_test.go +++ b/common/url_test.go @@ -31,24 +31,30 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +const ( + userName = "username" + password = "password" + loopbackAddress = "127.0.0.1" +) + func TestNewURLWithOptions(t *testing.T) { methods := []string{"Methodone,methodtwo"} params := url.Values{} params.Set("key", "value") u := NewURLWithOptions(WithPath("com.test.Service"), - WithUsername("username"), - WithPassword("password"), + WithUsername(userName), + WithPassword(password), WithProtocol("testprotocol"), - WithIp("127.0.0.1"), + WithIp(loopbackAddress), WithPort("8080"), WithMethods(methods), WithParams(params), WithParamsValue("key2", "value2")) assert.Equal(t, "/com.test.Service", u.Path) - assert.Equal(t, "username", u.Username) - assert.Equal(t, "password", u.Password) + assert.Equal(t, userName, u.Username) + assert.Equal(t, password, u.Password) assert.Equal(t, "testprotocol", u.Protocol) - assert.Equal(t, "127.0.0.1", u.Ip) + assert.Equal(t, loopbackAddress, u.Ip) assert.Equal(t, "8080", u.Port) assert.Equal(t, methods, u.Methods) assert.Equal(t, params, u.params) @@ -65,7 +71,7 @@ func TestURL(t *testing.T) { assert.Equal(t, "/com.ikurento.user.UserProvider", u.Path) assert.Equal(t, "127.0.0.1:20000", u.Location) assert.Equal(t, "dubbo", u.Protocol) - assert.Equal(t, "127.0.0.1", u.Ip) + assert.Equal(t, loopbackAddress, u.Ip) assert.Equal(t, "20000", u.Port) assert.Equal(t, URL{}.Methods, u.Methods) assert.Equal(t, "", u.Username) @@ -92,7 +98,7 @@ func TestURLWithoutSchema(t *testing.T) { assert.Equal(t, "/com.ikurento.user.UserProvider", u.Path) assert.Equal(t, "127.0.0.1:20000", u.Location) assert.Equal(t, "dubbo", u.Protocol) - assert.Equal(t, "127.0.0.1", u.Ip) + assert.Equal(t, loopbackAddress, u.Ip) assert.Equal(t, "20000", u.Port) assert.Equal(t, URL{}.Methods, u.Methods) assert.Equal(t, "", u.Username) @@ -108,7 +114,7 @@ func TestURLWithoutSchema(t *testing.T) { "ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245", u.String()) } -func TestURL_URLEqual(t *testing.T) { +func TestURLEqual(t *testing.T) { u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") assert.NoError(t, err) u2, err := NewURL("dubbo://127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") @@ -118,9 +124,36 @@ func TestURL_URLEqual(t *testing.T) { u3, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") assert.NoError(t, err) assert.False(t, u1.URLEqual(u3)) + + // urlGroupAnyValue's group is * + urlGroupAnyValue, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0") + assert.NoError(t, err) + assert.True(t, u3.URLEqual(urlGroupAnyValue)) + + // test for enabled + urlEnabledEmpty, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0&enabled=") + assert.NoError(t, err) + assert.True(t, u3.URLEqual(urlEnabledEmpty)) + + urlEnabledFalse, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0&enabled=1") + assert.NoError(t, err) + assert.False(t, u3.URLEqual(urlEnabledFalse)) + + urlEnabledTrue, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0&enabled=true") + assert.NoError(t, err) + assert.True(t, u3.URLEqual(urlEnabledTrue)) + + urlEnabledAny, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0&enabled=*") + assert.NoError(t, err) + assert.True(t, u3.URLEqual(urlEnabledAny)) + + // test for category + categoryAny, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=*&version=2.6.0&enabled=*&category=*") + assert.NoError(t, err) + assert.True(t, categoryAny.URLEqual(u3)) } -func TestURL_GetParam(t *testing.T) { +func TestURLGetParam(t *testing.T) { params := url.Values{} params.Set("key", "value") u := URL{baseUrl: baseUrl{params: params}} @@ -132,7 +165,7 @@ func TestURL_GetParam(t *testing.T) { assert.Equal(t, "default", v) } -func TestURL_GetParamInt(t *testing.T) { +func TestURLGetParamInt(t *testing.T) { params := url.Values{} params.Set("key", "3") u := URL{baseUrl: baseUrl{params: params}} @@ -144,7 +177,7 @@ func TestURL_GetParamInt(t *testing.T) { assert.Equal(t, int64(1), v) } -func TestURL_GetParamBool(t *testing.T) { +func TestURLGetParamBool(t *testing.T) { params := url.Values{} params.Set("force", "true") u := URL{baseUrl: baseUrl{params: params}} @@ -156,7 +189,7 @@ func TestURL_GetParamBool(t *testing.T) { assert.Equal(t, false, v) } -func TestURL_GetParamAndDecoded(t *testing.T) { +func TestURLGetParamAndDecoded(t *testing.T) { rule := "host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4" params := url.Values{} params.Set("rule", base64.URLEncoding.EncodeToString([]byte(rule))) @@ -165,20 +198,20 @@ func TestURL_GetParamAndDecoded(t *testing.T) { assert.Equal(t, rule, v) } -func TestURL_GetRawParam(t *testing.T) { +func TestURLGetRawParam(t *testing.T) { u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") u.Username = "test" u.Password = "test" assert.Equal(t, "condition", u.GetRawParam("protocol")) assert.Equal(t, "0.0.0.0", u.GetRawParam("host")) assert.Equal(t, "8080", u.GetRawParam("port")) - assert.Equal(t, "test", u.GetRawParam("username")) - assert.Equal(t, "test", u.GetRawParam("password")) + assert.Equal(t, "test", u.GetRawParam(userName)) + assert.Equal(t, "test", u.GetRawParam(password)) assert.Equal(t, "/com.foo.BarService", u.GetRawParam("path")) assert.Equal(t, "fastjson", u.GetRawParam("serialization")) } -func TestURL_ToMap(t *testing.T) { +func TestURLToMap(t *testing.T) { u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") u.Username = "test" u.Password = "test" @@ -188,13 +221,13 @@ func TestURL_ToMap(t *testing.T) { assert.Equal(t, "condition", m["protocol"]) assert.Equal(t, "0.0.0.0", m["host"]) assert.Equal(t, "8080", m["port"]) - assert.Equal(t, "test", m["username"]) - assert.Equal(t, "test", m["password"]) + assert.Equal(t, "test", m[userName]) + assert.Equal(t, "test", m[password]) assert.Equal(t, "/com.foo.BarService", m["path"]) assert.Equal(t, "fastjson", m["serialization"]) } -func TestURL_GetMethodParamInt(t *testing.T) { +func TestURLGetMethodParamInt(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.timeout", "3") u := URL{baseUrl: baseUrl{params: params}} @@ -206,7 +239,7 @@ func TestURL_GetMethodParamInt(t *testing.T) { assert.Equal(t, int64(1), v) } -func TestURL_GetMethodParam(t *testing.T) { +func TestURLGetMethodParam(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.timeout", "3s") u := URL{baseUrl: baseUrl{params: params}} @@ -218,7 +251,7 @@ func TestURL_GetMethodParam(t *testing.T) { assert.Equal(t, "1s", v) } -func TestURL_GetMethodParamBool(t *testing.T) { +func TestURLGetMethodParamBool(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.async", "true") u := URL{baseUrl: baseUrl{params: params}} @@ -252,7 +285,7 @@ func TestMergeUrl(t *testing.T) { assert.Equal(t, "2", mergedUrl.GetParam(constant.METHOD_KEYS+".testMethod."+constant.RETRIES_KEY, "")) } -func TestURL_SetParams(t *testing.T) { +func TestURLSetParams(t *testing.T) { u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") assert.NoError(t, err) params := url.Values{} diff --git a/common/yaml/yaml.go b/common/yaml/yaml.go index 93ebb166144510236aff27a67422a6377ccb5c9f..5edda1b3c7751e8171528d121148b6c3c60fe128 100644 --- a/common/yaml/yaml.go +++ b/common/yaml/yaml.go @@ -40,7 +40,7 @@ func LoadYMLConfig(confProFile string) ([]byte, error) { return ioutil.ReadFile(confProFile) } -// unmarshalYMLConfig Load yml config byte from file , then unmarshal to object +// unmarshalYMLConfig Load yml config byte from file, then unmarshal to object func UnmarshalYMLConfig(confProFile string, out interface{}) ([]byte, error) { confFileStream, err := LoadYMLConfig(confProFile) if err != nil { diff --git a/common/yaml/yaml_test.go b/common/yaml/yaml_test.go index c8b8258a68951a1437ac2e617c13ee5af4b3a5ee..5a271a25823f576cb9cdb10c657520bbf5666017 100644 --- a/common/yaml/yaml_test.go +++ b/common/yaml/yaml_test.go @@ -38,7 +38,7 @@ func TestUnmarshalYMLConfig(t *testing.T) { assert.Equal(t, "childStrTest", c.ChildConfig.StrTest) } -func TestUnmarshalYMLConfig_Error(t *testing.T) { +func TestUnmarshalYMLConfigError(t *testing.T) { c := &Config{} _, err := UnmarshalYMLConfig("./testdata/config", c) assert.Error(t, err) diff --git a/config/application_config.go b/config/application_config.go index 33b47c81dd0da9959984cd1f53648167863cb713..16e841792c7bffaa6f847d73c336c6a43431bc49 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -25,9 +25,9 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -// ApplicationConfig ... +// ApplicationConfig is a configuration for current application, whether the application is a provider or a consumer type ApplicationConfig struct { - Organization string `yaml:"organization" json:"organization,omitempty" property:"organization"` + Organization string `yaml:"organization" json:"organization,omitempty" property:"organization"` Name string `yaml:"name" json:"name,omitempty" property:"name"` Module string `yaml:"module" json:"module,omitempty" property:"module"` Version string `yaml:"version" json:"version,omitempty" property:"version"` @@ -36,22 +36,12 @@ type ApplicationConfig struct { MetadataType string `default:"local" yaml:"metadataType" json:"metadataType,omitempty" property:"metadataType"` //field for metadata report } -// Prefix ... +// nolint func (*ApplicationConfig) Prefix() string { return constant.DUBBO + ".application." } -// Id ... -func (c *ApplicationConfig) Id() string { - return "" -} - -// SetId ... -func (c *ApplicationConfig) SetId(id string) { - -} - -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ApplicationConfig by @unmarshal function func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err diff --git a/config/base_config.go b/config/base_config.go index 93c0ce6a6692193e7ea7b1b9f2f74e9eaed0c858..fd749a934b8344a5ed00343f1b18f10bc41fd820 100644 --- a/config/base_config.go +++ b/config/base_config.go @@ -69,7 +69,6 @@ func (c *BaseConfig) startConfigCenter() error { } func (c *BaseConfig) prepareEnvironment() error { - factory := extension.GetConfigCenterFactory(c.ConfigCenterConfig.Protocol) dynamicConfig, err := factory.GetDynamicConfiguration(c.configCenterUrl) config.GetEnvInstance().SetDynamicConfiguration(dynamicConfig) @@ -125,20 +124,20 @@ func getKeyPrefix(val reflect.Value) []string { var ( prefix string ) - + configPrefixMethod := "Prefix" if val.CanAddr() { - prefix = val.Addr().MethodByName("Prefix").Call(nil)[0].String() + prefix = val.Addr().MethodByName(configPrefixMethod).Call(nil)[0].String() } else { - prefix = val.MethodByName("Prefix").Call(nil)[0].String() + prefix = val.MethodByName(configPrefixMethod).Call(nil)[0].String() } - var retPrefixs []string + var retPrefixes []string for _, pfx := range strings.Split(prefix, "|") { - retPrefixs = append(retPrefixs, pfx) + retPrefixes = append(retPrefixes, pfx) } - return retPrefixs + return retPrefixes } @@ -165,13 +164,13 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC idStr string ) - prefixs := getKeyPrefix(val) + prefixes := getKeyPrefix(val) if id.Kind() == reflect.String { idStr = id.Interface().(string) } - for _, pfx := range prefixs { + for _, pfx := range prefixes { if len(pfx) > 0 { if len(idStr) > 0 { @@ -191,18 +190,20 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC } if ok { + errMsg := func(structName string, fieldName string, errorDetails error) { + logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", + structName, fieldName, errorDetails) + } switch f.Kind() { case reflect.Int64: x, err := strconv.Atoi(value) if err != nil { - logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", - val.Type().Name(), val.Type().Field(i).Name, err) + errMsg(val.Type().Name(), val.Type().Field(i).Name, err) } else { if !f.OverflowInt(int64(x)) { f.SetInt(int64(x)) } else { - logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", - val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the int64 value {%v} from config center is overflow", int64(x))) + errMsg(val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the int64 value {%v} from config center is overflow", int64(x))) } } case reflect.String: @@ -210,21 +211,18 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC case reflect.Bool: x, err := strconv.ParseBool(value) if err != nil { - logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", - val.Type().Name(), val.Type().Field(i).Name, err) + errMsg(val.Type().Name(), val.Type().Field(i).Name, err) } f.SetBool(x) case reflect.Float64: x, err := strconv.ParseFloat(value, 64) if err != nil { - logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", - val.Type().Name(), val.Type().Field(i).Name, err) + errMsg(val.Type().Name(), val.Type().Field(i).Name, err) } else { if !f.OverflowFloat(x) { f.SetFloat(x) } else { - logger.Errorf("Dynamic change the configuration in struct {%v} field {%v} error ,error message is {%v}", - val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the float64 value {%v} from config center is overflow", x)) + errMsg(val.Type().Name(), val.Type().Field(i).Name, perrors.Errorf("the float64 value {%v} from config center is overflow", x)) } } default: @@ -323,45 +321,46 @@ func (c *BaseConfig) freshInternalConfig(config *config.InmemoryConfiguration) { setFieldValue(val, reflect.Value{}, config) } -// SetFatherConfig ... +// SetFatherConfig sets father config by @fatherConfig func (c *BaseConfig) SetFatherConfig(fatherConfig interface{}) { c.fatherConfig = fatherConfig } func initializeStruct(t reflect.Type, v reflect.Value) { - if v.Kind() == reflect.Struct { - for i := 0; i < v.NumField(); i++ { - f := v.Field(i) - ft := t.Field(i) - - if ft.Tag.Get("property") != "" { - switch ft.Type.Kind() { - case reflect.Map: - if f.IsNil() { - f.Set(reflect.MakeMap(ft.Type)) - } - case reflect.Slice: - if f.IsNil() { - f.Set(reflect.MakeSlice(ft.Type, 0, 0)) - } - case reflect.Chan: - if f.IsNil() { - f.Set(reflect.MakeChan(ft.Type, 0)) - } - case reflect.Struct: - if f.IsNil() { - initializeStruct(ft.Type, f) - } - case reflect.Ptr: - if f.IsNil() { - fv := reflect.New(ft.Type.Elem()) - initializeStruct(ft.Type.Elem(), fv.Elem()) - f.Set(fv) - } - default: - } - } + if v.Kind() != reflect.Struct { + return + } + for i := 0; i < v.NumField(); i++ { + f := v.Field(i) + ft := t.Field(i) + if ft.Tag.Get("property") == "" { + continue + } + switch ft.Type.Kind() { + case reflect.Map: + if f.IsNil() { + f.Set(reflect.MakeMap(ft.Type)) + } + case reflect.Slice: + if f.IsNil() { + f.Set(reflect.MakeSlice(ft.Type, 0, 0)) + } + case reflect.Chan: + if f.IsNil() { + f.Set(reflect.MakeChan(ft.Type, 0)) + } + case reflect.Struct: + if f.IsNil() { + initializeStruct(ft.Type, f) + } + case reflect.Ptr: + if f.IsNil() { + fv := reflect.New(ft.Type.Elem()) + initializeStruct(ft.Type.Elem(), fv.Elem()) + f.Set(fv) + } + default: } } } diff --git a/config/base_config_test.go b/config/base_config_test.go index d16b2420922ece60ef2135729cd47d5aa73a3760..ca2875fb95e9bdabcc430edc15bd4a56c440bc45 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -21,9 +21,11 @@ import ( "reflect" "testing" ) + import ( "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/extension" @@ -31,89 +33,93 @@ import ( _ "github.com/apache/dubbo-go/config_center/apollo" ) -func Test_refresh(t *testing.T) { - c := &BaseConfig{} - mockMap := map[string]string{} - mockMap["dubbo.registries.shanghai_reg1.protocol"] = "mock100" - mockMap["dubbo.reference.com.MockService.MockService.retries"] = "10" - mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10" - mockMap["dubbo.consumer.check"] = "false" - mockMap["dubbo.application.name"] = "dubbo" - mockMap["dubbo.shutdown.timeout"] = "12s" +func getMockMap() map[string]string { + baseMockMap := map[string]string{ + "dubbo.registries.shanghai_reg1.protocol": "mock100", + "dubbo.reference.com.MockService.MockService.retries": "10", + "dubbo.com.MockService.MockService.GetUser.retries": "10", + "dubbo.consumer.check": "false", + "dubbo.application.name": "dubbo", + } + return baseMockMap +} - config.GetEnvInstance().UpdateExternalConfigMap(mockMap) +var baseAppConfig = &ApplicationConfig{ + Organization: "dubbo_org", + Name: "dubbo", + Module: "module", + Version: "2.6.0", + Owner: "dubbo", + Environment: "test", +} - father := &ConsumerConfig{ - Check: &[]bool{true}[0], - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, - "shanghai_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "shanghai_idc", - Address: "127.0.0.2:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg1": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.3:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.4:2181", - Username: "user1", - Password: "pwd1", +var baseRegistries = map[string]*RegistryConfig{ + "shanghai_reg2": { + Protocol: "mock", + TimeoutStr: "2s", + Group: "shanghai_idc", + Address: "127.0.0.2:2181", + Username: "user1", + Password: "pwd1", + }, + "hangzhou_reg1": { + Protocol: "mock", + TimeoutStr: "2s", + Group: "hangzhou_idc", + Address: "127.0.0.3:2181", + Username: "user1", + Password: "pwd1", + }, + "hangzhou_reg2": { + Protocol: "mock", + TimeoutStr: "2s", + Group: "hangzhou_idc", + Address: "127.0.0.4:2181", + Username: "user1", + Password: "pwd1", + }, +} + +var baseMockRef = map[string]*ReferenceConfig{ + "MockService": { + InterfaceName: "com.MockService", + Protocol: "mock", + Cluster: "failover", + Loadbalance: "random", + Retries: "3", + Group: "huadong_idc", + Version: "1.0.0", + Methods: []*MethodConfig{ + { + InterfaceId: "MockService", + InterfaceName: "com.MockService", + Name: "GetUser", + Retries: "2", + Loadbalance: "random", }, - }, - References: map[string]*ReferenceConfig{ - "MockService": { + { + InterfaceId: "MockService", InterfaceName: "com.MockService", - Protocol: "mock", - Cluster: "failover", + Name: "GetUser1", + Retries: "2", Loadbalance: "random", - Retries: "3", - Group: "huadong_idc", - Version: "1.0.0", - Methods: []*MethodConfig{ - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser", - Retries: "2", - Loadbalance: "random", - }, - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser1", - Retries: "2", - Loadbalance: "random", - }, - }, }, }, + }, +} + +func TestRefresh(t *testing.T) { + c := &BaseConfig{} + mockMap := getMockMap() + mockMap["dubbo.shutdown.timeout"] = "12s" + + config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + + father := &ConsumerConfig{ + Check: &[]bool{true}[0], + ApplicationConfig: baseAppConfig, + Registries: baseRegistries, + References: baseMockRef, ShutdownConfig: &ShutdownConfig{ Timeout: "12s", StepTimeout: "2s", @@ -133,90 +139,19 @@ func Test_refresh(t *testing.T) { assert.Equal(t, "dubbo", father.ApplicationConfig.Name) } -func Test_appExternal_refresh(t *testing.T) { +func TestAppExternalRefresh(t *testing.T) { c := &BaseConfig{} - mockMap := map[string]string{} - mockMap["dubbo.registries.shanghai_reg1.protocol"] = "mock100" - mockMap["dubbo.reference.com.MockService.MockService.retries"] = "10" + mockMap := getMockMap() mockMap["dubbo.reference.com.MockService.retries"] = "5" - mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10" - mockMap["dubbo.consumer.check"] = "false" - mockMap["dubbo.application.name"] = "dubbo" config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) mockMap["dubbo.consumer.check"] = "true" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) father := &ConsumerConfig{ - Check: &[]bool{true}[0], - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, - "shanghai_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "shanghai_idc", - Address: "127.0.0.2:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg1": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.3:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.4:2181", - Username: "user1", - Password: "pwd1", - }, - }, - References: map[string]*ReferenceConfig{ - "MockService": { - InterfaceName: "com.MockService", - Protocol: "mock", - Cluster: "failover", - Loadbalance: "random", - Retries: "3", - Group: "huadong_idc", - Version: "1.0.0", - Methods: []*MethodConfig{ - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser", - Retries: "2", - Loadbalance: "random", - }, - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser1", - Retries: "2", - Loadbalance: "random", - }, - }, - }, - }, + Check: &[]bool{true}[0], + ApplicationConfig: baseAppConfig, + Registries: baseRegistries, + References: baseMockRef, } c.SetFatherConfig(father) @@ -229,89 +164,20 @@ func Test_appExternal_refresh(t *testing.T) { assert.Equal(t, "dubbo", father.ApplicationConfig.Name) } -func Test_appExternalWithoutId_refresh(t *testing.T) { +func TestAppExternalWithoutIDRefresh(t *testing.T) { c := &BaseConfig{} - mockMap := map[string]string{} - mockMap["dubbo.registries.shanghai_reg1.protocol"] = "mock100" + mockMap := getMockMap() + delete(mockMap, "dubbo.reference.com.MockService.MockService.retries") mockMap["dubbo.reference.com.MockService.retries"] = "10" - mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10" - mockMap["dubbo.consumer.check"] = "false" - mockMap["dubbo.application.name"] = "dubbo" config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) mockMap["dubbo.consumer.check"] = "true" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) father := &ConsumerConfig{ - Check: &[]bool{true}[0], - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, - "shanghai_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "shanghai_idc", - Address: "127.0.0.2:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg1": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.3:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.4:2181", - Username: "user1", - Password: "pwd1", - }, - }, - References: map[string]*ReferenceConfig{ - "MockService": { - InterfaceName: "com.MockService", - Protocol: "mock", - Cluster: "failover", - Loadbalance: "random", - Retries: "3", - Group: "huadong_idc", - Version: "1.0.0", - Methods: []*MethodConfig{ - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser", - Retries: "3", - Loadbalance: "random", - }, - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser1", - Retries: "2", - Loadbalance: "random", - }, - }, - }, - }, + Check: &[]bool{true}[0], + ApplicationConfig: baseAppConfig, + Registries: baseRegistries, + References: baseMockRef, } c.SetFatherConfig(father) @@ -324,7 +190,7 @@ func Test_appExternalWithoutId_refresh(t *testing.T) { assert.Equal(t, "dubbo", father.ApplicationConfig.Name) } -func Test_refresh_singleRegistry(t *testing.T) { +func TestRefreshSingleRegistry(t *testing.T) { c := &BaseConfig{} mockMap := map[string]string{} mockMap["dubbo.registry.address"] = "mock100://127.0.0.1:2181" @@ -336,43 +202,11 @@ func Test_refresh_singleRegistry(t *testing.T) { config.GetEnvInstance().UpdateExternalConfigMap(mockMap) father := &ConsumerConfig{ - Check: &[]bool{true}[0], - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registries: map[string]*RegistryConfig{}, - Registry: &RegistryConfig{}, - References: map[string]*ReferenceConfig{ - "MockService": { - InterfaceName: "com.MockService", - Protocol: "mock", - Cluster: "failover", - Loadbalance: "random", - Retries: "3", - Group: "huadong_idc", - Version: "1.0.0", - Methods: []*MethodConfig{ - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser", - Retries: "2", - Loadbalance: "random", - }, - { - InterfaceId: "MockService", - InterfaceName: "com.MockService", - Name: "GetUser1", - Retries: "2", - Loadbalance: "random", - }, - }, - }, - }, + Check: &[]bool{true}[0], + ApplicationConfig: baseAppConfig, + Registries: map[string]*RegistryConfig{}, + Registry: &RegistryConfig{}, + References: baseMockRef, } c.SetFatherConfig(father) @@ -385,14 +219,11 @@ func Test_refresh_singleRegistry(t *testing.T) { assert.Equal(t, "dubbo", father.ApplicationConfig.Name) } -func Test_refreshProvider(t *testing.T) { +func TestRefreshProvider(t *testing.T) { c := &BaseConfig{} - mockMap := map[string]string{} - mockMap["dubbo.registries.shanghai_reg1.protocol"] = "mock100" + mockMap := getMockMap() + delete(mockMap, "dubbo.reference.com.MockService.MockService.retries") mockMap["dubbo.service.com.MockService.MockService.retries"] = "10" - mockMap["dubbo.com.MockService.MockService.GetUser.retries"] = "10" - mockMap["dubbo.consumer.check"] = "false" - mockMap["dubbo.application.name"] = "dubbo" mockMap["dubbo.protocols.jsonrpc1.name"] = "jsonrpc" mockMap["dubbo.protocols.jsonrpc1.ip"] = "127.0.0.1" mockMap["dubbo.protocols.jsonrpc1.port"] = "20001" @@ -400,48 +231,8 @@ func Test_refreshProvider(t *testing.T) { config.GetEnvInstance().UpdateExternalConfigMap(mockMap) father := &ProviderConfig{ - ApplicationConfig: &ApplicationConfig{ - Organization: "dubbo_org", - Name: "dubbo", - Module: "module", - Version: "2.6.0", - Owner: "dubbo", - Environment: "test"}, - Registries: map[string]*RegistryConfig{ - //"shanghai_reg1": { - // id: "shanghai_reg1", - // Protocol: "mock", - // TimeoutStr: "2s", - // Group: "shanghai_idc", - // Address: "127.0.0.1:2181", - // Username: "user1", - // Password: "pwd1", - //}, - "shanghai_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "shanghai_idc", - Address: "127.0.0.2:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg1": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.3:2181", - Username: "user1", - Password: "pwd1", - }, - "hangzhou_reg2": { - Protocol: "mock", - TimeoutStr: "2s", - Group: "hangzhou_idc", - Address: "127.0.0.4:2181", - Username: "user1", - Password: "pwd1", - }, - }, + ApplicationConfig: baseAppConfig, + Registries: baseRegistries, Services: map[string]*ServiceConfig{ "MockService": { InterfaceName: "com.MockService", @@ -459,7 +250,8 @@ func Test_refreshProvider(t *testing.T) { Retries: "2", Loadbalance: "random", }, - {InterfaceId: "MockService", + { + InterfaceId: "MockService", InterfaceName: "com.MockService", Name: "GetUser1", Retries: "2", @@ -480,8 +272,7 @@ func Test_refreshProvider(t *testing.T) { assert.Equal(t, "20001", father.Protocols["jsonrpc1"].Port) } -func Test_startConfigCenter(t *testing.T) { - +func TestStartConfigCenter(t *testing.T) { extension.SetConfigCenterFactory("mock", func() config_center.DynamicConfigurationFactory { return &config_center.MockDynamicConfigurationFactory{} }) @@ -498,22 +289,22 @@ func Test_startConfigCenter(t *testing.T) { assert.Equal(t, "ikurento.com", v) } -func Test_initializeStruct(t *testing.T) { - consumerConfig := &ConsumerConfig{} +func TestInitializeStruct(t *testing.T) { + testConsumerConfig := &ConsumerConfig{} tp := reflect.TypeOf(ConsumerConfig{}) v := reflect.New(tp) initializeStruct(tp, v.Elem()) - fmt.Println(reflect.ValueOf(consumerConfig).Elem().Type().String()) + fmt.Println(reflect.ValueOf(testConsumerConfig).Elem().Type().String()) fmt.Println(v.Elem().Type().String()) - reflect.ValueOf(consumerConfig).Elem().Set(v.Elem()) + reflect.ValueOf(testConsumerConfig).Elem().Set(v.Elem()) assert.Condition(t, func() (success bool) { - return consumerConfig.ApplicationConfig != nil + return testConsumerConfig.ApplicationConfig != nil }) assert.Condition(t, func() (success bool) { - return consumerConfig.Registries != nil + return testConsumerConfig.Registries != nil }) assert.Condition(t, func() (success bool) { - return consumerConfig.References != nil + return testConsumerConfig.References != nil }) } diff --git a/config/config_center_config.go b/config/config_center_config.go index 40b9b6517186a8a4f7956db3d23f0a1cdfbdc8cb..c9133dc26df0b05e3bb61df0f612d0e2914e98bb 100644 --- a/config/config_center_config.go +++ b/config/config_center_config.go @@ -31,7 +31,13 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -// ConfigCenterConfig ... +// ConfigCenterConfig is configuration for config center +// +// ConfigCenter also introduced concepts of namespace and group to better manage Key-Value pairs by group, +// those configs are already built-in in many professional third-party configuration centers. +// In most cases, namespace is used to isolate different tenants, while group is used to divide the key set from one tenant into groups. +// +// ConfigCenter has currently supported Zookeeper, Nacos, Etcd, Consul, Apollo type ConfigCenterConfig struct { context context.Context Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty"` @@ -40,6 +46,7 @@ type ConfigCenterConfig struct { Group string `default:"dubbo" yaml:"group" json:"group,omitempty"` Username string `yaml:"username" json:"username,omitempty"` Password string `yaml:"password" json:"password,omitempty"` + LogDir string `yaml:"log_dir" json:"log_dir,omitempty"` ConfigFile string `default:"dubbo.properties" yaml:"config_file" json:"config_file,omitempty"` Namespace string `default:"dubbo" yaml:"namespace" json:"namespace,omitempty"` AppConfigFile string `default:"dubbo.properties" yaml:"app_config_file" json:"app_config_file,omitempty"` @@ -48,7 +55,7 @@ type ConfigCenterConfig struct { timeout time.Duration } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ConfigCenterConfig by @unmarshal function func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -60,12 +67,13 @@ func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) er return nil } -// GetUrlMap ... +// GetUrlMap gets url map from ConfigCenterConfig func (c *ConfigCenterConfig) GetUrlMap() url.Values { urlMap := url.Values{} urlMap.Set(constant.CONFIG_NAMESPACE_KEY, c.Namespace) urlMap.Set(constant.CONFIG_GROUP_KEY, c.Group) urlMap.Set(constant.CONFIG_CLUSTER_KEY, c.Cluster) urlMap.Set(constant.CONFIG_APP_ID_KEY, c.AppId) + urlMap.Set(constant.CONFIG_LOG_DIR_KEY, c.LogDir) return urlMap } diff --git a/config/config_loader.go b/config/config_loader.go index 61cb49457b7a2435cb22f02a9df0a02a71ae68cb..f8f83533d4796cc6df3e2d4c2d77f42be56e5933 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -81,128 +81,142 @@ func checkApplicationName(config *ApplicationConfig) { } } -// Load Dubbo Init -func Load() { - - // init router - if confRouterFile != "" { - if errPro := RouterInit(confRouterFile); errPro != nil { - log.Printf("[routerConfig init] %#v", errPro) - } - } - - // reference config +func loadConsumerConfig() { if consumerConfig == nil { logger.Warnf("consumerConfig is nil!") - } else { - // init other consumer config - conConfigType := consumerConfig.ConfigType - for key, value := range extension.GetDefaultConfigReader() { - if conConfigType == nil { - if v, ok := conConfigType[key]; ok { - value = v - } - } - if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil { - logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value) + return + } + // init other consumer config + conConfigType := consumerConfig.ConfigType + for key, value := range extension.GetDefaultConfigReader() { + if conConfigType != nil { + if v, ok := conConfigType[key]; ok { + value = v } } + if err := extension.GetConfigReaders(value).ReadConsumerConfig(consumerConfig.fileStream); err != nil { + logger.Errorf("ReadConsumerConfig error: %#v for %s", perrors.WithStack(err), value) + } + } - metricConfig = consumerConfig.MetricConfig - applicationConfig = consumerConfig.ApplicationConfig + metricConfig = consumerConfig.MetricConfig + applicationConfig = consumerConfig.ApplicationConfig - checkApplicationName(consumerConfig.ApplicationConfig) - if err := configCenterRefreshConsumer(); err != nil { - logger.Errorf("[consumer config center refresh] %#v", err) + checkApplicationName(consumerConfig.ApplicationConfig) + if err := configCenterRefreshConsumer(); err != nil { + logger.Errorf("[consumer config center refresh] %#v", err) + } + checkRegistries(consumerConfig.Registries, consumerConfig.Registry) + for key, ref := range consumerConfig.References { + if ref.Generic { + genericService := NewGenericService(key) + SetConsumerService(genericService) } - checkRegistries(consumerConfig.Registries, consumerConfig.Registry) - for key, ref := range consumerConfig.References { - if ref.Generic { - genericService := NewGenericService(key) - SetConsumerService(genericService) - } - rpcService := GetConsumerService(key) - if rpcService == nil { - logger.Warnf("%s does not exist!", key) - continue - } - ref.id = key - ref.Refer(rpcService) - ref.Implement(rpcService) + rpcService := GetConsumerService(key) + if rpcService == nil { + logger.Warnf("%s does not exist!", key) + continue } + ref.id = key + ref.Refer(rpcService) + ref.Implement(rpcService) + } - //wait for invoker is available, if wait over default 3s, then panic - var count int - checkok := true - for { - for _, refconfig := range consumerConfig.References { - if (refconfig.Check != nil && *refconfig.Check) || - (refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) || - (refconfig.Check == nil && consumerConfig.Check == nil) { //default to true - - if refconfig.invoker != nil && - !refconfig.invoker.IsAvailable() { - checkok = false - count++ - if count > maxWait { - errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) - logger.Error(errMsg) - panic(errMsg) - } - time.Sleep(time.Second * 1) - break - } - if refconfig.invoker == nil { - logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName) + //wait for invoker is available, if wait over default 3s, then panic + var count int + checkok := true + for { + for _, refconfig := range consumerConfig.References { + if (refconfig.Check != nil && *refconfig.Check) || + (refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) || + (refconfig.Check == nil && consumerConfig.Check == nil) { //default to true + + if refconfig.invoker != nil && + !refconfig.invoker.IsAvailable() { + checkok = false + count++ + if count > maxWait { + errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) + logger.Error(errMsg) + panic(errMsg) } + time.Sleep(time.Second * 1) + break + } + if refconfig.invoker == nil { + logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName) } } - if checkok { - break - } - checkok = true } + if checkok { + break + } + checkok = true } +} - // service config +func loadProviderConfig() { if providerConfig == nil { logger.Warnf("providerConfig is nil!") - } else { - // init other provider config - proConfigType := providerConfig.ConfigType - for key, value := range extension.GetDefaultConfigReader() { - if proConfigType != nil { - if v, ok := proConfigType[key]; ok { - value = v - } - } - if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil { - logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value) + return + } + + // init other provider config + proConfigType := providerConfig.ConfigType + for key, value := range extension.GetDefaultConfigReader() { + if proConfigType != nil { + if v, ok := proConfigType[key]; ok { + value = v } } + if err := extension.GetConfigReaders(value).ReadProviderConfig(providerConfig.fileStream); err != nil { + logger.Errorf("ReadProviderConfig error: %#v for %s", perrors.WithStack(err), value) + } + } - // so, you should know that the consumer's config will be override - metricConfig = providerConfig.MetricConfig - applicationConfig = providerConfig.ApplicationConfig + // so, you should know that the consumer's config will be override + metricConfig = providerConfig.MetricConfig + applicationConfig = providerConfig.ApplicationConfig - checkApplicationName(providerConfig.ApplicationConfig) - if err := configCenterRefreshProvider(); err != nil { - logger.Errorf("[provider config center refresh] %#v", err) + checkApplicationName(providerConfig.ApplicationConfig) + if err := configCenterRefreshProvider(); err != nil { + logger.Errorf("[provider config center refresh] %#v", err) + } + checkRegistries(providerConfig.Registries, providerConfig.Registry) + for key, svs := range providerConfig.Services { + rpcService := GetProviderService(key) + if rpcService == nil { + logger.Warnf("%s does not exist!", key) + continue } - checkRegistries(providerConfig.Registries, providerConfig.Registry) - for key, svs := range providerConfig.Services { - rpcService := GetProviderService(key) - if rpcService == nil { - logger.Warnf("%s does not exist!", key) - continue - } - svs.id = key - svs.Implement(rpcService) - if err := svs.Export(); err != nil { - panic(fmt.Sprintf("service %s export failed! err: %#v", key, err)) - } + svs.id = key + svs.Implement(rpcService) + if err := svs.Export(); err != nil { + panic(fmt.Sprintf("service %s export failed! err: %#v", key, err)) } } +} + +func initRouter() { + if confRouterFile != "" { + if err := RouterInit(confRouterFile); err != nil { + log.Printf("[routerConfig init] %#v", err) + } + } +} + +// Load Dubbo Init +func Load() { + + // init router + initRouter() + + // reference config + loadConsumerConfig() + + // service config + loadProviderConfig() + // init the shutdown callback GracefulShutdownInit() } diff --git a/config/config_loader_test.go b/config/config_loader_test.go index 6368fcbd2c7bc675231e7b7835750f26743708af..a21a4998aae9eb6e0bd0632c7248cc39e7bbd9fa 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -36,10 +36,13 @@ import ( "github.com/apache/dubbo-go/config_center" ) +const mockConsumerConfigPath = "./testdata/consumer_config.yml" +const mockProviderConfigPath = "./testdata/provider_config.yml" + func TestConfigLoader(t *testing.T) { - conPath, err := filepath.Abs("./testdata/consumer_config.yml") + conPath, err := filepath.Abs(mockConsumerConfigPath) assert.NoError(t, err) - proPath, err := filepath.Abs("./testdata/provider_config.yml") + proPath, err := filepath.Abs(mockProviderConfigPath) assert.NoError(t, err) assert.Nil(t, consumerConfig) @@ -152,7 +155,7 @@ func TestConfigLoaderWithConfigCenter(t *testing.T) { conPath, err := filepath.Abs("./testdata/consumer_config_with_configcenter.yml") assert.NoError(t, err) - proPath, err := filepath.Abs("./testdata/provider_config.yml") + proPath, err := filepath.Abs(mockProviderConfigPath) assert.NoError(t, err) assert.Nil(t, consumerConfig) @@ -205,7 +208,7 @@ func TestConfigLoaderWithConfigCenterSingleRegistry(t *testing.T) { conPath, err := filepath.Abs("./testdata/consumer_config_with_configcenter.yml") assert.NoError(t, err) - proPath, err := filepath.Abs("./testdata/provider_config.yml") + proPath, err := filepath.Abs(mockProviderConfigPath) assert.NoError(t, err) assert.Nil(t, consumerConfig) diff --git a/config/config_utils.go b/config/config_utils.go index 6bc574a546ebad548aaa15ce7dc9bcf68b95c3a1..5759ff34f6e5673c4a6de8af3b4e1a34a2e7af4c 100644 --- a/config/config_utils.go +++ b/config/config_utils.go @@ -18,6 +18,7 @@ package config import ( + "fmt" "regexp" "strings" ) @@ -30,50 +31,35 @@ func mergeValue(str1, str2, def string) string { if str1 == "" && str2 == "" { return def } - str := "," + strings.Trim(str1, ",") - if str1 == "" { - str = "," + strings.Trim(str2, ",") - } else if str2 != "" { - str = str + "," + strings.Trim(str2, ",") - } + s1 := strings.Split(str1, ",") + s2 := strings.Split(str2, ",") + str := "," + strings.Join(append(s1, s2...), ",") defKey := strings.Contains(str, ","+constant.DEFAULT_KEY) if !defKey { str = "," + constant.DEFAULT_KEY + str } str = strings.TrimPrefix(strings.Replace(str, ","+constant.DEFAULT_KEY, ","+def, -1), ",") + return removeMinus(strings.Split(str, ",")) +} - strArr := strings.Split(str, ",") - strMap := make(map[string][]int) - for k, v := range strArr { - add := true +func removeMinus(strArr []string) string { + if len(strArr) == 0 { + return "" + } + var normalStr string + var minusStrArr []string + for _, v := range strArr { if strings.HasPrefix(v, "-") { - v = v[1:] - add = false - } - if _, ok := strMap[v]; !ok { - if add { - strMap[v] = []int{1, k} - } + minusStrArr = append(minusStrArr, v[1:]) } else { - if add { - strMap[v][0] += 1 - strMap[v] = append(strMap[v], k) - } else { - strMap[v][0] -= 1 - strMap[v] = strMap[v][:len(strMap[v])-1] - } + normalStr += fmt.Sprintf(",%s", v) } } - strArr = make([]string, len(strArr)) - for key, value := range strMap { - if value[0] == 0 { - continue - } - for i := 1; i < len(value); i++ { - strArr[value[i]] = key - } + normalStr = strings.Trim(normalStr, ",") + for _, v := range minusStrArr { + normalStr = strings.Replace(normalStr, v, "", 1) } reg := regexp.MustCompile("[,]+") - str = reg.ReplaceAllString(strings.Join(strArr, ","), ",") - return strings.Trim(str, ",") + normalStr = reg.ReplaceAllString(strings.Trim(normalStr, ","), ",") + return normalStr } diff --git a/config/config_utils_test.go b/config/config_utils_test.go index 5170b90c83a0f31bbbe1b5de5bca9b8dc5869ac6..81fc3a3721d5915ec3652e03f80ec203e170d1fa 100644 --- a/config/config_utils_test.go +++ b/config/config_utils_test.go @@ -41,3 +41,23 @@ func TestMergeValue(t *testing.T) { str = mergeValue("", "default,-b,e,f", "a,b") assert.Equal(t, "a,e,f", str) } + +func TestRemoveMinus(t *testing.T) { + strList := removeMinus([]string{}) + assert.Equal(t, strList, "") + + strList = removeMinus([]string{"a", "b", "c", "d", "-a"}) + assert.Equal(t, strList, "b,c,d") + + strList = removeMinus([]string{"a", "b", "c", "d", "-a", "-b"}) + assert.Equal(t, strList, "c,d") + + strList = removeMinus([]string{"a", "b", "c", "-c", "-a", "-b"}) + assert.Equal(t, strList, "") + + strList = removeMinus([]string{"b", "a", "-c", "c"}) + assert.Equal(t, strList, "b,a") + + strList = removeMinus([]string{"c", "b", "a", "d", "c", "-c", "-a", "e", "f"}) + assert.Equal(t, strList, "b,d,c,e,f") +} diff --git a/config/consumer_config.go b/config/consumer_config.go index debcd79fa281c40e5526f60f5c5cdb66688688f4..1453628ab5e24a3607183c349d44e9440868ab60 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -38,7 +38,7 @@ import ( // consumerConfig ///////////////////////// -// ConsumerConfig ... +// ConsumerConfig is Consumer default configuration type ConsumerConfig struct { BaseConfig `yaml:",inline"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` @@ -63,7 +63,7 @@ type ConsumerConfig struct { ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ConsumerConfig by @unmarshal function func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -75,17 +75,17 @@ func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } -// Prefix ... +// nolint func (*ConsumerConfig) Prefix() string { return constant.ConsumerConfigPrefix } -// SetConsumerConfig ... +// SetConsumerConfig sets consumerConfig by @c func SetConsumerConfig(c ConsumerConfig) { consumerConfig = &c } -// ConsumerInit ... +// ConsumerInit loads config file to init consumer config func ConsumerInit(confConFile string) error { if confConFile == "" { return perrors.Errorf("application configure(consumer) file name is nil") @@ -129,7 +129,7 @@ func configCenterRefreshConsumer() error { var err error if consumerConfig.ConfigCenterConfig != nil { consumerConfig.SetFatherConfig(consumerConfig) - if err := consumerConfig.startConfigCenter(); err != nil { + if err = consumerConfig.startConfigCenter(); err != nil { return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err)) } consumerConfig.fresh() @@ -144,6 +144,5 @@ func configCenterRefreshConsumer() error { return perrors.WithMessagef(err, "time.ParseDuration(Connect_Timeout{%#v})", consumerConfig.Connect_Timeout) } } - return nil } diff --git a/config/generic_service.go b/config/generic_service.go index b66e399f9e5f467e51c8eccf465f926ac44299d5..a3332afe04de1c9b32db42d18ed2590e4188be35 100644 --- a/config/generic_service.go +++ b/config/generic_service.go @@ -19,18 +19,18 @@ package config import "context" -// GenericService ... +// GenericService uses for generic invoke for service call type GenericService struct { Invoke func(ctx context.Context, req []interface{}) (interface{}, error) `dubbo:"$invoke"` referenceStr string } -// NewGenericService ... +// NewGenericService returns a GenericService instance func NewGenericService(referenceStr string) *GenericService { return &GenericService{referenceStr: referenceStr} } -// Reference ... +// Reference gets referenceStr from GenericService func (u *GenericService) Reference() string { return u.referenceStr } diff --git a/config/graceful_shutdown.go b/config/graceful_shutdown.go index 382f05c8d57c4363108873433fd03565d03b9a50..aa102f35e9048dbc6fbcb10db19cb802b2f3147b 100644 --- a/config/graceful_shutdown.go +++ b/config/graceful_shutdown.go @@ -52,7 +52,7 @@ import ( * We define them by using 'package build' feature https://golang.org/pkg/go/build/ */ -// GracefulShutdownInit ... +// nolint func GracefulShutdownInit() { signals := make(chan os.Signal, 1) @@ -83,7 +83,7 @@ func GracefulShutdownInit() { }() } -// BeforeShutdown ... +// BeforeShutdown provides processing flow before shutdown func BeforeShutdown() { destroyAllRegistries() @@ -126,10 +126,8 @@ func destroyConsumerProtocols(consumerProtocols *gxset.HashSet) { } } -/** - * destroy the provider's protocol. - * if the protocol is consumer's protocol too, we will keep it. - */ +// destroyProviderProtocols destroys the provider's protocol. +// if the protocol is consumer's protocol too, we will keep it func destroyProviderProtocols(consumerProtocols *gxset.HashSet) { logger.Info("Graceful shutdown --- Destroy provider's protocols. ") @@ -215,9 +213,7 @@ func totalTimeout() time.Duration { return timeout } -/* - * we can not get the protocols from consumerConfig because some protocol don't have configuration, like jsonrpc. - */ +// we can not get the protocols from consumerConfig because some protocol don't have configuration, like jsonrpc. func getConsumerProtocols() *gxset.HashSet { result := gxset.NewSet() if consumerConfig == nil || consumerConfig.References == nil { diff --git a/config/graceful_shutdown_config.go b/config/graceful_shutdown_config.go index 6bbabebf2538effcbbe4bddc50857acf5f962a61..87175166b7011e27985eac404819a1aba3522e49 100644 --- a/config/graceful_shutdown_config.go +++ b/config/graceful_shutdown_config.go @@ -31,7 +31,7 @@ const ( defaultStepTimeout = 10 * time.Second ) -// ShutdownConfig ... +// ShutdownConfig is used as configuration for graceful shutdown type ShutdownConfig struct { /* * Total timeout. Even though we don't release all resources, @@ -58,12 +58,12 @@ type ShutdownConfig struct { RequestsFinished bool } -// Prefix ... +// nolint func (config *ShutdownConfig) Prefix() string { return constant.ShutdownConfigPrefix } -// GetTimeout ... +// nolint func (config *ShutdownConfig) GetTimeout() time.Duration { result, err := time.ParseDuration(config.Timeout) if err != nil { @@ -74,7 +74,7 @@ func (config *ShutdownConfig) GetTimeout() time.Duration { return result } -// GetStepTimeout ... +// nolint func (config *ShutdownConfig) GetStepTimeout() time.Duration { result, err := time.ParseDuration(config.StepTimeout) if err != nil { diff --git a/config/graceful_shutdown_config_test.go b/config/graceful_shutdown_config_test.go index 583ed70b838a8271a47e180ee3c6eb32cbb46984..80eb5317386f4e9d966f7a9f07635a810727d77c 100644 --- a/config/graceful_shutdown_config_test.go +++ b/config/graceful_shutdown_config_test.go @@ -26,7 +26,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestShutdownConfig_GetTimeout(t *testing.T) { +func TestShutdownConfigGetTimeout(t *testing.T) { config := ShutdownConfig{} assert.False(t, config.RejectRequest) assert.False(t, config.RequestsFinished) diff --git a/config/graceful_shutdown_signal_darwin.go b/config/graceful_shutdown_signal_darwin.go index 8ad79ffa62ceed4096c60bfb9139b7ff1586808e..6f1fa982a30125096c553e65c13bae1a413ea141 100644 --- a/config/graceful_shutdown_signal_darwin.go +++ b/config/graceful_shutdown_signal_darwin.go @@ -23,12 +23,12 @@ import ( ) var ( - // ShutdownSignals ... + // ShutdownSignals receives shutdown signals to process ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} - // DumpHeapShutdownSignals ... + // DumpHeapShutdownSignals receives shutdown signals to process DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} ) diff --git a/config/graceful_shutdown_signal_linux.go b/config/graceful_shutdown_signal_linux.go index 8ad79ffa62ceed4096c60bfb9139b7ff1586808e..6f1fa982a30125096c553e65c13bae1a413ea141 100644 --- a/config/graceful_shutdown_signal_linux.go +++ b/config/graceful_shutdown_signal_linux.go @@ -23,12 +23,12 @@ import ( ) var ( - // ShutdownSignals ... + // ShutdownSignals receives shutdown signals to process ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGSTOP, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} - // DumpHeapShutdownSignals ... + // DumpHeapShutdownSignals receives shutdown signals to process DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} ) diff --git a/config/graceful_shutdown_signal_windows.go b/config/graceful_shutdown_signal_windows.go index 815a05ecb20a8fc202debaf6f39d699845cd689e..3136e5ae15081f026e8a6e602a5174e1d396abf7 100644 --- a/config/graceful_shutdown_signal_windows.go +++ b/config/graceful_shutdown_signal_windows.go @@ -23,11 +23,11 @@ import ( ) var ( - // ShutdownSignals ... + // ShutdownSignals receives shutdown signals to process ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT} - // DumpHeapShutdownSignals ... + // DumpHeapShutdownSignals receives shutdown signals to process DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT} ) diff --git a/config/instance/metedata_report.go b/config/instance/metadata_report.go similarity index 94% rename from config/instance/metedata_report.go rename to config/instance/metadata_report.go index cd54b0a7940df166c88f02234ab1a4e3bf384163..4c935d733283e6a79523dd0f35be60f092351dbb 100644 --- a/config/instance/metedata_report.go +++ b/config/instance/metadata_report.go @@ -32,7 +32,7 @@ var ( once sync.Once ) -// GetMetadataReportInstance ... +// GetMetadataReportInstance gets metadata report instance by @url func GetMetadataReportInstance(url *common.URL) metadata.MetadataReport { once.Do(func() { instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url) diff --git a/config/interfaces/config_reader.go b/config/interfaces/config_reader.go index 8b79a17d8903ffe888204875c12feed669cb2f9b..b23f989cc29b42de349d1d80ae594de32ab4abbd 100644 --- a/config/interfaces/config_reader.go +++ b/config/interfaces/config_reader.go @@ -19,7 +19,7 @@ package interfaces import "bytes" -// ConfigReader +// ConfigReader is used to read config from consumer or provider type ConfigReader interface { ReadConsumerConfig(reader *bytes.Buffer) error ReadProviderConfig(reader *bytes.Buffer) error diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go index 41fb6b4769e59784d8d18c3f82b956fd029d4ff7..11fb0cd65cf5500c09c51268115e5f7c60916657 100644 --- a/config/metadata_report_config.go +++ b/config/metadata_report_config.go @@ -32,7 +32,7 @@ import ( "github.com/apache/dubbo-go/config/instance" ) -// MethodConfig ... +// MethodConfig is method level configuration type MetadataReportConfig struct { Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty"` Address string `yaml:"address" json:"address,omitempty" property:"address"` @@ -43,12 +43,12 @@ type MetadataReportConfig struct { Group string `yaml:"group" json:"group,omitempty" property:"group"` } -// Prefix ... +// nolint func (c *MetadataReportConfig) Prefix() string { return constant.MetadataReportPrefix } -// UnmarshalYAML ... +// UnmarshalYAML unmarshal the MetadataReportConfig by @unmarshal function func (c *MetadataReportConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return perrors.WithStack(err) @@ -60,7 +60,7 @@ func (c *MetadataReportConfig) UnmarshalYAML(unmarshal func(interface{}) error) return nil } -// ToUrl ... +// nolint func (c *MetadataReportConfig) ToUrl() (*common.URL, error) { urlMap := make(url.Values) @@ -103,7 +103,7 @@ func startMetadataReport(metadataType string, metadataReportConfig *MetadataRepo if url, err := metadataReportConfig.ToUrl(); err == nil { instance.GetMetadataReportInstance(url) } else { - return perrors.New("MetadataConfig is invalid!") + return perrors.Wrap(err, "Start MetadataReport failed.") } return nil diff --git a/config/metadata_report_config_test.go b/config/metadata_report_config_test.go index 635feecc2d433366534566d184e058eb54a881ed..b80a77657568d0821fddca2fc16a0ccc90d7f96e 100644 --- a/config/metadata_report_config_test.go +++ b/config/metadata_report_config_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestMetadataReportConfig_ToUrl(t *testing.T) { +func TestMetadataReportConfigToUrl(t *testing.T) { metadataReportConfig := MetadataReportConfig{ Protocol: "mock", Address: "127.0.0.1:2181", diff --git a/config/method_config.go b/config/method_config.go index 8f196d9e2c03071a663db03cb185fb9106d6484a..e64773eb135b2f9ec55377bded815147e2e192af 100644 --- a/config/method_config.go +++ b/config/method_config.go @@ -42,7 +42,7 @@ type MethodConfig struct { RequestTimeout string `yaml:"timeout" json:"timeout,omitempty" property:"timeout"` } -// Prefix ... +// nolint func (c *MethodConfig) Prefix() string { if len(c.InterfaceId) != 0 { return constant.DUBBO + "." + c.InterfaceName + "." + c.InterfaceId + "." + c.Name + "." @@ -51,7 +51,7 @@ func (c *MethodConfig) Prefix() string { return constant.DUBBO + "." + c.InterfaceName + "." + c.Name + "." } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the MethodConfig by @unmarshal function func (c *MethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err diff --git a/config/mock_rpcservice.go b/config/mock_rpcservice.go index 6c43699128247bf0ec483eb83f879bf4c3b67a37..1e21b252f5cf094c3bd103e3624fbcb2e2b1e2b1 100644 --- a/config/mock_rpcservice.go +++ b/config/mock_rpcservice.go @@ -21,20 +21,20 @@ import ( "context" ) -// MockService ... +// MockService mocks the rpc service for test type MockService struct{} -// Reference ... +// Reference mocks the Reference method func (*MockService) Reference() string { return "MockService" } -// GetUser ... +// GetUser mocks the GetUser method func (*MockService) GetUser(ctx context.Context, itf []interface{}, str *struct{}) error { return nil } -// GetUser1 ... +// GetUser1 mocks the GetUser1 method func (*MockService) GetUser1(ctx context.Context, itf []interface{}, str *struct{}) error { return nil } diff --git a/config/protocol_config.go b/config/protocol_config.go index 33de976bc6f5bf7341ddcff8d51c505cf23bbccd..cee5b7aa7518ff55a15d05de4733cefbbc9c0a1c 100644 --- a/config/protocol_config.go +++ b/config/protocol_config.go @@ -25,14 +25,14 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -// ProtocolConfig ... +// ProtocolConfig is protocol configuration type ProtocolConfig struct { Name string `required:"true" yaml:"name" json:"name,omitempty" property:"name"` Ip string `required:"true" yaml:"ip" json:"ip,omitempty" property:"ip"` Port string `required:"true" yaml:"port" json:"port,omitempty" property:"port"` } -// Prefix ... +// nolint func (c *ProtocolConfig) Prefix() string { return constant.ProtocolConfigPrefix } diff --git a/config/provider_config.go b/config/provider_config.go index 79569917455773653750d1d5921a722daf079b0a..97d037ede008b5fc8cab2febb2177a4e8c3dbd1b 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -36,7 +36,7 @@ import ( // providerConfig ///////////////////////// -// ProviderConfig ... +// ProviderConfig is the default configuration of service provider type ProviderConfig struct { BaseConfig `yaml:",inline"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` @@ -55,7 +55,7 @@ type ProviderConfig struct { ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ProviderConfig by @unmarshal function func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -67,17 +67,17 @@ func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } -// Prefix ... +// nolint func (*ProviderConfig) Prefix() string { return constant.ProviderConfigPrefix } -// SetProviderConfig ... +// SetProviderConfig sets provider config by @p func SetProviderConfig(p ProviderConfig) { providerConfig = &p } -// ProviderInit ... +// ProviderInit loads config file to init provider config func ProviderInit(confProFile string) error { if len(confProFile) == 0 { return perrors.Errorf("application configure(provider) file name is nil") diff --git a/config/reference_config.go b/config/reference_config.go index 3710cbc4bc62a01a014e91bcb978742c4a93c5cb..5b7a8e9eac676e10276775cab327ae4de1eddf86 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -39,7 +39,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ReferenceConfig ... +// ReferenceConfig is the configuration of service consumer type ReferenceConfig struct { context context.Context pxy *proxy.Proxy @@ -66,7 +66,7 @@ type ReferenceConfig struct { ForceTag bool `yaml:"force.tag" json:"force.tag,omitempty" property:"force.tag"` } -// Prefix ... +// nolint func (c *ReferenceConfig) Prefix() string { return constant.ReferenceConfigPrefix + c.InterfaceName + "." } @@ -76,7 +76,7 @@ func NewReferenceConfig(id string, ctx context.Context) *ReferenceConfig { return &ReferenceConfig{id: id, context: ctx} } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ReferenceConfig by @unmarshal function func (c *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type rf ReferenceConfig raw := rf{} // Put your defaults here @@ -168,7 +168,7 @@ func (c *ReferenceConfig) Implement(v common.RPCService) { c.pxy.Implement(v) } -// GetRPCService ... +// GetRPCService gets RPCService from proxy func (c *ReferenceConfig) GetRPCService() common.RPCService { return c.pxy.Get() } diff --git a/config/reference_config_test.go b/config/reference_config_test.go index 7a65e55f09c997cb49b83f1f185faf9338cf0f5a..e43f5aa40af84b9f15a5595ce23696b6a1bae9a4 100644 --- a/config/reference_config_test.go +++ b/config/reference_config_test.go @@ -123,6 +123,7 @@ func (m *MockProvider) Reference() string { } func (m *MockProvider) CallBack(res common.CallbackResponse) { + // CallBack is a mock function. to implement the interface } func doInitConsumerAsync() { @@ -178,7 +179,7 @@ func doInitConsumerWithSingleRegistry() { } } -func Test_ReferMultireg(t *testing.T) { +func TestReferMultireg(t *testing.T) { doInitConsumer() extension.SetProtocol("registry", GetProtocol) extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) @@ -191,7 +192,7 @@ func Test_ReferMultireg(t *testing.T) { consumerConfig = nil } -func Test_Refer(t *testing.T) { +func TestRefer(t *testing.T) { doInitConsumer() extension.SetProtocol("registry", GetProtocol) extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) @@ -205,7 +206,7 @@ func Test_Refer(t *testing.T) { consumerConfig = nil } -func Test_ReferAsync(t *testing.T) { +func TestReferAsync(t *testing.T) { doInitConsumerAsync() extension.SetProtocol("registry", GetProtocol) extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) @@ -220,7 +221,7 @@ func Test_ReferAsync(t *testing.T) { consumerConfig = nil } -func Test_ReferP2P(t *testing.T) { +func TestReferP2P(t *testing.T) { doInitConsumer() extension.SetProtocol("dubbo", GetProtocol) m := consumerConfig.References["MockService"] @@ -234,7 +235,7 @@ func Test_ReferP2P(t *testing.T) { consumerConfig = nil } -func Test_ReferMultiP2P(t *testing.T) { +func TestReferMultiP2P(t *testing.T) { doInitConsumer() extension.SetProtocol("dubbo", GetProtocol) m := consumerConfig.References["MockService"] @@ -248,7 +249,7 @@ func Test_ReferMultiP2P(t *testing.T) { consumerConfig = nil } -func Test_ReferMultiP2PWithReg(t *testing.T) { +func TestReferMultiP2PWithReg(t *testing.T) { doInitConsumer() extension.SetProtocol("dubbo", GetProtocol) extension.SetProtocol("registry", GetProtocol) @@ -263,7 +264,7 @@ func Test_ReferMultiP2PWithReg(t *testing.T) { consumerConfig = nil } -func Test_Implement(t *testing.T) { +func TestImplement(t *testing.T) { doInitConsumer() extension.SetProtocol("registry", GetProtocol) extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) @@ -276,7 +277,7 @@ func Test_Implement(t *testing.T) { consumerConfig = nil } -func Test_Forking(t *testing.T) { +func TestForking(t *testing.T) { doInitConsumer() extension.SetProtocol("dubbo", GetProtocol) extension.SetProtocol("registry", GetProtocol) @@ -293,7 +294,7 @@ func Test_Forking(t *testing.T) { consumerConfig = nil } -func Test_Sticky(t *testing.T) { +func TestSticky(t *testing.T) { doInitConsumer() extension.SetProtocol("dubbo", GetProtocol) extension.SetProtocol("registry", GetProtocol) @@ -332,4 +333,6 @@ func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter return protocol.NewBaseExporter("test", invoker, &sync.Map{}) } -func (*mockRegistryProtocol) Destroy() {} +func (*mockRegistryProtocol) Destroy() { + // Destroy is a mock function +} diff --git a/config/registry_config.go b/config/registry_config.go index f3d22311b86d4cc3b66f12e9926dff9565ae4cd6..ef527c827e9dac4cd2762f579d30254e9e51150f 100644 --- a/config/registry_config.go +++ b/config/registry_config.go @@ -33,20 +33,21 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -// RegistryConfig ... +// RegistryConfig is the configuration of the registry center type RegistryConfig struct { Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty" property:"protocol"` - //I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig + // I changed "type" to "protocol" ,the same as "protocol" field in java class RegistryConfig TimeoutStr string `yaml:"timeout" default:"5s" json:"timeout,omitempty" property:"timeout"` // unit: second Group string `yaml:"group" json:"group,omitempty" property:"group"` - //for registry - Address string `yaml:"address" json:"address,omitempty" property:"address"` - Username string `yaml:"username" json:"username,omitempty" property:"username"` - Password string `yaml:"password" json:"password,omitempty" property:"password"` - Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` + // for registry + Address string `yaml:"address" json:"address,omitempty" property:"address"` + Username string `yaml:"username" json:"username,omitempty" property:"username"` + Password string `yaml:"password" json:"password,omitempty" property:"password"` + Simplified bool `yaml:"simplified" json:"simplified,omitempty" property:"simplified"` + Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the RegistryConfig by @unmarshal function func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -58,7 +59,7 @@ func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } -// Prefix ... +// nolint func (*RegistryConfig) Prefix() string { return constant.RegistryConfigPrefix + "|" + constant.SingleRegistryConfigPrefix } @@ -70,9 +71,11 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf for k, registryConf := range registries { target := false - // if user not config targetRegistries,default load all - // Notice:in func "func Split(s, sep string) []string" comment : if s does not contain sep and sep is not empty, SplitAfter returns a slice of length 1 whose only element is s. - // So we have to add the condition when targetRegistries string is not set (it will be "" when not set) + // if user not config targetRegistries, default load all + // Notice: in func "func Split(s, sep string) []string" comment: + // if s does not contain sep and sep is not empty, SplitAfter returns + // a slice of length 1 whose only element is s. So we have to add the + // condition when targetRegistries string is not set (it will be "" when not set) if len(trSlice) == 0 || (len(trSlice) == 1 && trSlice[0] == "") { target = true } else { @@ -86,29 +89,24 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf } if target { - var ( - url common.URL - err error - ) - addresses := strings.Split(registryConf.Address, ",") address := addresses[0] address = translateRegistryConf(address, registryConf) - url, err = common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address, + url, err := common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address, common.WithParams(registryConf.getUrlMap(roleType)), + common.WithParamsValue("simplified", strconv.FormatBool(registryConf.Simplified)), common.WithUsername(registryConf.Username), common.WithPassword(registryConf.Password), common.WithLocation(registryConf.Address), ) if err != nil { - logger.Errorf("The registry id:%s url is invalid , error: %#v", k, err) + logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err) panic(err) } else { urls = append(urls, &url) } } - } return urls @@ -123,7 +121,6 @@ func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values { for k, v := range c.Params { urlMap.Set(k, v) } - return urlMap } @@ -131,7 +128,7 @@ func translateRegistryConf(address string, registryConf *RegistryConfig) string if strings.Contains(address, "://") { translatedUrl, err := url.Parse(address) if err != nil { - logger.Errorf("The registry url is invalid , error: %#v", err) + logger.Errorf("The registry url is invalid, error: %#v", err) panic(err) } address = translatedUrl.Host diff --git a/config/registry_config_test.go b/config/registry_config_test.go index 6c2fed605d6c50b483f7ad2900e5a483b3986e1b..6e5dedc34ff5489fc190841bce73cd015eb78132 100644 --- a/config/registry_config_test.go +++ b/config/registry_config_test.go @@ -29,7 +29,7 @@ import ( "github.com/apache/dubbo-go/common" ) -func Test_loadRegistries(t *testing.T) { +func TestLoadRegistries(t *testing.T) { target := "shanghai1" regs := map[string]*RegistryConfig{ @@ -47,7 +47,7 @@ func Test_loadRegistries(t *testing.T) { assert.Equal(t, "127.0.0.2:2181,128.0.0.1:2181", urls[0].Location) } -func Test_loadRegistries1(t *testing.T) { +func TestLoadRegistries1(t *testing.T) { target := "shanghai1" regs := map[string]*RegistryConfig{ diff --git a/config/service.go b/config/service.go index b7e7dc2a425b42363d570fc37a70e2e5094e7d9d..b746141dd0d55f8e306d9f4d13de2b9021c70272 100644 --- a/config/service.go +++ b/config/service.go @@ -36,17 +36,17 @@ func SetProviderService(service common.RPCService) { proServices[service.Reference()] = service } -// GetConsumerService ... +// GetConsumerService gets ConsumerService by @name func GetConsumerService(name string) common.RPCService { return conServices[name] } -// GetProviderService ... +// GetProviderService gets ProviderService by @name func GetProviderService(name string) common.RPCService { return proServices[name] } -// GetCallback ... +// GetCallback gets CallbackResponse by @name func GetCallback(name string) func(response common.CallbackResponse) { service := GetConsumerService(name) if sv, ok := service.(common.AsyncCallbackService); ok { diff --git a/config/service_config.go b/config/service_config.go index 50bf5e12c3247340f177a84c72446383ec5c3450..70a344c7b8c858a25e9a556ac6f4e899b1626ab4 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -18,6 +18,7 @@ package config import ( + "container/list" "context" "fmt" "net/url" @@ -29,6 +30,7 @@ import ( import ( "github.com/creasty/defaults" + gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "go.uber.org/atomic" ) @@ -42,7 +44,7 @@ import ( "github.com/apache/dubbo-go/protocol/protocolwrapper" ) -// ServiceConfig ... +// ServiceConfig is the configuration of the service provider type ServiceConfig struct { context context.Context id string @@ -78,12 +80,12 @@ type ServiceConfig struct { cacheMutex sync.Mutex } -// Prefix ... +// nolint func (c *ServiceConfig) Prefix() string { return constant.ServiceConfigPrefix + c.InterfaceName + "." } -// UnmarshalYAML ... +// UnmarshalYAML unmarshals the ServiceConfig by @unmarshal function func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -105,6 +107,24 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig { } } +// Get Random Port +func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List { + ports := list.New() + for _, proto := range protocolConfigs { + if len(proto.Port) > 0 { + continue + } + + tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip) + if err != nil { + panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err))) + } + defer tcp.Close() + ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1]) + } + return ports +} + // Export ... func (c *ServiceConfig) Export() error { // TODO: config center start here @@ -127,19 +147,29 @@ func (c *ServiceConfig) Export() error { logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol) return nil } + + ports := getRandomPort(protocolConfigs) + nextPort := ports.Front() for _, proto := range protocolConfigs { // registry the service reflect methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService) if err != nil { - err := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) - logger.Errorf(err.Error()) - return err + formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) + logger.Errorf(formatErr.Error()) + return formatErr + } + + port := proto.Port + + if len(proto.Port) == 0 { + port = nextPort.Value.(string) + nextPort = nextPort.Next() } ivkURL := common.NewURLWithOptions( common.WithPath(c.id), common.WithProtocol(proto.Name), common.WithIp(proto.Ip), - common.WithPort(proto.Port), + common.WithPort(port), common.WithParams(urlMap), common.WithParamsValue(constant.BEAN_NAME_KEY, c.id), common.WithMethods(strings.Split(methods, ",")), diff --git a/config/service_config_test.go b/config/service_config_test.go index 6f3230890348e77ea26c9c0eaf9165090c8cd09f..798984874a7983f4236a3e60d369b0dc9b255357 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -21,6 +21,11 @@ import ( "testing" ) +import ( + gxnet "github.com/dubbogo/gost/net" + "github.com/stretchr/testify/assert" +) + import ( "github.com/apache/dubbo-go/common/extension" ) @@ -178,7 +183,7 @@ func doInitProviderWithSingleRegistry() { } } -func Test_Export(t *testing.T) { +func TestExport(t *testing.T) { doInitProvider() extension.SetProtocol("registry", GetProtocol) @@ -189,3 +194,35 @@ func Test_Export(t *testing.T) { } providerConfig = nil } + +func TestgetRandomPort(t *testing.T) { + protocolConfigs := make([]*ProtocolConfig, 0, 3) + + ip, err := gxnet.GetLocalIP() + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + protocolConfigs = append(protocolConfigs, &ProtocolConfig{ + Ip: ip, + }) + assert.NoError(t, err) + ports := getRandomPort(protocolConfigs) + + assert.Equal(t, ports.Len(), len(protocolConfigs)) + + front := ports.Front() + for { + if front == nil { + break + } + t.Logf("port:%v", front.Value) + front = front.Next() + } + + protocolConfigs = make([]*ProtocolConfig, 0, 3) + ports = getRandomPort(protocolConfigs) + assert.Equal(t, ports.Len(), len(protocolConfigs)) +} diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go index a95524b41b887313993aad4e774ed6d96b24c08f..335fb71045ca6349557cf7c736cead0565bdc193 100644 --- a/config_center/apollo/impl_test.go +++ b/config_center/apollo/impl_test.go @@ -125,7 +125,7 @@ func initApollo() *httptest.Server { return runMockConfigServer(handlerMap, notifyResponse) } -func configResponse(rw http.ResponseWriter, req *http.Request) { +func configResponse(rw http.ResponseWriter, _ *http.Request) { result := fmt.Sprintf(mockConfigRes) fmt.Fprintf(rw, "%s", result) } @@ -135,7 +135,7 @@ func notifyResponse(rw http.ResponseWriter, req *http.Request) { fmt.Fprintf(rw, "%s", result) } -func serviceConfigResponse(rw http.ResponseWriter, req *http.Request) { +func serviceConfigResponse(rw http.ResponseWriter, _ *http.Request) { result := fmt.Sprintf(mockServiceConfigRes) fmt.Fprintf(rw, "%s", result) } @@ -164,7 +164,7 @@ func runMockConfigServer(handlerMap map[string]func(http.ResponseWriter, *http.R return ts } -func Test_GetConfig(t *testing.T) { +func TestGetConfig(t *testing.T) { configuration := initMockApollo(t) configs, err := configuration.GetProperties(mockNamespace, config_center.WithGroup("dubbo")) assert.NoError(t, err) @@ -175,7 +175,7 @@ func Test_GetConfig(t *testing.T) { deleteMockJson(t) } -func Test_GetConfigItem(t *testing.T) { +func TestGetConfigItem(t *testing.T) { configuration := initMockApollo(t) configs, err := configuration.GetInternalProperty("application.organization") assert.NoError(t, err) @@ -238,7 +238,7 @@ func TestRemoveListener(t *testing.T) { apollo.RemoveListener(mockNamespace, listener) assert.Equal(t, "", listener.event) listenerCount := 0 - apollo.listeners.Range(func(key, value interface{}) bool { + apollo.listeners.Range(func(_, value interface{}) bool { apolloListener := value.(*apolloListener) for e := range apolloListener.listeners { fmt.Println(e) diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go index fb257a4828aed077f61568685ee7823e9c215cf9..1cf65ed22ba0a1f765af66191ed19a04f81b0fe6 100644 --- a/config_center/apollo/listener.go +++ b/config_center/apollo/listener.go @@ -29,7 +29,7 @@ type apolloListener struct { listeners map[config_center.ConfigurationListener]struct{} } -// NewApolloListener ... +// NewApolloListener creates a new apolloListener func NewApolloListener() *apolloListener { return &apolloListener{ listeners: make(map[config_center.ConfigurationListener]struct{}, 0), @@ -49,7 +49,7 @@ func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) { } } -// AddListener ... +// AddListener adds a listener for apollo func (a *apolloListener) AddListener(l config_center.ConfigurationListener) { if _, ok := a.listeners[l]; !ok { a.listeners[l] = struct{}{} @@ -57,7 +57,7 @@ func (a *apolloListener) AddListener(l config_center.ConfigurationListener) { } } -// RemoveListener ... +// RemoveListener removes listeners of apollo func (a *apolloListener) RemoveListener(l config_center.ConfigurationListener) { delete(a.listeners, l) } diff --git a/config_center/configuration_listener.go b/config_center/configuration_listener.go index e70e4f68075c51c33f1110ef44a7b703e36fb78d..97fd9c70923f5c921ce2ca2b4028a71ea2b49e27 100644 --- a/config_center/configuration_listener.go +++ b/config_center/configuration_listener.go @@ -25,12 +25,13 @@ import ( "github.com/apache/dubbo-go/remoting" ) -// ConfigurationListener ... +// ConfigurationListener for changing listener's event type ConfigurationListener interface { + // Process the notification event once there's any change happens on the config Process(*ConfigChangeEvent) } -// ConfigChangeEvent ... +// ConfigChangeEvent for changing listener's event type ConfigChangeEvent struct { Key string Value interface{} diff --git a/config_center/configurator.go b/config_center/configurator.go index ffa9034e05c4c3d4cc254886e2ed19576f155dec..9db4804365689d8eb357965973a2916e86383cf8 100644 --- a/config_center/configurator.go +++ b/config_center/configurator.go @@ -21,7 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) -// Configurator ... +// Configurator supports GetUrl and constructor type Configurator interface { GetUrl() *common.URL Configure(url *common.URL) diff --git a/config_center/configurator/mock.go b/config_center/configurator/mock.go index d294b9195db9cfe60056bc29ec26816f740ea396..7ec7179634cfd967cd27e85ed248e2075c387cb5 100644 --- a/config_center/configurator/mock.go +++ b/config_center/configurator/mock.go @@ -23,7 +23,7 @@ import ( "github.com/apache/dubbo-go/config_center" ) -// NewMockConfigurator ... +// NewMockConfigurator creates a new mockConfigurator func NewMockConfigurator(url *common.URL) config_center.Configurator { return &mockConfigurator{configuratorUrl: url} } @@ -32,12 +32,12 @@ type mockConfigurator struct { configuratorUrl *common.URL } -// GetUrl ... +// GetUrl gets a configuratorUrl func (c *mockConfigurator) GetUrl() *common.URL { return c.configuratorUrl } -// Configure ... +// Configure sets up param CLUSTER_KEY and cluster for url func (c *mockConfigurator) Configure(url *common.URL) { if cluster := c.GetUrl().GetParam(constant.CLUSTER_KEY, ""); cluster != "" { url.SetParam(constant.CLUSTER_KEY, cluster) diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go index 18415bee3a28b37ffc2f3f73cc7309b685de5408..294a60ebb2e4e18cfc47cd90aedeaa615b5626d2 100644 --- a/config_center/configurator/override.go +++ b/config_center/configurator/override.go @@ -50,12 +50,12 @@ func (c *overrideConfigurator) GetUrl() *common.URL { } func (c *overrideConfigurator) Configure(url *common.URL) { - //remove configuratorUrl some param that can not be configured + // remove configuratorUrl some param that can not be configured if c.configuratorUrl.GetParam(constant.ENABLED_KEY, "true") == "false" || len(c.configuratorUrl.Location) == 0 { return } - //branch for version 2.7.x + // branch for version 2.7.x apiVersion := c.configuratorUrl.GetParam(constant.CONFIG_VERSION_KEY, "") if len(apiVersion) != 0 { currentSide := url.GetParam(constant.SIDE_KEY, "") @@ -67,48 +67,51 @@ func (c *overrideConfigurator) Configure(url *common.URL) { c.configureIfMatch(url.Ip, url) } } else { - //branch for version 2.6.x and less + // branch for version 2.6.x and less c.configureDeprecated(url) } } -//translate from java, compatible rules in java +func (c *overrideConfigurator) configureIfMatchInternal(url *common.URL) { + configApp := c.configuratorUrl.GetParam(constant.APPLICATION_KEY, c.configuratorUrl.Username) + currentApp := url.GetParam(constant.APPLICATION_KEY, url.Username) + if len(configApp) == 0 || constant.ANY_VALUE == configApp || configApp == currentApp { + conditionKeys := gxset.NewSet() + conditionKeys.Add(constant.CATEGORY_KEY) + conditionKeys.Add(constant.CHECK_KEY) + conditionKeys.Add(constant.ENABLED_KEY) + conditionKeys.Add(constant.GROUP_KEY) + conditionKeys.Add(constant.VERSION_KEY) + conditionKeys.Add(constant.APPLICATION_KEY) + conditionKeys.Add(constant.SIDE_KEY) + conditionKeys.Add(constant.CONFIG_VERSION_KEY) + conditionKeys.Add(constant.COMPATIBLE_CONFIG_KEY) + returnUrl := false + c.configuratorUrl.RangeParams(func(k, _ string) bool { + value := c.configuratorUrl.GetParam(k, "") + if strings.HasPrefix(k, "~") || k == constant.APPLICATION_KEY || k == constant.SIDE_KEY { + conditionKeys.Add(k) + if len(value) != 0 && value != constant.ANY_VALUE && value != url.GetParam(strings.TrimPrefix(k, "~"), "") { + returnUrl = true + return false + } + } + return true + }) + if returnUrl { + return + } + configUrl := c.configuratorUrl.CloneExceptParams(conditionKeys) + url.SetParams(configUrl.GetParams()) + } +} + +// configureIfMatch translate from java, compatible rules in java func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) { if constant.ANYHOST_VALUE == c.configuratorUrl.Ip || host == c.configuratorUrl.Ip { providers := c.configuratorUrl.GetParam(constant.OVERRIDE_PROVIDERS_KEY, "") if len(providers) == 0 || strings.Index(providers, url.Location) >= 0 || strings.Index(providers, constant.ANYHOST_VALUE) >= 0 { - configApp := c.configuratorUrl.GetParam(constant.APPLICATION_KEY, c.configuratorUrl.Username) - currentApp := url.GetParam(constant.APPLICATION_KEY, url.Username) - if len(configApp) == 0 || constant.ANY_VALUE == configApp || configApp == currentApp { - conditionKeys := gxset.NewSet() - conditionKeys.Add(constant.CATEGORY_KEY) - conditionKeys.Add(constant.CHECK_KEY) - conditionKeys.Add(constant.ENABLED_KEY) - conditionKeys.Add(constant.GROUP_KEY) - conditionKeys.Add(constant.VERSION_KEY) - conditionKeys.Add(constant.APPLICATION_KEY) - conditionKeys.Add(constant.SIDE_KEY) - conditionKeys.Add(constant.CONFIG_VERSION_KEY) - conditionKeys.Add(constant.COMPATIBLE_CONFIG_KEY) - returnUrl := false - c.configuratorUrl.RangeParams(func(k, v string) bool { - value := c.configuratorUrl.GetParam(k, "") - if strings.HasPrefix(k, "~") || k == constant.APPLICATION_KEY || k == constant.SIDE_KEY { - conditionKeys.Add(k) - if len(value) != 0 && value != constant.ANY_VALUE && value != url.GetParam(strings.TrimPrefix(k, "~"), "") { - returnUrl = true - return false - } - } - return true - }) - if returnUrl { - return - } - configUrl := c.configuratorUrl.Clone() - configUrl.RemoveParams(conditionKeys) - url.SetParams(configUrl.GetParams()) - } + c.configureIfMatchInternal(url) } } } diff --git a/config_center/configurator/override_test.go b/config_center/configurator/override_test.go index c0aeb15130e7862fcb00d6cb82cbef60df777acb..8eccb5091272b033cf31b612dfb19bce6514ccce 100644 --- a/config_center/configurator/override_test.go +++ b/config_center/configurator/override_test.go @@ -30,51 +30,58 @@ import ( "github.com/apache/dubbo-go/common/extension" ) -func Test_configureVerison2p6(t *testing.T) { +const ( + defaults = "default" + override = "override" + failfast = "failfast" + failover = "failover" +) + +func TestConfigureVerison2p6(t *testing.T) { url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) - configurator := extension.GetConfigurator("default", &url) - assert.Equal(t, "override", configurator.GetUrl().Protocol) + configurator := extension.GetConfigurator(defaults, &url) + assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) configurator.Configure(&providerUrl) - assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) + assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func Test_configureVerisonOverrideAddr(t *testing.T) { +func TestConfigureVerisonOverrideAddr(t *testing.T) { url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001") assert.NoError(t, err) - configurator := extension.GetConfigurator("default", &url) - assert.Equal(t, "override", configurator.GetUrl().Protocol) + configurator := extension.GetConfigurator(defaults, &url) + assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) configurator.Configure(&providerUrl) - assert.Equal(t, "failover", providerUrl.GetParam(constant.CLUSTER_KEY, "")) + assert.Equal(t, failover, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func Test_configureVerison2p6WithIp(t *testing.T) { +func TestConfigureVerison2p6WithIp(t *testing.T) { url, err := common.NewURL("override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) - configurator := extension.GetConfigurator("default", &url) - assert.Equal(t, "override", configurator.GetUrl().Protocol) + configurator := extension.GetConfigurator(defaults, &url) + assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) configurator.Configure(&providerUrl) - assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) + assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func Test_configureVerison2p7(t *testing.T) { +func TestConfigureVerison2p7(t *testing.T) { url, err := common.NewURL("jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider") assert.NoError(t, err) - configurator := extension.GetConfigurator("default", &url) + configurator := extension.GetConfigurator(defaults, &url) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) configurator.Configure(&providerUrl) - assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) + assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index 9013d7140e757520f2e8f048ce53a5ac2a13f982..540febc9d38e164afcc62538478df140b7d671c7 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -40,7 +40,7 @@ const ( DEFAULT_CONFIG_TIMEOUT = "10s" ) -// DynamicConfiguration ... +// DynamicConfiguration for modify listener and get properties file type DynamicConfiguration interface { Parser() parser.ConfigurationParser SetParser(parser.ConfigurationParser) @@ -71,14 +71,14 @@ type Options struct { // Option ... type Option func(*Options) -// WithGroup ... +// WithGroup assigns group to opt.Group func WithGroup(group string) Option { return func(opt *Options) { opt.Group = group } } -// WithTimeout ... +// WithTimeout assigns time to opt.Timeout func WithTimeout(time time.Duration) Option { return func(opt *Options) { opt.Timeout = time diff --git a/config_center/dynamic_configuration_factory.go b/config_center/dynamic_configuration_factory.go index 9f9b13227f6623a02b0261c46d8d1e43624005f8..46faf864443b7f8780584213b758f26395224956 100644 --- a/config_center/dynamic_configuration_factory.go +++ b/config_center/dynamic_configuration_factory.go @@ -21,7 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) -// DynamicConfigurationFactory ... +// DynamicConfigurationFactory gets the DynamicConfiguration type DynamicConfigurationFactory interface { GetDynamicConfiguration(*common.URL) (DynamicConfiguration, error) } diff --git a/config_center/dynamic_configuration_test.go b/config_center/dynamic_configuration_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8b5a8d7cc5afea8559d563f82f5b2dd80c51488e --- /dev/null +++ b/config_center/dynamic_configuration_test.go @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 config_center + +import ( + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" +) + +func TestWithTimeout(t *testing.T) { + fa := WithTimeout(12 * time.Second) + opt := &Options{} + fa(opt) + assert.Equal(t, 12*time.Second, opt.Timeout) +} + +func TestGetRuleKey(t *testing.T) { + url, err := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider?interface=test&group=groupA&version=0") + assert.NoError(t, err) + assert.Equal(t, "test:0:groupA", GetRuleKey(url)) +} diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 59c788b65bce2a4773975ea1a96a314649781832..de208946f1715878c2afc62fdd41df93f74797c7 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -38,12 +38,16 @@ type MockDynamicConfigurationFactory struct { Content string } +const ( + mockServiceName = "org.apache.dubbo-go.mockService" +) + var ( once sync.Once dynamicConfiguration *MockDynamicConfiguration ) -// GetDynamicConfiguration ... +// GetDynamicConfiguration returns a DynamicConfiguration func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) (DynamicConfiguration, error) { var err error once.Do(func() { @@ -99,16 +103,17 @@ type MockDynamicConfiguration struct { listener map[string]ConfigurationListener } -// AddListener ... +// AddListener adds a listener for MockDynamicConfiguration func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, _ ...Option) { c.listener[key] = listener } -// RemoveListener ... +// RemoveListener removes the listener for MockDynamicConfiguration func (c *MockDynamicConfiguration) RemoveListener(_ string, _ ConfigurationListener, _ ...Option) { + // mock remove } -// GetConfig ... +// GetConfig returns content of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetConfig(_ string, _ ...Option) (string, error) { return c.content, nil @@ -119,17 +124,17 @@ func (c *MockDynamicConfiguration) GetConfigs(key string, opts ...Option) (strin return c.GetConfig(key, opts...) } -// Parser ... +// Parser returns a parser of MockDynamicConfiguration func (c *MockDynamicConfiguration) Parser() parser.ConfigurationParser { return c.parser } -// SetParser ... +// SetParser sets parser of MockDynamicConfiguration func (c *MockDynamicConfiguration) SetParser(p parser.ConfigurationParser) { c.parser = p } -// GetProperties ... +// GetProperties gets content of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetProperties(_ string, _ ...Option) (string, error) { return c.content, nil } @@ -139,7 +144,7 @@ func (c *MockDynamicConfiguration) GetInternalProperty(key string, opts ...Optio return c.GetProperties(key, opts...) } -// GetRule ... +// GetRule gets properties of MockDynamicConfiguration func (c *MockDynamicConfiguration) GetRule(key string, opts ...Option) (string, error) { return c.GetProperties(key, opts...) } @@ -149,20 +154,20 @@ func (c *MockDynamicConfiguration) MockServiceConfigEvent() { config := &parser.ConfiguratorConfig{ ConfigVersion: "2.7.1", Scope: parser.GeneralType, - Key: "org.apache.dubbo-go.mockService", + Key: mockServiceName, Enabled: true, Configs: []parser.ConfigItem{ {Type: parser.GeneralType, Enabled: true, Addresses: []string{"0.0.0.0"}, - Services: []string{"org.apache.dubbo-go.mockService"}, + Services: []string{mockServiceName}, Side: "provider", Parameters: map[string]string{"cluster": "mock1"}, }, }, } value, _ := yaml.Marshal(config) - key := "group*org.apache.dubbo-go.mockService:1.0.0" + constant.CONFIGURATORS_SUFFIX + key := "group*" + mockServiceName + ":1.0.0" + constant.CONFIGURATORS_SUFFIX c.listener[key].Process(&ConfigChangeEvent{Key: key, Value: string(value), ConfigType: remoting.EventTypeAdd}) } @@ -171,13 +176,13 @@ func (c *MockDynamicConfiguration) MockApplicationConfigEvent() { config := &parser.ConfiguratorConfig{ ConfigVersion: "2.7.1", Scope: parser.ScopeApplication, - Key: "org.apache.dubbo-go.mockService", + Key: mockServiceName, Enabled: true, Configs: []parser.ConfigItem{ {Type: parser.ScopeApplication, Enabled: true, Addresses: []string{"0.0.0.0"}, - Services: []string{"org.apache.dubbo-go.mockService"}, + Services: []string{mockServiceName}, Side: "provider", Parameters: map[string]string{"cluster": "mock1"}, }, diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go index d3373e249bf99873dd3aa05b7488b0e7f38730ec..3b432819f43327888ade3da5303e445d6a2ef0fe 100644 --- a/config_center/nacos/client.go +++ b/config_center/nacos/client.go @@ -18,6 +18,7 @@ package nacos import ( + "path/filepath" "strconv" "strings" "sync" @@ -36,7 +37,8 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -const logDir = "logs/nacos/log" +// Nacos Log dir, it can be override when creating client by config_center.log_dir +var logDir = filepath.Join("logs", "nacos", "log") // NacosClient Nacos client type NacosClient struct { @@ -87,6 +89,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error { } url := container.GetUrl() + logDir = url.GetParam(constant.CONFIG_LOG_DIR_KEY, logDir) if container.NacosClient() == nil { //in dubbo ,every registry only connect one node ,so this is []string{r.Address} diff --git a/config_center/nacos/client_test.go b/config_center/nacos/client_test.go index ef63eeff6ddf4e5cd6fa2ba7da7996b3dbed94ac..2e7f4645149b8cd70510713d49ced9ba31d66bb6 100644 --- a/config_center/nacos/client_test.go +++ b/config_center/nacos/client_test.go @@ -31,7 +31,7 @@ import ( "github.com/apache/dubbo-go/common" ) -func Test_newNacosClient(t *testing.T) { +func TestNewNacosClient(t *testing.T) { server := mockCommonNacosServer() nacosURL := strings.ReplaceAll(server.URL, "http", "registry") registryUrl, _ := common.NewURL(nacosURL) @@ -53,3 +53,62 @@ func Test_newNacosClient(t *testing.T) { <-c.client.Done() c.Destroy() } + +func TestSetNacosClient(t *testing.T) { + server := mockCommonNacosServer() + nacosURL := server.Listener.Addr().String() + registryUrl, _ := common.NewURL(nacosURL) + c := &nacosDynamicConfiguration{ + url: ®istryUrl, + done: make(chan struct{}), + } + var client *NacosClient + client = &NacosClient{ + name: nacosClientName, + NacosAddrs: []string{nacosURL}, + Timeout: 15, + exit: make(chan struct{}), + onceClose: func() { + close(client.exit) + }, + } + c.SetNacosClient(client) + err := ValidateNacosClient(c, WithNacosName(nacosClientName)) + assert.NoError(t, err) + c.wg.Add(1) + go HandleClientRestart(c) + go func() { + // c.client.Close() and <-c.client.Done() have order requirements. + // If c.client.Close() is called first.It is possible that "go HandleClientRestart(c)" + // sets c.client to nil before calling c.client.Done(). + time.Sleep(time.Second) + c.client.Close() + }() + <-c.client.Done() + c.Destroy() +} + +func TestNewNacosClient_connectError(t *testing.T) { + nacosURL := "registry://127.0.0.1:8888" + registryUrl, err := common.NewURL(nacosURL) + assert.NoError(t, err) + c := &nacosDynamicConfiguration{ + url: ®istryUrl, + done: make(chan struct{}), + } + err = ValidateNacosClient(c, WithNacosName(nacosClientName)) + assert.NoError(t, err) + c.wg.Add(1) + go HandleClientRestart(c) + go func() { + // c.client.Close() and <-c.client.Done() have order requirements. + // If c.client.Close() is called first.It is possible that "go HandleClientRestart(c)" + // sets c.client to nil before calling c.client.Done(). + time.Sleep(time.Second) + c.client.Close() + }() + <-c.client.Done() + // let client do retry + time.Sleep(5 * time.Second) + c.Destroy() +} diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go index 4032c91cda512b649140db6eea3dc11eeb482f27..453fa11f955c83ae2925a959e7a2465a2a0e7796 100644 --- a/config_center/nacos/impl_test.go +++ b/config_center/nacos/impl_test.go @@ -59,10 +59,10 @@ func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request), } func mockCommonNacosServer() *httptest.Server { - return runMockConfigServer(func(writer http.ResponseWriter, request *http.Request) { + return runMockConfigServer(func(writer http.ResponseWriter, _ *http.Request) { data := "true" fmt.Fprintf(writer, "%s", data) - }, func(writer http.ResponseWriter, request *http.Request) { + }, func(writer http.ResponseWriter, _ *http.Request) { data := `dubbo.properties%02dubbo%02dubbo.service.com.ikurento.user.UserProvider.cluster=failback` fmt.Fprintf(writer, "%s", data) }) @@ -72,15 +72,16 @@ func initNacosData(t *testing.T) (*nacosDynamicConfiguration, error) { server := mockCommonNacosServer() nacosURL := strings.ReplaceAll(server.URL, "http", "registry") regurl, _ := common.NewURL(nacosURL) - nacosConfiguration, err := newNacosDynamicConfiguration(®url) + factory := &nacosDynamicConfigurationFactory{} + nacosConfiguration, err := factory.GetDynamicConfiguration(®url) assert.NoError(t, err) nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{}) - return nacosConfiguration, err + return nacosConfiguration.(*nacosDynamicConfiguration), err } -func Test_GetConfig(t *testing.T) { +func TestGetConfig(t *testing.T) { nacos, err := initNacosData(t) assert.NoError(t, err) configs, err := nacos.GetProperties("dubbo.properties", config_center.WithGroup("dubbo")) @@ -88,7 +89,7 @@ func Test_GetConfig(t *testing.T) { assert.NoError(t, err) } -func TestNacosDynamicConfiguration_PublishConfig(t *testing.T) { +func TestNacosDynamicConfigurationPublishConfig(t *testing.T) { nacos, err := initNacosData(t) assert.Nil(t, err) key := "myKey" @@ -98,7 +99,7 @@ func TestNacosDynamicConfiguration_PublishConfig(t *testing.T) { assert.Nil(t, err) } -func Test_AddListener(t *testing.T) { +func TestAddListener(t *testing.T) { nacos, err := initNacosData(t) assert.NoError(t, err) listener := &mockDataListener{} @@ -108,7 +109,7 @@ func Test_AddListener(t *testing.T) { listener.wg.Wait() } -func Test_RemoveListener(t *testing.T) { +func TestRemoveListener(_ *testing.T) { //TODO not supported in current go_nacos_sdk version } diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index de74cff8f64683a47278825b670352a04b69b791..4c995389d38e1a39670aff26025f030bd4bfb1ec 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -31,7 +31,7 @@ import ( "github.com/apache/dubbo-go/remoting" ) -func callback(listener config_center.ConfigurationListener, namespace, group, dataId, data string) { +func callback(listener config_center.ConfigurationListener, _, _, dataId, data string) { listener.Process(&config_center.ConfigChangeEvent{Key: dataId, Value: data, ConfigType: remoting.EventTypeUpdate}) } diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go index f33b4ba866da69e1d23b493f42152bbb0f437878..6fbdc27d4339150bfec624f7dc5ea0f6a608d7a7 100644 --- a/config_center/parser/configuration_parser.go +++ b/config_center/parser/configuration_parser.go @@ -47,7 +47,7 @@ type ConfigurationParser interface { ParseToUrls(content string) ([]*common.URL, error) } -// DefaultConfigurationParser for support properties file in config center +// DefaultConfigurationParser for supporting properties file in config center type DefaultConfigurationParser struct{} // ConfiguratorConfig ... @@ -71,7 +71,7 @@ type ConfigItem struct { Side string `yaml:"side"` } -// Parse ... +// Parse load content func (parser *DefaultConfigurationParser) Parse(content string) (map[string]string, error) { pps, err := properties.LoadString(content) if err != nil { diff --git a/config_center/parser/configuration_parser_test.go b/config_center/parser/configuration_parser_test.go index 7a59ea9b48b8b8d2a84735a416bcba1bb9ec8652..be2d45b25e835eb6cf6c2cf69afe2d9d69d3f90a 100644 --- a/config_center/parser/configuration_parser_test.go +++ b/config_center/parser/configuration_parser_test.go @@ -25,10 +25,65 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDefaultConfigurationParser_Parser(t *testing.T) { +func TestDefaultConfigurationParserParser(t *testing.T) { parser := &DefaultConfigurationParser{} m, err := parser.Parse("dubbo.registry.address=172.0.0.1\ndubbo.registry.name=test") assert.NoError(t, err) assert.Equal(t, 2, len(m)) assert.Equal(t, "172.0.0.1", m["dubbo.registry.address"]) } + +func TestDefaultConfigurationParserAppItemToUrls_ParserToUrls(t *testing.T) { + parser := &DefaultConfigurationParser{} + content := `configVersion: 2.7.1 +scope: application +key: org.apache.dubbo-go.mockService +enabled: true +configs: +- type: application + enabled: true + addresses: + - 0.0.0.0 + providerAddresses: [] + services: + - org.apache.dubbo-go.mockService + applications: [] + parameters: + cluster: mock1 + side: provider` + urls, err := parser.ParseToUrls(content) + assert.NoError(t, err) + assert.Equal(t, 1, len(urls)) + assert.Equal(t, "org.apache.dubbo-go.mockService", urls[0].GetParam("application", "")) + assert.Equal(t, "mock1", urls[0].GetParam("cluster", "")) + assert.Equal(t, "override", urls[0].Protocol) + assert.Equal(t, "0.0.0.0", urls[0].Location) +} + +func TestDefaultConfigurationParserServiceItemToUrls_ParserToUrls(t *testing.T) { + parser := &DefaultConfigurationParser{} + content := `configVersion: 2.7.1 +scope: notApplication +key: groupA/test:1 +enabled: true +configs: +- type: application + enabled: true + addresses: + - 0.0.0.0 + providerAddresses: [] + services: + - org.apache.dubbo-go.mockService + applications: [] + parameters: + cluster: mock1 + side: provider` + urls, err := parser.ParseToUrls(content) + assert.NoError(t, err) + assert.Equal(t, 1, len(urls)) + assert.Equal(t, "groupA", urls[0].GetParam("group", "")) + assert.Equal(t, "/test", urls[0].Path) + assert.Equal(t, "mock1", urls[0].GetParam("cluster", "")) + assert.Equal(t, "override", urls[0].Protocol) + assert.Equal(t, "0.0.0.0", urls[0].Location) +} diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index 0a1ce35306dab98363ca475cd5d1b0648e924b90..ef579eb2d11cf5f5bafb132c3e201c12ee7845c0 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -20,11 +20,9 @@ package zookeeper import ( "strings" "sync" - "time" ) import ( - "github.com/dubbogo/go-zookeeper/zk" gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" ) @@ -81,32 +79,6 @@ func newZookeeperDynamicConfiguration(url *common.URL) (*zookeeperDynamicConfigu } -func newMockZookeeperDynamicConfiguration(url *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, *zookeeperDynamicConfiguration, error) { - c := &zookeeperDynamicConfiguration{ - url: url, - rootPath: "/" + url.GetParam(constant.CONFIG_NAMESPACE_KEY, config_center.DEFAULT_GROUP) + "/config", - } - var ( - tc *zk.TestCluster - err error - ) - tc, c.client, _, err = zookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) - if err != nil { - logger.Errorf("mock zookeeper client start error ,error message is %v", err) - return tc, c, err - } - c.wg.Add(1) - go zookeeper.HandleClientRestart(c) - - c.listener = zookeeper.NewZkEventListener(c.client) - c.cacheListener = NewCacheListener(c.rootPath) - - err = c.client.Create(c.rootPath) - go c.listener.ListenServiceEvent(url, c.rootPath, c.cacheListener) - return tc, c, err - -} - func (c *zookeeperDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener, opions ...config_center.Option) { c.cacheListener.AddListener(key, listener) } diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go index 30389122a3a06ee260f2ed8b21057523137995d5..ecc3527c486fdd2aa2bc6f4d2f2adab1147e495a 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -18,6 +18,8 @@ package zookeeper import ( "fmt" + "path" + "strconv" "sync" "testing" ) @@ -30,16 +32,32 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/config_center/parser" ) -func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicConfiguration) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111") - ts, reg, err := newMockZookeeperDynamicConfiguration(®url) - reg.SetParser(&parser.DefaultConfigurationParser{}) +const ( + dubboPropertyFileName = "dubbo.properties" +) +func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicConfiguration) { + ts, err := zk.StartTestCluster(1, nil, nil) + assert.NoError(t, err) + assert.NotNil(t, ts.Servers[0]) + urlString := "registry://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port) + regurl, err := common.NewURL(urlString) + assert.NoError(t, err) + regurl.AddParam(constant.REGISTRY_TIMEOUT_KEY, "15s") + zkFactory := &zookeeperDynamicConfigurationFactory{} + reg, err := zkFactory.GetDynamicConfiguration(®url) + zreg, ok := reg.(*zookeeperDynamicConfiguration) + assert.True(t, ok) assert.NoError(t, err) + assert.True(t, zreg.IsAvailable()) + assert.Equal(t, zreg.GetUrl(), regurl) + assert.True(t, zreg.RestartCallBack()) + zreg.SetParser(&parser.DefaultConfigurationParser{}) data := ` dubbo.consumer.request_timeout=5s @@ -63,37 +81,43 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` if group != "" { - err = reg.client.Create(reg.rootPath + "/dubbo/dubbo.properties") + err = zreg.client.Create(path.Join(zreg.rootPath, "dubbo", dubboPropertyFileName)) assert.NoError(t, err) - _, err = reg.client.Conn.Set(reg.rootPath+"/dubbo/dubbo.properties", []byte(data), 0) + _, err = zreg.client.Conn.Set(path.Join(zreg.rootPath, "dubbo", dubboPropertyFileName), []byte(data), 0) assert.NoError(t, err) } else { - err = reg.client.Create(reg.rootPath + "/dubbo.properties") + err = zreg.client.Create(path.Join(zreg.rootPath, dubboPropertyFileName)) assert.NoError(t, err) - _, err = reg.client.Conn.Set(reg.rootPath+"/dubbo.properties", []byte(data), 0) + _, err = zreg.client.Conn.Set(path.Join(zreg.rootPath, dubboPropertyFileName), []byte(data), 0) assert.NoError(t, err) } - return ts, reg + return ts, zreg } -func Test_GetConfig(t *testing.T) { +func TestGetConfig(t *testing.T) { ts, reg := initZkData("dubbo", t) defer ts.Stop() - configs, err := reg.GetProperties("dubbo.properties", config_center.WithGroup("dubbo")) + configs, err := reg.GetProperties(dubboPropertyFileName, config_center.WithGroup("dubbo")) assert.NoError(t, err) m, err := reg.Parser().Parse(configs) assert.NoError(t, err) assert.Equal(t, "5s", m["dubbo.consumer.request_timeout"]) + configs, err = reg.GetProperties(dubboPropertyFileName) + assert.Error(t, err) + configs, err = reg.GetInternalProperty(dubboPropertyFileName) + assert.Error(t, err) + configs, err = reg.GetRule(dubboPropertyFileName) + assert.Error(t, err) } -func Test_AddListener(t *testing.T) { +func TestAddListener(t *testing.T) { ts, reg := initZkData("", t) defer ts.Stop() listener := &mockDataListener{} - reg.AddListener("dubbo.properties", listener) + reg.AddListener(dubboPropertyFileName, listener) listener.wg.Add(1) data := ` dubbo.consumer.request_timeout=3s @@ -116,17 +140,17 @@ func Test_AddListener(t *testing.T) { dubbo.service.com.ikurento.user.UserProvider.warmup=100 dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` - _, err := reg.client.Conn.Set(reg.rootPath+"/dubbo.properties", []byte(data), 1) + _, err := reg.client.Conn.Set(path.Join(reg.rootPath, dubboPropertyFileName), []byte(data), 1) assert.NoError(t, err) listener.wg.Wait() - assert.Equal(t, "dubbo.properties", listener.event) + assert.Equal(t, dubboPropertyFileName, listener.event) } -func Test_RemoveListener(t *testing.T) { +func TestRemoveListener(t *testing.T) { ts, reg := initZkData("", t) defer ts.Stop() listener := &mockDataListener{} - reg.AddListener("dubbo.properties", listener) + reg.AddListener(dubboPropertyFileName, listener) listener.wg.Add(1) data := ` dubbo.consumer.request_timeout=3s @@ -149,15 +173,15 @@ func Test_RemoveListener(t *testing.T) { dubbo.service.com.ikurento.user.UserProvider.warmup=100 dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` - reg.RemoveListener("dubbo.properties", listener) + reg.RemoveListener(dubboPropertyFileName, listener) listener.wg.Done() - _, err := reg.client.Conn.Set(reg.rootPath+"/dubbo.properties", []byte(data), 1) + _, err := reg.client.Conn.Set(path.Join(reg.rootPath, dubboPropertyFileName), []byte(data), 1) assert.NoError(t, err) listener.wg.Wait() assert.Equal(t, "", listener.event) } -func TestZookeeperDynamicConfiguration_PublishConfig(t *testing.T) { +func TestZookeeperDynamicConfigurationPublishConfig(t *testing.T) { value := "Test Data" customGroup := "Custom Group" key := "myKey" diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go index 122dfaf4f268a706151de6acdaa78bb46e59f8fb..747c4be352add3f549eaf02e83da6442a8a84c6a 100644 --- a/config_center/zookeeper/listener.go +++ b/config_center/zookeeper/listener.go @@ -33,12 +33,12 @@ type CacheListener struct { rootPath string } -// NewCacheListener ... +// NewCacheListener creates a new CacheListener func NewCacheListener(rootPath string) *CacheListener { return &CacheListener{rootPath: rootPath} } -// AddListener ... +// AddListener will add a listener if loaded func (l *CacheListener) AddListener(key string, listener config_center.ConfigurationListener) { // reference from https://stackoverflow.com/questions/34018908/golang-why-dont-we-have-a-set-datastructure @@ -50,7 +50,7 @@ func (l *CacheListener) AddListener(key string, listener config_center.Configura } } -// RemoveListener ... +// RemoveListener will delete a listener if loaded func (l *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) { listeners, loaded := l.keyListeners.Load(key) if loaded { @@ -58,7 +58,7 @@ func (l *CacheListener) RemoveListener(key string, listener config_center.Config } } -// DataChange ... +// DataChange changes all listeners' event func (l *CacheListener) DataChange(event remoting.Event) bool { if event.Content == "" { //meanings new node diff --git a/doc/pic/arch/dubbo-go-ext.png b/doc/pic/arch/dubbo-go-ext.png index a5285c95570afa13212f7ee6eac5510f20243c3c..d065ef4d8e28e507b2ee36fd8c6c6928ab7c9b5d 100644 Binary files a/doc/pic/arch/dubbo-go-ext.png and b/doc/pic/arch/dubbo-go-ext.png differ diff --git a/filter/access_key.go b/filter/access_key.go index 40d4157b31d13ed8fd8b1ba8cc9d16b53638ac6a..4801d64fe46461424c5dac5aef2eebc719ee19c4 100644 --- a/filter/access_key.go +++ b/filter/access_key.go @@ -22,6 +22,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// AccessKeyPair stores the basic attributes for authentication. type AccessKeyPair struct { AccessKey string `yaml:"accessKey" json:"accessKey,omitempty" property:"accessKey"` SecretKey string `yaml:"secretKey" json:"secretKey,omitempty" property:"secretKey"` @@ -31,8 +32,7 @@ type AccessKeyPair struct { Options string `yaml:"options" json:"options,omitempty" property:"options"` } -// AccessKeyStorage -// This SPI Extension support us to store our AccessKeyPair or load AccessKeyPair from other +// AccessKeyStorage supports us to store our AccessKeyPair or load AccessKeyPair from other // storage, such as filesystem. type AccessKeyStorage interface { GetAccessKeyPair(protocol.Invocation, *common.URL) *AccessKeyPair diff --git a/filter/authenticator.go b/filter/authenticator.go index ac2c8601d4a0d2e5ae3aed56415d9d23856cb502..71f659d4918293e2eb05b8b7a72b6db1cece42ba 100644 --- a/filter/authenticator.go +++ b/filter/authenticator.go @@ -22,14 +22,13 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Authenticator +// Authenticator defines how an Authenticator works. +// Custom Authenticator must be set by calling auth.SetAuthenticator before use. type Authenticator interface { - // Sign - // give a sign to request + // Sign adds signature to the invocation Sign(protocol.Invocation, *common.URL) error - // Authenticate - // verify the signature of the request is valid or not + // Authenticate verifies the signature of the request Authenticate(protocol.Invocation, *common.URL) error } diff --git a/filter/filter.go b/filter/filter.go index c069510498c7ac68b2bb2169dfe7132a4ef63229..d20ca72c345c6812f4bce6df5dbaf683429a9874 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -24,9 +24,11 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Filter +// Filter interface defines the functions of a filter // Extension - Filter type Filter interface { + // Invoke is the core function of a filter, it determins the process of the filter Invoke(context.Context, protocol.Invoker, protocol.Invocation) protocol.Result + // OnResponse updates the results from Invoke and then returns the modified results. OnResponse(context.Context, protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result } diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go index fbfe7565170c7df468f755a4bd1aadde166a79c1..49cdc2287c28ae0cbbd0fcab3700536595bb0f5e 100644 --- a/filter/filter_impl/access_log_filter.go +++ b/filter/filter_impl/access_log_filter.go @@ -71,14 +71,18 @@ func init() { * * the value of "accesslog" can be "true" or "default" too. * If the value is one of them, the access log will be record in log file which defined in log.yml + * AccessLogFilter is designed to be singleton */ type AccessLogFilter struct { logChan chan AccessLogData } -// Invoke ... +// Invoke will check whether user wants to use this filter. +// If we find the value of key constant.ACCESS_LOG_KEY, we will log the invocation info func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { accessLog := invoker.GetUrl().GetParam(constant.ACCESS_LOG_KEY, "") + + // the user do not if len(accessLog) > 0 { accessLogData := AccessLogData{data: ef.buildAccessLogData(invoker, invocation), accessLog: accessLog} ef.logIntoChannel(accessLogData) @@ -86,7 +90,7 @@ func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, return invoker.Invoke(ctx, invocation) } -// it won't block the invocation +// logIntoChannel won't block the invocation func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { select { case ef.logChan <- accessLogData: @@ -97,6 +101,7 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { } } +// buildAccessLogData builds the access log data func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string { dataMap := make(map[string]string, 16) attachments := invocation.Attachments() @@ -130,11 +135,12 @@ func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation pro return dataMap } -// OnResponse ... +// OnResponse do nothing func (ef *AccessLogFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } +// writeLogToFile actually write the logs into file func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) { accessLog := data.accessLog if isDefault(accessLog) { @@ -156,6 +162,12 @@ func (ef *AccessLogFilter) writeLogToFile(data AccessLogData) { } } +// openLogFile will open the log file with append mode. +// If the file is not found, it will create the file. +// Actually, the accessLog is the filename +// You may find out that, once we want to write access log into log file, +// we open the file again and again. +// It needs to be optimized. func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { logFile, err := os.OpenFile(accessLog, os.O_CREATE|os.O_APPEND|os.O_RDWR, LogFileMode) if err != nil { @@ -169,6 +181,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { return nil, err } last := fileInfo.ModTime().Format(FileDateFormat) + + // this is confused. + // for example, if the last = '2020-03-04' + // and today is '2020-03-05' + // we will create one new file to log access data + // By this way, we can split the access log based on days. if now != last { err = os.Rename(fileInfo.Name(), fileInfo.Name()+"."+now) if err != nil { @@ -180,11 +198,12 @@ func (ef *AccessLogFilter) openLogFile(accessLog string) (*os.File, error) { return logFile, err } +// isDefault check whether accessLog == true or accessLog == default func isDefault(accessLog string) bool { return strings.EqualFold("true", accessLog) || strings.EqualFold("default", accessLog) } -// GetAccessLogFilter ... +// GetAccessLogFilter return the instance of AccessLogFilter func GetAccessLogFilter() filter.Filter { accessLogFilter := &AccessLogFilter{logChan: make(chan AccessLogData, LogMaxBuffer)} go func() { @@ -195,12 +214,13 @@ func GetAccessLogFilter() filter.Filter { return accessLogFilter } -// AccessLogData ... +// AccessLogData defines the data that will be log into file type AccessLogData struct { accessLog string data map[string]string } +// toLogMessage convert the AccessLogData to String func (ef *AccessLogData) toLogMessage() string { builder := strings.Builder{} builder.WriteString("[") diff --git a/filter/filter_impl/active_filter.go b/filter/filter_impl/active_filter.go index 23f2c8e25609dff89392107251715fe6f5175f09..795de968b57207830cc15fc8a0476bfdc3d2cb43 100644 --- a/filter/filter_impl/active_filter.go +++ b/filter/filter_impl/active_filter.go @@ -39,11 +39,11 @@ func init() { extension.SetFilter(active, GetActiveFilter) } -// ActiveFilter ... +// ActiveFilter tracks the requests status type ActiveFilter struct { } -// Invoke ... +// Invoke starts to record the requests status func (ef *ActiveFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { logger.Infof("invoking active filter. %v,%v", invocation.MethodName(), len(invocation.Arguments())) invocation.(*invocation2.RPCInvocation).SetAttachments(dubboInvokeStartTime, strconv.FormatInt(protocol.CurrentTimeMillis(), 10)) @@ -51,7 +51,7 @@ func (ef *ActiveFilter) Invoke(ctx context.Context, invoker protocol.Invoker, in return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse update the active count base on the request result. func (ef *ActiveFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { startTime, err := strconv.ParseInt(invocation.(*invocation2.RPCInvocation).AttachmentsByKey(dubboInvokeStartTime, "0"), 10, 64) if err != nil { @@ -64,7 +64,7 @@ func (ef *ActiveFilter) OnResponse(ctx context.Context, result protocol.Result, return result } -// GetActiveFilter ... +// GetActiveFilter creates ActiveFilter instance func GetActiveFilter() filter.Filter { return &ActiveFilter{} } diff --git a/filter/filter_impl/auth/accesskey_storage.go b/filter/filter_impl/auth/accesskey_storage.go index 5adb9d9ee37329228d1d02dc8802deeede68d327..90d3efb5ad897b874c89745740637804808b5133 100644 --- a/filter/filter_impl/auth/accesskey_storage.go +++ b/filter/filter_impl/auth/accesskey_storage.go @@ -25,13 +25,11 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// DefaultAccesskeyStorage -// The default implementation of AccesskeyStorage +// DefaultAccesskeyStorage is the default implementation of AccesskeyStorage type DefaultAccesskeyStorage struct { } -// GetAccessKeyPair -// get AccessKeyPair from url by the key "accessKeyId" and "secretAccessKey" +// GetAccessKeyPair retrieves AccessKeyPair from url by the key "accessKeyId" and "secretAccessKey" func (storage *DefaultAccesskeyStorage) GetAccessKeyPair(invocation protocol.Invocation, url *common.URL) *filter.AccessKeyPair { return &filter.AccessKeyPair{ AccessKey: url.GetParam(constant.ACCESS_KEY_ID_KEY, ""), @@ -43,6 +41,7 @@ func init() { extension.SetAccesskeyStorages(constant.DEFAULT_ACCESS_KEY_STORAGE, GetDefaultAccesskeyStorage) } +// GetDefaultAccesskeyStorage initiates an empty DefaultAccesskeyStorage func GetDefaultAccesskeyStorage() filter.AccessKeyStorage { return &DefaultAccesskeyStorage{} } diff --git a/filter/filter_impl/auth/consumer_sign.go b/filter/filter_impl/auth/consumer_sign.go index 062744771acf8ccd505265875a103d24afeb06af..945cf3e6e7e728042b5422174162dd5aded50361 100644 --- a/filter/filter_impl/auth/consumer_sign.go +++ b/filter/filter_impl/auth/consumer_sign.go @@ -29,8 +29,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ConsumerSignFilter -// This filter is working for signing the request on consumer side +// ConsumerSignFilter signs the request on consumer side type ConsumerSignFilter struct { } @@ -38,6 +37,7 @@ func init() { extension.SetFilter(constant.CONSUMER_SIGN_FILTER, getConsumerSignFilter) } +// Invoke retrieves the configured Authenticator to add signature to invocation func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { logger.Infof("invoking ConsumerSign filter.") url := invoker.GetUrl() @@ -52,6 +52,7 @@ func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invo return invoker.Invoke(ctx, invocation) } +// OnResponse dummy process, returns the result directly func (csf *ConsumerSignFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } diff --git a/filter/filter_impl/auth/default_authenticator.go b/filter/filter_impl/auth/default_authenticator.go index 2b8d55927807407f350ecc6cfc28b6913a6d1a81..5b86fc148e42b6364bcb0752c31bfbbc3cfa2b9d 100644 --- a/filter/filter_impl/auth/default_authenticator.go +++ b/filter/filter_impl/auth/default_authenticator.go @@ -37,13 +37,11 @@ func init() { extension.SetAuthenticator(constant.DEFAULT_AUTHENTICATOR, GetDefaultAuthenticator) } -// DefaultAuthenticator -// The default implemetation of Authenticator +// DefaultAuthenticator is the default implementation of Authenticator type DefaultAuthenticator struct { } -// Sign -// add the signature for the invocation +// Sign adds the signature to the invocation func (authenticator *DefaultAuthenticator) Sign(invocation protocol.Invocation, url *common.URL) error { currentTimeMillis := strconv.Itoa(int(time.Now().Unix() * 1000)) @@ -84,8 +82,7 @@ func getSignature(url *common.URL, invocation protocol.Invocation, secrectKey st return signature, nil } -// Authenticate -// This method verifies whether the signature sent by the requester is correct +// Authenticate verifies whether the signature sent by the requester is correct func (authenticator *DefaultAuthenticator) Authenticate(invocation protocol.Invocation, url *common.URL) error { accessKeyId := invocation.AttachmentsByKey(constant.AK_KEY, "") @@ -122,6 +119,7 @@ func getAccessKeyPair(invocation protocol.Invocation, url *common.URL) (*filter. } } +// GetDefaultAuthenticator creates an empty DefaultAuthenticator instance func GetDefaultAuthenticator() filter.Authenticator { return &DefaultAuthenticator{} } diff --git a/filter/filter_impl/auth/provider_auth.go b/filter/filter_impl/auth/provider_auth.go index 0d5772e5508894111a88443bfe2d1b02ebfac54a..d5f5db300d4e7c94978d5d52e32f741f7d27bb48 100644 --- a/filter/filter_impl/auth/provider_auth.go +++ b/filter/filter_impl/auth/provider_auth.go @@ -29,8 +29,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ProviderAuthFilter -// This filter is used to verify the correctness of the signature on provider side +// ProviderAuthFilter verifies the correctness of the signature on provider side type ProviderAuthFilter struct { } @@ -38,6 +37,7 @@ func init() { extension.SetFilter(constant.PROVIDER_AUTH_FILTER, getProviderAuthFilter) } +// Invoke retrieves the configured Authenticator to verify the signature in an invocation func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { logger.Infof("invoking providerAuth filter.") url := invoker.GetUrl() @@ -55,6 +55,7 @@ func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invo return invoker.Invoke(ctx, invocation) } +// OnResponse dummy process, returns the result directly func (paf *ProviderAuthFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } diff --git a/filter/filter_impl/auth/sign_util.go b/filter/filter_impl/auth/sign_util.go index 043a549a849dde66712e1bef389dd91a024660df..45170bb8117284275a87a3a57d14ce68d6cc4e9c 100644 --- a/filter/filter_impl/auth/sign_util.go +++ b/filter/filter_impl/auth/sign_util.go @@ -26,12 +26,12 @@ import ( "strings" ) -// Sign -// get a signature string with given information, such as metadata or parameters +// Sign gets a signature string with given bytes func Sign(metadata, key string) string { return doSign([]byte(metadata), key) } +// SignWithParams returns a signature with giving params and metadata. func SignWithParams(params []interface{}, metadata, key string) (string, error) { if params == nil || len(params) == 0 { return Sign(metadata, key), nil @@ -61,6 +61,7 @@ func doSign(bytes []byte, key string) string { return base64.URLEncoding.EncodeToString(signature) } +// IsEmpty verify whether the inputted string is empty func IsEmpty(s string, allowSpace bool) bool { if len(s) == 0 { return true diff --git a/filter/filter_impl/echo_filter.go b/filter/filter_impl/echo_filter.go index a12800a21a8ebe4545b4a8b5bd0f8a30c1462105..7da5ec7029ea698b1bf1a14ad36123fbec3aacf7 100644 --- a/filter/filter_impl/echo_filter.go +++ b/filter/filter_impl/echo_filter.go @@ -38,13 +38,13 @@ func init() { extension.SetFilter(ECHO, GetFilter) } -// EchoFilter +// EchoFilter health check // RPCService need a Echo method in consumer, if you want to use EchoFilter // eg: // Echo func(ctx context.Context, arg interface{}, rsp *Xxx) error type EchoFilter struct{} -// Invoke ... +// Invoke response to the callers with its first argument. func (ef *EchoFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { logger.Infof("invoking echo filter.") logger.Debugf("%v,%v", invocation.MethodName(), len(invocation.Arguments())) @@ -58,7 +58,7 @@ func (ef *EchoFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invo return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (ef *EchoFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { diff --git a/filter/filter_impl/execute_limit_filter.go b/filter/filter_impl/execute_limit_filter.go index 434c378045456eb13317e0a48630ebd33f244c05..bfc5096ca089867f6e6234089e387d3f9b48a3aa 100644 --- a/filter/filter_impl/execute_limit_filter.go +++ b/filter/filter_impl/execute_limit_filter.go @@ -45,9 +45,8 @@ func init() { extension.SetFilter(name, GetExecuteLimitFilter) } +// ExecuteLimitFilter will limit the number of in-progress request and it's thread-safe. /** - * ExecuteLimitFilter - * The filter will limit the number of in-progress request and it's thread-safe. * example: * "UserProvider": * registry: "hangzhouzk" @@ -80,7 +79,7 @@ type ExecuteState struct { concurrentCount int64 } -// Invoke ... +// Invoke judges whether the current processing requests over the threshold func (ef *ExecuteLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { methodConfigPrefix := "methods." + invocation.MethodName() + "." ivkURL := invoker.GetUrl() @@ -122,7 +121,7 @@ func (ef *ExecuteLimitFilter) Invoke(ctx context.Context, invoker protocol.Invok return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (ef *ExecuteLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } @@ -138,7 +137,7 @@ func (state *ExecuteState) decrease() { var executeLimitOnce sync.Once var executeLimitFilter *ExecuteLimitFilter -// GetExecuteLimitFilter ... +// GetExecuteLimitFilter returns the singleton ExecuteLimitFilter instance func GetExecuteLimitFilter() filter.Filter { executeLimitOnce.Do(func() { executeLimitFilter = &ExecuteLimitFilter{ diff --git a/filter/filter_impl/generic_filter.go b/filter/filter_impl/generic_filter.go index 9bc131ef8903942b84df2b8fc14fd11143d1a7b6..3f4d714e6b0cbdf48f5e1afce3222a18857041f9 100644 --- a/filter/filter_impl/generic_filter.go +++ b/filter/filter_impl/generic_filter.go @@ -50,7 +50,7 @@ func init() { // GenericFilter ... type GenericFilter struct{} -// Invoke ... +// Invoke turns the parameters to map for generic method func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 { oldArguments := invocation.Arguments() @@ -73,13 +73,13 @@ func (ef *GenericFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (ef *GenericFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } -// GetGenericFilter ... +// GetGenericFilter returns GenericFilter instance func GetGenericFilter() filter.Filter { return &GenericFilter{} } diff --git a/filter/filter_impl/graceful_shutdown_filter.go b/filter/filter_impl/graceful_shutdown_filter.go index 95e625b2d56895a4d57823e4e0e2e7d1d5e90a08..4a4e8ce466edabe82815b99244404ac024d73b26 100644 --- a/filter/filter_impl/graceful_shutdown_filter.go +++ b/filter/filter_impl/graceful_shutdown_filter.go @@ -53,6 +53,7 @@ type gracefulShutdownFilter struct { shutdownConfig *config.ShutdownConfig } +// Invoke adds the requests count and block the new requests if application is closing func (gf *gracefulShutdownFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { if gf.rejectNewRequest() { logger.Info("The application is closing, new request will be rejected.") @@ -62,6 +63,7 @@ func (gf *gracefulShutdownFilter) Invoke(ctx context.Context, invoker protocol.I return invoker.Invoke(ctx, invocation) } +// OnResponse reduces the number of active processes then return the process result func (gf *gracefulShutdownFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { atomic.AddInt32(&gf.activeCount, -1) // although this isn't thread safe, it won't be a problem if the gf.rejectNewRequest() is true. diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go index 9fd97b57b677c9aa8ec492151df9aace6dc78b62..711ef71c44192c5a1d76783a3b3d4cbd0b97632c 100644 --- a/filter/filter_impl/hystrix_filter.go +++ b/filter/filter_impl/hystrix_filter.go @@ -55,14 +55,14 @@ var ( //The filter in the server end of dubbo-go can't get the invoke result for now, //this filter ONLY works in CLIENT end (consumer side) temporarily -//Only after the callService logic is integrated into the filter chain of server end can this filter be used, +//Only after the callService logic is integrated into the filter chain of server end then the filter can be used, //which will be done soon func init() { extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer) extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider) } -// HystrixFilterError ... +// HystrixFilterError implements error interface type HystrixFilterError struct { err error failByHystrix bool @@ -72,12 +72,12 @@ func (hfError *HystrixFilterError) Error() string { return hfError.err.Error() } -// FailByHystrix ... +// FailByHystrix returns whether the fails causing by Hystrix func (hfError *HystrixFilterError) FailByHystrix() bool { return hfError.failByHystrix } -// NewHystrixFilterError ... +// NewHystrixFilterError return a HystrixFilterError instance func NewHystrixFilterError(err error, failByHystrix bool) error { return &HystrixFilterError{ err: err, @@ -92,7 +92,7 @@ type HystrixFilter struct { ifNewMap sync.Map } -// Invoke ... +// Invoke is an implentation of filter, provides Hystrix pattern latency and fault tolerance func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { cmdName := fmt.Sprintf("%s&method=%s", invoker.GetUrl().Key(), invocation.MethodName()) @@ -124,7 +124,7 @@ func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i _, _, err := hystrix.GetCircuit(cmdName) configLoadMutex.RUnlock() if err != nil { - logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: ", cmdName, err) + logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: %+v", cmdName, err) return invoker.Invoke(ctx, invocation) } logger.Infof("[Hystrix Filter]Using hystrix filter: %s", cmdName) @@ -154,12 +154,12 @@ func (hf *HystrixFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i return result } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (hf *HystrixFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } -// GetHystrixFilterConsumer ... +// GetHystrixFilterConsumer returns HystrixFilter instance for consumer func GetHystrixFilterConsumer() filter.Filter { //When first called, load the config in consumerConfigOnce.Do(func() { @@ -170,7 +170,7 @@ func GetHystrixFilterConsumer() filter.Filter { return &HystrixFilter{COrP: true} } -// GetHystrixFilterProvider ... +// GetHystrixFilterProvider returns HystrixFilter instance for provider func GetHystrixFilterProvider() filter.Filter { providerConfigOnce.Do(func() { if err := initHystrixConfigProvider(); err != nil { diff --git a/filter/filter_impl/token_filter.go b/filter/filter_impl/token_filter.go index 8ec3929b6ddc8dcfa430204cd22d2f6d297c59d3..23742c66e94d9ecfc09d004441a54aad86ef049e 100644 --- a/filter/filter_impl/token_filter.go +++ b/filter/filter_impl/token_filter.go @@ -42,10 +42,10 @@ func init() { extension.SetFilter(TOKEN, GetTokenFilter) } -// TokenFilter ... +// TokenFilter will verify if the token is valid type TokenFilter struct{} -// Invoke ... +// Invoke verifies the incoming token with the service configured token func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { invokerTkn := invoker.GetUrl().GetParam(constant.TOKEN_KEY, "") if len(invokerTkn) > 0 { @@ -61,7 +61,7 @@ func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, inv return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (tf *TokenFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go index d7a7f20a5d7648f6ee18fd26f041acb313dd97fe..b8b297e67267640a1c294541afdd4e062bfebb25 100644 --- a/filter/filter_impl/token_filter_test.go +++ b/filter/filter_impl/token_filter_test.go @@ -53,10 +53,10 @@ func TestTokenFilter_Invoke(t *testing.T) { func TestTokenFilter_InvokeEmptyToken(t *testing.T) { filter := GetTokenFilter() - url := common.URL{} + testUrl := common.URL{} attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "ori_key" - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -64,23 +64,23 @@ func TestTokenFilter_InvokeEmptyToken(t *testing.T) { func TestTokenFilter_InvokeEmptyAttach(t *testing.T) { filter := GetTokenFilter() - url := common.NewURLWithOptions( + testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } func TestTokenFilter_InvokeNotEqual(t *testing.T) { filter := GetTokenFilter() - url := common.NewURLWithOptions( + testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "err_key" result := filter.Invoke(context.Background(), - protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } diff --git a/filter/filter_impl/tps/tps_limit_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go index a9c2ac15a417ffa6ff8f5b8d78d5c6a94877db30..7419a4576122d4db334969b0711666b5b2816e60 100644 --- a/filter/filter_impl/tps/tps_limit_fix_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go @@ -39,8 +39,8 @@ func init() { extension.SetTpsLimitStrategy(constant.DEFAULT_KEY, creator) } +// FixedWindowTpsLimitStrategyImpl implements the TPS limit strategy base on requests count during the interval /** - * FixedWindowTpsLimitStrategyImpl * It's the same as default implementation in Java * It's not a thread-safe implementation. * It you want to use the thread-safe implementation, please use ThreadSafeFixedWindowTpsLimitStrategyImpl @@ -65,7 +65,8 @@ type FixedWindowTpsLimitStrategyImpl struct { timestamp int64 } -// IsAllowable ... +// IsAllowable determines if the requests over the TPS limit within the interval. +// It is not thread-safe. func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool { current := time.Now().UnixNano() @@ -82,6 +83,7 @@ func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool { type fixedWindowStrategyCreator struct{} +// Create returns a FixedWindowTpsLimitStrategyImpl instance with pre-configured limit rate and interval func (creator *fixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { return &FixedWindowTpsLimitStrategyImpl{ rate: int32(rate), diff --git a/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go index a781cc7bfbf297d0b9cf84ca0aa9dcfbbef7e14b..cbbba19fff65be222cb895dcbe9b2e4d02082985 100644 --- a/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go @@ -32,8 +32,8 @@ func init() { extension.SetTpsLimitStrategy("slidingWindow", &slidingWindowStrategyCreator{}) } +// SlidingWindowTpsLimitStrategyImpl implements a thread-safe TPS limit strategy base on requests count. /** - * SlidingWindowTpsLimitStrategyImpl * it's thread-safe. * "UserProvider": * registry: "hangzhouzk" @@ -54,7 +54,8 @@ type SlidingWindowTpsLimitStrategyImpl struct { queue *list.List } -// IsAllowable ... +// IsAllowable determins whether the number of requests within the time window overs the threshold +// It is thread-safe. func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool { impl.mutex.Lock() defer impl.mutex.Unlock() @@ -84,6 +85,7 @@ func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool { type slidingWindowStrategyCreator struct{} +// Create returns SlidingWindowTpsLimitStrategyImpl instance with configured limit rate and interval func (creator *slidingWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { return &SlidingWindowTpsLimitStrategyImpl{ rate: rate, diff --git a/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go index 16624836e6397df5adda3f2aa5a80966721a97fb..f78cd8211cd076dcab84759e2bf784d080c72a1c 100644 --- a/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go @@ -32,10 +32,9 @@ func init() { }) } +// ThreadSafeFixedWindowTpsLimitStrategyImpl is the thread-safe implementation. +// It's also a thread-safe decorator of FixedWindowTpsLimitStrategyImpl /** - * ThreadSafeFixedWindowTpsLimitStrategyImpl - * it's the thread-safe implementation. - * Also, it's a thread-safe decorator of FixedWindowTpsLimitStrategyImpl * "UserProvider": * registry: "hangzhouzk" * protocol : "dubbo" @@ -53,7 +52,7 @@ type ThreadSafeFixedWindowTpsLimitStrategyImpl struct { fixedWindow *FixedWindowTpsLimitStrategyImpl } -// IsAllowable ... +// IsAllowable implements thread-safe then run the FixedWindowTpsLimitStrategy func (impl *ThreadSafeFixedWindowTpsLimitStrategyImpl) IsAllowable() bool { impl.mutex.Lock() defer impl.mutex.Unlock() @@ -64,6 +63,7 @@ type threadSafeFixedWindowStrategyCreator struct { fixedWindowStrategyCreator *fixedWindowStrategyCreator } +// Create returns ThreadSafeFixedWindowTpsLimitStrategyImpl instance func (creator *threadSafeFixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { fixedWindowStrategy := creator.fixedWindowStrategyCreator.Create(rate, interval).(*FixedWindowTpsLimitStrategyImpl) return &ThreadSafeFixedWindowTpsLimitStrategyImpl{ diff --git a/filter/filter_impl/tps/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go index 7fe8de9237b82415a09083c2be59df5e232ecaf0..5761579a38a22500d54193a9564170cc0215cf0f 100644 --- a/filter/filter_impl/tps/tps_limiter_method_service.go +++ b/filter/filter_impl/tps/tps_limiter_method_service.go @@ -44,9 +44,8 @@ func init() { extension.SetTpsLimiter(name, GetMethodServiceTpsLimiter) } +// MethodServiceTpsLimiterImpl allows developer to config both method-level and service-level tps limiter. /** - * MethodServiceTpsLimiterImpl - * This implementation allows developer to config both method-level and service-level tps limiter. * for example: * "UserProvider": * registry: "hangzhouzk" @@ -115,7 +114,12 @@ type MethodServiceTpsLimiterImpl struct { tpsState *concurrent.Map } -// IsAllowable ... +// IsAllowable based on method-level and service-level. +// The method-level has high priority which means that if there is any rate limit configuration for the method, +// the service-level rate limit strategy will be ignored. +// The key point is how to keep thread-safe +// This implementation use concurrent map + loadOrStore to make implementation thread-safe +// You can image that even multiple threads create limiter, but only one could store the limiter into tpsState func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool { methodConfigPrefix := "methods." + invocation.MethodName() + "." @@ -123,23 +127,30 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio methodLimitRateConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_RATE_KEY, "") methodIntervalConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_INTERVAL_KEY, "") + // service-level tps limit limitTarget := url.ServiceKey() // method-level tps limit if len(methodIntervalConfig) > 0 || len(methodLimitRateConfig) > 0 { + // it means that if the method-level rate limit exist, we will use method-level rate limit strategy limitTarget = limitTarget + "#" + invocation.MethodName() } + // looking up the limiter from 'cache' limitState, found := limiter.tpsState.Load(limitTarget) if found { + // the limiter has been cached, we return its result return limitState.(filter.TpsLimitStrategy).IsAllowable() } + // we could not find the limiter, and try to create one. + limitRate := getLimitConfig(methodLimitRateConfig, url, invocation, constant.TPS_LIMIT_RATE_KEY, constant.DEFAULT_TPS_LIMIT_RATE) if limitRate < 0 { + // the limitTarget is not necessary to be limited. return true } @@ -150,13 +161,20 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio panic(fmt.Sprintf("The interval must be positive, please check your configuration! url: %s", url.String())) } + // find the strategy config and then create one limitStrategyConfig := url.GetParam(methodConfigPrefix+constant.TPS_LIMIT_STRATEGY_KEY, url.GetParam(constant.TPS_LIMIT_STRATEGY_KEY, constant.DEFAULT_KEY)) limitStateCreator := extension.GetTpsLimitStrategyCreator(limitStrategyConfig) + + // we using loadOrStore to ensure thread-safe limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator.Create(int(limitRate), int(limitInterval))) + return limitState.(filter.TpsLimitStrategy).IsAllowable() } +// getLimitConfig will try to fetch the configuration from url. +// If we can convert the methodLevelConfig to int64, return; +// Or, we will try to look up server-level configuration and then convert it to int64 func getLimitConfig(methodLevelConfig string, url common.URL, invocation protocol.Invocation, @@ -172,6 +190,8 @@ func getLimitConfig(methodLevelConfig string, return result } + // actually there is no method-level configuration, so we use the service-level configuration + result, err := strconv.ParseInt(url.GetParam(configKey, defaultVal), 0, 0) if err != nil { @@ -183,7 +203,7 @@ func getLimitConfig(methodLevelConfig string, var methodServiceTpsLimiterInstance *MethodServiceTpsLimiterImpl var methodServiceTpsLimiterOnce sync.Once -// GetMethodServiceTpsLimiter ... +// GetMethodServiceTpsLimiter will return an MethodServiceTpsLimiterImpl instance. func GetMethodServiceTpsLimiter() filter.TpsLimiter { methodServiceTpsLimiterOnce.Do(func() { methodServiceTpsLimiterInstance = &MethodServiceTpsLimiterImpl{ diff --git a/filter/filter_impl/tps_limit_filter.go b/filter/filter_impl/tps_limit_filter.go index fa78288f9678d67d0eb0d025a83b75493f7fda80..ea1e3bc15e1952799227d712db114ff790527720 100644 --- a/filter/filter_impl/tps_limit_filter.go +++ b/filter/filter_impl/tps_limit_filter.go @@ -39,8 +39,8 @@ func init() { extension.SetFilter(TpsLimitFilterKey, GetTpsLimitFilter) } +// TpsLimitFilter filters the requests by TPS /** - * TpsLimitFilter * if you wish to use the TpsLimiter, please add the configuration into your service provider configuration: * for example: * "UserProvider": @@ -56,7 +56,7 @@ func init() { type TpsLimitFilter struct { } -// Invoke ... +// Invoke gets the configured limter to impose TPS limiting func (t TpsLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { url := invoker.GetUrl() tpsLimiter := url.GetParam(constant.TPS_LIMITER_KEY, "") @@ -72,13 +72,13 @@ func (t TpsLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, in return invoker.Invoke(ctx, invocation) } -// OnResponse ... +// OnResponse dummy process, returns the result directly func (t TpsLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } -// GetTpsLimitFilter ... +// GetTpsLimitFilter returns an TpsLimitFilter instance. func GetTpsLimitFilter() filter.Filter { return &TpsLimitFilter{} } diff --git a/filter/handler/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go index 0f9003c7df2165a2f3a364a5afc47f578db1d243..52ac1765f78172c0062de8884198e759b8d494ca 100644 --- a/filter/handler/rejected_execution_handler_only_log.go +++ b/filter/handler/rejected_execution_handler_only_log.go @@ -36,6 +36,7 @@ const ( ) func init() { + // this implementation is the the default implementation of RejectedExecutionHandler extension.SetRejectedExecutionHandler(HandlerName, GetOnlyLogRejectedExecutionHandler) extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, GetOnlyLogRejectedExecutionHandler) } @@ -43,8 +44,8 @@ func init() { var onlyLogHandlerInstance *OnlyLogRejectedExecutionHandler var onlyLogHandlerOnce sync.Once +// OnlyLogRejectedExecutionHandler implements the RejectedExecutionHandler /** - * OnlyLogRejectedExecutionHandler * This implementation only logs the invocation info. * it always return en error inside the result. * "UserProvider": @@ -56,11 +57,12 @@ var onlyLogHandlerOnce sync.Once * tps.limit.rejected.handler: "default" or "log" * methods: * - name: "GetUser" + * OnlyLogRejectedExecutionHandler is designed to be singleton */ type OnlyLogRejectedExecutionHandler struct { } -// RejectedExecution ... +// RejectedExecution will do nothing, it only log the invocation. func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, _ protocol.Invocation) protocol.Result { @@ -68,7 +70,7 @@ func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL return &protocol.RPCResult{} } -// GetOnlyLogRejectedExecutionHandler ... +// GetOnlyLogRejectedExecutionHandler will return the instance of OnlyLogRejectedExecutionHandler func GetOnlyLogRejectedExecutionHandler() filter.RejectedExecutionHandler { onlyLogHandlerOnce.Do(func() { onlyLogHandlerInstance = &OnlyLogRejectedExecutionHandler{} diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go index caeea1db6631d0968fd58f59f9577ee9272f3ca0..3d1e1c1e641a836411ce0f71f97acf5d5a55f6d1 100644 --- a/filter/rejected_execution_handler.go +++ b/filter/rejected_execution_handler.go @@ -22,8 +22,8 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// RejectedExecutionHandler defines the handler to handle exceptions from invoking filters. /** - * RejectedExecutionHandler * If the invocation cannot pass any validation in filter, like ExecuteLimitFilter and TpsLimitFilter, * the implementation will be used. * The common case is that sometimes you want to return the default value when the request was rejected. @@ -31,5 +31,7 @@ import ( * In such situation, implement this interface and register it by invoking extension.SetRejectedExecutionHandler. */ type RejectedExecutionHandler interface { + + // RejectedExecution will be called if the invocation was rejected by some component. RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result } diff --git a/filter/tps_limit_strategy.go b/filter/tps_limit_strategy.go index 5edf32ce1912642c7ad0ea0b3f6144b45c267eb4..2ee876a0b340ed1f87b94c35b149b548371e2bf9 100644 --- a/filter/tps_limit_strategy.go +++ b/filter/tps_limit_strategy.go @@ -17,8 +17,8 @@ package filter +// TpsLimitStrategy defines how to do the TPS limiting in method level. /* - * TpsLimitStrategy * please register your implementation by invoking SetTpsLimitStrategy * "UserProvider": * registry: "hangzhouzk" @@ -33,10 +33,16 @@ package filter * tps.limit.strategy: "name of implementation" # method-level */ type TpsLimitStrategy interface { + // IsAllowable will return true if this invocation is not over limitation IsAllowable() bool } -// TpsLimitStrategyCreator ... +// TpsLimitStrategyCreator is the creator abstraction for TpsLimitStrategy type TpsLimitStrategyCreator interface { - Create(rate int, interval int) TpsLimitStrategy + // Create will create an instance of TpsLimitStrategy + // It will be a little hard to understand this method. + // The unit of interval is ms + // for example, if the limit = 100, interval = 1000 + // which means that the tps limitation is 100 times per 1000ms (100/1000ms) + Create(limit int, interval int) TpsLimitStrategy } diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go index dbc9f76838a4406b4788e7757453098613253d58..8385d7b5d84a420b54df6bf51e32a35d17e1b249 100644 --- a/filter/tps_limiter.go +++ b/filter/tps_limiter.go @@ -22,8 +22,8 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// TpsLimiter defines the Limiter that judge if the TPS overs the threshold /* - * TpsLimiter * please register your implementation by invoking SetTpsLimiter * The usage, for example: * "UserProvider": @@ -34,5 +34,6 @@ import ( * tps.limiter: "the name of limiter", */ type TpsLimiter interface { + // IsAllowable will check whether this invocation should be enabled for further process IsAllowable(common.URL, protocol.Invocation) bool } diff --git a/go.mod b/go.mod index b77fd3edae0f5b37392c37b6191468f03d2f44bb..9a9adc3b237b4c50c93e4a2c5a9ba7262f116626 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/Workiva/go-datastructures v1.0.50 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect - github.com/apache/dubbo-go-hessian2 v1.5.0 + github.com/apache/dubbo-go-hessian2 v1.6.1-0.20200623062814-707fde850279 github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible @@ -12,9 +12,9 @@ require ( github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/creasty/defaults v1.3.0 - github.com/dubbogo/getty v1.3.4 + github.com/dubbogo/getty v1.3.7 github.com/dubbogo/go-zookeeper v1.0.0 - github.com/dubbogo/gost v1.8.0 + github.com/dubbogo/gost v1.9.0 github.com/emicklei/go-restful/v3 v3.0.0 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/go-errors/errors v1.0.1 // indirect @@ -32,6 +32,8 @@ require ( github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/jonboulle/clockwork v0.1.0 // indirect + github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect + github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 // indirect github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // indirect @@ -40,7 +42,7 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c github.com/opentracing/opentracing-go v1.1.0 - github.com/pkg/errors v0.8.1 + github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.1.0 github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect @@ -51,10 +53,9 @@ require ( github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 - go.etcd.io/bbolt v1.3.3 // indirect - go.etcd.io/etcd v3.3.13+incompatible - go.uber.org/atomic v1.4.0 - go.uber.org/zap v1.10.0 + go.etcd.io/bbolt v1.3.4 // indirect + go.uber.org/atomic v1.6.0 + go.uber.org/zap v1.15.0 google.golang.org/grpc v1.22.1 gopkg.in/yaml.v2 v2.2.2 k8s.io/api v0.0.0-20190325185214-7544f9db76f6 diff --git a/go.sum b/go.sum index e0e1c7a9ff49221d4c2657ee7d8cb6fb7e6991af..f5610cb4b29d664c71c434a21c118de94f7986b1 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,7 @@ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v10.15.3+incompatible h1:nhKI/bvazIs3C3TFGoSqKY6hZ8f5od5mb5/UcS6HVIY= github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4= github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= @@ -38,10 +39,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e h1:MSuLXx/mveDbpDNhVrcWTMeV4lbYWKcyO4rH+jAxmX0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/apache/dubbo-go-hessian2 v1.4.0 h1:Cb9FQVTy3G93dnDr7P93U8DeKFYpDTJjQp44JG5TafA= -github.com/apache/dubbo-go-hessian2 v1.4.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= -github.com/apache/dubbo-go-hessian2 v1.5.0 h1:fzulDG5G7nX0ccgKdiN9XipJ7tZ4WXKgmk4stdlDS6s= -github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= +github.com/apache/dubbo-go-hessian2 v1.6.1-0.20200623062814-707fde850279 h1:1g3IJdaUjXWs++NA9Ail8+r6WgrkfhjS6hD/YXvRzjk= +github.com/apache/dubbo-go-hessian2 v1.6.1-0.20200623062814-707fde850279/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -104,24 +103,16 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1xXY= github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/divebomb/dubbo-go v1.0.0 h1:QsQxD6UU2WbcaA8YCxU9stiuPUsVCPabFg8hhKGJR8A= github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dubbogo/getty v1.3.3 h1:8m4zZBqFHO+NmhH7rMPlFuuYRVjcPD7cUhumevqMZZs= -github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvFTSPp0= -github.com/dubbogo/getty v1.3.4 h1:5TvH213pnSIKYzY7IK8TT/r6yr5uPTB/U6YNLT+GsU0= -github.com/dubbogo/getty v1.3.4/go.mod h1:36f+gH/ekaqcDWKbxNBQk9b9HXcGtaI6YHxp4YTntX8= +github.com/dubbogo/getty v1.3.7 h1:xlkYD2/AH34iGteuLMsGjLl2PwBVrbIhHjf3tlUsv1M= +github.com/dubbogo/getty v1.3.7/go.mod h1:XWO4+wAaMqgnBN9Ykv2YxxOAkGxymg6LGO9RK+EiCDY= github.com/dubbogo/go-zookeeper v1.0.0 h1:RsYdlGwhDW+iKXM3eIIcvt34P2swLdmQfuIJxsHlGoM= github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= -github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8= -github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.7.0 h1:lWNBIE2hk1Aj2be2uXkyRTpZG0RQZj0/xbXnkIq6EHE= -github.com/dubbogo/gost v1.7.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -github.com/dubbogo/gost v1.8.0 h1:9ACbQe5OwMjqtinQcNJC5xp16kky27OsfSGw5L9A6vw= -github.com/dubbogo/gost v1.8.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y= @@ -205,6 +196,7 @@ github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSN github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhpy9g= github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= @@ -213,8 +205,8 @@ github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1: github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= @@ -331,7 +323,12 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9 h1:hJix6idebFclqlfZCHE7EUX7uqLCyb70nHNHH1XKGBg= github.com/juju/errors v0.0.0-20190930114154-d42613fe1ab9/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b h1:Rrp0ByJXEjhREMPGTt3aWYjoIsUGCbt21ekbeJcTWv0= +github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b h1:VE6r2OwP5gj+Z9aCkSKl3MlmnZbfMAjhvR5T7abKHEo= github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= @@ -391,8 +388,6 @@ github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9 github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb h1:lbmvw8r9W55w+aQgWn35W1nuleRIECMoqUrmwAOAvoI= -github.com/nacos-group/nacos-sdk-go v0.0.0-20190723125407-0242d42e3dbb/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c h1:WoCa3AvgQMVKNs+RIFlWPRgY9QVJwUxJDrGxHs0fcRo= github.com/nacos-group/nacos-sdk-go v0.0.0-20191128082542-fe1b325b125c/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= @@ -428,6 +423,8 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -453,6 +450,7 @@ github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDa github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735 h1:7YvPJVmEeFHR1Tj9sZEYsmarJEQfMVYpd/Vyy/A8dqE= github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -508,23 +506,28 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8 h1:k8TV7Gz7cpWpOw/dz71fx8cCZdWoPuckHJ/wkJl+meg= github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY= -go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw= -go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= +go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529 h1:iMGN4xG0cnqj3t+zOM8wUB0BiPKHEwSxEZCvzcbZuvk= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -533,8 +536,10 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -554,10 +559,13 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= @@ -573,6 +581,11 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180829000535-087779f1d2c9 h1:z1TeLUmxf9ws9KLICfmX+KGXTs+rjm+aGWzfsv7MZ9w= google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs= @@ -590,6 +603,7 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= @@ -614,6 +628,8 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI= k8s.io/api v0.0.0-20180806132203-61b11ee65332/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= k8s.io/api v0.0.0-20190325185214-7544f9db76f6 h1:9MWtbqhwTyDvF4cS1qAhxDb9Mi8taXiAu+5nEacl7gY= diff --git a/integrate_test.sh b/integrate_test.sh new file mode 100644 index 0000000000000000000000000000000000000000..c9c2f23b5b07f0baf96260d8092e7464d4d15659 --- /dev/null +++ b/integrate_test.sh @@ -0,0 +1,66 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You 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. + +#!/bin/bash + +set -e +set -x + +echo 'start integrate-test' + +# set root workspace +ROOT_DIR=$(pwd) +echo "integrate-test root work-space -> ${ROOT_DIR}" + +# show all travis-env +echo "travis current commit id -> ${TRAVIS_COMMIT}" +echo "travis pull request -> ${TRAVIS_PULL_REQUEST}" +echo "travis pull request branch -> ${TRAVIS_PULL_REQUEST_BRANCH}" +echo "travis pull request slug -> ${TRAVIS_PULL_REQUEST_SLUG}" +echo "travis pull request sha -> ${TRAVIS_PULL_REQUEST_SHA}" +echo "travis pull request repo slug -> ${TRAVIS_REPO_SLUG}" + + +# #start etcd registry insecure listen in [:]:2379 +# docker run -d --network host k8s.gcr.io/etcd:3.3.10 etcd +# echo "etcdv3 listen in [:]2379" + +# #start consul registry insecure listen in [:]:8500 +# docker run -d --network host consul +# echo "consul listen in [:]8500" + +# #start nacos registry insecure listen in [:]:8848 +# docker run -d --network host nacos/nacos-server:latest +# echo "ncacos listen in [:]8848" + +# default use zk as registry +#start zookeeper registry insecure listen in [:]:2181 +docker run -d --network host zookeeper +echo "zookeeper listen in [:]2181" + +# build go-server image +cd ./test/integrate/dubbo/go-server +docker build . -t ci-provider --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +cd ${ROOT_DIR} +docker run -d --network host ci-provider + +# build go-client image +cd ./test/integrate/dubbo/go-client +docker build . -t ci-consumer --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +cd ${ROOT_DIR} +# run provider +# check consumer status +docker run -it --network host ci-consumer diff --git a/metadata/identifier/base_metadata_identifier.go b/metadata/identifier/base_metadata_identifier.go index a314671055be523844fd7d8f9589b8b6031632bc..5f3df4c607e69d2b56e1258d081c148524cd7aca 100644 --- a/metadata/identifier/base_metadata_identifier.go +++ b/metadata/identifier/base_metadata_identifier.go @@ -25,11 +25,13 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// BaseMetadataIdentifier defined for description the Metadata base identify type BaseMetadataIdentifier interface { getFilePathKey(params ...string) string getIdentifierKey(params ...string) string } +// BaseMetadataIdentifier is the base implement of BaseMetadataIdentifier interface type BaseServiceMetadataIdentifier struct { serviceInterface string version string @@ -37,7 +39,7 @@ type BaseServiceMetadataIdentifier struct { side string } -// joinParams... +// joinParams will join the specified char in slice, and return a string func joinParams(joinChar string, params []string) string { var joinedStr string for _, param := range params { @@ -47,7 +49,7 @@ func joinParams(joinChar string, params []string) string { return joinedStr } -// getIdentifierKey... +// getIdentifierKey returns string that format is service:Version:Group:Side:param1:param2... func (mdi *BaseServiceMetadataIdentifier) getIdentifierKey(params ...string) string { return mdi.serviceInterface + constant.KEY_SEPARATOR + mdi.version + @@ -56,7 +58,7 @@ func (mdi *BaseServiceMetadataIdentifier) getIdentifierKey(params ...string) str joinParams(constant.KEY_SEPARATOR, params) } -// getFilePathKey... +// getFilePathKey returns string that format is metadata/path/Version/Group/Side/param1/param2... func (mdi *BaseServiceMetadataIdentifier) getFilePathKey(params ...string) string { path := serviceToPath(mdi.serviceInterface) @@ -69,7 +71,6 @@ func (mdi *BaseServiceMetadataIdentifier) getFilePathKey(params ...string) strin } -// serviceToPath... func serviceToPath(serviceInterface string) string { if serviceInterface == constant.ANY_VALUE { return "" @@ -83,7 +84,6 @@ func serviceToPath(serviceInterface string) string { } -//withPathSeparator... func withPathSeparator(path string) string { if len(path) != 0 { path = constant.PATH_SEPARATOR + path diff --git a/metadata/identifier/metadata_identifier.go b/metadata/identifier/metadata_identifier.go index f3df8f36546093a826279c4e9ec1546f78d444bd..7e72c10da9c088ca167fa4fbc4dcb57f44b8c06d 100644 --- a/metadata/identifier/metadata_identifier.go +++ b/metadata/identifier/metadata_identifier.go @@ -17,17 +17,18 @@ package identifier +// MetadataIdentifier is inherit baseMetaIdentifier with Application name type MetadataIdentifier struct { application string BaseMetadataIdentifier } -// getIdentifierKey... +// GetIdentifierKey returns string that format is service:Version:Group:Side:Application func (mdi *MetadataIdentifier) getIdentifierKey(params ...string) string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.application) } -// getIdentifierKey... +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Application func (mdi *MetadataIdentifier) getFilePathKey(params ...string) string { return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.application) } diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go index 373df0130dd1f87e3175918bde50060c4be89616..ccc149f7306c125b19a25373d4da660a154cc84e 100644 --- a/metadata/identifier/service_metadata_identifier.go +++ b/metadata/identifier/service_metadata_identifier.go @@ -21,18 +21,19 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// ServiceMetadataIdentifier is inherit baseMetaIdentifier with service params: Revision and Protocol type ServiceMetadataIdentifier struct { revision string protocol string BaseMetadataIdentifier } -// getIdentifierKey... +// GetIdentifierKey returns string that format is service:Version:Group:Side:Protocol:"revision"+Revision func (mdi *ServiceMetadataIdentifier) getIdentifierKey(params ...string) string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) } -// getIdentifierKey... +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Protocol/"revision"+Revision func (mdi *ServiceMetadataIdentifier) getFilePathKey(params ...string) string { return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.protocol + constant.KEY_REVISON_PREFIX + mdi.revision) } diff --git a/metadata/identifier/subscribe_metadata_identifier.go b/metadata/identifier/subscribe_metadata_identifier.go index 321a216a3e3ad3f2390ab832782924a81e226160..38f3ebbd462338b581d83cd19403a00a5064b5a4 100644 --- a/metadata/identifier/subscribe_metadata_identifier.go +++ b/metadata/identifier/subscribe_metadata_identifier.go @@ -17,17 +17,18 @@ package identifier +// SubscriberMetadataIdentifier is inherit baseMetaIdentifier with service params: Revision type SubscriberMetadataIdentifier struct { revision string BaseMetadataIdentifier } -// getIdentifierKey... +// GetIdentifierKey returns string that format is service:Version:Group:Side:Revision func (mdi *SubscriberMetadataIdentifier) getIdentifierKey(params ...string) string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.revision) } -// getIdentifierKey... +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Revision func (mdi *SubscriberMetadataIdentifier) getFilePathKey(params ...string) string { return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.revision) } diff --git a/metadata/report.go b/metadata/report.go index 3fcc71241411d4a8f9577bb5fb3233e67942cd52..f2380f50cd0eb15182c137f02e5f78b4ba8e4fd2 100644 --- a/metadata/report.go +++ b/metadata/report.go @@ -25,7 +25,7 @@ import ( type MetadataReport interface { StoreProviderMetadata(*identifier.MetadataIdentifier, *definition.ServiceDefinition) - StoreConsumeretadata(*identifier.MetadataIdentifier, map[string]string) + StoreConsumerMetadata(*identifier.MetadataIdentifier, map[string]string) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string diff --git a/metadata/service.go b/metadata/service.go index d85703c95a57183d5c0a5b2445839e946dc6a59b..89df68fb313b1abe63082c0c220b0114c11fca19 100644 --- a/metadata/service.go +++ b/metadata/service.go @@ -22,6 +22,9 @@ import ( gxset "github.com/dubbogo/gost/container/set" ) +// Metadata service is a built-in service around the metadata of Dubbo services, +// whose interface is provided by Dubbo Framework and exported automatically before subscription after other services exporting, +// which may be used for Dubbo subscribers and admin. type MetadataService interface { ServiceName() string ExportURL(url *common.URL) bool diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go index 1636b14da2fe5ab714853aa662eaa774ddbc1791..bd1e7986ca709a4e10dfcad04d2380931308d568 100644 --- a/metrics/prometheus/reporter.go +++ b/metrics/prometheus/reporter.go @@ -68,9 +68,8 @@ func init() { extension.SetMetricReporter(reporterName, newPrometheusReporter) } -// PrometheusReporter -// it will collect the data for Prometheus -// if you want to use this, you should initialize your prometheus. +// PrometheusReporter will collect the data for Prometheus +// if you want to use this feature, you need to initialize your prometheus. // https://prometheus.io/docs/guides/go-application/ type PrometheusReporter struct { @@ -85,7 +84,7 @@ type PrometheusReporter struct { consumerHistogramVec *prometheus.HistogramVec } -// Report report the duration to Prometheus +// Report reports the duration to Prometheus // the role in url must be consumer or provider // or it will be ignored func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) { @@ -99,7 +98,7 @@ func (reporter *PrometheusReporter) Report(ctx context.Context, invoker protocol sumVec = reporter.consumerSummaryVec hisVec = reporter.consumerHistogramVec } else { - logger.Warnf("The url is not the consumer's or provider's, "+ + logger.Warnf("The url belongs neither the consumer nor the provider, "+ "so the invocation will be ignored. url: %s", url.String()) return } diff --git a/metrics/reporter.go b/metrics/reporter.go index 85ef1dcdf0dad275edecc1f3a85502c1493c1395..9a7094fa62d9c0fa3e6ee0a8ef373f91c28d2c90 100644 --- a/metrics/reporter.go +++ b/metrics/reporter.go @@ -29,7 +29,7 @@ const ( NameSpace = "dubbo" ) -// it will be use to report the invocation's duration +// Reporter will be used to report the invocation's duration type Reporter interface { // report the duration of an invocation Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go index 5ec7db51af0ddfa6e49d3c65910355f0bf2de414..6d1b771bf4108d17372e0ceb5ca818323278afd2 100644 --- a/protocol/dubbo/client.go +++ b/protocol/dubbo/client.go @@ -88,7 +88,7 @@ func init() { rand.Seed(time.Now().UnixNano()) } -// SetClientConf ... +// SetClientConf set dubbo client config. func SetClientConf(c ClientConfig) { clientConf = &c err := clientConf.CheckValidity() @@ -99,7 +99,7 @@ func SetClientConf(c ClientConfig) { setClientGrpool() } -// GetClientConf ... +// GetClientConf get dubbo client config. func GetClientConf() ClientConfig { return *clientConf } @@ -111,7 +111,7 @@ func setClientGrpool() { } } -// Options ... +// Options is option for create dubbo client type Options struct { // connect timeout ConnectTimeout time.Duration @@ -129,7 +129,7 @@ type AsyncCallbackResponse struct { Reply interface{} } -// Client ... +// Client is dubbo protocol client. type Client struct { opts Options conf ClientConfig @@ -139,7 +139,7 @@ type Client struct { pendingResponses *sync.Map } -// NewClient ... +// NewClient create a new Client. func NewClient(opt Options) *Client { switch { @@ -167,7 +167,7 @@ func NewClient(opt Options) *Client { return c } -// Request ... +// Request is dubbo protocol request. type Request struct { addr string svcUrl common.URL @@ -176,7 +176,7 @@ type Request struct { atta map[string]string } -// NewRequest ... +// NewRequest create a new Request. func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request { return &Request{ addr: addr, @@ -187,13 +187,13 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, } } -// Response ... +// Response is dubbo protocol response. type Response struct { reply interface{} atta map[string]string } -// NewResponse ... +// NewResponse create a new Response. func NewResponse(reply interface{}, atta map[string]string) *Response { return &Response{ reply: reply, @@ -201,15 +201,14 @@ func NewResponse(reply interface{}, atta map[string]string) *Response { } } -// CallOneway call one way +// CallOneway call by one way func (c *Client) CallOneway(request *Request) error { return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) } -// Call if @response is nil, the transport layer will get the response without notify the invoker. +// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way. func (c *Client) Call(request *Request, response *Response) error { - ct := CT_TwoWay if response.reply == nil { ct = CT_OneWay @@ -218,14 +217,12 @@ func (c *Client) Call(request *Request, response *Response) error { return perrors.WithStack(c.call(ct, request, response, nil)) } -// AsyncCall ... +// AsyncCall call remoting by async with callback. func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error { - return perrors.WithStack(c.call(CT_TwoWay, request, response, callback)) } func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error { - p := &DubboPackage{} p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/") p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "") @@ -293,7 +290,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac return perrors.WithStack(err) } -// Close ... +// Close close the client pool. func (c *Client) Close() { if c.pool != nil { c.pool.close() diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go index 744ffa80d6bc65e8526201b8cd327bb12b43caef..8b0ba169b82910652c64011c47568c7a018ae5e0 100644 --- a/protocol/dubbo/client_test.go +++ b/protocol/dubbo/client_test.go @@ -37,7 +37,13 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func TestClient_CallOneway(t *testing.T) { +const ( + mockMethodNameGetUser = "GetUser" + mockMethodNameGetBigPkg = "GetBigPkg" + mockAddress = "127.0.0.1:20000" +) + +func TestClientCallOneway(t *testing.T) { proto, url := InitTest(t) c := &Client{ @@ -50,15 +56,14 @@ func TestClient_CallOneway(t *testing.T) { } c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - //user := &User{} - err := c.CallOneway(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil)) + err := c.CallOneway(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil)) assert.NoError(t, err) // destroy proto.Destroy() } -func TestClient_Call(t *testing.T) { +func TestClientCall(t *testing.T) { proto, url := InitTest(t) c := &Client{ @@ -77,50 +82,50 @@ func TestClient_Call(t *testing.T) { ) user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetBigPkg", []interface{}{nil}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetBigPkg, []interface{}{nil}, nil), NewResponse(user, nil)) assert.NoError(t, err) assert.NotEqual(t, "", user.Id) assert.NotEqual(t, "", user.Name) user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil), NewResponse(user, nil)) assert.NoError(t, err) assert.Equal(t, User{Id: "1", Name: "username"}, *user) user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) assert.NoError(t, err) assert.Equal(t, User{Id: "1", Name: "username"}, *user) - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) assert.NoError(t, err) - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser2", []interface{}{}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser2", []interface{}{}, nil), NewResponse(user, nil)) assert.EqualError(t, err, "error") user2 := []interface{}{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser3", []interface{}{}, nil), NewResponse(&user2, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser3", []interface{}{}, nil), NewResponse(&user2, nil)) assert.NoError(t, err) assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) user2 = []interface{}{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), NewResponse(&user2, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), NewResponse(&user2, nil)) assert.NoError(t, err) assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) user3 := map[interface{}]interface{}{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), NewResponse(&user3, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), NewResponse(&user3, nil)) assert.NoError(t, err) assert.NotNil(t, user3) assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"]) user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{0}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{0}, nil), NewResponse(user, nil)) assert.NoError(t, err) assert.Equal(t, User{Id: "", Name: ""}, *user) user = &User{} - err = c.Call(NewRequest("127.0.0.1:20000", url, "GetUser6", []interface{}{1}, nil), NewResponse(user, nil)) + err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{1}, nil), NewResponse(user, nil)) assert.NoError(t, err) assert.Equal(t, User{Id: "1", Name: ""}, *user) @@ -128,7 +133,7 @@ func TestClient_Call(t *testing.T) { proto.Destroy() } -func TestClient_AsyncCall(t *testing.T) { +func TestClientAsyncCall(t *testing.T) { proto, url := InitTest(t) c := &Client{ @@ -144,7 +149,7 @@ func TestClient_AsyncCall(t *testing.T) { user := &User{} lock := sync.Mutex{} lock.Lock() - err := c.AsyncCall(NewRequest("127.0.0.1:20000", url, "GetUser", []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { + err := c.AsyncCall(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { r := response.(AsyncCallbackResponse) assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) lock.Unlock() diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go index 76416b2baf1e1db516c00d92ecb8ad618bf186bd..1f7d107544a06d0ef83bcb54ff6f03daf2dc517b 100644 --- a/protocol/dubbo/codec.go +++ b/protocol/dubbo/codec.go @@ -65,11 +65,12 @@ type DubboPackage struct { Err error } +// String prints dubbo package detail include header銆乸ath銆乥ody etc. func (p DubboPackage) String() string { return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) } -// Marshal ... +// Marshal encode hessian package. func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { codec := hessian.NewHessianCodec(nil) @@ -81,7 +82,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { return bytes.NewBuffer(pkg), nil } -// Unmarshal ... +// Unmarshal dncode hessian package. func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { // fix issue https://github.com/apache/dubbo-go/issues/380 bufLen := buf.Len() @@ -125,7 +126,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { // PendingResponse //////////////////////////////////////////// -// PendingResponse ... +// PendingResponse is a pending response. type PendingResponse struct { seq uint64 err error @@ -136,7 +137,7 @@ type PendingResponse struct { done chan struct{} } -// NewPendingResponse ... +// NewPendingResponse create a PendingResponses. func NewPendingResponse() *PendingResponse { return &PendingResponse{ start: time.Now(), @@ -145,7 +146,7 @@ func NewPendingResponse() *PendingResponse { } } -// GetCallResponse ... +// GetCallResponse get AsyncCallbackResponse. func (r PendingResponse) GetCallResponse() common.CallbackResponse { return AsyncCallbackResponse{ Cause: r.err, diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/codec_test.go index 5dc71f0d080c8c862d68029c7983a4407913307e..c2ca443637e23101679770e464f49e0cbdeab2a9 100644 --- a/protocol/dubbo/codec_test.go +++ b/protocol/dubbo/codec_test.go @@ -29,7 +29,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) { +func TestDubboPackageMarshalAndUnmarshal(t *testing.T) { pkg := &DubboPackage{} pkg.Body = []interface{}{"a"} pkg.Header.Type = hessian.PackageHeartbeat diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go index dbc6989c54780afacef717f1d110833d92967f9f..635d12109add17cfac1056316c9d53817525fd67 100644 --- a/protocol/dubbo/config.go +++ b/protocol/dubbo/config.go @@ -27,7 +27,7 @@ import ( ) type ( - // GettySessionParam ... + // GettySessionParam is session configuration for getty. GettySessionParam struct { CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` @@ -47,8 +47,7 @@ type ( SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"` } - // ServerConfig - //Config holds supported types by the multiconfig package + // ServerConfig holds supported types by the multiconfig package ServerConfig struct { // session SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` @@ -64,8 +63,7 @@ type ( GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` } - // ClientConfig - //Config holds supported types by the multiconfig package + // ClientConfig holds supported types by the multiconfig package ClientConfig struct { ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` @@ -94,7 +92,7 @@ type ( } ) -// GetDefaultClientConfig ... +// GetDefaultClientConfig gets client default configuration. func GetDefaultClientConfig() ClientConfig { return ClientConfig{ ReconnectInterval: 0, @@ -122,7 +120,7 @@ func GetDefaultClientConfig() ClientConfig { }} } -// GetDefaultServerConfig ... +// GetDefaultServerConfig gets server default configuration. func GetDefaultServerConfig() ServerConfig { return ServerConfig{ SessionTimeout: "180s", @@ -147,7 +145,7 @@ func GetDefaultServerConfig() ServerConfig { } } -// CheckValidity ... +// CheckValidity confirm getty sessian params. func (c *GettySessionParam) CheckValidity() error { var err error @@ -170,7 +168,7 @@ func (c *GettySessionParam) CheckValidity() error { return nil } -// CheckValidity ... +// CheckValidity confirm client params. func (c *ClientConfig) CheckValidity() error { var err error @@ -192,7 +190,7 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithStack(c.GettySessionParam.CheckValidity()) } -// CheckValidity ... +// CheckValidity confirm server params. func (c *ServerConfig) CheckValidity() error { var err error diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go index 1c45c40056f7690ba64838f641fe0b13a1554727..dd80937c5bdf6718c2047b102115d8c08afcd899 100644 --- a/protocol/dubbo/dubbo_exporter.go +++ b/protocol/dubbo/dubbo_exporter.go @@ -28,19 +28,19 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// DubboExporter ... +// DubboExporter is dubbo service exporter. type DubboExporter struct { protocol.BaseExporter } -// NewDubboExporter ... +// NewDubboExporter get a DubboExporter. func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter { return &DubboExporter{ BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap), } } -// Unexport ... +// Unexport unexport dubbo service exporter. func (de *DubboExporter) Unexport() { serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 09c3725710d2a0b821d8e641b0cb7b367189c244..59202d5f49f30acb9e75c4e2f135601285f08e13 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -39,8 +39,9 @@ import ( ) var ( - // ErrNoReply ... - ErrNoReply = perrors.New("request need @response") + // ErrNoReply + ErrNoReply = perrors.New("request need @response") + // ErrDestroyedInvoker ErrDestroyedInvoker = perrors.New("request Destroyed invoker") ) @@ -48,7 +49,7 @@ var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} ) -// DubboInvoker ... +// DubboInvoker is dubbo client invoker. type DubboInvoker struct { protocol.BaseInvoker client *Client @@ -57,7 +58,7 @@ type DubboInvoker struct { reqNum int64 } -// NewDubboInvoker ... +// NewDubboInvoker create dubbo client invoker. func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { return &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), @@ -66,7 +67,7 @@ func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { } } -// Invoke ... +// Invoke call remoting. func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( err error @@ -122,7 +123,7 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati return &result } -// Destroy ... +// Destroy destroy dubbo client invoker. func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { for { diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 1a64301f8200a4264001284cca1af3f0f1e07814..c0640d5558fcb9fb00f02eba0fddc54bb4162592 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -35,7 +35,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func TestDubboInvoker_Invoke(t *testing.T) { +func TestDubboInvokerInvoke(t *testing.T) { proto, url := InitTest(t) c := &Client{ @@ -51,7 +51,7 @@ func TestDubboInvoker_Invoke(t *testing.T) { invoker := NewDubboInvoker(url, c) user := &User{} - inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}), + inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(mockMethodNameGetUser), invocation.WithArguments([]interface{}{"1", "username"}), invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"})) // Call diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 355dbc802488338ef4dbdd7290166038b312f183..9eeefd079279d82241da8e21df5edfe77b8003e0 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -33,7 +33,7 @@ import ( // dubbo protocol constant const ( - // DUBBO ... + // DUBBO is dubbo protocol name DUBBO = "dubbo" ) @@ -45,14 +45,14 @@ var ( dubboProtocol *DubboProtocol ) -// DubboProtocol ... +// DubboProtocol is a dubbo protocol implement. type DubboProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } -// NewDubboProtocol ... +// NewDubboProtocol create a dubbo protocol. func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -60,7 +60,7 @@ func NewDubboProtocol() *DubboProtocol { } } -// Export ... +// Export export dubbo service. func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -73,7 +73,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } -// Refer ... +// Refer create dubbo service reference. func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { //default requestTimeout var requestTimeout = config.GetConsumerConfig().RequestTimeout @@ -92,7 +92,7 @@ func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { return invoker } -// Destroy ... +// Destroy destroy dubbo service. func (dp *DubboProtocol) Destroy() { logger.Infof("DubboProtocol destroy.") @@ -124,7 +124,7 @@ func (dp *DubboProtocol) openServer(url common.URL) { } } -// GetProtocol ... +// GetProtocol get a single dubbo protocol. func GetProtocol() protocol.Protocol { if dubboProtocol == nil { dubboProtocol = NewDubboProtocol() diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 14f6868ad4a7f2bf6f549a2fbbce8234cbb4aa12..6f3892be67be533dea09dc7bd54de56844dbc79c 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -31,15 +31,19 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func TestDubboProtocol_Export(t *testing.T) { - // Export - proto := GetProtocol() - srvConf = &ServerConfig{} - url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + +const ( + mockCommonUrl = "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") + "side=provider&timeout=3000×tamp=1556509797245" +) + +func TestDubboProtocolExport(t *testing.T) { + // Export + proto := GetProtocol() + srvConf = &ServerConfig{} + url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) exporter := proto.Export(protocol.NewBaseInvoker(url)) @@ -48,11 +52,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.True(t, eq) // second service: the same path and the different version - url2, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ - "side=provider&timeout=3000×tamp=1556509797245", common.WithParamsValue(constant.VERSION_KEY, "v1.1")) + url2, err := common.NewURL(mockCommonUrl, common.WithParamsValue(constant.VERSION_KEY, "v1.1")) assert.NoError(t, err) exporter2 := proto.Export(protocol.NewBaseInvoker(url2)) // make sure url @@ -74,14 +74,10 @@ func TestDubboProtocol_Export(t *testing.T) { assert.False(t, ok) } -func TestDubboProtocol_Refer(t *testing.T) { +func TestDubboProtocolRefer(t *testing.T) { // Refer proto := GetProtocol() - url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") + url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) clientConf = &ClientConfig{} invoker := proto.Refer(url) diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go index 0251b78a2b0d27a68461c16c284b1af53bcb08aa..4834459390f39912f0683dfe52f65faa72b7c26d 100644 --- a/protocol/dubbo/listener.go +++ b/protocol/dubbo/listener.go @@ -41,10 +41,9 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -// todo: WritePkg_Timeout will entry *.yml +// todo: writePkg_Timeout will entry *.yml const ( - // WritePkg_Timeout ... - WritePkg_Timeout = 5 * time.Second + writePkg_Timeout = 5 * time.Second ) var ( @@ -56,10 +55,12 @@ type rpcSession struct { reqNum int32 } +// AddReqNum adds total request number safely func (s *rpcSession) AddReqNum(num int32) { atomic.AddInt32(&s.reqNum, num) } +// GetReqNum gets total request number safely func (s *rpcSession) GetReqNum() int32 { return atomic.LoadInt32(&s.reqNum) } @@ -68,35 +69,35 @@ func (s *rpcSession) GetReqNum() int32 { // RpcClientHandler // ////////////////////////////////////////// -// RpcClientHandler ... +// RpcClientHandler is handler of RPC Client type RpcClientHandler struct { conn *gettyRPCClient } -// NewRpcClientHandler ... +// NewRpcClientHandler creates RpcClientHandler with @gettyRPCClient func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { return &RpcClientHandler{conn: client} } -// OnOpen ... +// OnOpen notified when RPC client session opened func (h *RpcClientHandler) OnOpen(session getty.Session) error { h.conn.addSession(session) return nil } -// OnError ... +// OnError notified when RPC client session got any error func (h *RpcClientHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.conn.removeSession(session) } -// OnClose ... +// OnOpen notified when RPC client session closed func (h *RpcClientHandler) OnClose(session getty.Session) { logger.Infof("session{%s} is closing......", session.Stat()) h.conn.removeSession(session) } -// OnMessage ... +// OnMessage notified when RPC client session got any message in connection func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { p, ok := pkg.(*DubboPackage) if !ok { @@ -141,9 +142,9 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { } } -// OnCron ... +// OnCron notified when RPC client session got any message in cron job func (h *RpcClientHandler) OnCron(session getty.Session) { - rpcSession, err := h.conn.getClientRpcSession(session) + clientRpcSession, err := h.conn.getClientRpcSession(session) if err != nil { logger.Errorf("client.getClientSession(session{%s}) = error{%v}", session.Stat(), perrors.WithStack(err)) @@ -151,7 +152,7 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { } if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) + session.Stat(), time.Since(session.GetActive()).String(), clientRpcSession.reqNum) h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) return } @@ -163,7 +164,7 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { // RpcServerHandler // ////////////////////////////////////////// -// RpcServerHandler ... +// RpcServerHandler is handler of RPC Server type RpcServerHandler struct { maxSessionNum int sessionTimeout time.Duration @@ -171,7 +172,7 @@ type RpcServerHandler struct { rwlock sync.RWMutex } -// NewRpcServerHandler ... +// NewRpcServerHandler creates RpcServerHandler with @maxSessionNum and @sessionTimeout func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { return &RpcServerHandler{ maxSessionNum: maxSessionNum, @@ -180,7 +181,7 @@ func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcSe } } -// OnOpen ... +// OnOpen notified when RPC server session opened func (h *RpcServerHandler) OnOpen(session getty.Session) error { var err error h.rwlock.RLock() @@ -199,15 +200,15 @@ func (h *RpcServerHandler) OnOpen(session getty.Session) error { return nil } -// OnError ... +// OnError notified when RPC server session got any error func (h *RpcServerHandler) OnError(session getty.Session, err error) { - logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) + logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.rwlock.Lock() delete(h.sessionMap, session) h.rwlock.Unlock() } -// OnClose ... +// OnOpen notified when RPC server session closed func (h *RpcServerHandler) OnClose(session getty.Session) { logger.Infof("session{%s} is closing......", session.Stat()) h.rwlock.Lock() @@ -215,7 +216,7 @@ func (h *RpcServerHandler) OnClose(session getty.Session) { h.rwlock.Unlock() } -// OnMessage ... +// OnMessage notified when RPC server session got any message in connection func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { h.rwlock.Lock() if _, ok := h.sessionMap[session]; ok { @@ -306,7 +307,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { reply(session, p, hessian.PackageResponse) } -// OnCron ... +// OnCron notified when RPC server session got any message in cron job func (h *RpcServerHandler) OnCron(session getty.Session) { var ( flag bool @@ -363,7 +364,7 @@ func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { resp.Body = nil } - if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { + if err := session.WritePkg(resp, writePkg_Timeout); err != nil { logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header) } } diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go index 918514c2676cfc69336a9f53e6d16d7f23cf7dca..c9f5e34fadf61fb36e92356f1b1d40fbc67e4c99 100644 --- a/protocol/dubbo/pool.go +++ b/protocol/dubbo/pool.go @@ -219,25 +219,25 @@ func (c *gettyRPCClient) updateSession(session getty.Session) { func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { var ( - err error - rpcSession rpcSession + err error + rpcClientSession rpcSession ) c.lock.RLock() defer c.lock.RUnlock() if c.sessions == nil { - return rpcSession, errClientClosed + return rpcClientSession, errClientClosed } err = errSessionNotExist for _, s := range c.sessions { if s.session == session { - rpcSession = *s + rpcClientSession = *s err = nil break } } - return rpcSession, perrors.WithStack(err) + return rpcClientSession, perrors.WithStack(err) } func (c *gettyRPCClient) isAvailable() bool { @@ -319,8 +319,7 @@ func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPC conn, err := p.get() if err == nil && conn == nil { // create new conn - rpcClientConn, err := newGettyRPCClientConn(p, protocol, addr) - return rpcClientConn, perrors.WithStack(err) + conn, err = newGettyRPCClientConn(p, protocol, addr) } return conn, perrors.WithStack(err) } diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go index b5c4f509190dbdc85825ad424656240b234786df..9cc7ea25cdfa6152d632f278b33285d7d38f47c9 100644 --- a/protocol/dubbo/readwriter.go +++ b/protocol/dubbo/readwriter.go @@ -38,16 +38,17 @@ import ( // RpcClientPackageHandler //////////////////////////////////////////// -// RpcClientPackageHandler ... +// RpcClientPackageHandler handle package for client in getty. type RpcClientPackageHandler struct { client *Client } -// NewRpcClientPackageHandler ... +// NewRpcClientPackageHandler create a RpcClientPackageHandler. func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { return &RpcClientPackageHandler{client: client} } +// Read decode @data to DubboPackage. func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { pkg := &DubboPackage{} @@ -72,6 +73,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } +// Write encode @pkg. func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { req, ok := pkg.(*DubboPackage) if !ok { @@ -96,9 +98,10 @@ var ( rpcServerPkgHandler = &RpcServerPackageHandler{} ) -// RpcServerPackageHandler ... +// RpcServerPackageHandler handle package for server in getty. type RpcServerPackageHandler struct{} +// Read decode @data to DubboPackage. func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { pkg := &DubboPackage{ Body: make([]interface{}, 7), @@ -169,6 +172,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } +// Write encode @pkg. func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { res, ok := pkg.(*DubboPackage) if !ok { diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go index bd2b37b7a9f055745e183524d19a442af03360f4..8de353a0b372785e89585f4bf2b00b2c42540a2b 100644 --- a/protocol/dubbo/server.go +++ b/protocol/dubbo/server.go @@ -71,10 +71,10 @@ func init() { if err := srvConf.CheckValidity(); err != nil { panic(err) } - SetServerGrpool() + setServerGrpool() } -// SetServerConfig ... +// SetServerConfig set dubbo server config. func SetServerConfig(s ServerConfig) { srvConf = &s err := srvConf.CheckValidity() @@ -82,30 +82,29 @@ func SetServerConfig(s ServerConfig) { logger.Warnf("[ServerConfig CheckValidity] error: %v", err) return } - SetServerGrpool() + setServerGrpool() } -// GetServerConfig ... +// GetServerConfig get dubbo server config. func GetServerConfig() ServerConfig { return *srvConf } -// SetServerGrpool ... -func SetServerGrpool() { +func setServerGrpool() { if srvConf.GrPoolSize > 1 { srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) } } -// Server ... +// Server is dubbo protocol server. type Server struct { conf ServerConfig tcpServer getty.Server rpcHandler *RpcServerHandler } -// NewServer ... +// NewServer create a new Server. func NewServer() *Server { s := &Server{ @@ -156,7 +155,7 @@ func (s *Server) newSession(session getty.Session) error { return nil } -// Start ... +// Start start dubbo server. func (s *Server) Start(url common.URL) { var ( addr string @@ -173,7 +172,7 @@ func (s *Server) Start(url common.URL) { } -// Stop ... +// Stop stop dubbo server. func (s *Server) Stop() { s.tcpServer.Close() } diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go index 0c7499a179571d623eccc607dd4cc8f1950f3239..a0ab0be80cc905115e675c1c4dea2b1c748f6c09 100644 --- a/protocol/grpc/client.go +++ b/protocol/grpc/client.go @@ -25,27 +25,78 @@ import ( "github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc" "github.com/opentracing/opentracing-go" "google.golang.org/grpc" + "gopkg.in/yaml.v2" ) import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" ) -// Client ... +var ( + clientConf *ClientConfig +) + +func init() { + // load clientconfig from consumer_config + consumerConfig := config.GetConsumerConfig() + + clientConfig := GetClientConfig() + clientConf = &clientConfig + + // check client config and decide whether to use the default config + defer func() { + if clientConf == nil || len(clientConf.ContentSubType) == 0 { + defaultClientConfig := GetDefaultClientConfig() + clientConf = &defaultClientConfig + } + if err := clientConf.Validate(); err != nil { + panic(err) + } + }() + + if consumerConfig.ApplicationConfig == nil { + return + } + protocolConf := config.GetConsumerConfig().ProtocolConf + + if protocolConf == nil { + logger.Info("protocol_conf default use dubbo config") + } else { + grpcConf := protocolConf.(map[interface{}]interface{})[GRPC] + if grpcConf == nil { + logger.Warnf("grpcConf is nil") + return + } + grpcConfByte, err := yaml.Marshal(grpcConf) + if err != nil { + panic(err) + } + err = yaml.Unmarshal(grpcConfByte, clientConf) + if err != nil { + panic(err) + } + } + +} + +// Client is gRPC client include client connection and invoker type Client struct { *grpc.ClientConn invoker reflect.Value } -// NewClient ... +// NewClient creates a new gRPC client. func NewClient(url common.URL) *Client { // if global trace instance was set , it means trace function enabled. If not , will return Nooptracer tracer := opentracing.GlobalTracer() - conn, err := grpc.Dial(url.Location, grpc.WithInsecure(), grpc.WithBlock(), - grpc.WithUnaryInterceptor( - otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads()))) + dailOpts := make([]grpc.DialOption, 0, 4) + dailOpts = append(dailOpts, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor( + otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads())), + grpc.WithDefaultCallOptions(grpc.CallContentSubtype(clientConf.ContentSubType))) + conn, err := grpc.Dial(url.Location, dailOpts...) if err != nil { panic(err) } diff --git a/protocol/grpc/codec.go b/protocol/grpc/codec.go new file mode 100644 index 0000000000000000000000000000000000000000..7235a3941baefcdce5964ae21dbdbfe9d6ca9cc8 --- /dev/null +++ b/protocol/grpc/codec.go @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpc + +import ( + "bytes" + "encoding/json" +) + +import ( + "github.com/golang/protobuf/jsonpb" + "github.com/golang/protobuf/proto" + "google.golang.org/grpc/encoding" +) + +const ( + codecJson = "json" + codecProto = "proto" +) + +func init() { + encoding.RegisterCodec(grpcJson{ + Marshaler: jsonpb.Marshaler{ + EmitDefaults: true, + OrigName: true, + }, + }) +} + +type grpcJson struct { + jsonpb.Marshaler + jsonpb.Unmarshaler +} + +// Name implements grpc encoding package Codec interface method, +// returns the name of the Codec implementation. +func (_ grpcJson) Name() string { + return codecJson +} + +// Marshal implements grpc encoding package Codec interface method,returns the wire format of v. +func (j grpcJson) Marshal(v interface{}) (out []byte, err error) { + if pm, ok := v.(proto.Message); ok { + b := new(bytes.Buffer) + err := j.Marshaler.Marshal(b, pm) + if err != nil { + return nil, err + } + return b.Bytes(), nil + } + return json.Marshal(v) +} + +// Unmarshal implements grpc encoding package Codec interface method,Unmarshal parses the wire format into v. +func (j grpcJson) Unmarshal(data []byte, v interface{}) (err error) { + if pm, ok := v.(proto.Message); ok { + b := bytes.NewBuffer(data) + return j.Unmarshaler.Unmarshal(b, pm) + } + return json.Unmarshal(data, v) +} diff --git a/protocol/grpc/common_test.go b/protocol/grpc/common_test.go index 33c2fc617d52795d13d9b4fc02054ef5a79d0934..b732283a9bf9d316c1b9d4356a1b9563bfa1d3ec 100644 --- a/protocol/grpc/common_test.go +++ b/protocol/grpc/common_test.go @@ -106,7 +106,7 @@ func dubboGreeterSayHelloHandler(srv interface{}, ctx context.Context, Server: srv, FullMethod: "/helloworld.Greeter/SayHello", } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { + handler := func(context.Context, interface{}) (interface{}, error) { result := base.GetProxyImpl().Invoke(context.Background(), invo) return result.Result(), result.Error() } diff --git a/protocol/grpc/config.go b/protocol/grpc/config.go new file mode 100644 index 0000000000000000000000000000000000000000..e8a9baac8999a009bab435a5bd4e70d102f77b56 --- /dev/null +++ b/protocol/grpc/config.go @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 grpc + +import ( + perrors "github.com/pkg/errors" +) + +type ( + // ServerConfig currently is empty struct,for future expansion + ServerConfig struct { + } + + // ClientConfig wrap client call parameters + ClientConfig struct { + // content type, more information refer by https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests + ContentSubType string `default:"proto" yaml:"content_sub_type" json:"content_sub_type,omitempty"` + } +) + +// GetDefaultClientConfig return grpc client default call options +func GetDefaultClientConfig() ClientConfig { + return ClientConfig{ + ContentSubType: codecProto, + } +} + +// GetDefaultServerConfig currently return empty struct,for future expansion +func GetDefaultServerConfig() ServerConfig { + return ServerConfig{} +} + +// GetClientConfig return grpc client custom call options +func GetClientConfig() ClientConfig { + return ClientConfig{} +} + +// Validate check if custom config encoding is supported in dubbo grpc +func (c *ClientConfig) Validate() error { + if c.ContentSubType != codecJson && c.ContentSubType != codecProto { + return perrors.Errorf(" dubbo-go grpc codec currently only support proto銆乯son, %s isn't supported,"+ + " please check protocol content_sub_type config", c.ContentSubType) + } + return nil +} + +// Validate currently return empty struct,for future expansion +func (c *ServerConfig) Validate() error { + return nil +} diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go index 0296ad8b5b06b693fd5e0972f18be77a69cd21ed..79962b59e29bb0e3aeb58776f6c26abc2e6832de 100644 --- a/protocol/grpc/grpc_exporter.go +++ b/protocol/grpc/grpc_exporter.go @@ -33,14 +33,14 @@ type GrpcExporter struct { *protocol.BaseExporter } -// NewGrpcExporter ... +// NewGrpcExporter creates a new gRPC exporter func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *GrpcExporter { return &GrpcExporter{ BaseExporter: protocol.NewBaseExporter(key, invoker, exporterMap), } } -// Unexport ... +// Unexport and unregister gRPC service from registry and memory. func (gg *GrpcExporter) Unexport() { serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := gg.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go index 78439fcd9b7f7d3b845f326bf432ea486855965e..e150d05e6fb39890bd3e355f4042b4ef34db42ed 100644 --- a/protocol/grpc/grpc_invoker.go +++ b/protocol/grpc/grpc_invoker.go @@ -35,8 +35,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// ErrNoReply ... -var ErrNoReply = errors.New("request need @response") +var errNoReply = errors.New("request need @response") // GrpcInvoker ... type GrpcInvoker struct { @@ -60,7 +59,7 @@ func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio ) if invocation.Reply() == nil { - result.Err = ErrNoReply + result.Err = errNoReply } in := []reflect.Value{} diff --git a/protocol/grpc/grpc_invoker_test.go b/protocol/grpc/grpc_invoker_test.go index 3054ada13340a9c9cc038a63d89c45ced9ec7ac7..d5ebbb4f47a324791a3367a649bd49b06281540f 100644 --- a/protocol/grpc/grpc_invoker_test.go +++ b/protocol/grpc/grpc_invoker_test.go @@ -33,11 +33,19 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) +const ( + mockGrpcCommonUrl = "grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl" + + "&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter" + + "&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=" + + "&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=" + + "&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!" +) + func TestInvoke(t *testing.T) { go internal.InitGrpcServer() defer internal.ShutdownGrpcServer() - url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!") + url, err := common.NewURL(mockGrpcCommonUrl) assert.Nil(t, err) cli := NewClient(url) diff --git a/protocol/grpc/grpc_protocol.go b/protocol/grpc/grpc_protocol.go index cc4aba10bf69f5e80d761649b9830fd61c4084cd..68594a4b35921b6e3b1d59d404ed163025d57a81 100644 --- a/protocol/grpc/grpc_protocol.go +++ b/protocol/grpc/grpc_protocol.go @@ -39,14 +39,14 @@ func init() { var grpcProtocol *GrpcProtocol -// GrpcProtocol ... +// GrpcProtocol is gRPC protocol type GrpcProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } -// NewGRPCProtocol ... +// NewGRPCProtocol creates new gRPC protocol func NewGRPCProtocol() *GrpcProtocol { return &GrpcProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -54,7 +54,7 @@ func NewGRPCProtocol() *GrpcProtocol { } } -// Export ... +// Export gRPC service for remote invocation func (gp *GrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -84,7 +84,7 @@ func (gp *GrpcProtocol) openServer(url common.URL) { } } -// Refer ... +// Refer a remote gRPC service func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker { invoker := NewGrpcInvoker(url, NewClient(url)) gp.SetInvokers(invoker) @@ -92,7 +92,7 @@ func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker { return invoker } -// Destroy ... +// Destroy will destroy gRPC all invoker and exporter, so it only is called once. func (gp *GrpcProtocol) Destroy() { logger.Infof("GrpcProtocol destroy.") @@ -104,7 +104,7 @@ func (gp *GrpcProtocol) Destroy() { } } -// GetProtocol ... +// GetProtocol gets gRPC protocol , will create if null. func GetProtocol() protocol.Protocol { if grpcProtocol == nil { grpcProtocol = NewGRPCProtocol() diff --git a/protocol/grpc/grpc_protocol_test.go b/protocol/grpc/grpc_protocol_test.go index d028f8ef4285b0183e6e0b5b32deede59ce5c531..87ce714fc7437eca53509bb368ed7bc774929634 100644 --- a/protocol/grpc/grpc_protocol_test.go +++ b/protocol/grpc/grpc_protocol_test.go @@ -32,12 +32,12 @@ import ( "github.com/apache/dubbo-go/protocol/grpc/internal" ) -func TestGrpcProtocol_Export(t *testing.T) { +func TestGrpcProtocolExport(t *testing.T) { // Export addService() proto := GetProtocol() - url, err := common.NewURL("grpc://127.0.0.1:40000/GrpcGreeterImpl?accesslog=&app.version=0.0.1&application=BDTService&bean.name=GrpcGreeterImpl&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown×tamp=1576923717&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100") + url, err := common.NewURL(mockGrpcCommonUrl) assert.NoError(t, err) exporter := proto.Export(protocol.NewBaseInvoker(url)) time.Sleep(time.Second) @@ -61,13 +61,13 @@ func TestGrpcProtocol_Export(t *testing.T) { assert.False(t, ok) } -func TestGrpcProtocol_Refer(t *testing.T) { +func TestGrpcProtocolRefer(t *testing.T) { go internal.InitGrpcServer() defer internal.ShutdownGrpcServer() time.Sleep(time.Second) proto := GetProtocol() - url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!") + url, err := common.NewURL(mockGrpcCommonUrl) assert.NoError(t, err) invoker := proto.Refer(url) diff --git a/protocol/grpc/internal/server.go b/protocol/grpc/internal/server.go index a6b861cac6ccb73f8bdf894f462f380123fa9ae3..f7b99fa0ba557fa002321b5d2435d17cf792dd38 100644 --- a/protocol/grpc/internal/server.go +++ b/protocol/grpc/internal/server.go @@ -42,7 +42,7 @@ func (s *server) SayHello(ctx context.Context, in *HelloRequest) (*HelloReply, e return &HelloReply{Message: "Hello " + in.GetName()}, nil } -// InitGrpcServer ... +// InitGrpcServer creates global gRPC server. func InitGrpcServer() { port := ":30000" @@ -57,7 +57,7 @@ func InitGrpcServer() { } } -// ShutdownGrpcServer ... +// ShutdownGrpcServer shuts down gRPC server gracefully func ShutdownGrpcServer() { if s == nil { return diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go index 83d28519f6f8e28bf471ce2ea6807603c1324911..1af4fafdc606783e937ede63f99e5a08f0b2419e 100644 --- a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go +++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go @@ -107,7 +107,6 @@ func (g *dubboGrpc) GenerateImports(file *generator.FileDescriptor) { g.P(`dgrpc "github.com/apache/dubbo-go/protocol/grpc"`) g.P(`"github.com/apache/dubbo-go/protocol/invocation"`) g.P(`"github.com/apache/dubbo-go/protocol"`) - g.P(`"github.com/apache/dubbo-go/config"`) g.P(` ) `) } @@ -266,7 +265,7 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * g.P(`invo := invocation.NewRPCInvocation("`, methName, `", args, nil)`) g.P("if interceptor == nil {") - g.P("result := base.GetProxyImpl().Invoke(invo)") + g.P("result := base.GetProxyImpl().Invoke(context.Background(), invo)") g.P("return result.Result(), result.Error()") g.P("}") @@ -276,7 +275,7 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * g.P("}") g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {") - g.P("result := base.GetProxyImpl().Invoke(invo)") + g.P("result := base.GetProxyImpl().Invoke(context.Background(), invo)") g.P("return result.Result(), result.Error()") g.P("}") diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go index 63783040f9840c26dbd0cc843e9d49cdc981e64a..4017b65dd5c35ef19795b390e40d7afff6699306 100644 --- a/protocol/grpc/server.go +++ b/protocol/grpc/server.go @@ -37,24 +37,27 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Server ... +// Server is a gRPC server type Server struct { grpcServer *grpc.Server } -// NewServer ... +// NewServer creates a new server func NewServer() *Server { return &Server{} } -// DubboGrpcService ... +// DubboGrpcService is gRPC service type DubboGrpcService interface { + // SetProxyImpl sets proxy. SetProxyImpl(impl protocol.Invoker) + // GetProxyImpl gets proxy. GetProxyImpl() protocol.Invoker + // ServiceDesc gets an RPC service's specification. ServiceDesc() *grpc.ServiceDesc } -// Start ... +// Start gRPC server with @url func (s *Server) Start(url common.URL) { var ( addr string @@ -106,7 +109,7 @@ func (s *Server) Start(url common.URL) { }() } -// Stop ... +// Stop gRPC server func (s *Server) Stop() { s.grpcServer.Stop() } diff --git a/protocol/invocation.go b/protocol/invocation.go index f32f2c3449ac063ecb89952bd4653312a07a3df4..ba5949794c0120874ebdf31cfb1fd9c7d8ac08e4 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -21,14 +21,26 @@ import ( "reflect" ) -// Invocation ... +// Invocation is a invocation for each remote method. type Invocation interface { + // MethodName gets invocation method name. MethodName() string + // ParameterTypes gets invocation parameter types. ParameterTypes() []reflect.Type + // ParameterValues gets invocation parameter values. ParameterValues() []reflect.Value + // Arguments gets arguments. Arguments() []interface{} + // Reply gets response of request Reply() interface{} + // Attachments gets all attachments Attachments() map[string]string + // AttachmentsByKey gets attachment by key , if nil then return default value AttachmentsByKey(string, string) string + // Attributes refers to dubbo 2.7.6. It is different from attachment. It is used in internal process. + Attributes() map[string]interface{} + // AttributeByKey gets attribute by key , if nil then return default value + AttributeByKey(string, interface{}) interface{} + // Invoker gets the invoker in current context. Invoker() Invoker } diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index b207fd0b0cc4eb7de8409a8c46c6fc9ef0baa5c7..b8b5b509702ea5ee62df83eb55bf7f1c86625b26 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -31,7 +31,7 @@ import ( // /////////////////////////// // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation -// RPCInvocation ... +// nolint type RPCInvocation struct { methodName string parameterTypes []reflect.Type @@ -40,64 +40,70 @@ type RPCInvocation struct { reply interface{} callBack interface{} attachments map[string]string - invoker protocol.Invoker - lock sync.RWMutex + // Refer to dubbo 2.7.6. It is different from attachment. It is used in internal process. + attributes map[string]interface{} + invoker protocol.Invoker + lock sync.RWMutex } -// NewRPCInvocation ... +// NewRPCInvocation creates a RPC invocation. func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation { return &RPCInvocation{ methodName: methodName, arguments: arguments, attachments: attachments, + attributes: make(map[string]interface{}, 8), } } -// NewRPCInvocationWithOptions ... +// NewRPCInvocationWithOptions creates a RPC invocation with @opts. func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { invo := &RPCInvocation{} for _, opt := range opts { opt(invo) } + if invo.attributes == nil { + invo.attributes = make(map[string]interface{}) + } return invo } -// MethodName ... +// MethodName gets RPC invocation method name. func (r *RPCInvocation) MethodName() string { return r.methodName } -// ParameterTypes ... +// ParameterTypes gets RPC invocation parameter types. func (r *RPCInvocation) ParameterTypes() []reflect.Type { return r.parameterTypes } -// ParameterValues ... +// ParameterValues gets RPC invocation parameter values. func (r *RPCInvocation) ParameterValues() []reflect.Value { return r.parameterValues } -// Arguments ... +// Arguments gets RPC arguments. func (r *RPCInvocation) Arguments() []interface{} { return r.arguments } -// Reply ... +// Reply gets response of RPC request. func (r *RPCInvocation) Reply() interface{} { return r.reply } -// SetReply ... +// SetReply sets response of RPC request. func (r *RPCInvocation) SetReply(reply interface{}) { r.reply = reply } -// Attachments ... +// Attachments gets all attachments of RPC. func (r *RPCInvocation) Attachments() map[string]string { return r.attachments } -// AttachmentsByKey ... +// AttachmentsByKey gets RPC attachment by key , if nil then return default value. func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string { r.lock.RLock() defer r.lock.RUnlock() @@ -111,7 +117,23 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string return defaultValue } -// SetAttachments ... +// Attributes gets all attributes of RPC. +func (r *RPCInvocation) Attributes() map[string]interface{} { + return r.attributes +} + +// AttributeByKey gets attribute by @key. If it is not exist, it will return default value. +func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) interface{} { + r.lock.RLock() + defer r.lock.RUnlock() + value, ok := r.attributes[key] + if ok { + return value + } + return defaultValue +} + +// SetAttachments sets attribute by @key and @value. func (r *RPCInvocation) SetAttachments(key string, value string) { r.lock.Lock() defer r.lock.Unlock() @@ -121,22 +143,29 @@ func (r *RPCInvocation) SetAttachments(key string, value string) { r.attachments[key] = value } -// Invoker ... +// SetAttribute sets attribute by @key and @value. +func (r *RPCInvocation) SetAttribute(key string, value interface{}) { + r.lock.Lock() + defer r.lock.Unlock() + r.attributes[key] = value +} + +// Invoker gets the invoker in current context. func (r *RPCInvocation) Invoker() protocol.Invoker { return r.invoker } -// SetInvoker ... -func (r *RPCInvocation) SetInvoker() protocol.Invoker { - return r.invoker +// nolint +func (r *RPCInvocation) SetInvoker(invoker protocol.Invoker) { + r.invoker = invoker } -// CallBack ... +// CallBack sets RPC callback method. func (r *RPCInvocation) CallBack() interface{} { return r.callBack } -// SetCallBack ... +// SetCallBack sets RPC callback method. func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } @@ -147,56 +176,56 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { type option func(invo *RPCInvocation) -// WithMethodName ... +// WithMethodName creates option with @methodName. func WithMethodName(methodName string) option { return func(invo *RPCInvocation) { invo.methodName = methodName } } -// WithParameterTypes ... +// WithParameterTypes creates option with @parameterTypes. func WithParameterTypes(parameterTypes []reflect.Type) option { return func(invo *RPCInvocation) { invo.parameterTypes = parameterTypes } } -// WithParameterValues ... +// WithParameterValues creates option with @parameterValues func WithParameterValues(parameterValues []reflect.Value) option { return func(invo *RPCInvocation) { invo.parameterValues = parameterValues } } -// WithArguments ... +// WithArguments creates option with @arguments function. func WithArguments(arguments []interface{}) option { return func(invo *RPCInvocation) { invo.arguments = arguments } } -// WithReply ... +// WithReply creates option with @reply function. func WithReply(reply interface{}) option { return func(invo *RPCInvocation) { invo.reply = reply } } -// WithCallBack ... +// WithCallBack creates option with @callback function. func WithCallBack(callBack interface{}) option { return func(invo *RPCInvocation) { invo.callBack = callBack } } -// WithAttachments ... +// WithAttachments creates option with @attachments. func WithAttachments(attachments map[string]string) option { return func(invo *RPCInvocation) { invo.attachments = attachments } } -// WithInvoker ... +// WithInvoker creates option with @invoker. func WithInvoker(invoker protocol.Invoker) option { return func(invo *RPCInvocation) { invo.invoker = invoker diff --git a/protocol/invoker.go b/protocol/invoker.go index bb71bab1cfa2ede7fb035912ae996f9adb7411e0..3ca370479cbe2255f26628b855b11b07396f1b6d 100644 --- a/protocol/invoker.go +++ b/protocol/invoker.go @@ -31,6 +31,7 @@ import ( // Extension - Invoker type Invoker interface { common.Node + // Invoke the invocation and return result. Invoke(context.Context, Invocation) Result } @@ -38,14 +39,14 @@ type Invoker interface { // base invoker ///////////////////////////// -// BaseInvoker ... +// BaseInvoker provides default invoker implement type BaseInvoker struct { url common.URL available bool destroyed bool } -// NewBaseInvoker ... +// NewBaseInvoker creates a new BaseInvoker func NewBaseInvoker(url common.URL) *BaseInvoker { return &BaseInvoker{ url: url, @@ -54,27 +55,27 @@ func NewBaseInvoker(url common.URL) *BaseInvoker { } } -// GetUrl ... +// GetUrl gets base invoker URL func (bi *BaseInvoker) GetUrl() common.URL { return bi.url } -// IsAvailable ... +// IsAvailable gets available flag func (bi *BaseInvoker) IsAvailable() bool { return bi.available } -// IsDestroyed ... +// IsDestroyed gets destroyed flag func (bi *BaseInvoker) IsDestroyed() bool { return bi.destroyed } -// Invoke ... +// Invoke provides default invoker implement func (bi *BaseInvoker) Invoke(context context.Context, invocation Invocation) Result { return &RPCResult{} } -// Destroy ... +// Destroy changes available and destroyed flag func (bi *BaseInvoker) Destroy() { logger.Infof("Destroy invoker: %s", bi.GetUrl().String()) bi.destroyed = true diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go index ba7197dbc857c2ed7acda1a9f246a5b826e86915..5fca66d99399b2974f858cbedb31d9615a303637 100644 --- a/protocol/jsonrpc/http.go +++ b/protocol/jsonrpc/http.go @@ -63,7 +63,7 @@ type Request struct { // HTTP Client // //////////////////////////////////////////// -// HTTPOptions ... +// HTTPOptions is a HTTP option include HandshakeTimeout and HTTPTimeout. type HTTPOptions struct { HandshakeTimeout time.Duration HTTPTimeout time.Duration @@ -74,13 +74,13 @@ var defaultHTTPOptions = HTTPOptions{ HTTPTimeout: 3 * time.Second, } -// HTTPClient ... +// HTTPClient is a HTTP client ,include ID and options. type HTTPClient struct { ID int64 options HTTPOptions } -// NewHTTPClient ... +// NewHTTPClient creates a new HTTP client with HTTPOptions. func NewHTTPClient(opt *HTTPOptions) *HTTPClient { if opt == nil { opt = &defaultHTTPOptions @@ -100,7 +100,7 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient { } } -// NewRequest ... +// NewRequest creates a new HTTP request with @service ,@method and @arguments. func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request { return &Request{ @@ -114,7 +114,7 @@ func (c *HTTPClient) NewRequest(service common.URL, method string, args interfac } } -// Call ... +// Call makes a HTTP call with @ctx , @service ,@req and @rsp func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error { // header httpHeader := http.Header{} @@ -172,7 +172,7 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) httpReq.Close = true reqBuf := bytes.NewBuffer(make([]byte, 0)) - if err := httpReq.Write(reqBuf); err != nil { + if err = httpReq.Write(reqBuf); err != nil { return nil, perrors.WithStack(err) } @@ -191,7 +191,7 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) } setNetConnTimeout(tcpConn, c.options.HTTPTimeout) - if _, err := reqBuf.WriteTo(tcpConn); err != nil { + if _, err = reqBuf.WriteTo(tcpConn); err != nil { return nil, perrors.WithStack(err) } diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index f8480bf32e15ad428209eedb72757a299455eb20..576591940dd3021e7bbd9d2eda0ac5498391a1f7 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -48,7 +48,15 @@ type ( } ) -func TestHTTPClient_Call(t *testing.T) { +const ( + mockJsonCommonUrl = "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider" +) + +func TestHTTPClientCall(t *testing.T) { methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", &UserProvider{}) assert.NoError(t, err) @@ -56,11 +64,7 @@ func TestHTTPClient_Call(t *testing.T) { // Export proto := GetProtocol() - url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider") + url, err := common.NewURL(mockJsonCommonUrl) assert.NoError(t, err) proto.Export(&proxy_factory.ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go index d1c2a858b4e4223ac32fc1160b56f6ee1862c8ce..7b05a229437958b44a405780bbe1649a995478d0 100644 --- a/protocol/jsonrpc/json.go +++ b/protocol/jsonrpc/json.go @@ -37,7 +37,7 @@ const ( VERSION = "2.0" ) -// CodecData ... +// CodecData is codec data for json RPC. type CodecData struct { ID int64 Method string @@ -64,11 +64,12 @@ type Error struct { Data interface{} `json:"data,omitempty"` } +// Error decodes response error for a string. func (e *Error) Error() string { buf, err := json.Marshal(e) if err != nil { - msg, err := json.Marshal(err.Error()) - if err != nil { + msg, retryErr := json.Marshal(err.Error()) + if retryErr != nil { msg = []byte("jsonrpc2.Error: json.Marshal failed") } return fmt.Sprintf(`{"code":%d,"message":%s}`, -32001, string(msg)) @@ -114,6 +115,7 @@ func newJsonClientCodec() *jsonClientCodec { } } +// Write codec data as byte. func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { // If return error: it will be returned as is for this call. // Allow param to be only Array, Slice, Map or Struct. @@ -122,10 +124,8 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { if param != nil { switch k := reflect.TypeOf(param).Kind(); k { case reflect.Map: - if reflect.TypeOf(param).Key().Kind() == reflect.String { - if reflect.ValueOf(param).IsNil() { - param = nil - } + if reflect.TypeOf(param).Key().Kind() == reflect.String && reflect.ValueOf(param).IsNil() { + param = nil } case reflect.Slice: if reflect.ValueOf(param).IsNil() { @@ -133,12 +133,10 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { } case reflect.Array, reflect.Struct: case reflect.Ptr: - switch k := reflect.TypeOf(param).Elem().Kind(); k { + switch ptrK := reflect.TypeOf(param).Elem().Kind(); ptrK { case reflect.Map: - if reflect.TypeOf(param).Elem().Key().Kind() == reflect.String { - if reflect.ValueOf(param).Elem().IsNil() { - param = nil - } + if reflect.TypeOf(param).Elem().Key().Kind() == reflect.String && reflect.ValueOf(param).Elem().IsNil() { + param = nil } case reflect.Slice: if reflect.ValueOf(param).Elem().IsNil() { @@ -146,7 +144,7 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { } case reflect.Array, reflect.Struct: default: - return nil, perrors.New("unsupported param type: Ptr to " + k.String()) + return nil, perrors.New("unsupported param type: Ptr to " + ptrK.String()) } default: return nil, perrors.New("unsupported param type: " + k.String()) @@ -170,6 +168,7 @@ func (c *jsonClientCodec) Write(d *CodecData) ([]byte, error) { return buf.Bytes(), nil } +// Read bytes as structured data func (c *jsonClientCodec) Read(streamBytes []byte, x interface{}) error { c.rsp.reset() @@ -223,6 +222,7 @@ func (r *serverRequest) reset() { } } +// UnmarshalJSON unmarshals JSON for server request. func (r *serverRequest) UnmarshalJSON(raw []byte) error { r.reset() @@ -281,7 +281,7 @@ type serverResponse struct { Error interface{} `json:"error,omitempty"` } -// ServerCodec ... +// ServerCodec is codec data for request server. type ServerCodec struct { req serverRequest } @@ -296,7 +296,7 @@ func newServerCodec() *ServerCodec { return &ServerCodec{} } -// ReadHeader ... +// ReadHeader reads header and unmarshal to server codec func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error { if header["HttpMethod"] != "POST" { return &Error{Code: -32601, Message: "Method not found"} @@ -328,7 +328,7 @@ func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error { return nil } -// ReadBody ... +// ReadBody reads @x as request body. func (c *ServerCodec) ReadBody(x interface{}) error { // If x!=nil and return error e: // - Write() will be called with e.Error() in r.Error @@ -339,7 +339,7 @@ func (c *ServerCodec) ReadBody(x interface{}) error { return nil } - // 鍦ㄨ繖閲屾妸璇锋眰鍙傛暟json 瀛楃涓茶浆鎹㈡垚浜嗙浉搴旂殑struct + // the request parameter JSON string is converted to the corresponding struct params := []byte(*c.req.Params) if err := json.Unmarshal(*c.req.Params, x); err != nil { // Note: if c.request.Params is nil it's not an error, it's an optional member. @@ -362,7 +362,7 @@ func (c *ServerCodec) ReadBody(x interface{}) error { return nil } -// NewError ... +// NewError creates a error with @code and @message func NewError(code int, message string) *Error { return &Error{Code: code, Message: message} } @@ -380,6 +380,7 @@ func newError(message string) *Error { } } +// Write responses as byte func (c *ServerCodec) Write(errMsg string, x interface{}) ([]byte, error) { // If return error: nothing happens. // In r.Error will be "" or .Error() of error returned by: diff --git a/protocol/jsonrpc/json_test.go b/protocol/jsonrpc/json_test.go index ade74246121b5f275c8dbeaa5923228dbab2804f..a3814e9ad5e1df2c3a8d8cc4305e5787b44596ec 100644 --- a/protocol/jsonrpc/json_test.go +++ b/protocol/jsonrpc/json_test.go @@ -30,7 +30,7 @@ type TestData struct { Test string } -func TestJsonClientCodec_Write(t *testing.T) { +func TestJsonClientCodecWrite(t *testing.T) { cd := &CodecData{ ID: 1, Method: "GetUser", @@ -46,7 +46,7 @@ func TestJsonClientCodec_Write(t *testing.T) { assert.EqualError(t, err, "unsupported param type: int") } -func TestJsonClientCodec_Read(t *testing.T) { +func TestJsonClientCodecRead(t *testing.T) { codec := newJsonClientCodec() codec.pending[1] = "GetUser" rsp := &TestData{} @@ -60,7 +60,7 @@ func TestJsonClientCodec_Read(t *testing.T) { assert.EqualError(t, err, "{\"code\":-32000,\"message\":\"error\"}") } -func TestServerCodec_Write(t *testing.T) { +func TestServerCodecWrite(t *testing.T) { codec := newServerCodec() a := json.RawMessage([]byte("1")) codec.req = serverRequest{Version: "1.0", Method: "GetUser", ID: &a} @@ -73,7 +73,7 @@ func TestServerCodec_Write(t *testing.T) { assert.Equal(t, "{\"jsonrpc\":\"2.0\",\"id\":1,\"result\":{\"Test\":\"test\"},\"error\":{\"code\":-32000,\"message\":\"error\"}}\n", string(data)) } -func TestServerCodec_Read(t *testing.T) { +func TestServerCodecRead(t *testing.T) { codec := newServerCodec() header := map[string]string{} err := codec.ReadHeader(header, []byte("{\"jsonrpc\":\"2.0\",\"method\":\"GetUser\",\"params\":[\"args\",2],\"id\":1}\n")) diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go index c61cf9adaebe9105a87ece1dcbae4dbe706cb3fc..6f75f6aeae9fb1a8d75ded5f558e0ddae84686a0 100644 --- a/protocol/jsonrpc/jsonrpc_exporter.go +++ b/protocol/jsonrpc/jsonrpc_exporter.go @@ -28,19 +28,19 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// JsonrpcExporter ... +// JsonrpcExporter is JSON RPC exporter and extends from base invoker. type JsonrpcExporter struct { protocol.BaseExporter } -// NewJsonrpcExporter ... +// NewJsonrpcExporter creates JSON RPC exporter with @key, @invoker and @exporterMap func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *JsonrpcExporter { return &JsonrpcExporter{ BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap), } } -// Unexport ... +// Unexport exported JSON RPC service. func (je *JsonrpcExporter) Unexport() { serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index b6e194ce0e93e84c164eccf8574e5eb20430f6e8..d84b980216ade6e569e68af31fc90e1ea16b3056 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -29,13 +29,13 @@ import ( invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) -// JsonrpcInvoker ... +// JsonrpcInvoker is JSON RPC invoker type JsonrpcInvoker struct { protocol.BaseInvoker client *HTTPClient } -// NewJsonrpcInvoker ... +// NewJsonrpcInvoker creates JSON RPC invoker with @url and @client func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { return &JsonrpcInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), @@ -43,7 +43,7 @@ func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { } } -// Invoke ... +// Invoke the JSON RPC invocation and return result. func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go index 0f14ba11e2dec18bbd4d63e87e8c0fb2727f3755..d7124ca07c6ba6d79dc72e7fb6bd98cd4b3a97b2 100644 --- a/protocol/jsonrpc/jsonrpc_invoker_test.go +++ b/protocol/jsonrpc/jsonrpc_invoker_test.go @@ -34,7 +34,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func TestJsonrpcInvoker_Invoke(t *testing.T) { +func TestJsonrpcInvokerInvoke(t *testing.T) { methods, err := common.ServiceMap.Register("UserProvider", "jsonrpc", &UserProvider{}) assert.NoError(t, err) diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index bed7099ab60a6c05c3799f993c0bb348a4b00f02..90a6bf5ef7aa017488f723804b22cc613850bcf2 100644 --- a/protocol/jsonrpc/jsonrpc_protocol.go +++ b/protocol/jsonrpc/jsonrpc_protocol.go @@ -44,14 +44,14 @@ func init() { var jsonrpcProtocol *JsonrpcProtocol -// JsonrpcProtocol ... +// JsonrpcProtocol is JSON RPC protocol. type JsonrpcProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } -// NewJsonrpcProtocol ... +// NewJsonrpcProtocol creates JSON RPC protocol func NewJsonrpcProtocol() *JsonrpcProtocol { return &JsonrpcProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -59,7 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol { } } -// Export ... +// Export JSON RPC service for remote invocation func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := strings.TrimPrefix(url.Path, "/") @@ -74,7 +74,7 @@ func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } -// Refer ... +// Refer a remote JSON PRC service from registry func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker { //default requestTimeout var requestTimeout = config.GetConsumerConfig().RequestTimeout @@ -93,7 +93,7 @@ func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker { return invoker } -// Destroy ... +// Destroy will destroy all invoker and exporter, so it only is called once. func (jp *JsonrpcProtocol) Destroy() { logger.Infof("jsonrpcProtocol destroy.") @@ -109,8 +109,8 @@ func (jp *JsonrpcProtocol) Destroy() { func (jp *JsonrpcProtocol) openServer(url common.URL) { _, ok := jp.serverMap[url.Location] if !ok { - _, ok := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/")) - if !ok { + _, loadOk := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/")) + if !loadOk { panic("[JsonrpcProtocol]" + url.Key() + "is not existing") } @@ -125,7 +125,7 @@ func (jp *JsonrpcProtocol) openServer(url common.URL) { } } -// GetProtocol ... +// GetProtocol gets JSON RPC protocol. func GetProtocol() protocol.Protocol { if jsonrpcProtocol == nil { jsonrpcProtocol = NewJsonrpcProtocol() diff --git a/protocol/jsonrpc/jsonrpc_protocol_test.go b/protocol/jsonrpc/jsonrpc_protocol_test.go index c00bed12fe9fbb4937f21810cee548a25e3b1c05..10a9016913c77551efc54a92149ef377ade399f5 100644 --- a/protocol/jsonrpc/jsonrpc_protocol_test.go +++ b/protocol/jsonrpc/jsonrpc_protocol_test.go @@ -34,7 +34,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func TestJsonrpcProtocol_Export(t *testing.T) { +func TestJsonrpcProtocolExport(t *testing.T) { // Export proto := GetProtocol() url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + @@ -65,7 +65,7 @@ func TestJsonrpcProtocol_Export(t *testing.T) { assert.False(t, ok) } -func TestJsonrpcProtocol_Refer(t *testing.T) { +func TestJsonrpcProtocolRefer(t *testing.T) { // Refer proto := GetProtocol() url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go index 8600f02dad3d32d797613823de0bbe40261d2e71..aa458a1614df29997b05ac4462200f9e9ffffc25 100644 --- a/protocol/jsonrpc/server.go +++ b/protocol/jsonrpc/server.go @@ -59,7 +59,7 @@ const ( PathPrefix = byte('/') ) -// Server ... +// Server is JSON RPC server wrapper type Server struct { done chan struct{} once sync.Once @@ -69,7 +69,7 @@ type Server struct { timeout time.Duration } -// NewServer ... +// NewServer creates new JSON RPC server. func NewServer() *Server { return &Server{ done: make(chan struct{}), @@ -228,7 +228,7 @@ func accept(listener net.Listener, fn func(net.Conn)) error { } } -// Start ... +// Start JSON RPC server then ready for accept request. func (s *Server) Start(url common.URL) { listener, err := net.Listen("tcp", url.Location) if err != nil { @@ -255,7 +255,7 @@ func (s *Server) Start(url common.URL) { }() } -// Stop ... +// Stop JSON RPC server, just can be call once. func (s *Server) Stop() { s.once.Do(func() { close(s.done) @@ -349,9 +349,9 @@ func serveRequest(ctx context.Context, constant.PATH_KEY: path, constant.VERSION_KEY: codec.req.Version})) if err := result.Error(); err != nil { - rspStream, err := codec.Write(err.Error(), invalidRequest) - if err != nil { - return perrors.WithStack(err) + rspStream, codecErr := codec.Write(err.Error(), invalidRequest) + if codecErr != nil { + return perrors.WithStack(codecErr) } if errRsp := sendErrorResp(header, rspStream); errRsp != nil { logger.Warnf("Exporter: sendErrorResp(header:%#v, error:%v) = error:%s", diff --git a/protocol/protocol.go b/protocol/protocol.go index a873469a8ba361c9dfc922b071ffbf256c6a8b98..6bed5ec4c2be82edda7a642eb342381c53387460 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -29,15 +29,20 @@ import ( // Protocol // Extension - protocol type Protocol interface { + // Export service for remote invocation Export(invoker Invoker) Exporter + // Refer a remote service Refer(url common.URL) Invoker + // Destroy will destroy all invoker and exporter, so it only is called once. Destroy() } // Exporter // wrapping invoker type Exporter interface { + // GetInvoker gets invoker. GetInvoker() Invoker + // Unexport exported service. Unexport() } @@ -45,45 +50,45 @@ type Exporter interface { // base protocol ///////////////////////////// -// BaseProtocol ... +// BaseProtocol is default protocol implement. type BaseProtocol struct { exporterMap *sync.Map invokers []Invoker } -// NewBaseProtocol ... +// NewBaseProtocol creates a new BaseProtocol func NewBaseProtocol() BaseProtocol { return BaseProtocol{ exporterMap: new(sync.Map), } } -// SetExporterMap ... +// SetExporterMap set @exporter with @key to local memory. func (bp *BaseProtocol) SetExporterMap(key string, exporter Exporter) { bp.exporterMap.Store(key, exporter) } -// ExporterMap ... +// ExporterMap gets exporter map. func (bp *BaseProtocol) ExporterMap() *sync.Map { return bp.exporterMap } -// SetInvokers ... +// SetInvokers sets invoker into local memory func (bp *BaseProtocol) SetInvokers(invoker Invoker) { bp.invokers = append(bp.invokers, invoker) } -// Invokers ... +// Invokers gets all invokers func (bp *BaseProtocol) Invokers() []Invoker { return bp.invokers } -// Export ... +// Export is default export implement. func (bp *BaseProtocol) Export(invoker Invoker) Exporter { return NewBaseExporter("base", invoker, bp.exporterMap) } -// Refer ... +// Refer is default refer implement. func (bp *BaseProtocol) Refer(url common.URL) Invoker { return NewBaseInvoker(url) } @@ -113,14 +118,14 @@ func (bp *BaseProtocol) Destroy() { // base exporter ///////////////////////////// -// BaseExporter ... +// BaseExporter is default exporter implement. type BaseExporter struct { key string invoker Invoker exporterMap *sync.Map } -// NewBaseExporter ... +// NewBaseExporter creates a new BaseExporter func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseExporter { return &BaseExporter{ key: key, @@ -129,13 +134,13 @@ func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseEx } } -// GetInvoker ... +// GetInvoker gets invoker func (de *BaseExporter) GetInvoker() Invoker { return de.invoker } -// Unexport ... +// Unexport exported service. func (de *BaseExporter) Unexport() { logger.Infof("Exporter unexport.") de.invoker.Destroy() diff --git a/protocol/protocolwrapper/mock_protocol_filter.go b/protocol/protocolwrapper/mock_protocol_filter.go index dedf8aa64b6ae1b7b4782350e2625b02171aac44..2e9ffed06f03d196c91f6679df21538612bac405 100644 --- a/protocol/protocolwrapper/mock_protocol_filter.go +++ b/protocol/protocolwrapper/mock_protocol_filter.go @@ -28,19 +28,22 @@ import ( type mockProtocolFilter struct{} -// NewMockProtocolFilter ... +// NewMockProtocolFilter creates a new mock protocol func NewMockProtocolFilter() protocol.Protocol { return &mockProtocolFilter{} } +// Export mock service for remote invocation func (pfw *mockProtocolFilter) Export(invoker protocol.Invoker) protocol.Exporter { return protocol.NewBaseExporter("key", invoker, &sync.Map{}) } +// Refer a mock remote service func (pfw *mockProtocolFilter) Refer(url common.URL) protocol.Invoker { return protocol.NewBaseInvoker(url) } +// Destroy will do nothing func (pfw *mockProtocolFilter) Destroy() { - + return } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go index 70d2da0faed3bc9797eb23cec653bea05d445d91..cba1d5d5bce8c3de387381d17cc3f7965bf3adac 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper.go @@ -31,7 +31,7 @@ import ( ) const ( - // FILTER ... + // FILTER is protocol key. FILTER = "filter" ) @@ -45,7 +45,7 @@ type ProtocolFilterWrapper struct { protocol protocol.Protocol } -// Export ... +// Export service for remote invocation func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Exporter { if pfw.protocol == nil { pfw.protocol = extension.GetProtocol(invoker.GetUrl().Protocol) @@ -54,7 +54,7 @@ func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Expo return pfw.protocol.Export(invoker) } -// Refer ... +// Refer a remote service func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker { if pfw.protocol == nil { pfw.protocol = extension.GetProtocol(url.Protocol) @@ -62,7 +62,7 @@ func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker { return buildInvokerChain(pfw.protocol.Refer(url), constant.REFERENCE_FILTER_KEY) } -// Destroy ... +// Destroy will destroy all invoker and exporter. func (pfw *ProtocolFilterWrapper) Destroy() { pfw.protocol.Destroy() } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go index 8491d57462d47d6af72040d41b78dcb30e6da697..b03ea7b9b66d926aff8851f88e1bd7434b254903 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go @@ -36,7 +36,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) -func TestProtocolFilterWrapper_Export(t *testing.T) { +func TestProtocolFilterWrapperExport(t *testing.T) { filtProto := extension.GetProtocol(FILTER) filtProto.(*ProtocolFilterWrapper).protocol = &protocol.BaseProtocol{} @@ -48,7 +48,7 @@ func TestProtocolFilterWrapper_Export(t *testing.T) { assert.True(t, ok) } -func TestProtocolFilterWrapper_Refer(t *testing.T) { +func TestProtocolFilterWrapperRefer(t *testing.T) { filtProto := extension.GetProtocol(FILTER) filtProto.(*ProtocolFilterWrapper).protocol = &protocol.BaseProtocol{} diff --git a/protocol/rest/client/client_impl/resty_client.go b/protocol/rest/client/client_impl/resty_client.go index b60f50a5a70cde01a051dbb2a4490cbb792e0116..f301d945045a445b5370252044442080f042d126 100644 --- a/protocol/rest/client/client_impl/resty_client.go +++ b/protocol/rest/client/client_impl/resty_client.go @@ -50,7 +50,7 @@ func NewRestyClient(restOption *client.RestOptions) client.RestClient { client := resty.New() client.SetTransport( &http.Transport{ - DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + DialContext: func(_ context.Context, network, addr string) (net.Conn, error) { c, err := net.DialTimeout(network, addr, restOption.ConnectTimeout) if err != nil { return nil, err diff --git a/protocol/rest/config/reader/rest_config_reader_test.go b/protocol/rest/config/reader/rest_config_reader_test.go index d2dba40b9b85a6cd7772e0fee619720c79e91eb4..c6f891262ce3ba5ec6645ade9084005d14716789 100644 --- a/protocol/rest/config/reader/rest_config_reader_test.go +++ b/protocol/rest/config/reader/rest_config_reader_test.go @@ -31,7 +31,7 @@ import ( "github.com/apache/dubbo-go/protocol/rest/config" ) -func TestRestConfigReader_ReadConsumerConfig(t *testing.T) { +func TestRestConfigReaderReadConsumerConfig(t *testing.T) { bs, err := yaml.LoadYMLConfig("./testdata/consumer_config.yml") assert.NoError(t, err) configReader := NewRestConfigReader() @@ -40,7 +40,7 @@ func TestRestConfigReader_ReadConsumerConfig(t *testing.T) { assert.NotEmpty(t, config.GetRestConsumerServiceConfigMap()) } -func TestRestConfigReader_ReadProviderConfig(t *testing.T) { +func TestRestConfigReaderReadProviderConfig(t *testing.T) { bs, err := yaml.LoadYMLConfig("./testdata/provider_config.yml") assert.NoError(t, err) configReader := NewRestConfigReader() diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go index 2ea260c58d03c27a691e48b953ce6d64f75040a2..9df97a211e9d90daa3206b94926ceeac42df4606 100644 --- a/protocol/rest/rest_invoker_test.go +++ b/protocol/rest/rest_invoker_test.go @@ -39,7 +39,15 @@ import ( "github.com/apache/dubbo-go/protocol/rest/server/server_impl" ) -func TestRestInvoker_Invoke(t *testing.T) { +const ( + mockRestCommonUrl = "rest://127.0.0.1:8877/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245" +) + +func TestRestInvokerInvoke(t *testing.T) { // Refer proto := GetRestProtocol() defer proto.Destroy() @@ -55,11 +63,7 @@ func TestRestInvoker_Invoke(t *testing.T) { chain.ProcessFilter(request, response) }) - url, err := common.NewURL("rest://127.0.0.1:8877/com.ikurento.user.UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") + url, err := common.NewURL(mockRestCommonUrl) assert.NoError(t, err) _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) assert.NoError(t, err) diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go index 9117148777ca868cb7d2672236e800c836d3de84..9ff4e7df7fe41d7fd028bf476cc2192cf7cefcca 100644 --- a/protocol/rest/rest_protocol_test.go +++ b/protocol/rest/rest_protocol_test.go @@ -38,14 +38,10 @@ import ( rest_config "github.com/apache/dubbo-go/protocol/rest/config" ) -func TestRestProtocol_Refer(t *testing.T) { +func TestRestProtocolRefer(t *testing.T) { // Refer proto := GetRestProtocol() - url, err := common.NewURL("rest://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") + url, err := common.NewURL(mockRestCommonUrl) assert.NoError(t, err) con := config.ConsumerConfig{ ConnectTimeout: 5 * time.Second, @@ -71,14 +67,10 @@ func TestRestProtocol_Refer(t *testing.T) { assert.Equal(t, 0, invokersLen) } -func TestRestProtocol_Export(t *testing.T) { +func TestRestProtocolExport(t *testing.T) { // Export proto := GetRestProtocol() - url, err := common.NewURL("rest://127.0.0.1:8888/com.ikurento.user.UserProvider?anyhost=true&" + - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245") + url, err := common.NewURL(mockRestCommonUrl) assert.NoError(t, err) _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) assert.NoError(t, err) diff --git a/protocol/result.go b/protocol/result.go index 34e76d2dddbaed33b2e2c015631443565cfaea87..2e7a6e492a60888ec9d9f420c77e6b77aee6aa70 100644 --- a/protocol/result.go +++ b/protocol/result.go @@ -17,15 +17,23 @@ package protocol -// Result ... +// Result is a RPC result type Result interface { + // SetError sets error. SetError(error) + // Error gets error. Error() error + // SetResult sets invoker result. SetResult(interface{}) + // Result gets invoker result. Result() interface{} + // SetAttachments replaces the existing attachments with the specified param. SetAttachments(map[string]string) + // Attachments gets all attachments Attachments() map[string]string + // AddAttachment adds the specified map to existing attachments in this instance. AddAttachment(string, string) + // Attachment gets attachment by key with default value. Attachment(string, string) string } @@ -33,48 +41,49 @@ type Result interface { // Result Impletment of RPC ///////////////////////////// -// RPCResult ... +// RPCResult is default RPC result. type RPCResult struct { Attrs map[string]string Err error Rest interface{} } -// SetError ... +// SetError sets error. func (r *RPCResult) SetError(err error) { r.Err = err } +// Error gets error. func (r *RPCResult) Error() error { return r.Err } -// SetResult ... +// SetResult sets invoker result. func (r *RPCResult) SetResult(rest interface{}) { r.Rest = rest } -// Result ... +// Result gets invoker result. func (r *RPCResult) Result() interface{} { return r.Rest } -// SetAttachments ... +// SetAttachments replaces the existing attachments with the specified param. func (r *RPCResult) SetAttachments(attr map[string]string) { r.Attrs = attr } -// Attachments ... +// Attachments gets all attachments func (r *RPCResult) Attachments() map[string]string { return r.Attrs } -// AddAttachment ... +// AddAttachment adds the specified map to existing attachments in this instance. func (r *RPCResult) AddAttachment(key, value string) { r.Attrs[key] = value } -// Attachment ... +// Attachment gets attachment by key with default value. func (r *RPCResult) Attachment(key, defaultValue string) string { v, ok := r.Attrs[key] if !ok { diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go index 13be47c98ece1cc006250ad49ab2b9a8c3b1f625..60becfb34135470b0e69972c25a743f44efe19d5 100644 --- a/protocol/rpc_status.go +++ b/protocol/rpc_status.go @@ -32,7 +32,7 @@ var ( serviceStatistic sync.Map // url -> RPCStatus ) -// RPCStatus ... +// RPCStatus is URL statistics. type RPCStatus struct { active int32 failed int32 @@ -46,63 +46,63 @@ type RPCStatus struct { lastRequestFailedTimestamp int64 } -// GetActive ... +// GetActive gets active. func (rpc *RPCStatus) GetActive() int32 { return atomic.LoadInt32(&rpc.active) } -// GetFailed ... +// GetFailed gets failed. func (rpc *RPCStatus) GetFailed() int32 { return atomic.LoadInt32(&rpc.failed) } -// GetTotal ... +// GetTotal gets total. func (rpc *RPCStatus) GetTotal() int32 { return atomic.LoadInt32(&rpc.total) } -// GetTotalElapsed ... +// GetTotalElapsed gets total elapsed. func (rpc *RPCStatus) GetTotalElapsed() int64 { return atomic.LoadInt64(&rpc.totalElapsed) } -// GetFailedElapsed ... +// GetFailedElapsed gets failed elapsed. func (rpc *RPCStatus) GetFailedElapsed() int64 { return atomic.LoadInt64(&rpc.failedElapsed) } -// GetMaxElapsed ... +// GetMaxElapsed gets max elapsed. func (rpc *RPCStatus) GetMaxElapsed() int64 { return atomic.LoadInt64(&rpc.maxElapsed) } -// GetFailedMaxElapsed ... +// GetFailedMaxElapsed gets failed max elapsed. func (rpc *RPCStatus) GetFailedMaxElapsed() int64 { return atomic.LoadInt64(&rpc.failedMaxElapsed) } -// GetSucceededMaxElapsed ... +// GetSucceededMaxElapsed gets succeeded max elapsed. func (rpc *RPCStatus) GetSucceededMaxElapsed() int64 { return atomic.LoadInt64(&rpc.succeededMaxElapsed) } -// GetLastRequestFailedTimestamp ... +// GetLastRequestFailedTimestamp gets last request failed timestamp. func (rpc *RPCStatus) GetLastRequestFailedTimestamp() int64 { return atomic.LoadInt64(&rpc.lastRequestFailedTimestamp) } -// GetSuccessiveRequestFailureCount ... +// GetSuccessiveRequestFailureCount gets successive request failure count. func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 { return atomic.LoadInt32(&rpc.successiveRequestFailureCount) } -// GetURLStatus ... +// GetURLStatus get URL RPC status. func GetURLStatus(url common.URL) *RPCStatus { rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{}) return rpcStatus.(*RPCStatus) } -// GetMethodStatus ... +// GetMethodStatus get method RPC status. func GetMethodStatus(url common.URL, methodName string) *RPCStatus { identifier := url.Key() methodMap, found := methodStatistics.Load(identifier) @@ -122,13 +122,13 @@ func GetMethodStatus(url common.URL, methodName string) *RPCStatus { return status } -// BeginCount ... +// BeginCount gets begin count. func BeginCount(url common.URL, methodName string) { beginCount0(GetURLStatus(url)) beginCount0(GetMethodStatus(url, methodName)) } -// EndCount ... +// EndCount gets end count. func EndCount(url common.URL, methodName string, elapsed int64, succeeded bool) { endCount0(GetURLStatus(url), elapsed, succeeded) endCount0(GetMethodStatus(url, methodName), elapsed, succeeded) @@ -163,19 +163,19 @@ func endCount0(rpcStatus *RPCStatus, elapsed int64, succeeded bool) { } } -// CurrentTimeMillis ... +// CurrentTimeMillis get current timestamp func CurrentTimeMillis() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } // Destroy is used to clean all status func CleanAllStatus() { - delete1 := func(key interface{}, value interface{}) bool { + delete1 := func(key, _ interface{}) bool { methodStatistics.Delete(key) return true } methodStatistics.Range(delete1) - delete2 := func(key interface{}, value interface{}) bool { + delete2 := func(key, _ interface{}) bool { serviceStatistic.Delete(key) return true } diff --git a/protocol/rpc_status_test.go b/protocol/rpc_status_test.go index 611b7cba6e453cdf00624b4414d468278fd62cb1..cc12753cf25007ab6d1010836b203aee22d78ca9 100644 --- a/protocol/rpc_status_test.go +++ b/protocol/rpc_status_test.go @@ -30,10 +30,14 @@ import ( "github.com/apache/dubbo-go/common" ) +const ( + mockCommonDubboUrl = "dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider" +) + func TestBeginCount(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) BeginCount(url, "test") urlStatus := GetURLStatus(url) methodStatus := GetMethodStatus(url, "test") @@ -47,7 +51,7 @@ func TestBeginCount(t *testing.T) { func TestEndCount(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) EndCount(url, "test", 100, true) urlStatus := GetURLStatus(url) methodStatus := GetMethodStatus(url, "test") @@ -60,7 +64,7 @@ func TestEndCount(t *testing.T) { func TestGetMethodStatus(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) status := GetMethodStatus(url, "test") assert.NotNil(t, status) assert.Equal(t, int32(0), status.total) @@ -69,25 +73,25 @@ func TestGetMethodStatus(t *testing.T) { func TestGetUrlStatus(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) status := GetURLStatus(url) assert.NotNil(t, status) assert.Equal(t, int32(0), status.total) } -func Test_beginCount0(t *testing.T) { +func TestbeginCount0(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) status := GetURLStatus(url) beginCount0(status) assert.Equal(t, int32(1), status.active) } -func Test_All(t *testing.T) { +func TestAll(t *testing.T) { defer CleanAllStatus() - url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + url, _ := common.NewURL(mockCommonDubboUrl) request(url, "test", 100, false, true) urlStatus := GetURLStatus(url) methodStatus := GetMethodStatus(url, "test") diff --git a/registry/base_configuration_listener.go b/registry/base_configuration_listener.go index 55418318dfc52ed9f17f1ec6a18ad9ef9d8163bf..7b28d7ee1b30a03b8211c4c1efa5824c3b61453a 100644 --- a/registry/base_configuration_listener.go +++ b/registry/base_configuration_listener.go @@ -29,19 +29,19 @@ import ( "github.com/apache/dubbo-go/remoting" ) -// BaseConfigurationListener ... +// nolint type BaseConfigurationListener struct { configurators []config_center.Configurator dynamicConfiguration config_center.DynamicConfiguration defaultConfiguratorFunc func(url *common.URL) config_center.Configurator } -// Configurators ... +// Configurators gets Configurator from config center func (bcl *BaseConfigurationListener) Configurators() []config_center.Configurator { return bcl.configurators } -// InitWith ... +// InitWith will init BaseConfigurationListener by @key+@Listener+@f func (bcl *BaseConfigurationListener) InitWith(key string, listener config_center.ConfigurationListener, f func(url *common.URL) config_center.Configurator) { bcl.dynamicConfiguration = config.GetEnvInstance().GetDynamicConfiguration() if bcl.dynamicConfiguration == nil { @@ -60,7 +60,7 @@ func (bcl *BaseConfigurationListener) InitWith(key string, listener config_cente } } -// Process ... +// Process the notification event once there's any change happens on the config. func (bcl *BaseConfigurationListener) Process(event *config_center.ConfigChangeEvent) { logger.Infof("Notification of overriding rule, change type is: %v , raw config content is:%v", event.ConfigType, event.Value) if event.ConfigType == remoting.EventTypeDel { @@ -82,14 +82,14 @@ func (bcl *BaseConfigurationListener) genConfiguratorFromRawRule(rawConfig strin return nil } -// OverrideUrl ... +// OverrideUrl gets existing configuration rule and overrides provider url before exporting. func (bcl *BaseConfigurationListener) OverrideUrl(url *common.URL) { for _, v := range bcl.configurators { v.Configure(url) } } -// ToConfigurators ... +// ToConfigurators converts @urls by @f to config_center.Configurators func ToConfigurators(urls []*common.URL, f func(url *common.URL) config_center.Configurator) []config_center.Configurator { if len(urls) == 0 { return nil diff --git a/registry/consul/listener.go b/registry/consul/listener.go index b047a4c08c9f6c809ed3dca8bd0d06ceaa576cae..cf3888dd16ee4a9b504664383be69ca1faf4d842 100644 --- a/registry/consul/listener.go +++ b/registry/consul/listener.go @@ -142,7 +142,6 @@ func (l *consulListener) run() { func (l *consulListener) handler(idx uint64, raw interface{}) { var ( service *consul.ServiceEntry - event *registry.ServiceEvent url common.URL ok bool err error @@ -183,11 +182,12 @@ func (l *consulListener) handler(idx uint64, raw interface{}) { } l.urls = newUrls - for _, event = range events { + for _, event := range events { l.eventCh <- event } } +// Next returns the service event from consul. func (l *consulListener) Next() (*registry.ServiceEvent, error) { select { case event := <-l.eventCh: @@ -197,6 +197,7 @@ func (l *consulListener) Next() (*registry.ServiceEvent, error) { } } +// Close closes this listener func (l *consulListener) Close() { close(l.done) l.plan.Stop() diff --git a/registry/consul/registry.go b/registry/consul/registry.go index c5b8510a6c87068a5b4f1ce52203d401a896a6c2..4ef87394687aecc8804b2cebedd58fc0e72e8e6e 100644 --- a/registry/consul/registry.go +++ b/registry/consul/registry.go @@ -36,8 +36,7 @@ import ( ) const ( - // RegistryConnDelay ... - RegistryConnDelay = 3 + registryConnDelay = 3 ) func init() { @@ -74,6 +73,7 @@ func newConsulRegistry(url *common.URL) (registry.Registry, error) { return r, nil } +// Register service to consul registry center func (r *consulRegistry) Register(url common.URL) error { var err error @@ -95,6 +95,7 @@ func (r *consulRegistry) register(url common.URL) error { return r.client.Agent().ServiceRegister(service) } +// Unregister service from consul registry center func (r *consulRegistry) Unregister(url common.URL) error { var err error @@ -112,6 +113,7 @@ func (r *consulRegistry) unregister(url common.URL) error { return r.client.Agent().ServiceDeregister(buildId(url)) } +// Subscribe service from consul registry center func (r *consulRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) { role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) if role == common.CONSUMER { @@ -133,7 +135,7 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti return } logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) - time.Sleep(time.Duration(RegistryConnDelay) * time.Second) + time.Sleep(time.Duration(registryConnDelay) * time.Second) continue } @@ -156,10 +158,12 @@ func (r *consulRegistry) getListener(url common.URL) (registry.Listener, error) return listener, err } +// GetUrl get registry URL of consul registry center func (r *consulRegistry) GetUrl() common.URL { return *r.URL } +// IsAvailable checks consul registry center whether is available func (r *consulRegistry) IsAvailable() bool { select { case <-r.done: @@ -169,6 +173,7 @@ func (r *consulRegistry) IsAvailable() bool { } } +// Destroy consul registry center func (r *consulRegistry) Destroy() { close(r.done) } diff --git a/registry/directory/directory.go b/registry/directory/directory.go index 552aa57061c99bf92ff986b6e672743ebb375e76..e845db01f1b8f76897f2beeaee45a84537c96d83 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -46,6 +46,8 @@ func init() { extension.SetDefaultRegistryDirectory(NewRegistryDirectory) } +// RegistryDirectory implementation of Directory: +// Invoker list returned from this Directory's list method have been filtered by Routers type RegistryDirectory struct { directory.BaseDirectory cacheInvokers []protocol.Invoker @@ -61,7 +63,7 @@ type RegistryDirectory struct { forbidden atomic.Bool } -// NewRegistryDirectory ... +// NewRegistryDirectory will create a new RegistryDirectory func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster.Directory, error) { if url.SubURL == nil { return nil, perrors.Errorf("url is invalid, suburl can not be nil") @@ -79,13 +81,14 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster. return dir, nil } -//subscribe from registry +// subscribe from registry func (dir *RegistryDirectory) subscribe(url *common.URL) { dir.consumerConfigurationListener.addNotifyListener(dir) dir.referenceConfigurationListener = newReferenceConfigurationListener(dir, url) dir.registry.Subscribe(url, dir) } +// Notify monitor changes from registry,and update the cacheServices func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) { go dir.update(event) } @@ -152,9 +155,11 @@ func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) { } func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { - newInvokersList := []protocol.Invoker{} + var ( + err error + newInvokersList []protocol.Invoker + ) groupInvokersMap := make(map[string][]protocol.Invoker) - groupInvokersList := []protocol.Invoker{} dir.cacheInvokersMap.Range(func(key, value interface{}) bool { newInvokersList = append(newInvokersList, value.(protocol.Invoker)) @@ -170,6 +175,7 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { groupInvokersMap[group] = []protocol.Invoker{invoker} } } + groupInvokersList := make([]protocol.Invoker, 0, len(groupInvokersMap)) if len(groupInvokersMap) == 1 { //len is 1 it means no group setting ,so do not need cluster again for _, invokers := range groupInvokersMap { @@ -178,9 +184,13 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { } else { for _, invokers := range groupInvokersMap { staticDir := directory.NewStaticDirectory(invokers) - cluster := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) - staticDir.BuildRouterChain(invokers) - groupInvokersList = append(groupInvokersList, cluster.Join(staticDir)) + cst := extension.GetCluster(dir.GetUrl().SubURL.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) + err = staticDir.BuildRouterChain(invokers) + if err != nil { + logger.Error(err) + continue + } + groupInvokersList = append(groupInvokersList, cst.Join(staticDir)) } } @@ -244,6 +254,7 @@ func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.In return routerChain.Route(invokers, dir.cacheOriginUrl, invocation) } +// IsAvailable whether the directory is available func (dir *RegistryDirectory) IsAvailable() bool { if !dir.BaseDirectory.IsAvailable() { return dir.BaseDirectory.IsAvailable() @@ -258,6 +269,7 @@ func (dir *RegistryDirectory) IsAvailable() bool { return false } +// Destroy method func (dir *RegistryDirectory) Destroy() { //TODO:unregister & unsubscribe dir.BaseDirectory.Destroy(func() { @@ -297,6 +309,7 @@ func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL) return listener } +// Process handle events and update Invokers func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) l.directory.refreshInvokers(nil) @@ -322,6 +335,7 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti l.listeners = append(l.listeners, listener) } +// Process handles events from Configuration Center and update Invokers func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) l.directory.refreshInvokers(nil) diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go index f1d5ce434aa00185f784f208eefe603274f05ab0..ac3f7124c12788959f529193b871652085fe6303 100644 --- a/registry/directory/directory_test.go +++ b/registry/directory/directory_test.go @@ -25,6 +25,7 @@ import ( ) import ( + "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -37,6 +38,7 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/mock" "github.com/apache/dubbo-go/protocol/protocolwrapper" "github.com/apache/dubbo-go/registry" "github.com/apache/dubbo-go/remoting" @@ -169,7 +171,21 @@ Loop1: break Loop1 } } +} +func Test_toGroupInvokers(t *testing.T) { + registryDirectory, _ := normalRegistryDir() + ctrl := gomock.NewController(t) + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + newUrl, _ := common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + invoker.EXPECT().GetUrl().Return(newUrl).AnyTimes() + + registryDirectory.cacheInvokersMap.Store("group1", invoker) + registryDirectory.cacheInvokersMap.Store("group2", invoker) + registryDirectory.cacheInvokersMap.Store("group1", invoker) + groupInvokers := registryDirectory.toGroupInvokers() + assert.Len(t, groupInvokers, 2) } func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockRegistry) { diff --git a/registry/etcdv3/listener.go b/registry/etcdv3/listener.go index 51fdf21f5d229932646b4da464de960c2f2985de..436b6eca5b0dfb8514cbc21a47032490f7b1c21f 100644 --- a/registry/etcdv3/listener.go +++ b/registry/etcdv3/listener.go @@ -38,15 +38,17 @@ type dataListener struct { listener config_center.ConfigurationListener } -// NewRegistryDataListener +// NewRegistryDataListener creates a data listener for etcd func NewRegistryDataListener(listener config_center.ConfigurationListener) *dataListener { return &dataListener{listener: listener} } +// AddInterestedURL adds a registration @url to listen func (l *dataListener) AddInterestedURL(url *common.URL) { l.interestedURL = append(l.interestedURL, url) } +// DataChange processes the data change event from registry center of etcd func (l *dataListener) DataChange(eventType remoting.Event) bool { index := strings.Index(eventType.Path, "/providers/") @@ -88,10 +90,12 @@ func NewConfigurationListener(reg *etcdV3Registry) *configurationListener { return &configurationListener{registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32)} } +// Process data change event from config center of etcd func (l *configurationListener) Process(configType *config_center.ConfigChangeEvent) { l.events <- configType } +// Next returns next service event once received func (l *configurationListener) Next() (*registry.ServiceEvent, error) { for { select { @@ -114,6 +118,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { } } +// Close etcd registry center func (l *configurationListener) Close() { l.registry.WaitGroup().Done() } diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index e691ae3cf1204ee97f130764496a7fc5bf67ac42..f27e7ce8ba5ab3515f8290b208b4823872ba48a3 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -24,9 +24,9 @@ import ( ) import ( + "github.com/coreos/etcd/embed" "github.com/dubbogo/getty" "github.com/stretchr/testify/suite" - "go.etcd.io/etcd/embed" ) import ( diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go index 5d389c36374fe9de5561418bc90d44a7d780fd48..f3df78177bda2b068d0ad88156b593ab3d48c5d7 100644 --- a/registry/etcdv3/registry.go +++ b/registry/etcdv3/registry.go @@ -57,17 +57,17 @@ type etcdV3Registry struct { configListener *configurationListener } -// Client get the etcdv3 client +// Client gets the etcdv3 client func (r *etcdV3Registry) Client() *etcdv3.Client { return r.client } -//SetClient set the etcdv3 client +// SetClient sets the etcdv3 client func (r *etcdV3Registry) SetClient(client *etcdv3.Client) { r.client = client } -// +// ClientLock returns lock for client func (r *etcdV3Registry) ClientLock() *sync.Mutex { return &r.cltLock } @@ -104,27 +104,32 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) { return r, nil } +// InitListeners init listeners of etcd registry center func (r *etcdV3Registry) InitListeners() { r.listener = etcdv3.NewEventListener(r.client) r.configListener = NewConfigurationListener(r) r.dataListener = NewRegistryDataListener(r.configListener) } +// DoRegister actually do the register job in the registry center of etcd func (r *etcdV3Registry) DoRegister(root string, node string) error { return r.client.Create(path.Join(root, node), "") } +// CloseAndNilClient closes listeners and clear client func (r *etcdV3Registry) CloseAndNilClient() { r.client.Close() r.client = nil } +// CloseListener closes listeners func (r *etcdV3Registry) CloseListener() { if r.configListener != nil { r.configListener.Close() } } +// CreatePath create the path in the registry center of etcd func (r *etcdV3Registry) CreatePath(k string) error { var tmpPath string for _, str := range strings.Split(k, "/")[1:] { @@ -137,6 +142,7 @@ func (r *etcdV3Registry) CreatePath(k string) error { return nil } +// DoSubscribe actually subscribe the provider URL func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) { var ( diff --git a/registry/event.go b/registry/event.go index be9f11d00bb5a70b0d787d15bcdc98471aad0a4b..5fe6df6a379e1de8662917fb76c6d16fa9a17f37 100644 --- a/registry/event.go +++ b/registry/event.go @@ -36,7 +36,7 @@ func init() { // service event // //////////////////////////////////////// -// ServiceEvent ... +// ServiceEvent includes create, update, delete event type ServiceEvent struct { Action remoting.EventType Service common.URL diff --git a/registry/event_listener.go b/registry/event_listener.go index b8d6148442d9e10e210958dead690c4a95b33fb6..1805f2833c96bd08c4cf9c92337d7d221e8829e9 100644 --- a/registry/event_listener.go +++ b/registry/event_listener.go @@ -36,6 +36,7 @@ type ConditionalEventListener interface { Accept(e Event) bool } +// ServiceInstancesChangedListener is used when the Service Discovery Changed // TODO (implement ConditionalEventListener) type ServiceInstancesChangedListener struct { ServiceName string diff --git a/registry/kubernetes/listener.go b/registry/kubernetes/listener.go index f8869fea7b77541eb929624cc1fa708c1218d7dd..24c8d81614c7dd323e4f23ec7de5d28b24eecb70 100644 --- a/registry/kubernetes/listener.go +++ b/registry/kubernetes/listener.go @@ -38,12 +38,12 @@ type dataListener struct { listener config_center.ConfigurationListener } -// NewRegistryDataListener +// NewRegistryDataListener creates a data listener for kubernetes func NewRegistryDataListener(listener config_center.ConfigurationListener) *dataListener { return &dataListener{listener: listener} } -// AddInterestedURL +// AddInterestedURL adds the @url of registry center to the listener func (l *dataListener) AddInterestedURL(url *common.URL) { l.interestedURL = append(l.interestedURL, url) } @@ -91,10 +91,12 @@ func NewConfigurationListener(reg *kubernetesRegistry) *configurationListener { return &configurationListener{registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32)} } +// Process processes the data change event from config center of kubernetes func (l *configurationListener) Process(configType *config_center.ConfigChangeEvent) { l.events <- configType } +// Next returns next service event once received func (l *configurationListener) Next() (*registry.ServiceEvent, error) { for { select { @@ -103,7 +105,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { return nil, perrors.New("listener stopped") case e := <-l.events: - logger.Infof("got kubernetes event %#v", e) + logger.Debugf("got kubernetes event %#v", e) if e.ConfigType == remoting.EventTypeDel && !l.registry.client.Valid() { select { case <-l.registry.Done(): @@ -116,6 +118,8 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { } } } + +// Close kubernetes registry center func (l *configurationListener) Close() { l.registry.WaitGroup().Done() } diff --git a/registry/kubernetes/listener_test.go b/registry/kubernetes/listener_test.go index c50b5b670a5491b9813652f7aa46bec18a35a7d7..ccaaf80907f9eb3d4956758374f518c66fa613d5 100644 --- a/registry/kubernetes/listener_test.go +++ b/registry/kubernetes/listener_test.go @@ -18,24 +18,15 @@ package kubernetes import ( - "encoding/json" - "os" - "strconv" "testing" - "time" ) import ( "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" - "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" ) import ( "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/remoting" ) @@ -184,67 +175,7 @@ type MockDataListener struct{} func (*MockDataListener) Process(configType *config_center.ConfigChangeEvent) {} -type KubernetesRegistryTestSuite struct { - suite.Suite - - currentPod v1.Pod -} - -func (s *KubernetesRegistryTestSuite) initRegistry() *kubernetesRegistry { - - t := s.T() - - regurl, err := common.NewURL("registry://127.0.0.1:443", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - if err != nil { - t.Fatal(err) - } - - mock, err := newMockKubernetesRegistry(®url, s.currentPod.GetNamespace(), func() (kubernetes.Interface, error) { - - out := fake.NewSimpleClientset() - - // mock current pod - if _, err := out.CoreV1().Pods(s.currentPod.GetNamespace()).Create(&s.currentPod); err != nil { - t.Fatal(err) - } - return out, nil - }) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Second) - return mock.(*kubernetesRegistry) -} - -func (s *KubernetesRegistryTestSuite) SetupSuite() { - - t := s.T() - - const ( - // kubernetes inject the var - podNameKey = "HOSTNAME" - nameSpaceKey = "NAMESPACE" - ) - - // 1. install test data - if err := json.Unmarshal([]byte(clientPodJsonData), &s.currentPod); err != nil { - t.Fatal(err) - } - - // 2. set downward-api inject env - if err := os.Setenv(podNameKey, s.currentPod.GetName()); err != nil { - t.Fatal(err) - } - if err := os.Setenv(nameSpaceKey, s.currentPod.GetNamespace()); err != nil { - t.Fatal(err) - } - -} - -func (s *KubernetesRegistryTestSuite) TestDataChange() { - - t := s.T() +func TestDataChange(t *testing.T) { listener := NewRegistryDataListener(&MockDataListener{}) url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") @@ -253,7 +184,3 @@ func (s *KubernetesRegistryTestSuite) TestDataChange() { t.Fatal("data change not ok") } } - -func TestKubernetesRegistrySuite(t *testing.T) { - suite.Run(t, &KubernetesRegistryTestSuite{}) -} diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go index 8a02d0e3e693b58946a97e7b47238e0be4272dcf..f06d80124b7a627954ca3f4de0ae189e424708fd 100644 --- a/registry/kubernetes/registry.go +++ b/registry/kubernetes/registry.go @@ -29,7 +29,7 @@ import ( "github.com/dubbogo/getty" "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" - k8s "k8s.io/client-go/kubernetes" + v1 "k8s.io/api/core/v1" ) import ( @@ -68,23 +68,28 @@ type kubernetesRegistry struct { configListener *configurationListener } +// Client gets the etcdv3 kubernetes func (r *kubernetesRegistry) Client() *kubernetes.Client { r.cltLock.RLock() client := r.client r.cltLock.RUnlock() return client } + +// SetClient sets the kubernetes client func (r *kubernetesRegistry) SetClient(client *kubernetes.Client) { r.cltLock.Lock() r.client = client r.cltLock.Unlock() } +// CloseAndNilClient closes listeners and clear client func (r *kubernetesRegistry) CloseAndNilClient() { r.client.Close() r.client = nil } +// CloseListener closes listeners func (r *kubernetesRegistry) CloseListener() { r.cltLock.Lock() @@ -96,6 +101,7 @@ func (r *kubernetesRegistry) CloseListener() { r.configListener = nil } +// CreatePath create the path in the registry center of kubernetes func (r *kubernetesRegistry) CreatePath(k string) error { if err := r.client.Create(k, ""); err != nil { return perrors.WithMessagef(err, "create path %s in kubernetes", k) @@ -103,10 +109,12 @@ func (r *kubernetesRegistry) CreatePath(k string) error { return nil } +// DoRegister actually do the register job in the registry center of kubernetes func (r *kubernetesRegistry) DoRegister(root string, node string) error { return r.client.Create(path.Join(root, node), "") } +// DoSubscribe actually subscribe the provider URL func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, error) { var ( @@ -139,6 +147,7 @@ func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, er return configListener, nil } +// InitListeners init listeners of kubernetes registry center func (r *kubernetesRegistry) InitListeners() { r.listener = kubernetes.NewEventListener(r.client) r.configListener = NewConfigurationListener(r) @@ -160,15 +169,14 @@ func newKubernetesRegistry(url *common.URL) (registry.Registry, error) { go r.HandleClientRestart() r.InitListeners() - logger.Debugf("the kubernetes registry started") + logger.Debugf("kubernetes registry started") return r, nil } func newMockKubernetesRegistry( url *common.URL, - namespace string, - clientGeneratorFunc func() (k8s.Interface, error), + podsList *v1.PodList, ) (registry.Registry, error) { var err error @@ -176,7 +184,7 @@ func newMockKubernetesRegistry( r := &kubernetesRegistry{} r.InitBaseRegistry(url, r) - r.client, err = kubernetes.NewMockClient(namespace, clientGeneratorFunc) + r.client, err = kubernetes.NewMockClient(podsList) if err != nil { return nil, perrors.WithMessage(err, "new mock client") } @@ -184,6 +192,7 @@ func newMockKubernetesRegistry( return r, nil } +// HandleClientRestart will reconnect to kubernetes registry center func (r *kubernetesRegistry) HandleClientRestart() { var ( diff --git a/registry/kubernetes/registry_test.go b/registry/kubernetes/registry_test.go index ea6d7663a9ceeab241c7a94a91f94288ab2990fc..347dadcd2c462e3a1caf9829b051a665ec61e8e3 100644 --- a/registry/kubernetes/registry_test.go +++ b/registry/kubernetes/registry_test.go @@ -18,12 +18,17 @@ package kubernetes import ( + "encoding/json" + "os" "strconv" + "sync" + "testing" "time" ) import ( "github.com/stretchr/testify/assert" + v1 "k8s.io/api/core/v1" ) import ( @@ -31,11 +36,212 @@ import ( "github.com/apache/dubbo-go/common/constant" ) -func (s *KubernetesRegistryTestSuite) TestRegister() { +var clientPodListJsonData = `{ + "apiVersion": "v1", + "items": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "dubbo.io/annotation": "W3siayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzL2R1YmJvJTNBJTJGJTJGMTcyLjE3LjAuNiUzQTIwMDAwJTJGVXNlclByb3ZpZGVyJTNGYWNjZXNzbG9nJTNEJTI2YW55aG9zdCUzRHRydWUlMjZhcHAudmVyc2lvbiUzRDAuMC4xJTI2YXBwbGljYXRpb24lM0RCRFRTZXJ2aWNlJTI2YXV0aCUzRCUyNmJlYW4ubmFtZSUzRFVzZXJQcm92aWRlciUyNmNsdXN0ZXIlM0RmYWlsb3ZlciUyNmVudmlyb25tZW50JTNEZGV2JTI2ZXhlY3V0ZS5saW1pdCUzRCUyNmV4ZWN1dGUubGltaXQucmVqZWN0ZWQuaGFuZGxlciUzRCUyNmdyb3VwJTNEJTI2aW50ZXJmYWNlJTNEY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyJTI2aXAlM0QxNzIuMTcuMC42JTI2bG9hZGJhbGFuY2UlM0RyYW5kb20lMjZtZXRob2RzLkdldFVzZXIubG9hZGJhbGFuY2UlM0RyYW5kb20lMjZtZXRob2RzLkdldFVzZXIucmV0cmllcyUzRDElMjZtZXRob2RzLkdldFVzZXIudHBzLmxpbWl0LmludGVydmFsJTNEJTI2bWV0aG9kcy5HZXRVc2VyLnRwcy5saW1pdC5yYXRlJTNEJTI2bWV0aG9kcy5HZXRVc2VyLnRwcy5saW1pdC5zdHJhdGVneSUzRCUyNm1ldGhvZHMuR2V0VXNlci53ZWlnaHQlM0QwJTI2bW9kdWxlJTNEZHViYm9nbyUyQnVzZXItaW5mbyUyQnNlcnZlciUyNm5hbWUlM0RCRFRTZXJ2aWNlJTI2b3JnYW5pemF0aW9uJTNEaWt1cmVudG8uY29tJTI2b3duZXIlM0RaWCUyNnBhcmFtLnNpZ24lM0QlMjZwaWQlM0Q2JTI2cmVnaXN0cnkucm9sZSUzRDMlMjZyZWxlYXNlJTNEZHViYm8tZ29sYW5nLTEuMy4wJTI2cmV0cmllcyUzRCUyNnNlcnZpY2UuZmlsdGVyJTNEZWNobyUyNTJDdG9rZW4lMjUyQ2FjY2Vzc2xvZyUyNTJDdHBzJTI1MkNnZW5lcmljX3NlcnZpY2UlMjUyQ2V4ZWN1dGUlMjUyQ3BzaHV0ZG93biUyNnNpZGUlM0Rwcm92aWRlciUyNnRpbWVzdGFtcCUzRDE1OTExNTYxNTUlMjZ0cHMubGltaXQuaW50ZXJ2YWwlM0QlMjZ0cHMubGltaXQucmF0ZSUzRCUyNnRwcy5saW1pdC5yZWplY3RlZC5oYW5kbGVyJTNEJTI2dHBzLmxpbWl0LnN0cmF0ZWd5JTNEJTI2dHBzLmxpbWl0ZXIlM0QlMjZ2ZXJzaW9uJTNEJTI2d2FybXVwJTNEMTAwIiwidiI6IiJ9XQ==" + }, + "creationTimestamp": "2020-06-03T03:49:14Z", + "generateName": "server-84c864f5bc-", + "labels": { + "dubbo.io/label": "dubbo.io-value", + "pod-template-hash": "84c864f5bc", + "role": "server" + }, + "name": "server-84c864f5bc-r8qvz", + "namespace": "default", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "server-84c864f5bc", + "uid": "fa376dbb-4f37-4705-8e80-727f592c19b3" + } + ], + "resourceVersion": "517460", + "selfLink": "/api/v1/namespaces/default/pods/server-84c864f5bc-r8qvz", + "uid": "f4fc811c-200c-4445-8d4f-532144957dcc" + }, + "spec": { + "containers": [ + { + "env": [ + { + "name": "DUBBO_NAMESPACE", + "value": "default" + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } + } + ], + "image": "192.168.240.101:5000/scott/go-server", + "imagePullPolicy": "Always", + "name": "server", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "dubbo-sa-token-5qbtb", + "readOnly": true + } + ] + } + ], + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "nodeName": "minikube", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "dubbo-sa", + "serviceAccountName": "dubbo-sa", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, + { + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "dubbo-sa-token-5qbtb", + "secret": { + "defaultMode": 420, + "secretName": "dubbo-sa-token-5qbtb" + } + } + ] + }, + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:14Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:15Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:15Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:14Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "docker://b6421e05ce44f8a1c4fa6b72274980777c7c0f945516209f7c0558cd0cd65406", + "image": "192.168.240.101:5000/scott/go-server:latest", + "imageID": "docker-pullable://192.168.240.101:5000/scott/go-server@sha256:4eecf895054f0ff93d80db64992a561d10504e55582def6dcb6093a6d6d92461", + "lastState": {}, + "name": "server", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2020-06-03T03:49:15Z" + } + } + } + ], + "hostIP": "10.0.2.15", + "phase": "Running", + "podIP": "172.17.0.6", + "podIPs": [ + { + "ip": "172.17.0.6" + } + ], + "qosClass": "BestEffort", + "startTime": "2020-06-03T03:49:14Z" + } + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "", + "selfLink": "" + } +} +` + +func getTestRegistry(t *testing.T) *kubernetesRegistry { - t := s.T() + const ( + podNameKey = "HOSTNAME" + nameSpaceKey = "NAMESPACE" + needWatchedNameSpaceKey = "DUBBO_NAMESPACE" + ) + pl := &v1.PodList{} + // 1. install test data + if err := json.Unmarshal([]byte(clientPodListJsonData), &pl); err != nil { + t.Fatal(err) + } + currentPod := pl.Items[0] - r := s.initRegistry() + env := map[string]string{ + nameSpaceKey: currentPod.GetNamespace(), + podNameKey: currentPod.GetName(), + needWatchedNameSpaceKey: "default", + } + + for k, v := range env { + if err := os.Setenv(k, v); err != nil { + t.Fatal(err) + } + } + + regurl, err := common.NewURL("registry://127.0.0.1:443", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if err != nil { + t.Fatal(err) + } + out, err := newMockKubernetesRegistry(®url, pl) + if err != nil { + t.Fatal(err) + } + + return out.(*kubernetesRegistry) +} + +func TestRegister(t *testing.T) { + + r := getTestRegistry(t) defer r.Destroy() url, _ := common.NewURL( @@ -52,41 +258,44 @@ func (s *KubernetesRegistryTestSuite) TestRegister() { } } -func (s *KubernetesRegistryTestSuite) TestSubscribe() { +func TestSubscribe(t *testing.T) { - t := s.T() - - r := s.initRegistry() + r := getTestRegistry(t) defer r.Destroy() - url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + if err != nil { + t.Fatal(err) + } listener, err := r.DoSubscribe(&url) if err != nil { t.Fatal(err) } - time.Sleep(1e9) + wg := sync.WaitGroup{} + wg.Add(1) go func() { - err := r.Register(url) - if err != nil { - t.Fatal(err) + + defer wg.Done() + registerErr := r.Register(url) + if registerErr != nil { + t.Fatal(registerErr) } }() + wg.Wait() + serviceEvent, err := listener.Next() if err != nil { t.Fatal(err) } - - t.Logf("got event %s", serviceEvent) + t.Logf("get service event %s", serviceEvent) } -func (s *KubernetesRegistryTestSuite) TestConsumerDestroy() { - - t := s.T() +func TestConsumerDestroy(t *testing.T) { - r := s.initRegistry() + r := getTestRegistry(t) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), @@ -105,11 +314,9 @@ func (s *KubernetesRegistryTestSuite) TestConsumerDestroy() { } -func (s *KubernetesRegistryTestSuite) TestProviderDestroy() { +func TestProviderDestroy(t *testing.T) { - t := s.T() - - r := s.initRegistry() + r := getTestRegistry(t) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), @@ -122,9 +329,7 @@ func (s *KubernetesRegistryTestSuite) TestProviderDestroy() { assert.Equal(t, false, r.IsAvailable()) } -func (s *KubernetesRegistryTestSuite) TestNewRegistry() { - - t := s.T() +func TestNewRegistry(t *testing.T) { regUrl, err := common.NewURL("registry://127.0.0.1:443", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) @@ -137,9 +342,9 @@ func (s *KubernetesRegistryTestSuite) TestNewRegistry() { } } -func (s *KubernetesRegistryTestSuite) TestHandleClientRestart() { +func TestHandleClientRestart(t *testing.T) { - r := s.initRegistry() + r := getTestRegistry(t) r.WaitGroup().Add(1) go r.HandleClientRestart() time.Sleep(timeSecondDuration(1)) diff --git a/registry/mock_registry.go b/registry/mock_registry.go index 9591928eebd22bf2a99ec9dcfeb285c4519a3b90..2b83d5ab8892f673e1123cd01fa74e48e3d2dc22 100644 --- a/registry/mock_registry.go +++ b/registry/mock_registry.go @@ -30,13 +30,13 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -// MockRegistry ... +// MockRegistry is used as mock registry type MockRegistry struct { listener *listener destroyed *atomic.Bool } -// NewMockRegistry ... +// NewMockRegistry creates a mock registry func NewMockRegistry(url *common.URL) (Registry, error) { registry := &MockRegistry{ destroyed: atomic.NewBool(false), @@ -46,23 +46,23 @@ func NewMockRegistry(url *common.URL) (Registry, error) { return registry, nil } -// Register ... +// Register is used as a mock registry func (*MockRegistry) Register(url common.URL) error { return nil } -// Destroy ... +// nolint func (r *MockRegistry) Destroy() { if r.destroyed.CAS(false, true) { } } -// IsAvailable ... +// IsAvailable is use for determine a mock registry available func (r *MockRegistry) IsAvailable() bool { return !r.destroyed.Load() } -// GetUrl ... +// nolint func (r *MockRegistry) GetUrl() common.URL { return common.URL{} } @@ -71,7 +71,7 @@ func (r *MockRegistry) subscribe(*common.URL) (Listener, error) { return r.listener, nil } -// Subscribe ... +// nolint func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) { go func() { for { @@ -123,7 +123,7 @@ func (*listener) Close() { } -// MockEvent ... +// nolint func (r *MockRegistry) MockEvent(event *ServiceEvent) { r.listener.listenChan <- event } diff --git a/registry/nacos/base_registry.go b/registry/nacos/base_registry.go index 63f4999675470853d0f48d1a22b709efdc1c9d26..f09754ebfb069da892881ea0d1754dfb11746675 100644 --- a/registry/nacos/base_registry.go +++ b/registry/nacos/base_registry.go @@ -95,6 +95,7 @@ func getNacosConfig(url *common.URL) (map[string]interface{}, error) { clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "") clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "") clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "") + clientConfig.NamespaceId = url.GetParam(constant.NACOS_NAMESPACE_ID, "") clientConfig.NotLoadCacheAtStart = true configMap["clientConfig"] = clientConfig diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go index a2237dca265f25b07b19a8e1f4fe5a5f6ea9183e..36f733df5a32f57e3410a2f31f9ab4b0af735d49 100644 --- a/registry/nacos/listener.go +++ b/registry/nacos/listener.go @@ -51,7 +51,7 @@ type nacosListener struct { subscribeParam *vo.SubscribeParam } -// NewNacosListener ... +// NewRegistryDataListener creates a data listener for nacos func NewNacosListener(url common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) { listener := &nacosListener{ namingClient: namingClient, @@ -109,6 +109,7 @@ func generateUrl(instance model.Instance) *common.URL { ) } +// Callback will be invoked when got subscribed events. func (nl *nacosListener) Callback(services []model.SubscribeService, err error) { if err != nil { logger.Errorf("nacos subscribe callback error:%s , subscribe:%+v ", err.Error(), nl.subscribeParam) @@ -198,6 +199,7 @@ func (nl *nacosListener) process(configType *config_center.ConfigChangeEvent) { nl.events <- configType } +// Next returns the service event from nacos. func (nl *nacosListener) Next() (*registry.ServiceEvent, error) { for { select { @@ -212,6 +214,7 @@ func (nl *nacosListener) Next() (*registry.ServiceEvent, error) { } } +// nolint func (nl *nacosListener) Close() { nl.stopListen() close(nl.done) diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go index a436b85064829b9f42c9dcc45545e5bf2fd2fefe..3eeb7680abb3da98f5ed08f1aea57d490b2caf85 100644 --- a/registry/nacos/registry.go +++ b/registry/nacos/registry.go @@ -123,6 +123,7 @@ func createRegisterParam(url common.URL, serviceName string) vo.RegisterInstance return instance } +// Register will register the service @url to its nacos registry center func (nr *nacosRegistry) Register(url common.URL) error { serviceName := getServiceName(url) param := createRegisterParam(url, serviceName) @@ -140,7 +141,7 @@ func (nr *nacosRegistry) subscribe(conf *common.URL) (registry.Listener, error) return NewNacosListener(*conf, nr.namingClient) } -//subscribe from registry +// subscribe from registry func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) { for { if !nr.IsAvailable() { @@ -174,14 +175,17 @@ func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.Noti } } +// GetUrl gets its registration URL func (nr *nacosRegistry) GetUrl() common.URL { return *nr.URL } +// IsAvailable determines nacos registry center whether it is available func (nr *nacosRegistry) IsAvailable() bool { return true } +// nolint func (nr *nacosRegistry) Destroy() { return } diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go index 7475b455c0dda09da65012465711ece264bb3dd5..d0311b200b27081c60bc97b2307a54774ca977bd 100644 --- a/registry/nacos/registry_test.go +++ b/registry/nacos/registry_test.go @@ -42,7 +42,7 @@ func TestNacosRegistry_Register(t *testing.T) { urlMap.Set(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider") urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") - url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, err := newNacosRegistry(®url) assert.Nil(t, err) @@ -50,7 +50,7 @@ func TestNacosRegistry_Register(t *testing.T) { t.Errorf("new nacos registry error:%s \n", err.Error()) return } - err = reg.Register(url) + err = reg.Register(testUrl) assert.Nil(t, err) if err != nil { t.Errorf("register error:%s \n", err.Error()) @@ -72,10 +72,10 @@ func TestNacosRegistry_Subscribe(t *testing.T) { urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") - url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, _ := newNacosRegistry(®url) - err := reg.Register(url) + err := reg.Register(testUrl) assert.Nil(t, err) if err != nil { t.Errorf("new nacos registry error:%s \n", err.Error()) @@ -84,7 +84,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) { regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) reg2, _ := newNacosRegistry(®url) - listener, err := reg2.(*nacosRegistry).subscribe(&url) + listener, err := reg2.(*nacosRegistry).subscribe(&testUrl) assert.Nil(t, err) if err != nil { t.Errorf("subscribe error:%s \n", err.Error()) diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go index 7d3406cac233dd5293c7522b4f12148fdcdd704e..2611a8dc58d2a45e578d90aa6a5d1aeb7e7f4f63 100644 --- a/registry/nacos/service_discovery.go +++ b/registry/nacos/service_discovery.go @@ -34,8 +34,9 @@ import ( ) const ( - defaultGroup = "DEFAULT_GROUP" - idKey = "id" + defaultGroup = "DEFAULT_GROUP" + idKey = "id" + defaultPageSize = 100 ) // init will put the service discovery into extension @@ -92,7 +93,7 @@ func (n *nacosServiceDiscovery) Unregister(instance registry.ServiceInstance) er // GetDefaultPageSize will return the constant registry.DefaultPageSize func (n *nacosServiceDiscovery) GetDefaultPageSize() int { - return registry.DefaultPageSize + return defaultPageSize } // GetServices will return the all services diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go index a756e8669301919d406a4bcf0e1c962cf532a5c6..04431a614b40288b2a21f75d69c4be313bd7721f 100644 --- a/registry/nacos/service_discovery_test.go +++ b/registry/nacos/service_discovery_test.go @@ -113,7 +113,7 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) { func TestNacosServiceDiscovery_GetDefaultPageSize(t *testing.T) { serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, mockUrl()) - assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize()) + assert.Equal(t, defaultPageSize, serviceDiscovry.GetDefaultPageSize()) } func mockUrl() *common.URL { diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index f57ecdf6809bcabc2eeeecbb7bff52e3962a1077..4c669b2cee74b95ceb3bc8287f145ccd6b99bc0b 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -22,7 +22,6 @@ import ( "strings" "sync" ) - import ( gxset "github.com/dubbogo/gost/container/set" ) @@ -44,16 +43,21 @@ import ( ) var ( - regProtocol *registryProtocol - once sync.Once + regProtocol *registryProtocol + once sync.Once + reserveParams = []string{ + "application", "codec", "exchanger", "serialization", "cluster", "connections", "deprecated", "group", + "loadbalance", "mock", "path", "timeout", "token", "version", "warmup", "weight", "timestamp", "dubbo", + "release", "interface", + } ) type registryProtocol struct { invokers []protocol.Invoker // Registry Map<RegistryAddress, Registry> registries *sync.Map - //To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. - //providerurl <--> exporter + // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. + // providerurl <--> exporter bounds *sync.Map overrideListeners *sync.Map serviceConfigurationListeners *sync.Map @@ -66,10 +70,8 @@ func init() { } func getCacheKey(url *common.URL) string { - newUrl := url.Clone() delKeys := gxset.NewSet("dynamic", "enabled") - newUrl.RemoveParams(delKeys) - return newUrl.String() + return url.CloneExceptParams(delKeys).String() } func newRegistryProtocol() *registryProtocol { @@ -88,12 +90,34 @@ func getRegistry(regUrl *common.URL) registry.Registry { return reg } +func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common.URL { + if registryUrl.GetParamBool("simplified", false) { + return providerUrl.CloneWithParams(reserveParams) + } else { + return filterHideKey(providerUrl) + } +} + +// filterHideKey filter the parameters that do not need to be output in url(Starting with .) +func filterHideKey(url *common.URL) *common.URL { + + // be careful params maps in url is map type + removeSet := gxset.NewSet() + for k, _ := range url.GetParams() { + if strings.HasPrefix(k, ".") { + removeSet.Add(k) + } + } + return url.CloneExceptParams(removeSet) +} + func (proto *registryProtocol) initConfigurationListeners() { proto.overrideListeners = &sync.Map{} proto.serviceConfigurationListeners = &sync.Map{} proto.providerConfigurationListener = newProviderConfigurationListener(proto.overrideListeners) } +// Refer provider service from registry center func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { var registryUrl = url var serviceUrl = registryUrl.SubURL @@ -111,7 +135,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { reg = regI.(registry.Registry) } - //new registry directory for store service url from registry + // new registry directory for store service url from registry directory, err := extension.GetDefaultRegistryDirectory(®istryUrl, reg) if err != nil { logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!", @@ -125,7 +149,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { serviceUrl.String(), registryUrl.String(), err.Error()) } - //new cluster invoker + // new cluster invoker cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) invoker := cluster.Join(directory) @@ -133,6 +157,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { return invoker } +// Export provider service to registry center func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporter { proto.once.Do(func() { proto.initConfigurationListeners() @@ -150,7 +175,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte serviceConfigurationListener.OverrideUrl(providerUrl) var reg registry.Registry - if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { reg = getRegistry(registryUrl) proto.registries.Store(registryUrl.Key(), reg) @@ -158,7 +182,8 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte reg = regI.(registry.Registry) } - err := reg.Register(*providerUrl) + registeredProviderUrl := getUrlToRegistry(providerUrl, registryUrl) + err := reg.Register(*registeredProviderUrl) if err != nil { logger.Errorf("provider service %v register registry %v error, error message is %s", providerUrl.Key(), registryUrl.Key(), err.Error()) @@ -190,7 +215,7 @@ func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common oldExporter.(protocol.Exporter).Unexport() proto.bounds.Delete(key) proto.Export(wrappedNewInvoker) - //TODO: unregister & unsubscribe + // TODO: unregister & unsubscribe } } @@ -206,6 +231,7 @@ func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Inv return &overrideSubscribeListener{url: overriderUrl, originInvoker: invoker, protocol: proto} } +// Notify will be triggered when a service change notification is received. func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) { if isMatched(&(event.Service), nl.url) && event.Action == remoting.EventTypeAdd { nl.configurator = extension.GetDefaultConfigurator(&(event.Service)) @@ -273,7 +299,7 @@ func isMatched(providerUrl *common.URL, consumerUrl *common.URL) bool { providerGroup := providerUrl.GetParam(constant.GROUP_KEY, "") providerVersion := providerUrl.GetParam(constant.VERSION_KEY, "") providerClassifier := providerUrl.GetParam(constant.CLASSIFIER_KEY, "") - //todo: public static boolean isContains(String values, String value) { + // todo: public static boolean isContains(String values, String value) { // return isNotEmpty(values) && isContains(COMMA_SPLIT_PATTERN.split(values), value); // } return (consumerGroup == constant.ANY_VALUE || consumerGroup == providerGroup || @@ -302,6 +328,7 @@ func getSubscribedOverrideUrl(providerUrl *common.URL) *common.URL { return newUrl } +// Destroy registry protocol func (proto *registryProtocol) Destroy() { for _, ivk := range proto.invokers { ivk.Destroy() @@ -326,9 +353,9 @@ func (proto *registryProtocol) Destroy() { } func getRegistryUrl(invoker protocol.Invoker) *common.URL { - //here add * for return a new url + // here add * for return a new url url := invoker.GetUrl() - //if the protocol == registry ,set protocol the registry value in url.params + // if the protocol == registry ,set protocol the registry value in url.params if url.Protocol == constant.REGISTRY_PROTOCOL { protocol := url.GetParam(constant.REGISTRY_KEY, "") url.Protocol = protocol @@ -338,7 +365,7 @@ func getRegistryUrl(invoker protocol.Invoker) *common.URL { func getProviderUrl(invoker protocol.Invoker) *common.URL { url := invoker.GetUrl() - //be careful params maps in url is map type + // be careful params maps in url is map type return url.SubURL.Clone() } @@ -366,6 +393,7 @@ func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoke } } +// Invoke remote service base on URL of wrappedInvoker func (ivk *wrappedInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { // get right url ivk.invoker.(*proxy_factory.ProxyInvoker).BaseInvoker = *protocol.NewBaseInvoker(ivk.GetUrl()) @@ -388,6 +416,7 @@ func newProviderConfigurationListener(overrideListeners *sync.Map) *providerConf return listener } +// Process notified once there's any change happens on the provider config func (listener *providerConfigurationListener) Process(event *config_center.ConfigChangeEvent) { listener.BaseConfigurationListener.Process(event) listener.overrideListeners.Range(func(key, value interface{}) bool { @@ -412,6 +441,7 @@ func newServiceConfigurationListener(overrideListener *overrideSubscribeListener return listener } +// Process notified once there's any change happens on the service config func (listener *serviceConfigurationListener) Process(event *config_center.ConfigChangeEvent) { listener.BaseConfigurationListener.Process(event) listener.overrideListener.doOverrideIfNecessary() diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go index cee2a6a625368f655d1b9bc5fe8cc37031e1aef7..15fd3cacfacad36309e0ad4deb3c7c7441e47e26 100644 --- a/registry/protocol/protocol_test.go +++ b/registry/protocol/protocol_test.go @@ -284,3 +284,12 @@ func TestExportWithApplicationConfig(t *testing.T) { v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl)) assert.NotNil(t, v2) } + +func TestGetProviderUrlWithHideKey(t *testing.T) { + url, _ := common.NewURL("dubbo://127.0.0.1:1111?a=a1&b=b1&.c=c1&.d=d1&e=e1&protocol=registry") + providerUrl := getUrlToRegistry(&url, &url) + assert.NotContains(t, providerUrl.GetParams(), ".c") + assert.NotContains(t, providerUrl.GetParams(), ".d") + assert.Contains(t, providerUrl.GetParams(), "a") + +} diff --git a/registry/registry.go b/registry/registry.go index d673864700e6ba99e8f0283247d53760b85598aa..5b37aa684ca90d1f18898b9f62f27d86a2c0fba3 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -45,13 +45,16 @@ type Registry interface { Subscribe(*common.URL, NotifyListener) } -// NotifyListener ... +// nolint type NotifyListener interface { + // Notify supports notifications on the service interface and the dimension of the data type. Notify(*ServiceEvent) } // Listener Deprecated! type Listener interface { + // Next returns next service event once received Next() (*ServiceEvent, error) + // Close closes this listener Close() } diff --git a/registry/service_discovery.go b/registry/service_discovery.go index a8228a4abe8ed07e3c5afda300702f778daea4ae..1d5a3593e392083d2115222e131974b941a391c3 100644 --- a/registry/service_discovery.go +++ b/registry/service_discovery.go @@ -26,8 +26,7 @@ import ( gxpage "github.com/dubbogo/gost/page" ) -const DefaultPageSize = 100 - +// ServiceDiscovery is the common operations of Service Discovery type ServiceDiscovery interface { fmt.Stringer diff --git a/registry/service_instance.go b/registry/service_instance.go index 2cc229ee3b056da2d9f1a1b70d3e0f5858c9da5f..247c8567659d1d512a6685ddb0404fecd9968bcd 100644 --- a/registry/service_instance.go +++ b/registry/service_instance.go @@ -17,6 +17,7 @@ package registry +// ServiceInstance is the model class of an instance of a service, which is used for service registration and discovery. type ServiceInstance interface { // GetId will return this instance's id. It should be unique. diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go index 88d5d6221b4bc7136ba4c3e7c95fb53ba35a9a58..1e7bd08adef5ac9920413fd198b726f49c11ecd4 100644 --- a/registry/zookeeper/registry.go +++ b/registry/zookeeper/registry.go @@ -86,12 +86,12 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { return r, nil } -// Options ... +// nolint type Options struct { client *zookeeper.ZookeeperClient } -// Option ... +// nolint type Option func(*Options) func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, *zkRegistry, error) { @@ -116,6 +116,7 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust return c, r, nil } +// InitListeners initializes listeners of zookeeper registry center func (r *zkRegistry) InitListeners() { r.listener = zookeeper.NewZkEventListener(r.client) newDataListener := NewRegistryDataListener() @@ -141,35 +142,43 @@ func (r *zkRegistry) InitListeners() { r.dataListener = newDataListener } +// CreatePath creates the path in the registry center of zookeeper func (r *zkRegistry) CreatePath(path string) error { return r.ZkClient().Create(path) } +// DoRegister actually do the register job in the registry center of zookeeper func (r *zkRegistry) DoRegister(root string, node string) error { return r.registerTempZookeeperNode(root, node) } +// DoSubscribe actually subscribes the provider URL func (r *zkRegistry) DoSubscribe(conf *common.URL) (registry.Listener, error) { return r.getListener(conf) } +// CloseAndNilClient closes listeners and clear client func (r *zkRegistry) CloseAndNilClient() { r.client.Close() r.client = nil } +// nolint func (r *zkRegistry) ZkClient() *zookeeper.ZookeeperClient { return r.client } +// nolint func (r *zkRegistry) SetZkClient(client *zookeeper.ZookeeperClient) { r.client = client } +// nolint func (r *zkRegistry) ZkClientLock() *sync.Mutex { return &r.cltLock } +// CloseListener closes listeners func (r *zkRegistry) CloseListener() { if r.dataListener != nil { r.dataListener.Close() diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go index ba3ea6e864923b1e70cc4a0d31ee98415807699c..b337c79584cc5058e89bd582b007e72fb10da7ee 100644 --- a/remoting/etcdv3/client.go +++ b/remoting/etcdv3/client.go @@ -45,13 +45,12 @@ const ( ) var ( - // ErrNilETCDV3Client ... + // Defines related errors ErrNilETCDV3Client = perrors.New("etcd raw client is nil") // full describe the ERR - // ErrKVPairNotFound ... - ErrKVPairNotFound = perrors.New("k/v pair not found") + ErrKVPairNotFound = perrors.New("k/v pair not found") ) -// Options ... +// nolint type Options struct { name string endpoints []string @@ -60,38 +59,38 @@ type Options struct { heartbeat int // heartbeat second } -// Option ... +// Option will define a function of handling Options type Option func(*Options) -// WithEndpoints ... +// WithEndpoints sets etcd client endpoints func WithEndpoints(endpoints ...string) Option { return func(opt *Options) { opt.endpoints = endpoints } } -// WithName ... +// WithName sets etcd client name func WithName(name string) Option { return func(opt *Options) { opt.name = name } } -// WithTimeout ... +// WithTimeout sets etcd client timeout func WithTimeout(timeout time.Duration) Option { return func(opt *Options) { opt.timeout = timeout } } -// WithHeartbeat ... +// WithHeartbeat sets etcd client heartbeat func WithHeartbeat(heartbeat int) Option { return func(opt *Options) { opt.heartbeat = heartbeat } } -// ValidateClient ... +// ValidateClient validates client and sets options func ValidateClient(container clientFacade, opts ...Option) error { options := &Options{ @@ -131,7 +130,7 @@ func ValidateClient(container clientFacade, opts ...Option) error { return nil } -// Client ... +// Client represents etcd client Configuration type Client struct { lock sync.RWMutex @@ -142,7 +141,7 @@ type Client struct { heartbeat int ctx context.Context // if etcd server connection lose, the ctx.Done will be sent msg - cancel context.CancelFunc // cancel the ctx, all watcher will stopped + cancel context.CancelFunc // cancel the ctx, all watcher will stopped rawClient *clientv3.Client exit chan struct{} @@ -206,7 +205,7 @@ func (c *Client) stop() bool { return false } -// Close ... +// nolint func (c *Client) Close() { if c == nil { @@ -265,8 +264,7 @@ func (c *Client) maintenanceStatusLoop(s *concurrency.Session) { } } -// if k not exist will put k/v in etcd -// if k is already exist in etcd, return nil +// if k not exist will put k/v in etcd, otherwise return nil func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error { c.lock.RLock() @@ -325,7 +323,7 @@ func (c *Client) get(k string) (string, error) { return string(resp.Kvs[0].Value), nil } -// CleanKV ... +// nolint func (c *Client) CleanKV() error { c.lock.RLock() @@ -425,12 +423,12 @@ func (c *Client) keepAliveKV(k string, v string) error { return nil } -// Done ... +// nolint func (c *Client) Done() <-chan struct{} { return c.exit } -// Valid ... +// nolint func (c *Client) Valid() bool { select { case <-c.exit: @@ -447,7 +445,7 @@ func (c *Client) Valid() bool { return true } -// Create ... +// nolint func (c *Client) Create(k string, v string) error { err := c.put(k, v) @@ -457,7 +455,7 @@ func (c *Client) Create(k string, v string) error { return nil } -// Delete ... +// nolint func (c *Client) Delete(k string) error { err := c.delete(k) @@ -468,7 +466,7 @@ func (c *Client) Delete(k string) error { return nil } -// RegisterTemp ... +// RegisterTemp registers a temporary node func (c *Client) RegisterTemp(basePath string, node string) (string, error) { completeKey := path.Join(basePath, node) @@ -481,7 +479,7 @@ func (c *Client) RegisterTemp(basePath string, node string) (string, error) { return completeKey, nil } -// GetChildrenKVList ... +// GetChildrenKVList gets children kv list by @k func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) { kList, vList, err := c.getChildren(k) @@ -491,7 +489,7 @@ func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) { return kList, vList, nil } -// Get ... +// Get gets value by @k func (c *Client) Get(k string) (string, error) { v, err := c.get(k) @@ -502,7 +500,7 @@ func (c *Client) Get(k string) (string, error) { return v, nil } -// Watch ... +// Watch watches on spec key func (c *Client) Watch(k string) (clientv3.WatchChan, error) { wc, err := c.watch(k) @@ -512,7 +510,7 @@ func (c *Client) Watch(k string) (clientv3.WatchChan, error) { return wc, nil } -// WatchWithPrefix ... +// WatchWithPrefix watches on spec prefix func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) { wc, err := c.watchWithPrefix(prefix) diff --git a/remoting/etcdv3/client_test.go b/remoting/etcdv3/client_test.go index 895cc2954adf93b5899d0ae5daedbd35834b7ef4..9a5ef60993598cf93c469989d68c991657bd5fb8 100644 --- a/remoting/etcdv3/client_test.go +++ b/remoting/etcdv3/client_test.go @@ -29,11 +29,11 @@ import ( ) import ( + "github.com/coreos/etcd/embed" "github.com/coreos/etcd/mvcc/mvccpb" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" - "go.etcd.io/etcd/embed" "google.golang.org/grpc/connectivity" ) @@ -154,7 +154,7 @@ func (suite *ClientTestSuite) TestClientValid() { c := suite.client t := suite.T() - if c.Valid() != true { + if !c.Valid() { t.Fatal("client is not valid") } c.Close() @@ -174,7 +174,7 @@ func (suite *ClientTestSuite) TestClientDone() { c.Wait.Wait() - if c.Valid() == true { + if c.Valid() { suite.T().Fatal("client should be invalid then") } } diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go index 35befc85e449ec02a6377faec300aa6b46bcc8bf..2edbb6650890d6d655d8356e1c0a2979a022f0a8 100644 --- a/remoting/etcdv3/facade.go +++ b/remoting/etcdv3/facade.go @@ -43,7 +43,7 @@ type clientFacade interface { common.Node } -// HandleClientRestart ... +// HandleClientRestart keeps the connection between client and server func HandleClientRestart(r clientFacade) { var ( @@ -85,10 +85,8 @@ LOOP: ) logger.Infof("ETCDV3ProviderRegistry.validateETCDV3Client(etcd Addr{%s}) = error{%#v}", endpoint, perrors.WithStack(err)) - if err == nil { - if r.RestartCallBack() { - break - } + if err == nil && r.RestartCallBack() { + break } failTimes++ if MaxFailTimes <= failTimes { diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go index e3cb74e4f676efa1f325ac45e32b21b39d1bbd6a..00b5b19b36d3baa8871efdd3d53e80f05d7aeac1 100644 --- a/remoting/etcdv3/listener.go +++ b/remoting/etcdv3/listener.go @@ -33,7 +33,7 @@ import ( "github.com/apache/dubbo-go/remoting" ) -// EventListener ... +// nolint type EventListener struct { client *Client keyMapLock sync.Mutex @@ -41,7 +41,7 @@ type EventListener struct { wg sync.WaitGroup } -// NewEventListener ... +// NewEventListener returns a EventListener instance func NewEventListener(client *Client) *EventListener { return &EventListener{ client: client, @@ -92,12 +92,10 @@ func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting. } } } - - return false } -// return true mean the event type is DELETE -// return false mean the event type is CREATE || UPDATE +// return true means the event type is DELETE +// return false means the event type is CREATE || UPDATE func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remoting.DataListener) bool { logger.Infof("got a etcd event {type: %s, key: %s}", event.Type, event.Kv.Key) @@ -135,7 +133,7 @@ func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remotin panic("unreachable") } -// ListenServiceNodeEventWithPrefix Listen on a set of key with spec prefix +// ListenServiceNodeEventWithPrefix listens on a set of key with spec prefix func (l *EventListener) ListenServiceNodeEventWithPrefix(prefix string, listener ...remoting.DataListener) { defer l.wg.Done() for { @@ -151,12 +149,12 @@ func (l *EventListener) ListenServiceNodeEventWithPrefix(prefix string, listener logger.Warnf("etcd client stopped") return - // client ctx stop + // client ctx stop case <-l.client.ctx.Done(): logger.Warnf("etcd client ctx cancel") return - // etcd event stream + // etcd event stream case e, ok := <-wc: if !ok { @@ -230,7 +228,7 @@ func (l *EventListener) ListenServiceEvent(key string, listener remoting.DataLis }(key) } -// Close ... +// nolint func (l *EventListener) Close() { l.wg.Wait() } diff --git a/remoting/kubernetes/client.go b/remoting/kubernetes/client.go index 0c9ffd2b914e6ad584023725867a3aaa4b641224..0a0548959a3e6d839321d03a627bb6aba66d8474 100644 --- a/remoting/kubernetes/client.go +++ b/remoting/kubernetes/client.go @@ -19,444 +19,62 @@ package kubernetes import ( "context" - "encoding/base64" - "encoding/json" - "os" + "strconv" "sync" - "time" ) import ( perrors "github.com/pkg/errors" - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/strategicpatch" - "k8s.io/apimachinery/pkg/watch" + v1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" + "k8s.io/client-go/kubernetes/fake" ) import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" ) -const ( - // kubernetes inject the var - podNameKey = "HOSTNAME" - nameSpaceKey = "NAMESPACE" - // all pod annotation key - DubboIOAnnotationKey = "dubbo.io/annotation" - - DubboIOLabelKey = "dubbo.io/label" - DubboIOLabelValue = "dubbo.io-value" -) - -var ( - ErrDubboLabelAlreadyExist = perrors.New("dubbo label already exist") -) - type Client struct { - - // kubernetes connection config - cfg *rest.Config - - // the kubernetes interface - rawClient kubernetes.Interface - - // current pod config - currentPodName string - - ns string - - // current resource version - lastResourceVersion string - - // the memory watcherSet - watcherSet WatcherSet - - // protect the wg && currentPod lock sync.RWMutex - // current pod status - currentPod *v1.Pod - // protect the watchPods loop && watcher - wg sync.WaitGroup // manage the client lifecycle ctx context.Context cancel context.CancelFunc -} -// load CurrentPodName -func getCurrentPodName() (string, error) { - - v := os.Getenv(podNameKey) - if len(v) == 0 { - return "", perrors.New("read value from env by key (HOSTNAME)") - } - return v, nil + controller *dubboRegistryController } -// load CurrentNameSpace -func getCurrentNameSpace() (string, error) { - - v := os.Getenv(nameSpaceKey) - if len(v) == 0 { - return "", perrors.New("read value from env by key (NAMESPACE)") - } - return v, nil -} - -// NewMockClient -// export for registry package test -func NewMockClient(namespace string, mockClientGenerator func() (kubernetes.Interface, error)) (*Client, error) { - return newMockClient(namespace, mockClientGenerator) -} - -// newMockClient -// new a client for test -func newMockClient(namespace string, mockClientGenerator func() (kubernetes.Interface, error)) (*Client, error) { - - rawClient, err := mockClientGenerator() - if err != nil { - return nil, perrors.WithMessage(err, "call mock generator") - } - - currentPodName, err := getCurrentPodName() - if err != nil { - return nil, perrors.WithMessage(err, "get pod name") - } +// newClient returns Client instance for registry +func newClient(url common.URL) (*Client, error) { ctx, cancel := context.WithCancel(context.Background()) - c := &Client{ - currentPodName: currentPodName, - ns: namespace, - rawClient: rawClient, - ctx: ctx, - watcherSet: newWatcherSet(ctx), - cancel: cancel, - } - - currentPod, err := c.initCurrentPod() + // read type + r, err := strconv.Atoi(url.GetParams().Get(constant.ROLE_KEY)) if err != nil { - return nil, perrors.WithMessage(err, "init current pod") - } - - // record current status - c.currentPod = currentPod - - // init the watcherSet by current pods - if err := c.initWatchSet(); err != nil { - return nil, perrors.WithMessage(err, "init watcherSet") + return nil, perrors.WithMessage(err, "atoi role") } - - c.lastResourceVersion = c.currentPod.GetResourceVersion() - - // start kubernetes watch loop - if err := c.watchPods(); err != nil { - return nil, perrors.WithMessage(err, "watch pods") - } - - logger.Infof("init kubernetes registry client success @namespace = %q @Podname = %q", namespace, c.currentPod.Name) - return c, nil -} - -// newClient -// new a client for registry -func newClient(namespace string) (*Client, error) { - - cfg, err := rest.InClusterConfig() - if err != nil { - return nil, perrors.WithMessage(err, "get in-cluster config") - } - - rawClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - return nil, perrors.WithMessage(err, "new kubernetes client by in cluster config") - } - - currentPodName, err := getCurrentPodName() + controller, err := newDubboRegistryController(ctx, common.RoleType(r), GetInClusterKubernetesClient) if err != nil { - return nil, perrors.WithMessage(err, "get pod name") + return nil, perrors.WithMessage(err, "new dubbo-registry controller") } - ctx, cancel := context.WithCancel(context.Background()) - c := &Client{ - currentPodName: currentPodName, - ns: namespace, - cfg: cfg, - rawClient: rawClient, - ctx: ctx, - watcherSet: newWatcherSet(ctx), - cancel: cancel, + ctx: ctx, + cancel: cancel, + controller: controller, } - currentPod, err := c.initCurrentPod() - if err != nil { - return nil, perrors.WithMessage(err, "init current pod") + if r == common.CONSUMER { + // only consumer have to start informer factory + c.controller.startALLInformers() } - - // record current status - c.currentPod = currentPod - - // init the watcherSet by current pods - if err := c.initWatchSet(); err != nil { - return nil, perrors.WithMessage(err, "init watcherSet") - } - - // start kubernetes watch loop - if err := c.watchPods(); err != nil { - return nil, perrors.WithMessage(err, "watch pods") - } - - logger.Infof("init kubernetes registry client success @namespace = %q @Podname = %q", namespace, c.currentPod.Name) return c, nil } -// initCurrentPod -// 1. get current pod -// 2. give the dubbo-label for this pod -func (c *Client) initCurrentPod() (*v1.Pod, error) { - - // read the current pod status - currentPod, err := c.rawClient.CoreV1().Pods(c.ns).Get(c.currentPodName, metav1.GetOptions{}) - if err != nil { - return nil, perrors.WithMessagef(err, "get current (%s) pod in namespace (%s)", c.currentPodName, c.ns) - } - - oldPod, newPod, err := c.assembleDUBBOLabel(currentPod) - if err != nil { - if err != ErrDubboLabelAlreadyExist { - return nil, perrors.WithMessage(err, "assemble dubbo label") - } - // current pod don't have label - } - - p, err := c.getPatch(oldPod, newPod) - if err != nil { - return nil, perrors.WithMessage(err, "get patch") - } - - currentPod, err = c.patchCurrentPod(p) - if err != nil { - return nil, perrors.WithMessage(err, "patch to current pod") - } - - return currentPod, nil -} - -// initWatchSet -// 1. get all with dubbo label pods -// 2. put every element to watcherSet -func (c *Client) initWatchSet() error { - - pods, err := c.rawClient.CoreV1().Pods(c.ns).List(metav1.ListOptions{ - LabelSelector: fields.OneTermEqualSelector(DubboIOLabelKey, DubboIOLabelValue).String(), - }) - if err != nil { - return perrors.WithMessagef(err, "list pods in namespace (%s)", c.ns) - } - - // set resource version - c.lastResourceVersion = pods.GetResourceVersion() - - for _, pod := range pods.Items { - logger.Debugf("got the pod (name: %s), (label: %v), (annotations: %v)", pod.Name, pod.GetLabels(), pod.GetAnnotations()) - c.handleWatchedPodEvent(&pod, watch.Added) - } - - return nil -} - -// watchPods -// try to watch kubernetes pods -func (c *Client) watchPods() error { - - // try once - watcher, err := c.rawClient.CoreV1().Pods(c.ns).Watch(metav1.ListOptions{ - LabelSelector: fields.OneTermEqualSelector(DubboIOLabelKey, DubboIOLabelValue).String(), - Watch: true, - ResourceVersion: c.lastResourceVersion, - }) - if err != nil { - return perrors.WithMessagef(err, "try to watch the namespace (%s) pods", c.ns) - } - - watcher.Stop() - - c.wg.Add(1) - // add wg, grace close the client - go c.watchPodsLoop() - return nil -} - -type resourceVersionGetter interface { - GetResourceVersion() string -} - -// watchPods -// try to notify -func (c *Client) watchPodsLoop() { - - defer func() { - // notify other goroutine, this loop over - c.wg.Done() - logger.Info("watchPodsLoop goroutine game over") - }() - - for { - onceWatch: - wc, err := c.rawClient.CoreV1().Pods(c.ns).Watch(metav1.ListOptions{ - LabelSelector: fields.OneTermEqualSelector(DubboIOLabelKey, DubboIOLabelValue).String(), - Watch: true, - ResourceVersion: c.lastResourceVersion, - }) - if err != nil { - logger.Warnf("watch the namespace (%s) pods: %v, retry after 2 seconds", c.ns, err) - time.Sleep(2 * time.Second) - continue - } - - logger.Infof("the old kubernetes client broken, collect the resource status from resource version (%s)", c.lastResourceVersion) - - for { - select { - // double check ctx - case <-c.ctx.Done(): - logger.Infof("the kubernetes client stopped, resultChan len %d", len(wc.ResultChan())) - return - - // get one element from result-chan - case event, ok := <-wc.ResultChan(): - if !ok { - wc.Stop() - logger.Info("kubernetes watch chan die, create new") - goto onceWatch - } - - if event.Type == watch.Error { - // watched a error event - logger.Warnf("kubernetes watch api report err (%#v)", event) - continue - } - - o, ok := event.Object.(resourceVersionGetter) - if !ok { - logger.Warnf("kubernetes response object not a versioned object, its real type %T", event.Object) - continue - } - - // record the last resource version avoid to sync all pod - c.lastResourceVersion = o.GetResourceVersion() - logger.Infof("kubernetes get the current resource version %v", c.lastResourceVersion) - - // check event object type - p, ok := event.Object.(*v1.Pod) - if !ok { - logger.Warnf("kubernetes response object not a Pod, its real type %T", event.Object) - continue - } - - logger.Debugf("kubernetes got pod %#v", p) - // handle the watched pod - go c.handleWatchedPodEvent(p, event.Type) - } - } - } -} - -// handleWatchedPodEvent -// handle watched pod event -func (c *Client) handleWatchedPodEvent(p *v1.Pod, eventType watch.EventType) { - - for ak, av := range p.GetAnnotations() { - - // not dubbo interest annotation - if ak != DubboIOAnnotationKey { - continue - } - - ol, err := c.unmarshalRecord(av) - if err != nil { - logger.Errorf("there a pod with dubbo annotation, but unmarshal dubbo value %v", err) - return - } - - for _, o := range ol { - - switch eventType { - case watch.Added: - // if pod is added, the record always be create - o.EventType = Create - case watch.Modified: - o.EventType = Update - case watch.Deleted: - o.EventType = Delete - default: - logger.Errorf("no valid kubernetes event-type (%s) ", eventType) - return - } - - logger.Debugf("prepare to put object (%#v) to kubernetes-watcherSet", o) - - if err := c.watcherSet.Put(o); err != nil { - logger.Errorf("put (%#v) to cache watcherSet: %v ", o, err) - return - } - - } - - } -} - -// unmarshalRecord -// unmarshal the kubernetes dubbo annotation value -func (c *Client) unmarshalRecord(record string) ([]*WatcherEvent, error) { - - if len(record) == 0 { - // []*WatcherEvent is nil. - return nil, nil - } - - rawMsg, err := base64.URLEncoding.DecodeString(record) - if err != nil { - return nil, perrors.WithMessagef(err, "decode record (%s)", record) - } - - var out []*WatcherEvent - if err := json.Unmarshal(rawMsg, &out); err != nil { - return nil, perrors.WithMessage(err, "decode json") - } - return out, nil -} - -// marshalRecord -// marshal the kubernetes dubbo annotation value -func (c *Client) marshalRecord(ol []*WatcherEvent) (string, error) { - - msg, err := json.Marshal(ol) - if err != nil { - return "", perrors.WithMessage(err, "json encode object list") - } - return base64.URLEncoding.EncodeToString(msg), nil -} - -// readCurrentPod -// read the current pod status from kubernetes api -func (c *Client) readCurrentPod() (*v1.Pod, error) { - - currentPod, err := c.rawClient.CoreV1().Pods(c.ns).Get(c.currentPodName, metav1.GetOptions{}) - if err != nil { - return nil, perrors.WithMessagef(err, "get current (%s) pod in namespace (%s)", c.currentPodName, c.ns) - } - return currentPod, nil -} - -// Create -// create k/v pair in watcher-set +// Create creates k/v pair in watcher-set func (c *Client) Create(k, v string) error { // the read current pod must be lock, protect every @@ -464,132 +82,18 @@ func (c *Client) Create(k, v string) error { c.lock.Lock() defer c.lock.Unlock() - // 1. accord old pod && (k, v) assemble new pod dubbo annotion v - // 2. get patch data - // 3. PATCH the pod - currentPod, err := c.readCurrentPod() - if err != nil { - return perrors.WithMessage(err, "read current pod") + if err := c.controller.addAnnotationForCurrentPod(k, v); err != nil { + return perrors.WithMessagef(err, "add annotation @key = %s @value = %s", k, v) } - oldPod, newPod, err := c.assembleDUBBOAnnotations(k, v, currentPod) - if err != nil { - return perrors.WithMessage(err, "assemble") - } - - patchBytes, err := c.getPatch(oldPod, newPod) - if err != nil { - return perrors.WithMessage(err, "get patch") - } - - updatedPod, err := c.patchCurrentPod(patchBytes) - if err != nil { - return perrors.WithMessage(err, "patch current pod") - } - - c.currentPod = updatedPod logger.Debugf("put the @key = %s @value = %s success", k, v) - // not update the watcherSet, the watcherSet should be write by the watchPodsLoop return nil } -// patch current pod -// write new meta for current pod -func (c *Client) patchCurrentPod(patch []byte) (*v1.Pod, error) { - - updatedPod, err := c.rawClient.CoreV1().Pods(c.ns).Patch(c.currentPodName, types.StrategicMergePatchType, patch) - if err != nil { - return nil, perrors.WithMessage(err, "patch in kubernetes pod ") - } - return updatedPod, nil -} - -// assemble the dubbo kubernetes label -// every dubbo instance should be labeled spec {"dubbo.io/label":"dubbo.io/label-value"} label -func (c *Client) assembleDUBBOLabel(currentPod *v1.Pod) (*v1.Pod, *v1.Pod, error) { - - var ( - oldPod = &v1.Pod{} - newPod = &v1.Pod{} - ) - - oldPod.Labels = make(map[string]string, 8) - newPod.Labels = make(map[string]string, 8) - - if currentPod.GetLabels() != nil { - - if currentPod.GetLabels()[DubboIOLabelKey] == DubboIOLabelValue { - // already have label - return nil, nil, ErrDubboLabelAlreadyExist - } - } - - // copy current pod labels to oldPod && newPod - for k, v := range currentPod.GetLabels() { - oldPod.Labels[k] = v - newPod.Labels[k] = v - } - // assign new label for current pod - newPod.Labels[DubboIOLabelKey] = DubboIOLabelValue - return oldPod, newPod, nil -} - -// assemble the dubbo kubernetes annotations -// accord the current pod && (k,v) assemble the old-pod, new-pod -func (c *Client) assembleDUBBOAnnotations(k, v string, currentPod *v1.Pod) (oldPod *v1.Pod, newPod *v1.Pod, err error) { - - oldPod = &v1.Pod{} - newPod = &v1.Pod{} - oldPod.Annotations = make(map[string]string, 8) - newPod.Annotations = make(map[string]string, 8) - - for k, v := range currentPod.GetAnnotations() { - oldPod.Annotations[k] = v - newPod.Annotations[k] = v - } - - al, err := c.unmarshalRecord(oldPod.GetAnnotations()[DubboIOAnnotationKey]) - if err != nil { - err = perrors.WithMessage(err, "unmarshal record") - return - } - - newAnnotations, err := c.marshalRecord(append(al, &WatcherEvent{Key: k, Value: v})) - if err != nil { - err = perrors.WithMessage(err, "marshal record") - return - } - - newPod.Annotations[DubboIOAnnotationKey] = newAnnotations - return -} - -// getPatch -// get the kubernetes pod patch bytes -func (c *Client) getPatch(oldPod, newPod *v1.Pod) ([]byte, error) { - - oldData, err := json.Marshal(oldPod) - if err != nil { - return nil, perrors.WithMessage(err, "marshal old pod") - } - - newData, err := json.Marshal(newPod) - if err != nil { - return nil, perrors.WithMessage(err, "marshal newPod pod") - } - - patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Pod{}) - if err != nil { - return nil, perrors.WithMessage(err, "create two-way-merge-patch") - } - return patchBytes, nil -} - -// GetChildren -// get k children list from kubernetes-watcherSet +// GetChildren gets k children list from kubernetes-watcherSet func (c *Client) GetChildren(k string) ([]string, []string, error) { - objectList, err := c.watcherSet.Get(k, true) + objectList, err := c.controller.watcherSet.Get(k, true) if err != nil { return nil, nil, perrors.WithMessagef(err, "get children from watcherSet on (%s)", k) } @@ -605,11 +109,10 @@ func (c *Client) GetChildren(k string) ([]string, []string, error) { return kList, vList, nil } -// Watch -// watch on spec key +// Watch watches on spec key func (c *Client) Watch(k string) (<-chan *WatcherEvent, <-chan struct{}, error) { - w, err := c.watcherSet.Watch(k, false) + w, err := c.controller.watcherSet.Watch(k, false) if err != nil { return nil, nil, perrors.WithMessagef(err, "watch on (%s)", k) } @@ -617,11 +120,10 @@ func (c *Client) Watch(k string) (<-chan *WatcherEvent, <-chan struct{}, error) return w.ResultChan(), w.done(), nil } -// Watch -// watch on spec prefix +// WatchWithPrefix watches on spec prefix func (c *Client) WatchWithPrefix(prefix string) (<-chan *WatcherEvent, <-chan struct{}, error) { - w, err := c.watcherSet.Watch(prefix, true) + w, err := c.controller.watcherSet.Watch(prefix, true) if err != nil { return nil, nil, perrors.WithMessagef(err, "watch on prefix (%s)", prefix) } @@ -629,9 +131,7 @@ func (c *Client) WatchWithPrefix(prefix string) (<-chan *WatcherEvent, <-chan st return w.ResultChan(), w.done(), nil } -// Valid -// Valid the client -// if return false, the client is die +// if returns false, the client is die func (c *Client) Valid() bool { select { @@ -641,17 +141,15 @@ func (c *Client) Valid() bool { } c.lock.RLock() defer c.lock.RUnlock() - return c.rawClient != nil + return c.controller != nil } -// Done -// read the client status +// nolint func (c *Client) Done() <-chan struct{} { return c.ctx.Done() } -// Stop -// read the client status +// nolint func (c *Client) Close() { select { @@ -665,28 +163,44 @@ func (c *Client) Close() { // the client ctx be canceled // will trigger the watcherSet watchers all stopped // so, just wait - c.wg.Wait() } -// ValidateClient -// validate the kubernetes client +// ValidateClient validates the kubernetes client func ValidateClient(container clientFacade) error { client := container.Client() // new Client if client == nil || client.Valid() { - ns, err := getCurrentNameSpace() - if err != nil { - return perrors.WithMessage(err, "get current namespace") - } - newClient, err := newClient(ns) + + newClient, err := newClient(container.GetUrl()) if err != nil { - logger.Warnf("new kubernetes client (namespace{%s}: %v)", ns, err) - return perrors.WithMessagef(err, "new kubernetes client (:%+v)", ns) + logger.Warnf("new kubernetes client: %v)", err) + return perrors.WithMessage(err, "new kubernetes client") } container.SetClient(newClient) } return nil } + +// NewMockClient exports for registry package test +func NewMockClient(podList *v1.PodList) (*Client, error) { + + ctx, cancel := context.WithCancel(context.Background()) + controller, err := newDubboRegistryController(ctx, common.CONSUMER, func() (kubernetes.Interface, error) { + return fake.NewSimpleClientset(podList), nil + }) + if err != nil { + return nil, perrors.WithMessage(err, "new dubbo-registry controller") + } + + c := &Client{ + ctx: ctx, + cancel: cancel, + controller: controller, + } + + c.controller.startALLInformers() + return c, nil +} diff --git a/remoting/kubernetes/client_test.go b/remoting/kubernetes/client_test.go index 342285b345b5e45682fe792d35f2f910e7d86d9d..d6c5a2e88057459c0a87cd9a607e9c10970b07b2 100644 --- a/remoting/kubernetes/client_test.go +++ b/remoting/kubernetes/client_test.go @@ -20,20 +20,15 @@ package kubernetes import ( "encoding/json" "fmt" - "net/http" + _ "net/http/pprof" "os" - "runtime" "strings" "sync" "testing" - "time" ) import ( - "github.com/stretchr/testify/suite" v1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" ) // tests dataset @@ -64,255 +59,236 @@ var tests = []struct { // test dataset prefix const prefix = "name" -var clientPodJsonData = `{ +var ( + watcherStopLog = "the watcherSet watcher was stopped" +) +var clientPodListJsonData = `{ "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "annotations": { - "dubbo.io/annotation": "W3siayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzL2NvbnN1bWVyJTNBJTJGJTJGMTcyLjE3LjAuOCUyRlVzZXJQcm92aWRlciUzRmNhdGVnb3J5JTNEY29uc3VtZXJzJTI2ZHViYm8lM0RkdWJib2dvLWNvbnN1bWVyLTIuNi4wJTI2cHJvdG9jb2wlM0RkdWJibyIsInYiOiIifV0=" - }, - "creationTimestamp": "2020-03-13T03:38:57Z", - "labels": { - "dubbo.io/label": "dubbo.io-value" - }, - "name": "client", - "namespace": "default", - "resourceVersion": "2449700", - "selfLink": "/api/v1/namespaces/default/pods/client", - "uid": "3ec394f5-dcc6-49c3-8061-57b4b2b41344" - }, - "spec": { - "containers": [ - { - "env": [ + "items": [ + { + "apiVersion": "v1", + "kind": "Pod", + "metadata": { + "annotations": { + "dubbo.io/annotation": "W3siayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzL2R1YmJvJTNBJTJGJTJGMTcyLjE3LjAuNiUzQTIwMDAwJTJGVXNlclByb3ZpZGVyJTNGYWNjZXNzbG9nJTNEJTI2YW55aG9zdCUzRHRydWUlMjZhcHAudmVyc2lvbiUzRDAuMC4xJTI2YXBwbGljYXRpb24lM0RCRFRTZXJ2aWNlJTI2YXV0aCUzRCUyNmJlYW4ubmFtZSUzRFVzZXJQcm92aWRlciUyNmNsdXN0ZXIlM0RmYWlsb3ZlciUyNmVudmlyb25tZW50JTNEZGV2JTI2ZXhlY3V0ZS5saW1pdCUzRCUyNmV4ZWN1dGUubGltaXQucmVqZWN0ZWQuaGFuZGxlciUzRCUyNmdyb3VwJTNEJTI2aW50ZXJmYWNlJTNEY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyJTI2aXAlM0QxNzIuMTcuMC42JTI2bG9hZGJhbGFuY2UlM0RyYW5kb20lMjZtZXRob2RzLkdldFVzZXIubG9hZGJhbGFuY2UlM0RyYW5kb20lMjZtZXRob2RzLkdldFVzZXIucmV0cmllcyUzRDElMjZtZXRob2RzLkdldFVzZXIudHBzLmxpbWl0LmludGVydmFsJTNEJTI2bWV0aG9kcy5HZXRVc2VyLnRwcy5saW1pdC5yYXRlJTNEJTI2bWV0aG9kcy5HZXRVc2VyLnRwcy5saW1pdC5zdHJhdGVneSUzRCUyNm1ldGhvZHMuR2V0VXNlci53ZWlnaHQlM0QwJTI2bW9kdWxlJTNEZHViYm9nbyUyQnVzZXItaW5mbyUyQnNlcnZlciUyNm5hbWUlM0RCRFRTZXJ2aWNlJTI2b3JnYW5pemF0aW9uJTNEaWt1cmVudG8uY29tJTI2b3duZXIlM0RaWCUyNnBhcmFtLnNpZ24lM0QlMjZwaWQlM0Q2JTI2cmVnaXN0cnkucm9sZSUzRDMlMjZyZWxlYXNlJTNEZHViYm8tZ29sYW5nLTEuMy4wJTI2cmV0cmllcyUzRCUyNnNlcnZpY2UuZmlsdGVyJTNEZWNobyUyNTJDdG9rZW4lMjUyQ2FjY2Vzc2xvZyUyNTJDdHBzJTI1MkNnZW5lcmljX3NlcnZpY2UlMjUyQ2V4ZWN1dGUlMjUyQ3BzaHV0ZG93biUyNnNpZGUlM0Rwcm92aWRlciUyNnRpbWVzdGFtcCUzRDE1OTExNTYxNTUlMjZ0cHMubGltaXQuaW50ZXJ2YWwlM0QlMjZ0cHMubGltaXQucmF0ZSUzRCUyNnRwcy5saW1pdC5yZWplY3RlZC5oYW5kbGVyJTNEJTI2dHBzLmxpbWl0LnN0cmF0ZWd5JTNEJTI2dHBzLmxpbWl0ZXIlM0QlMjZ2ZXJzaW9uJTNEJTI2d2FybXVwJTNEMTAwIiwidiI6IiJ9XQ==" + }, + "creationTimestamp": "2020-06-03T03:49:14Z", + "generateName": "server-84c864f5bc-", + "labels": { + "dubbo.io/label": "dubbo.io-value", + "pod-template-hash": "84c864f5bc", + "role": "server" + }, + "name": "server-84c864f5bc-r8qvz", + "namespace": "default", + "ownerReferences": [ + { + "apiVersion": "apps/v1", + "blockOwnerDeletion": true, + "controller": true, + "kind": "ReplicaSet", + "name": "server-84c864f5bc", + "uid": "fa376dbb-4f37-4705-8e80-727f592c19b3" + } + ], + "resourceVersion": "517460", + "selfLink": "/api/v1/namespaces/default/pods/server-84c864f5bc-r8qvz", + "uid": "f4fc811c-200c-4445-8d4f-532144957dcc" + }, + "spec": { + "containers": [ { - "name": "NAMESPACE", - "valueFrom": { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" + "env": [ + { + "name": "DUBBO_NAMESPACE", + "value": "default" + }, + { + "name": "NAMESPACE", + "valueFrom": { + "fieldRef": { + "apiVersion": "v1", + "fieldPath": "metadata.namespace" + } + } } - } + ], + "image": "192.168.240.101:5000/scott/go-server", + "imagePullPolicy": "Always", + "name": "server", + "resources": {}, + "terminationMessagePath": "/dev/termination-log", + "terminationMessagePolicy": "File", + "volumeMounts": [ + { + "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", + "name": "dubbo-sa-token-5qbtb", + "readOnly": true + } + ] } ], - "image": "registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client", - "imagePullPolicy": "Always", - "name": "client", - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "volumeMounts": [ + "dnsPolicy": "ClusterFirst", + "enableServiceLinks": true, + "nodeName": "minikube", + "priority": 0, + "restartPolicy": "Always", + "schedulerName": "default-scheduler", + "securityContext": {}, + "serviceAccount": "dubbo-sa", + "serviceAccountName": "dubbo-sa", + "terminationGracePeriodSeconds": 30, + "tolerations": [ + { + "effect": "NoExecute", + "key": "node.kubernetes.io/not-ready", + "operator": "Exists", + "tolerationSeconds": 300 + }, { - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "dubbo-sa-token-l2lzh", - "readOnly": true + "effect": "NoExecute", + "key": "node.kubernetes.io/unreachable", + "operator": "Exists", + "tolerationSeconds": 300 + } + ], + "volumes": [ + { + "name": "dubbo-sa-token-5qbtb", + "secret": { + "defaultMode": 420, + "secretName": "dubbo-sa-token-5qbtb" + } } ] - } - ], - "dnsPolicy": "ClusterFirst", - "enableServiceLinks": true, - "nodeName": "minikube", - "priority": 0, - "restartPolicy": "Never", - "schedulerName": "default-scheduler", - "securityContext": {}, - "serviceAccount": "dubbo-sa", - "serviceAccountName": "dubbo-sa", - "terminationGracePeriodSeconds": 30, - "tolerations": [ - { - "effect": "NoExecute", - "key": "node.kubernetes.io/not-ready", - "operator": "Exists", - "tolerationSeconds": 300 - }, - { - "effect": "NoExecute", - "key": "node.kubernetes.io/unreachable", - "operator": "Exists", - "tolerationSeconds": 300 - } - ], - "volumes": [ - { - "name": "dubbo-sa-token-l2lzh", - "secret": { - "defaultMode": 420, - "secretName": "dubbo-sa-token-l2lzh" - } - } - ] - }, - "status": { - "conditions": [ - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:38:57Z", - "status": "True", - "type": "Initialized" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:40:18Z", - "status": "True", - "type": "Ready" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:40:18Z", - "status": "True", - "type": "ContainersReady" }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:38:57Z", - "status": "True", - "type": "PodScheduled" - } - ], - "containerStatuses": [ - { - "containerID": "docker://2870d6abc19ca7fe22ca635ebcfac5d48c6d5550a659bafd74fb48104f6dfe3c", - "image": "registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client:latest", - "imageID": "docker-pullable://registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client@sha256:1f075131f708a0d400339e81549d7c4d4ed917ab0b6bd38ef458dd06ad25a559", - "lastState": {}, - "name": "client", - "ready": true, - "restartCount": 0, - "state": { - "running": { - "startedAt": "2020-03-13T03:40:17Z" + "status": { + "conditions": [ + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:14Z", + "status": "True", + "type": "Initialized" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:15Z", + "status": "True", + "type": "Ready" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:15Z", + "status": "True", + "type": "ContainersReady" + }, + { + "lastProbeTime": null, + "lastTransitionTime": "2020-06-03T03:49:14Z", + "status": "True", + "type": "PodScheduled" + } + ], + "containerStatuses": [ + { + "containerID": "docker://b6421e05ce44f8a1c4fa6b72274980777c7c0f945516209f7c0558cd0cd65406", + "image": "192.168.240.101:5000/scott/go-server:latest", + "imageID": "docker-pullable://192.168.240.101:5000/scott/go-server@sha256:4eecf895054f0ff93d80db64992a561d10504e55582def6dcb6093a6d6d92461", + "lastState": {}, + "name": "server", + "ready": true, + "restartCount": 0, + "started": true, + "state": { + "running": { + "startedAt": "2020-06-03T03:49:15Z" + } + } + } + ], + "hostIP": "10.0.2.15", + "phase": "Running", + "podIP": "172.17.0.6", + "podIPs": [ + { + "ip": "172.17.0.6" } - } + ], + "qosClass": "BestEffort", + "startTime": "2020-06-03T03:49:14Z" } - ], - "hostIP": "10.0.2.15", - "phase": "Running", - "podIP": "172.17.0.8", - "qosClass": "BestEffort", - "startTime": "2020-03-13T03:38:57Z" + } + ], + "kind": "List", + "metadata": { + "resourceVersion": "", + "selfLink": "" } } ` -type KubernetesClientTestSuite struct { - suite.Suite - - currentPod v1.Pod -} - -func (s *KubernetesClientTestSuite) initClient() *Client { - - t := s.T() - - client, err := newMockClient(s.currentPod.GetNamespace(), func() (kubernetes.Interface, error) { - - out := fake.NewSimpleClientset() - - // mock current pod - if _, err := out.CoreV1().Pods(s.currentPod.GetNamespace()).Create(&s.currentPod); err != nil { - t.Fatal(err) - } - return out, nil - }) - if err != nil { - t.Fatal(err) - } - - time.Sleep(time.Second) - return client -} - -func (s *KubernetesClientTestSuite) SetupSuite() { - - runtime.GOMAXPROCS(1) - - t := s.T() +func getTestClient(t *testing.T) *Client { + pl := &v1.PodList{} // 1. install test data - if err := json.Unmarshal([]byte(clientPodJsonData), &s.currentPod); err != nil { + if err := json.Unmarshal([]byte(clientPodListJsonData), &pl); err != nil { t.Fatal(err) } + currentPod := pl.Items[0] - // 2. set downward-api inject env - if err := os.Setenv(podNameKey, s.currentPod.GetName()); err != nil { - t.Fatal(err) - } - if err := os.Setenv(nameSpaceKey, s.currentPod.GetNamespace()); err != nil { - t.Fatal(err) - } - - go http.ListenAndServe(":6061", nil) - -} - -func (s *KubernetesClientTestSuite) TestReadCurrentPodName() { - t := s.T() - - n, err := getCurrentPodName() - if err != nil { - t.Fatal(err) + env := map[string]string{ + nameSpaceKey: currentPod.GetNamespace(), + podNameKey: currentPod.GetName(), + needWatchedNameSpaceKey: "default", } - if n != s.currentPod.GetName() { - t.Fatalf("expect %s but got %s", s.currentPod.GetName(), n) + for k, v := range env { + if err := os.Setenv(k, v); err != nil { + t.Fatal(err) + } } -} -func (s *KubernetesClientTestSuite) TestReadCurrentNameSpace() { - t := s.T() - - ns, err := getCurrentNameSpace() + client, err := NewMockClient(pl) if err != nil { t.Fatal(err) } - if ns != s.currentPod.GetNamespace() { - t.Fatalf("expect %s but got %s", s.currentPod.GetNamespace(), ns) - } - + return client } -func (s *KubernetesClientTestSuite) TestClientValid() { - t := s.T() +func TestClientValid(t *testing.T) { - client := s.initClient() + client := getTestClient(t) defer client.Close() - if client.Valid() != true { + if !client.Valid() { t.Fatal("client is not valid") } client.Close() - if client.Valid() != false { + if client.Valid() { t.Fatal("client is valid") } } -func (s *KubernetesClientTestSuite) TestClientDone() { - - t := s.T() +func TestClientDone(t *testing.T) { - client := s.initClient() + client := getTestClient(t) go func() { - time.Sleep(time.Second) client.Close() }() <-client.Done() - if client.Valid() == true { - t.Fatal("client should be invalid then") + if client.Valid() { + t.Fatal("client should be invalid") } } -func (s *KubernetesClientTestSuite) TestClientCreateKV() { +func TestClientCreateKV(t *testing.T) { - t := s.T() - - client := s.initClient() + client := getTestClient(t) defer client.Close() for _, tc := range tests { @@ -327,11 +303,9 @@ func (s *KubernetesClientTestSuite) TestClientCreateKV() { } } -func (s *KubernetesClientTestSuite) TestClientGetChildrenKVList() { - - t := s.T() +func TestClientGetChildrenKVList(t *testing.T) { - client := s.initClient() + client := getTestClient(t) defer client.Close() wg := sync.WaitGroup{} @@ -360,7 +334,7 @@ func (s *KubernetesClientTestSuite) TestClientGetChildrenKVList() { return } case <-done: - t.Log("the watcherSet watcher was stopped") + t.Log(watcherStopLog) return } } @@ -407,11 +381,9 @@ func (s *KubernetesClientTestSuite) TestClientGetChildrenKVList() { } -func (s *KubernetesClientTestSuite) TestClientWatchPrefix() { +func TestClientWatchPrefix(t *testing.T) { - t := s.T() - - client := s.initClient() + client := getTestClient(t) wg := sync.WaitGroup{} wg.Add(1) @@ -430,7 +402,7 @@ func (s *KubernetesClientTestSuite) TestClientWatchPrefix() { case e := <-wc: t.Logf("got event %v k %s v %s", e.EventType, e.Key, e.Value) case <-done: - t.Log("the watcherSet watcher was stopped") + t.Log(watcherStopLog) return } } @@ -452,22 +424,9 @@ func (s *KubernetesClientTestSuite) TestClientWatchPrefix() { client.Close() } -func (s *KubernetesClientTestSuite) TestNewClient() { - - t := s.T() - - _, err := newClient(s.currentPod.GetNamespace()) - if err == nil { - t.Fatal("the out of cluster test should fail") - } - -} - -func (s *KubernetesClientTestSuite) TestClientWatch() { - - t := s.T() +func TestClientWatch(t *testing.T) { - client := s.initClient() + client := getTestClient(t) wg := sync.WaitGroup{} wg.Add(1) @@ -485,7 +444,7 @@ func (s *KubernetesClientTestSuite) TestClientWatch() { case e := <-wc: t.Logf("got event %v k %s v %s", e.EventType, e.Key, e.Value) case <-done: - t.Log("the watcherSet watcher was stopped") + t.Log(watcherStopLog) return } } @@ -507,7 +466,3 @@ func (s *KubernetesClientTestSuite) TestClientWatch() { client.Close() } - -func TestKubernetesClient(t *testing.T) { - suite.Run(t, new(KubernetesClientTestSuite)) -} diff --git a/remoting/kubernetes/facade.go b/remoting/kubernetes/facade.go index dd15c918b45c353b8395e0b82aee82216f48cd0e..dc060bbb0eb673c6e380dfa3e9d5f7bacbd3fc0b 100644 --- a/remoting/kubernetes/facade.go +++ b/remoting/kubernetes/facade.go @@ -17,7 +17,10 @@ package kubernetes +import "github.com/apache/dubbo-go/common" + type clientFacade interface { Client() *Client SetClient(*Client) + common.Node } diff --git a/remoting/kubernetes/facade_test.go b/remoting/kubernetes/facade_test.go index 024264ffdee14c2ad3fb01a8a5279084c0f085d9..65c5d715a38bd0862245255e0276ff5e959de3a3 100644 --- a/remoting/kubernetes/facade_test.go +++ b/remoting/kubernetes/facade_test.go @@ -18,14 +18,18 @@ package kubernetes import ( + "strconv" "sync" + "testing" ) + import ( - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/fake" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" ) type mockFacade struct { + *common.URL client *Client cltLock sync.Mutex done chan struct{} @@ -39,25 +43,32 @@ func (r *mockFacade) SetClient(client *Client) { r.client = client } -func (s *KubernetesClientTestSuite) Test_Facade() { +func (r *mockFacade) GetUrl() common.URL { + return *r.URL +} - t := s.T() +func (r *mockFacade) Destroy() { + // TODO implementation me +} - mockClient, err := newMockClient(s.currentPod.GetNamespace(), func() (kubernetes.Interface, error) { +func (r *mockFacade) RestartCallBack() bool { + return true +} - out := fake.NewSimpleClientset() +func (r *mockFacade) IsAvailable() bool { + return true +} +func Test_Facade(t *testing.T) { - // mock current pod - if _, err := out.CoreV1().Pods(s.currentPod.GetNamespace()).Create(&s.currentPod); err != nil { - t.Fatal(err) - } - return out, nil - }) + regUrl, err := common.NewURL("registry://127.0.0.1:443", + common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))) if err != nil { t.Fatal(err) } + mockClient := getTestClient(t) m := &mockFacade{ + URL: ®Url, client: mockClient, } diff --git a/remoting/kubernetes/listener.go b/remoting/kubernetes/listener.go index 4c198c66cc3e02006291a195af9d023ec5a02340..a737f4e0d4eae7d78bb17c47e9c216661c8b9c86 100644 --- a/remoting/kubernetes/listener.go +++ b/remoting/kubernetes/listener.go @@ -45,8 +45,8 @@ func NewEventListener(client *Client) *EventListener { } // Listen on a spec key -// this method will return true when spec key deleted, -// this method will return false when deep layer connection lose +// this method returns true when spec key deleted, +// this method returns false when deep layer connection lose func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting.DataListener) bool { defer l.wg.Done() for { @@ -81,12 +81,10 @@ func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting. } } } - - return false } -// return true mean the event type is DELETE -// return false mean the event type is CREATE || UPDATE +// return true means the event type is DELETE +// return false means the event type is CREATE || UPDATE func (l *EventListener) handleEvents(event *WatcherEvent, listeners ...remoting.DataListener) bool { logger.Infof("got a kubernetes-watcherSet event {type: %d, key: %s}", event.EventType, event.Key) diff --git a/remoting/kubernetes/listener_test.go b/remoting/kubernetes/listener_test.go index a9446782a5c336268c3d6418e5882031d1566ae8..1f398485b2f16defddf44ce1a08a7ecfd9760dd1 100644 --- a/remoting/kubernetes/listener_test.go +++ b/remoting/kubernetes/listener_test.go @@ -18,7 +18,7 @@ package kubernetes import ( - "time" + "testing" ) import ( @@ -67,9 +67,7 @@ func (m *mockDataListener) DataChange(eventType remoting.Event) bool { return true } -func (s *KubernetesClientTestSuite) TestListener() { - - t := s.T() +func TestListener(t *testing.T) { var tests = []struct { input struct { @@ -83,15 +81,13 @@ func (s *KubernetesClientTestSuite) TestListener() { }{k: "/dubbo", v: changedData}}, } - c := s.initClient() + c := getTestClient(t) defer c.Close() listener := NewEventListener(c) dataListener := &mockDataListener{client: c, changedData: changedData, rc: make(chan remoting.Event)} listener.ListenServiceEvent("/dubbo", dataListener) - // NOTICE: direct listen will lose create msg - time.Sleep(time.Second) for _, tc := range tests { k := tc.input.k diff --git a/remoting/kubernetes/registry_controller.go b/remoting/kubernetes/registry_controller.go new file mode 100644 index 0000000000000000000000000000000000000000..f93a00a6f2df6022d0436f56e8c719f108be66f3 --- /dev/null +++ b/remoting/kubernetes/registry_controller.go @@ -0,0 +1,598 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 kubernetes + +import ( + "context" + "encoding/base64" + "encoding/json" + "fmt" + "os" + "strconv" + "strings" + "sync" + "time" +) + +import ( + perrors "github.com/pkg/errors" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/strategicpatch" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/informers" + informerscorev1 "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/cache" + "k8s.io/client-go/util/workqueue" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" +) + +const ( + // kubernetes inject env var + podNameKey = "HOSTNAME" + nameSpaceKey = "NAMESPACE" + needWatchedNameSpaceKey = "DUBBO_NAMESPACE" + + // all pod annotation key + DubboIOAnnotationKey = "dubbo.io/annotation" + // all pod label key and value pair + DubboIOLabelKey = "dubbo.io/label" + DubboIOConsumerLabelValue = "dubbo.io.consumer" + DubboIOProviderLabelValue = "dubbo.io.provider" + + // kubernetes suggest resync + defaultResync = 5 * time.Minute +) + +var ( + ErrDubboLabelAlreadyExist = perrors.New("dubbo label already exist") +) + +// dubboRegistryController works like a kubernetes controller +type dubboRegistryController struct { + + // clone from client + // manage lifecycle + ctx context.Context + + role common.RoleType + + // protect patch current pod operation + lock sync.Mutex + + // current pod config + needWatchedNamespace map[string]struct{} + namespace string + name string + + watcherSet WatcherSet + + // kubernetes + kc kubernetes.Interface + listAndWatchStartResourceVersion uint64 + namespacedInformerFactory map[string]informers.SharedInformerFactory + namespacedPodInformers map[string]informerscorev1.PodInformer + queue workqueue.Interface //shared by namespaced informers +} + +func newDubboRegistryController( + ctx context.Context, + // different provider and consumer have behavior + roleType common.RoleType, + // used to inject mock kubernetes client + kcGetter func() (kubernetes.Interface, error), +) (*dubboRegistryController, error) { + + kc, err := kcGetter() + if err != nil { + return nil, perrors.WithMessage(err, "get kubernetes client") + } + + c := &dubboRegistryController{ + ctx: ctx, + role: roleType, + watcherSet: newWatcherSet(ctx), + needWatchedNamespace: make(map[string]struct{}), + namespacedInformerFactory: make(map[string]informers.SharedInformerFactory), + namespacedPodInformers: make(map[string]informerscorev1.PodInformer), + kc: kc, + } + + if err := c.readConfig(); err != nil { + return nil, perrors.WithMessage(err, "read config") + } + + if err := c.initCurrentPod(); err != nil { + return nil, perrors.WithMessage(err, "init current pod") + } + + if err := c.initWatchSet(); err != nil { + return nil, perrors.WithMessage(err, "init watch set") + } + + if err := c.initPodInformer(); err != nil { + return nil, perrors.WithMessage(err, "init pod informer") + } + + go c.run() + + return c, nil +} + +// GetInClusterKubernetesClient +// current pod running in kubernetes-cluster +func GetInClusterKubernetesClient() (kubernetes.Interface, error) { + + // read in-cluster config + cfg, err := rest.InClusterConfig() + if err != nil { + return nil, perrors.WithMessage(err, "get in-cluster config") + } + + return kubernetes.NewForConfig(cfg) +} + +// initWatchSet +// 1. get all with dubbo label pods +// 2. put every element to watcherSet +// 3. refresh watch book-mark +func (c *dubboRegistryController) initWatchSet() error { + + req, err := labels.NewRequirement(DubboIOLabelKey, selection.In, []string{DubboIOConsumerLabelValue, DubboIOProviderLabelValue}) + if err != nil { + return perrors.WithMessage(err, "new requirement") + } + + for ns := range c.needWatchedNamespace { + pods, err := c.kc.CoreV1().Pods(ns).List(metav1.ListOptions{ + LabelSelector: req.String(), + }) + if err != nil { + return perrors.WithMessagef(err, "list pods in namespace (%s)", ns) + } + for _, p := range pods.Items { + // set resource version + rv, err := strconv.ParseUint(p.GetResourceVersion(), 10, 0) + if err != nil { + return perrors.WithMessagef(err, "parse resource version %s", p.GetResourceVersion()) + } + if c.listAndWatchStartResourceVersion < rv { + c.listAndWatchStartResourceVersion = rv + } + c.handleWatchedPodEvent(&p, watch.Added) + } + } + return nil +} + +// read dubbo-registry controller config +// 1. current pod name +// 2. current pod working namespace +func (c *dubboRegistryController) readConfig() error { + + // read current pod name && namespace + c.name = os.Getenv(podNameKey) + if len(c.name) == 0 { + return perrors.New("read value from env by key (HOSTNAME)") + } + c.namespace = os.Getenv(nameSpaceKey) + if len(c.namespace) == 0 { + return perrors.New("read value from env by key (NAMESPACE)") + } + return nil +} + +func (c *dubboRegistryController) initNamespacedPodInformer(ns string) error { + + req, err := labels.NewRequirement(DubboIOLabelKey, selection.In, []string{DubboIOConsumerLabelValue, DubboIOProviderLabelValue}) + if err != nil { + return perrors.WithMessage(err, "new requirement") + } + + informersFactory := informers.NewSharedInformerFactoryWithOptions( + c.kc, + defaultResync, + informers.WithNamespace(ns), + informers.WithTweakListOptions(func(options *metav1.ListOptions) { + options.LabelSelector = req.String() + options.ResourceVersion = strconv.FormatUint(c.listAndWatchStartResourceVersion, 10) + }), + ) + podInformer := informersFactory.Core().V1().Pods() + + podInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: c.addPod, + UpdateFunc: c.updatePod, + DeleteFunc: c.deletePod, + }) + + c.namespacedInformerFactory[ns] = informersFactory + c.namespacedPodInformers[ns] = podInformer + + return nil +} + +func (c *dubboRegistryController) initPodInformer() error { + + if c.role == common.PROVIDER { + return nil + } + + // read need watched namespaces list + needWatchedNameSpaceList := os.Getenv(needWatchedNameSpaceKey) + if len(needWatchedNameSpaceList) == 0 { + return perrors.New("read value from env by key (DUBBO_NAMESPACE)") + } + for _, ns := range strings.Split(needWatchedNameSpaceList, ",") { + c.needWatchedNamespace[ns] = struct{}{} + } + // current work namespace should be watched + c.needWatchedNamespace[c.namespace] = struct{}{} + + c.queue = workqueue.New() + + // init all watch needed pod-informer + for watchedNS := range c.needWatchedNamespace { + if err := c.initNamespacedPodInformer(watchedNS); err != nil { + return err + } + } + return nil +} + +type kubernetesEvent struct { + p *v1.Pod + t watch.EventType +} + +func (c *dubboRegistryController) addPod(obj interface{}) { + p, ok := obj.(*v1.Pod) + if !ok { + logger.Warnf("pod-informer got object %T not *v1.Pod", obj) + return + } + c.queue.Add(&kubernetesEvent{ + t: watch.Added, + p: p, + }) +} + +func (c *dubboRegistryController) updatePod(oldObj, newObj interface{}) { + op, ok := oldObj.(*v1.Pod) + if !ok { + logger.Warnf("pod-informer got object %T not *v1.Pod", oldObj) + return + } + np, ok := newObj.(*v1.Pod) + if !ok { + logger.Warnf("pod-informer got object %T not *v1.Pod", newObj) + return + } + if op.GetResourceVersion() == np.GetResourceVersion() { + return + } + c.queue.Add(&kubernetesEvent{ + p: np, + t: watch.Modified, + }) +} + +func (c *dubboRegistryController) deletePod(obj interface{}) { + p, ok := obj.(*v1.Pod) + if !ok { + logger.Warnf("pod-informer got object %T not *v1.Pod", obj) + return + } + c.queue.Add(&kubernetesEvent{ + p: p, + t: watch.Deleted, + }) +} + +func (c *dubboRegistryController) startALLInformers() { + logger.Debugf("starting namespaced informer-factory") + for _, factory := range c.namespacedInformerFactory { + go factory.Start(c.ctx.Done()) + } +} + +// run +// controller process every event in work-queue +func (c *dubboRegistryController) run() { + + if c.role == common.PROVIDER { + return + } + + defer logger.Warn("dubbo registry controller work stopped") + defer c.queue.ShutDown() + + for ns, podInformer := range c.namespacedPodInformers { + if !cache.WaitForCacheSync(c.ctx.Done(), podInformer.Informer().HasSynced) { + logger.Errorf("wait for cache sync finish @namespace %s fail", ns) + return + } + } + + logger.Infof("kubernetes registry-controller running @Namespace = %q @PodName = %q", c.namespace, c.name) + + // start work + go c.work() + // block wait context cancel + <-c.ctx.Done() +} + +func (c *dubboRegistryController) work() { + for c.processNextWorkItem() { + } +} + +// processNextWorkItem process work-queue elements +func (c *dubboRegistryController) processNextWorkItem() bool { + item, shutdown := c.queue.Get() + if shutdown { + return false + } + defer c.queue.Done(item) + o := item.(*kubernetesEvent) + c.handleWatchedPodEvent(o.p, o.t) + return true +} + +// handleWatchedPodEvent handles watched pod event +func (c *dubboRegistryController) handleWatchedPodEvent(p *v1.Pod, eventType watch.EventType) { + logger.Debugf("get @type = %s event from @pod = %s", eventType, p.GetName()) + + for ak, av := range p.GetAnnotations() { + // not dubbo interest annotation + if ak != DubboIOAnnotationKey { + continue + } + ol, err := c.unmarshalRecord(av) + if err != nil { + logger.Errorf("there a pod with dubbo annotation, but unmarshal dubbo value %v", err) + return + } + for _, o := range ol { + switch eventType { + case watch.Added: + // if pod is added, the record always be create + o.EventType = Create + case watch.Modified: + o.EventType = Update + case watch.Deleted: + o.EventType = Delete + default: + logger.Errorf("no valid kubernetes event-type (%s) ", eventType) + return + } + + logger.Debugf("putting @key=%s @value=%s to watcherSet", o.Key, o.Value) + if err := c.watcherSet.Put(o); err != nil { + logger.Errorf("put (%#v) to cache watcherSet: %v ", o, err) + return + } + } + } +} + +// unmarshalRecord unmarshals the kubernetes dubbo annotation value +func (c *dubboRegistryController) unmarshalRecord(record string) ([]*WatcherEvent, error) { + + if len(record) == 0 { + // []*WatcherEvent is nil. + return nil, nil + } + + rawMsg, err := base64.URLEncoding.DecodeString(record) + if err != nil { + return nil, perrors.WithMessagef(err, "decode record (%s)", record) + } + + var out []*WatcherEvent + if err := json.Unmarshal(rawMsg, &out); err != nil { + return nil, perrors.WithMessage(err, "decode json") + } + return out, nil +} + +// initCurrentPod +// 1. get current pod +// 2. give the dubbo-label for this pod +func (c *dubboRegistryController) initCurrentPod() error { + currentPod, err := c.readCurrentPod() + if err != nil { + return perrors.WithMessagef(err, "get current (%s) pod in namespace (%s)", c.name, c.namespace) + } + + oldPod, newPod, err := c.assembleDUBBOLabel(currentPod) + if err != nil { + if err == ErrDubboLabelAlreadyExist { + return nil + } + return perrors.WithMessage(err, "assemble dubbo label") + } + // current pod don't have label + p, err := c.getPatch(oldPod, newPod) + if err != nil { + return perrors.WithMessage(err, "get patch") + } + + currentPod, err = c.patchCurrentPod(p) + if err != nil { + return perrors.WithMessage(err, "patch to current pod") + } + + return nil +} + +// patchCurrentPod writes new meta for current pod +func (c *dubboRegistryController) patchCurrentPod(patch []byte) (*v1.Pod, error) { + updatedPod, err := c.kc.CoreV1().Pods(c.namespace).Patch(c.name, types.StrategicMergePatchType, patch) + if err != nil { + return nil, perrors.WithMessage(err, "patch in kubernetes pod ") + } + return updatedPod, nil +} + +// assembleDUBBOLabel assembles the dubbo kubernetes label +// every dubbo instance should be labeled spec {"dubbo.io/label":"dubbo.io/label-value"} label +func (c *dubboRegistryController) assembleDUBBOLabel(p *v1.Pod) (*v1.Pod, *v1.Pod, error) { + var ( + oldPod = &v1.Pod{} + newPod = &v1.Pod{} + ) + oldPod.Labels = make(map[string]string, 8) + newPod.Labels = make(map[string]string, 8) + + if p.GetLabels() != nil { + if _, ok := p.GetLabels()[DubboIOLabelKey]; ok { + // already have label + return nil, nil, ErrDubboLabelAlreadyExist + } + } + + // copy current pod labels to oldPod && newPod + for k, v := range p.GetLabels() { + oldPod.Labels[k] = v + newPod.Labels[k] = v + } + + // assign new label for current pod + switch c.role { + case common.CONSUMER: + newPod.Labels[DubboIOLabelKey] = DubboIOConsumerLabelValue + case common.PROVIDER: + newPod.Labels[DubboIOLabelKey] = DubboIOProviderLabelValue + default: + return nil, nil, perrors.New(fmt.Sprintf("unknown role %s", c.role)) + } + return oldPod, newPod, nil +} + +// assembleDUBBOAnnotations assembles the dubbo kubernetes annotations +// accord the current pod && (k,v) assemble the old-pod, new-pod +func (c *dubboRegistryController) assembleDUBBOAnnotations(k, v string, currentPod *v1.Pod) (oldPod *v1.Pod, newPod *v1.Pod, err error) { + + oldPod = &v1.Pod{} + newPod = &v1.Pod{} + oldPod.Annotations = make(map[string]string, 8) + newPod.Annotations = make(map[string]string, 8) + + for k, v := range currentPod.GetAnnotations() { + oldPod.Annotations[k] = v + newPod.Annotations[k] = v + } + + al, err := c.unmarshalRecord(oldPod.GetAnnotations()[DubboIOAnnotationKey]) + if err != nil { + err = perrors.WithMessage(err, "unmarshal record") + return + } + + newAnnotations, err := c.marshalRecord(append(al, &WatcherEvent{Key: k, Value: v})) + if err != nil { + err = perrors.WithMessage(err, "marshal record") + return + } + + newPod.Annotations[DubboIOAnnotationKey] = newAnnotations + return +} + +// getPatch gets the kubernetes pod patch bytes +func (c *dubboRegistryController) getPatch(oldPod, newPod *v1.Pod) ([]byte, error) { + oldData, err := json.Marshal(oldPod) + if err != nil { + return nil, perrors.WithMessage(err, "marshal old pod") + } + + newData, err := json.Marshal(newPod) + if err != nil { + return nil, perrors.WithMessage(err, "marshal newPod pod") + } + + patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, v1.Pod{}) + if err != nil { + return nil, perrors.WithMessage(err, "create two-way-merge-patch") + } + return patchBytes, nil +} + +// marshalRecord marshals the kubernetes dubbo annotation value +func (c *dubboRegistryController) marshalRecord(ol []*WatcherEvent) (string, error) { + msg, err := json.Marshal(ol) + if err != nil { + return "", perrors.WithMessage(err, "json encode object list") + } + return base64.URLEncoding.EncodeToString(msg), nil +} + +// readCurrentPod reads from kubernetes-env current pod status +func (c *dubboRegistryController) readCurrentPod() (*v1.Pod, error) { + currentPod, err := c.kc.CoreV1().Pods(c.namespace).Get(c.name, metav1.GetOptions{}) + if err != nil { + return nil, perrors.WithMessagef(err, "get current (%s) pod in namespace (%s)", c.name, c.namespace) + } + return currentPod, nil +} + +// addAnnotationForCurrentPod adds annotation for current pod +func (c *dubboRegistryController) addAnnotationForCurrentPod(k string, v string) error { + + c.lock.Lock() + defer c.lock.Unlock() + + // 1. accord old pod && (k, v) assemble new pod dubbo annotion v + // 2. get patch data + // 3. PATCH the pod + currentPod, err := c.readCurrentPod() + if err != nil { + return perrors.WithMessage(err, "read current pod") + } + + oldPod, newPod, err := c.assembleDUBBOAnnotations(k, v, currentPod) + if err != nil { + return perrors.WithMessage(err, "assemble") + } + + patchBytes, err := c.getPatch(oldPod, newPod) + if err != nil { + return perrors.WithMessage(err, "get patch") + } + + _, err = c.patchCurrentPod(patchBytes) + if err != nil { + return perrors.WithMessage(err, "patch current pod") + } + + return c.watcherSet.Put(&WatcherEvent{ + Key: k, + Value: v, + EventType: Create, + }) +} diff --git a/remoting/kubernetes/watch.go b/remoting/kubernetes/watch.go index c99a3ebcc041f2fed0160f1f286e72937d2c9aee..07eeb09b4dd4627fdd3b18ee4d59356911b3a9b1 100644 --- a/remoting/kubernetes/watch.go +++ b/remoting/kubernetes/watch.go @@ -71,6 +71,7 @@ type WatcherEvent struct { } // Watchable WatcherSet +// thread-safe type WatcherSet interface { // put the watch event to the watch set @@ -139,17 +140,15 @@ func (s *watcherSetImpl) Watch(key string, prefix bool) (Watcher, error) { return s.addWatcher(key, prefix) } -// Done -// get the watcher-set status +// Done gets the watcher-set status func (s *watcherSetImpl) Done() <-chan struct{} { return s.ctx.Done() } -// Put -// put the watch event to watcher-set +// Put puts the watch event to watcher-set func (s *watcherSetImpl) Put(watcherEvent *WatcherEvent) error { - sendMsg := func(object *WatcherEvent, w *watcher) { + blockSendMsg := func(object *WatcherEvent, w *watcher) { select { case <-w.done(): @@ -167,40 +166,40 @@ func (s *watcherSetImpl) Put(watcherEvent *WatcherEvent) error { } // put to watcher-set - if watcherEvent.EventType == Delete { + switch watcherEvent.EventType { + case Delete: + // delete from store delete(s.cache, watcherEvent.Key) - } else { - - old, ok := s.cache[watcherEvent.Key] - if ok { - if old.Value == watcherEvent.Value { - // already have this k/v pair - return nil - } + case Update, Create: + o, ok := s.cache[watcherEvent.Key] + if !ok { + // pod update, but create new k/v pair + watcherEvent.EventType = Create + s.cache[watcherEvent.Key] = watcherEvent + break } - - // refresh the watcherEvent + // k/v pair already latest + if o.Value == watcherEvent.Value { + return nil + } + // update to latest status s.cache[watcherEvent.Key] = watcherEvent } // notify watcher for _, w := range s.watchers { - - w := w - if !strings.Contains(watcherEvent.Key, w.interested.key) { // this watcher no interest in this element continue } - if !w.interested.prefix { if watcherEvent.Key == w.interested.key { - go sendMsg(watcherEvent, w) + blockSendMsg(watcherEvent, w) } // not interest continue } - go sendMsg(watcherEvent, w) + blockSendMsg(watcherEvent, w) } return nil } @@ -242,8 +241,7 @@ func (s *watcherSetImpl) addWatcher(key string, prefix bool) (Watcher, error) { return w, nil } -// Get -// get elements from watcher-set +// Get gets elements from watcher-set func (s *watcherSetImpl) Get(key string, prefix bool) ([]*WatcherEvent, error) { s.lock.RLock() @@ -296,19 +294,17 @@ type watcher struct { exit chan struct{} } -// ResultChan +// nolint func (w *watcher) ResultChan() <-chan *WatcherEvent { return w.ch } -// ID -// the watcher's id +// nolint func (w *watcher) ID() string { return strconv.FormatUint(w.id, 10) } -// stop -// stop the watcher +// nolint func (w *watcher) stop() { // double close will panic @@ -317,14 +313,12 @@ func (w *watcher) stop() { }) } -// done -// check watcher status +// done checks watcher status func (w *watcher) done() <-chan struct{} { return w.exit } -// newWatcherSet -// new watcher set from parent context +// newWatcherSet returns new watcher set from parent context func newWatcherSet(ctx context.Context) WatcherSet { s := &watcherSetImpl{ ctx: ctx, diff --git a/remoting/listener.go b/remoting/listener.go index 3713ba0ccf9d98d4470741785a9490e657cf051c..6cbb883181ff8ec1c9124f8d8cc3d7ec0920abd9 100644 --- a/remoting/listener.go +++ b/remoting/listener.go @@ -21,7 +21,7 @@ import ( "fmt" ) -// DataListener ... +// DataListener defines common data listener interface type DataListener interface { DataChange(eventType Event) bool //bool is return for interface implement is interesting } @@ -30,15 +30,15 @@ type DataListener interface { // event type ////////////////////////////////////////// -// EventType ... +// EventType means SourceObjectEventType type EventType int const ( - // EventTypeAdd ... + // EventTypeAdd means add event EventTypeAdd = iota - // EventTypeDel ... + // EventTypeDel means del event EventTypeDel - // EventTypeUpdate ... + // EventTypeUpdate means update event EventTypeUpdate ) @@ -56,7 +56,7 @@ func (t EventType) String() string { // service event ////////////////////////////////////////// -// Event ... +// Event defines common elements for service event type Event struct { Path string Action EventType diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index bd1da547766abb12dc742234787262212e3db314..226ec24085ddd0cfaea0c0d6a0ba6c91a9840ad0 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -47,11 +47,11 @@ var ( errNilNode = perrors.Errorf("node does not exist") ) -// ZookeeperClient ... +// ZookeeperClient represents zookeeper client Configuration type ZookeeperClient struct { name string ZkAddrs []string - sync.Mutex // for conn + sync.RWMutex // for conn Conn *zk.Conn Timeout time.Duration exit chan struct{} @@ -59,7 +59,7 @@ type ZookeeperClient struct { eventRegistry map[string][]*chan struct{} } -// StateToString ... +// nolint func StateToString(state zk.State) string { switch state { case zk.StateDisconnected: @@ -87,11 +87,9 @@ func StateToString(state zk.State) string { default: return state.String() } - - return "zookeeper unknown state" } -// Options ... +// nolint type Options struct { zkName string client *ZookeeperClient @@ -99,24 +97,22 @@ type Options struct { ts *zk.TestCluster } -// Option ... +// Option will define a function of handling Options type Option func(*Options) -// WithZkName ... +// WithZkName sets zk client name func WithZkName(name string) Option { return func(opt *Options) { opt.zkName = name } } -// ValidateZookeeperClient ... +// ValidateZookeeperClient validates client and sets options func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { - var ( - err error - ) - opions := &Options{} + var err error + options := &Options{} for _, opt := range opts { - opt(opions) + opt(options) } connected := false err = nil @@ -129,17 +125,18 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { if container.ZkClient() == nil { //in dubbo ,every registry only connect one node ,so this is []string{r.Address} - timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) + var timeout time.Duration + timeout, err = time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) if err != nil { logger.Errorf("timeout config %v is invalid ,err is %v", url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), err.Error()) return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) } zkAddresses := strings.Split(url.Location, ",") - newClient, err := newZookeeperClient(opions.zkName, zkAddresses, timeout) + newClient, err := newZookeeperClient(options.zkName, zkAddresses, timeout) if err != nil { logger.Warnf("newZookeeperClient(name{%s}, zk address{%v}, timeout{%d}) = error{%v}", - opions.zkName, url.Location, timeout.String(), err) + options.zkName, url.Location, timeout.String(), err) return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) } container.SetZkClient(newClient) @@ -157,7 +154,7 @@ func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { } if connected { - logger.Info("Connect to zookeeper successfully, name{%s}, zk address{%v}", opions.zkName, url.Location) + logger.Info("Connect to zookeeper successfully, name{%s}, zk address{%v}", options.zkName, url.Location) container.WaitGroup().Add(1) //zk client start successful, then registry wg +1 } @@ -190,14 +187,14 @@ func newZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (* return z, nil } -// WithTestCluster ... +// WithTestCluster sets test cluser for zk client func WithTestCluster(ts *zk.TestCluster) Option { return func(opt *Options) { opt.ts = ts } } -// NewMockZookeeperClient ... +// NewMockZookeeperClient returns a mock client instance func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event, error) { var ( err error @@ -229,21 +226,15 @@ func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) } } - //callbackChan := make(chan zk.Event) - //f := func(event zk.Event) { - // callbackChan <- event - //} - z.Conn, event, err = ts.ConnectWithOptions(timeout) if err != nil { return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect") } - //z.wait.Add(1) return ts, z, event, nil } -// HandleZkEvent ... +// HandleZkEvent handles zookeeper events func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) { var ( state int @@ -304,7 +295,7 @@ LOOP: } } -// RegisterEvent ... +// RegisterEvent registers zookeeper events func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" || event == nil { return @@ -319,7 +310,7 @@ func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { z.Unlock() } -// UnregisterEvent ... +// UnregisterEvent unregisters zookeeper events func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" { return @@ -346,7 +337,7 @@ func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { } } -// Done ... +// nolint func (z *ZookeeperClient) Done() <-chan struct{} { return z.exit } @@ -362,7 +353,7 @@ func (z *ZookeeperClient) stop() bool { return false } -// ZkConnValid ... +// ZkConnValid validates zookeeper connection func (z *ZookeeperClient) ZkConnValid() bool { select { case <-z.exit: @@ -380,7 +371,7 @@ func (z *ZookeeperClient) ZkConnValid() bool { return valid } -// Close ... +// nolint func (z *ZookeeperClient) Close() { if z == nil { return @@ -439,7 +430,7 @@ func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { return nil } -// Delete ... +// nolint func (z *ZookeeperClient) Delete(basePath string) error { var ( err error @@ -456,7 +447,7 @@ func (z *ZookeeperClient) Delete(basePath string) error { return perrors.WithMessagef(err, "Delete(basePath:%s)", basePath) } -// RegisterTemp ... +// RegisterTemp registers temporary node by @basePath and @node func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, error) { var ( err error @@ -475,7 +466,6 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er tmpPath, err = conn.Create(zkPath, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) } - //if err != nil && err != zk.ErrNodeExists { if err != nil { logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err)) return zkPath, perrors.WithStack(err) @@ -485,7 +475,7 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er return tmpPath, nil } -// RegisterTempSeq ... +// RegisterTempSeq register temporary sequence node by @basePath and @data func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, error) { var ( err error @@ -516,7 +506,7 @@ func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, return tmpPath, nil } -// GetChildrenW ... +// GetChildrenW gets children watch by @path func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { var ( err error @@ -553,7 +543,7 @@ func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, return children, watcher.EvtCh, nil } -// GetChildren ... +// GetChildren gets children by @path func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { var ( err error @@ -586,7 +576,7 @@ func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { return children, nil } -// ExistW ... +// ExistW to judge watch whether it exists or not by @zkPath func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { var ( exist bool @@ -614,7 +604,7 @@ func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { return watcher.EvtCh, nil } -// GetContent ... +// GetContent gets content by @zkPath func (z *ZookeeperClient) GetContent(zkPath string) ([]byte, *zk.Stat, error) { return z.Conn.Get(zkPath) } diff --git a/remoting/zookeeper/client_test.go b/remoting/zookeeper/client_test.go index cb41eb326be95470e39694fc5df233fdf073b905..0f6899568ad4744dc58022c41e22db6f901ad5de 100644 --- a/remoting/zookeeper/client_test.go +++ b/remoting/zookeeper/client_test.go @@ -106,7 +106,6 @@ func TestCreateDelete(t *testing.T) { assert.NoError(t, err) err2 := z.Delete("/test1/test2/test3/test4") assert.NoError(t, err2) - //verifyEventOrder(t, event, []zk.EventType{zk.EventNodeCreated}, "event channel") } func TestRegisterTemp(t *testing.T) { diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index 4e3945388ff402f60a02150615a8914f9cba2435..f9b9332504f445724a54b94356771e4ad49b62f0 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -40,7 +40,7 @@ type zkClientFacade interface { common.Node } -// HandleClientRestart ... +// HandleClientRestart keeps the connection between client and server func HandleClientRestart(r zkClientFacade) { var ( err error @@ -78,10 +78,8 @@ LOOP: err = ValidateZookeeperClient(r, WithZkName(zkName)) logger.Infof("ZkProviderRegistry.validateZookeeperClient(zkAddr{%s}) = error{%#v}", zkAddress, perrors.WithStack(err)) - if err == nil { - if r.RestartCallBack() { - break - } + if err == nil && r.RestartCallBack() { + break } failTimes++ if MaxFailTimes <= failTimes { diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index 84877667763ce870e76202844e9dc9dc1c3f008c..5188ce8c44355cf78eeb8c6078ab4f2f49e516db 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -18,7 +18,6 @@ package zookeeper import ( - "github.com/apache/dubbo-go/common" "path" "strings" "sync" @@ -32,12 +31,13 @@ import ( ) import ( + "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/remoting" ) -// ZkEventListener ... +// nolint type ZkEventListener struct { client *ZookeeperClient pathMapLock sync.Mutex @@ -45,7 +45,7 @@ type ZkEventListener struct { wg sync.WaitGroup } -// NewZkEventListener ... +// NewZkEventListener returns a EventListener instance func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { return &ZkEventListener{ client: client, @@ -53,12 +53,12 @@ func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { } } -// SetClient ... +// nolint func (l *ZkEventListener) SetClient(client *ZookeeperClient) { l.client = client } -// ListenServiceNodeEvent ... +// nolint func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener ...remoting.DataListener) bool { defer l.wg.Done() var zkEvent zk.Event @@ -96,8 +96,6 @@ func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener ...remo return false } } - - return false } func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, listener remoting.DataListener) { @@ -249,8 +247,14 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen l.pathMapLock.Lock() l.pathMap[dubboPath] = struct{}{} l.pathMapLock.Unlock() - + //When Zk disconnected, the Conn will be set to nil, so here need check the value of Conn + l.client.RLock() + if l.client.Conn == nil { + l.client.RUnlock() + break + } content, _, err := l.client.Conn.Get(dubboPath) + l.client.RUnlock() if err != nil { logger.Errorf("Get new node path {%v} 's content error,message is {%v}", dubboPath, perrors.WithStack(err)) } @@ -314,7 +318,7 @@ func (l *ZkEventListener) valid() bool { return l.client.ZkConnValid() } -// Close ... +// nolint func (l *ZkEventListener) Close() { l.wg.Wait() } diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go index ba7d6ba81b6af97dc5ad3788e8399d08cbe5b2bb..37ef1b4b967d2f6708a4a099875ae90f273ae483 100644 --- a/remoting/zookeeper/listener_test.go +++ b/remoting/zookeeper/listener_test.go @@ -32,6 +32,10 @@ import ( "github.com/apache/dubbo-go/remoting" ) +var ( + dubboPropertiesPath = "/dubbo/dubbo.properties" +) + func initZkData(t *testing.T) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event) { ts, client, event, err := NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) @@ -58,10 +62,10 @@ func initZkData(t *testing.T) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Even dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` - err = client.Create("/dubbo/dubbo.properties") + err = client.Create(dubboPropertiesPath) assert.NoError(t, err) - _, err = client.Conn.Set("/dubbo/dubbo.properties", []byte(data), 0) + _, err = client.Conn.Set(dubboPropertiesPath, []byte(data), 0) assert.NoError(t, err) return ts, client, event @@ -99,7 +103,7 @@ func TestListener(t *testing.T) { dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait} listener.ListenServiceEvent(nil, "/dubbo", dataListener) time.Sleep(1 * time.Second) - _, err := client.Conn.Set("/dubbo/dubbo.properties", []byte(changedData), 1) + _, err := client.Conn.Set(dubboPropertiesPath, []byte(changedData), 1) assert.NoError(t, err) wait.Wait() assert.Equal(t, changedData, dataListener.eventList[1].Content) diff --git a/test/integrate/dubbo/go-client/Dockerfile b/test/integrate/dubbo/go-client/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..d48df36dc72d7e75f8c2c8c91d5acbb01e39757d --- /dev/null +++ b/test/integrate/dubbo/go-client/Dockerfile @@ -0,0 +1,36 @@ +# +#Licensed to the Apache Software Foundation (ASF) under one or more +#contributor license agreements. See the NOTICE file distributed with +#this work for additional information regarding copyright ownership. +#The ASF licenses this file to You 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. +# + +FROM golang + +WORKDIR /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +ENV CONF_CONSUMER_FILE_PATH "client.yml" +ENV APP_LOG_CONF_FILE "log.yml" + +ARG PR_ORIGIN_REPO +ARG PR_ORIGIN_COMMITID + +ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +# update dubbo-go to current commit id +RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' +RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop + +RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +CMD go-client \ No newline at end of file diff --git a/test/integrate/dubbo/go-client/client.go b/test/integrate/dubbo/go-client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..4c62674d33dba7caca72ca7552e73c4c0fdf14c9 --- /dev/null +++ b/test/integrate/dubbo/go-client/client.go @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +import ( + "context" + "fmt" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "github.com/apache/dubbo-go/filter/filter_impl" + + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" +) + +var ( + survivalTimeout int = 10e9 +) + +func println(format string, args ...interface{}) { + fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...) +} + +// they are necessary: +// export CONF_CONSUMER_FILE_PATH="xxx" +// export APP_LOG_CONF_FILE="xxx" +func main() { + hessian.RegisterPOJO(&User{}) + config.Load() + time.Sleep(3e9) + + println("\n\n\nstart to test dubbo") + + go func() { + select { + case <-time.After(time.Minute): + panic("provider not start after client already running 1min") + } + }() + user := &User{} + err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user) + if err != nil { + panic(err) + } + println("response result: %v\n", user) +} diff --git a/test/integrate/dubbo/go-client/client.yml b/test/integrate/dubbo/go-client/client.yml new file mode 100644 index 0000000000000000000000000000000000000000..df44a13da38a14fa4a81b6189aa05708cf6f5599 --- /dev/null +++ b/test/integrate/dubbo/go-client/client.yml @@ -0,0 +1,61 @@ +# dubbo client yaml configure file + + +check: true +# client +request_timeout : "3s" +# connect timeout +connect_timeout : "3s" + +# application config +application: + organization : "ikurento.com" + name : "BDTService" + module : "dubbogo user-info client" + version : "0.0.1" + owner : "ZX" + environment : "dev" + +registries : + "demoZk": + protocol: "zookeeper" + timeout : "3s" + address: "127.0.0.1:2181" + username: "" + password: "" + + +references: + "UserProvider": + # 鍙互鎸囧畾澶氫釜registry锛屼娇鐢ㄩ€楀彿闅斿紑;涓嶆寚瀹氶粯璁ゅ悜鎵€鏈夋敞鍐屼腑蹇冩敞鍐� + registry: "demoZk" + protocol : "dubbo" + interface : "com.ikurento.user.UserProvider" + cluster: "failover" + methods : + - name: "GetUser" + retries: 3 + + +protocol_conf: + dubbo: + reconnect_interval: 0 + connection_number: 2 + heartbeat_period: "5s" + session_timeout: "20s" + pool_size: 64 + pool_ttl: 600 + getty_session_param: + compress_encoding: false + tcp_no_delay: true + tcp_keep_alive: true + keep_alive_period: "120s" + tcp_r_buf_size: 262144 + tcp_w_buf_size: 65536 + pkg_rq_size: 1024 + pkg_wq_size: 512 + tcp_read_timeout: "1s" + tcp_write_timeout: "5s" + wait_timeout: "1s" + max_msg_len: 1024000 + session_name: "client" diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..4708eb1f0f48c10acc254880ecb6dad3a03529f2 --- /dev/null +++ b/test/integrate/dubbo/go-client/go.mod @@ -0,0 +1,3 @@ +module github.com/apache/dubbo-go/test/integrate/dubbo/go-client + +go 1.13 diff --git a/test/integrate/dubbo/go-client/log.yml b/test/integrate/dubbo/go-client/log.yml new file mode 100644 index 0000000000000000000000000000000000000000..59fa4279ad85272c4c49d532beaf23b74d00f58a --- /dev/null +++ b/test/integrate/dubbo/go-client/log.yml @@ -0,0 +1,28 @@ + +level: "debug" +development: true +disableCaller: false +disableStacktrace: false +sampling: +encoding: "console" + +# encoder +encoderConfig: + messageKey: "message" + levelKey: "level" + timeKey: "time" + nameKey: "logger" + callerKey: "caller" + stacktraceKey: "stacktrace" + lineEnding: "" + levelEncoder: "capitalColor" + timeEncoder: "iso8601" + durationEncoder: "seconds" + callerEncoder: "short" + nameEncoder: "" + +outputPaths: + - "stderr" +errorOutputPaths: + - "stderr" +initialFields: diff --git a/test/integrate/dubbo/go-client/user.go b/test/integrate/dubbo/go-client/user.go new file mode 100644 index 0000000000000000000000000000000000000000..ff4486f07975ebbb0064a8c83b71c952e6311dbb --- /dev/null +++ b/test/integrate/dubbo/go-client/user.go @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +import ( + "context" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" +) + +var userProvider = new(UserProvider) + +func init() { + config.SetConsumerService(userProvider) + hessian.RegisterPOJO(&User{}) +} + +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { + GetUser func(ctx context.Context, req []interface{}, rsp *User) error +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/test/integrate/dubbo/go-client/version.go b/test/integrate/dubbo/go-client/version.go new file mode 100644 index 0000000000000000000000000000000000000000..c6138584f1ddeab3a4927774f44f9e78a8f08da7 --- /dev/null +++ b/test/integrate/dubbo/go-client/version.go @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +var ( + Version = "2.6.0" +) diff --git a/test/integrate/dubbo/go-server/Dockerfile b/test/integrate/dubbo/go-server/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..c2f2d63462d94df7624ac100023e8b8c24e23e11 --- /dev/null +++ b/test/integrate/dubbo/go-server/Dockerfile @@ -0,0 +1,35 @@ +# +#Licensed to the Apache Software Foundation (ASF) under one or more +#contributor license agreements. See the NOTICE file distributed with +#this work for additional information regarding copyright ownership. +#The ASF licenses this file to You 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. +# + +FROM golang + +WORKDIR /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +ENV CONF_PROVIDER_FILE_PATH "server.yml" +ENV APP_LOG_CONF_FILE "log.yml" + +ARG PR_ORIGIN_REPO +ARG PR_ORIGIN_COMMITID + +ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-server +# update dubbo-go to current commit id +RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' +RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop + +RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +CMD go-server diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..9e1162327de374fb131c2a0b89d1be3baa578a1b --- /dev/null +++ b/test/integrate/dubbo/go-server/go.mod @@ -0,0 +1,3 @@ +module github.com/apache/dubbo-go/test/integrate/dubbo/go-server + +go 1.13 diff --git a/test/integrate/dubbo/go-server/log.yml b/test/integrate/dubbo/go-server/log.yml new file mode 100644 index 0000000000000000000000000000000000000000..59fa4279ad85272c4c49d532beaf23b74d00f58a --- /dev/null +++ b/test/integrate/dubbo/go-server/log.yml @@ -0,0 +1,28 @@ + +level: "debug" +development: true +disableCaller: false +disableStacktrace: false +sampling: +encoding: "console" + +# encoder +encoderConfig: + messageKey: "message" + levelKey: "level" + timeKey: "time" + nameKey: "logger" + callerKey: "caller" + stacktraceKey: "stacktrace" + lineEnding: "" + levelEncoder: "capitalColor" + timeEncoder: "iso8601" + durationEncoder: "seconds" + callerEncoder: "short" + nameEncoder: "" + +outputPaths: + - "stderr" +errorOutputPaths: + - "stderr" +initialFields: diff --git a/test/integrate/dubbo/go-server/server.go b/test/integrate/dubbo/go-server/server.go new file mode 100644 index 0000000000000000000000000000000000000000..115bf0a4d78f171eb7f786808def91879ed93947 --- /dev/null +++ b/test/integrate/dubbo/go-server/server.go @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +import ( + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/filter_impl" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + _ "github.com/apache/dubbo-go/registry/zookeeper" +) + +var ( + stopC = make(chan struct{}) +) + +// they are necessary: +// export CONF_PROVIDER_FILE_PATH="xxx" +// export APP_LOG_CONF_FILE="xxx" +func main() { + + hessian.RegisterPOJO(&User{}) + config.Load() + + select { + case <-stopC: + // wait getty send resp to consumer + time.Sleep(3*time.Second) + return + case <-time.After(time.Minute): + panic("provider already running 1 min, but can't be call by consumer") + } +} diff --git a/test/integrate/dubbo/go-server/server.yml b/test/integrate/dubbo/go-server/server.yml new file mode 100644 index 0000000000000000000000000000000000000000..8a17297b10119c217e68b39d58a10493f6dfc7a7 --- /dev/null +++ b/test/integrate/dubbo/go-server/server.yml @@ -0,0 +1,57 @@ +# dubbo server yaml configure file + + +# application config +application: + organization : "ikurento.com" + name : "BDTService" + module : "dubbogo user-info server" + version : "0.0.1" + owner : "ZX" + environment : "dev" + +registries : + "demoZk": + protocol: "zookeeper" + timeout : "3s" + address: "127.0.0.1:2181" + +services: + "UserProvider": + # 鍙互鎸囧畾澶氫釜registry锛屼娇鐢ㄩ€楀彿闅斿紑;涓嶆寚瀹氶粯璁ゅ悜鎵€鏈夋敞鍐屼腑蹇冩敞鍐� + registry: "demoZk" + protocol : "dubbo" + # 鐩稿綋浜巇ubbo.xml涓殑interface + interface : "com.ikurento.user.UserProvider" + loadbalance: "random" + warmup: "100" + cluster: "failover" + methods: + - name: "GetUser" + retries: 1 + loadbalance: "random" + +protocols: + "dubbo": + name: "dubbo" + port: 20000 + + +protocol_conf: + dubbo: + session_number: 700 + session_timeout: "20s" + getty_session_param: + compress_encoding: false + tcp_no_delay: true + tcp_keep_alive: true + keep_alive_period: "120s" + tcp_r_buf_size: 262144 + tcp_w_buf_size: 65536 + pkg_rq_size: 1024 + pkg_wq_size: 512 + tcp_read_timeout: "1s" + tcp_write_timeout: "5s" + wait_timeout: "1s" + max_msg_len: 1024000 + session_name: "server" diff --git a/test/integrate/dubbo/go-server/user.go b/test/integrate/dubbo/go-server/user.go new file mode 100644 index 0000000000000000000000000000000000000000..7bff41566152940f885dec2eb256b261d04ad59a --- /dev/null +++ b/test/integrate/dubbo/go-server/user.go @@ -0,0 +1,65 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +import ( + "context" + "fmt" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" +) + +func init() { + config.SetProviderService(new(UserProvider)) + // ------for hessian2------ + hessian.RegisterPOJO(&User{}) +} + +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) { + println("req:%#v", req) + rsp := User{"A001", "Alex Stocks", 18, time.Now()} + println("rsp:%#v", rsp) + close(stopC) + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} + +func println(format string, args ...interface{}) { + fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...) +} diff --git a/test/integrate/dubbo/go-server/version.go b/test/integrate/dubbo/go-server/version.go new file mode 100644 index 0000000000000000000000000000000000000000..c6138584f1ddeab3a4927774f44f9e78a8f08da7 --- /dev/null +++ b/test/integrate/dubbo/go-server/version.go @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 main + +var ( + Version = "2.6.0" +)