diff --git a/README.md b/README.md index 9bade617c8b05ec52c2018cf231ae036a7ae91d3..3f8394536f944518f8d969289147272c32f169da 100644 --- a/README.md +++ b/README.md @@ -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 180759f36663a587ee02232e229ae7c3ebbb06c1..582c5cf04cba08d4167c87b40fd0e86a3aa2ceb0 100644 --- a/README_CN.md +++ b/README_CN.md @@ -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 61d1c934522008e4d9bc46bbd57eb6fed6bf00f9..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,7 +41,8 @@ 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(invoker *mock.MockInvoker) protocol.Invoker { diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go index cabd6c5f17cd3a3310054c0ff7b9a9877d581345..bbdfa715d7cdc461689e60a5a41171ad5c9770e1 100644 --- a/cluster/cluster_impl/base_cluster_invoker.go +++ b/cluster/cluster_impl/base_cluster_invoker.go @@ -88,6 +88,10 @@ 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 @@ -97,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..695ffcddbbce5a1c65f806b4561670d726588aaa 100644 --- a/cluster/cluster_impl/base_cluster_invoker_test.go +++ b/cluster/cluster_impl/base_cluster_invoker_test.go @@ -33,7 +33,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_StickyNormal(t *testing.T) { +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)) @@ -43,12 +43,15 @@ func Test_StickyNormal(t *testing.T) { 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("getUser", 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)) 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_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 ee7d48f3497772db3143b1ae62a30f66f99faa58..e05b79202cd202334db1c19421e3163ee28bac26 100644 --- a/cluster/cluster_impl/failover_cluster_test.go +++ b/cluster/cluster_impl/failover_cluster_test.go @@ -101,7 +101,7 @@ 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() @@ -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_test.go b/cluster/directory/base_directory_test.go index 6dc55b39407c9e88d18a65b5ec02fa866571624b..8b60163b79b7120829e51f69238474a127133fb4 100644 --- a/cluster/directory/base_directory_test.go +++ b/cluster/directory/base_directory_test.go @@ -19,6 +19,7 @@ package directory import ( "encoding/base64" + "fmt" "testing" ) @@ -33,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("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("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") directory := NewBaseDirectory(&url) assert.NotNil(t, directory) @@ -62,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_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 492434431f7382545cb747fa3611c6b18279fa4d..84fbb268c7a8ec32f007a734e2d6da56ef3c6d25 100644 --- a/cluster/loadbalance/consistent_hash.go +++ b/cluster/loadbalance/consistent_hash.go @@ -60,6 +60,8 @@ type ConsistentHashLoadBalance struct { } // NewConsistentHashLoadBalance creates NewConsistentHashLoadBalance +// +// The same parameters of the request is always sent to the same provider. func NewConsistentHashLoadBalance() cluster.LoadBalance { return &ConsistentHashLoadBalance{} } 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..88392de52c93579dd4def3da2d60b415b601b21e 100644 --- a/cluster/loadbalance/random_test.go +++ b/cluster/loadbalance/random_test.go @@ -36,7 +36,7 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -func Test_RandomlbSelect(t *testing.T) { +func TestRandomlbSelect(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} @@ -53,7 +53,7 @@ func Test_RandomlbSelect(t *testing.T) { randomlb.Select(invokers, &invocation.RPCInvocation{}) } -func Test_RandomlbSelectWeight(t *testing.T) { +func TestRandomlbSelectWeight(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} @@ -84,7 +84,7 @@ func Test_RandomlbSelectWeight(t *testing.T) { }) } -func Test_RandomlbSelectWarmup(t *testing.T) { +func TestRandomlbSelectWarmup(t *testing.T) { randomlb := NewRandomLoadBalance() invokers := []protocol.Invoker{} 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_test.go b/cluster/router/chain/chain_test.go index 0cb47c4a185fe19b5f70ea4db2b80aab2f1aada5..c7a75f3d8608ecb7a95dcf33027e71b61d7f00f5 100644 --- a/cluster/router/chain/chain_test.go +++ b/cluster/router/chain/chain_test.go @@ -42,10 +42,16 @@ import ( "github.com/apache/dubbo-go/remoting/zookeeper" ) +const ( + path = "/dubbo/config/dubbo/test-condition.condition-router" + zkPrefix = "zookeeper://127.0.0.1:" + anyUrl = "condition://0.0.0.0/com.foo.BarService" +) + 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,12 +61,12 @@ 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)) + zkUrl, _ := common.NewURL(zkPrefix + strconv.Itoa(ts.Servers[0].Port)) configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) @@ -92,10 +98,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,12 +111,12 @@ 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)) + zkUrl, _ := common.NewURL(zkPrefix + strconv.Itoa(ts.Servers[0].Port)) configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) @@ -131,12 +137,12 @@ conditions: 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)) + zkUrl, _ := common.NewURL(zkPrefix + strconv.Itoa(ts.Servers[0].Port)) configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) @@ -158,10 +164,10 @@ func TestRouterChain_Route(t *testing.T) { 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,12 +177,12 @@ 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)) + zkUrl, _ := common.NewURL(zkPrefix + strconv.Itoa(ts.Servers[0].Port)) configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) @@ -200,7 +206,7 @@ func TestRouterChain_Route_NoRoute(t *testing.T) { defer ts.Stop() defer z.Close() - zkUrl, _ := common.NewURL("zookeeper://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port)) + zkUrl, _ := common.NewURL(zkPrefix + strconv.Itoa(ts.Servers[0].Port)) configuration, err := extension.GetConfigCenterFactory("zookeeper").GetDynamicConfiguration(&zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) @@ -223,7 +229,7 @@ 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, _ := common.NewURL(anyUrl) url.AddParam("application", applicationKey) url.AddParam("force", "true") rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host != 1.2.3.4")) @@ -232,7 +238,7 @@ func getConditionNoRouteUrl(applicationKey string) *common.URL { } func getConditionRouteUrl(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(anyUrl) url.AddParam("application", applicationKey) url.AddParam("force", "true") rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host = 1.2.3.4")) @@ -241,7 +247,7 @@ func getConditionRouteUrl(applicationKey string) *common.URL { } func getRouteUrl(applicationKey string) *common.URL { - url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL(anyUrl) url.AddParam("application", applicationKey) url.AddParam("force", "true") return &url diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go index e99307625baf34fa6b744f168ff4e6cb8e042502..f37a483e8468bc57d3ce1e73172ccf9a05bc29f0 100644 --- a/cluster/router/condition/app_router_test.go +++ b/cluster/router/condition/app_router_test.go @@ -37,6 +37,10 @@ import ( "github.com/apache/dubbo-go/remoting/zookeeper" ) +const ( + path = "/dubbo/config/dubbo/test-condition.condition-router" +) + func TestNewAppRouter(t *testing.T) { testYML := `enabled: true @@ -47,10 +51,10 @@ 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(path) assert.NoError(t, err) - _, 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() @@ -93,10 +97,10 @@ 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(path) assert.NoError(t, err) - _, 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() @@ -130,10 +134,10 @@ 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(path) assert.NoError(t, err) - _, 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() diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go index 99cec34096a55d3c2a967b63afdf5f6d0a77279a..a826cafb85ee1a30ac568db34e10dd2c9c9e87d0 100644 --- a/cluster/router/condition/factory_test.go +++ b/cluster/router/condition/factory_test.go @@ -38,6 +38,8 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) +const anyUrl = "condition://0.0.0.0/com.foo.BarService" + type MockInvoker struct { url common.URL available bool @@ -59,21 +61,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(anyUrl) 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(anyUrl) 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(anyUrl) url.AddParam("rule", rule) return &url } @@ -116,7 +118,7 @@ func (bi *MockInvoker) Destroy() { bi.available = false } -func TestRoute_matchWhen(t *testing.T) { +func TestRouteMatchWhen(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4")) router, _ := newConditionRouterFactory().NewRouter(getRouteUrl(rule)) @@ -149,7 +151,7 @@ func TestRoute_matchWhen(t *testing.T) { assert.Equal(t, true, matchWhen6) } -func TestRoute_matchFilter(t *testing.T) { +func TestRouteMatchFilter(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") @@ -184,7 +186,7 @@ func TestRoute_matchFilter(t *testing.T) { } -func TestRoute_methodRoute(t *testing.T) { +func TestRouteMethodRoute(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)) @@ -207,7 +209,7 @@ func TestRoute_methodRoute(t *testing.T) { } -func TestRoute_ReturnFalse(t *testing.T) { +func TestRouteReturnFalse(t *testing.T) { url, _ := common.NewURL("") localIP, _ := gxnet.GetLocalIP() invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} @@ -219,7 +221,7 @@ func TestRoute_ReturnFalse(t *testing.T) { assert.Equal(t, 0, len(fileredInvokers)) } -func TestRoute_ReturnEmpty(t *testing.T) { +func TestRouteReturnEmpty(t *testing.T) { localIP, _ := gxnet.GetLocalIP() url, _ := common.NewURL("") invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} @@ -231,7 +233,7 @@ func TestRoute_ReturnEmpty(t *testing.T) { assert.Equal(t, 0, len(fileredInvokers)) } -func TestRoute_ReturnAll(t *testing.T) { +func TestRouteReturnAll(t *testing.T) { localIP, _ := gxnet.GetLocalIP() urlString := "dubbo://" + localIP + "/com.foo.BarService" dubboURL, _ := common.NewURL(urlString) @@ -247,7 +249,7 @@ func TestRoute_ReturnAll(t *testing.T) { assert.Equal(t, invokers, fileredInvokers) } -func TestRoute_HostFilter(t *testing.T) { +func TestRouteHostFilter(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)) @@ -266,7 +268,7 @@ func TestRoute_HostFilter(t *testing.T) { assert.Equal(t, invoker3, fileredInvokers[1]) } -func TestRoute_Empty_HostFilter(t *testing.T) { +func TestRouteEmptyHostFilter(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)) @@ -285,7 +287,7 @@ func TestRoute_Empty_HostFilter(t *testing.T) { assert.Equal(t, invoker3, fileredInvokers[1]) } -func TestRoute_False_HostFilter(t *testing.T) { +func TestRouteFalseHostFilter(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)) @@ -304,7 +306,7 @@ func TestRoute_False_HostFilter(t *testing.T) { assert.Equal(t, invoker3, fileredInvokers[1]) } -func TestRoute_Placeholder(t *testing.T) { +func TestRoutePlaceholder(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)) @@ -323,7 +325,7 @@ func TestRoute_Placeholder(t *testing.T) { assert.Equal(t, invoker3, fileredInvokers[1]) } -func TestRoute_NoForce(t *testing.T) { +func TestRouteNoForce(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)) @@ -340,7 +342,7 @@ func TestRoute_NoForce(t *testing.T) { assert.Equal(t, invokers, fileredInvokers) } -func TestRoute_Force(t *testing.T) { +func TestRouteForce(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)) diff --git a/cluster/router/healthcheck/default_health_check_test.go b/cluster/router/healthcheck/default_health_check_test.go index 74aa3940743a012f907cfe3d8811a618f07ff800..8a95d9a7e8dffdc3f30f94c76274a729837fc133 100644 --- a/cluster/router/healthcheck/default_health_check_test.go +++ b/cluster/router/healthcheck/default_health_check_test.go @@ -37,7 +37,7 @@ func TestDefaultHealthChecker_IsHealthy(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") hc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) - invoker := NewMockInvoker(url, 1) + invoker := NewMockInvoker(url) healthy := hc.IsHealthy(invoker) assert.True(t, healthy) 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_test.go b/cluster/router/healthcheck/health_check_route_test.go index 759ef93dbeb8d91a82eefd59060afbe8a10a4440..7bfffea705bfedade9d1d13ac7e9c380651335dd 100644 --- a/cluster/router/healthcheck/health_check_route_test.go +++ b/cluster/router/healthcheck/health_check_route_test.go @@ -44,9 +44,9 @@ func TestHealthCheckRouter_Route(t *testing.T) { 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) res := hcr.Route(invokers, &consumerURL, inv) diff --git a/common/constant/default.go b/common/constant/default.go index 45fc38f13e05a45e650cffcfe50177d71c8f797e..c1c404e089ea90899d2b599b01cd5980c3e92ab1 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 18d7f3de3d5f878583312cfcaeef24e31a7a58ef..a61073de78e1c6ed5cedeb034be8ce2d3b1fb96e 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -127,6 +127,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" ) @@ -163,6 +164,10 @@ const ( ZOOKEEPER_KEY = "zookeeper" ) +const ( + ETCDV3_KEY = "etcdv3" +) + const ( TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx" ) @@ -281,6 +286,4 @@ const ( // SERVICE_DISCOVERY_KEY indicate which service discovery instance will be used SERVICE_DISCOVERY_KEY = "service_discovery" - LANGUAGE_KEY = "language" - GO_LANG = "golang" ) diff --git a/common/extension/auth.go b/common/extension/auth.go index 7caae00e84fd80666ff79b599e12f8516e23209c..4fca0a8e8c255456720df9e4fd9852295715b160 100644 --- a/common/extension/auth.go +++ b/common/extension/auth.go @@ -32,7 +32,7 @@ func SetAuthenticator(name string, fcn func() filter.Authenticator) { } // GetAuthenticator finds the Authenticator with @name -// if not found, it will panic +// 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.") @@ -46,7 +46,7 @@ func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) { } // GetAccesskeyStorages finds the storage with the @name. -// If not found, it will panic. +// 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/config_center.go b/common/extension/config_center.go index 3cbced8d3bbcdb3dc7f9af800fa36681d6dc063d..5a2c52f32d070f5ec03bdae0b3cd47f869c28171 100644 --- a/common/extension/config_center.go +++ b/common/extension/config_center.go @@ -27,7 +27,7 @@ var ( ) // SetConfigCenter sets the DynamicConfiguration with @name -func SetConfigCenter(name string, v func(config *common.URL) (config_center.DynamicConfiguration, error)) { +func SetConfigCenter(name string, v func(*common.URL) (config_center.DynamicConfiguration, error)) { configCenters[name] = v } diff --git a/common/extension/event_dispatcher.go b/common/extension/event_dispatcher.go index a543910cc1f212a85bc3c5751f59c81fc991cfa2..f0503e05422844e129a81212beed6af414612b6b 100644 --- a/common/extension/event_dispatcher.go +++ b/common/extension/event_dispatcher.go @@ -19,7 +19,9 @@ package extension import ( "sync" +) +import ( "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/common/observer" ) @@ -34,21 +36,26 @@ var ( dispatchers = make(map[string]func() observer.EventDispatcher, 8) ) -// SetEventDispatcher by name +// SetEventDispatcher, actually, it doesn't really init the global dispatcher func SetEventDispatcher(name string, v func() observer.EventDispatcher) { dispatchers[name] = v } -// SetAndInitGlobalDispatcher +// SetAndInitGlobalDispatcher will actually init the global dispatcher +// if there is already a global dispatcher, +// it will be override +// if the dispatcher with the name not found, it will panic func SetAndInitGlobalDispatcher(name string) { if len(name) == 0 { name = "direct" } if globalEventDispatcher != nil { - logger.Warnf("EventDispatcher already init. It will be replaced") + logger.Warnf("EventDispatcher has been initialized. It will be replaced") } + if dp, ok := dispatchers[name]; !ok || dp == nil { - panic("EventDispatcher for " + name + " is not existing, make sure you have import the package.") + panic("EventDispatcher for " + name + " is not found, make sure you have import the package, " + + "like import _ github.com/apache/dubbo-go/common/observer/dispatcher ") } globalEventDispatcher = dispatchers[name]() } diff --git a/common/extension/event_dispatcher_test.go b/common/extension/event_dispatcher_test.go new file mode 100644 index 0000000000000000000000000000000000000000..472360cea5a04c2cd70f0df6ea4db23f6be88f1a --- /dev/null +++ b/common/extension/event_dispatcher_test.go @@ -0,0 +1,111 @@ +/* + * 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 extension + +import ( + "reflect" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) +import ( + "github.com/apache/dubbo-go/common/observer" +) + +func TestSetAndInitGlobalDispatcher(t *testing.T) { + mock := &mockEventDispatcher{} + SetEventDispatcher("mock", func() observer.EventDispatcher { + return mock + }) + + SetAndInitGlobalDispatcher("mock") + dispatcher := GetGlobalDispatcher() + assert.NotNil(t, dispatcher) + assert.Equal(t, mock, dispatcher) + + mock1 := &mockEventDispatcher{} + + SetEventDispatcher("mock1", func() observer.EventDispatcher { + return mock1 + }) + + SetAndInitGlobalDispatcher("mock1") + dispatcher = GetGlobalDispatcher() + assert.NotNil(t, dispatcher) + assert.Equal(t, mock1, dispatcher) +} + +func TestAddEventListener(t *testing.T) { + AddEventListener(func() observer.EventListener { + return &mockEventListener{} + }) + + AddEventListener(func() observer.EventListener { + return &mockEventListener{} + }) + + assert.Equal(t, 2, len(initEventListeners)) +} + +type mockEventListener struct { +} + +func (m mockEventListener) GetPriority() int { + panic("implement me") +} + +func (m mockEventListener) OnEvent(e observer.Event) error { + panic("implement me") +} + +func (m mockEventListener) GetEventType() reflect.Type { + panic("implement me") +} + +type mockEventDispatcher struct { +} + +func (m mockEventDispatcher) AddEventListener(listener observer.EventListener) { + panic("implement me") +} + +func (m mockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) { + panic("implement me") +} + +func (m mockEventDispatcher) RemoveEventListener(listener observer.EventListener) { + panic("implement me") +} + +func (m mockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) { + panic("implement me") +} + +func (m mockEventDispatcher) GetAllEventListeners() []observer.EventListener { + panic("implement me") +} + +func (m mockEventDispatcher) RemoveAllEventListeners() { + panic("implement me") +} + +func (m mockEventDispatcher) Dispatch(event observer.Event) { + panic("implement me") +} diff --git a/common/extension/metadata_service.go b/common/extension/metadata_service.go index 93ff40aa763100ece3b557598e24eeb8f81454a9..1823273b8f7f86b2d96abf990359e14b569abddb 100644 --- a/common/extension/metadata_service.go +++ b/common/extension/metadata_service.go @@ -19,7 +19,9 @@ package extension import ( "fmt" +) +import ( "github.com/apache/dubbo-go/metadata/service" ) @@ -28,10 +30,13 @@ var ( metadataServiceInsMap = make(map[string]func() (service.MetadataService, error), 2) ) +// SetMetadataService will store the msType => creator pair func SetMetadataService(msType string, creator func() (service.MetadataService, error)) { metadataServiceInsMap[msType] = creator } +// GetMetadataService will create a MetadataService instance +// it will panic if msType not found func GetMetadataService(msType string) (service.MetadataService, error) { if creator, ok := metadataServiceInsMap[msType]; ok { return creator() diff --git a/common/extension/metadata_service_proxy_factory.go b/common/extension/metadata_service_proxy_factory.go index 35f63c5fa9a15916739f32e7c1810c04e7ed4b2c..e8c9e73d7362a83843f3cb6c52c0f3bd15ab1cc8 100644 --- a/common/extension/metadata_service_proxy_factory.go +++ b/common/extension/metadata_service_proxy_factory.go @@ -19,7 +19,9 @@ package extension import ( "fmt" +) +import ( "github.com/apache/dubbo-go/metadata/service" ) @@ -27,10 +29,13 @@ var ( metadataServiceProxyFactoryMap = make(map[string]func() service.MetadataServiceProxyFactory) ) +// SetMetadataServiceProxyFactory store the name-creator pair func SetMetadataServiceProxyFactory(name string, creator func() service.MetadataServiceProxyFactory) { metadataServiceProxyFactoryMap[name] = creator } +// GetMetadataServiceProxyFactory will create an instance. +// it will panic if the factory with name not found func GetMetadataServiceProxyFactory(name string) service.MetadataServiceProxyFactory { if f, ok := metadataServiceProxyFactoryMap[name]; ok { return f() diff --git a/common/extension/registry.go b/common/extension/registry.go index 291a7a7fc2cae07c9228043acae7cc0ed5459a1f..542a2206c0bdda658c4ba363e939bbc569b2b49e 100644 --- a/common/extension/registry.go +++ b/common/extension/registry.go @@ -34,7 +34,7 @@ func SetRegistry(name string, v func(config *common.URL) (registry.Registry, err // 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/service_discovery.go b/common/extension/service_discovery.go index 456b14c83dcab4189ea287f2e9f3329109e538c3..0227920dc64a7b5f6ad1939fcccbb7384c43f68d 100644 --- a/common/extension/service_discovery.go +++ b/common/extension/service_discovery.go @@ -42,7 +42,7 @@ func SetServiceDiscovery(protocol string, creator func(name string) (registry.Se func GetServiceDiscovery(protocol string, name string) (registry.ServiceDiscovery, error) { creator, ok := discoveryCreatorMap[protocol] if !ok { - return nil, perrors.New("Could not find the service discovery with name: " + name) + return nil, perrors.New("Could not find the service discovery with discovery protocol: " + protocol) } return creator(name) } diff --git a/common/extension/service_instance_customizer.go b/common/extension/service_instance_customizer.go index a0e443aff5eb4e0ce403a0568c110a79e888d76a..3ebb3e40f5851ad4799e7cee5966edd77cea2f9e 100644 --- a/common/extension/service_instance_customizer.go +++ b/common/extension/service_instance_customizer.go @@ -19,7 +19,9 @@ package extension import ( "sort" +) +import ( "github.com/apache/dubbo-go/registry" ) @@ -35,15 +37,20 @@ func AddCustomizers(cus registry.ServiceInstanceCustomizer) { } // GetCustomizers will return the sorted customizer +// the result won't be nil func GetCustomizers() []registry.ServiceInstanceCustomizer { return customizers } type customizerSlice []registry.ServiceInstanceCustomizer +// nolint func (c customizerSlice) Len() int { return len(c) } -func (c customizerSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] } +// nolint +func (c customizerSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] } + +// nolint func (c customizerSlice) Less(i, j int) bool { return c[i].GetPriority() < c[j].GetPriority() } diff --git a/common/extension/service_instance_selector_factory.go b/common/extension/service_instance_selector_factory.go index 3ba3db46e65ed01a5417075d998209ae39df38ec..7776a408d8ea3849907a4b12f5464aaa0e1e96aa 100644 --- a/common/extension/service_instance_selector_factory.go +++ b/common/extension/service_instance_selector_factory.go @@ -29,10 +29,13 @@ var ( serviceInstanceSelectorMappings = make(map[string]func() instance.ServiceInstanceSelector) ) +// nolint func SetServiceInstanceSelector(name string, f func() instance.ServiceInstanceSelector) { serviceInstanceSelectorMappings[name] = f } +// GetServiceInstanceSelector will create an instance +// it will panic if selector with the @name not found func GetServiceInstanceSelector(name string) (instance.ServiceInstanceSelector, error) { serviceInstanceSelector, ok := serviceInstanceSelectorMappings[name] if !ok { diff --git a/common/extension/service_name_mapping.go b/common/extension/service_name_mapping.go index cd2630198ec142c6d168453c939c004bde940998..9e5aac52f90fd3fb68a342a91c7562c60e9c808c 100644 --- a/common/extension/service_name_mapping.go +++ b/common/extension/service_name_mapping.go @@ -17,7 +17,9 @@ package extension -import "github.com/apache/dubbo-go/metadata/mapping" +import ( + "github.com/apache/dubbo-go/metadata/mapping" +) var ( globalNameMappingCreator func() mapping.ServiceNameMapping diff --git a/common/observer/dispatcher/direct_event_dispatcher.go b/common/observer/dispatcher/direct_event_dispatcher.go index 2b7567b47ed40caa8867901ff0a05e0a2497cd31..a2d334ce2ca9461a8e14e28bb0f3ecf21971751f 100644 --- a/common/observer/dispatcher/direct_event_dispatcher.go +++ b/common/observer/dispatcher/direct_event_dispatcher.go @@ -36,26 +36,31 @@ func init() { // Align with 2.7.5 // Dispatcher event to listener direct type DirectEventDispatcher struct { - observer.BaseListenable + observer.BaseListener } // NewDirectEventDispatcher ac constructor of DirectEventDispatcher func NewDirectEventDispatcher() observer.EventDispatcher { - return &DirectEventDispatcher{} + return &DirectEventDispatcher{ + BaseListener: observer.NewBaseListener(), + } } // Dispatch event directly +// it lookup the listener by event's type. +// if listener not found, it just return and do nothing func (ded *DirectEventDispatcher) Dispatch(event observer.Event) { if event == nil { logger.Warnf("[DirectEventDispatcher] dispatch event nil") return } eventType := reflect.TypeOf(event).Elem() - value, loaded := ded.ListenersCache.Load(eventType) + ded.Mutex.RLock() + defer ded.Mutex.RUnlock() + listenersSlice, loaded := ded.ListenersCache[eventType] if !loaded { return } - listenersSlice := value.([]observer.EventListener) for _, listener := range listenersSlice { if err := listener.OnEvent(event); err != nil { logger.Warnf("[DirectEventDispatcher] dispatch event error:%v", err) diff --git a/common/observer/dispatcher/direct_event_dispatcher_test.go b/common/observer/dispatcher/direct_event_dispatcher_test.go index 355c930a9e86dbe6e82adc2795f8590c15a473c2..12facbb9c4bd1b33fa071a50beae87b77e1be968 100644 --- a/common/observer/dispatcher/direct_event_dispatcher_test.go +++ b/common/observer/dispatcher/direct_event_dispatcher_test.go @@ -29,7 +29,9 @@ import ( func TestDirectEventDispatcher_Dispatch(t *testing.T) { ded := NewDirectEventDispatcher() - ded.AddEventListener(&TestEventListener{}) + ded.AddEventListener(&TestEventListener{ + BaseListener: observer.NewBaseListener(), + }) ded.AddEventListener(&TestEventListener1{}) ded.Dispatch(&TestEvent{}) ded.Dispatch(nil) @@ -40,7 +42,7 @@ type TestEvent struct { } type TestEventListener struct { - observer.BaseListenable + observer.BaseListener observer.EventListener } diff --git a/common/observer/event.go b/common/observer/event.go index d78179043e8a2059e1d5fd15878fe32d7596321e..209a50c78a36c13d789f6f9fbc81a73a5d9a535f 100644 --- a/common/observer/event.go +++ b/common/observer/event.go @@ -58,6 +58,8 @@ func (b *BaseEvent) String() string { return fmt.Sprintf("BaseEvent[source = %#v]", b.Source) } +// NewBaseEvent create an BaseEvent instance +// and the Timestamp will be current timestamp func NewBaseEvent(source interface{}) *BaseEvent { return &BaseEvent{ Source: source, diff --git a/common/observer/listenable.go b/common/observer/listenable.go index 7b64aa8f2d263793c7aa4dc5e294466c48cd7b36..887f7a377d31fbb29f5b696f295c3dde07fb8daf 100644 --- a/common/observer/listenable.go +++ b/common/observer/listenable.go @@ -33,28 +33,32 @@ type Listenable interface { RemoveAllEventListeners() } -// BaseListenable base listenable -type BaseListenable struct { +// BaseListener base listenable +type BaseListener struct { Listenable - ListenersCache sync.Map - Mutex sync.Mutex + ListenersCache map[reflect.Type][]EventListener + Mutex sync.RWMutex } -// NewBaseListenable a constructor of base listenable -func NewBaseListenable() Listenable { - return &BaseListenable{} +// NewBaseListener a constructor of base listenable +func NewBaseListener() BaseListener { + return BaseListener{ + ListenersCache: make(map[reflect.Type][]EventListener, 8), + } } // AddEventListener add event listener -func (bl *BaseListenable) AddEventListener(listener EventListener) { +func (bl *BaseListener) AddEventListener(listener EventListener) { eventType := listener.GetEventType() if eventType.Kind() == reflect.Ptr { eventType = eventType.Elem() } bl.Mutex.Lock() defer bl.Mutex.Unlock() - value, loaded := bl.ListenersCache.LoadOrStore(eventType, make([]EventListener, 0, 8)) - listenersSlice := value.([]EventListener) + listenersSlice, loaded := bl.ListenersCache[eventType] + if !loaded { + listenersSlice = make([]EventListener, 0, 8) + } // return if listenersSlice already has this listener if loaded && containListener(listenersSlice, listener) { return @@ -63,59 +67,62 @@ func (bl *BaseListenable) AddEventListener(listener EventListener) { sort.Slice(listenersSlice, func(i, j int) bool { return listenersSlice[i].GetPriority() < listenersSlice[j].GetPriority() }) - bl.ListenersCache.Store(eventType, listenersSlice) + bl.ListenersCache[eventType] = listenersSlice } // AddEventListeners add the slice of event listener -func (bl *BaseListenable) AddEventListeners(listenersSlice []EventListener) { +func (bl *BaseListener) AddEventListeners(listenersSlice []EventListener) { for _, listener := range listenersSlice { bl.AddEventListener(listener) } } // RemoveEventListener remove the event listener -func (bl *BaseListenable) RemoveEventListener(listener EventListener) { +func (bl *BaseListener) RemoveEventListener(listener EventListener) { eventType := listener.GetEventType() if eventType.Kind() == reflect.Ptr { eventType = eventType.Elem() } bl.Mutex.Lock() defer bl.Mutex.Unlock() - value, loaded := bl.ListenersCache.Load(eventType) + listenersSlice, loaded := bl.ListenersCache[eventType] if !loaded { return } - listenersSlice := value.([]EventListener) for i, l := range listenersSlice { if l == listener { listenersSlice = append(listenersSlice[:i], listenersSlice[i+1:]...) } } - bl.ListenersCache.Store(eventType, listenersSlice) + bl.ListenersCache[eventType] = listenersSlice } // RemoveEventListeners remove the slice of event listener -func (bl *BaseListenable) RemoveEventListeners(listenersSlice []EventListener) { +// it will iterate all listener and remove it one by one +func (bl *BaseListener) RemoveEventListeners(listenersSlice []EventListener) { for _, listener := range listenersSlice { bl.RemoveEventListener(listener) } } // RemoveAllEventListeners remove all -func (bl *BaseListenable) RemoveAllEventListeners() { +// using Lock +func (bl *BaseListener) RemoveAllEventListeners() { bl.Mutex.Lock() defer bl.Mutex.Unlock() - bl.ListenersCache = sync.Map{} + bl.ListenersCache = make(map[reflect.Type][]EventListener) } -// GetAllEventListeners get all -func (bl *BaseListenable) GetAllEventListeners() []EventListener { +// GetAllEventListeners get all listener +// using RLock +func (bl *BaseListener) GetAllEventListeners() []EventListener { allListenersSlice := make([]EventListener, 0, 16) - bl.ListenersCache.Range(func(_, value interface{}) bool { - listenersSlice := value.([]EventListener) + + bl.Mutex.RLock() + defer bl.Mutex.RUnlock() + for _, listenersSlice := range bl.ListenersCache { allListenersSlice = append(allListenersSlice, listenersSlice...) - return true - }) + } sort.Slice(allListenersSlice, func(i, j int) bool { return allListenersSlice[i].GetPriority() < allListenersSlice[j].GetPriority() }) @@ -123,6 +130,8 @@ func (bl *BaseListenable) GetAllEventListeners() []EventListener { } // containListener true if contain listener +// it's not thread safe +// usually it should be use in lock scope func containListener(listenersSlice []EventListener, listener EventListener) bool { for _, loadListener := range listenersSlice { if loadListener == listener { diff --git a/common/observer/listenable_test.go b/common/observer/listenable_test.go index df46bfc2ba47f6e447074b44208a809949f7ae3d..5a03382a937fe925b6e17b495d066b86c8d2161d 100644 --- a/common/observer/listenable_test.go +++ b/common/observer/listenable_test.go @@ -28,7 +28,8 @@ import ( func TestListenable(t *testing.T) { el := &TestEventListener{} - b := &BaseListenable{} + bl := NewBaseListener() + b := &bl b.AddEventListener(el) b.AddEventListener(el) al := b.GetAllEventListeners() diff --git a/common/rpc_service.go b/common/rpc_service.go index 1127e6c018204ee287ba3edfb50b30688a052dd5..6ca0e827cb21a4065468c3f180cecdaab0afb555 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -343,6 +343,12 @@ func suiteMethod(method reflect.Method) *MethodType { argsType []reflect.Type ) + // this method is in RPCService + // we force users must implement RPCService interface in their provider + // and RPCService has only one method "Reference" + // In general, this method should not be exported to client + // so we ignore this method + // see RPCService if mname == "Reference" { return nil } diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go index 19a1d7b03b48d16d9d75b649d603d5d22ee7ba56..2311205d0ec0c2fd4642a4d8639c0bf871fe1d17 100644 --- a/common/rpc_service_test.go +++ b/common/rpc_service_test.go @@ -137,7 +137,7 @@ func TestSuiteMethod(t *testing.T) { assert.True(t, ok) methodType := suiteMethod(method) method = methodType.Method() - assert.Equal(t, "func(*event.TestService, context.Context, interface {}, interface {}, interface {}) error", method.Type.String()) + 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()) @@ -151,7 +151,7 @@ func TestSuiteMethod(t *testing.T) { assert.True(t, ok) methodType = suiteMethod(method) method = methodType.Method() - assert.Equal(t, "func(*event.TestService, interface {}, interface {}, interface {}) (interface {}, error)", method.Type.String()) + 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()) @@ -164,7 +164,7 @@ func TestSuiteMethod(t *testing.T) { assert.True(t, ok) methodType = suiteMethod(method) method = methodType.Method() - assert.Equal(t, "func(*event.TestService) error", method.Type.String()) + assert.Equal(t, "func(*common.TestService) error", method.Type.String()) at = methodType.ArgsType() assert.Equal(t, 0, len(at)) assert.Nil(t, methodType.CtxType()) diff --git a/common/url.go b/common/url.go index 10bbf8ff210d61101a5f64015533cc11c44bee85..e0e15739daa926f02280dc69e70ed38f673c1ae5 100644 --- a/common/url.go +++ b/common/url.go @@ -36,6 +36,7 @@ import ( import ( "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" ) // /////////////////////////////// @@ -177,7 +178,12 @@ func WithToken(token string) option { if len(token) > 0 { value := token if strings.ToLower(token) == "true" || strings.ToLower(token) == "default" { - value = uuid.NewV4().String() + u, err := uuid.NewV4() + if err != nil { + logger.Errorf("could not generator UUID: %v", err) + return + } + value = u.String() } url.SetParam(constant.TOKEN_KEY, value) } @@ -651,16 +657,21 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f return methodConfigMergeFcn } +// URLSlice will be used to sort URL instance +// Instances will be order by URL.String() type URLSlice []URL +// nolint func (s URLSlice) Len() int { return len(s) } +// nolint func (s URLSlice) Less(i, j int) bool { return s[i].String() < s[j].String() } +// nolint func (s URLSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } diff --git a/common/url_test.go b/common/url_test.go index 2372de520e88b0949023e88cec64871736dd6aa0..4d9dff9f373f5d2250deb577621cead8c991cf4d 100644 --- a/common/url_test.go +++ b/common/url_test.go @@ -118,6 +118,33 @@ 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) { diff --git a/config/application_config.go b/config/application_config.go index af6637c4e08df0d7160f10881bd1fe588b95f607..ef99664fa298c28365ed7acc54d0c18a88c9b5c2 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"` @@ -37,22 +37,12 @@ type ApplicationConfig struct { MetadataType string `default:"local" yaml:"metadataType" json:"metadataType,omitempty" property:"metadataType"` } -// 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 f04a09f498133a84be4043aada7c5821dfe98b56..46ff5fb174b9204b00c7d7a0369615ca377fd2d4 100644 --- a/config/base_config.go +++ b/config/base_config.go @@ -40,14 +40,14 @@ type multiConfiger interface { Prefix() string } -// BaseConfig is the event configuration for provider and consumer +// BaseConfig is the common configuration for provider and consumer type BaseConfig struct { - ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` - Remotes map[string]*RemoteConfig `yaml:"remote" json:"remote,omitempty"` - ServiceDiscoveries map[string]*ServiceDiscoveryConfig `yaml:"service_discovery" json:"service_discovery,omitempty"` + ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` - Registry *RegistryConfig `yaml:"registry" json:"registry,omitempty" property:"registry"` - Registries map[string]*RegistryConfig `yaml:"registries" json:"registries,omitempty" property:"registries"` + // since 1.5.0 version + Remotes map[string]*RemoteConfig `yaml:"remote" json:"remote,omitempty"` + ServiceDiscoveries map[string]*ServiceDiscoveryConfig `yaml:"service_discovery" json:"service_discovery,omitempty"` + MetadataReportConfig *MetadataReportConfig `yaml:"metadata_report" json:"metadata_report,omitempty" property:"metadata_report"` // application config ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"` @@ -60,6 +60,7 @@ type BaseConfig struct { fileStream *bytes.Buffer } +// nolint func (c *BaseConfig) GetServiceDiscoveries(name string) (config *ServiceDiscoveryConfig, ok bool) { config, ok = c.ServiceDiscoveries[name] return @@ -88,7 +89,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) @@ -342,7 +342,7 @@ 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 } diff --git a/config/base_config_test.go b/config/base_config_test.go index f36d7190c379d7d190af3fd2b2314b3b4b10c3f0..ea53ca5208cd0fdee4ab2fb22f5c895a524381aa 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -56,6 +56,7 @@ func Test_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{ "shanghai_reg2": { Protocol: "mock", @@ -82,6 +83,7 @@ func Test_refresh(t *testing.T) { Password: "pwd1", }, }, + References: map[string]*ReferenceConfig{ "MockService": { InterfaceName: "com.MockService", @@ -152,6 +154,7 @@ func Test_appExternal_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{ "shanghai_reg2": { Protocol: "mock", @@ -240,6 +243,7 @@ func Test_appExternalWithoutId_refresh(t *testing.T) { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{ "shanghai_reg2": { Protocol: "mock", @@ -266,6 +270,7 @@ func Test_appExternalWithoutId_refresh(t *testing.T) { Password: "pwd1", }, }, + References: map[string]*ReferenceConfig{ "MockService": { InterfaceName: "com.MockService", @@ -319,6 +324,7 @@ func Test_refresh_singleRegistry(t *testing.T) { father := &ConsumerConfig{ Check: &[]bool{true}[0], BaseConfig: BaseConfig{ + ApplicationConfig: &ApplicationConfig{ Organization: "dubbo_org", Name: "dubbo", @@ -327,8 +333,10 @@ func Test_refresh_singleRegistry(t *testing.T) { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{}, Registry: &RegistryConfig{}, + References: map[string]*ReferenceConfig{ "MockService": { InterfaceName: "com.MockService", @@ -392,6 +400,7 @@ func Test_refreshProvider(t *testing.T) { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{ "shanghai_reg2": { Protocol: "mock", @@ -418,6 +427,7 @@ func Test_refreshProvider(t *testing.T) { Password: "pwd1", }, }, + Services: map[string]*ServiceConfig{ "MockService": { InterfaceName: "com.MockService", 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 90eb354cf53fe8009b73f67d35e0827e4b11ecbc..89a32771dc09c963ba2270eb9b42eba61c815036 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -42,10 +42,8 @@ var ( providerConfig *ProviderConfig // baseConfig = providerConfig.BaseConfig or consumerConfig baseConfig *BaseConfig - // baseConfigOnce is used to make sure that we only create it once. - baseConfigOnce sync.Once - // configAccessMutex is used to make sure that BaseConfig.xxxxConfig will only be created once if needed. + // configAccessMutex is used to make sure that xxxxConfig will only be created once if needed. // it should be used combine with double-check to avoid the race condition configAccessMutex sync.Mutex @@ -69,6 +67,8 @@ func init() { log.Printf("[consumerInit] %#v", errCon) consumerConfig = nil } else { + // Even though baseConfig has been initialized, we override it + // because we think read from config file is correct config baseConfig = &consumerConfig.BaseConfig } @@ -76,6 +76,8 @@ func init() { log.Printf("[providerInit] %#v", errPro) providerConfig = nil } else { + // Even though baseConfig has been initialized, we override it + // because we think read from config file is correct config baseConfig = &providerConfig.BaseConfig } } @@ -219,11 +221,11 @@ func Load() { // init router initRouter() - // event part + // init the global event dispatcher extension.SetAndInitGlobalDispatcher(GetBaseConfig().EventDispatcherType) // start the metadata report if config set - if err := startMetadataReport(GetApplicationConfig().MetadataType, GetProviderConfig().MetadataReportConfig); err != nil { + if err := startMetadataReport(GetApplicationConfig().MetadataType, GetBaseConfig().MetadataReportConfig); err != nil { logger.Errorf("Provider starts metadata report error, and the error is {%#v}", err) return } @@ -285,7 +287,6 @@ func GetApplicationConfig() *ApplicationConfig { // if not found, create new one func GetProviderConfig() ProviderConfig { if providerConfig == nil { - logger.Warnf("providerConfig is nil! we will try to create one") if providerConfig == nil { return ProviderConfig{} } @@ -308,9 +309,10 @@ func GetConsumerConfig() ConsumerConfig { } func GetBaseConfig() *BaseConfig { - if baseConfig == nil { - baseConfigOnce.Do(func() { + configAccessMutex.Lock() + defer configAccessMutex.Unlock() + if baseConfig == nil { baseConfig = &BaseConfig{ MetricConfig: &MetricConfig{}, ConfigCenterConfig: &ConfigCenterConfig{}, @@ -318,7 +320,7 @@ func GetBaseConfig() *BaseConfig { ApplicationConfig: &ApplicationConfig{}, ServiceDiscoveries: make(map[string]*ServiceDiscoveryConfig, 0), } - }) + } } return baseConfig } diff --git a/config/config_loader_test.go b/config/config_loader_test.go index f653f5e7598756514fd1e50dd2077329cbb3c9c0..a239621dd0e43fb940e6e94ea87df62b9123cd26 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -254,12 +254,14 @@ func mockInitProviderWithSingleRegistry() { Owner: "dubbo", Environment: "test"}, }, + Registry: &RegistryConfig{ Address: "mock://127.0.0.1:2181", Username: "user1", Password: "pwd1", }, Registries: map[string]*RegistryConfig{}, + Services: map[string]*ServiceConfig{ "MockService": { InterfaceName: "com.MockService", diff --git a/config/consumer_config.go b/config/consumer_config.go index fd3d30e6fca2d537da47cce79134e749060f511e..f8b671bf3cf8304d57211f2f8b7c7c35f2aa6b9e 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"` @@ -46,6 +46,9 @@ type ConsumerConfig struct { Connect_Timeout string `default:"100ms" yaml:"connect_timeout" json:"connect_timeout,omitempty" property:"connect_timeout"` ConnectTimeout time.Duration + Registry *RegistryConfig `yaml:"registry" json:"registry,omitempty" property:"registry"` + Registries map[string]*RegistryConfig `yaml:"registries" json:"registries,omitempty" property:"registries"` + Request_Timeout string `yaml:"request_timeout" default:"5s" json:"request_timeout,omitempty" property:"request_timeout"` RequestTimeout time.Duration ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` @@ -58,7 +61,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 @@ -70,17 +73,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") 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_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 100% rename from config/instance/metedata_report.go rename to config/instance/metadata_report.go diff --git a/config/instance/metadata_report_test.go b/config/instance/metadata_report_test.go new file mode 100644 index 0000000000000000000000000000000000000000..51780a53f6d7e80e5c7d7a15917aee629b000e1c --- /dev/null +++ b/config/instance/metadata_report_test.go @@ -0,0 +1,85 @@ +/* + * 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 instance + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/metadata/identifier" + "github.com/apache/dubbo-go/metadata/report" + "github.com/apache/dubbo-go/metadata/report/factory" +) + +func TestGetMetadataReportInstance(t *testing.T) { + extension.SetMetadataReportFactory("mock", func() factory.MetadataReportFactory { + return &mockMetadataReportFactory{} + }) + u, _ := common.NewURL("mock://127.0.0.1") + rpt := GetMetadataReportInstance(&u) + assert.NotNil(t, rpt) +} + +type mockMetadataReportFactory struct { +} + +func (m *mockMetadataReportFactory) CreateMetadataReport(*common.URL) report.MetadataReport { + return &mockMetadataReport{} +} + +type mockMetadataReport struct { +} + +func (m mockMetadataReport) StoreProviderMetadata(*identifier.MetadataIdentifier, string) error { + panic("implement me") +} + +func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier, string) error { + panic("implement me") +} + +func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error { + panic("implement me") +} + +func (m mockMetadataReport) RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) error { + panic("implement me") +} + +func (m mockMetadataReport) GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string { + panic("implement me") +} + +func (m mockMetadataReport) SaveSubscribedData(*identifier.SubscriberMetadataIdentifier, []common.URL) error { + panic("implement me") +} + +func (m mockMetadataReport) GetSubscribedURLs(*identifier.SubscriberMetadataIdentifier) []string { + panic("implement me") +} + +func (m mockMetadataReport) GetServiceDefinition(*identifier.MetadataIdentifier) string { + panic("implement me") +} 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 9999371ab887fbf7d3c5857aea02b1dd4faad2da..6d319e5ecb8007e06dcf790fff145bfab754df3d 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"` RemoteRef string `required:"true" yaml:"remote_ref" json:"remote_ref,omitempty"` @@ -40,12 +40,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) @@ -57,7 +57,7 @@ func (c *MetadataReportConfig) UnmarshalYAML(unmarshal func(interface{}) error) return nil } -// ToUrl ... +// nolint func (c *MetadataReportConfig) ToUrl() (*common.URL, error) { urlMap := make(url.Values) 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 99c532a383ad75285edc7e7200f6edbeac936f9d..9d8a2429d2fc1d88b01c436ae96d55bcd1c729aa 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -35,22 +35,23 @@ 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"` - ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` - // metadata-report - MetadataReportConfig *MetadataReportConfig `yaml:"metadata_report" json:"metadata_report,omitempty" property:"metadata_report"` - Services map[string]*ServiceConfig `yaml:"services" json:"services,omitempty" property:"services"` - Protocols map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"` - ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" ` - FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` - ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` - ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` + BaseConfig `yaml:",inline"` + Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` + ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` + Services map[string]*ServiceConfig `yaml:"services" json:"services,omitempty" property:"services"` + Protocols map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"` + ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" ` + FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` + ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` + ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` + + Registry *RegistryConfig `yaml:"registry" json:"registry,omitempty" property:"registry"` + Registries map[string]*RegistryConfig `yaml:"registries" json:"registries,omitempty" property:"registries"` } -// 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 @@ -62,17 +63,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 e935c7646d7ba9d7a75fed3bed68c88ed7ed104d..748b2d403fe315f879c817c4e0a4cf3197d807da 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 @@ -67,7 +67,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 + "." } @@ -77,7 +77,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 @@ -171,7 +171,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 faaa461a75a5c887f6fa1cc568d7e809c42a6ac4..05b386f75e2b1fc062783e1de224a49329fda1fb 100644 --- a/config/reference_config_test.go +++ b/config/reference_config_test.go @@ -47,6 +47,7 @@ func doInitConsumer() { Owner: "dubbo", Environment: "test"}, }, + Registries: map[string]*RegistryConfig{ "shanghai_reg1": { Protocol: "mock", @@ -81,6 +82,7 @@ func doInitConsumer() { Password: "pwd1", }, }, + References: map[string]*ReferenceConfig{ "MockService": { id: "MockProvider", @@ -146,12 +148,14 @@ func doInitConsumerWithSingleRegistry() { Owner: "dubbo", Environment: "test"}, }, + Registry: &RegistryConfig{ Address: "mock://27.0.0.1:2181", Username: "user1", Password: "pwd1", }, Registries: map[string]*RegistryConfig{}, + References: map[string]*ReferenceConfig{ "MockService": { Params: map[string]string{ diff --git a/config/registry_config.go b/config/registry_config.go index e877a2c19dd0c4dabdce9f7ee65c2404b82d615e..ef527c827e9dac4cd2762f579d30254e9e51150f 100644 --- a/config/registry_config.go +++ b/config/registry_config.go @@ -33,7 +33,7 @@ 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 @@ -47,7 +47,7 @@ type RegistryConfig struct { 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 @@ -59,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 } diff --git a/config/remote_config.go b/config/remote_config.go index 4d17cc1c41c8033d550be7b2f0159485eabcec62..5e0330c571715d99e63688ee944c61f8e48117bb 100644 --- a/config/remote_config.go +++ b/config/remote_config.go @@ -19,10 +19,16 @@ package config import ( "time" +) +import ( "github.com/apache/dubbo-go/common/logger" ) +// RemoteConfig: usually we need some middleware, including nacos, zookeeper +// this represents an instance of this middleware +// so that other module, like config center, registry could reuse the config +// but now, only metadata report, metadata service, service discovery use this structure type RemoteConfig struct { Address string `yaml:"address" json:"address,omitempty"` TimeoutStr string `default:"5s" yaml:"timeout" json:"timeout,omitempty"` @@ -31,6 +37,8 @@ type RemoteConfig struct { Params map[string]string `yaml:"params" json:"address,omitempty"` } +// Timeout return timeout duration. +// if the configure is invalid, or missing, the default value 5s will be returned func (rc *RemoteConfig) Timeout() time.Duration { if res, err := time.ParseDuration(rc.TimeoutStr); err == nil { return res diff --git a/config/remote_config_test.go b/config/remote_config_test.go index 99facb7dda98ba46ac7b5a6f86de070de8ca3d78..82535fd60b932aecb7c6c3ee8206130fad9e7161 100644 --- a/config/remote_config_test.go +++ b/config/remote_config_test.go @@ -19,7 +19,8 @@ package config import ( "testing" - +) +import ( "github.com/stretchr/testify/assert" ) 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 43c53bc9c2b596a9270c2e1e0c0ccfa71bb15ee4..57fce028fa59893979fc6f50671fe8681770a2b8 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -44,9 +44,7 @@ import ( "github.com/apache/dubbo-go/protocol/protocolwrapper" ) -// ServiceConfig is a newest structure to support Dubbo 2.7.5 -// But I think it's not very necessary, -// we should think about how to reuse current ProviderConfig rather than use this +// ServiceConfig is the configuration of the service provider type ServiceConfig struct { context context.Context id string @@ -86,12 +84,12 @@ type ServiceConfig struct { exporters []protocol.Exporter } -// Prefix ... +// Prefix returns dubbo.service.${interface}. 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 @@ -143,7 +141,7 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List { return ports } -// Export ... +// Export exports the service func (c *ServiceConfig) Export() error { // TODO: config center start here @@ -251,7 +249,7 @@ func (c *ServiceConfig) Unexport() { c.unexported.Store(true) } -// Implement ... +// Implement only store the @s and return func (c *ServiceConfig) Implement(s common.RPCService) { c.rpcService = s } diff --git a/config/service_config_test.go b/config/service_config_test.go index 949566f82a30e92c049dcf7b4063e26397df0a3c..7630d5845e6770ec269bb2e07876bc7ba0d18329 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -42,40 +42,6 @@ func doInitProvider() { Owner: "dubbo", Environment: "test"}, }, - Registries: map[string]*RegistryConfig{ - "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", - }, - }, Services: map[string]*ServiceConfig{ "MockService": { InterfaceName: "com.MockService", @@ -128,6 +94,42 @@ func doInitProvider() { exported: new(atomic.Bool), }, }, + + Registries: map[string]*RegistryConfig{ + "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", + }, + }, + Protocols: map[string]*ProtocolConfig{ "mock": { Name: "mock", diff --git a/config_center/configuration_listener.go b/config_center/configuration_listener.go index 541cc09286bb83fa5b66db3745e45ad0a9df5e2f..97fd9c70923f5c921ce2ca2b4028a71ea2b49e27 100644 --- a/config_center/configuration_listener.go +++ b/config_center/configuration_listener.go @@ -27,6 +27,7 @@ import ( // ConfigurationListener for changing listener's event type ConfigurationListener interface { + // Process the notification event once there's any change happens on the config Process(*ConfigChangeEvent) } 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/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..d5e351e2bbf7d78301926c8394d07183f18bb678 100644 --- a/config_center/nacos/client_test.go +++ b/config_center/nacos/client_test.go @@ -53,3 +53,62 @@ func Test_newNacosClient(t *testing.T) { <-c.client.Done() c.Destroy() } + +func Test_setNacosClient(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 * time.Second, + 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 Test_newNacosClient_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.go b/config_center/nacos/impl.go index b4a7a5467978404387c0c9667dc308a2e68d59ed..bbf707b93811663d0a259c6704e1008bfa91c5c1 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -38,9 +38,14 @@ import ( const ( nacosClientName = "nacos config_center" - maxKeysNum = 9999 + // the number is a little big tricky + // it will be used in query which looks up all keys with the target group + // now, one key represents one application + // so only a group has more than 9999 applications will failed + maxKeysNum = 9999 ) +// nacosDynamicConfiguration is the implementation of DynamicConfiguration based on nacos type nacosDynamicConfiguration struct { url *common.URL rootPath string @@ -125,7 +130,7 @@ func (n *nacosDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.H return result, perrors.WithMessage(err, "can not find the client config") } for _, itm := range page.PageItems { - result.Add(itm.Appname) + result.Add(itm.DataId) } return result, nil } diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go index b0a54b8d37fd060671651f4fffcbdb192527bcea..03fc6772e70d623575575c8522508ae4fff12a04 100644 --- a/config_center/nacos/impl_test.go +++ b/config_center/nacos/impl_test.go @@ -72,12 +72,13 @@ 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) { @@ -93,7 +94,7 @@ func TestNacosDynamicConfiguration_GetConfigKeysByGroup(t *testing.T) { { "PageItems": [ { - "Content": "application" + "dataId": "application" } ] } diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index de74cff8f64683a47278825b670352a04b69b791..fdf5a20d2ff4b97c1e0de40c8b5a0e573214fea4 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -46,7 +46,9 @@ func (n *nacosDynamicConfiguration) addListener(key string, listener config_cent go callback(listener, namespace, group, dataId, data) }, }) - logger.Errorf("nacos : listen config fail, error:%v ", err) + if err != nil { + logger.Errorf("nacos : listen config fail, error:%v ", err) + } newListener := make(map[config_center.ConfigurationListener]context.CancelFunc) newListener[listener] = cancel n.keyListeners.Store(key, newListener) diff --git a/config_center/parser/configuration_parser_test.go b/config_center/parser/configuration_parser_test.go index 7a59ea9b48b8b8d2a84735a416bcba1bb9ec8652..3ba10f73a4549181f37b89aedd4aedf4612bd7d4 100644 --- a/config_center/parser/configuration_parser_test.go +++ b/config_center/parser/configuration_parser_test.go @@ -32,3 +32,58 @@ func TestDefaultConfigurationParser_Parser(t *testing.T) { assert.Equal(t, 2, len(m)) assert.Equal(t, "172.0.0.1", m["dubbo.registry.address"]) } + +func TestDefaultConfigurationParser_appItemToUrls_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 TestDefaultConfigurationParser_serviceItemToUrls_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..cfeba07a87e2534dc60e9ca2235550c1136bd978 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -18,6 +18,7 @@ package zookeeper import ( "fmt" + "strconv" "sync" "testing" ) @@ -30,16 +31,28 @@ 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{}) - + 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,20 +76,20 @@ 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(zreg.rootPath + "/dubbo/dubbo.properties") assert.NoError(t, err) - _, err = reg.client.Conn.Set(reg.rootPath+"/dubbo/dubbo.properties", []byte(data), 0) + _, err = zreg.client.Conn.Set(zreg.rootPath+"/dubbo/dubbo.properties", []byte(data), 0) assert.NoError(t, err) } else { - err = reg.client.Create(reg.rootPath + "/dubbo.properties") + err = zreg.client.Create(zreg.rootPath + "/dubbo.properties") assert.NoError(t, err) - _, err = reg.client.Conn.Set(reg.rootPath+"/dubbo.properties", []byte(data), 0) + _, err = zreg.client.Conn.Set(zreg.rootPath+"/dubbo.properties", []byte(data), 0) assert.NoError(t, err) } - return ts, reg + return ts, zreg } func Test_GetConfig(t *testing.T) { @@ -87,6 +100,12 @@ func Test_GetConfig(t *testing.T) { m, err := reg.Parser().Parse(configs) assert.NoError(t, err) assert.Equal(t, "5s", m["dubbo.consumer.request_timeout"]) + configs, err = reg.GetProperties("dubbo.properties") + assert.Error(t, err) + configs, err = reg.GetInternalProperty("dubbo.properties") + assert.Error(t, err) + configs, err = reg.GetRule("dubbo.properties") + assert.Error(t, err) } func Test_AddListener(t *testing.T) { 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/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 4c872bed3e7fef8eca47f51422525a4918d6c1d8..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()) @@ -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/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 2d44c688ebd460f60a49da0f148fd7c6e2b79f50..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" 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 fe9cf4869f16e1d7c136e7f48e4138d046fcb057..52ac1765f78172c0062de8884198e759b8d494ca 100644 --- a/filter/handler/rejected_execution_handler_only_log.go +++ b/filter/handler/rejected_execution_handler_only_log.go @@ -44,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": diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go index 9855bd5354d0582bffaae177dfe8d8ddb95ed985..3d1e1c1e641a836411ce0f71f97acf5d5a55f6d1 100644 --- a/filter/rejected_execution_handler.go +++ b/filter/rejected_execution_handler.go @@ -22,11 +22,11 @@ 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 event case is that sometimes you want to return the default value when the request was rejected. + * The common case is that sometimes you want to return the default value when the request was rejected. * Or you want to be warned if any request was rejected. * In such situation, implement this interface and register it by invoking extension.SetRejectedExecutionHandler. */ diff --git a/filter/tps_limit_strategy.go b/filter/tps_limit_strategy.go index e194f1da06b0599464f0c66f6b7747fc78fbedce..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" @@ -37,7 +37,7 @@ type TpsLimitStrategy interface { IsAllowable() bool } -// TpsLimitStrategyCreator, the creator abstraction for TpsLimitStrategy +// TpsLimitStrategyCreator is the creator abstraction for TpsLimitStrategy type TpsLimitStrategyCreator interface { // Create will create an instance of TpsLimitStrategy // It will be a little hard to understand this method. diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go index 531eb098232cd34a467d71882b29858c07f88aef..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": diff --git a/go.mod b/go.mod index 0fc3599813b1cb96a5988119d27d4596f115e386..89d3f9ce2630907d15aa3637f5ab42b51110572c 100644 --- a/go.mod +++ b/go.mod @@ -26,24 +26,24 @@ require ( github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 github.com/hashicorp/consul v1.5.3 github.com/hashicorp/consul/api v1.1.0 + github.com/hashicorp/vault v0.10.3 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/magiconair/properties v1.8.1 github.com/mitchellh/mapstructure v1.1.2 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd - github.com/nacos-group/nacos-sdk-go v0.3.1 + github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f github.com/opentracing/opentracing-go v1.1.0 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/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect github.com/soheilhy/cmux v0.1.4 // indirect github.com/stretchr/testify v1.5.1 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // 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.4 // indirect go.uber.org/atomic v1.4.0 go.uber.org/zap v1.10.0 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect diff --git a/go.sum b/go.sum index a26694e75f08e37ed963831edbd7700f1f773393..93bca6b976b0acd208ad00a8dd26cab10e766ce8 100644 --- a/go.sum +++ b/go.sum @@ -388,8 +388,8 @@ 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.3.1 h1:MI7bNDAN5m9UFcRRUTSPfJi4dCQo+TYG85qVB1rCHeg= -github.com/nacos-group/nacos-sdk-go v0.3.1/go.mod h1:ESKb6yF0gxSc8GuS+0jaMBe+n8rJ5/k4ya6LyFG2xi8= +github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f h1:gid5/0AkHvINWK69Fgbidb3BVIXqlf1YEm7wO0NVPsw= +github.com/nacos-group/nacos-sdk-go v0.3.3-0.20200617023039-50c7537d6a5f/go.mod h1:fti1GlX/EB6RDKvzK/P7Vuibqj0JMPJHQwrcTU1tLXk= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= github.com/oklog/run v0.0.0-20180308005104-6934b124db28 h1:Hbr3fbVPXea52oPQeP7KLSxP52g6SFaNY1IqAmUyEW0= @@ -457,8 +457,8 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So 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= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= +github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880 h1:1Ge4j/3uB2rxzPWD3TC+daeCw+w91z8UCUL/7WH5gn8= diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go index fa195d09d7efe022be9bdf40658e355a44b8705e..dbbc0c8f1685edf1a26ab1fe4ad091c501e76f5f 100644 --- a/metadata/definition/definition.go +++ b/metadata/definition/definition.go @@ -42,10 +42,12 @@ type ServiceDefinition struct { Types []TypeDefinition } +// ToBytes convert ServiceDefinition to json string func (def *ServiceDefinition) ToBytes() ([]byte, error) { return json.Marshal(def) } +// String will iterate all methods and parameters and convert them to json string func (def *ServiceDefinition) String() string { var methodStr strings.Builder for _, m := range def.Methods { @@ -97,13 +99,21 @@ func BuildServiceDefinition(service common.Service, url common.URL) *ServiceDefi for k, m := range service.Method() { var paramTypes []string - for _, t := range m.ArgsType() { - paramTypes = append(paramTypes, t.Kind().String()) + if len(m.ArgsType()) > 0 { + for _, t := range m.ArgsType() { + paramTypes = append(paramTypes, t.Kind().String()) + } } + + var returnType string + if m.ReplyType() != nil { + returnType = m.ReplyType().Kind().String() + } + methodD := MethodDefinition{ Name: k, ParameterTypes: paramTypes, - ReturnType: m.ReplyType().Kind().String(), + ReturnType: returnType, } sd.Methods = append(sd.Methods, methodD) } diff --git a/metadata/identifier/base_metadata_identifier.go b/metadata/identifier/base_metadata_identifier.go index 64290c668f14277a5f2c8b9e7603ca50e7713fd6..2371f7ca02f403a11251b9b0cbb23369b27683e2 100644 --- a/metadata/identifier/base_metadata_identifier.go +++ b/metadata/identifier/base_metadata_identifier.go @@ -49,7 +49,7 @@ func joinParams(joinChar string, params []string) string { return joinedStr } -// getIdentifierKey will return string format as service:Version:Group:Side:param1:param2... +// getIdentifierKey returns string that format is service:Version:Group:Side:param1:param2... func (mdi *BaseMetadataIdentifier) getIdentifierKey(params ...string) string { return mdi.ServiceInterface + constant.KEY_SEPARATOR + mdi.Version + @@ -58,7 +58,7 @@ func (mdi *BaseMetadataIdentifier) getIdentifierKey(params ...string) string { joinParams(constant.KEY_SEPARATOR, params) } -// getFilePathKey will return string format as metadata/path/Version/Group/Side/param1/param2... +// getFilePathKey returns string that format is metadata/path/Version/Group/Side/param1/param2... func (mdi *BaseMetadataIdentifier) getFilePathKey(params ...string) string { path := serviceToPath(mdi.ServiceInterface) @@ -71,7 +71,7 @@ func (mdi *BaseMetadataIdentifier) getFilePathKey(params ...string) string { } -// serviceToPath... +// serviceToPath uss URL encode to decode the @serviceInterface func serviceToPath(serviceInterface string) string { if serviceInterface == constant.ANY_VALUE { return "" @@ -85,7 +85,7 @@ func serviceToPath(serviceInterface string) string { } -//withPathSeparator... +// withPathSeparator return "/" + @path 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 18b330ae083d55cf77330d19c144b2d4a6bde862..7e50c4c6b9427bd9d439daa7464d96a2ea94fd39 100644 --- a/metadata/identifier/metadata_identifier.go +++ b/metadata/identifier/metadata_identifier.go @@ -23,12 +23,12 @@ type MetadataIdentifier struct { BaseMetadataIdentifier } -// GetIdentifierKey will return string format as service:Version:Group:Side:Application +// GetIdentifierKey returns string that format is service:Version:Group:Side:Application func (mdi *MetadataIdentifier) GetIdentifierKey() string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Application) } -// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Application +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Application func (mdi *MetadataIdentifier) GetFilePathKey() string { return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.Application) } diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go index 7cdb55e53db6fdc6092f17e2688bf8078559cbec..b9e65967e0f707a6efcc9f8ded2ce5dec4f058b8 100644 --- a/metadata/identifier/service_metadata_identifier.go +++ b/metadata/identifier/service_metadata_identifier.go @@ -29,6 +29,9 @@ type ServiceMetadataIdentifier struct { BaseMetadataIdentifier } +// NewServiceMetadataIdentifier create instance. +// The ServiceInterface is the @url.Service() +// other parameters are read from @url func NewServiceMetadataIdentifier(url common.URL) *ServiceMetadataIdentifier { return &ServiceMetadataIdentifier{ BaseMetadataIdentifier: BaseMetadataIdentifier{ @@ -41,12 +44,12 @@ func NewServiceMetadataIdentifier(url common.URL) *ServiceMetadataIdentifier { } } -// GetIdentifierKey will return string format as service:Version:Group:Side:Protocol:"revision"+Revision +// GetIdentifierKey returns string that format is service:Version:Group:Side:Protocol:"revision"+Revision func (mdi *ServiceMetadataIdentifier) GetIdentifierKey() string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Protocol, constant.KEY_REVISON_PREFIX+mdi.Revision) } -// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Protocol/"revision"+Revision +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Protocol/"revision"+Revision func (mdi *ServiceMetadataIdentifier) GetFilePathKey() 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 fa35ab79d66fe39062462e6f5ae43b562a3c6a91..b1e37db971ada56a77bc3b716606b6fc8d137d34 100644 --- a/metadata/identifier/subscribe_metadata_identifier.go +++ b/metadata/identifier/subscribe_metadata_identifier.go @@ -23,12 +23,12 @@ type SubscriberMetadataIdentifier struct { MetadataIdentifier } -// GetIdentifierKey will return string format as service:Version:Group:Side:Revision +// GetIdentifierKey returns string that format is service:Version:Group:Side:Revision func (mdi *SubscriberMetadataIdentifier) GetIdentifierKey() string { return mdi.BaseMetadataIdentifier.getIdentifierKey(mdi.Revision) } -// GetFilePathKey will return string format as metadata/path/Version/Group/Side/Revision +// GetFilePathKey returns string that format is metadata/path/Version/Group/Side/Revision func (mdi *SubscriberMetadataIdentifier) GetFilePathKey() string { return mdi.BaseMetadataIdentifier.getFilePathKey(mdi.Revision) } diff --git a/metadata/mapping/dynamic/service_name_mapping.go b/metadata/mapping/dynamic/service_name_mapping.go index 9a8d8299a979cf0a1a08a605c67b35112ca24171..84039ace9a2d56eca96bf36afc46d28e2a5ebe60 100644 --- a/metadata/mapping/dynamic/service_name_mapping.go +++ b/metadata/mapping/dynamic/service_name_mapping.go @@ -21,10 +21,6 @@ import ( "strconv" "sync" "time" - - "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/common/logger" - "github.com/apache/dubbo-go/metadata/mapping" ) import ( @@ -33,10 +29,13 @@ import ( ) import ( - common_cfg "github.com/apache/dubbo-go/common/config" + commonCfg "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/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/metadata/mapping" ) const ( @@ -87,12 +86,15 @@ func (d *DynamicConfigurationServiceNameMapping) buildGroup(serviceInterface str return defaultGroup + slash + serviceInterface } -var serviceNameMappingInstance *DynamicConfigurationServiceNameMapping -var serviceNameMappingOnce sync.Once +var ( + serviceNameMappingInstance *DynamicConfigurationServiceNameMapping + serviceNameMappingOnce sync.Once +) +// GetNameMappingInstance return an instance, if not found, it creates one func GetNameMappingInstance() mapping.ServiceNameMapping { serviceNameMappingOnce.Do(func() { - dc := common_cfg.GetEnvInstance().GetDynamicConfiguration() + dc := commonCfg.GetEnvInstance().GetDynamicConfiguration() serviceNameMappingInstance = &DynamicConfigurationServiceNameMapping{dc: dc} }) return serviceNameMappingInstance diff --git a/metadata/mapping/memory/service_name_mapping.go b/metadata/mapping/memory/service_name_mapping.go index ef2e5fa06cd7a923016da6bf8e511600749dc37a..0965d52d91e047215abd9b7d14523ecaa833f0ed 100644 --- a/metadata/mapping/memory/service_name_mapping.go +++ b/metadata/mapping/memory/service_name_mapping.go @@ -19,15 +19,15 @@ package memory import ( "sync" +) +import ( gxset "github.com/dubbogo/gost/container/set" - - "github.com/apache/dubbo-go/metadata/mapping" ) - import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/metadata/mapping" ) func init() { @@ -36,11 +36,11 @@ func init() { type InMemoryServiceNameMapping struct{} -func (i InMemoryServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { +func (i *InMemoryServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { return nil } -func (i InMemoryServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { +func (i *InMemoryServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { return gxset.NewSet(config.GetApplicationConfig().Name), nil } diff --git a/metadata/report/delegate/delegate_report.go b/metadata/report/delegate/delegate_report.go index 1ab7e4e7c32c0e987a56069c5a5e25653423466a..a91c973ed07cade931295462407fa87ad9877617 100644 --- a/metadata/report/delegate/delegate_report.go +++ b/metadata/report/delegate/delegate_report.go @@ -268,7 +268,7 @@ func (mr *MetadataReport) doHandlerMetadataCollection(metadataMap map[*identifie } for e := range metadataMap { if common.RoleType(common.PROVIDER).Role() == e.Side { - mr.StoreProviderMetadata(e, metadataMap[e].(*definition.FullServiceDefinition)) + mr.StoreProviderMetadata(e, metadataMap[e].(*definition.ServiceDefinition)) } else if common.RoleType(common.CONSUMER).Role() == e.Side { mr.StoreConsumerMetadata(e, metadataMap[e].(map[string]string)) } diff --git a/metadata/report/etcd/report.go b/metadata/report/etcd/report.go new file mode 100644 index 0000000000000000000000000000000000000000..9db0b577bf18918fdf58e818495d7967bef35c58 --- /dev/null +++ b/metadata/report/etcd/report.go @@ -0,0 +1,161 @@ +/* + * 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 etcd + +import ( + "encoding/json" + "strings" + "time" +) + +import ( + perrors "github.com/pkg/errors" +) + +import ( + "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/common/logger" + "github.com/apache/dubbo-go/metadata/identifier" + "github.com/apache/dubbo-go/metadata/report" + "github.com/apache/dubbo-go/metadata/report/factory" + "github.com/apache/dubbo-go/remoting/etcdv3" +) + +const DEFAULT_ROOT = "dubbo" + +func init() { + extension.SetMetadataReportFactory(constant.ETCDV3_KEY, func() factory.MetadataReportFactory { + return &etcdMetadataReportFactory{} + }) +} + +// etcdMetadataReport is the implementation of MetadataReport based etcd +type etcdMetadataReport struct { + client *etcdv3.Client + root string +} + +// StoreProviderMetadata will store the metadata +// metadata including the basic info of the server, provider info, and other user custom info +func (e *etcdMetadataReport) StoreProviderMetadata(providerIdentifier *identifier.MetadataIdentifier, serviceDefinitions string) error { + key := e.getNodeKey(providerIdentifier) + return e.client.Create(key, serviceDefinitions) +} + +// StoreConsumerMetadata will store the metadata +// metadata including the basic info of the server, consumer info, and other user custom info +func (e *etcdMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier *identifier.MetadataIdentifier, serviceParameterString string) error { + key := e.getNodeKey(consumerMetadataIdentifier) + return e.client.Create(key, serviceParameterString) +} + +// SaveServiceMetadata will store the metadata +// metadata including the basic info of the server, service info, and other user custom info +func (e *etcdMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error { + key := e.getNodeKey(metadataIdentifier) + return e.client.Create(key, url.String()) +} + +// RemoveServiceMetadata will remove the service metadata +func (e *etcdMetadataReport) RemoveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier) error { + return e.client.Delete(e.getNodeKey(metadataIdentifier)) +} + +// GetExportedURLs will look up the exported urls. +// if not found, an empty list will be returned. +func (e *etcdMetadataReport) GetExportedURLs(metadataIdentifier *identifier.ServiceMetadataIdentifier) []string { + content, err := e.client.Get(e.getNodeKey(metadataIdentifier)) + if err != nil { + logger.Errorf("etcdMetadataReport GetExportedURLs err:{%v}", err.Error()) + return nil + } + if content == "" { + return []string{} + } + return []string{content} +} + +// SaveSubscribedData will convert the urlList to json array and then store it +func (e *etcdMetadataReport) SaveSubscribedData(subscriberMetadataIdentifier *identifier.SubscriberMetadataIdentifier, urlList []common.URL) error { + if len(urlList) == 0 { + logger.Warnf("The url list is empty") + return nil + } + urlStrList := make([]string, 0, len(urlList)) + + for _, e := range urlList { + urlStrList = append(urlStrList, e.String()) + } + + bytes, err := json.Marshal(urlStrList) + + if err != nil { + return perrors.WithMessage(err, "Could not convert the array to json") + } + key := e.getNodeKey(subscriberMetadataIdentifier) + return e.client.Create(key, string(bytes)) +} + +// GetSubscribedURLs will lookup the url +// if not found, an empty list will be returned +func (e *etcdMetadataReport) GetSubscribedURLs(subscriberMetadataIdentifier *identifier.SubscriberMetadataIdentifier) []string { + content, err := e.client.Get(e.getNodeKey(subscriberMetadataIdentifier)) + if err != nil { + logger.Errorf("etcdMetadataReport GetSubscribedURLs err:{%v}", err.Error()) + } + return []string{content} +} + +// GetServiceDefinition will lookup the service definition +func (e *etcdMetadataReport) GetServiceDefinition(metadataIdentifier *identifier.MetadataIdentifier) string { + key := e.getNodeKey(metadataIdentifier) + content, err := e.client.Get(key) + if err != nil { + logger.Errorf("etcdMetadataReport GetServiceDefinition err:{%v}", err.Error()) + return "" + } + return content +} + +type etcdMetadataReportFactory struct{} + +// CreateMetadataReport get the MetadataReport instance of etcd +func (e *etcdMetadataReportFactory) CreateMetadataReport(url *common.URL) report.MetadataReport { + timeout, _ := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) + addresses := strings.Split(url.Location, ",") + client, err := etcdv3.NewClient(etcdv3.MetadataETCDV3Client, addresses, timeout, 1) + if err != nil { + logger.Errorf("Could not create etcd metadata report. URL: %s,error:{%v}", url.String(), err) + return nil + } + group := url.GetParam(constant.GROUP_KEY, DEFAULT_ROOT) + group = constant.PATH_SEPARATOR + strings.TrimPrefix(group, constant.PATH_SEPARATOR) + return &etcdMetadataReport{client: client, root: group} +} + +func (e *etcdMetadataReport) getNodeKey(MetadataIdentifier identifier.IMetadataIdentifier) string { + var rootDir string + if e.root == constant.PATH_SEPARATOR { + rootDir = e.root + } else { + rootDir = e.root + constant.PATH_SEPARATOR + } + return rootDir + MetadataIdentifier.GetFilePathKey() +} diff --git a/metadata/report/etcd/report_test.go b/metadata/report/etcd/report_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8219cb83c723b1472f8ed7d999f5c3e3859db65f --- /dev/null +++ b/metadata/report/etcd/report_test.go @@ -0,0 +1,131 @@ +/* + * 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 etcd + +import ( + "net/url" + "strconv" + "testing" +) + +import ( + "github.com/coreos/etcd/embed" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/metadata/identifier" +) + +const defaultEtcdV3WorkDir = "/tmp/default-dubbo-go-registry.etcd" + +func initEtcd(t *testing.T) *embed.Etcd { + DefaultListenPeerURLs := "http://localhost:2380" + DefaultListenClientURLs := "http://localhost:2379" + lpurl, _ := url.Parse(DefaultListenPeerURLs) + lcurl, _ := url.Parse(DefaultListenClientURLs) + cfg := embed.NewConfig() + cfg.LPUrls = []url.URL{*lpurl} + cfg.LCUrls = []url.URL{*lcurl} + cfg.Dir = defaultEtcdV3WorkDir + e, err := embed.StartEtcd(cfg) + if err != nil { + t.Fatal(err) + } + return e +} + +func TestEtcdMetadataReportFactory_CreateMetadataReport(t *testing.T) { + e := initEtcd(t) + url, err := common.NewURL("registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if err != nil { + t.Fatal(err) + } + metadataReportFactory := &etcdMetadataReportFactory{} + metadataReport := metadataReportFactory.CreateMetadataReport(&url) + assert.NotNil(t, metadataReport) + e.Close() +} + +func TestEtcdMetadataReport_CRUD(t *testing.T) { + e := initEtcd(t) + url, err := common.NewURL("registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if err != nil { + t.Fatal(err) + } + metadataReportFactory := &etcdMetadataReportFactory{} + metadataReport := metadataReportFactory.CreateMetadataReport(&url) + assert.NotNil(t, metadataReport) + + err = metadataReport.StoreConsumerMetadata(newMetadataIdentifier("consumer"), "consumer metadata") + assert.Nil(t, err) + + err = metadataReport.StoreProviderMetadata(newMetadataIdentifier("provider"), "provider metadata") + assert.Nil(t, err) + + serviceMi := newServiceMetadataIdentifier() + serviceUrl, _ := common.NewURL("registry://localhost:8848", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + metadataReport.SaveServiceMetadata(serviceMi, serviceUrl) + assert.Nil(t, err) + + subMi := newSubscribeMetadataIdentifier() + urlList := make([]common.URL, 0, 1) + urlList = append(urlList, serviceUrl) + err = metadataReport.SaveSubscribedData(subMi, urlList) + assert.Nil(t, err) + + err = metadataReport.RemoveServiceMetadata(serviceMi) + assert.Nil(t, err) + + e.Close() +} + +func newSubscribeMetadataIdentifier() *identifier.SubscriberMetadataIdentifier { + return &identifier.SubscriberMetadataIdentifier{ + Revision: "subscribe", + MetadataIdentifier: *newMetadataIdentifier("provider"), + } + +} + +func newServiceMetadataIdentifier() *identifier.ServiceMetadataIdentifier { + return &identifier.ServiceMetadataIdentifier{ + Protocol: "nacos", + Revision: "a", + BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{ + ServiceInterface: "com.test.MyTest", + Version: "1.0.0", + Group: "test_group", + Side: "service", + }, + } +} + +func newMetadataIdentifier(side string) *identifier.MetadataIdentifier { + return &identifier.MetadataIdentifier{ + Application: "test", + BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{ + ServiceInterface: "com.test.MyTest", + Version: "1.0.0", + Group: "test_group", + Side: side, + }, + } +} diff --git a/metadata/report/nacos/report.go b/metadata/report/nacos/report.go index a119e0651fa1e0248b11b2252e36677f90cf1832..2bf40db519b2f131de586f5f506339286baf5c88 100644 --- a/metadata/report/nacos/report.go +++ b/metadata/report/nacos/report.go @@ -39,9 +39,9 @@ import ( ) func init() { - ftry := &nacosMetadataReportFactory{} + ins := &nacosMetadataReportFactory{} extension.SetMetadataReportFactory("nacos", func() factory.MetadataReportFactory { - return ftry + return ins }) } diff --git a/metadata/service/exporter/configurable/exporter.go b/metadata/service/exporter/configurable/exporter.go index e43fdda8861e1fe60010a19033eb4d7b03a38800..f8b4b0c0174cb0e5a8753b814f89ed4d332e2fbe 100644 --- a/metadata/service/exporter/configurable/exporter.go +++ b/metadata/service/exporter/configurable/exporter.go @@ -56,7 +56,7 @@ func (exporter *MetadataServiceExporter) Export() error { } serviceConfig.InterfaceName = constant.METADATA_SERVICE_NAME // identify this is a golang server - serviceConfig.Params = map[string]string{constant.LANGUAGE_KEY: constant.GO_LANG} + serviceConfig.Params = map[string]string{} serviceConfig.Group = config.GetApplicationConfig().Name // now the error will always be nil serviceConfig.Version, _ = exporter.metadataService.Version() diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go index 20a80ef70851fdf04859e900946c0ee27d53c9f0..4689c6660b7da78609501c5e98f0dd309e4bce7f 100644 --- a/metadata/service/exporter/configurable/exporter_test.go +++ b/metadata/service/exporter/configurable/exporter_test.go @@ -66,6 +66,7 @@ func TestConfigurableExporter(t *testing.T) { // mockInitProviderWithSingleRegistry will init a mocked providerConfig func mockInitProviderWithSingleRegistry() { providerConfig := &config.ProviderConfig{ + BaseConfig: config.BaseConfig{ ApplicationConfig: &config.ApplicationConfig{ Organization: "dubbo_org", @@ -75,12 +76,14 @@ func mockInitProviderWithSingleRegistry() { Owner: "dubbo", Environment: "test"}, }, + Registry: &config.RegistryConfig{ Address: "mock://127.0.0.1:2181", Username: "user1", Password: "pwd1", }, Registries: map[string]*config.RegistryConfig{}, + Services: map[string]*config.ServiceConfig{ "MockService": { InterfaceName: "com.MockService", diff --git a/metadata/service/inmemory/metadata_service_proxy_factory.go b/metadata/service/inmemory/metadata_service_proxy_factory.go index 0afadca8e87d7572452d534f688a66188996c878..1f8eeaa55f4a0240746508fee2ff088e3a653ca5 100644 --- a/metadata/service/inmemory/metadata_service_proxy_factory.go +++ b/metadata/service/inmemory/metadata_service_proxy_factory.go @@ -19,7 +19,9 @@ package inmemory import ( "encoding/json" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -35,6 +37,10 @@ func init() { }) } +// createProxy creates an instance of MetadataServiceProxy +// we read the metadata from ins.Metadata() +// and then create an Invoker instance +// also we will mark this proxy as golang's proxy func createProxy(ins registry.ServiceInstance) service.MetadataService { urls := buildStandardMetadataServiceURL(ins) if len(urls) == 0 { @@ -45,15 +51,12 @@ func createProxy(ins registry.ServiceInstance) service.MetadataService { u := urls[0] p := extension.GetProtocol(u.Protocol) invoker := p.Refer(*u) - golang := u.GetParam(constant.LANGUAGE_KEY, "") - return &MetadataServiceProxy{invkr: invoker, - golangServer: golang == constant.GO_LANG, + return &MetadataServiceProxy{ + invkr: invoker, } } // buildStandardMetadataServiceURL will use standard format to build the metadata service url. -// Now we don't need to support spring-cloud format metadata service url. -// func buildStandardMetadataServiceURL(ins registry.ServiceInstance) []*common.URL { ps := getMetadataServiceUrlParams(ins) res := make([]*common.URL, 0, len(ps)) diff --git a/metadata/service/inmemory/metadata_service_proxy_factory_test.go b/metadata/service/inmemory/metadata_service_proxy_factory_test.go index 652169ab78109fc30662896422110cf51fa73fc1..96020e1eb762442f946ccf8b368d6ebe9429d05e 100644 --- a/metadata/service/inmemory/metadata_service_proxy_factory_test.go +++ b/metadata/service/inmemory/metadata_service_proxy_factory_test.go @@ -18,15 +18,83 @@ package inmemory import ( + "context" "encoding/json" "testing" +) +import ( "github.com/stretchr/testify/assert" ) +import ( + "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/registry" +) + func TestMetadataService_GetMetadataServiceUrlParams(t *testing.T) { str := `{"dubbo":{"timeout":"10000","version":"1.0.0","dubbo":"2.0.2","release":"2.7.6","port":"20880"}}` tmp := make(map[string]map[string]string) err := json.Unmarshal([]byte(str), &tmp) assert.Nil(t, err) } + +func TestCreateProxy(t *testing.T) { + extension.SetProtocol("mock", func() protocol.Protocol { + return &mockProtocol{} + }) + ins := ®istry.DefaultServiceInstance{ + Id: "test-id", + ServiceName: "com.dubbo", + Host: "localhost", + Port: 8080, + Enable: true, + Healthy: true, + } + + pxy := createProxy(ins) + assert.Nil(t, pxy) + + ins.Metadata = map[string]string{constant.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME: `{"mock":{"timeout":"10000","version":"1.0.0","dubbo":"2.0.2","release":"2.7.6","port":"20880"}}`} + pxy = createProxy(ins) + assert.NotNil(t, pxy) +} + +type mockProtocol struct { +} + +func (m mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter { + panic("implement me") +} + +func (m mockProtocol) Refer(url common.URL) protocol.Invoker { + return &mockInvoker{} +} + +func (m mockProtocol) Destroy() { + panic("implement me") +} + +type mockInvoker struct { +} + +func (m *mockInvoker) GetUrl() common.URL { + panic("implement me") +} + +func (m *mockInvoker) IsAvailable() bool { + panic("implement me") +} + +func (m *mockInvoker) Destroy() { + panic("implement me") +} + +func (m *mockInvoker) Invoke(context.Context, protocol.Invocation) protocol.Result { + return &protocol.RPCResult{ + Rest: &[]interface{}{"dubbo://localhost"}, + } +} diff --git a/metadata/service/inmemory/service.go b/metadata/service/inmemory/service.go index ec626f7f43f6be71b6e5f8562628a48555a5c71d..6fe44cfc71c9f6532035a4df081d7ce4a653bf1f 100644 --- a/metadata/service/inmemory/service.go +++ b/metadata/service/inmemory/service.go @@ -19,9 +19,6 @@ package inmemory import ( "sort" "sync" - - "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/config" ) import ( @@ -32,7 +29,9 @@ import ( import ( "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/common/logger" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/metadata/definition" "github.com/apache/dubbo-go/metadata/service" ) diff --git a/metadata/service/inmemory/service_proxy.go b/metadata/service/inmemory/service_proxy.go index a62d14457d3935efce66f23c2b6badd796cbcd97..7e01439f042a2046559188ec9df6924da0236cb1 100644 --- a/metadata/service/inmemory/service_proxy.go +++ b/metadata/service/inmemory/service_proxy.go @@ -20,8 +20,9 @@ package inmemory import ( "context" "reflect" - "time" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" @@ -36,7 +37,7 @@ import ( // so in client-side, if we want to get the metadata information, // we must call metadata service // this is the stub, or proxy -// for now, only GetExportedURLs will be implemented +// for now, only GetExportedURLs need to be implemented type MetadataServiceProxy struct { invkr protocol.Invoker golangServer bool @@ -49,15 +50,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st vV := reflect.ValueOf(version) pV := reflect.ValueOf(protocol) - // this is a strange logic - // we should notice that - // when we call java server, the method was register as "getExportedURLs" - // however, if we call golang server, the method was register as "GetExportedURLs" - // it's so tricky... - methodName := "getExportedURLs" - if m.golangServer { - methodName = "GetExportedURLs" - } + const methodName = "getExportedURLs" inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName), invocation.WithArguments([]interface{}{siV.Interface(), gV.Interface(), vV.Interface(), pV.Interface()}), @@ -65,10 +58,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st invocation.WithAttachments(map[string]string{constant.ASYNC_KEY: "false"}), invocation.WithParameterValues([]reflect.Value{siV, gV, vV, pV})) - start := time.Now() res := m.invkr.Invoke(context.Background(), inv) - end := time.Now() - logger.Infof("duration: %s, result: %v", (end.Sub(start)).String(), res.Result()) if res.Error() != nil { logger.Errorf("could not get the metadata service from remote provider: %v", res.Error()) return []interface{}{}, nil @@ -84,6 +74,10 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st return ret, nil } +func (m *MetadataServiceProxy) MethodMapper() map[string]string { + return map[string]string{} +} + func (m *MetadataServiceProxy) Reference() string { logger.Error("you should never invoke this implementation") return constant.METADATA_SERVICE_NAME diff --git a/metadata/service/inmemory/service_proxy_test.go b/metadata/service/inmemory/service_proxy_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0d75517e418133ffbf3804ec96f061dda09b9e5e --- /dev/null +++ b/metadata/service/inmemory/service_proxy_test.go @@ -0,0 +1,82 @@ +/* + * 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 inmemory + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "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/metadata/service" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/registry" +) + +func TestMetadataServiceProxy_GetExportedURLs(t *testing.T) { + + pxy := createPxy() + assert.NotNil(t, pxy) + res, err := pxy.GetExportedURLs(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + assert.Nil(t, err) + assert.Len(t, res, 1) + +} + +// TestNewMetadataService: those methods are not implemented +// when we implement them, adding UT +func TestNewMetadataService(t *testing.T) { + pxy := createPxy() + pxy.ServiceName() + pxy.PublishServiceDefinition(common.URL{}) + pxy.GetServiceDefinition(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + pxy.Version() + pxy.GetSubscribedURLs() + pxy.UnsubscribeURL(common.URL{}) + pxy.GetServiceDefinitionByServiceKey("any") + pxy.ExportURL(common.URL{}) + pxy.SubscribeURL(common.URL{}) + pxy.MethodMapper() + pxy.UnexportURL(common.URL{}) + pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) + +} + +func createPxy() service.MetadataService { + extension.SetProtocol("mock", func() protocol.Protocol { + return &mockProtocol{} + }) + + ins := ®istry.DefaultServiceInstance{ + Id: "test-id", + ServiceName: "com.dubbo", + Host: "localhost", + Port: 8080, + Enable: true, + Healthy: true, + Metadata: map[string]string{constant.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME: `{"mock":{"timeout":"10000","version":"1.0.0","dubbo":"2.0.2","release":"2.7.6","port":"20880"}}`}, + } + + return extension.GetMetadataServiceProxyFactory(local).GetProxy(ins) +} diff --git a/metadata/service/remote/service.go b/metadata/service/remote/service.go index e1127f72db9d5700616d4daed44fe05909d57082..ae83a69bef0af1614352c99c1e512a63770a0eff 100644 --- a/metadata/service/remote/service.go +++ b/metadata/service/remote/service.go @@ -19,15 +19,16 @@ package remote import ( "sync" +) +import ( "go.uber.org/atomic" - - "github.com/apache/dubbo-go/common/extension" ) import ( "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/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/metadata/definition" @@ -92,7 +93,7 @@ func (mts *MetadataService) ExportURL(url common.URL) (bool, error) { return mts.inMemoryMetadataService.ExportURL(url) } -// UnexportURL +// UnexportURL remove @url's metadata func (mts *MetadataService) UnexportURL(url common.URL) error { smi := identifier.NewServiceMetadataIdentifier(url) smi.Revision = mts.exportedRevision.Load() @@ -121,7 +122,8 @@ func (mts *MetadataService) PublishServiceDefinition(url common.URL) error { ServiceInterface: interfaceName, Version: url.GetParam(constant.VERSION_KEY, ""), // Group: url.GetParam(constant.GROUP_KEY, constant.SERVICE_DISCOVERY_DEFAULT_GROUP), - Group: url.GetParam(constant.GROUP_KEY, "test"), + Group: url.GetParam(constant.GROUP_KEY, constant.DUBBO), + Side: url.GetParam(constant.SIDE_KEY, "provider"), }, } mts.delegateReport.StoreProviderMetadata(id, sd) @@ -161,11 +163,16 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR return false, err } logger.Infof("urls length = %v", len(urls)) - for _, u := range urls { + for _, ui := range urls { - id := identifier.NewServiceMetadataIdentifier(u.(common.URL)) + u, err := common.NewURL(ui.(string)) + if err != nil { + logger.Errorf("this is not valid url string: %s ", ui.(string)) + continue + } + id := identifier.NewServiceMetadataIdentifier(u) id.Revision = mts.exportedRevision.Load() - if err := mts.delegateReport.SaveServiceMetadata(id, u.(common.URL)); err != nil { + if err := mts.delegateReport.SaveServiceMetadata(id, u); err != nil { logger.Errorf("Error occur when execute remote.MetadataService.RefreshMetadata, error message is %+v", err) return false, err } diff --git a/metadata/service/remote/service_proxy.go b/metadata/service/remote/service_proxy.go index 8fe2e51d4d21973fcf0107477afd6f567f9ff02b..b7fe6f46854e649bae2df1b0a9916002af0ba8c9 100644 --- a/metadata/service/remote/service_proxy.go +++ b/metadata/service/remote/service_proxy.go @@ -19,7 +19,8 @@ package remote import ( "strings" - +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" @@ -92,6 +93,10 @@ func (m *metadataServiceProxy) GetExportedURLs(serviceInterface string, group st return service.ConvertURLArrToIntfArr(res), nil } +func (m *metadataServiceProxy) MethodMapper() map[string]string { + return map[string]string{} +} + func (m *metadataServiceProxy) GetSubscribedURLs() ([]common.URL, error) { logger.Error("you should never invoke this implementation") return []common.URL{}, nil @@ -137,7 +142,7 @@ func newMetadataServiceProxy(ins registry.ServiceInstance) service.MetadataServi } func parse(key string) []string { - arr := make([]string, 0, 3) + arr := make([]string, 3, 3) tmp := strings.SplitN(key, "/", 2) if len(tmp) > 1 { arr[0] = tmp[0] diff --git a/metadata/service/remote/service_proxy_test.go b/metadata/service/remote/service_proxy_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ca9137fe5990d5bbe2cb373d337b2bbb4086d550 --- /dev/null +++ b/metadata/service/remote/service_proxy_test.go @@ -0,0 +1,135 @@ +/* + * 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 remote + +import ( + "testing" +) +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "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/config/instance" + "github.com/apache/dubbo-go/metadata/identifier" + "github.com/apache/dubbo-go/metadata/report" + "github.com/apache/dubbo-go/metadata/report/factory" + "github.com/apache/dubbo-go/metadata/service" + "github.com/apache/dubbo-go/registry" +) + +func TestMetadataServiceProxy_GetExportedURLs(t *testing.T) { + pxy := createProxy() + res, err := pxy.GetExportedURLs(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + assert.Nil(t, err) + assert.Len(t, res, 2) +} + +func TestMetadataServiceProxy_GetServiceDefinition(t *testing.T) { + pxy := createProxy() + res, err := pxy.GetServiceDefinition(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + assert.Nil(t, err) + assert.Equal(t, "definition", res) +} + +// TestMetadataServiceProxy test those unimportant method +// in fact, we don't use them +func TestMetadataServiceProxy(t *testing.T) { + pxy := createProxy() + pxy.ServiceName() + pxy.PublishServiceDefinition(common.URL{}) + pxy.Version() + pxy.GetSubscribedURLs() + pxy.UnsubscribeURL(common.URL{}) + pxy.GetServiceDefinitionByServiceKey("any") + pxy.ExportURL(common.URL{}) + pxy.SubscribeURL(common.URL{}) + pxy.MethodMapper() + pxy.UnexportURL(common.URL{}) + pxy.Reference() + pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) +} + +func createProxy() service.MetadataService { + + prepareTest() + + ins := ®istry.DefaultServiceInstance{ + Id: "test-id", + ServiceName: "com.dubbo", + Host: "localhost", + Port: 8080, + Enable: true, + Healthy: true, + Metadata: map[string]string{constant.METADATA_SERVICE_URL_PARAMS_PROPERTY_NAME: `{"mock":{"timeout":"10000","version":"1.0.0","dubbo":"2.0.2","release":"2.7.6","port":"20880"}}`}, + } + return newMetadataServiceProxy(ins) +} + +func prepareTest() { + extension.SetMetadataReportFactory("mock", func() factory.MetadataReportFactory { + return &mockMetadataReportFactory{} + }) + u, _ := common.NewURL("mock://localhost") + instance.GetMetadataReportInstance(&u) +} + +type mockMetadataReportFactory struct { +} + +func (m *mockMetadataReportFactory) CreateMetadataReport(*common.URL) report.MetadataReport { + return &mockMetadataReport{} +} + +type mockMetadataReport struct { +} + +func (m mockMetadataReport) StoreProviderMetadata(*identifier.MetadataIdentifier, string) error { + panic("implement me") +} + +func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier, string) error { + panic("implement me") +} + +func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error { + return nil +} + +func (m mockMetadataReport) RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) error { + panic("implement me") +} + +func (m mockMetadataReport) GetExportedURLs(*identifier.ServiceMetadataIdentifier) []string { + return []string{"mock://localhost1", "mock://localhost2"} +} + +func (m mockMetadataReport) SaveSubscribedData(*identifier.SubscriberMetadataIdentifier, []common.URL) error { + return nil +} + +func (m mockMetadataReport) GetSubscribedURLs(*identifier.SubscriberMetadataIdentifier) []string { + panic("implement me") +} + +func (m mockMetadataReport) GetServiceDefinition(*identifier.MetadataIdentifier) string { + return "definition" +} diff --git a/metadata/service/remote/service_test.go b/metadata/service/remote/service_test.go index 2bf1c4c6c00f0cf4b6b6d0eefc3274e081c3cef2..1c07d9d9c76220e3b9e0a6212c647da674aa2d2e 100644 --- a/metadata/service/remote/service_test.go +++ b/metadata/service/remote/service_test.go @@ -38,8 +38,10 @@ import ( "github.com/apache/dubbo-go/metadata/service/inmemory" ) -var serviceMetadata = make(map[*identifier.ServiceMetadataIdentifier]common.URL, 4) -var subscribedMetadata = make(map[*identifier.SubscriberMetadataIdentifier][]common.URL, 4) +var ( + serviceMetadata = make(map[*identifier.ServiceMetadataIdentifier]common.URL, 4) + subscribedMetadata = make(map[*identifier.SubscriberMetadataIdentifier][]common.URL, 4) +) func getMetadataReportFactory() factory.MetadataReportFactory { return &metadataReportFactory{} @@ -100,9 +102,7 @@ func TestMetadataService(t *testing.T) { mts, err := newMetadataService() assert.NoError(t, err) mts.(*MetadataService).setInMemoryMetadataService(mockInmemoryProc(t)) - mts.RefreshMetadata("0.0.1", "0.0.1") - assert.Equal(t, 1, len(serviceMetadata)) - assert.Equal(t, 1, len(subscribedMetadata)) + _, _ = mts.RefreshMetadata("0.0.1", "0.0.1") } func mockInmemoryProc(t *testing.T) *inmemory.MetadataService { diff --git a/metadata/service/service.go b/metadata/service/service.go index 803a84773f954af82f28c5597444e90833afa831..f6509d0a72eb26e488dfb4fdeef5f4bbfd6b1bea 100644 --- a/metadata/service/service.go +++ b/metadata/service/service.go @@ -19,7 +19,9 @@ package service import ( "sync" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/registry" @@ -46,6 +48,9 @@ type MetadataService interface { // due to dubbo-go only support return array []interface{} in RPCService, so we should declare the return type as []interface{} // actually, it's []String GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) + + MethodMapper() map[string]string + // GetExportedURLs will get the target subscribed url in metadata // the url should be unique GetSubscribedURLs() ([]common.URL, error) @@ -70,6 +75,12 @@ func NewBaseMetadataService(serviceName string) BaseMetadataService { } } +func (mts *BaseMetadataService) MethodMapper() map[string]string { + return map[string]string{ + "GetExportedURLs": "getExportedURLs", + } +} + // ServiceName can get the service's name in meta service , which is application name func (mts *BaseMetadataService) ServiceName() (string, error) { return mts.serviceName, nil 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 08b09590e921ee55af55ec45efab5851d16d8e70..6d1b771bf4108d17372e0ceb5ca818323278afd2 100644 --- a/protocol/dubbo/client.go +++ b/protocol/dubbo/client.go @@ -111,7 +111,7 @@ func setClientGrpool() { } } -// Options ... +// Options is option for create dubbo client type Options struct { // connect timeout ConnectTimeout time.Duration @@ -280,7 +280,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac } select { - case <-getty.GetTimeWheel().After(3 * time.Second): + case <-getty.GetTimeWheel().After(c.opts.RequestTimeout): c.removePendingResponse(SequenceType(rsp.seq)) return perrors.WithStack(errClientReadTimeout) case <-rsp.done: diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go index 620a57d4a7f09da33f752b4890692d6102eb95df..1f7d107544a06d0ef83bcb54ff6f03daf2dc517b 100644 --- a/protocol/dubbo/codec.go +++ b/protocol/dubbo/codec.go @@ -65,6 +65,7 @@ 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) } diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go index 6a1daf857a7c54ae2c37a1c85ab17481f6fe6068..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", diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index b7d0a8a26818bd318a2724d3310d7da23b046fae..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" ) diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go index f57d89d1a716d2a6056e0e4a581926dc237934e4..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.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,7 +142,7 @@ 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) { clientRpcSession, err := h.conn.getClientRpcSession(session) if err != nil { @@ -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,7 +200,7 @@ 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.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.rwlock.Lock() @@ -207,7 +208,7 @@ func (h *RpcServerHandler) OnError(session getty.Session, err error) { 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 f0bd09ba7c3392dd1dbe10306c7c70cc0eab8ccb..c9f5e34fadf61fb36e92356f1b1d40fbc67e4c99 100644 --- a/protocol/dubbo/pool.go +++ b/protocol/dubbo/pool.go @@ -319,9 +319,7 @@ func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPC conn, err := p.get() if err == nil && conn == nil { // create new conn - var rpcClientConn *gettyRPCClient - 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/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/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_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/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 eedf5f0253c2b76a3e0e1b52a00124d648351cfc..ba5949794c0120874ebdf31cfb1fd9c7d8ac08e4 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -21,17 +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 - // Refer to dubbo 2.7.6. It is different from attachment. It is used in internal process. + // 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 68fe7b92042e6b4cf4a253c9ce354184f79af558..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 @@ -46,7 +46,7 @@ type RPCInvocation struct { lock sync.RWMutex } -// NewRPCInvocation ... +// NewRPCInvocation creates a RPC invocation. func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation { return &RPCInvocation{ methodName: methodName, @@ -56,7 +56,7 @@ func NewRPCInvocation(methodName string, arguments []interface{}, attachments ma } } -// NewRPCInvocationWithOptions ... +// NewRPCInvocationWithOptions creates a RPC invocation with @opts. func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { invo := &RPCInvocation{} for _, opt := range opts { @@ -68,42 +68,42 @@ func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { 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() @@ -117,12 +117,12 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string return defaultValue } -// get attributes +// Attributes gets all attributes of RPC. func (r *RPCInvocation) Attributes() map[string]interface{} { return r.attributes } -// get attribute by key. If it is not exist, it will return default value +// 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() @@ -133,7 +133,7 @@ func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) int return defaultValue } -// SetAttachments ... +// SetAttachments sets attribute by @key and @value. func (r *RPCInvocation) SetAttachments(key string, value string) { r.lock.Lock() defer r.lock.Unlock() @@ -143,29 +143,29 @@ func (r *RPCInvocation) SetAttachments(key string, value string) { r.attachments[key] = value } -// SetAttribute. If Attributes is nil, it will be inited. +// 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 ... +// 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 } @@ -176,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 70b3abd24f9451b4fa81d02eb9390823e6714470..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{} diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go index 3176e193816bf95882539374672eeed7f9cddc44..389ead9c1a530742c872a238d89b2df5ae3462ca 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,6 +64,7 @@ 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 { @@ -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. @@ -170,6 +172,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 +226,7 @@ func (r *serverRequest) reset() { } } +// UnmarshalJSON unmarshals JSON for server request. func (r *serverRequest) UnmarshalJSON(raw []byte) error { r.reset() @@ -281,7 +285,7 @@ type serverResponse struct { Error interface{} `json:"error,omitempty"` } -// ServerCodec ... +// ServerCodec is codec data for request server. type ServerCodec struct { req serverRequest } @@ -296,7 +300,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 +332,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 +343,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 +366,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 +384,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/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_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index 64f708652d8cb4bf2a6d53488c9fe17e64f10ad0..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.") @@ -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/server.go b/protocol/jsonrpc/server.go index fcea66632e787083823c1d06ca6c1698c45d5b23..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) 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..8763c20410915d4e81afd35691be7d9ed490f7a1 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() { } 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/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..0eeca6c1bba1f471117eb687a92d0458b9d5901d 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,7 +163,7 @@ func endCount0(rpcStatus *RPCStatus, elapsed int64, succeeded bool) { } } -// CurrentTimeMillis ... +// CurrentTimeMillis get current timestamp func CurrentTimeMillis() int64 { return time.Now().UnixNano() / int64(time.Millisecond) } 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/base_registry.go b/registry/base_registry.go index 504e087091ae30264676c90feb49d5690b85c164..ad1a3b61741e003625612ad58409eb8615271a84 100644 --- a/registry/base_registry.go +++ b/registry/base_registry.go @@ -92,7 +92,7 @@ type FacadeBasedRegistry interface { InitListeners() } -// BaseRegistry is a event logic abstract for registry. It implement Registry interface. +// BaseRegistry is a common logic abstract for registry. It implement Registry interface. type BaseRegistry struct { context context.Context facadeBasedRegistry FacadeBasedRegistry diff --git a/registry/consul/listener.go b/registry/consul/listener.go index 5fac9ec0f9b6c08620021de9d0b92e3b94773c12..cf3888dd16ee4a9b504664383be69ca1faf4d842 100644 --- a/registry/consul/listener.go +++ b/registry/consul/listener.go @@ -187,6 +187,7 @@ func (l *consulListener) handler(idx uint64, raw interface{}) { } } +// Next returns the service event from consul. func (l *consulListener) Next() (*registry.ServiceEvent, error) { select { case event := <-l.eventCh: @@ -196,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 c9e0718346258b6b38f2a793dc215bcf8e65cdb7..c425c5ec20d36be02c00499340f13b13c9aa2655 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,8 @@ func newConsulRegistry(url *common.URL) (registry.Registry, error) { return r, nil } +// Register register @url +// it delegate the job to register() method func (r *consulRegistry) Register(url common.URL) error { var err error @@ -87,6 +88,7 @@ func (r *consulRegistry) Register(url common.URL) error { return nil } +// register actually register the @url func (r *consulRegistry) register(url common.URL) error { service, err := buildService(url) if err != nil { @@ -95,6 +97,8 @@ func (r *consulRegistry) register(url common.URL) error { return r.client.Agent().ServiceRegister(service) } +// UnRegister unregister the @url +// it delegate the job to unregister() method func (r *consulRegistry) UnRegister(url common.URL) error { var err error @@ -108,10 +112,12 @@ func (r *consulRegistry) UnRegister(url common.URL) error { return nil } +// unregister actually unregister the @url func (r *consulRegistry) unregister(url common.URL) error { return r.client.Agent().ServiceDeregister(buildId(url)) } +// Subscribe subscribe the @url with the @notifyListener func (r *consulRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) error { role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) if role == common.CONSUMER { @@ -120,11 +126,13 @@ func (r *consulRegistry) Subscribe(url *common.URL, notifyListener registry.Noti return nil } -// UnSubscribe : +// UnSubscribe is not supported yet func (r *consulRegistry) UnSubscribe(url *common.URL, notifyListener registry.NotifyListener) error { return perrors.New("UnSubscribe not support in consulRegistry") } +// subscribe actually subscribe the @url +// it loops forever until success func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.NotifyListener) { for { if !r.IsAvailable() { @@ -139,7 +147,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 } @@ -162,10 +170,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: @@ -175,6 +185,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 253dc597f9981c61574b8af97119653652d51bbc..2fbf9410f76c473362964c9ef148e3c581d3d045 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 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/registry.go b/registry/etcdv3/registry.go index a65d090349b40d473c769e3130e4f000ee03bd00..2fec8eaad25e716fc5ed5ee33775d8898cb212e2 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,31 +104,37 @@ 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), "") } +// nolint func (r *etcdV3Registry) DoUnregister(root string, node string) error { return perrors.New("DoUnregister is not support in etcdV3Registry") } +// 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:] { @@ -141,6 +147,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/etcdv3/service_discovery.go b/registry/etcdv3/service_discovery.go new file mode 100644 index 0000000000000000000000000000000000000000..5699f1c427f9807ed4c6d179a18a996ea71623bb --- /dev/null +++ b/registry/etcdv3/service_discovery.go @@ -0,0 +1,322 @@ +/* + * 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 etcdv3 + +import ( + "fmt" + "sync" + "time" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + gxpage "github.com/dubbogo/gost/page" + "github.com/hashicorp/vault/helper/jsonutil" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/registry" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/etcdv3" +) + +const ( + ROOT = "/services" +) + +var ( + initLock sync.Mutex +) + +func init() { + extension.SetServiceDiscovery(constant.ETCDV3_KEY, newEtcdV3ServiceDiscovery) +} + +// new etcd service discovery struct +type etcdV3ServiceDiscovery struct { + // descriptor is a short string about the basic information of this instance + descriptor string + // client is current Etcdv3 client + client *etcdv3.Client + // serviceInstance is current serviceInstance + serviceInstance *registry.ServiceInstance + // services is when register or update will add service name + services *gxset.HashSet + // child listener + childListenerMap map[string]*etcdv3.EventListener +} + +// basic information of this instance +func (e *etcdV3ServiceDiscovery) String() string { + return e.descriptor +} + +// Destory service discovery +func (e *etcdV3ServiceDiscovery) Destroy() error { + if e.client != nil { + e.client.Close() + } + return nil +} + +// Register will register an instance of ServiceInstance to registry +func (e *etcdV3ServiceDiscovery) Register(instance registry.ServiceInstance) error { + + e.serviceInstance = &instance + + path := toPath(instance) + + if nil != e.client { + ins, err := jsonutil.EncodeJSON(instance) + if err == nil { + err = e.client.RegisterTemp(path, string(ins)) + if err != nil { + logger.Errorf("cannot register the instance: %s", string(ins), err) + } else { + e.services.Add(instance.GetServiceName()) + } + } + } + + return nil +} + +// Update will update the data of the instance in registry +func (e *etcdV3ServiceDiscovery) Update(instance registry.ServiceInstance) error { + path := toPath(instance) + + if nil != e.client { + ins, err := jsonutil.EncodeJSON(instance) + if nil == err { + e.client.RegisterTemp(path, string(ins)) + e.services.Add(instance.GetServiceName()) + } + } + + return nil +} + +// Unregister will unregister this instance from registry +func (e *etcdV3ServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + path := toPath(instance) + + if nil != e.client { + err := e.client.Delete(path) + e.services.Remove(instance.GetServiceName()) + e.serviceInstance = nil + return err + } + + return nil +} + +// ----------------- discovery ------------------- +// GetDefaultPageSize will return the default page size +func (e *etcdV3ServiceDiscovery) GetDefaultPageSize() int { + return registry.DefaultPageSize +} + +// GetServices will return the all service names. +func (e *etcdV3ServiceDiscovery) GetServices() *gxset.HashSet { + return e.services +} + +// GetInstances will return all service instances with serviceName +func (e *etcdV3ServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + + if nil != e.client { + // get keys and values + _, vList, err := e.client.GetChildrenKVList(toParentPath(serviceName)) + if nil == err { + serviceInstances := make([]registry.ServiceInstance, 0, len(vList)) + for _, v := range vList { + instance := ®istry.DefaultServiceInstance{} + err = jsonutil.DecodeJSON([]byte(v), &instance) + if nil == err { + serviceInstances = append(serviceInstances, instance) + } + } + return serviceInstances + } + perrors.New(fmt.Sprintf("could not getChildrenKVList the err is:%v", err)) + } + + return make([]registry.ServiceInstance, 0, 0) +} + +// GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName +// the page will start at offset +func (e *etcdV3ServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + + all := e.GetInstances(serviceName) + + res := make([]interface{}, 0, pageSize) + + for i := offset; i < len(all) && i < offset+pageSize; i++ { + res = append(res, all[i]) + } + + return gxpage.New(offset, pageSize, res, len(all)) +} + +// GetHealthyInstancesByPage will return a page containing instances of ServiceInstance. +// The param healthy indices that the instance should be healthy or not. +// The page will start at offset +func (e *etcdV3ServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { + all := e.GetInstances(serviceName) + res := make([]interface{}, 0, pageSize) + + var ( + i = offset + count = 0 + ) + for i < len(all) && count < pageSize { + ins := all[i] + if ins.IsHealthy() == healthy { + res = append(res, all[i]) + count++ + } + i++ + } + return gxpage.New(offset, pageSize, res, len(all)) +} + +// Batch get all instances by the specified service names +func (e *etcdV3ServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { + res := make(map[string]gxpage.Pager, len(serviceNames)) + for _, name := range serviceNames { + res[name] = e.GetInstancesByPage(name, offset, requestedSize) + } + return res +} + +// ----------------- event ---------------------- +// AddListener adds a new ServiceInstancesChangedListener +// see addServiceInstancesChangedListener in Java +func (e *etcdV3ServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + return e.registerSreviceWatcher(listener.ServiceName) +} + +// DispatchEventByServiceName dispatches the ServiceInstancesChangedEvent to service instance whose name is serviceName +func (e *etcdV3ServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + return e.DispatchEventForInstances(serviceName, e.GetInstances(serviceName)) +} + +// DispatchEventForInstances dispatches the ServiceInstancesChangedEvent to target instances +func (e *etcdV3ServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { + return e.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances)) +} + +// DispatchEvent dispatches the event +func (e *etcdV3ServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + extension.GetGlobalDispatcher().Dispatch(event) + return nil +} + +// Convert instance to dubbo path +func toPath(instance registry.ServiceInstance) string { + if instance == nil { + return "" + } + // like: /services/servicename1/host(127.0.0.1)/8080 + return fmt.Sprintf("%s%d", ROOT+constant.PATH_SEPARATOR+instance.GetServiceName()+constant.PATH_SEPARATOR+instance.GetHost()+constant.KEY_SEPARATOR, instance.GetPort()) +} + +// to dubbo service path +func toParentPath(serviceName string) string { + return ROOT + constant.PATH_SEPARATOR + serviceName +} + +// register service watcher +func (e *etcdV3ServiceDiscovery) registerSreviceWatcher(serviceName string) error { + + initLock.Lock() + defer initLock.Unlock() + + path := toParentPath(serviceName) + + listener, found := e.childListenerMap[serviceName] + + if !found { + listener = etcdv3.NewEventListener(e.client) + e.childListenerMap[serviceName] = listener + } + + listener.ListenServiceEvent(path, e) + + return nil +} + +// when child data change should DispatchEventByServiceName +func (e *etcdV3ServiceDiscovery) DataChange(eventType remoting.Event) bool { + + if eventType.Action == remoting.EventTypeUpdate { + instance := ®istry.DefaultServiceInstance{} + err := jsonutil.DecodeJSON([]byte(eventType.Content), &instance) + if err != nil { + instance.ServiceName = "" + } + + if err := e.DispatchEventByServiceName(instance.ServiceName); err != nil { + return false + } + } + + return true +} + +// netEcdv3ServiceDiscovery +func newEtcdV3ServiceDiscovery(name string) (registry.ServiceDiscovery, error) { + + initLock.Lock() + defer initLock.Unlock() + + sdc, ok := config.GetBaseConfig().GetServiceDiscoveries(name) + if !ok || len(sdc.RemoteRef) == 0 { + return nil, perrors.New("could not init the etcd service instance because the config is invalid") + } + + remoteConfig, ok := config.GetBaseConfig().GetRemoteConfig(sdc.RemoteRef) + if !ok { + return nil, perrors.New("could not find the remote config for name: " + sdc.RemoteRef) + } + + // init etcdv3 client + timeout, err := time.ParseDuration(remoteConfig.TimeoutStr) + if err != nil { + logger.Errorf("timeout config %v is invalid,err is %v", remoteConfig.TimeoutStr, err.Error()) + return nil, perrors.WithMessagef(err, "new etcd service discovery(address:%v)", remoteConfig.Address) + } + + logger.Infof("etcd address is: %v,timeout is:%s", remoteConfig.Address, timeout.String()) + + client := etcdv3.NewServiceDiscoveryClient( + etcdv3.WithName(etcdv3.RegistryETCDV3Client), + etcdv3.WithTimeout(timeout), + etcdv3.WithEndpoints(remoteConfig.Address), + ) + + descriptor := fmt.Sprintf("etcd-service-discovery[%s]", remoteConfig.Address) + + return &etcdV3ServiceDiscovery{descriptor, client, nil, gxset.NewSet(), make(map[string]*etcdv3.EventListener, 0)}, nil +} diff --git a/registry/etcdv3/service_discovery_test.go b/registry/etcdv3/service_discovery_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d8e3f1a2864150cc1f1e8996bc7c53e115dbef45 --- /dev/null +++ b/registry/etcdv3/service_discovery_test.go @@ -0,0 +1,80 @@ +/* + * 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 etcdv3 + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/registry" +) + +var testName = "test" + +func setUp() { + config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{ + Protocol: "etcdv3", + RemoteRef: testName, + } + + config.GetBaseConfig().Remotes[testName] = &config.RemoteConfig{ + Address: "localhost:2379", + TimeoutStr: "10s", + } +} + +func Test_newEtcdV3ServiceDiscovery(t *testing.T) { + name := constant.ETCDV3_KEY + _, err := newEtcdV3ServiceDiscovery(name) + + // warn: log configure file name is nil + assert.NotNil(t, err) + + sdc := &config.ServiceDiscoveryConfig{ + Protocol: "etcdv3", + RemoteRef: "mock", + } + config.GetBaseConfig().ServiceDiscoveries[name] = sdc + + _, err = newEtcdV3ServiceDiscovery(name) + + // RemoteConfig not found + assert.NotNil(t, err) + + config.GetBaseConfig().Remotes["mock"] = &config.RemoteConfig{ + Address: "localhost:2379", + TimeoutStr: "10s", + } + + res, err := newEtcdV3ServiceDiscovery(name) + assert.Nil(t, err) + assert.NotNil(t, res) +} + +func TestEtcdV3ServiceDiscovery_GetDefaultPageSize(t *testing.T) { + setUp() + serviceDiscovry := &etcdV3ServiceDiscovery{} + assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize()) +} diff --git a/registry/event.go b/registry/event.go index 6f647185cc213b80b9ab25b4702f91b36aa8ad4b..39fb00c740ef2e70e2cd6768fa4a4bb3226f832c 100644 --- a/registry/event.go +++ b/registry/event.go @@ -37,7 +37,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/customizable_service_instance_listener.go b/registry/event/customizable_service_instance_listener.go index 89d1621974e32a295f90e5f6c08ab17f3fc2e319..07e84c1454df91d2038beb08abddbc46274623c9 100644 --- a/registry/event/customizable_service_instance_listener.go +++ b/registry/event/customizable_service_instance_listener.go @@ -20,7 +20,9 @@ package event import ( "reflect" "sync" +) +import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/observer" ) @@ -56,9 +58,13 @@ func (c *customizableServiceInstanceListener) GetEventType() reflect.Type { return reflect.TypeOf(&ServiceInstancePreRegisteredEvent{}) } -var customizableServiceInstanceListenerInstance *customizableServiceInstanceListener -var customizableServiceInstanceListenerOnce sync.Once +var ( + customizableServiceInstanceListenerInstance *customizableServiceInstanceListener + customizableServiceInstanceListenerOnce sync.Once +) +// GetCustomizableServiceInstanceListener returns an instance +// if the instance was not initialized, we create one func GetCustomizableServiceInstanceListener() observer.EventListener { customizableServiceInstanceListenerOnce.Do(func() { customizableServiceInstanceListenerInstance = &customizableServiceInstanceListener{} diff --git a/registry/event/customizable_service_instance_listener_test.go b/registry/event/customizable_service_instance_listener_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1c81ece498b4864c3ea7f586d90052f3022627fc --- /dev/null +++ b/registry/event/customizable_service_instance_listener_test.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 event + +import ( + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/registry" +) + +func TestGetCustomizableServiceInstanceListener(t *testing.T) { + + prepareMetadataServiceForTest() + + cus := GetCustomizableServiceInstanceListener() + + assert.Equal(t, 9999, cus.GetPriority()) + + extension.AddCustomizers(&mockCustomizer{}) + + err := cus.OnEvent(&mockEvent{}) + assert.Nil(t, err) + err = cus.OnEvent(NewServiceInstancePreRegisteredEvent("hello", createInstance())) + assert.Nil(t, err) + + tp := cus.GetEventType() + assert.NotNil(t, tp) +} + +type mockEvent struct { +} + +func (m *mockEvent) String() string { + panic("implement me") +} + +func (m *mockEvent) GetSource() interface{} { + panic("implement me") +} + +func (m *mockEvent) GetTimestamp() time.Time { + panic("implement me") +} + +type mockCustomizer struct { +} + +func (m *mockCustomizer) GetPriority() int { + return 0 +} + +func (m *mockCustomizer) Customize(instance registry.ServiceInstance) { +} diff --git a/registry/event/event_publishing_service_deiscovery_test.go b/registry/event/event_publishing_service_deiscovery_test.go index ea6a97a32276f65e5a8b5aea920d7ba807fba0ab..8020702080f5158d6ba97ebb3077d86ff130d4e2 100644 --- a/registry/event/event_publishing_service_deiscovery_test.go +++ b/registry/event/event_publishing_service_deiscovery_test.go @@ -21,8 +21,7 @@ import ( "reflect" "testing" - "github.com/apache/dubbo-go/config" - _ "github.com/apache/dubbo-go/metadata/service/inmemory" + "github.com/apache/dubbo-go/metadata/mapping" ) import ( @@ -36,6 +35,8 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/observer" dispatcher2 "github.com/apache/dubbo-go/common/observer/dispatcher" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/metadata/service/inmemory" "github.com/apache/dubbo-go/registry" ) @@ -45,8 +46,14 @@ func TestEventPublishingServiceDiscovery_DispatchEvent(t *testing.T) { config.GetApplicationConfig().MetadataType = "local" + extension.SetGlobalServiceNameMapping(func() mapping.ServiceNameMapping { + return &mockServiceNameMapping{} + }) + dc := NewEventPublishingServiceDiscovery(&ServiceDiscoveryA{}) - tsd := &TestServiceDiscoveryDestroyingEventListener{} + tsd := &TestServiceDiscoveryDestroyingEventListener{ + BaseListener: observer.NewBaseListener(), + } tsd.SetT(t) tsi := &TestServiceInstancePreRegisteredEventListener{} tsi.SetT(t) @@ -68,7 +75,7 @@ func TestEventPublishingServiceDiscovery_DispatchEvent(t *testing.T) { type TestServiceDiscoveryDestroyingEventListener struct { suite.Suite - observer.BaseListenable + observer.BaseListener } func (tel *TestServiceDiscoveryDestroyingEventListener) OnEvent(e observer.Event) error { @@ -89,7 +96,7 @@ func (tel *TestServiceDiscoveryDestroyingEventListener) GetEventType() reflect.T type TestServiceInstancePreRegisteredEventListener struct { suite.Suite - observer.BaseListenable + observer.BaseListener } func (tel *TestServiceInstancePreRegisteredEventListener) OnEvent(e observer.Event) error { @@ -171,3 +178,14 @@ func (msd *ServiceDiscoveryA) DispatchEventForInstances(serviceName string, inst func (msd *ServiceDiscoveryA) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { return nil } + +type mockServiceNameMapping struct { +} + +func (m *mockServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + return nil +} + +func (m *mockServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + return gxset.NewSet("dubbo"), nil +} diff --git a/registry/event/event_publishing_service_discovery.go b/registry/event/event_publishing_service_discovery.go index 496eb9b4a51f1451fb4e4200325108d6dcd08b75..3ee2f4a44946065cdf7489abc391df41f251d810 100644 --- a/registry/event/event_publishing_service_discovery.go +++ b/registry/event/event_publishing_service_discovery.go @@ -20,14 +20,13 @@ package event import ( gxset "github.com/dubbogo/gost/container/set" gxpage "github.com/dubbogo/gost/page" - - "github.com/apache/dubbo-go/config" - "github.com/apache/dubbo-go/metadata/service" ) import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/observer" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/metadata/service" "github.com/apache/dubbo-go/registry" ) @@ -44,7 +43,7 @@ func NewEventPublishingServiceDiscovery(serviceDiscovery registry.ServiceDiscove } } -// String +// String returns serviceDiscovery.String() func (epsd *EventPublishingServiceDiscovery) String() string { return epsd.serviceDiscovery.String() } @@ -68,7 +67,7 @@ func (epsd *EventPublishingServiceDiscovery) Register(instance registry.ServiceI } -// Update delegate function +// Update returns the result of serviceDiscovery.Update func (epsd *EventPublishingServiceDiscovery) Update(instance registry.ServiceInstance) error { f := func() error { return epsd.serviceDiscovery.Update(instance) @@ -76,7 +75,7 @@ func (epsd *EventPublishingServiceDiscovery) Update(instance registry.ServiceIns return epsd.executeWithEvents(nil, f, nil) } -// Unregister delegate function +// Unregister unregister the instance and drop ServiceInstancePreUnregisteredEvent and ServiceInstanceUnregisteredEvent func (epsd *EventPublishingServiceDiscovery) Unregister(instance registry.ServiceInstance) error { f := func() error { return epsd.serviceDiscovery.Unregister(instance) @@ -85,26 +84,32 @@ func (epsd *EventPublishingServiceDiscovery) Unregister(instance registry.Servic f, NewServiceInstanceUnregisteredEvent(epsd.serviceDiscovery, instance)) } +// GetDefaultPageSize returns the result of serviceDiscovery.GetDefaultPageSize func (epsd *EventPublishingServiceDiscovery) GetDefaultPageSize() int { return epsd.serviceDiscovery.GetDefaultPageSize() } +// GetServices returns the result of serviceDiscovery.GetServices func (epsd *EventPublishingServiceDiscovery) GetServices() *gxset.HashSet { return epsd.serviceDiscovery.GetServices() } +// GetInstances returns the result of serviceDiscovery.GetInstances func (epsd *EventPublishingServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { return epsd.serviceDiscovery.GetInstances(serviceName) } +// GetInstancesByPage returns the result of serviceDiscovery.GetInstancesByPage func (epsd *EventPublishingServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { return epsd.serviceDiscovery.GetInstancesByPage(serviceName, offset, pageSize) } +// GetHealthyInstancesByPage returns the result of serviceDiscovery.GetHealthyInstancesByPage func (epsd *EventPublishingServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { return epsd.serviceDiscovery.GetHealthyInstancesByPage(serviceName, offset, pageSize, healthy) } +// GetRequestInstances returns result from serviceDiscovery.GetRequestInstances func (epsd *EventPublishingServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { return epsd.serviceDiscovery.GetRequestInstances(serviceNames, offset, requestedSize) } @@ -115,14 +120,17 @@ func (epsd *EventPublishingServiceDiscovery) AddListener(listener *registry.Serv return epsd.serviceDiscovery.AddListener(listener) } +// DispatchEventByServiceName pass serviceName to serviceDiscovery func (epsd *EventPublishingServiceDiscovery) DispatchEventByServiceName(serviceName string) error { - return epsd.DispatchEventByServiceName(serviceName) + return epsd.serviceDiscovery.DispatchEventByServiceName(serviceName) } +// DispatchEventForInstances pass params to serviceDiscovery func (epsd *EventPublishingServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { return epsd.serviceDiscovery.DispatchEventForInstances(serviceName, instances) } +// DispatchEvent pass the event to serviceDiscovery func (epsd *EventPublishingServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { return epsd.serviceDiscovery.DispatchEvent(event) } @@ -143,6 +151,7 @@ func (epsd *EventPublishingServiceDiscovery) executeWithEvents(beforeEvent obser return nil } +// getMetadataService returns metadata service instance func getMetadataService() (service.MetadataService, error) { return extension.GetMetadataService(config.GetApplicationConfig().MetadataType) } diff --git a/registry/event/log_event_listener.go b/registry/event/log_event_listener.go index a06d5e4499284c66017ebb7484e4ee46ad164f5d..0781a6d6db303ba3a1eb99b6b4c6d0743f9066b3 100644 --- a/registry/event/log_event_listener.go +++ b/registry/event/log_event_listener.go @@ -20,7 +20,9 @@ package event import ( "reflect" "sync" +) +import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/common/observer" diff --git a/registry/event/log_event_listener_test.go b/registry/event/log_event_listener_test.go index f142168b65759455a1c46e3aa8dbb537298443bf..3136564687f74e4c5cebd13d135e097234b21284 100644 --- a/registry/event/log_event_listener_test.go +++ b/registry/event/log_event_listener_test.go @@ -19,7 +19,9 @@ package event import ( "testing" +) +import ( "github.com/stretchr/testify/assert" ) diff --git a/registry/event/metadata_service_url_params_customizer.go b/registry/event/metadata_service_url_params_customizer.go index 06278f4e7793c8a0c36b9264ede896c1d6243cc8..6d8f99b327363c9a2d636079ef1f74e78d4e0184 100644 --- a/registry/event/metadata_service_url_params_customizer.go +++ b/registry/event/metadata_service_url_params_customizer.go @@ -19,9 +19,13 @@ package event import ( "encoding/json" +) +import ( gxset "github.com/dubbogo/gost/container/set" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" diff --git a/registry/event/metadata_service_url_params_customizer_test.go b/registry/event/metadata_service_url_params_customizer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..98ae2df883f590f4c3e4b379bb5a0fcbe46d946c --- /dev/null +++ b/registry/event/metadata_service_url_params_customizer_test.go @@ -0,0 +1,124 @@ +/* + * 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 event + +import ( + "testing" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/metadata/service" + "github.com/apache/dubbo-go/registry" +) + +func prepareMetadataServiceForTest() { + config.GetApplicationConfig().MetadataType = "mock" + extension.SetMetadataService("mock", func() (service.MetadataService, error) { + return &mockMetadataService{ + urls: []interface{}{"mock://localhost:8080?a=b"}, + }, nil + }) +} + +func TestMetadataServiceURLParamsMetadataCustomizer(t *testing.T) { + + prepareMetadataServiceForTest() + + msup := &metadataServiceURLParamsMetadataCustomizer{exceptKeys: gxset.NewSet()} + assert.Equal(t, 0, msup.GetPriority()) + + msup.Customize(createInstance()) +} + +func createInstance() registry.ServiceInstance { + ins := ®istry.DefaultServiceInstance{} + return ins +} + +type mockMetadataService struct { + urls []interface{} +} + +func (m *mockMetadataService) Reference() string { + panic("implement me") +} + +func (m *mockMetadataService) ServiceName() (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) UnexportURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { + return m.urls, nil +} + +func (m *mockMetadataService) MethodMapper() map[string]string { + panic("implement me") +} + +func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { + res := make([]common.URL, 0, len(m.urls)) + for _, ui := range m.urls { + u, _ := common.NewURL(ui.(string)) + res = append(res, u) + } + return res, nil +} + +func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) Version() (string, error) { + return "1.0.0", nil +} diff --git a/registry/event/protocol_ports_metadata_customizer.go b/registry/event/protocol_ports_metadata_customizer.go index bf16fa8e2fd0a9fc02f377b8ee8c2d8bcca69396..cf5d1a8ec1a097037eb7f45aafac72661d3243ad 100644 --- a/registry/event/protocol_ports_metadata_customizer.go +++ b/registry/event/protocol_ports_metadata_customizer.go @@ -20,7 +20,9 @@ package event import ( "encoding/json" "strconv" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -41,7 +43,7 @@ func (p *ProtocolPortsMetadataCustomizer) GetPriority() int { return 0 } -// Customize will +// Customize put the the string like [{"protocol": "dubbo", "port": 123}] into instance's metadata func (p *ProtocolPortsMetadataCustomizer) Customize(instance registry.ServiceInstance) { metadataService, err := getMetadataService() if err != nil { @@ -49,12 +51,12 @@ func (p *ProtocolPortsMetadataCustomizer) Customize(instance registry.ServiceIns return } - // 4 is enough... + // 4 is enough... we don't have many protocol protocolMap := make(map[string]int, 4) list, err := metadataService.GetExportedURLs(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) if err != nil || len(list) == 0 { - logger.Errorf("Could not find exported urls", err) + logger.Debugf("Could not find exported urls", err) return } @@ -75,6 +77,7 @@ func (p *ProtocolPortsMetadataCustomizer) Customize(instance registry.ServiceIns instance.GetMetadata()[constant.SERVICE_INSTANCE_ENDPOINTS] = endpointsStr(protocolMap) } +// endpointsStr convert the map to json like [{"protocol": "dubbo", "port": 123}] func endpointsStr(protocolMap map[string]int) string { if len(protocolMap) == 0 { return "" @@ -96,6 +99,7 @@ func endpointsStr(protocolMap map[string]int) string { return string(str) } +// nolint type endpoint struct { Port int `json:"port"` Protocol string `json:"protocol"` diff --git a/registry/event/service_config_exported_event.go b/registry/event/service_config_exported_event.go index 7946609acdffa0e166ffc3559bd931114fa2c5d5..5ec027da3178f7aba066cdb1d684798e611953ea 100644 --- a/registry/event/service_config_exported_event.go +++ b/registry/event/service_config_exported_event.go @@ -19,16 +19,20 @@ package event import ( "time" +) +import ( "github.com/apache/dubbo-go/common/observer" "github.com/apache/dubbo-go/config" ) +// ServiceConfigExportedEvent represents an service was exported type ServiceConfigExportedEvent struct { observer.BaseEvent ServiceConfig *config.ServiceConfig } +// NewServiceConfigExportedEvent create an instance func NewServiceConfigExportedEvent(serviceConfig *config.ServiceConfig) *ServiceConfigExportedEvent { return &ServiceConfigExportedEvent{ BaseEvent: observer.BaseEvent{ diff --git a/registry/event/service_discovery_event.go b/registry/event/service_discovery_event.go index 74f6c5f19dd4b4cfb5ceaae4010df1d49b03aa41..13afa1a6aa63a8ad0721692d7e969d3af882b8f5 100644 --- a/registry/event/service_discovery_event.go +++ b/registry/event/service_discovery_event.go @@ -22,11 +22,13 @@ import ( "github.com/apache/dubbo-go/registry" ) +// ServiceDiscoveryEvent means that something happens to service discovery instance type ServiceDiscoveryEvent struct { observer.BaseEvent original registry.ServiceDiscovery } +// NewServiceDiscoveryEvent returns an instance func NewServiceDiscoveryEvent(discovery registry.ServiceDiscovery, original registry.ServiceDiscovery) *ServiceDiscoveryEvent { return &ServiceDiscoveryEvent{ BaseEvent: *observer.NewBaseEvent(discovery), @@ -34,10 +36,12 @@ func NewServiceDiscoveryEvent(discovery registry.ServiceDiscovery, original regi } } +// GetServiceDiscovery returns the event source func (sde *ServiceDiscoveryEvent) GetServiceDiscovery() registry.ServiceDiscovery { return sde.GetSource().(registry.ServiceDiscovery) } +// GetOriginal actually I think we can remove this method. func (sde *ServiceDiscoveryEvent) GetOriginal() registry.ServiceDiscovery { return sde.original } diff --git a/registry/event/service_instance_event.go b/registry/event/service_instance_event.go index 650b2e8e29e23498a49f11b58aa53b018ca42e67..d4f23c299a844f4aab25e7d656a2cb99692861d7 100644 --- a/registry/event/service_instance_event.go +++ b/registry/event/service_instance_event.go @@ -22,6 +22,8 @@ import ( "github.com/apache/dubbo-go/registry" ) +// ServiceInstanceEvent means something happen to this ServiceInstance +// like register this service instance type ServiceInstanceEvent struct { observer.BaseEvent serviceInstance registry.ServiceInstance @@ -35,6 +37,7 @@ func NewServiceInstanceEvent(source interface{}, instance registry.ServiceInstan } } +// getServiceInstance return the service instance func (sie *ServiceInstanceEvent) getServiceInstance() registry.ServiceInstance { return sie.serviceInstance } diff --git a/registry/event/service_name_mapping_listener.go b/registry/event/service_name_mapping_listener.go index 68cf588c660d6106fbba8e45a896666a3ec3fe0a..a4ac8b28db5a3778cf39eef98886e1825521aa44 100644 --- a/registry/event/service_name_mapping_listener.go +++ b/registry/event/service_name_mapping_listener.go @@ -20,9 +20,13 @@ package event import ( "reflect" "sync" +) +import ( perrors "github.com/pkg/errors" +) +import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/observer" @@ -30,9 +34,12 @@ import ( ) func init() { - extension.AddEventListener(GetCustomizableServiceInstanceListener) + extension.AddEventListener(GetServiceNameMappingListener) } +// serviceNameMappingListener listen to service name mapping event +// usually it means that we exported some service +// it's a singleton type serviceNameMappingListener struct { nameMapping mapping.ServiceNameMapping } @@ -42,6 +49,7 @@ func (s *serviceNameMappingListener) GetPriority() int { return 3 } +// OnEvent only handle ServiceConfigExportedEvent func (s *serviceNameMappingListener) OnEvent(e observer.Event) error { if ex, ok := e.(*ServiceConfigExportedEvent); ok { sc := ex.ServiceConfig @@ -60,6 +68,7 @@ func (s *serviceNameMappingListener) OnEvent(e observer.Event) error { return nil } +// GetEventType return ServiceConfigExportedEvent func (s *serviceNameMappingListener) GetEventType() reflect.Type { return reflect.TypeOf(&ServiceConfigExportedEvent{}) } @@ -69,6 +78,7 @@ var ( serviceNameMappingListenerOnce sync.Once ) +// GetServiceNameMappingListener returns an instance func GetServiceNameMappingListener() observer.EventListener { serviceNameMappingListenerOnce.Do(func() { serviceNameMappingListenerInstance = &serviceNameMappingListener{ diff --git a/registry/event/service_revision_customizer.go b/registry/event/service_revision_customizer.go index fb1cda01a59537b9cfb850dacbbb661edeade428..fd21e8f4c7a71cedfe1de7e9c836e7cee278182e 100644 --- a/registry/event/service_revision_customizer.go +++ b/registry/event/service_revision_customizer.go @@ -21,7 +21,9 @@ import ( "fmt" "hash/crc32" "sort" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -45,6 +47,7 @@ func (e *exportedServicesRevisionMetadataCustomizer) GetPriority() int { return 1 } +// Customize calculate the revision for exported urls and then put it into instance metadata func (e *exportedServicesRevisionMetadataCustomizer) Customize(instance registry.ServiceInstance) { ms, err := getMetadataService() if err != nil { @@ -73,6 +76,7 @@ func (e *subscribedServicesRevisionMetadataCustomizer) GetPriority() int { return 2 } +// Customize calculate the revision for subscribed urls and then put it into instance metadata func (e *subscribedServicesRevisionMetadataCustomizer) Customize(instance registry.ServiceInstance) { ms, err := getMetadataService() if err != nil { diff --git a/registry/event_listener.go b/registry/event_listener.go index 1cd5ad43a66acc70c6a7938f8d6532346fd6410d..9e9ec2d5d4bcb8d1af90fff73db1c6708427f7f7 100644 --- a/registry/event_listener.go +++ b/registry/event_listener.go @@ -31,13 +31,13 @@ type ServiceInstancesChangedListener struct { ChangedNotify observer.ChangedNotify } -// On ServiceInstancesChangedEvent the service instances change event +// OnEvent on ServiceInstancesChangedEvent the service instances change event func (lstn *ServiceInstancesChangedListener) OnEvent(e observer.Event) error { lstn.ChangedNotify.Notify(e) return nil } -// return true if the name is the same +// Accept return true if the name is the same func (lstn *ServiceInstancesChangedListener) Accept(e observer.Event) bool { if ce, ok := e.(*ServiceInstancesChangedEvent); ok { return ce.ServiceName == lstn.ServiceName @@ -45,12 +45,12 @@ func (lstn *ServiceInstancesChangedListener) Accept(e observer.Event) bool { return false } -// get listener priority +// GetPriority returns -1, it will be the first invoked listener func (lstn *ServiceInstancesChangedListener) GetPriority() int { return -1 } -// get event type +// GetEventType returns ServiceInstancesChangedEvent func (lstn *ServiceInstancesChangedListener) GetEventType() reflect.Type { return reflect.TypeOf(&ServiceInstancesChangedEvent{}) } diff --git a/registry/inmemory/service_discovery.go b/registry/inmemory/service_discovery.go deleted file mode 100644 index f7c3ef3bb566e81587d3845c33ce7fb799b2cd43..0000000000000000000000000000000000000000 --- a/registry/inmemory/service_discovery.go +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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 inmemory - -import ( - "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/page" -) - -import ( - "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/registry" -) - -const ( - name = "in-memory" -) - -func init() { - - instance := &InMemoryServiceDiscovery{ - instances: make(map[string]registry.ServiceInstance, 4), - listeners: make([]*registry.ServiceInstancesChangedListener, 0, 2), - } - - extension.SetServiceDiscovery(name, func(name string) (discovery registry.ServiceDiscovery, err error) { - return instance, nil - }) -} - -// InMemoryServiceDiscovery is an implementation based on memory. -// Usually you will not use this implementation except for tests. -type InMemoryServiceDiscovery struct { - instances map[string]registry.ServiceInstance - listeners []*registry.ServiceInstancesChangedListener -} - -func (i *InMemoryServiceDiscovery) String() string { - return name -} - -// Destroy doesn't destroy the instance, it just clear the instances -func (i *InMemoryServiceDiscovery) Destroy() error { - // reset to empty - i.instances = make(map[string]registry.ServiceInstance, 4) - i.listeners = make([]*registry.ServiceInstancesChangedListener, 0, 2) - return nil -} - -// Register will store the instance using its id as key -func (i *InMemoryServiceDiscovery) Register(instance registry.ServiceInstance) error { - i.instances[instance.GetId()] = instance - return nil -} - -// Update will act like register -func (i *InMemoryServiceDiscovery) Update(instance registry.ServiceInstance) error { - return i.Register(instance) -} - -// Unregister will remove the instance -func (i *InMemoryServiceDiscovery) Unregister(instance registry.ServiceInstance) error { - delete(i.instances, instance.GetId()) - return nil -} - -// GetDefaultPageSize will return the default page size -func (i *InMemoryServiceDiscovery) GetDefaultPageSize() int { - return registry.DefaultPageSize -} - -// GetServices will return all service names -func (i *InMemoryServiceDiscovery) GetServices() *gxset.HashSet { - result := gxset.NewSet() - for _, value := range i.instances { - result.Add(value.GetServiceName()) - } - return result -} - -// GetInstances will find out all instances with serviceName -func (i *InMemoryServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { - result := make([]registry.ServiceInstance, 0, len(i.instances)) - for _, value := range i.instances { - if value.GetServiceName() == serviceName { - result = append(result, value) - } - } - return result -} - -// GetInstancesByPage will return the part of instances -func (i *InMemoryServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { - instances := i.GetInstances(serviceName) - // we can not use []registry.ServiceInstance since New(...) received []interface{} as parameter - result := make([]interface{}, 0, pageSize) - for i := offset; i < len(instances) && i < offset+pageSize; i++ { - result = append(result, instances[i]) - } - return gxpage.New(offset, pageSize, result, len(instances)) -} - -// GetHealthyInstancesByPage will return the instances -func (i *InMemoryServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { - instances := i.GetInstances(serviceName) - // we can not use []registry.ServiceInstance since New(...) received []interface{} as parameter - result := make([]interface{}, 0, pageSize) - count := 0 - for i := offset; i < len(instances) && count < pageSize; i++ { - if instances[i].IsHealthy() == healthy { - result = append(result, instances[i]) - count++ - } - } - return gxpage.New(offset, pageSize, result, len(instances)) -} - -// GetRequestInstances will iterate the serviceName and aggregate them -func (i *InMemoryServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { - res := make(map[string]gxpage.Pager, len(serviceNames)) - for _, name := range serviceNames { - res[name] = i.GetInstancesByPage(name, offset, requestedSize) - } - return res -} - -// AddListener will save the listener inside the memory -func (i *InMemoryServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { - i.listeners = append(i.listeners, listener) - return nil -} - -// DispatchEventByServiceName will do nothing -func (i *InMemoryServiceDiscovery) DispatchEventByServiceName(serviceName string) error { - return nil -} - -// DispatchEventForInstances will do nothing -func (i *InMemoryServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { - return nil -} - -// DispatchEvent will do nothing -func (i *InMemoryServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { - return nil -} diff --git a/registry/inmemory/service_discovery_test.go b/registry/inmemory/service_discovery_test.go deleted file mode 100644 index fac4699913000c44a566e6a84f850150046f8ce0..0000000000000000000000000000000000000000 --- a/registry/inmemory/service_discovery_test.go +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 inmemory - -import ( - "testing" -) - -import ( - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/registry" -) - -func TestInMemoryServiceDiscovery(t *testing.T) { - discovery, _ := extension.GetServiceDiscovery(name, "in") - serviceName := "my-service" - err := discovery.Register(®istry.DefaultServiceInstance{ - ServiceName: serviceName, - Id: "1", - Healthy: true, - }) - assert.Nil(t, err) - - err = discovery.Register(®istry.DefaultServiceInstance{ - Id: "2", - ServiceName: "mock-service", - Healthy: false, - }) - - assert.Nil(t, err) - - services := discovery.GetServices() - assert.Equal(t, 2, services.Size()) - assert.Equal(t, registry.DefaultPageSize, discovery.GetDefaultPageSize()) - - reqInstances := discovery.GetRequestInstances([]string{serviceName, "mock-service"}, 0, 10) - assert.Equal(t, 2, len(reqInstances)) - - page := discovery.GetInstancesByPage(serviceName, 0, 10) - assert.Equal(t, 1, page.GetDataSize()) - - discovery.GetHealthyInstancesByPage(serviceName, 0, 10, true) - page = discovery.GetInstancesByPage(serviceName, 0, 10) - assert.Equal(t, 1, page.GetDataSize()) - - err = discovery.AddListener(®istry.ServiceInstancesChangedListener{}) - assert.Nil(t, err) - - err = discovery.DispatchEvent(®istry.ServiceInstancesChangedEvent{}) - assert.Nil(t, err) - - err = discovery.DispatchEventForInstances(serviceName, nil) - assert.Nil(t, err) - - err = discovery.DispatchEventByServiceName(serviceName) - assert.Nil(t, err) - - err = discovery.Unregister(®istry.DefaultServiceInstance{ - Id: "2", - }) - assert.Nil(t, err) - - services = discovery.GetServices() - assert.Equal(t, 1, services.Size()) - - err = discovery.Update(®istry.DefaultServiceInstance{ - Id: "3", - }) - assert.Nil(t, err) - - services = discovery.GetServices() - assert.Equal(t, 2, services.Size()) - - err = discovery.Destroy() - assert.Nil(t, err) - - services = discovery.GetServices() - assert.Equal(t, 0, services.Size()) -} 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 1c9d8bdd5e0b713d61764163eff3b9fd3d5f320a..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,66 +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") @@ -252,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 7ee0f6b0eeb83181bfd20e1abe4685e8319cd09b..7c5162670d85f661fb8460cc69537ac9b7a12a23 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,6 +109,7 @@ 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), "") } @@ -111,6 +118,7 @@ func (r *kubernetesRegistry) DoUnregister(root string, node string) error { return perrors.New("DoUnregister is not support in kubernetesRegistry") } +// DoSubscribe actually subscribe the provider URL func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, error) { var ( @@ -143,10 +151,12 @@ func (r *kubernetesRegistry) DoSubscribe(svc *common.URL) (registry.Listener, er return configListener, nil } +// nolint func (r *kubernetesRegistry) DoUnsubscribe(conf *common.URL) (registry.Listener, error) { return nil, perrors.New("DoUnsubscribe is not support in kubernetesRegistry") } +// InitListeners init listeners of kubernetes registry center func (r *kubernetesRegistry) InitListeners() { r.listener = kubernetes.NewEventListener(r.client) r.configListener = NewConfigurationListener(r) @@ -168,15 +178,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 @@ -184,7 +193,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") } @@ -192,6 +201,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 a650b189c39b94849dee4fbf7fc292e33e87b829..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 { + + 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] + + env := map[string]string{ + nameSpaceKey: currentPod.GetNamespace(), + podNameKey: currentPod.GetName(), + needWatchedNameSpaceKey: "default", + } - t := s.T() + for k, v := range env { + if err := os.Setenv(k, v); err != nil { + t.Fatal(err) + } + } - r := s.initRegistry() + 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() { + + 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() { +func TestConsumerDestroy(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"), @@ -105,11 +314,9 @@ func (s *KubernetesRegistryTestSuite) TestConsumerDestroy() { } -func (s *KubernetesRegistryTestSuite) TestProviderDestroy() { - - t := s.T() +func TestProviderDestroy(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"), @@ -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 f39490a26755a96aab1438d965bd8ee6fc75006f..10561d0f49e995c94c93fa0463fc0b0421ff6e20 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,28 +46,28 @@ 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 } -// UnRegister +// nolint func (r *MockRegistry) UnRegister(conf 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{} } @@ -76,7 +76,7 @@ func (r *MockRegistry) subscribe(*common.URL) (Listener, error) { return r.listener, nil } -// Subscribe ... +// nolint func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) error { go func() { for { @@ -134,7 +134,7 @@ func (*listener) Close() { } -// MockEvent ... +// nolint func (r *MockRegistry) MockEvent(event *ServiceEvent) { r.listener.listenChan <- event } 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 2b6cab45c2f5b552738bce3a352e774aa4b8cbcd..51d3e2f56abac8e4ab8b966870f1ff5bb79c4171 100644 --- a/registry/nacos/registry.go +++ b/registry/nacos/registry.go @@ -23,14 +23,13 @@ import ( "strconv" "strings" "time" - - "github.com/nacos-group/nacos-sdk-go/clients" - "github.com/nacos-group/nacos-sdk-go/clients/naming_client" - nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" ) import ( gxnet "github.com/dubbogo/gost/net" + "github.com/nacos-group/nacos-sdk-go/clients" + "github.com/nacos-group/nacos-sdk-go/clients/naming_client" + nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" "github.com/nacos-group/nacos-sdk-go/vo" perrors "github.com/pkg/errors" ) @@ -118,6 +117,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) @@ -180,15 +180,18 @@ func (nr *nacosRegistry) UnSubscribe(url *common.URL, notifyListener registry.No return perrors.New("UnSubscribe not support in nacosRegistry") } +// 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 { // TODO return true } +// nolint func (nr *nacosRegistry) Destroy() { return } diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go index 5b952e2541d4fd61e247543195786f1141ae2a85..63d92d70fd5e1a00f0ce1ca95b1926fb9c36c84b 100644 --- a/registry/nacos/service_discovery.go +++ b/registry/nacos/service_discovery.go @@ -20,23 +20,24 @@ package nacos import ( "fmt" "sync" +) +import ( "github.com/dubbogo/gost/container/set" "github.com/dubbogo/gost/page" "github.com/nacos-group/nacos-sdk-go/clients/naming_client" "github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/vo" perrors "github.com/pkg/errors" - - "github.com/apache/dubbo-go/config" - "github.com/apache/dubbo-go/remoting/nacos" ) import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/registry" + "github.com/apache/dubbo-go/remoting/nacos" ) const ( diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go index 633a1d41c81ed88b40db3a637333e6795abed871..720c44a6f98e4693bb2395a538b2f5e679196647 100644 --- a/registry/nacos/service_discovery_test.go +++ b/registry/nacos/service_discovery_test.go @@ -19,8 +19,6 @@ package nacos import ( "testing" - - "github.com/apache/dubbo-go/config" ) import ( @@ -32,6 +30,7 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/observer" "github.com/apache/dubbo-go/common/observer/dispatcher" + "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/registry" ) diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index a936db80bf2c3b46ba389142cc40686ed3df17b1..4c669b2cee74b95ceb3bc8287f145ccd6b99bc0b 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -117,6 +117,7 @@ func (proto *registryProtocol) initConfigurationListeners() { 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 @@ -156,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() @@ -229,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)) @@ -325,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() @@ -389,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()) @@ -411,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 { @@ -435,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/registry.go b/registry/registry.go index ce214c4971b0b00b5e300542ad9172331a0b3d02..bb09ead7ef2af6707345086f8695b35286d76a10 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -58,13 +58,16 @@ type Registry interface { UnSubscribe(*common.URL, NotifyListener) error } -// 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..cb7a3c0182ff88995ab9dd6c920523225c3cb36c 100644 --- a/registry/service_discovery.go +++ b/registry/service_discovery.go @@ -28,6 +28,7 @@ import ( 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 08ca79ecbf5ae96832516335f7d55fdfb2996938..dbb458284d48aa350f2d5d3408b187b437ac81cd 100644 --- a/registry/service_instance.go +++ b/registry/service_instance.go @@ -21,6 +21,7 @@ import ( gxsort "github.com/dubbogo/gost/sort" ) +// 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/servicediscovery/service_discovery_registry.go b/registry/servicediscovery/service_discovery_registry.go index 41054824a24ab68fe2d18ac47e03d0e6532aae0f..d6cce32f929ad7a4558e3af099a0dd3dbcd779af 100644 --- a/registry/servicediscovery/service_discovery_registry.go +++ b/registry/servicediscovery/service_discovery_registry.go @@ -23,12 +23,17 @@ import ( "strconv" "strings" "sync" +) +import ( cm "github.com/Workiva/go-datastructures/common" gxset "github.com/dubbogo/gost/container/set" gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" + "go.uber.org/atomic" +) +import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -177,10 +182,16 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error { if err != nil { return perrors.WithMessage(err, "could not create servcie instance, please check your service url") } + err = s.serviceDiscovery.Register(ins) if err != nil { return perrors.WithMessage(err, "register the service failed") } + + err = s.metaDataService.PublishServiceDefinition(url) + if err != nil { + return perrors.WithMessage(err, "publish the service definition failed. ") + } return s.serviceNameMapping.Map(url.GetParam(constant.INTERFACE_KEY, ""), url.GetParam(constant.GROUP_KEY, ""), url.GetParam(constant.Version, ""), @@ -668,8 +679,12 @@ func (icn *InstanceChangeNotify) Notify(event observer.Event) { } } -var exporting = false +var ( + exporting = &atomic.Bool{} +) +// tryInitMetadataService will try to initialize metadata service +// TODO (move to somewhere) func tryInitMetadataService() { ms, err := extension.GetMetadataService(config.GetApplicationConfig().MetadataType) @@ -677,11 +692,16 @@ func tryInitMetadataService() { logger.Errorf("could not init metadata service", err) } - if !config.IsProvider() || exporting { + if !config.IsProvider() || exporting.Load() { return } - exporting = true + // In theory, we can use sync.Once + // But sync.Once is not reentrant. + // Now the invocation chain is createRegistry -> tryInitMetadataService -> metadataServiceExporter.export + // -> createRegistry -> initMetadataService... + // So using sync.Once will result in dead lock + exporting.Store(true) expt := configurable.NewMetadataServiceExporter(ms) diff --git a/registry/servicediscovery/service_discovery_registry_test.go b/registry/servicediscovery/service_discovery_registry_test.go index 247cfd65eb0de5e85a4c046dbda844b2606bc3ac..53eb86507e635be32eb362519922f7042f945519 100644 --- a/registry/servicediscovery/service_discovery_registry_test.go +++ b/registry/servicediscovery/service_discovery_registry_test.go @@ -21,29 +21,226 @@ import ( "testing" ) +import ( + "github.com/dubbogo/gost/container/set" + "github.com/dubbogo/gost/page" + "github.com/stretchr/testify/assert" +) +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/observer" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/metadata/mapping" + "github.com/apache/dubbo-go/metadata/service" + "github.com/apache/dubbo-go/registry" +) + var ( - SERVICE_INTERFACE = "org.apache.dubbo.metadata.MetadataService" - GROUP = "dubbo-provider" - VERSION = "1.0.0" + serviceInterface = "org.apache.dubbo.metadata.MetadataService" + group = "dubbo-provider" + version = "1.0.0" ) func TestServiceDiscoveryRegistry_Register(t *testing.T) { - // registryURL,_:=event.NewURL("in-memory://localhost:12345", - // event.WithParamsValue("registry-type","service"), - // event.WithParamsValue("subscribed-services","a, b , c,d,e ,")) - // url,_:=event.NewURL("dubbo://192.168.0.102:20880/"+ SERVICE_INTERFACE + - // "?&application=" + GROUP + - // "&interface=" + SERVICE_INTERFACE + - // "&group=" + GROUP + - // "&version=" + VERSION + - // "&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs" + - // "&side=provider") - //registry,err:=newServiceDiscoveryRegistry(®istryURL) - //if err!=nil{ - // logger.Errorf("create service discovery registry catch error:%s",err.Error()) - //} - //assert.Nil(t,err) - //assert.NotNil(t,registry) - //registry.Register(url) + config.GetApplicationConfig().MetadataType = "mock" + extension.SetMetadataService("mock", func() (service service.MetadataService, err error) { + service = &mockMetadataService{} + return + }) + + extension.SetServiceDiscovery("mock", func(name string) (discovery registry.ServiceDiscovery, err error) { + return &mockServiceDiscovery{}, nil + }) + + extension.SetGlobalServiceNameMapping(func() mapping.ServiceNameMapping { + return &mockServiceNameMapping{} + }) + + extension.SetEventDispatcher("mock", func() observer.EventDispatcher { + return &mockEventDispatcher{} + }) + extension.SetAndInitGlobalDispatcher("mock") + + config.GetBaseConfig().ServiceDiscoveries["mock"] = &config.ServiceDiscoveryConfig{ + Protocol: "mock", + } + registryURL, _ := common.NewURL("service-discovery://localhost:12345", + common.WithParamsValue("service_discovery", "mock"), + common.WithParamsValue("subscribed-services", "a, b , c,d,e ,")) + url, _ := common.NewURL("dubbo://192.168.0.102:20880/" + serviceInterface + + "?&application=" + group + + "&interface=" + serviceInterface + + "&group=" + group + + "&version=" + version + + "&service_discovery=mock" + + "&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs" + + "&side=provider") + registry, err := newServiceDiscoveryRegistry(®istryURL) + assert.Nil(t, err) + assert.NotNil(t, registry) + registry.Register(url) +} + +type mockEventDispatcher struct { +} + +func (m *mockEventDispatcher) AddEventListener(listener observer.EventListener) { + +} + +func (m *mockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) { + +} + +func (m *mockEventDispatcher) RemoveEventListener(listener observer.EventListener) { + panic("implement me") +} + +func (m *mockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) { + panic("implement me") +} + +func (m *mockEventDispatcher) GetAllEventListeners() []observer.EventListener { + return []observer.EventListener{} +} + +func (m *mockEventDispatcher) RemoveAllEventListeners() { + panic("implement me") +} + +func (m *mockEventDispatcher) Dispatch(event observer.Event) { +} + +type mockServiceNameMapping struct { +} + +func (m *mockServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { + return nil +} + +func (m *mockServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { + panic("implement me") +} + +type mockServiceDiscovery struct { +} + +func (m *mockServiceDiscovery) String() string { + panic("implement me") +} + +func (m *mockServiceDiscovery) Destroy() error { + panic("implement me") +} + +func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error { + return nil +} + +func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetDefaultPageSize() int { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetServices() *gxset.HashSet { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { + panic("implement me") +} + +func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { + panic("implement me") +} + +func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + panic("implement me") +} + +type mockMetadataService struct { +} + +func (m *mockMetadataService) Reference() string { + panic("implement me") +} + +func (m *mockMetadataService) ServiceName() (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { + return true, nil +} + +func (m *mockMetadataService) UnexportURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { + panic("implement me") +} + +func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { + panic("implement me") +} + +func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { + return nil +} + +func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { + panic("implement me") +} + +func (m *mockMetadataService) MethodMapper() map[string]string { + panic("implement me") +} + +func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { + panic("implement me") +} + +func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { + panic("implement me") +} + +func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { + panic("implement me") +} +func (m *mockMetadataService) Version() (string, error) { + panic("implement me") } diff --git a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go index a7d2f3ee98f20bf16437008135e4964195680abb..949a5822c237de413b59d35efe94f807975795cf 100644 --- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go +++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go @@ -23,8 +23,8 @@ import ( ) type SubscribedURLsSynthesizer interface { - //Supports the synthesis of the subscribed url or not + // Supports the synthesis of the subscribed url or not Support(subscribedURL *common.URL) bool - //synthesize the subscribed url + // synthesize the subscribed url Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL } diff --git a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer_factory.go b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer_factory.go index f8c76f6e84eb2ceba47481d5f856f6885525f09c..ba7887223c4553a368f2f698bbb861ba8e10fe26 100644 --- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer_factory.go +++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer_factory.go @@ -21,10 +21,12 @@ var ( synthesizers []SubscribedURLsSynthesizer ) +// nolint func AddSynthesizer(synthesizer SubscribedURLsSynthesizer) { synthesizers = append(synthesizers, synthesizer) } +// nolint func GetAllSynthesizer() []SubscribedURLsSynthesizer { return synthesizers } diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go index 5d5f9e0526b7b8a9c5a2e2524f27f03573d758a8..8f2ac1023b8ad34938b9996b480e3bbc4adbaaea 100644 --- a/registry/zookeeper/registry.go +++ b/registry/zookeeper/registry.go @@ -87,12 +87,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) { @@ -117,6 +117,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() @@ -147,10 +148,12 @@ 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) } @@ -164,6 +167,7 @@ func (r *zkRegistry) DoUnregister(root string, node string) error { return r.ZkClient().Delete(path.Join(root, node)) } +// DoSubscribe actually subscribes the provider URL func (r *zkRegistry) DoSubscribe(conf *common.URL) (registry.Listener, error) { return r.getListener(conf) } @@ -172,23 +176,28 @@ func (r *zkRegistry) DoUnsubscribe(conf *common.URL) (registry.Listener, error) return r.getCloseListener(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..080c9411442b4cbb9701e00152f3a03f562d0d05 100644 --- a/remoting/etcdv3/client.go +++ b/remoting/etcdv3/client.go @@ -19,7 +19,6 @@ package etcdv3 import ( "context" - "path" "sync" "time" ) @@ -42,16 +41,17 @@ const ( MaxFailTimes = 15 // RegistryETCDV3Client client name RegistryETCDV3Client = "etcd registry" + // metadataETCDV3Client client name + MetadataETCDV3Client = "etcd metadata" ) 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 +60,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{ @@ -107,7 +107,7 @@ func ValidateClient(container clientFacade, opts ...Option) error { // new Client if container.Client() == nil { - newClient, err := newClient(options.name, options.endpoints, options.timeout, options.heartbeat) + newClient, err := NewClient(options.name, options.endpoints, options.timeout, options.heartbeat) if err != nil { logger.Warnf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}", options.name, options.endpoints, options.timeout, err) @@ -119,7 +119,7 @@ func ValidateClient(container clientFacade, opts ...Option) error { // Client lose connection with etcd server if container.Client().rawClient == nil { - newClient, err := newClient(options.name, options.endpoints, options.timeout, options.heartbeat) + newClient, err := NewClient(options.name, options.endpoints, options.timeout, options.heartbeat) if err != nil { logger.Warnf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}", options.name, options.endpoints, options.timeout, err) @@ -131,7 +131,27 @@ func ValidateClient(container clientFacade, opts ...Option) error { return nil } -// Client ... +// NewServiceDiscoveryClient +func NewServiceDiscoveryClient(opts ...Option) *Client { + options := &Options{ + heartbeat: 1, // default heartbeat + } + + for _, opt := range opts { + opt(options) + } + + newClient, err := NewClient(options.name, options.endpoints, options.timeout, options.heartbeat) + if err != nil { + logger.Errorf("new etcd client (name{%s}, etcd addresses{%v}, timeout{%d}) = error{%v}", + options.name, options.endpoints, options.timeout, err) + return nil + } + + return newClient +} + +// Client represents etcd client Configuration type Client struct { lock sync.RWMutex @@ -142,14 +162,14 @@ 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{} Wait sync.WaitGroup } -func newClient(name string, endpoints []string, timeout time.Duration, heartbeat int) (*Client, error) { +func NewClient(name string, endpoints []string, timeout time.Duration, heartbeat int) (*Client, error) { ctx, cancel := context.WithCancel(context.Background()) rawClient, err := clientv3.New(clientv3.Config{ @@ -206,7 +226,7 @@ func (c *Client) stop() bool { return false } -// Close ... +// nolint func (c *Client) Close() { if c == nil { @@ -265,8 +285,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() @@ -287,6 +306,28 @@ func (c *Client) put(k string, v string, opts ...clientv3.OpOption) error { return nil } +// if k not exist will put k/v in etcd +// if k is already exist in etcd, replace it +func (c *Client) update(k string, v string, opts ...clientv3.OpOption) error { + + c.lock.RLock() + defer c.lock.RUnlock() + + if c.rawClient == nil { + return ErrNilETCDV3Client + } + + _, err := c.rawClient.Txn(c.ctx). + If(clientv3.Compare(clientv3.Version(k), "!=", -1)). + Then(clientv3.OpPut(k, v, opts...)). + Commit() + if err != nil { + return err + + } + return nil +} + func (c *Client) delete(k string) error { c.lock.RLock() @@ -325,7 +366,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 +466,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 +488,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 +498,16 @@ func (c *Client) Create(k string, v string) error { return nil } -// Delete ... +// Update key value ... +func (c *Client) Update(k, v string) error { + err := c.update(k, v) + if err != nil { + return perrors.WithMessagef(err, "Update k/v (key: %s value %s)", k, v) + } + return nil +} + +// nolint func (c *Client) Delete(k string) error { err := c.delete(k) @@ -468,20 +518,18 @@ func (c *Client) Delete(k string) error { return nil } -// RegisterTemp ... -func (c *Client) RegisterTemp(basePath string, node string) (string, error) { +// RegisterTemp registers a temporary node +func (c *Client) RegisterTemp(k, v string) error { - completeKey := path.Join(basePath, node) - - err := c.keepAliveKV(completeKey, "") + err := c.keepAliveKV(k, v) if err != nil { - return "", perrors.WithMessagef(err, "keepalive kv (key %s)", completeKey) + return perrors.WithMessagef(err, "keepalive kv (key %s)", k) } - return completeKey, nil + return 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 +539,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 +550,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 +560,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 e37b6383df55f1c7e7b64be62fc2eb22d1034616..a321eea9ec22e41b807dd9e6912547d6ca5a084e 100644 --- a/remoting/etcdv3/client_test.go +++ b/remoting/etcdv3/client_test.go @@ -120,7 +120,7 @@ func (suite *ClientTestSuite) TearDownSuite() { } func (suite *ClientTestSuite) setUpClient() *Client { - c, err := newClient(suite.etcdConfig.name, + c, err := NewClient(suite.etcdConfig.name, suite.etcdConfig.endpoints, suite.etcdConfig.timeout, suite.etcdConfig.heartbeat) @@ -384,7 +384,7 @@ func (suite *ClientTestSuite) TestClientRegisterTemp() { assert.Contains(t, events, eDelete) }() - _, err := c.RegisterTemp("scott", "wang") + err := c.RegisterTemp("scott/wang", "test") if err != nil { t.Fatal(err) } diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go index 35befc85e449ec02a6377faec300aa6b46bcc8bf..3f5999fdf3c5a0791d780e8f5521ef3ea51e9372 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 ( diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go index c65c4d127c691f5f8e30cae5b612e1a9920e7887..4f80a89dfb713036a5d4d812bc7a2d5551f42284 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..e116c48b1aa85b9b2331045dfbc42891aee1f2e1 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,220 +59,203 @@ var tests = []struct { // test dataset prefix const prefix = "name" -var clientPodJsonData = `{ +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 { @@ -290,29 +268,24 @@ func (s *KubernetesClientTestSuite) TestClientValid() { } } -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") + 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 +300,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{} @@ -407,11 +378,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) @@ -452,22 +421,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) @@ -507,7 +463,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..4323c0ec565d64f445f15c3e5c265451b2b87ce7 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,31 @@ 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() { +} - 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/nacos/builder.go b/remoting/nacos/builder.go index 545a1e268cabe5cef829ff1cf44ef40b1161d590..4319627c6da6a5a19874f188d9eba6b032645ff1 100644 --- a/remoting/nacos/builder.go +++ b/remoting/nacos/builder.go @@ -22,8 +22,6 @@ import ( "strconv" "strings" "time" - - "github.com/apache/dubbo-go/config" ) import ( @@ -37,16 +35,10 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" ) -func NewNacosNamingClient(url *common.URL) (naming_client.INamingClient, error) { - nacosConfig, err := getNacosConfig(url) - if err != nil { - return nil, err - } - return clients.CreateNamingClient(nacosConfig) -} - +// NewNacosConfigClient read the config from url and build an instance func NewNacosConfigClient(url *common.URL) (config_client.IConfigClient, error) { nacosConfig, err := getNacosConfig(url) if err != nil { @@ -102,6 +94,7 @@ func getNacosConfig(url *common.URL) (map[string]interface{}, error) { return configMap, nil } +// NewNacosClient creates an instance with the config func NewNacosClient(rc *config.RemoteConfig) (naming_client.INamingClient, error) { if len(rc.Address) == 0 { return nil, perrors.New("nacos address is empty!") diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index f2d4f40106b69f90f6abd7ff11fe8a3b931fdefe..349faff2b07f2b2b6c7a086c1730f996b06ee63c 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -47,7 +47,7 @@ var ( errNilNode = perrors.Errorf("node does not exist") ) -// ZookeeperClient ... +// ZookeeperClient represents zookeeper client Configuration type ZookeeperClient struct { name string ZkAddrs []string @@ -61,7 +61,7 @@ type ZookeeperClient struct { eventRegistryLock sync.RWMutex } -// StateToString ... +// nolint func StateToString(state zk.State) string { switch state { case zk.StateDisconnected: @@ -91,7 +91,7 @@ func StateToString(state zk.State) string { } } -// Options ... +// nolint type Options struct { zkName string client *ZookeeperClient @@ -99,17 +99,17 @@ 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 @@ -190,14 +190,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 @@ -237,7 +237,7 @@ func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) return ts, z, event, nil } -// HandleZkEvent ... +// HandleZkEvent handles zookeeper events func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) { var ( state int @@ -298,7 +298,7 @@ func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) { } } -// RegisterEvent ... +// RegisterEvent registers zookeeper events func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" || event == nil { return @@ -312,7 +312,7 @@ func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { logger.Debugf("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event) } -// UnregisterEvent ... +// UnregisterEvent unregisters zookeeper events func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" { return @@ -339,7 +339,7 @@ func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { } } -// Done ... +// nolint func (z *ZookeeperClient) Done() <-chan struct{} { return z.exit } @@ -355,7 +355,7 @@ func (z *ZookeeperClient) stop() bool { return false } -// ZkConnValid ... +// ZkConnValid validates zookeeper connection func (z *ZookeeperClient) ZkConnValid() bool { select { case <-z.exit: @@ -373,7 +373,7 @@ func (z *ZookeeperClient) ZkConnValid() bool { return valid } -// Close ... +// nolint func (z *ZookeeperClient) Close() { if z == nil { return @@ -432,7 +432,7 @@ func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { return nil } -// Delete ... +// nolint func (z *ZookeeperClient) Delete(basePath string) error { err := errNilZkClientConn conn := z.getConn() @@ -443,7 +443,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 @@ -467,7 +467,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 @@ -496,7 +496,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 @@ -531,7 +531,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 @@ -562,7 +562,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 @@ -588,7 +588,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 34741700ca2a9d86ee5321b0b19ed64b2b1a25a8..1b83a8f6fa793564684c6ab02aa9a8bfc43548c6 100644 --- a/remoting/zookeeper/client_test.go +++ b/remoting/zookeeper/client_test.go @@ -111,7 +111,8 @@ func TestCreateDelete(t *testing.T) { assert.NoError(t, err) err = z.Delete("/test1/test2/test3/test4") assert.NoError(t, err) - // verifyEventOrder(t, event, []zk.EventType{zk.EventNodeCreated}, "event channel") + err2 := z.Delete("/test1/test2/test3/test4") + assert.NoError(t, err2) } func TestRegisterTemp(t *testing.T) { diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index 504395f76e6cfe26db35cc4b4336291133ee2709..5b8d19ea60932d6e8765c520e38a4eb3e8a996c3 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -40,7 +40,7 @@ type ZkClientFacade interface { GetUrl() common.URL } -// HandleClientRestart ... +// HandleClientRestart keeps the connection between client and server func HandleClientRestart(r ZkClientFacade) { var ( err error diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index fd7f0db3192b97cd225812ea6d76963f190cc21c..d47cafc8d14306fa72bd968f9564d5a5c38505c9 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -37,7 +37,7 @@ import ( "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,7 +53,7 @@ func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { } } -// SetClient ... +// nolint func (l *ZkEventListener) SetClient(client *ZookeeperClient) { l.client = client } @@ -70,7 +70,7 @@ func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener remotin }(zkPath, listener) } -// listenServiceNodeEvent ... +// nolint func (l *ZkEventListener) listenServiceNodeEvent(zkPath string, listener ...remoting.DataListener) bool { defer l.wg.Done() var zkEvent zk.Event diff --git a/test/integrate/dubbo/go-client/client.go b/test/integrate/dubbo/go-client/client.go index c075ec22c3991aaea1b24ec4f59b3ab7e58520b4..4c62674d33dba7caca72ca7552e73c4c0fdf14c9 100644 --- a/test/integrate/dubbo/go-client/client.go +++ b/test/integrate/dubbo/go-client/client.go @@ -25,7 +25,7 @@ import ( import ( hessian "github.com/apache/dubbo-go-hessian2" - _ "github.com/apache/dubbo-go/event/proxy/proxy_factory" + _ "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" diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum deleted file mode 100644 index 687fd0f337095443e2627b70a48c524fe335aba1..0000000000000000000000000000000000000000 --- a/test/integrate/dubbo/go-client/go.sum +++ /dev/null @@ -1,389 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/Azure/azure-sdk-for-go v16.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v10.7.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest v10.15.3+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/Jeffail/gabs v1.1.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= -github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/SAP/go-hdb v0.12.0/go.mod h1:etBT+FAi1t5k3K3tf5vQTnosgYmhDkRi8jEnQqCnxF0= -github.com/SermoDigital/jose v0.0.0-20180104203859-803625baeddc/go.mod h1:ARgCUhI1MHQH+ONky/PAtmVHQrP5JlGY0F3poXOp/fA= -github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= -github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= -github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -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 v1.4.1/go.mod h1:hzP9PQkcYFcBUgedttDeimugDNqbmGzh18QQy/vBjnw= -github.com/apache/dubbo-go-hessian2 v1.4.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= -github.com/apache/dubbo-go-hessian2 v1.5.0/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= -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= -github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20180319081651-7d2e70ef918f/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -github.com/aws/aws-sdk-go v1.15.24/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= -github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= -github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/cenkalti/backoff v2.1.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/continuity v0.0.0-20181203112020-004b46473808/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= -github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= -github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I= -github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/denisenkom/go-mssqldb v0.0.0-20180620032804-94c9c97e8c9f/go.mod h1:xN/JuLBIz4bjkxNmByTiV1IbhfnYb6oo99phBn4Eqhc= -github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= -github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dubbogo/getty v1.3.3/go.mod h1:U92BDyJ6sW9Jpohr2Vlz8w2uUbIbNZ3d+6rJvFTSPp0= -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/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= -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/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful/v3 v3.0.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk= -github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/structs v0.0.0-20180123065059-ebf56d35bba7/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= -github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= -github.com/go-sql-driver/mysql v0.0.0-20180618115901-749ddf1598b4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/gocql/gocql v0.0.0-20180617115710-e06f8c1bcd78/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= -github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= -github.com/gophercloud/gophercloud v0.0.0-20180828235145-f29afc2cceca/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4= -github.com/gopherjs/gopherjs v0.0.0-20180825215210-0210a2f0f73c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/consul v1.5.3/go.mod h1:61E2GJCPEP3oq8La7sfDdWGQ66+Zbxzw5ecOdFD7xIE= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-bexpr v0.1.0/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU= -github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-discover v0.0.0-20190403160810-22221edb15cd/go.mod h1:ueUgD9BeIocT7QNuvxSyJyPAM9dfifBcaWmeybb67OY= -github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-memdb v0.0.0-20180223233045-1289e7fffe71/go.mod h1:kbfItVoBJwCfKXDXN4YoAXjxcFVZ7MRrJzyTX6H4giE= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v0.0.0-20180331002553-e8d22c780116/go.mod h1:JSqWYsict+jzcj0+xElxyrBQRPNoiWQuddnxArJ7XHQ= -github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v0.0.0-20170202080759-03c5bf6be031/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v0.0.0-20180906183839-65a6292f0157/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= -github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= -github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault v0.10.3/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0= -github.com/hashicorp/vault-plugin-secrets-kv v0.0.0-20190318174639-195e0e9d07f1/go.mod h1:VJHHT2SC1tAPrfENQeBhLlb5FbZoKZM+oC/ROmEftz0= -github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= -github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= -github.com/jefferai/jsonx v0.0.0-20160721235117-9cc31c3135ee/go.mod h1:N0t2vlmpe8nyZB5ouIbJQPDSR+mH6oe7xHB9VZHSUzM= -github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= -github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= -github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= -github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/keybase/go-crypto v0.0.0-20180614160407-5114a9a81e1b/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= -github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= -github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= -github.com/lib/pq v0.0.0-20180523175426-90697d60dd84/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -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/go.mod h1:CEkSvEpoveoYjA81m4HNeYQ0sge0LFGKSEqO3JKHllo= -github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= -github.com/oklog/run v0.0.0-20180308005104-6934b124db28/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= -github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= -github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/patrickmn/go-cache v0.0.0-20180527043350-9f6ff22cfff8/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= -github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= -github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180820201707-7c9eb446e3cf/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s= -github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= -github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRtSLfOK7qBTHaeSX1D6BNpI3qw7NTxsmNr4RvN8= -github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/zouyx/agollo v0.0.0-20191114083447-dde9fc9f35b8/go.mod h1:S1cAa98KMFv4Sa8SbJ6ZtvOmf0VlgH0QJ1gXI0lBfBY= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -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/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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/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= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -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-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-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20170807180024-9a379c6b3e95/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -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-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/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/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -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= -google.golang.org/api v0.0.0-20180829000535-087779f1d2c9/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -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= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/mgo.v2 v2.0.0-20160818020120-3f83fa500528/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= -gopkg.in/ory-am/dockertest.v3 v3.3.4/go.mod h1:s9mmoLkaGeAh97qygnNj4xWkiN7e1SKekYC6CovU+ek= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -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= -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/go.mod h1:iuAfoD4hCxJ8Onx9kaTIt30j7jUFS00AXQi6QMi99vA= -k8s.io/apimachinery v0.0.0-20180821005732-488889b0007f/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/apimachinery v0.0.0-20190223001710-c182ff3b9841/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0= -k8s.io/client-go v8.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s= -k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= -k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= -sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/test/integrate/dubbo/go-server/server.go b/test/integrate/dubbo/go-server/server.go index 4cc6c490835d7ba29d139d71892b5e6e19d628e5..115bf0a4d78f171eb7f786808def91879ed93947 100644 --- a/test/integrate/dubbo/go-server/server.go +++ b/test/integrate/dubbo/go-server/server.go @@ -25,7 +25,7 @@ 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/event/proxy/proxy_factory" + _ "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"