diff --git a/.gitignore b/.gitignore index f369c2833aeacbff3aa85a6cd1cdc25520928209..e5ba291004ab0d89c1ef1db6f353361232fddcc8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ coverage.txt remoting/zookeeper/zookeeper-4unittest/ config_center/zookeeper/zookeeper-4unittest/ registry/zookeeper/zookeeper-4unittest/ +registry/consul/agent* \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index ea6b2035584ba07ae7caa709f83be59cf20c730e..707e64481416b2c090bad05cddce2b3ccebf4535 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,3 +18,6 @@ script: after_success: - bash <(curl -s https://codecov.io/bash) + +notifications: + webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f \ No newline at end of file diff --git a/CHANGE.md b/CHANGE.md index cdfca4fb6d5d7106ed0c56e40cf40647607c3015..947a695ca854fe8c3d91d8ea989b52dcddbe1523 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,5 +1,43 @@ # Release Notes +## 1.2.0 + +### New Features + +- Add etcdv3 registry support<https://github.com/apache/dubbo-go/pull/148> +- Add nacos registry support<https://github.com/apache/dubbo-go/pull/151> +- Add fail fast cluster support<https://github.com/apache/dubbo-go/pull/140> +- Add available cluster support<https://github.com/apache/dubbo-go/pull/155> +- Add broadcast cluster support<https://github.com/apache/dubbo-go/pull/158> +- Add forking cluster support<https://github.com/apache/dubbo-go/pull/161> +- Add service token authorization support<https://github.com/apache/dubbo-go/pull/202> +- Add accessLog filter support<https://github.com/apache/dubbo-go/pull/214> +- Add tps limit support<https://github.com/apache/dubbo-go/pull/237> +- Add execute limit support<https://github.com/apache/dubbo-go/pull/246> +- Move callService to invoker & support attachments<https://github.com/apache/dubbo-go/pull/193> +- Move example in dubbo-go project away<https://github.com/apache/dubbo-go/pull/228> +- Support dynamic config center which compatible with dubbo 2.6.x & 2.7.x and commit the zookeeper impl<https://github.com/apache/dubbo-go/pull/194> + +### Enhancement + +- Split gettyRPCClient.close and gettyRPCClientPool.remove in protocol/dubbo/pool.go<https://github.com/apache/dubbo-go/pull/186> +- Remove client from pool before closing it<https://github.com/apache/dubbo-go/pull/190> +- Enhance the logic for fetching the local address<https://github.com/apache/dubbo-go/pull/209> +- Add protocol_conf default values<https://github.com/apache/dubbo-go/pull/221> +- Add task pool for getty<https://github.com/apache/dubbo-go/pull/141> +- Update getty: remove read queue<https://github.com/apache/dubbo-go/pull/137> +- Clean heartbeat from PendingResponse<https://github.com/apache/dubbo-go/pull/166> + +### Bugfixes + +- GettyRPCClientPool remove deadlock<https://github.com/apache/dubbo-go/pull/183/files> +- Fix failover cluster bug and url parameter retries change int to string type<https://github.com/apache/dubbo-go/pull/195> +- Fix url params unsafe map<https://github.com/apache/dubbo-go/pull/201> +- Read protocol config by map key in config yaml instead of protocol name<https://github.com/apache/dubbo-go/pull/218> +- Fix dubbo group issues #238<https://github.com/apache/dubbo-go/pull/243>/<https://github.com/apache/dubbo-go/pull/244> +- Fix bug in reference_config<https://github.com/apache/dubbo-go/pull/157> +- Fix high memory bug in zookeeper listener<https://github.com/apache/dubbo-go/pull/168> + ## 1.1.0 ### New Features diff --git a/LICENSE b/LICENSE index e76f9d9dd705daad997776153b1060f5bf8c2a1d..75b52484ea471f882c29e02693b4f02dba175b5e 100644 --- a/LICENSE +++ b/LICENSE @@ -176,6 +176,19 @@ END OF TERMS AND CONDITIONS + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/NOTICE b/NOTICE index a9bd809c5c43a9d88a773b5f0c421b252abf38de..d7aa899d1cef0fba67826bebd0d587e9cc17ba5d 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Dubbo Go -Copyright 2018-2019 The Apache Software Foundation +Copyright 2018-2020 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index fdc3062d6f086be14ad19b1f718f713c54fb48ed..f387b934e25e0b19c6d4c6cb9b23548344c3d543 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,11 @@ Finished List: * JsonRPC V2 * Hessian V2 +- Protocol + * Dubbo + * Jsonrpc2.0 + * [gRPC](https://github.com/apache/dubbo-go/pull/311) + - Registry * ZooKeeper * [etcd v3](https://github.com/apache/dubbo-go/pull/148) @@ -73,6 +78,7 @@ Finished List: * [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214) * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237) * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246) + * [GenericServiceFilter](https://github.com/apache/dubbo-go/pull/291) - Invoke * [generic invoke](https://github.com/apache/dubbo-go/pull/122) @@ -98,11 +104,11 @@ You can know more about dubbo-go by its [roadmap](https://github.com/apache/dubb ## Document -TODO +https://dubbogo.github.io/dubbo-go-website (**Improving**) ## Quick Start -[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples) shows how to use dubbo-go. Please read the [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) carefully to learn how to dispose the configuration and compile the program. +[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) shows how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) carefully to learn how to dispose the configuration and compile the program. ## Running unit tests diff --git a/README_CN.md b/README_CN.md index edabe64d376cd4d49f6ab4251db9387aca48bbf0..22af253416017403eaad2579ff977c6925936d7a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -41,6 +41,11 @@ Apache License, Version 2.0 - 序列化协议 * JsonRPC V2 * Hessian V2 + +- 协议 + * Dubbo + * Jsonrpc2.0 + * [gRPC](https://github.com/apache/dubbo-go/pull/311) - 注册中心 * ZooKeeper @@ -97,11 +102,11 @@ Apache License, Version 2.0 ## 文档 -TODO +https://dubbogo.github.io/dubbo-go-website (**完善中**) ## 快速开始 ## -[dubbogo-samples](https://github.com/dubbogo/dubbogo-samples)这个项目的事例展示了如何使用 dubbo-go 。请仔细阅读 [dubbogo-samples/README.md](https://github.com/dubbogo/dubbogo-samples/blob/master/README.md) 学习如何处理配置并编译程序。 +[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)这个项目的事例展示了如何使用 dubbo-go 。请仔细阅读 [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) 学习如何处理配置并编译程序。 ## 运行单测 diff --git a/cluster/cluster.go b/cluster/cluster.go index d89df5a21954d63552b857d988fa9a9e7d1fcfb5..617ce5ebf0fa7b5dc7f6047caacec9865aa6960f 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -21,6 +21,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// 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 7e748cd938319ff437bb3fb6c7945b857d316069..2ad140b93e15b97d1517119b07b1080a68a0503f 100644 --- a/cluster/cluster_impl/available_cluster.go +++ b/cluster/cluster_impl/available_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(available, NewAvailableCluster) } +// NewAvailableCluster ... 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 c59c0702c216fe5c58d190a023322aaa00ac9c17..6f6d2dffbbbf2f6c758097b11713ae0c1b6bd387 100644 --- a/cluster/cluster_impl/available_cluster_invoker.go +++ b/cluster/cluster_impl/available_cluster_invoker.go @@ -18,6 +18,7 @@ limitations under the License. package cluster_impl import ( + "context" "fmt" ) @@ -34,13 +35,14 @@ type availableClusterInvoker struct { baseClusterInvoker } +// NewAvailableClusterInvoker ... func NewAvailableClusterInvoker(directory cluster.Directory) protocol.Invoker { return &availableClusterInvoker{ baseClusterInvoker: newBaseClusterInvoker(directory), } } -func (invoker *availableClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *availableClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) if err != nil { @@ -54,7 +56,7 @@ func (invoker *availableClusterInvoker) Invoke(invocation protocol.Invocation) p for _, ivk := range invokers { if ivk.IsAvailable() { - return ivk.Invoke(invocation) + return ivk.Invoke(ctx, invocation) } } return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("no provider available in %v", invokers))} diff --git a/cluster/cluster_impl/available_cluster_invoker_test.go b/cluster/cluster_impl/available_cluster_invoker_test.go index 04032a7f24dec0e73acb15921f753921391f1515..dc0666d5afa633c86fdfaa48b93a334c19bb0248 100644 --- a/cluster/cluster_impl/available_cluster_invoker_test.go +++ b/cluster/cluster_impl/available_cluster_invoker_test.go @@ -39,7 +39,7 @@ import ( ) var ( - availableUrl, _ = common.NewURL(context.Background(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + availableUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) func registerAvailable(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { @@ -66,7 +66,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) { invoker.EXPECT().IsAvailable().Return(true) invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) } @@ -80,7 +80,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) { invoker.EXPECT().IsAvailable().Return(false) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.TODO(), &invocation.RPCInvocation{}) assert.NotNil(t, result.Error()) assert.True(t, strings.Contains(result.Error().Error(), "no provider available")) diff --git a/cluster/cluster_impl/base_cluster_invoker_test.go b/cluster/cluster_impl/base_cluster_invoker_test.go index d06d3cc23e75cf2227fa22894475f141ffe09a96..49df78c41b3c3cc7dacf92153fc7e4515a0caec0 100644 --- a/cluster/cluster_impl/base_cluster_invoker_test.go +++ b/cluster/cluster_impl/base_cluster_invoker_test.go @@ -18,7 +18,6 @@ package cluster_impl import ( - "context" "fmt" "testing" ) @@ -37,7 +36,7 @@ import ( func Test_StickyNormal(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) url.SetParam("sticky", "true") invokers = append(invokers, NewMockInvoker(url, 1)) } @@ -51,7 +50,7 @@ func Test_StickyNormal(t *testing.T) { func Test_StickyNormalWhenError(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) url.SetParam("sticky", "true") invokers = append(invokers, NewMockInvoker(url, 1)) } diff --git a/cluster/cluster_impl/broadcast_cluster.go b/cluster/cluster_impl/broadcast_cluster.go index 50aae3cfab8d67570b50dcab4e53bbfad29d6d30..9b27a4ce37bc73e42b55e4e20deb9593fd837444 100644 --- a/cluster/cluster_impl/broadcast_cluster.go +++ b/cluster/cluster_impl/broadcast_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(broadcast, NewBroadcastCluster) } +// NewBroadcastCluster ... func NewBroadcastCluster() cluster.Cluster { return &broadcastCluster{} } diff --git a/cluster/cluster_impl/broadcast_cluster_invoker.go b/cluster/cluster_impl/broadcast_cluster_invoker.go index 238df0acfa7fb946e38bfbfd490bce7c0bb34e60..1b49e9a115252d4eca94bedd557ebcc21fee4cc7 100644 --- a/cluster/cluster_impl/broadcast_cluster_invoker.go +++ b/cluster/cluster_impl/broadcast_cluster_invoker.go @@ -17,6 +17,9 @@ limitations under the License. package cluster_impl +import ( + "context" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/common/logger" @@ -33,7 +36,7 @@ func newBroadcastClusterInvoker(directory cluster.Directory) protocol.Invoker { } } -func (invoker *broadcastClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *broadcastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) if err != nil { @@ -46,7 +49,7 @@ func (invoker *broadcastClusterInvoker) Invoke(invocation protocol.Invocation) p var result protocol.Result for _, ivk := range invokers { - result = ivk.Invoke(invocation) + result = ivk.Invoke(ctx, invocation) if result.Error() != nil { logger.Warnf("broadcast invoker invoke err: %v when use invoker: %v\n", result.Error(), ivk) err = result.Error() diff --git a/cluster/cluster_impl/broadcast_cluster_invoker_test.go b/cluster/cluster_impl/broadcast_cluster_invoker_test.go index 565684a8ae25c648ff77aef71d2ced0665202fe7..1de5270265a79b4d1d62317730bd06927d939acd 100644 --- a/cluster/cluster_impl/broadcast_cluster_invoker_test.go +++ b/cluster/cluster_impl/broadcast_cluster_invoker_test.go @@ -39,7 +39,7 @@ import ( ) var ( - broadcastUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + broadcastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker { @@ -74,7 +74,7 @@ func Test_BroadcastInvokeSuccess(t *testing.T) { clusterInvoker := registerBroadcast(t, invokers...) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) } @@ -104,6 +104,6 @@ func Test_BroadcastInvokeFailed(t *testing.T) { clusterInvoker := registerBroadcast(t, invokers...) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + 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 de22c78e947d0b8124add721ab7ff42efebcdbe4..76573571684c07f63609009f59ab0ac881ae1b50 100644 --- a/cluster/cluster_impl/failback_cluster.go +++ b/cluster/cluster_impl/failback_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(failback, NewFailbackCluster) } +// NewFailbackCluster ... func NewFailbackCluster() cluster.Cluster { return &failbackCluster{} } diff --git a/cluster/cluster_impl/failback_cluster_invoker.go b/cluster/cluster_impl/failback_cluster_invoker.go index c8dbeda09f62e88b51dd4ad2b6b09d5715f0b224..46b0ff634e56c45223a5aeb5566b9b1401518960 100644 --- a/cluster/cluster_impl/failback_cluster_invoker.go +++ b/cluster/cluster_impl/failback_cluster_invoker.go @@ -18,6 +18,7 @@ package cluster_impl import ( + "context" "strconv" "sync" "time" @@ -71,7 +72,7 @@ func newFailbackClusterInvoker(directory cluster.Directory) protocol.Invoker { return invoker } -func (invoker *failbackClusterInvoker) process() { +func (invoker *failbackClusterInvoker) process(ctx context.Context) { invoker.ticker = time.NewTicker(time.Second * 1) for range invoker.ticker.C { // check each timeout task and re-run @@ -102,7 +103,7 @@ func (invoker *failbackClusterInvoker) process() { retryInvoker := invoker.doSelect(retryTask.loadbalance, retryTask.invocation, retryTask.invokers, invoked) var result protocol.Result - result = retryInvoker.Invoke(retryTask.invocation) + result = retryInvoker.Invoke(ctx, retryTask.invocation) if result.Error() != nil { retryTask.lastInvoker = retryInvoker invoker.checkRetry(retryTask, result.Error()) @@ -126,7 +127,7 @@ func (invoker *failbackClusterInvoker) checkRetry(retryTask *retryTimerTask, err } } -func (invoker *failbackClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *failbackClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) if err != nil { @@ -150,11 +151,11 @@ func (invoker *failbackClusterInvoker) Invoke(invocation protocol.Invocation) pr ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked) //DO INVOKE - result = ivk.Invoke(invocation) + result = ivk.Invoke(ctx, invocation) if result.Error() != nil { invoker.once.Do(func() { invoker.taskList = queue.New(invoker.failbackTasks) - go invoker.process() + go invoker.process(ctx) }) taskLen := invoker.taskList.Len() diff --git a/cluster/cluster_impl/failback_cluster_test.go b/cluster/cluster_impl/failback_cluster_test.go index 1d2266cabebf591b09188fb723f02126a3f1e0ec..4571fccec59a2f995009f57c35b56ec5e1cb3ea6 100644 --- a/cluster/cluster_impl/failback_cluster_test.go +++ b/cluster/cluster_impl/failback_cluster_test.go @@ -41,7 +41,7 @@ import ( ) var ( - failbackUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failbackUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) // registerFailback register failbackCluster to cluster extension. @@ -72,7 +72,7 @@ func Test_FailbackSuceess(t *testing.T) { mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) } @@ -102,7 +102,7 @@ func Test_FailbackRetryOneSuccess(t *testing.T) { return mockSuccResult }) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) assert.Equal(t, 0, len(result.Attachments())) @@ -150,7 +150,7 @@ func Test_FailbackRetryFailed(t *testing.T) { } // first call should failed. - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) assert.Equal(t, 0, len(result.Attachments())) @@ -192,7 +192,7 @@ func Test_FailbackRetryFailed10Times(t *testing.T) { }).Times(10) for i := 0; i < 10; i++ { - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) assert.Equal(t, 0, len(result.Attachments())) @@ -222,14 +222,14 @@ func Test_FailbackOutOfLimit(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).Return(mockFailedResult).Times(11) // reached limit - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) assert.Equal(t, 0, len(result.Attachments())) // all will be out of limit for i := 0; i < 10; i++ { - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) assert.Equal(t, 0, len(result.Attachments())) diff --git a/cluster/cluster_impl/failfast_cluster.go b/cluster/cluster_impl/failfast_cluster.go index 6301d945626103226132b433dd21e8647f53a38b..e0b80ded041cd30b379857ff00d307811e53765d 100644 --- a/cluster/cluster_impl/failfast_cluster.go +++ b/cluster/cluster_impl/failfast_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(failfast, NewFailFastCluster) } +// NewFailFastCluster ... func NewFailFastCluster() cluster.Cluster { return &failfastCluster{} } diff --git a/cluster/cluster_impl/failfast_cluster_invoker.go b/cluster/cluster_impl/failfast_cluster_invoker.go index 734ea2c6cb19bf54a338a76a10c9cfcc59d3954b..49e7c7689f5a19a36154e092a6a83cc39da604ba 100644 --- a/cluster/cluster_impl/failfast_cluster_invoker.go +++ b/cluster/cluster_impl/failfast_cluster_invoker.go @@ -17,6 +17,9 @@ limitations under the License. package cluster_impl +import ( + "context" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/protocol" @@ -32,7 +35,7 @@ func newFailFastClusterInvoker(directory cluster.Directory) protocol.Invoker { } } -func (invoker *failfastClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *failfastClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) if err != nil { @@ -47,5 +50,5 @@ func (invoker *failfastClusterInvoker) Invoke(invocation protocol.Invocation) pr } ivk := invoker.doSelect(loadbalance, invocation, invokers, nil) - return ivk.Invoke(invocation) + return ivk.Invoke(ctx, invocation) } diff --git a/cluster/cluster_impl/failfast_cluster_test.go b/cluster/cluster_impl/failfast_cluster_test.go index 1a4342e6c2b74fd6b1359646eeb463bb6dc17d0a..38e258199e05c396e22118337bbdb3ae2b955bd0 100644 --- a/cluster/cluster_impl/failfast_cluster_test.go +++ b/cluster/cluster_impl/failfast_cluster_test.go @@ -39,7 +39,7 @@ import ( ) var ( - failfastUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failfastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) // registerFailfast register failfastCluster to cluster extension. @@ -69,7 +69,7 @@ func Test_FailfastInvokeSuccess(t *testing.T) { mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) res := result.Result().(rest) @@ -89,7 +89,7 @@ func Test_FailfastInvokeFail(t *testing.T) { mockResult := &protocol.RPCResult{Err: perrors.New("error")} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NotNil(t, result.Error()) assert.Equal(t, "error", result.Error().Error()) diff --git a/cluster/cluster_impl/failover_cluster.go b/cluster/cluster_impl/failover_cluster.go index 0f1aa0371f57df5414a04a59e2a6772a4cd382b3..b16be3bafd43c7de8e2fadd109a73a3ea710e225 100644 --- a/cluster/cluster_impl/failover_cluster.go +++ b/cluster/cluster_impl/failover_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(name, NewFailoverCluster) } +// NewFailoverCluster ... 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 dcce7369931a11f31fb6b9e4e1a6c0aa0ec7cdf6..6178a05a1226ba629d2456ad6886b02a26288e45 100644 --- a/cluster/cluster_impl/failover_cluster_invoker.go +++ b/cluster/cluster_impl/failover_cluster_invoker.go @@ -18,6 +18,7 @@ package cluster_impl import ( + "context" "strconv" ) @@ -43,7 +44,7 @@ func newFailoverClusterInvoker(directory cluster.Directory) protocol.Invoker { } } -func (invoker *failoverClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) @@ -95,7 +96,7 @@ func (invoker *failoverClusterInvoker) Invoke(invocation protocol.Invocation) pr } invoked = append(invoked, ivk) //DO INVOKE - result = ivk.Invoke(invocation) + result = ivk.Invoke(ctx, invocation) if result.Error() != nil { providers = append(providers, ivk.GetUrl().Key()) continue diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go index 78b799320dfa58d55e531c658ec5eb0e69306cff..99c584bb583c1d59ece505feafd74ad6e11f6b9a 100644 --- a/cluster/cluster_impl/failover_cluster_test.go +++ b/cluster/cluster_impl/failover_cluster_test.go @@ -39,9 +39,9 @@ import ( "github.com/apache/dubbo-go/protocol/invocation" ) -///////////////////////////// +// /////////////////////////// // mock invoker -///////////////////////////// +// /////////////////////////// type MockInvoker struct { url common.URL @@ -77,10 +77,12 @@ type rest struct { success bool } -func (bi *MockInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (bi *MockInvoker) Invoke(c context.Context, invocation protocol.Invocation) protocol.Result { count++ - var success bool - var err error = nil + var ( + success bool + err error + ) if count >= bi.successCount { success = true } else { @@ -105,16 +107,16 @@ func normalInvoke(t *testing.T, successCount int, urlParam url.Values, invocatio invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i), common.WithParams(urlParam)) invokers = append(invokers, NewMockInvoker(url, successCount)) } staticDir := directory.NewStaticDirectory(invokers) clusterInvoker := failoverCluster.Join(staticDir) if len(invocations) > 0 { - return clusterInvoker.Invoke(invocations[0]) + return clusterInvoker.Invoke(context.Background(), invocations[0]) } - return clusterInvoker.Invoke(&invocation.RPCInvocation{}) + return clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) } func Test_FailoverInvokeSuccess(t *testing.T) { urlParams := url.Values{} @@ -155,14 +157,14 @@ func Test_FailoverDestroy(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, NewMockInvoker(url, 1)) } staticDir := directory.NewStaticDirectory(invokers) clusterInvoker := failoverCluster.Join(staticDir) assert.Equal(t, true, clusterInvoker.IsAvailable()) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) count = 0 clusterInvoker.Destroy() diff --git a/cluster/cluster_impl/failsafe_cluster.go b/cluster/cluster_impl/failsafe_cluster.go index 3ff97d25eae80980a90a03e71865bb8f9a63defe..177d24a585b5f72fb0667215beb8d11147cc2922 100644 --- a/cluster/cluster_impl/failsafe_cluster.go +++ b/cluster/cluster_impl/failsafe_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(failsafe, NewFailsafeCluster) } +// NewFailsafeCluster ... func NewFailsafeCluster() cluster.Cluster { return &failsafeCluster{} } diff --git a/cluster/cluster_impl/failsafe_cluster_invoker.go b/cluster/cluster_impl/failsafe_cluster_invoker.go index b95f997fef87cf466f07c4e506e41758e7998e52..4d8fe27719eb71fa287fe4142d8e92ca17acfba4 100644 --- a/cluster/cluster_impl/failsafe_cluster_invoker.go +++ b/cluster/cluster_impl/failsafe_cluster_invoker.go @@ -17,6 +17,9 @@ package cluster_impl +import ( + "context" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/common/constant" @@ -42,7 +45,7 @@ func newFailsafeClusterInvoker(directory cluster.Directory) protocol.Invoker { } } -func (invoker *failsafeClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *failsafeClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) err := invoker.checkInvokers(invokers, invocation) @@ -65,7 +68,7 @@ func (invoker *failsafeClusterInvoker) Invoke(invocation protocol.Invocation) pr ivk := invoker.doSelect(loadbalance, invocation, invokers, invoked) //DO INVOKE - result = ivk.Invoke(invocation) + result = ivk.Invoke(ctx, invocation) if result.Error() != nil { // ignore logger.Errorf("Failsafe ignore exception: %v.\n", result.Error().Error()) diff --git a/cluster/cluster_impl/failsafe_cluster_test.go b/cluster/cluster_impl/failsafe_cluster_test.go index 7888b97c3a02bd4679f8ec5267637b8d2a7c12e4..2e35de8da91cc78730b6380bf039f0626ca75ec0 100644 --- a/cluster/cluster_impl/failsafe_cluster_test.go +++ b/cluster/cluster_impl/failsafe_cluster_test.go @@ -39,11 +39,11 @@ import ( ) var ( - failsafeUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + failsafeUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) -// register_failsafe register failsafeCluster to cluster extension. -func register_failsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { +// registerFailsafe register failsafeCluster to cluster extension. +func registerFailsafe(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) failsafeCluster := NewFailsafeCluster() @@ -62,14 +62,14 @@ func Test_FailSafeInvokeSuccess(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := register_failsafe(t, invoker) + clusterInvoker := registerFailsafe(t, invoker) invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) res := result.Result().(rest) @@ -81,14 +81,14 @@ func Test_FailSafeInvokeFail(t *testing.T) { defer ctrl.Finish() invoker := mock.NewMockInvoker(ctrl) - clusterInvoker := register_failsafe(t, invoker) + clusterInvoker := registerFailsafe(t, invoker) invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() mockResult := &protocol.RPCResult{Err: perrors.New("error")} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) assert.Nil(t, result.Result()) diff --git a/cluster/cluster_impl/forking_cluster.go b/cluster/cluster_impl/forking_cluster.go index 0a3c2b313ff3c4e89e592af9256fc42713419914..6b0572b15088e86870b3d9fd911a1d0b022378be 100644 --- a/cluster/cluster_impl/forking_cluster.go +++ b/cluster/cluster_impl/forking_cluster.go @@ -31,6 +31,7 @@ func init() { extension.SetCluster(forking, NewForkingCluster) } +// NewForkingCluster ... 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 d6cf2f4b89ab4f322fa758deecae90c60742ef49..058d7fefd6edf6c43e5eda4b8f2f6a9c161189e2 100644 --- a/cluster/cluster_impl/forking_cluster_invoker.go +++ b/cluster/cluster_impl/forking_cluster_invoker.go @@ -18,7 +18,7 @@ limitations under the License. package cluster_impl import ( - "errors" + "context" "fmt" "time" ) @@ -44,7 +44,8 @@ func newForkingClusterInvoker(directory cluster.Directory) protocol.Invoker { } } -func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +// Invoke ... +func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { err := invoker.checkWhetherDestroyed() if err != nil { return &protocol.RPCResult{Err: err} @@ -75,7 +76,7 @@ func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) pro resultQ := queue.New(1) for _, ivk := range selected { go func(k protocol.Invoker) { - result := k.Invoke(invocation) + result := k.Invoke(ctx, invocation) err := resultQ.Put(result) if err != nil { logger.Errorf("resultQ put failed with exception: %v.\n", err) @@ -86,14 +87,18 @@ func (invoker *forkingClusterInvoker) Invoke(invocation protocol.Invocation) pro rsps, err := resultQ.Poll(1, time.Millisecond*time.Duration(timeouts)) if err != nil { return &protocol.RPCResult{ - Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but no luck to perform the invocation. Last error is: %s", selected, err.Error()))} + Err: fmt.Errorf("failed to forking invoke provider %v, "+ + "but no luck to perform the invocation. Last error is: %v", selected, err), + } } if len(rsps) == 0 { - return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but no resp", selected))} + return &protocol.RPCResult{Err: fmt.Errorf("failed to forking invoke provider %v, but no resp", selected)} } + result, ok := rsps[0].(protocol.Result) if !ok { - return &protocol.RPCResult{Err: errors.New(fmt.Sprintf("failed to forking invoke provider %v, but not legal resp", selected))} + 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 8603f8aedc4e28a3a4ca2f115355debc1a5ecc62..9797ecbd041ae2e09c3d0566f88d08b7246b900f 100644 --- a/cluster/cluster_impl/forking_cluster_test.go +++ b/cluster/cluster_impl/forking_cluster_test.go @@ -42,7 +42,7 @@ import ( ) var ( - forkingUrl, _ = common.NewURL(context.TODO(), "dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") + forkingUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") ) func registerForking(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker { @@ -87,7 +87,7 @@ func Test_ForkingInvokeSuccess(t *testing.T) { clusterInvoker := registerForking(t, invokers...) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) wg.Wait() } @@ -117,7 +117,7 @@ func Test_ForkingInvokeTimeout(t *testing.T) { clusterInvoker := registerForking(t, invokers...) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NotNil(t, result) assert.NotNil(t, result.Error()) wg.Wait() @@ -156,7 +156,7 @@ func Test_ForkingInvokeHalfTimeout(t *testing.T) { clusterInvoker := registerForking(t, invokers...) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.Equal(t, mockResult, result) wg.Wait() } diff --git a/cluster/cluster_impl/mock_cluster.go b/cluster/cluster_impl/mock_cluster.go index 50b2735554b61600fb090f382f3d2920b3d445e3..943c2add68281d01e320252d07b7d58e27b51283 100644 --- a/cluster/cluster_impl/mock_cluster.go +++ b/cluster/cluster_impl/mock_cluster.go @@ -24,6 +24,7 @@ import ( type mockCluster struct{} +// NewMockCluster ... 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 f4a28d6dcd5fbab8c62ee1f79bdd8576d8774a4c..079b688da65b3e6f6595212ad6e93c3b6ecc6504 100644 --- a/cluster/cluster_impl/registry_aware_cluster.go +++ b/cluster/cluster_impl/registry_aware_cluster.go @@ -29,6 +29,7 @@ func init() { extension.SetCluster("registryAware", NewRegistryAwareCluster) } +// NewRegistryAwareCluster ... func NewRegistryAwareCluster() cluster.Cluster { return ®istryAwareCluster{} } diff --git a/cluster/cluster_impl/registry_aware_cluster_invoker.go b/cluster/cluster_impl/registry_aware_cluster_invoker.go index 5785c02489f95168d5419f0087f38b07c851a4a3..cded5bf16432e6b0c590e15b81c28369889a5f88 100644 --- a/cluster/cluster_impl/registry_aware_cluster_invoker.go +++ b/cluster/cluster_impl/registry_aware_cluster_invoker.go @@ -17,6 +17,9 @@ package cluster_impl +import ( + "context" +) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/common/constant" @@ -33,19 +36,19 @@ func newRegistryAwareClusterInvoker(directory cluster.Directory) protocol.Invoke } } -func (invoker *registryAwareClusterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (invoker *registryAwareClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { invokers := invoker.directory.List(invocation) //First, pick the invoker (XXXClusterInvoker) that comes from the local registry, distinguish by a 'default' key. for _, invoker := range invokers { if invoker.IsAvailable() && invoker.GetUrl().GetParam(constant.REGISTRY_DEFAULT_KEY, "false") == "true" { - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } } //If none of the invokers has a local signal, pick the first one available. for _, invoker := range invokers { if invoker.IsAvailable() { - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } } return nil diff --git a/cluster/cluster_impl/registry_aware_cluster_test.go b/cluster/cluster_impl/registry_aware_cluster_test.go index 4ae15cc5066c70646dee66cf4ef601202653cb07..3d0dcc0159839eb0a08aed842ee084449458c645 100644 --- a/cluster/cluster_impl/registry_aware_cluster_test.go +++ b/cluster/cluster_impl/registry_aware_cluster_test.go @@ -39,13 +39,13 @@ func Test_RegAwareInvokeSuccess(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, NewMockInvoker(url, 1)) } staticDir := directory.NewStaticDirectory(invokers) clusterInvoker := regAwareCluster.Join(staticDir) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) count = 0 } @@ -55,14 +55,14 @@ func TestDestroy(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, NewMockInvoker(url, 1)) } staticDir := directory.NewStaticDirectory(invokers) clusterInvoker := regAwareCluster.Join(staticDir) assert.Equal(t, true, clusterInvoker.IsAvailable()) - result := clusterInvoker.Invoke(&invocation.RPCInvocation{}) + result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) assert.NoError(t, result.Error()) count = 0 clusterInvoker.Destroy() diff --git a/cluster/directory.go b/cluster/directory.go index 045296ce549b13c327b6f479ca0bd75d0b6ce131..5a03b3a4490ce0b3aadece8a9ef43395f845dd12 100644 --- a/cluster/directory.go +++ b/cluster/directory.go @@ -22,7 +22,8 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Extension - Directory +// Directory +//Extension - Directory type Directory interface { common.Node List(invocation protocol.Invocation) []protocol.Invoker diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go index 1d59b51cc36858b80fb43c1d76e368e89e26ae36..e1a38c4c82cbac61fdfd96cd284c4eea44c97ccc 100644 --- a/cluster/directory/base_directory.go +++ b/cluster/directory/base_directory.go @@ -27,25 +27,32 @@ import ( "github.com/apache/dubbo-go/common" ) +// BaseDirectory ... type BaseDirectory struct { url *common.URL destroyed *atomic.Bool mutex sync.Mutex } +// NewBaseDirectory ... func NewBaseDirectory(url *common.URL) BaseDirectory { return BaseDirectory{ url: url, destroyed: atomic.NewBool(false), } } + +// GetUrl ... func (dir *BaseDirectory) GetUrl() common.URL { return *dir.url } + +// GetDirectoryUrl ... func (dir *BaseDirectory) GetDirectoryUrl() *common.URL { return dir.url } +// Destroy ... func (dir *BaseDirectory) Destroy(doDestroy func()) { if dir.destroyed.CAS(false, true) { dir.mutex.Lock() @@ -54,6 +61,7 @@ func (dir *BaseDirectory) Destroy(doDestroy func()) { } } +// IsAvailable ... func (dir *BaseDirectory) IsAvailable() bool { return !dir.destroyed.Load() } diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go index e7a0e6e569db620ee83521505c9568199d45fe1e..7d2d5490b02d22b12d55385458715fa8b31f2cac 100644 --- a/cluster/directory/static_directory.go +++ b/cluster/directory/static_directory.go @@ -27,6 +27,7 @@ type staticDirectory struct { invokers []protocol.Invoker } +// NewStaticDirectory ... func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory { var url common.URL diff --git a/cluster/directory/static_directory_test.go b/cluster/directory/static_directory_test.go index 3ce772e8a6287aebef3fdad039e2b12be421c4e3..42ef1bcd0b51ce94040b3cbfac37a1f6de686fbb 100644 --- a/cluster/directory/static_directory_test.go +++ b/cluster/directory/static_directory_test.go @@ -18,7 +18,6 @@ package directory import ( - "context" "fmt" "testing" ) @@ -36,7 +35,7 @@ import ( func Test_StaticDirList(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } @@ -47,7 +46,7 @@ func Test_StaticDirList(t *testing.T) { func Test_StaticDirDestroy(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } diff --git a/cluster/loadbalance.go b/cluster/loadbalance.go index 9ae4e4eb808b28581d12b72829c921c4f0cc9ac8..fb3641a77377eabbd692729a32e2c0c096282f18 100644 --- a/cluster/loadbalance.go +++ b/cluster/loadbalance.go @@ -21,7 +21,8 @@ import ( "github.com/apache/dubbo-go/protocol" ) -// Extension - LoadBalance +// 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 365e6a66242e4a4618ab922f80b4b4247076484d..957c110663d6c56ada15543d372e210fa83bf74b 100644 --- a/cluster/loadbalance/consistent_hash.go +++ b/cluster/loadbalance/consistent_hash.go @@ -36,9 +36,12 @@ import ( ) const ( + // ConsistentHash ... ConsistentHash = "consistenthash" - HashNodes = "hash.nodes" - HashArguments = "hash.arguments" + // HashNodes ... + HashNodes = "hash.nodes" + // HashArguments ... + HashArguments = "hash.arguments" ) var ( @@ -50,13 +53,16 @@ func init() { extension.SetLoadbalance(ConsistentHash, NewConsistentHashLoadBalance) } +// ConsistentHashLoadBalance ... type ConsistentHashLoadBalance struct { } +// NewConsistentHashLoadBalance ... func NewConsistentHashLoadBalance() cluster.LoadBalance { return &ConsistentHashLoadBalance{} } +// Select ... func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocation protocol.Invocation) protocol.Invoker { methodName := invocation.MethodName() key := invokers[0].GetUrl().ServiceKey() + "." + methodName @@ -79,6 +85,7 @@ func (lb *ConsistentHashLoadBalance) Select(invokers []protocol.Invoker, invocat return selector.Select(invocation) } +// Uint32Slice ... type Uint32Slice []uint32 func (s Uint32Slice) Len() int { @@ -93,6 +100,7 @@ func (s Uint32Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +// ConsistentHashSelector ... type ConsistentHashSelector struct { hashCode uint32 replicaNum int @@ -133,6 +141,7 @@ func newConsistentHashSelector(invokers []protocol.Invoker, methodName string, return selector } +// Select ... func (c *ConsistentHashSelector) Select(invocation protocol.Invocation) protocol.Invoker { key := c.toKey(invocation.Arguments()) digest := md5.Sum([]byte(key)) diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go index 174d5715dd1258d329f40251e76ca47d98791ea9..a44293172c6e2c96bd098a19306f69260b713689 100644 --- a/cluster/loadbalance/consistent_hash_test.go +++ b/cluster/loadbalance/consistent_hash_test.go @@ -18,7 +18,6 @@ package loadbalance import ( - "context" "testing" ) @@ -44,7 +43,7 @@ type consistentHashSelectorSuite struct { func (s *consistentHashSelectorSuite) SetupTest() { var invokers []protocol.Invoker - url, _ := common.NewURL(context.TODO(), + url, _ := common.NewURL( "dubbo://192.168.1.0:20000/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") invokers = append(invokers, protocol.NewBaseInvoker(url)) s.selector = newConsistentHashSelector(invokers, "echo", 999944) @@ -56,8 +55,8 @@ func (s *consistentHashSelectorSuite) TestToKey() { } func (s *consistentHashSelectorSuite) TestSelectForKey() { - url1, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:8080") - url2, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:8081") + url1, _ := common.NewURL("dubbo://192.168.1.0:8080") + url2, _ := common.NewURL("dubbo://192.168.1.0:8081") s.selector.virtualInvokers = make(map[uint32]protocol.Invoker) s.selector.virtualInvokers[99874] = protocol.NewBaseInvoker(url1) s.selector.virtualInvokers[9999945] = protocol.NewBaseInvoker(url2) @@ -84,11 +83,11 @@ type consistentHashLoadBalanceSuite struct { func (s *consistentHashLoadBalanceSuite) SetupTest() { var err error - s.url1, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url1, err = common.NewURL("dubbo://192.168.1.0:8080/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") s.NoError(err) - s.url2, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url2, err = common.NewURL("dubbo://192.168.1.0:8081/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") s.NoError(err) - s.url3, err = common.NewURL(context.TODO(), "dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") + s.url3, err = common.NewURL("dubbo://192.168.1.0:8082/org.apache.demo.HelloService?methods.echo.hash.arguments=0,1") s.NoError(err) s.invoker1 = protocol.NewBaseInvoker(s.url1) diff --git a/cluster/loadbalance/least_active.go b/cluster/loadbalance/least_active.go index aa69f3cc207ae7465bc6d5472bc075d0902c8978..e7c41aac93e8d3dfcef5d49fa486483bd045f569 100644 --- a/cluster/loadbalance/least_active.go +++ b/cluster/loadbalance/least_active.go @@ -28,6 +28,7 @@ import ( ) const ( + // LeastActive ... LeastActive = "leastactive" ) @@ -38,6 +39,7 @@ func init() { type leastActiveLoadBalance struct { } +// NewLeastActiveLoadBalance ... func NewLeastActiveLoadBalance() cluster.LoadBalance { return &leastActiveLoadBalance{} } @@ -52,18 +54,18 @@ func (lb *leastActiveLoadBalance) Select(invokers []protocol.Invoker, invocation } var ( - leastActive int32 = -1 // The least active value of all invokers - totalWeight int64 = 0 // The number of invokers having the same least active value (LEAST_ACTIVE) - firstWeight int64 = 0 // Initial value, used for comparison - leastIndexes = make([]int, count) // The index of invokers having the same least active value (LEAST_ACTIVE) - leastCount = 0 // The number of invokers having the same least active value (LEAST_ACTIVE) - sameWeight = true // Every invoker has the same weight value? + leastActive int32 = -1 // The least active value of all invokers + totalWeight int64 // The number of invokers having the same least active value (LEAST_ACTIVE) + firstWeight int64 // Initial value, used for comparison + leastCount int // The number of invokers having the same least active value (LEAST_ACTIVE) + leastIndexes = make([]int, count) // The index of invokers having the same least active value (LEAST_ACTIVE) + sameWeight = true // Every invoker has the same weight value? ) for i := 0; i < count; i++ { invoker := invokers[i] // Active number - active := protocol.GetStatus(invoker.GetUrl(), invocation.MethodName()).GetActive() + active := protocol.GetMethodStatus(invoker.GetUrl(), invocation.MethodName()).GetActive() // current weight (maybe in warmUp) weight := GetWeight(invoker, invocation) // There are smaller active services diff --git a/cluster/loadbalance/least_active_test.go b/cluster/loadbalance/least_active_test.go index 6bc6985678d7c392ad21b49ec341c3550265f622..54e57e930f17008cf6d767ef47c0e754ac85d8f7 100644 --- a/cluster/loadbalance/least_active_test.go +++ b/cluster/loadbalance/least_active_test.go @@ -18,7 +18,6 @@ package loadbalance import ( - "context" "fmt" "testing" ) @@ -38,13 +37,13 @@ func TestLeastActiveSelect(t *testing.T) { var invokers []protocol.Invoker - url, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:20000/org.apache.demo.HelloService") + url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService") invokers = append(invokers, protocol.NewBaseInvoker(url)) i := loadBalance.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) for i := 1; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } loadBalance.Select(invokers, &invocation.RPCInvocation{}) @@ -56,7 +55,7 @@ func TestLeastActiveByWeight(t *testing.T) { var invokers []protocol.Invoker loop := 3 for i := 1; i <= loop; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("test%v://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i, i)) + url, _ := common.NewURL(fmt.Sprintf("test%v://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i, i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } diff --git a/cluster/loadbalance/random.go b/cluster/loadbalance/random.go index 919792162dc527fa8c1e5cf2911f2933fa8232ef..56f13631b653ed070dae7def5bea97d924141209 100644 --- a/cluster/loadbalance/random.go +++ b/cluster/loadbalance/random.go @@ -38,6 +38,7 @@ func init() { type randomLoadBalance struct { } +// NewRandomLoadBalance ... func NewRandomLoadBalance() cluster.LoadBalance { return &randomLoadBalance{} } diff --git a/cluster/loadbalance/random_test.go b/cluster/loadbalance/random_test.go index ffe65d78ac61e5210d23e44c7f802597fed78f96..ff876f4aef8d229e8041594aaaa096f3ad5b1834 100644 --- a/cluster/loadbalance/random_test.go +++ b/cluster/loadbalance/random_test.go @@ -18,7 +18,6 @@ package loadbalance import ( - "context" "fmt" "net/url" "strconv" @@ -42,13 +41,13 @@ func Test_RandomlbSelect(t *testing.T) { invokers := []protocol.Invoker{} - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", 0)) invokers = append(invokers, protocol.NewBaseInvoker(url)) i := randomlb.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) for i := 1; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } randomlb.Select(invokers, &invocation.RPCInvocation{}) @@ -59,13 +58,13 @@ func Test_RandomlbSelectWeight(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } urlParams := url.Values{} urlParams.Set("methods.test."+constant.WEIGHT_KEY, "10000000000000") - urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) + urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) invokers = append(invokers, protocol.NewBaseInvoker(urll)) ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test")) @@ -80,7 +79,7 @@ func Test_RandomlbSelectWeight(t *testing.T) { } assert.Condition(t, func() bool { - //really is 0.9999999999999 + // really is 0.9999999999999 return selected/10000 > 0.9 }) } @@ -90,13 +89,13 @@ func Test_RandomlbSelectWarmup(t *testing.T) { invokers := []protocol.Invoker{} for i := 0; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } urlParams := url.Values{} urlParams.Set(constant.REMOTE_TIMESTAMP_KEY, strconv.FormatInt(time.Now().Add(time.Minute*(-9)).Unix(), 10)) - urll, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) + urll, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.100:20000/com.ikurento.user.UserProvider"), common.WithParams(urlParams)) invokers = append(invokers, protocol.NewBaseInvoker(urll)) ivc := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("test")) diff --git a/cluster/loadbalance/round_robin.go b/cluster/loadbalance/round_robin.go index 075acac7cdc60086ececb7b655dee86ec5198369..4d039999677aefb1093071666a845279dc357ce9 100644 --- a/cluster/loadbalance/round_robin.go +++ b/cluster/loadbalance/round_robin.go @@ -31,16 +31,19 @@ import ( ) const ( + // RoundRobin ... RoundRobin = "roundrobin" + // COMPLETE ... COMPLETE = 0 + // UPDATING ... UPDATING = 1 ) var ( - methodWeightMap sync.Map // [string]invokers - state int32 = COMPLETE // update lock acquired ? - recyclePeriod int64 = 60 * time.Second.Nanoseconds() + methodWeightMap sync.Map // [string]invokers + state = int32(COMPLETE) // update lock acquired ? + recyclePeriod = 60 * time.Second.Nanoseconds() ) func init() { @@ -49,6 +52,7 @@ func init() { type roundRobinLoadBalance struct{} +// NewRoundRobinLoadBalance ... func NewRoundRobinLoadBalance() cluster.LoadBalance { return &roundRobinLoadBalance{} } diff --git a/cluster/loadbalance/round_robin_test.go b/cluster/loadbalance/round_robin_test.go index 0b8e89c7349afb056309b5a70d91aa5cce309aa0..1517f2a20b473af57cc23e61b988aa5a6a04de31 100644 --- a/cluster/loadbalance/round_robin_test.go +++ b/cluster/loadbalance/round_robin_test.go @@ -18,7 +18,6 @@ package loadbalance import ( - "context" "fmt" "strconv" "testing" @@ -39,13 +38,13 @@ func TestRoundRobinSelect(t *testing.T) { var invokers []protocol.Invoker - url, _ := common.NewURL(context.TODO(), "dubbo://192.168.1.0:20000/org.apache.demo.HelloService") + url, _ := common.NewURL("dubbo://192.168.1.0:20000/org.apache.demo.HelloService") invokers = append(invokers, protocol.NewBaseInvoker(url)) i := loadBalance.Select(invokers, &invocation.RPCInvocation{}) assert.True(t, i.GetUrl().URLEqual(url)) for i := 1; i < 10; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService", i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } loadBalance.Select(invokers, &invocation.RPCInvocation{}) @@ -57,7 +56,7 @@ func TestRoundRobinByWeight(t *testing.T) { var invokers []protocol.Invoker loop := 10 for i := 1; i <= loop; i++ { - url, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i)) + url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/org.apache.demo.HelloService?weight=%v", i, i)) invokers = append(invokers, protocol.NewBaseInvoker(url)) } diff --git a/cluster/loadbalance/util.go b/cluster/loadbalance/util.go index 7e0c2e265073c0a96032a6dd3294a6d73c1a4001..9f36ad9379a3a09a4a058f6179e3e537b9e105bc 100644 --- a/cluster/loadbalance/util.go +++ b/cluster/loadbalance/util.go @@ -26,6 +26,7 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// GetWeight ... 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.go b/cluster/router.go index 54a19695574f245fcac236e9308a2469f306a4f8..589eb9a2696e5772070a94e8c764c78c8e0ca8a2 100644 --- a/cluster/router.go +++ b/cluster/router.go @@ -24,18 +24,22 @@ import ( // Extension - Router +// RouterFactory ... type RouterFactory interface { Router(*common.URL) (Router, error) } +// Router ... type Router interface { Route([]protocol.Invoker, common.URL, protocol.Invocation) []protocol.Invoker } +// RouterChain ... type RouterChain struct { routers []Router } +// NewRouterChain ... func NewRouterChain(url common.URL) { } diff --git a/cluster/router/condition_router.go b/cluster/router/condition_router.go index efae65ccb34eb8a78e281cfaf7b1fcec79b3d163..c38e9718cca6d5e67a874a61225bac7d27d5d352 100644 --- a/cluster/router/condition_router.go +++ b/cluster/router/condition_router.go @@ -24,7 +24,7 @@ import ( ) import ( - "github.com/dubbogo/gost/container/gxset" + gxset "github.com/dubbogo/gost/container/set" gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) @@ -37,9 +37,12 @@ import ( ) const ( + //ROUTE_PATTERN route pattern regex ROUTE_PATTERN = `([&!=,]*)\\s*([^&!=,\\s]+)` - FORCE = "force" - PRIORITY = "priority" + // FORCE ... + FORCE = "force" + // PRIORITY ... + PRIORITY = "priority" ) //ConditionRouter condition router struct @@ -104,7 +107,8 @@ func newConditionRouter(url *common.URL) (*ConditionRouter, error) { }, nil } -//Router determine the target server list. +// Route +// Router determine the target server list. func (c *ConditionRouter) Route(invokers []protocol.Invoker, url common.URL, invocation protocol.Invocation) []protocol.Invoker { if len(invokers) == 0 { return invokers @@ -212,7 +216,7 @@ func parseRule(rule string) (map[string]MatchPair, error) { return condition, nil } -// +//MatchWhen MatchWhen func (c *ConditionRouter) MatchWhen(url common.URL, invocation protocol.Invocation) (bool, error) { condition, err := MatchCondition(c.WhenCondition, &url, nil, invocation) return len(c.WhenCondition) == 0 || condition, err @@ -245,20 +249,21 @@ func MatchCondition(pairs map[string]MatchPair, url *common.URL, param *common.U if len(sampleValue) > 0 { if !matchPair.isMatch(sampleValue, param) { return false, nil - } else { - result = true } + + result = true } else { if !(matchPair.Matches.Empty()) { return false, nil - } else { - result = true } + + result = true } } return result, nil } +// MatchPair ... type MatchPair struct { Matches *gxset.HashSet Mismatches *gxset.HashSet diff --git a/cluster/router/condition_router_test.go b/cluster/router/condition_router_test.go index 7d8b0d88cab688e6ea10d1562a27de4609d51f58..fc639b9fc77b3b83d01c663fa02a7185a2d03756 100644 --- a/cluster/router/condition_router_test.go +++ b/cluster/router/condition_router_test.go @@ -59,21 +59,21 @@ func (bi *MockInvoker) GetUrl() common.URL { } func getRouteUrl(rule string) *common.URL { - url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") url.AddParam("rule", rule) url.AddParam("force", "true") return &url } func getRouteUrlWithForce(rule, force string) *common.URL { - url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") url.AddParam("rule", rule) url.AddParam("force", force) return &url } func getRouteUrlWithNoForce(rule string) *common.URL { - url, _ := common.NewURL(context.TODO(), "condition://0.0.0.0/com.foo.BarService") + url, _ := common.NewURL("condition://0.0.0.0/com.foo.BarService") url.AddParam("rule", rule) return &url } @@ -93,15 +93,19 @@ type rest struct { var count int -func (bi *MockInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result { count++ - var success bool - var err error = nil + + var ( + success bool + err error + ) if count >= bi.successCount { success = true } else { err = perrors.New("error") } + result := &protocol.RPCResult{Err: err, Rest: rest{tried: count, success: success}} return result } @@ -116,7 +120,7 @@ func TestRoute_matchWhen(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4")) router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) - cUrl, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService") + cUrl, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService") matchWhen, _ := router.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen) rule1 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) @@ -148,9 +152,9 @@ func TestRoute_matchWhen(t *testing.T) { func TestRoute_matchFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() t.Logf("The local ip is %s", localIP) - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invokers := []protocol.Invoker{NewMockInvoker(url1, 1), NewMockInvoker(url2, 2), NewMockInvoker(url3, 3)} rule1 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.3")) rule2 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.* & host != 10.20.3.3")) @@ -164,7 +168,7 @@ func TestRoute_matchFilter(t *testing.T) { router4, _ := NewConditionRouterFactory().Router(getRouteUrl(rule4)) router5, _ := NewConditionRouterFactory().Router(getRouteUrl(rule5)) router6, _ := NewConditionRouterFactory().Router(getRouteUrl(rule6)) - cUrl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + cUrl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") fileredInvokers1 := router1.Route(invokers, cUrl, &invocation.RPCInvocation{}) fileredInvokers2 := router2.Route(invokers, cUrl, &invocation.RPCInvocation{}) fileredInvokers3 := router3.Route(invokers, cUrl, &invocation.RPCInvocation{}) @@ -184,18 +188,18 @@ func TestRoute_methodRoute(t *testing.T) { inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{})) rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) - url, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo") + url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo") matchWhen, _ := router.(*ConditionRouter).MatchWhen(url, inv) assert.Equal(t, true, matchWhen) - url1, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url1, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") matchWhen, _ = router.(*ConditionRouter).MatchWhen(url1, inv) assert.Equal(t, true, matchWhen) - url2, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url2, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4")) router2, _ := NewConditionRouterFactory().Router(getRouteUrl(rule2)) matchWhen, _ = router2.(*ConditionRouter).MatchWhen(url2, inv) assert.Equal(t, false, matchWhen) - url3, _ := common.NewURL(context.TODO(), "consumer://1.1.1.1/com.foo.BarService?methods=getFoo") + url3, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=getFoo") rule3 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4")) router3, _ := NewConditionRouterFactory().Router(getRouteUrl(rule3)) matchWhen, _ = router3.(*ConditionRouter).MatchWhen(url3, inv) @@ -204,12 +208,12 @@ func TestRoute_methodRoute(t *testing.T) { } func TestRoute_ReturnFalse(t *testing.T) { - url, _ := common.NewURL(context.TODO(), "") + url, _ := common.NewURL("") localIP, _ := gxnet.GetLocalIP() invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false")) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 0, len(fileredInvokers)) @@ -217,11 +221,11 @@ func TestRoute_ReturnFalse(t *testing.T) { func TestRoute_ReturnEmpty(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url, _ := common.NewURL(context.TODO(), "") + url, _ := common.NewURL("") invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => ")) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 0, len(fileredInvokers)) @@ -232,7 +236,7 @@ func TestRoute_ReturnAll(t *testing.T) { invokers := []protocol.Invoker{&MockInvoker{}, &MockInvoker{}, &MockInvoker{}} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, invokers, fileredInvokers) @@ -240,16 +244,16 @@ func TestRoute_ReturnAll(t *testing.T) { func TestRoute_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 2, len(fileredInvokers)) @@ -259,16 +263,16 @@ func TestRoute_HostFilter(t *testing.T) { func TestRoute_Empty_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP)) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 2, len(fileredInvokers)) @@ -278,16 +282,16 @@ func TestRoute_Empty_HostFilter(t *testing.T) { func TestRoute_False_HostFilter(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 2, len(fileredInvokers)) @@ -297,16 +301,16 @@ func TestRoute_False_HostFilter(t *testing.T) { func TestRoute_Placeholder(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host")) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrl(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 2, len(fileredInvokers)) @@ -316,16 +320,16 @@ func TestRoute_Placeholder(t *testing.T) { func TestRoute_NoForce(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4")) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrlWithNoForce(rule)) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, invokers, fileredInvokers) @@ -333,16 +337,16 @@ func TestRoute_NoForce(t *testing.T) { func TestRoute_Force(t *testing.T) { localIP, _ := gxnet.GetLocalIP() - url1, _ := common.NewURL(context.TODO(), "dubbo://10.20.3.3:20880/com.foo.BarService") - url2, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) - url3, _ := common.NewURL(context.TODO(), fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService") + url2, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) + url3, _ := common.NewURL(fmt.Sprintf("dubbo://%s:20880/com.foo.BarService", localIP)) invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) invokers := []protocol.Invoker{invoker1, invoker2, invoker3} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 1.2.3.4")) - curl, _ := common.NewURL(context.TODO(), "consumer://"+localIP+"/com.foo.BarService") + curl, _ := common.NewURL("consumer://" + localIP + "/com.foo.BarService") router, _ := NewConditionRouterFactory().Router(getRouteUrlWithForce(rule, "true")) fileredInvokers := router.(*ConditionRouter).Route(invokers, curl, inv) assert.Equal(t, 0, len(fileredInvokers)) diff --git a/cluster/router/router_factory.go b/cluster/router/router_factory.go index a9794cb885badae98445ef4d7c0bbc2230d25d5f..723050939e5080f1fefd230986dc679dfbdc06ed 100644 --- a/cluster/router/router_factory.go +++ b/cluster/router/router_factory.go @@ -27,11 +27,15 @@ func init() { extension.SetRouterFactory("condition", NewConditionRouterFactory) } +// ConditionRouterFactory ... type ConditionRouterFactory struct{} +// NewConditionRouterFactory ... func NewConditionRouterFactory() cluster.RouterFactory { return ConditionRouterFactory{} } + +// Router ... func (c ConditionRouterFactory) Router(url *common.URL) (cluster.Router, error) { return newConditionRouter(url) } diff --git a/common/config/environment.go b/common/config/environment.go index 931f0460917d68d3bd7adcd6a6bc1b49c7d3bba8..071af31152ba4ce3c579f70aa23df59d718ce506 100644 --- a/common/config/environment.go +++ b/common/config/environment.go @@ -27,6 +27,7 @@ import ( "github.com/apache/dubbo-go/config_center" ) +// Environment // There is dubbo.properties file and application level config center configuration which higner than normal config center in java. So in java the // configuration sequence will be config center > application level config center > dubbo.properties > spring bean configuration. // But in go, neither the dubbo.properties file or application level config center configuration will not support for the time being. @@ -45,12 +46,15 @@ var ( once sync.Once ) +// GetEnvInstance ... func GetEnvInstance() *Environment { once.Do(func() { instance = &Environment{configCenterFirst: true} }) return instance } + +// NewEnvInstance ... func NewEnvInstance() { instance = &Environment{configCenterFirst: true} } @@ -63,34 +67,40 @@ func NewEnvInstance() { // return env.configCenterFirst //} +// UpdateExternalConfigMap ... func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.externalConfigMap.Store(k, v) } } +// UpdateAppExternalConfigMap ... func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.appExternalConfigMap.Store(k, v) } } +// Configuration ... func (env *Environment) Configuration() *list.List { - list := list.New() + cfgList := list.New() // The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration - list.PushFront(newInmemoryConfiguration(&(env.externalConfigMap))) - list.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap))) - return list + cfgList.PushFront(newInmemoryConfiguration(&(env.externalConfigMap))) + cfgList.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap))) + return cfgList } +// SetDynamicConfiguration ... func (env *Environment) SetDynamicConfiguration(dc config_center.DynamicConfiguration) { env.dynamicConfiguration = dc } +// GetDynamicConfiguration ... func (env *Environment) GetDynamicConfiguration() config_center.DynamicConfiguration { return env.dynamicConfiguration } +// InmemoryConfiguration ... type InmemoryConfiguration struct { store *sync.Map } @@ -99,6 +109,7 @@ func newInmemoryConfiguration(p *sync.Map) *InmemoryConfiguration { return &InmemoryConfiguration{store: p} } +// GetProperty ... func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { if conf.store == nil { return false, "" @@ -112,6 +123,7 @@ func (conf *InmemoryConfiguration) GetProperty(key string) (bool, string) { return false, "" } +// GetSubProperty ... func (conf *InmemoryConfiguration) GetSubProperty(subKey string) map[string]struct{} { if conf.store == nil { return nil diff --git a/common/constant/default.go b/common/constant/default.go index 6e0f8488783ebe66939436ca14670395e2719be7..992fc32748bb4fc7777cffecc9137663c681c3f7 100644 --- a/common/constant/default.go +++ b/common/constant/default.go @@ -46,7 +46,7 @@ const ( const ( DEFAULT_KEY = "default" PREFIX_DEFAULT_KEY = "default." - DEFAULT_SERVICE_FILTERS = "echo,token,accesslog,tps,execute,pshutdown" + DEFAULT_SERVICE_FILTERS = "echo,token,accesslog,tps,generic_service,execute,pshutdown" DEFAULT_REFERENCE_FILTERS = "cshutdown" GENERIC_REFERENCE_FILTERS = "generic" GENERIC = "$invoke" diff --git a/common/constant/env.go b/common/constant/env.go index 759cb0be0a2bd36a2a345a360c541b7d56813d70..cb5394bb82ec29d1d24e02627e9d8fafff212efa 100644 --- a/common/constant/env.go +++ b/common/constant/env.go @@ -18,7 +18,10 @@ package constant const ( + // CONF_CONSUMER_FILE_PATH ... CONF_CONSUMER_FILE_PATH = "CONF_CONSUMER_FILE_PATH" + // CONF_PROVIDER_FILE_PATH ... CONF_PROVIDER_FILE_PATH = "CONF_PROVIDER_FILE_PATH" - APP_LOG_CONF_FILE = "APP_LOG_CONF_FILE" + // APP_LOG_CONF_FILE ... + APP_LOG_CONF_FILE = "APP_LOG_CONF_FILE" ) diff --git a/common/constant/key.go b/common/constant/key.go index 7538a2995a89b0951c29f532b9ce9e475198f54e..eff704371c7c5b66ca11a846ad7603a01f8b5708 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -137,3 +137,25 @@ const ( NACOS_PROTOCOL_KEY = "protocol" NACOS_PATH_KEY = "path" ) + +const ( + TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx" +) + +const ( + CONSUMER_SIGN_FILTER = "sign" + PROVIDER_AUTH_FILTER = "auth" + SERVICE_AUTH_KEY = "auth" + AUTHENTICATOR_KEY = "authenticator" + DEFAULT_AUTHENTICATOR = "accesskeys" + DEFAULT_ACCESS_KEY_STORAGE = "urlstorage" + ACCESS_KEY_STORAGE_KEY = "accessKey.storage" + REQUEST_TIMESTAMP_KEY = "timestamp" + REQUEST_SIGNATURE_KEY = "signature" + AK_KEY = "ak" + SIGNATURE_STRING_FORMAT = "%s#%s#%s#%s" + PARAMTER_SIGNATURE_ENABLE_KEY = "param.sign" + CONSUMER = "consumer" + ACCESS_KEY_ID_KEY = "accessKeyId" + SECRET_ACCESS_KEY_KEY = "secretAccessKey" +) diff --git a/common/constant/time.go b/common/constant/time.go new file mode 100644 index 0000000000000000000000000000000000000000..be1baaca67f474aa92e86e529d03400948ef4612 --- /dev/null +++ b/common/constant/time.go @@ -0,0 +1,26 @@ +/* + * 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 constant + +import ( + "time" +) + +var ( + MsToNanoRate = int64(time.Millisecond / time.Nanosecond) +) diff --git a/common/constant/version.go b/common/constant/version.go index d4c6821e76894cbd82dc5fae09124263b5c6aa0f..730224376054a36b0c7cfeda7d5ea5e7ce058618 100644 --- a/common/constant/version.go +++ b/common/constant/version.go @@ -18,7 +18,10 @@ package constant const ( - Version = "2.6.0" - Name = "dubbogo" - DATE = "2019/05/06" + // Version apache/dubbo-go version + Version = "1.3.0" + // Name module name + Name = "dubbogo" + // Date release date + DATE = "2020/01/12" ) diff --git a/common/extension/auth.go b/common/extension/auth.go new file mode 100644 index 0000000000000000000000000000000000000000..e57e22f660b6d4dec63f8b4a06c25b05bd5c8d72 --- /dev/null +++ b/common/extension/auth.go @@ -0,0 +1,32 @@ +package extension + +import ( + "github.com/apache/dubbo-go/filter" +) + +var ( + authenticators = make(map[string]func() filter.Authenticator) + accesskeyStorages = make(map[string]func() filter.AccessKeyStorage) +) + +func SetAuthenticator(name string, fcn func() filter.Authenticator) { + authenticators[name] = fcn +} + +func GetAuthenticator(name string) filter.Authenticator { + if authenticators[name] == nil { + panic("authenticator for " + name + " is not existing, make sure you have import the package.") + } + return authenticators[name]() +} + +func SetAccesskeyStorages(name string, fcn func() filter.AccessKeyStorage) { + accesskeyStorages[name] = fcn +} + +func GetAccesskeyStorages(name string) filter.AccessKeyStorage { + if accesskeyStorages[name] == nil { + panic("accesskeyStorages for " + name + " is not existing, make sure you have import the package.") + } + return accesskeyStorages[name]() +} diff --git a/common/extension/cluster.go b/common/extension/cluster.go index 91e9f953b505e31c1a4f448e1504a6ae50a9663f..b2d81f6b1e56bb487b1d408b878308f6dfe042e4 100644 --- a/common/extension/cluster.go +++ b/common/extension/cluster.go @@ -25,10 +25,12 @@ var ( clusters = make(map[string]func() cluster.Cluster) ) +// SetCluster ... func SetCluster(name string, fcn func() cluster.Cluster) { clusters[name] = fcn } +// GetCluster ... func GetCluster(name string) cluster.Cluster { if clusters[name] == nil { panic("cluster for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center.go b/common/extension/config_center.go index be4b62ccdd9c36500c306c7f16abd054f91ae86b..03d27db46c94b0ea0e212646077d97f948a8e328 100644 --- a/common/extension/config_center.go +++ b/common/extension/config_center.go @@ -26,10 +26,12 @@ var ( configCenters = make(map[string]func(config *common.URL) (config_center.DynamicConfiguration, error)) ) +// SetConfigCenter ... func SetConfigCenter(name string, v func(config *common.URL) (config_center.DynamicConfiguration, error)) { configCenters[name] = v } +// GetConfigCenter ... func GetConfigCenter(name string, config *common.URL) (config_center.DynamicConfiguration, error) { if configCenters[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/config_center_factory.go b/common/extension/config_center_factory.go index 82e0ef6ebcf632ccff32aec5c69c2082a28c51af..85913fdce1ed3472c2bd9eb4aadbb0f631481dbd 100644 --- a/common/extension/config_center_factory.go +++ b/common/extension/config_center_factory.go @@ -25,10 +25,12 @@ var ( configCenterFactories = make(map[string]func() config_center.DynamicConfigurationFactory) ) +// SetConfigCenterFactory ... func SetConfigCenterFactory(name string, v func() config_center.DynamicConfigurationFactory) { configCenterFactories[name] = v } +// GetConfigCenterFactory ... func GetConfigCenterFactory(name string) config_center.DynamicConfigurationFactory { if configCenterFactories[name] == nil { panic("config center for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/configurator.go b/common/extension/configurator.go index 40d134f474ae792afb76f1d8e3f56d172bfd07e2..de98f8a260ea1f3a2e2a1f32c82dc869585e2789 100644 --- a/common/extension/configurator.go +++ b/common/extension/configurator.go @@ -22,7 +22,10 @@ import ( "github.com/apache/dubbo-go/config_center" ) -const DefaultKey = "default" +const ( + // DefaultKey ... + DefaultKey = "default" +) type getConfiguratorFunc func(url *common.URL) config_center.Configurator @@ -30,10 +33,12 @@ var ( configurator = make(map[string]getConfiguratorFunc) ) +// SetConfigurator ... func SetConfigurator(name string, v getConfiguratorFunc) { configurator[name] = v } +// GetConfigurator ... func GetConfigurator(name string, url *common.URL) config_center.Configurator { if configurator[name] == nil { panic("configurator for " + name + " is not existing, make sure you have import the package.") @@ -41,10 +46,13 @@ func GetConfigurator(name string, url *common.URL) config_center.Configurator { return configurator[name](url) } + +// SetDefaultConfigurator ... func SetDefaultConfigurator(v getConfiguratorFunc) { configurator[DefaultKey] = v } +// GetDefaultConfigurator ... func GetDefaultConfigurator(url *common.URL) config_center.Configurator { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") @@ -52,6 +60,8 @@ func GetDefaultConfigurator(url *common.URL) config_center.Configurator { return configurator[DefaultKey](url) } + +// GetDefaultConfiguratorFunc ... func GetDefaultConfiguratorFunc() getConfiguratorFunc { if configurator[DefaultKey] == nil { panic("configurator for default is not existing, make sure you have import the package.") diff --git a/common/extension/filter.go b/common/extension/filter.go index 93f7f8cf7ccc4108fe1120b685fad36a2f9f83df..deea2d908bc2741e0f15ecc36e9d4fc5975e531e 100644 --- a/common/extension/filter.go +++ b/common/extension/filter.go @@ -19,18 +19,19 @@ package extension import ( "github.com/apache/dubbo-go/filter" - "github.com/apache/dubbo-go/filter/common" ) var ( filters = make(map[string]func() filter.Filter) - rejectedExecutionHandler = make(map[string]func() common.RejectedExecutionHandler) + rejectedExecutionHandler = make(map[string]func() filter.RejectedExecutionHandler) ) +// SetFilter ... func SetFilter(name string, v func() filter.Filter) { filters[name] = v } +// GetFilter ... func GetFilter(name string) filter.Filter { if filters[name] == nil { panic("filter for " + name + " is not existing, make sure you have imported the package.") @@ -38,11 +39,13 @@ func GetFilter(name string) filter.Filter { return filters[name]() } -func SetRejectedExecutionHandler(name string, creator func() common.RejectedExecutionHandler) { +// SetRejectedExecutionHandler ... +func SetRejectedExecutionHandler(name string, creator func() filter.RejectedExecutionHandler) { rejectedExecutionHandler[name] = creator } -func GetRejectedExecutionHandler(name string) common.RejectedExecutionHandler { +// GetRejectedExecutionHandler ... +func GetRejectedExecutionHandler(name string) filter.RejectedExecutionHandler { creator, ok := rejectedExecutionHandler[name] if !ok { panic("RejectedExecutionHandler for " + name + " is not existing, make sure you have import the package " + diff --git a/common/extension/graceful_shutdown.go b/common/extension/graceful_shutdown.go index c8807fcc28c18c1a6fddb4e97708e9b0d5cda243..3abd75c0aa328f3553c3d83340ae440b8dfe3356 100644 --- a/common/extension/graceful_shutdown.go +++ b/common/extension/graceful_shutdown.go @@ -26,6 +26,7 @@ var ( ) /** + * AddCustomShutdownCallback * you should not make any assumption about the order. * For example, if you have more than one callbacks, and you wish the order is: * callback1() @@ -48,6 +49,7 @@ func AddCustomShutdownCallback(callback func()) { customShutdownCallbacks.PushBack(callback) } +// GetAllCustomShutdownCallbacks ... func GetAllCustomShutdownCallbacks() *list.List { return customShutdownCallbacks } diff --git a/common/extension/loadbalance.go b/common/extension/loadbalance.go index f1f97b9399a2b33a3e06213fc0b2f84e73b002b7..0d557a4640ed892a18ad59a3247763ab5807a593 100644 --- a/common/extension/loadbalance.go +++ b/common/extension/loadbalance.go @@ -25,10 +25,12 @@ var ( loadbalances = make(map[string]func() cluster.LoadBalance) ) +// SetLoadbalance ... func SetLoadbalance(name string, fcn func() cluster.LoadBalance) { loadbalances[name] = fcn } +// GetLoadbalance ... func GetLoadbalance(name string) cluster.LoadBalance { if loadbalances[name] == nil { panic("loadbalance for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/metrics.go b/common/extension/metrics.go new file mode 100644 index 0000000000000000000000000000000000000000..42fca7a2db36614fcef31dd5ba7324a156164d4f --- /dev/null +++ b/common/extension/metrics.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 extension + +import ( + "github.com/apache/dubbo-go/metrics" +) + +var ( + // we couldn't store the instance because the some instance may initialize before loading configuration + // so lazy initialization will be better. + metricReporterMap = make(map[string]func() metrics.Reporter, 4) +) + +// SetMetricReporter set a reporter with the name +func SetMetricReporter(name string, reporterFunc func() metrics.Reporter) { + metricReporterMap[name] = reporterFunc +} + +// GetMetricReporter find the reporter with name. +// if not found, it will panic. +// we should know that this method usually is called when system starts, so we should panic +func GetMetricReporter(name string) metrics.Reporter { + reporterFunc, found := metricReporterMap[name] + if !found { + panic("Cannot find the reporter with name: " + name) + } + return reporterFunc() +} diff --git a/common/extension/metrics_test.go b/common/extension/metrics_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6a8a3fe538a9cd68c57c91592a88ec257ae4a267 --- /dev/null +++ b/common/extension/metrics_test.go @@ -0,0 +1,49 @@ +/* + * 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 ( + "context" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/metrics" + "github.com/apache/dubbo-go/protocol" +) + +func TestGetMetricReporter(t *testing.T) { + reporter := &mockReporter{} + name := "mock" + SetMetricReporter(name, func() metrics.Reporter { + return reporter + }) + res := GetMetricReporter(name) + assert.Equal(t, reporter, res) +} + +type mockReporter struct { +} + +func (m mockReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) { +} diff --git a/common/extension/protocol.go b/common/extension/protocol.go index 50d339476d024c04b182c38632689a99bc5c1680..009687a17ace8cea567248af655e04604d09d9b8 100644 --- a/common/extension/protocol.go +++ b/common/extension/protocol.go @@ -25,10 +25,12 @@ var ( protocols = make(map[string]func() protocol.Protocol) ) +// SetProtocol ... func SetProtocol(name string, v func() protocol.Protocol) { protocols[name] = v } +// GetProtocol ... func GetProtocol(name string) protocol.Protocol { if protocols[name] == nil { panic("protocol for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/proxy_factory.go b/common/extension/proxy_factory.go index 53cbbee54054bf8ad87964393b01ca6601106066..19826bb0560ea0d3fa471c04873b20a6878f57d8 100644 --- a/common/extension/proxy_factory.go +++ b/common/extension/proxy_factory.go @@ -22,18 +22,21 @@ import ( ) var ( - proxy_factories = make(map[string]func(...proxy.Option) proxy.ProxyFactory) + proxyFactories = make(map[string]func(...proxy.Option) proxy.ProxyFactory) ) +// SetProxyFactory ... func SetProxyFactory(name string, f func(...proxy.Option) proxy.ProxyFactory) { - proxy_factories[name] = f + proxyFactories[name] = f } + +// GetProxyFactory ... func GetProxyFactory(name string) proxy.ProxyFactory { if name == "" { name = "default" } - if proxy_factories[name] == nil { + if proxyFactories[name] == nil { panic("proxy factory for " + name + " is not existing, make sure you have import the package.") } - return proxy_factories[name]() + return proxyFactories[name]() } diff --git a/common/extension/registry.go b/common/extension/registry.go index 776c2b5df542e56f8c120c850f20093a971d8602..6ba746dc47382927d12ce39b7936212c5d75153d 100644 --- a/common/extension/registry.go +++ b/common/extension/registry.go @@ -26,10 +26,12 @@ var ( registrys = make(map[string]func(config *common.URL) (registry.Registry, error)) ) +// SetRegistry ... func SetRegistry(name string, v func(config *common.URL) (registry.Registry, error)) { registrys[name] = v } +// GetRegistry ... 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.") diff --git a/common/extension/router_factory.go b/common/extension/router_factory.go index 6f27aafaebf87147116e74272cc229657f436201..c77cc291369ab02c5f58dfc6c283902ac0df4b95 100644 --- a/common/extension/router_factory.go +++ b/common/extension/router_factory.go @@ -25,10 +25,12 @@ var ( routers = make(map[string]func() cluster.RouterFactory) ) +// SetRouterFactory ... func SetRouterFactory(name string, fun func() cluster.RouterFactory) { routers[name] = fun } +// GetRouterFactory ... func GetRouterFactory(name string) cluster.RouterFactory { if routers[name] == nil { panic("router_factory for " + name + " is not existing, make sure you have import the package.") diff --git a/common/extension/tps_limit.go b/common/extension/tps_limit.go index 151c33ad5e64ffa4059489e2cbcfae6f2e823328..c72c2b030fc0f391362189bfe18a65582543693a 100644 --- a/common/extension/tps_limit.go +++ b/common/extension/tps_limit.go @@ -18,19 +18,21 @@ package extension import ( - "github.com/apache/dubbo-go/filter/impl/tps" + "github.com/apache/dubbo-go/filter" ) var ( - tpsLimitStrategy = make(map[string]tps.TpsLimitStrategyCreator) - tpsLimiter = make(map[string]func() tps.TpsLimiter) + tpsLimitStrategy = make(map[string]filter.TpsLimitStrategyCreator) + tpsLimiter = make(map[string]func() filter.TpsLimiter) ) -func SetTpsLimiter(name string, creator func() tps.TpsLimiter) { +// SetTpsLimiter ... +func SetTpsLimiter(name string, creator func() filter.TpsLimiter) { tpsLimiter[name] = creator } -func GetTpsLimiter(name string) tps.TpsLimiter { +// GetTpsLimiter ... +func GetTpsLimiter(name string) filter.TpsLimiter { creator, ok := tpsLimiter[name] if !ok { panic("TpsLimiter for " + name + " is not existing, make sure you have import the package " + @@ -39,11 +41,13 @@ func GetTpsLimiter(name string) tps.TpsLimiter { return creator() } -func SetTpsLimitStrategy(name string, creator tps.TpsLimitStrategyCreator) { +// SetTpsLimitStrategy ... +func SetTpsLimitStrategy(name string, creator filter.TpsLimitStrategyCreator) { tpsLimitStrategy[name] = creator } -func GetTpsLimitStrategyCreator(name string) tps.TpsLimitStrategyCreator { +// GetTpsLimitStrategyCreator ... +func GetTpsLimitStrategyCreator(name string) filter.TpsLimitStrategyCreator { creator, ok := tpsLimitStrategy[name] if !ok { panic("TpsLimitStrategy for " + name + " is not existing, make sure you have import the package " + diff --git a/common/logger/logger.go b/common/logger/logger.go index db91d2e7c1e5f7a647eefbfa5aec14073c2b14a7..016afe69808f2007541c617f406db64beb511f1c 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -40,11 +40,13 @@ var ( logger Logger ) +// DubboLogger ... type DubboLogger struct { Logger dynamicLevel zap.AtomicLevel } +// Logger ... type Logger interface { Info(args ...interface{}) Warn(args ...interface{}) @@ -65,6 +67,7 @@ func init() { } } +// InitLog ... func InitLog(logConfFile string) error { if logConfFile == "" { InitLogger(nil) @@ -93,6 +96,7 @@ func InitLog(logConfFile string) error { return nil } +// InitLogger ... func InitLogger(conf *zap.Config) { var zapLoggerConfig zap.Config if conf == nil { @@ -121,15 +125,18 @@ func InitLogger(conf *zap.Config) { getty.SetLogger(logger) } +// SetLogger ... func SetLogger(log Logger) { logger = log getty.SetLogger(logger) } +// GetLogger ... func GetLogger() Logger { return logger } +// SetLoggerLevel ... func SetLoggerLevel(level string) bool { if l, ok := logger.(OpsLogger); ok { l.SetLoggerLevel(level) @@ -138,11 +145,13 @@ func SetLoggerLevel(level string) bool { return false } +// OpsLogger ... type OpsLogger interface { Logger SetLoggerLevel(level string) } +// SetLoggerLevel ... func (dl *DubboLogger) SetLoggerLevel(level string) { l := new(zapcore.Level) l.Set(level) diff --git a/common/logger/logging.go b/common/logger/logging.go index 4638c9a41dfe986d256e1ff4d52b690c1747fc94..36d48ee61e8a4a986abfbaa79f3d361cd81494f4 100644 --- a/common/logger/logging.go +++ b/common/logger/logging.go @@ -17,27 +17,42 @@ package logger +// Info ... func Info(args ...interface{}) { logger.Info(args...) } + +// Warn ... func Warn(args ...interface{}) { logger.Warn(args...) } + +// Error ... func Error(args ...interface{}) { logger.Error(args...) } + +// Debug ... func Debug(args ...interface{}) { logger.Debug(args...) } + +// Infof ... func Infof(fmt string, args ...interface{}) { logger.Infof(fmt, args...) } + +// Warnf ... func Warnf(fmt string, args ...interface{}) { logger.Warnf(fmt, args...) } + +// Errorf ... func Errorf(fmt string, args ...interface{}) { logger.Errorf(fmt, args...) } + +// Debugf ... func Debugf(fmt string, args ...interface{}) { logger.Debugf(fmt, args...) } diff --git a/common/node.go b/common/node.go index 992ead38d8acf85bbb14f02eebca4c4fe5a0a1e2..979eee31ef3a63eb21af6c9045aee7f6d784f2ba 100644 --- a/common/node.go +++ b/common/node.go @@ -17,6 +17,7 @@ package common +// Node ... type Node interface { GetUrl() URL IsAvailable() bool diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go index d13646dba86eea04adb3726d33ee9d20457276b6..43ca720d0e71577a446829f702c1d2fe23a32905 100644 --- a/common/proxy/proxy.go +++ b/common/proxy/proxy.go @@ -18,6 +18,7 @@ package proxy import ( + "context" "reflect" "sync" ) @@ -39,8 +40,11 @@ type Proxy struct { once sync.Once } -var typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type() +var ( + typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type() +) +// NewProxy ... func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy { return &Proxy{ invoke: invoke, @@ -49,6 +53,7 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str } } +// Implement // proxy implement // In consumer, RPCService like: // type XxxProvider struct { @@ -72,10 +77,11 @@ func (p *Proxy) Implement(v common.RPCService) { makeDubboCallProxy := func(methodName string, outs []reflect.Type) func(in []reflect.Value) []reflect.Value { return func(in []reflect.Value) []reflect.Value { var ( - err error - inv *invocation_impl.RPCInvocation - inArr []interface{} - reply reflect.Value + err error + inv *invocation_impl.RPCInvocation + inIArr []interface{} + inVArr []reflect.Value + reply reflect.Value ) if methodName == "Echo" { methodName = "$echo" @@ -93,8 +99,13 @@ func (p *Proxy) Implement(v common.RPCService) { start := 0 end := len(in) + invCtx := context.Background() if end > 0 { if in[0].Type().String() == "context.Context" { + if !in[0].IsNil() { + // the user declared context as method's parameter + invCtx = in[0].Interface().(context.Context) + } start += 1 } if len(outs) == 1 && in[end-1].Type().Kind() == reflect.Ptr { @@ -104,27 +115,31 @@ func (p *Proxy) Implement(v common.RPCService) { } if end-start <= 0 { - inArr = []interface{}{} + inIArr = []interface{}{} + inVArr = []reflect.Value{} } else if v, ok := in[start].Interface().([]interface{}); ok && end-start == 1 { - inArr = v + inIArr = v + inVArr = []reflect.Value{in[start]} } else { - inArr = make([]interface{}, end-start) + inIArr = make([]interface{}, end-start) + inVArr = make([]reflect.Value, end-start) index := 0 for i := start; i < end; i++ { - inArr[index] = in[i].Interface() + inIArr[index] = in[i].Interface() + inVArr[index] = in[i] index++ } } inv = invocation_impl.NewRPCInvocationWithOptions(invocation_impl.WithMethodName(methodName), - invocation_impl.WithArguments(inArr), invocation_impl.WithReply(reply.Interface()), - invocation_impl.WithCallBack(p.callBack)) + invocation_impl.WithArguments(inIArr), invocation_impl.WithReply(reply.Interface()), + invocation_impl.WithCallBack(p.callBack), invocation_impl.WithParameterValues(inVArr)) for k, value := range p.attachments { inv.SetAttachments(k, value) } - result := p.invoke.Invoke(inv) + result := p.invoke.Invoke(invCtx, inv) err = result.Error() logger.Infof("[makeDubboCallProxy] result: %v, err: %v", result.Result(), err) @@ -178,10 +193,12 @@ func (p *Proxy) Implement(v common.RPCService) { } +// Get ... func (p *Proxy) Get() common.RPCService { return p.rpc } +// GetCallback ... func (p *Proxy) GetCallback() interface{} { return p.callBack } diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go index 116cfe06693b6923ca10e0df6964317dabd91d0e..7b249a3e9754b097130a80bf3819d282dad6b6e8 100644 --- a/common/proxy/proxy_factory.go +++ b/common/proxy/proxy_factory.go @@ -22,10 +22,12 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// ProxyFactory ... type ProxyFactory interface { GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy GetInvoker(url common.URL) protocol.Invoker } +// Option ... type Option func(ProxyFactory) diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go index 06824fdc1e27cde5e1905be3277451dd4395049c..114cfee2363022da5f7957a825a16fc42b8c928f 100644 --- a/common/proxy/proxy_factory/default.go +++ b/common/proxy/proxy_factory/default.go @@ -18,6 +18,7 @@ package proxy_factory import ( + "context" "reflect" "strings" ) @@ -39,6 +40,7 @@ func init() { extension.SetProxyFactory("default", NewDefaultProxyFactory) } +// DefaultProxyFactory ... type DefaultProxyFactory struct { //delegate ProxyFactory } @@ -51,13 +53,17 @@ type DefaultProxyFactory struct { // } //} +// NewDefaultProxyFactory ... func NewDefaultProxyFactory(options ...proxy.Option) proxy.ProxyFactory { return &DefaultProxyFactory{} } + +// GetProxy ... func (factory *DefaultProxyFactory) GetProxy(invoker protocol.Invoker, url *common.URL) *proxy.Proxy { return factory.GetAsyncProxy(invoker, nil, url) } +// GetAsyncProxy ... func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *proxy.Proxy { //create proxy attachments := map[string]string{} @@ -65,17 +71,20 @@ func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, call return proxy.NewProxy(invoker, callBack, attachments) } +// GetInvoker ... func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker { return &ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), } } +// ProxyInvoker ... type ProxyInvoker struct { protocol.BaseInvoker } -func (pi *ProxyInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +// Invoke ... +func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { result := &protocol.RPCResult{} result.SetAttachments(invocation.Attachments()) @@ -104,7 +113,7 @@ func (pi *ProxyInvoker) Invoke(invocation protocol.Invocation) protocol.Result { in := []reflect.Value{svc.Rcvr()} if method.CtxType() != nil { - in = append(in, method.SuiteContext(nil)) // todo: ctx will be used later. + in = append(in, method.SuiteContext(ctx)) } // prepare argv diff --git a/common/rpc_service.go b/common/rpc_service.go index 4c9f083dd0850c3f110491ef820c7b677c8009aa..b235c32abc9a971d7144605c8b4b82953ac8f3c4 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -34,19 +34,22 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -// rpc service interface +// RPCService +//rpc service interface type RPCService interface { - Reference() string // rpc service id or reference id + // Reference: + // rpc service id or reference id + Reference() string } //AsyncCallbackService callback interface for async type AsyncCallbackService interface { - CallBack(response CallbackResponse) // callback + // Callback: callback + CallBack(response CallbackResponse) } //CallbackResponse for different protocol -type CallbackResponse interface { -} +type CallbackResponse interface{} //AsyncCallback async callback method type AsyncCallback func(response CallbackResponse) @@ -55,13 +58,17 @@ type AsyncCallback func(response CallbackResponse) // func MethodMapper() map[string][string] { // return map[string][string]{} // } -const METHOD_MAPPER = "MethodMapper" +const ( + // METHOD_MAPPER ... + METHOD_MAPPER = "MethodMapper" +) var ( // Precompute the reflect type for error. Can't use error directly // because Typeof takes an empty interface value. This is annoying. typeOfError = reflect.TypeOf((*error)(nil)).Elem() + // ServiceMap ... // todo: lowerecas? ServiceMap = &serviceMap{ serviceMap: make(map[string]map[string]*Service), @@ -72,6 +79,7 @@ var ( // info of method ////////////////////////// +// MethodType ... type MethodType struct { method reflect.Method ctxType reflect.Type // request context @@ -79,18 +87,27 @@ type MethodType struct { replyType reflect.Type // return value, otherwise it is nil } +// Method ... func (m *MethodType) Method() reflect.Method { return m.method } + +// CtxType ... func (m *MethodType) CtxType() reflect.Type { return m.ctxType } + +// ArgsType ... func (m *MethodType) ArgsType() []reflect.Type { return m.argsType } + +// ReplyType ... func (m *MethodType) ReplyType() reflect.Type { return m.replyType } + +// SuiteContext ... func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { if contextv := reflect.ValueOf(ctx); contextv.IsValid() { return contextv @@ -102,6 +119,7 @@ func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { // info of service interface ////////////////////////// +// Service ... type Service struct { name string rcvr reflect.Value @@ -109,12 +127,17 @@ type Service struct { methods map[string]*MethodType } +// Method ... func (s *Service) Method() map[string]*MethodType { return s.methods } + +// RcvrType ... func (s *Service) RcvrType() reflect.Type { return s.rcvrType } + +// Rcvr ... func (s *Service) Rcvr() reflect.Value { return s.rcvr } @@ -210,8 +233,8 @@ func (sm *serviceMap) UnRegister(protocol, serviceId string) error { // Is this an exported - upper case - name func isExported(name string) bool { - rune, _ := utf8.DecodeRuneInString(name) - return unicode.IsUpper(rune) + s, _ := utf8.DecodeRuneInString(name) + return unicode.IsUpper(s) } // Is this type exported or a builtin? diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go index 7df039b905d3cc064c5d6d9404fc874cf693dac9..8c9b9d15cdd4061dbe2f445b5fff7a868e5ae67e 100644 --- a/common/rpc_service_test.go +++ b/common/rpc_service_test.go @@ -122,9 +122,8 @@ func TestServiceMap_UnRegister(t *testing.T) { func TestMethodType_SuiteContext(t *testing.T) { mt := &MethodType{ctxType: reflect.TypeOf(context.TODO())} - c := context.TODO() - c = context.WithValue(c, "key", "value") - assert.Equal(t, reflect.ValueOf(c), mt.SuiteContext(c)) + ctx := context.WithValue(context.Background(), "key", "value") + assert.Equal(t, reflect.ValueOf(ctx), mt.SuiteContext(ctx)) assert.Equal(t, reflect.Zero(mt.ctxType), mt.SuiteContext(nil)) } diff --git a/common/url.go b/common/url.go index c010298bf58d65b6c2fcfc7859e03b0131bf7f54..360f0aa4caa185d8086eb07497176ae627f47d47 100644 --- a/common/url.go +++ b/common/url.go @@ -19,7 +19,6 @@ package common import ( "bytes" - "context" "encoding/base64" "fmt" "math" @@ -31,7 +30,7 @@ import ( ) import ( - "github.com/dubbogo/gost/container/gxset" + gxset "github.com/dubbogo/gost/container/set" "github.com/jinzhu/copier" perrors "github.com/pkg/errors" "github.com/satori/go.uuid" @@ -45,24 +44,33 @@ import ( // dubbo role type ///////////////////////////////// +// role constant const ( + // CONSUMER ... CONSUMER = iota + // CONFIGURATOR ... CONFIGURATOR + // ROUTER ... ROUTER + // PROVIDER ... PROVIDER ) var ( + // DubboNodes ... DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"} - DubboRole = [...]string{"consumer", "", "", "provider"} + // DubboRole ... + DubboRole = [...]string{"consumer", "", "", "provider"} ) +// RoleType ... type RoleType int func (t RoleType) String() string { return DubboNodes[t] } +// Role ... func (t RoleType) Role() string { return DubboRole[t] } @@ -76,9 +84,9 @@ type baseUrl struct { paramsLock sync.RWMutex params url.Values PrimitiveURL string - ctx context.Context } +// URL ... type URL struct { baseUrl Path string // like /com.ikurento.dubbo.UserProvider3 @@ -91,66 +99,77 @@ type URL struct { type option func(*URL) +// WithUsername ... func WithUsername(username string) option { return func(url *URL) { url.Username = username } } +// WithPassword ... func WithPassword(pwd string) option { return func(url *URL) { url.Password = pwd } } +// WithMethods ... func WithMethods(methods []string) option { return func(url *URL) { url.Methods = methods } } +// WithParams ... func WithParams(params url.Values) option { return func(url *URL) { url.params = params } } +// WithParamsValue ... func WithParamsValue(key, val string) option { return func(url *URL) { url.SetParam(key, val) } } +// WithProtocol ... func WithProtocol(proto string) option { return func(url *URL) { url.Protocol = proto } } +// WithIp ... func WithIp(ip string) option { return func(url *URL) { url.Ip = ip } } +// WithPort ... func WithPort(port string) option { return func(url *URL) { url.Port = port } } +// WithPath ... func WithPath(path string) option { return func(url *URL) { url.Path = "/" + strings.TrimPrefix(path, "/") } } +// WithLocation ... func WithLocation(location string) option { return func(url *URL) { url.Location = location } } +// WithToken ... func WithToken(token string) option { return func(url *URL) { if len(token) > 0 { @@ -163,6 +182,7 @@ func WithToken(token string) option { } } +// NewURLWithOptions ... func NewURLWithOptions(opts ...option) *URL { url := &URL{} for _, opt := range opts { @@ -172,13 +192,15 @@ func NewURLWithOptions(opts ...option) *URL { return url } -func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) { +// NewURL will create a new url +// the urlString should not be empty +func NewURL(urlString string, opts ...option) (URL, error) { var ( err error rawUrlString string serviceUrl *url.URL - s = URL{baseUrl: baseUrl{ctx: ctx}} + s = URL{baseUrl: baseUrl{}} ) // new a null instance @@ -193,7 +215,7 @@ func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) //rawUrlString = "//" + rawUrlString if strings.Index(rawUrlString, "//") < 0 { - t := URL{baseUrl: baseUrl{ctx: ctx}} + t := URL{baseUrl: baseUrl{}} for _, opt := range opts { opt(&t) } @@ -227,6 +249,7 @@ func NewURL(ctx context.Context, urlString string, opts ...option) (URL, error) return s, nil } +// URLEqual ... func (c URL) URLEqual(url URL) bool { c.Ip = "" c.Port = "" @@ -282,6 +305,7 @@ func (c URL) String() string { return buildString } +// Key ... func (c URL) Key() string { buildString := fmt.Sprintf( "%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s", @@ -290,6 +314,7 @@ func (c URL) Key() string { //return c.ServiceKey() } +// ServiceKey ... func (c URL) ServiceKey() string { intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if intf == "" { @@ -313,15 +338,35 @@ func (c URL) ServiceKey() string { return buf.String() } +// ColonSeparatedKey +// The format is "{interface}:[version]:[group]" +func (c *URL) ColonSeparatedKey() string { + intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) + if intf == "" { + return "" + } + buf := &bytes.Buffer{} + buf.WriteString(intf) + buf.WriteString(":") + version := c.GetParam(constant.VERSION_KEY, "") + if version != "" && version != "0.0.0" { + buf.WriteString(version) + } + group := c.GetParam(constant.GROUP_KEY, "") + buf.WriteString(":") + if group != "" { + buf.WriteString(group) + } + return buf.String() +} + +// EncodedServiceKey ... func (c *URL) EncodedServiceKey() string { serviceKey := c.ServiceKey() return strings.Replace(serviceKey, "/", "*", 1) } -func (c URL) Context() context.Context { - return c.ctx -} - +// Service ... func (c URL) Service() string { service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if service != "" { @@ -335,18 +380,21 @@ func (c URL) Service() string { return "" } +// AddParam ... func (c *URL) AddParam(key string, value string) { c.paramsLock.Lock() c.params.Add(key, value) c.paramsLock.Unlock() } +// SetParam ... func (c *URL) SetParam(key string, value string) { c.paramsLock.Lock() c.params.Set(key, value) c.paramsLock.Unlock() } +// RangeParams ... func (c *URL) RangeParams(f func(key, value string) bool) { c.paramsLock.RLock() defer c.paramsLock.RUnlock() @@ -357,6 +405,7 @@ func (c *URL) RangeParams(f func(key, value string) bool) { } } +// GetParam ... func (c URL) GetParam(s string, d string) string { var r string c.paramsLock.RLock() @@ -367,10 +416,12 @@ func (c URL) GetParam(s string, d string) string { return r } +// GetParams ... func (c URL) GetParams() url.Values { return c.params } +// GetParamAndDecoded ... func (c URL) GetParamAndDecoded(key string) (string, error) { c.paramsLock.RLock() defer c.paramsLock.RUnlock() @@ -379,6 +430,7 @@ func (c URL) GetParamAndDecoded(key string) (string, error) { return value, err } +// GetRawParam ... func (c URL) GetRawParam(key string) string { switch key { case "protocol": @@ -398,7 +450,7 @@ func (c URL) GetRawParam(key string) string { } } -// GetParamBool +// GetParamBool ... func (c URL) GetParamBool(s string, d bool) bool { var r bool @@ -409,6 +461,7 @@ func (c URL) GetParamBool(s string, d bool) bool { return r } +// GetParamInt ... func (c URL) GetParamInt(s string, d int64) int64 { var r int var err error @@ -419,6 +472,7 @@ func (c URL) GetParamInt(s string, d int64) int64 { return int64(r) } +// GetMethodParamInt ... func (c URL) GetMethodParamInt(method string, key string, d int64) int64 { var r int var err error @@ -430,6 +484,7 @@ func (c URL) GetMethodParamInt(method string, key string, d int64) int64 { return int64(r) } +// GetMethodParamInt64 ... func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { r := c.GetMethodParamInt(method, key, math.MinInt64) if r == math.MinInt64 { @@ -439,6 +494,7 @@ func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { return r } +// GetMethodParam ... func (c URL) GetMethodParam(method string, key string, d string) string { var r string if r = c.GetParam("methods."+method+"."+key, ""); r == "" { @@ -447,11 +503,13 @@ func (c URL) GetMethodParam(method string, key string, d string) string { return r } +// GetMethodParamBool ... func (c URL) GetMethodParamBool(method string, key string, d bool) bool { r := c.GetParamBool("methods."+method+"."+key, d) return r } +// RemoveParams ... func (c *URL) RemoveParams(set *gxset.HashSet) { c.paramsLock.Lock() defer c.paramsLock.Unlock() @@ -461,6 +519,7 @@ func (c *URL) RemoveParams(set *gxset.HashSet) { } } +// SetParams ... func (c *URL) SetParams(m url.Values) { for k := range m { c.SetParam(k, m.Get(k)) @@ -512,6 +571,7 @@ func (c URL) ToMap() map[string]string { // in this function we should merge the reference local url config into the service url from registry. //TODO configuration merge, in the future , the configuration center's config should merge too. +// MergeUrl ... func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { mergedUrl := serviceUrl.Clone() @@ -523,7 +583,7 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { return true }) //loadBalance,cluster,retries strategy config - methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY}) + methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY}) //remote timestamp if v := serviceUrl.GetParam(constant.TIMESTAMP_KEY, ""); len(v) > 0 { @@ -540,6 +600,8 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { return mergedUrl } + +// Clone ... func (c *URL) Clone() *URL { newUrl := &URL{} copier.Copy(newUrl, c) diff --git a/common/url_test.go b/common/url_test.go index 4d60d7f13f5d139e964b0837380f1054871c5d15..835973065b6d7426e5487fe76602ca27701130a1 100644 --- a/common/url_test.go +++ b/common/url_test.go @@ -18,7 +18,6 @@ package common import ( - "context" "encoding/base64" "net/url" "testing" @@ -56,10 +55,10 @@ func TestNewURLWithOptions(t *testing.T) { } func TestURL(t *testing.T) { - u, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + u, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) @@ -83,7 +82,7 @@ func TestURL(t *testing.T) { } func TestURLWithoutSchema(t *testing.T) { - u, err := NewURL(context.TODO(), "127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ + u, err := NewURL("127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ @@ -110,13 +109,13 @@ func TestURLWithoutSchema(t *testing.T) { } func TestURL_URLEqual(t *testing.T) { - u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") + u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") assert.NoError(t, err) - u2, err := NewURL(context.TODO(), "dubbo://127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") + u2, err := NewURL("dubbo://127.0.0.2:20001/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0") assert.NoError(t, err) assert.True(t, u1.URLEqual(u2)) - u3, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + 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)) } @@ -166,7 +165,7 @@ func TestURL_GetParamAndDecoded(t *testing.T) { assert.Equal(t, rule, v) } func TestURL_GetRawParam(t *testing.T) { - u, _ := NewURL(context.TODO(), "condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") + u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") u.Username = "test" u.Password = "test" assert.Equal(t, "condition", u.GetRawParam("protocol")) @@ -178,7 +177,7 @@ func TestURL_GetRawParam(t *testing.T) { assert.Equal(t, "fastjson", u.GetRawParam("serialization")) } func TestURL_ToMap(t *testing.T) { - u, _ := NewURL(context.TODO(), "condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") + u, _ := NewURL("condition://0.0.0.0:8080/com.foo.BarService?serialization=fastjson") u.Username = "test" u.Password = "test" @@ -239,20 +238,20 @@ func TestMergeUrl(t *testing.T) { serviceUrlParams.Set("test2", "1") serviceUrlParams.Set(constant.CLUSTER_KEY, "roundrobin") serviceUrlParams.Set(constant.RETRIES_KEY, "2") - serviceUrlParams.Set("methods.testMethod."+constant.RETRIES_KEY, "2") - referenceUrl, _ := NewURL(context.TODO(), "mock1://127.0.0.1:1111", WithParams(referenceUrlParams), WithMethods([]string{"testMethod"})) - serviceUrl, _ := NewURL(context.TODO(), "mock2://127.0.0.1:20000", WithParams(serviceUrlParams)) + serviceUrlParams.Set(constant.METHOD_KEYS+".testMethod."+constant.RETRIES_KEY, "2") + referenceUrl, _ := NewURL("mock1://127.0.0.1:1111", WithParams(referenceUrlParams), WithMethods([]string{"testMethod"})) + serviceUrl, _ := NewURL("mock2://127.0.0.1:20000", WithParams(serviceUrlParams)) mergedUrl := MergeUrl(&serviceUrl, &referenceUrl) assert.Equal(t, "random", mergedUrl.GetParam(constant.CLUSTER_KEY, "")) assert.Equal(t, "1", mergedUrl.GetParam("test2", "")) assert.Equal(t, "1", mergedUrl.GetParam("test3", "")) assert.Equal(t, "1", mergedUrl.GetParam(constant.RETRIES_KEY, "")) - assert.Equal(t, "1", mergedUrl.GetParam("methods.testMethod."+constant.RETRIES_KEY, "")) + assert.Equal(t, "2", mergedUrl.GetParam(constant.METHOD_KEYS+".testMethod."+constant.RETRIES_KEY, "")) } func TestURL_SetParams(t *testing.T) { - u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") + u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") assert.NoError(t, err) params := url.Values{} params.Set("key", "3") @@ -262,7 +261,7 @@ func TestURL_SetParams(t *testing.T) { } func TestClone(t *testing.T) { - u1, err := NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") + u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") assert.NoError(t, err) u2 := u1.Clone() assert.Equal(t, u2.Protocol, "dubbo") @@ -271,3 +270,17 @@ func TestClone(t *testing.T) { assert.Equal(t, u1.Protocol, "dubbo") assert.Equal(t, u2.Protocol, "provider") } + +func TestColonSeparatedKey(t *testing.T) { + u1, _ := NewURL("dubbo://127.0.0.1:20000") + u1.AddParam(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider") + + assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::") + u1.AddParam(constant.VERSION_KEY, "version1") + assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:") + u1.AddParam(constant.GROUP_KEY, "group1") + assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:group1") + u1.SetParam(constant.VERSION_KEY, "") + assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::group1") + +} diff --git a/config/application_config.go b/config/application_config.go index fcd4d38c9b55963c32d58fdd1b80375083a76d8c..23ab7d34aceaba02d7f592906d6f4e3d6cf36dae 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -25,6 +25,7 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// ApplicationConfig ... type ApplicationConfig struct { Organization string `yaml:"organization" json:"organization,omitempty" property:"organization"` Name string `yaml:"name" json:"name,omitempty" property:"name"` @@ -34,15 +35,22 @@ type ApplicationConfig struct { Environment string `yaml:"environment" json:"environment,omitempty" property:"environment"` } +// Prefix ... func (*ApplicationConfig) Prefix() string { return constant.DUBBO + ".application." } + +// Id ... func (c *ApplicationConfig) Id() string { return "" } + +// SetId ... func (c *ApplicationConfig) SetId(id string) { } + +// UnmarshalYAML ... 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 64418f0a6d4c09270d48e6e9e6366a02783508d3..942e966eb31e9570e957ad74aa696aa9ef29c5b0 100644 --- a/config/base_config.go +++ b/config/base_config.go @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config import ( - "context" "reflect" "strconv" "strings" @@ -39,15 +39,21 @@ type multiConfiger interface { Prefix() string } +// BaseConfig is the common configuration for provider and consumer type BaseConfig struct { ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` configCenterUrl *common.URL prefix string fatherConfig interface{} + + MetricConfig *MetricConfig `yaml:"metrics" json:"metrics,omitempty"` } -func (c *BaseConfig) startConfigCenter(ctx context.Context) error { - url, err := common.NewURL(ctx, c.ConfigCenterConfig.Address, common.WithProtocol(c.ConfigCenterConfig.Protocol), common.WithParams(c.ConfigCenterConfig.GetUrlMap())) +// startConfigCenter will start the config center. +// it will prepare the environment +func (c *BaseConfig) startConfigCenter() error { + url, err := common.NewURL(c.ConfigCenterConfig.Address, + common.WithProtocol(c.ConfigCenterConfig.Protocol), common.WithParams(c.ConfigCenterConfig.GetUrlMap())) if err != nil { return err } @@ -89,6 +95,9 @@ func (c *BaseConfig) prepareEnvironment() error { configFile = c.ConfigCenterConfig.ConfigFile } appContent, err = dynamicConfig.GetProperties(configFile, config_center.WithGroup(appGroup)) + if err != nil { + return perrors.WithStack(err) + } } //global config file mapContent, err := dynamicConfig.Parser().Parse(content) @@ -294,8 +303,8 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC func (c *BaseConfig) fresh() { configList := config.GetEnvInstance().Configuration() for element := configList.Front(); element != nil; element = element.Next() { - config := element.Value.(*config.InmemoryConfiguration) - c.freshInternalConfig(config) + cfg := element.Value.(*config.InmemoryConfiguration) + c.freshInternalConfig(cfg) } } @@ -308,6 +317,7 @@ func (c *BaseConfig) freshInternalConfig(config *config.InmemoryConfiguration) { setFieldValue(val, reflect.Value{}, config) } +// SetFatherConfig ... func (c *BaseConfig) SetFatherConfig(fatherConfig interface{}) { c.fatherConfig = fatherConfig } diff --git a/config/base_config_test.go b/config/base_config_test.go index ab2769578072387e4686593f3c2c10fb8e49731d..d16b2420922ece60ef2135729cd47d5aa73a3760 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -17,7 +17,6 @@ package config import ( - "context" "fmt" "reflect" "testing" @@ -492,7 +491,7 @@ func Test_startConfigCenter(t *testing.T) { Group: "dubbo", ConfigFile: "mockDubbo.properties", }} - err := c.startConfigCenter(context.Background()) + err := c.startConfigCenter() assert.NoError(t, err) b, v := config.GetEnvInstance().Configuration().Back().Value.(*config.InmemoryConfiguration).GetProperty("dubbo.application.organization") assert.True(t, b) diff --git a/config/config_center_config.go b/config/config_center_config.go index 013d23946a042906021d3b1d37b38f326f67f50a..40b9b6517186a8a4f7956db3d23f0a1cdfbdc8cb 100644 --- a/config/config_center_config.go +++ b/config/config_center_config.go @@ -31,6 +31,7 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// ConfigCenterConfig ... type ConfigCenterConfig struct { context context.Context Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty"` @@ -40,13 +41,14 @@ type ConfigCenterConfig struct { Username string `yaml:"username" json:"username,omitempty"` Password string `yaml:"password" json:"password,omitempty"` ConfigFile string `default:"dubbo.properties" yaml:"config_file" json:"config_file,omitempty"` - Namespace string `default:"dubbo.properties" yaml:"namespace" json:"namespace,omitempty"` + Namespace string `default:"dubbo" yaml:"namespace" json:"namespace,omitempty"` AppConfigFile string `default:"dubbo.properties" yaml:"app_config_file" json:"app_config_file,omitempty"` AppId string `default:"dubbo" yaml:"app_id" json:"app_id,omitempty"` TimeoutStr string `yaml:"timeout" json:"timeout,omitempty"` timeout time.Duration } +// UnmarshalYAML ... func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -58,6 +60,7 @@ func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) er return nil } +// GetUrlMap ... func (c *ConfigCenterConfig) GetUrlMap() url.Values { urlMap := url.Values{} urlMap.Set(constant.CONFIG_NAMESPACE_KEY, c.Namespace) diff --git a/config/config_loader.go b/config/config_loader.go index 414bb479025c5d6111a6373fa2626f21ffa73ef0..875d1f6ddb84434d32296076cd31be96c1385b8a 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -31,9 +31,11 @@ import ( ) var ( - consumerConfig *ConsumerConfig - providerConfig *ProviderConfig - maxWait = 3 + consumerConfig *ConsumerConfig + providerConfig *ProviderConfig + metricConfig *MetricConfig + applicationConfig *ApplicationConfig + maxWait = 3 ) // loaded consumer & provider config from xxx.yml, and log config from xxx.xml @@ -69,12 +71,16 @@ func checkApplicationName(config *ApplicationConfig) { } } -// Dubbo Init +// Load Dubbo Init func Load() { // reference config if consumerConfig == nil { logger.Warnf("consumerConfig is nil!") } else { + + metricConfig = consumerConfig.MetricConfig + applicationConfig = consumerConfig.ApplicationConfig + checkApplicationName(consumerConfig.ApplicationConfig) if err := configCenterRefreshConsumer(); err != nil { logger.Errorf("[consumer config center refresh] %#v", err) @@ -91,7 +97,7 @@ func Load() { continue } ref.id = key - ref.Refer() + ref.Refer(rpcService) ref.Implement(rpcService) } //wait for invoker is available, if wait over default 3s, then panic @@ -131,6 +137,11 @@ func Load() { if providerConfig == nil { logger.Warnf("providerConfig is nil!") } else { + + // so, you should know that the consumer's config will be override + metricConfig = providerConfig.MetricConfig + applicationConfig = providerConfig.ApplicationConfig + checkApplicationName(providerConfig.ApplicationConfig) if err := configCenterRefreshProvider(); err != nil { logger.Errorf("[provider config center refresh] %#v", err) @@ -153,12 +164,51 @@ func Load() { GracefulShutdownInit() } -// get rpc service for consumer +// GetRPCService get rpc service for consumer func GetRPCService(name string) common.RPCService { return consumerConfig.References[name].GetRPCService() } -// create rpc service for consumer +// RPCService create rpc service for consumer func RPCService(service common.RPCService) { consumerConfig.References[service.Reference()].Implement(service) } + +// GetMetricConfig find the MetricConfig +// if it is nil, create a new one +func GetMetricConfig() *MetricConfig { + if metricConfig == nil { + metricConfig = &MetricConfig{} + } + return metricConfig +} + +// GetApplicationConfig find the application config +// if not, we will create one +// Usually applicationConfig will be initialized when system start +func GetApplicationConfig() *ApplicationConfig { + if applicationConfig == nil { + applicationConfig = &ApplicationConfig{} + } + return applicationConfig +} + +// GetProviderConfig find the provider config +// if not found, create new one +func GetProviderConfig() ProviderConfig { + if providerConfig == nil { + logger.Warnf("providerConfig is nil!") + return ProviderConfig{} + } + return *providerConfig +} + +// GetConsumerConfig find the consumer config +// if not found, create new one +func GetConsumerConfig() ConsumerConfig { + if consumerConfig == nil { + logger.Warnf("consumerConfig is nil!") + return ConsumerConfig{} + } + return *consumerConfig +} diff --git a/config/consumer_config.go b/config/consumer_config.go index 72f60b5f77b9b9cc633d8939713c0eb93563deac..98917ebca2ff3abcce6c952a9bc78d93cd74f5d8 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config import ( - "context" "io/ioutil" "path" "time" @@ -25,6 +25,7 @@ import ( import ( "github.com/creasty/defaults" + "github.com/dubbogo/getty" perrors "github.com/pkg/errors" "gopkg.in/yaml.v2" ) @@ -38,6 +39,7 @@ import ( // consumerConfig ///////////////////////// +// ConsumerConfig ... type ConsumerConfig struct { BaseConfig `yaml:",inline"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` @@ -60,6 +62,7 @@ type ConsumerConfig struct { ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` } +// UnmarshalYAML ... func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -71,22 +74,17 @@ func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } +// Prefix ... func (*ConsumerConfig) Prefix() string { return constant.ConsumerConfigPrefix } +// SetConsumerConfig ... func SetConsumerConfig(c ConsumerConfig) { consumerConfig = &c } -func GetConsumerConfig() ConsumerConfig { - if consumerConfig == nil { - logger.Warnf("consumerConfig is nil!") - return ConsumerConfig{} - } - return *consumerConfig -} - +// ConsumerInit ... func ConsumerInit(confConFile string) error { if confConFile == "" { return perrors.Errorf("application configure(consumer) file name is nil") @@ -118,6 +116,10 @@ func ConsumerInit(confConFile string) error { if consumerConfig.RequestTimeout, err = time.ParseDuration(consumerConfig.Request_Timeout); err != nil { return perrors.WithMessagef(err, "time.ParseDuration(Request_Timeout{%#v})", consumerConfig.Request_Timeout) } + if consumerConfig.RequestTimeout >= time.Duration(getty.MaxWheelTimeSpan) { + return perrors.WithMessagef(err, "request_timeout %s should be less than %s", + consumerConfig.Request_Timeout, time.Duration(getty.MaxWheelTimeSpan)) + } } if consumerConfig.Connect_Timeout != "" { if consumerConfig.ConnectTimeout, err = time.ParseDuration(consumerConfig.Connect_Timeout); err != nil { @@ -133,7 +135,7 @@ func configCenterRefreshConsumer() error { var err error if consumerConfig.ConfigCenterConfig != nil { consumerConfig.SetFatherConfig(consumerConfig) - if err := consumerConfig.startConfigCenter(context.Background()); err != nil { + if err := consumerConfig.startConfigCenter(); err != nil { return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err)) } consumerConfig.fresh() diff --git a/config/generic_service.go b/config/generic_service.go index 8a4e88df9788554bc4a5ee33884166e4ccede37f..9895486e977a9848e576597f31b724d51d144d4e 100644 --- a/config/generic_service.go +++ b/config/generic_service.go @@ -14,17 +14,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config +// GenericService ... type GenericService struct { Invoke func(req []interface{}) (interface{}, error) `dubbo:"$invoke"` referenceStr string } +// NewGenericService ... func NewGenericService(referenceStr string) *GenericService { return &GenericService{referenceStr: referenceStr} } +// Reference ... func (u *GenericService) Reference() string { return u.referenceStr } diff --git a/config/graceful_shutdown.go b/config/graceful_shutdown.go index fedb2c15ecdab62d17f0a4e83c45522f1c18acb0..382f05c8d57c4363108873433fd03565d03b9a50 100644 --- a/config/graceful_shutdown.go +++ b/config/graceful_shutdown.go @@ -25,7 +25,7 @@ import ( ) import ( - "github.com/dubbogo/gost/container/gxset" + gxset "github.com/dubbogo/gost/container/set" ) import ( @@ -52,6 +52,7 @@ import ( * We define them by using 'package build' feature https://golang.org/pkg/go/build/ */ +// GracefulShutdownInit ... func GracefulShutdownInit() { signals := make(chan os.Signal, 1) @@ -82,6 +83,7 @@ func GracefulShutdownInit() { }() } +// BeforeShutdown ... func BeforeShutdown() { destroyAllRegistries() diff --git a/config/graceful_shutdown_config.go b/config/graceful_shutdown_config.go index df55728565f6cf14ce4357f8c9c7927c30d80e40..6bbabebf2538effcbbe4bddc50857acf5f962a61 100644 --- a/config/graceful_shutdown_config.go +++ b/config/graceful_shutdown_config.go @@ -31,6 +31,7 @@ const ( defaultStepTimeout = 10 * time.Second ) +// ShutdownConfig ... type ShutdownConfig struct { /* * Total timeout. Even though we don't release all resources, @@ -57,10 +58,12 @@ type ShutdownConfig struct { RequestsFinished bool } +// Prefix ... func (config *ShutdownConfig) Prefix() string { return constant.ShutdownConfigPrefix } +// GetTimeout ... func (config *ShutdownConfig) GetTimeout() time.Duration { result, err := time.ParseDuration(config.Timeout) if err != nil { @@ -71,6 +74,7 @@ func (config *ShutdownConfig) GetTimeout() time.Duration { return result } +// GetStepTimeout ... 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 59c1a5d149c2e9db8e9ac981adec107cafc863ad..8ad79ffa62ceed4096c60bfb9139b7ff1586808e 100644 --- a/config/graceful_shutdown_signal_darwin.go +++ b/config/graceful_shutdown_signal_darwin.go @@ -22,9 +22,13 @@ import ( "syscall" ) -var 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} +var ( + // ShutdownSignals ... + 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} -var DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, - syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} + // DumpHeapShutdownSignals ... + 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 59c1a5d149c2e9db8e9ac981adec107cafc863ad..8ad79ffa62ceed4096c60bfb9139b7ff1586808e 100644 --- a/config/graceful_shutdown_signal_linux.go +++ b/config/graceful_shutdown_signal_linux.go @@ -22,9 +22,13 @@ import ( "syscall" ) -var 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} +var ( + // ShutdownSignals ... + 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} -var DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, - syscall.SIGTRAP, syscall.SIGABRT, syscall.SIGSYS} + // DumpHeapShutdownSignals ... + 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 91b2bce7c2311ecbe9a1255be3e7b7b357a9b403..815a05ecb20a8fc202debaf6f39d699845cd689e 100644 --- a/config/graceful_shutdown_signal_windows.go +++ b/config/graceful_shutdown_signal_windows.go @@ -22,8 +22,12 @@ import ( "syscall" ) -var ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, - syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, - syscall.SIGABRT} +var ( + // ShutdownSignals ... + ShutdownSignals = []os.Signal{os.Interrupt, os.Kill, syscall.SIGKILL, + syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, + syscall.SIGABRT} -var DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT} + // DumpHeapShutdownSignals ... + DumpHeapShutdownSignals = []os.Signal{syscall.SIGQUIT, syscall.SIGILL, syscall.SIGTRAP, syscall.SIGABRT} +) diff --git a/config/method_config.go b/config/method_config.go index 876abeeae0c7d37070c5938107d1bb1dd5dbbaa9..8f196d9e2c03071a663db03cb185fb9106d6484a 100644 --- a/config/method_config.go +++ b/config/method_config.go @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config import ( @@ -24,6 +25,7 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// MethodConfig ... type MethodConfig struct { InterfaceId string InterfaceName string @@ -37,16 +39,19 @@ type MethodConfig struct { ExecuteLimit string `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"` ExecuteLimitRejectedHandler string `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"` Sticky bool `yaml:"sticky" json:"sticky,omitempty" property:"sticky"` + RequestTimeout string `yaml:"timeout" json:"timeout,omitempty" property:"timeout"` } +// Prefix ... func (c *MethodConfig) Prefix() string { - if c.InterfaceId != "" { + if len(c.InterfaceId) != 0 { return constant.DUBBO + "." + c.InterfaceName + "." + c.InterfaceId + "." + c.Name + "." - } else { - return constant.DUBBO + "." + c.InterfaceName + "." + c.Name + "." } + + return constant.DUBBO + "." + c.InterfaceName + "." + c.Name + "." } +// UnmarshalYAML ... func (c *MethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err diff --git a/config/metric_config.go b/config/metric_config.go new file mode 100644 index 0000000000000000000000000000000000000000..73a3ca1cfe4f1461db2e225947dd13199b2ad55e --- /dev/null +++ b/config/metric_config.go @@ -0,0 +1,37 @@ +/* + * 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 + +var ( + defaultHistogramBucket = []float64{10, 50, 100, 200, 500, 1000, 10000} +) + +// This is the config struct for all metrics implementation +type MetricConfig struct { + Reporters []string `yaml:"reporters" json:"reporters,omitempty"` + HistogramBucket []float64 `yaml:"histogram_bucket" json:"histogram_bucket,omitempty"` +} + +// find the histogram bucket +// if it's empty, the default value will be return +func (mc *MetricConfig) GetHistogramBucket() []float64 { + if len(mc.HistogramBucket) == 0 { + mc.HistogramBucket = defaultHistogramBucket + } + return mc.HistogramBucket +} diff --git a/config/metric_config_test.go b/config/metric_config_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fe9d2493f37c0bd563931f5acf133105d72d0e53 --- /dev/null +++ b/config/metric_config_test.go @@ -0,0 +1,31 @@ +/* + * 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 + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestGetMetricConfig(t *testing.T) { + empty := GetMetricConfig() + assert.NotNil(t, empty) +} diff --git a/config/mock_rpcservice.go b/config/mock_rpcservice.go index 64d431ffb6dfbc7e25a988c6093cf0ab5cbd2db5..6c43699128247bf0ec483eb83f879bf4c3b67a37 100644 --- a/config/mock_rpcservice.go +++ b/config/mock_rpcservice.go @@ -21,16 +21,20 @@ import ( "context" ) +// MockService ... type MockService struct{} +// Reference ... func (*MockService) Reference() string { return "MockService" } +// GetUser ... func (*MockService) GetUser(ctx context.Context, itf []interface{}, str *struct{}) error { return nil } +// GetUser1 ... 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 2803456dbcd44211fb6deef883beb7f5dbbf54ad..4828d6e5bd28de19d896340f39c5633d0acd4874 100644 --- a/config/protocol_config.go +++ b/config/protocol_config.go @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config import ( @@ -24,12 +25,14 @@ import ( "github.com/apache/dubbo-go/common/constant" ) +// ProtocolConfig ... 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 ... func (c *ProtocolConfig) Prefix() string { return constant.ProtocolConfigPrefix } diff --git a/config/provider_config.go b/config/provider_config.go index 0fed44c81b124cd40825695981a5394c273203fa..2967ecad3a49218aecf87be7d8e2f76281bfb1b4 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -14,10 +14,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package config import ( - "context" "io/ioutil" "path" ) @@ -37,6 +37,7 @@ import ( // providerConfig ///////////////////////// +// ProviderConfig ... type ProviderConfig struct { BaseConfig `yaml:",inline"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` @@ -52,6 +53,7 @@ type ProviderConfig struct { ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` } +// UnmarshalYAML ... func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -63,21 +65,17 @@ func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } +// Prefix ... func (*ProviderConfig) Prefix() string { return constant.ProviderConfigPrefix } +// SetProviderConfig ... func SetProviderConfig(p ProviderConfig) { providerConfig = &p } -func GetProviderConfig() ProviderConfig { - if providerConfig == nil { - logger.Warnf("providerConfig is nil!") - return ProviderConfig{} - } - return *providerConfig -} +// ProviderInit ... func ProviderInit(confProFile string) error { if len(confProFile) == 0 { return perrors.Errorf("application configure(provider) file name is nil") @@ -115,7 +113,7 @@ func configCenterRefreshProvider() error { //fresh it if providerConfig.ConfigCenterConfig != nil { providerConfig.fatherConfig = providerConfig - if err := providerConfig.startConfigCenter(context.Background()); err != nil { + if err := providerConfig.startConfigCenter(); err != nil { return perrors.Errorf("start config center error , error message is {%v}", perrors.WithStack(err)) } providerConfig.fresh() diff --git a/config/reference_config.go b/config/reference_config.go index 4e0c56c0bc25e3b71b8edf015580cbe5ac5f0d9c..edfa17a27e88a605b71bc7f6dec1b133bd29abe9 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -39,40 +39,44 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// ReferenceConfig ... type ReferenceConfig struct { - context context.Context - pxy *proxy.Proxy - id string - InterfaceName string `required:"true" yaml:"interface" json:"interface,omitempty" property:"interface"` - Check *bool `yaml:"check" json:"check,omitempty" property:"check"` - Url string `yaml:"url" json:"url,omitempty" property:"url"` - Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` - Protocol string `default:"dubbo" yaml:"protocol" json:"protocol,omitempty" property:"protocol"` - Registry string `yaml:"registry" json:"registry,omitempty" property:"registry"` - Cluster string `yaml:"cluster" json:"cluster,omitempty" property:"cluster"` - Loadbalance string `yaml:"loadbalance" json:"loadbalance,omitempty" property:"loadbalance"` - Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` - Group string `yaml:"group" json:"group,omitempty" property:"group"` - Version string `yaml:"version" json:"version,omitempty" property:"version"` - Methods []*MethodConfig `yaml:"methods" json:"methods,omitempty" property:"methods"` - Async bool `yaml:"async" json:"async,omitempty" property:"async"` - Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` - invoker protocol.Invoker - urls []*common.URL - Generic bool `yaml:"generic" json:"generic,omitempty" property:"generic"` - Sticky bool `yaml:"sticky" json:"sticky,omitempty" property:"sticky"` + context context.Context + pxy *proxy.Proxy + id string + InterfaceName string `required:"true" yaml:"interface" json:"interface,omitempty" property:"interface"` + Check *bool `yaml:"check" json:"check,omitempty" property:"check"` + Url string `yaml:"url" json:"url,omitempty" property:"url"` + Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` + Protocol string `default:"dubbo" yaml:"protocol" json:"protocol,omitempty" property:"protocol"` + Registry string `yaml:"registry" json:"registry,omitempty" property:"registry"` + Cluster string `yaml:"cluster" json:"cluster,omitempty" property:"cluster"` + Loadbalance string `yaml:"loadbalance" json:"loadbalance,omitempty" property:"loadbalance"` + Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` + Group string `yaml:"group" json:"group,omitempty" property:"group"` + Version string `yaml:"version" json:"version,omitempty" property:"version"` + Methods []*MethodConfig `yaml:"methods" json:"methods,omitempty" property:"methods"` + Async bool `yaml:"async" json:"async,omitempty" property:"async"` + Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` + invoker protocol.Invoker + urls []*common.URL + Generic bool `yaml:"generic" json:"generic,omitempty" property:"generic"` + Sticky bool `yaml:"sticky" json:"sticky,omitempty" property:"sticky"` + RequestTimeout string `yaml:"timeout" json:"timeout,omitempty" property:"timeout"` } +// Prefix ... func (c *ReferenceConfig) Prefix() string { return constant.ReferenceConfigPrefix + c.InterfaceName + "." } -// The only way to get a new ReferenceConfig +// NewReferenceConfig The only way to get a new ReferenceConfig func NewReferenceConfig(id string, ctx context.Context) *ReferenceConfig { return &ReferenceConfig{id: id, context: ctx} } -func (refconfig *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { +// UnmarshalYAML ... +func (c *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { type rf ReferenceConfig raw := rf{} // Put your defaults here @@ -80,53 +84,59 @@ func (refconfig *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) erro return err } - *refconfig = ReferenceConfig(raw) - if err := defaults.Set(refconfig); err != nil { + *c = ReferenceConfig(raw) + if err := defaults.Set(c); err != nil { return err } return nil } -func (refconfig *ReferenceConfig) Refer() { - url := common.NewURLWithOptions(common.WithPath(refconfig.id), common.WithProtocol(refconfig.Protocol), common.WithParams(refconfig.getUrlMap())) +// Refer ... +func (c *ReferenceConfig) Refer(_ interface{}) { + cfgURL := common.NewURLWithOptions( + common.WithPath(c.id), + common.WithProtocol(c.Protocol), + common.WithParams(c.getUrlMap()), + common.WithParamsValue(constant.BEAN_NAME_KEY, c.id), + ) //1. user specified URL, could be peer-to-peer address, or register center's address. - if refconfig.Url != "" { - urlStrings := gxstrings.RegSplit(refconfig.Url, "\\s*[;]+\\s*") + if c.Url != "" { + urlStrings := gxstrings.RegSplit(c.Url, "\\s*[;]+\\s*") for _, urlStr := range urlStrings { - serviceUrl, err := common.NewURL(context.Background(), urlStr) + serviceUrl, err := common.NewURL(urlStr) if err != nil { panic(fmt.Sprintf("user specified URL %v refer error, error message is %v ", urlStr, err.Error())) } if serviceUrl.Protocol == constant.REGISTRY_PROTOCOL { - serviceUrl.SubURL = url - refconfig.urls = append(refconfig.urls, &serviceUrl) + serviceUrl.SubURL = cfgURL + c.urls = append(c.urls, &serviceUrl) } else { if serviceUrl.Path == "" { - serviceUrl.Path = "/" + refconfig.id + serviceUrl.Path = "/" + c.id } // merge url need to do - newUrl := common.MergeUrl(&serviceUrl, url) - refconfig.urls = append(refconfig.urls, newUrl) + newUrl := common.MergeUrl(&serviceUrl, cfgURL) + c.urls = append(c.urls, newUrl) } } } else { //2. assemble SubURL from register center's configuration模式 - refconfig.urls = loadRegistries(refconfig.Registry, consumerConfig.Registries, common.CONSUMER) + c.urls = loadRegistries(c.Registry, consumerConfig.Registries, common.CONSUMER) //set url to regUrls - for _, regUrl := range refconfig.urls { - regUrl.SubURL = url + for _, regUrl := range c.urls { + regUrl.SubURL = cfgURL } } - if len(refconfig.urls) == 1 { - refconfig.invoker = extension.GetProtocol(refconfig.urls[0].Protocol).Refer(*refconfig.urls[0]) + if len(c.urls) == 1 { + c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(*c.urls[0]) } else { invokers := []protocol.Invoker{} var regUrl *common.URL - for _, u := range refconfig.urls { + for _, u := range c.urls { invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(*u)) if u.Protocol == constant.REGISTRY_PROTOCOL { regUrl = u @@ -134,49 +144,54 @@ func (refconfig *ReferenceConfig) Refer() { } if regUrl != nil { cluster := extension.GetCluster("registryAware") - refconfig.invoker = cluster.Join(directory.NewStaticDirectory(invokers)) + c.invoker = cluster.Join(directory.NewStaticDirectory(invokers)) } else { - cluster := extension.GetCluster(refconfig.Cluster) - refconfig.invoker = cluster.Join(directory.NewStaticDirectory(invokers)) + cluster := extension.GetCluster(c.Cluster) + c.invoker = cluster.Join(directory.NewStaticDirectory(invokers)) } } //create proxy - if refconfig.Async { - callback := GetCallback(refconfig.id) - refconfig.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetAsyncProxy(refconfig.invoker, callback, url) + if c.Async { + callback := GetCallback(c.id) + c.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetAsyncProxy(c.invoker, callback, cfgURL) } else { - refconfig.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetProxy(refconfig.invoker, url) + c.pxy = extension.GetProxyFactory(consumerConfig.ProxyFactory).GetProxy(c.invoker, cfgURL) } } +// Implement // @v is service provider implemented RPCService -func (refconfig *ReferenceConfig) Implement(v common.RPCService) { - refconfig.pxy.Implement(v) +func (c *ReferenceConfig) Implement(v common.RPCService) { + c.pxy.Implement(v) } -func (refconfig *ReferenceConfig) GetRPCService() common.RPCService { - return refconfig.pxy.Get() +// GetRPCService ... +func (c *ReferenceConfig) GetRPCService() common.RPCService { + return c.pxy.Get() } -func (refconfig *ReferenceConfig) getUrlMap() url.Values { +func (c *ReferenceConfig) getUrlMap() url.Values { urlMap := url.Values{} //first set user params - for k, v := range refconfig.Params { + for k, v := range c.Params { urlMap.Set(k, v) } - urlMap.Set(constant.INTERFACE_KEY, refconfig.InterfaceName) + urlMap.Set(constant.INTERFACE_KEY, c.InterfaceName) urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10)) - urlMap.Set(constant.CLUSTER_KEY, refconfig.Cluster) - urlMap.Set(constant.LOADBALANCE_KEY, refconfig.Loadbalance) - urlMap.Set(constant.RETRIES_KEY, refconfig.Retries) - urlMap.Set(constant.GROUP_KEY, refconfig.Group) - urlMap.Set(constant.VERSION_KEY, refconfig.Version) - urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(refconfig.Generic)) + urlMap.Set(constant.CLUSTER_KEY, c.Cluster) + urlMap.Set(constant.LOADBALANCE_KEY, c.Loadbalance) + urlMap.Set(constant.RETRIES_KEY, c.Retries) + urlMap.Set(constant.GROUP_KEY, c.Group) + urlMap.Set(constant.VERSION_KEY, c.Version) + urlMap.Set(constant.GENERIC_KEY, strconv.FormatBool(c.Generic)) urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) + if len(c.RequestTimeout) != 0 { + urlMap.Set(constant.TIMEOUT_KEY, c.RequestTimeout) + } //getty invoke async or sync - urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(refconfig.Async)) - urlMap.Set(constant.STICKY_KEY, strconv.FormatBool(refconfig.Sticky)) + urlMap.Set(constant.ASYNC_KEY, strconv.FormatBool(c.Async)) + urlMap.Set(constant.STICKY_KEY, strconv.FormatBool(c.Sticky)) //application info urlMap.Set(constant.APPLICATION_KEY, consumerConfig.ApplicationConfig.Name) @@ -189,25 +204,30 @@ func (refconfig *ReferenceConfig) getUrlMap() url.Values { //filter var defaultReferenceFilter = constant.DEFAULT_REFERENCE_FILTERS - if refconfig.Generic { + if c.Generic { defaultReferenceFilter = constant.GENERIC_REFERENCE_FILTERS + "," + defaultReferenceFilter } - urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, refconfig.Filter, defaultReferenceFilter)) + urlMap.Set(constant.REFERENCE_FILTER_KEY, mergeValue(consumerConfig.Filter, c.Filter, defaultReferenceFilter)) - for _, v := range refconfig.Methods { + for _, v := range c.Methods { urlMap.Set("methods."+v.Name+"."+constant.LOADBALANCE_KEY, v.Loadbalance) urlMap.Set("methods."+v.Name+"."+constant.RETRIES_KEY, v.Retries) urlMap.Set("methods."+v.Name+"."+constant.STICKY_KEY, strconv.FormatBool(v.Sticky)) + if len(v.RequestTimeout) != 0 { + urlMap.Set("methods."+v.Name+"."+constant.TIMEOUT_KEY, v.RequestTimeout) + } } return urlMap } -func (refconfig *ReferenceConfig) GenericLoad(id string) { - genericService := NewGenericService(refconfig.id) + +// GenericLoad ... +func (c *ReferenceConfig) GenericLoad(id string) { + genericService := NewGenericService(c.id) SetConsumerService(genericService) - refconfig.id = id - refconfig.Refer() - refconfig.Implement(genericService) + c.id = id + c.Refer(genericService) + c.Implement(genericService) return } diff --git a/config/reference_config_test.go b/config/reference_config_test.go index e689c471ed12b58a40d4416efaa16abfe107e09b..7a65e55f09c997cb49b83f1f185faf9338cf0f5a 100644 --- a/config/reference_config_test.go +++ b/config/reference_config_test.go @@ -184,7 +184,7 @@ func Test_ReferMultireg(t *testing.T) { extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) } @@ -197,7 +197,7 @@ func Test_Refer(t *testing.T) { extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.Equal(t, "soa.mock", reference.Params["serviceid"]) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) @@ -211,7 +211,7 @@ func Test_ReferAsync(t *testing.T) { extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.Equal(t, "soa.mock", reference.Params["serviceid"]) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) @@ -227,7 +227,7 @@ func Test_ReferP2P(t *testing.T) { m.Url = "dubbo://127.0.0.1:20000" for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) } @@ -241,7 +241,7 @@ func Test_ReferMultiP2P(t *testing.T) { m.Url = "dubbo://127.0.0.1:20000;dubbo://127.0.0.2:20000" for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) } @@ -256,7 +256,7 @@ func Test_ReferMultiP2PWithReg(t *testing.T) { m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000" for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) assert.NotNil(t, reference.invoker) assert.NotNil(t, reference.pxy) } @@ -268,7 +268,7 @@ func Test_Implement(t *testing.T) { extension.SetProtocol("registry", GetProtocol) extension.SetCluster("registryAware", cluster_impl.NewRegistryAwareCluster) for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) reference.Implement(&MockService{}) assert.NotNil(t, reference.GetRPCService()) @@ -284,7 +284,7 @@ func Test_Forking(t *testing.T) { m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000" for _, reference := range consumerConfig.References { - reference.Refer() + reference.Refer(nil) forks := int(reference.invoker.GetUrl().GetParamInt(constant.FORKS_KEY, constant.DEFAULT_FORKS)) assert.Equal(t, 5, forks) assert.NotNil(t, reference.pxy) @@ -301,7 +301,7 @@ func Test_Sticky(t *testing.T) { m.Url = "dubbo://127.0.0.1:20000;registry://127.0.0.2:20000" reference := consumerConfig.References["MockService"] - reference.Refer() + reference.Refer(nil) referenceSticky := reference.invoker.GetUrl().GetParam(constant.STICKY_KEY, "false") assert.Equal(t, "false", referenceSticky) diff --git a/config/registry_config.go b/config/registry_config.go index 9ffa41eb5b5b3b5ae4dc9f77812c0aef5ce9835f..4e4b6e97d79a9402616b6cac954f7a09b2973dcc 100644 --- a/config/registry_config.go +++ b/config/registry_config.go @@ -18,7 +18,6 @@ package config import ( - "context" "net/url" "strconv" "strings" @@ -34,6 +33,7 @@ import ( "github.com/apache/dubbo-go/common/logger" ) +// RegistryConfig ... 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 @@ -46,6 +46,7 @@ type RegistryConfig struct { Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` } +// UnmarshalYAML ... func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -57,6 +58,7 @@ func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return nil } +// Prefix ... func (*RegistryConfig) Prefix() string { return constant.RegistryConfigPrefix + "|" + constant.SingleRegistryConfigPrefix } @@ -92,9 +94,7 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf addresses := strings.Split(registryConf.Address, ",") address := addresses[0] address = traslateRegistryConf(address, registryConf) - url, err = common.NewURL( - context.Background(), - constant.REGISTRY_PROTOCOL+"://"+address, + url, err = common.NewURL(constant.REGISTRY_PROTOCOL+"://"+address, common.WithParams(registryConf.getUrlMap(roleType)), common.WithUsername(registryConf.Username), common.WithPassword(registryConf.Password), @@ -114,15 +114,16 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf return urls } -func (regconfig *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values { +func (c *RegistryConfig) getUrlMap(roleType common.RoleType) url.Values { urlMap := url.Values{} - urlMap.Set(constant.GROUP_KEY, regconfig.Group) + urlMap.Set(constant.GROUP_KEY, c.Group) urlMap.Set(constant.ROLE_KEY, strconv.Itoa(int(roleType))) - urlMap.Set(constant.REGISTRY_KEY, regconfig.Protocol) - urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, regconfig.TimeoutStr) - for k, v := range regconfig.Params { + urlMap.Set(constant.REGISTRY_KEY, c.Protocol) + urlMap.Set(constant.REGISTRY_TIMEOUT_KEY, c.TimeoutStr) + for k, v := range c.Params { urlMap.Set(k, v) } + return urlMap } diff --git a/config/service.go b/config/service.go index f1b51790ca13df0534882837397181e45e56ffa3..b7e7dc2a425b42363d570fc37a70e2e5094e7d9d 100644 --- a/config/service.go +++ b/config/service.go @@ -26,24 +26,27 @@ var ( proServices = map[string]common.RPCService{} // service name -> service ) -// SetConService is called by init() of implement of RPCService +// SetConsumerService is called by init() of implement of RPCService func SetConsumerService(service common.RPCService) { conServices[service.Reference()] = service } -// SetProService is called by init() of implement of RPCService +// SetProviderService is called by init() of implement of RPCService func SetProviderService(service common.RPCService) { proServices[service.Reference()] = service } +// GetConsumerService ... func GetConsumerService(name string) common.RPCService { return conServices[name] } +// GetProviderService ... func GetProviderService(name string) common.RPCService { return proServices[name] } +// GetCallback ... 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 c17846322e20120bfdf00f1afe24bd20efe7510b..2111838395d507ebac4f72883c99dd2bb1615850 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -42,6 +42,7 @@ import ( "github.com/apache/dubbo-go/protocol/protocolwrapper" ) +// ServiceConfig ... type ServiceConfig struct { context context.Context id string @@ -66,6 +67,8 @@ type ServiceConfig struct { TpsLimitRejectedHandler string `yaml:"tps.limit.rejected.handler" json:"tps.limit.rejected.handler,omitempty" property:"tps.limit.rejected.handler"` ExecuteLimit string `yaml:"execute.limit" json:"execute.limit,omitempty" property:"execute.limit"` ExecuteLimitRejectedHandler string `yaml:"execute.limit.rejected.handler" json:"execute.limit.rejected.handler,omitempty" property:"execute.limit.rejected.handler"` + Auth string `yaml:"auth" json:"auth,omitempty" property:"auth"` + ParamSign string `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"` unexported *atomic.Bool exported *atomic.Bool @@ -74,10 +77,12 @@ type ServiceConfig struct { cacheMutex sync.Mutex } +// Prefix ... func (c *ServiceConfig) Prefix() string { return constant.ServiceConfigPrefix + c.InterfaceName + "." } +// UnmarshalYAML ... func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { if err := defaults.Set(c); err != nil { return err @@ -89,7 +94,7 @@ func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return nil } -// The only way to get a new ServiceConfig +// NewServiceConfig The only way to get a new ServiceConfig func NewServiceConfig(id string, context context.Context) *ServiceConfig { return &ServiceConfig{ @@ -101,63 +106,69 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig { } -func (srvconfig *ServiceConfig) Export() error { +// Export ... +func (c *ServiceConfig) Export() error { // TODO: config center start here // TODO:delay export - if srvconfig.unexported != nil && srvconfig.unexported.Load() { - err := perrors.Errorf("The service %v has already unexported! ", srvconfig.InterfaceName) + if c.unexported != nil && c.unexported.Load() { + err := perrors.Errorf("The service %v has already unexported! ", c.InterfaceName) logger.Errorf(err.Error()) return err } - if srvconfig.unexported != nil && srvconfig.exported.Load() { - logger.Warnf("The service %v has already exported! ", srvconfig.InterfaceName) + if c.unexported != nil && c.exported.Load() { + logger.Warnf("The service %v has already exported! ", c.InterfaceName) return nil } - regUrls := loadRegistries(srvconfig.Registry, providerConfig.Registries, common.PROVIDER) - urlMap := srvconfig.getUrlMap() - - for _, proto := range loadProtocol(srvconfig.Protocol, providerConfig.Protocols) { + regUrls := loadRegistries(c.Registry, providerConfig.Registries, common.PROVIDER) + urlMap := c.getUrlMap() + protocolConfigs := loadProtocol(c.Protocol, providerConfig.Protocols) + if len(protocolConfigs) == 0 { + logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol) + return nil + } + for _, proto := range protocolConfigs { // registry the service reflect - methods, err := common.ServiceMap.Register(proto.Name, srvconfig.rpcService) + methods, err := common.ServiceMap.Register(proto.Name, c.rpcService) if err != nil { - err := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", srvconfig.InterfaceName, proto.Name, err.Error()) + err := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) logger.Errorf(err.Error()) return err } - url := common.NewURLWithOptions(common.WithPath(srvconfig.id), + ivkURL := common.NewURLWithOptions( + common.WithPath(c.id), common.WithProtocol(proto.Name), common.WithIp(proto.Ip), common.WithPort(proto.Port), common.WithParams(urlMap), - common.WithParamsValue(constant.BEAN_NAME_KEY, srvconfig.id), + common.WithParamsValue(constant.BEAN_NAME_KEY, c.id), common.WithMethods(strings.Split(methods, ",")), - common.WithToken(srvconfig.Token), + common.WithToken(c.Token), ) if len(regUrls) > 0 { for _, regUrl := range regUrls { - regUrl.SubURL = url + regUrl.SubURL = ivkURL - srvconfig.cacheMutex.Lock() - if srvconfig.cacheProtocol == nil { - logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", url)) - srvconfig.cacheProtocol = extension.GetProtocol("registry") + c.cacheMutex.Lock() + if c.cacheProtocol == nil { + logger.Infof(fmt.Sprintf("First load the registry protocol , url is {%v}!", ivkURL)) + c.cacheProtocol = extension.GetProtocol("registry") } - srvconfig.cacheMutex.Unlock() + c.cacheMutex.Unlock() invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl) - exporter := srvconfig.cacheProtocol.Export(invoker) + exporter := c.cacheProtocol.Export(invoker) if exporter == nil { - panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, url))) + panic(perrors.New(fmt.Sprintf("Registry protocol new exporter error,registry is {%v},url is {%v}", regUrl, ivkURL))) } } } else { - invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*url) + invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL) exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker) if exporter == nil { - panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", url))) + panic(perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error,url is {%v}", ivkURL))) } } @@ -166,24 +177,25 @@ func (srvconfig *ServiceConfig) Export() error { } -func (srvconfig *ServiceConfig) Implement(s common.RPCService) { - srvconfig.rpcService = s +// Implement ... +func (c *ServiceConfig) Implement(s common.RPCService) { + c.rpcService = s } -func (srvconfig *ServiceConfig) getUrlMap() url.Values { +func (c *ServiceConfig) getUrlMap() url.Values { urlMap := url.Values{} // first set user params - for k, v := range srvconfig.Params { + for k, v := range c.Params { urlMap.Set(k, v) } - urlMap.Set(constant.INTERFACE_KEY, srvconfig.InterfaceName) + urlMap.Set(constant.INTERFACE_KEY, c.InterfaceName) urlMap.Set(constant.TIMESTAMP_KEY, strconv.FormatInt(time.Now().Unix(), 10)) - urlMap.Set(constant.CLUSTER_KEY, srvconfig.Cluster) - urlMap.Set(constant.LOADBALANCE_KEY, srvconfig.Loadbalance) - urlMap.Set(constant.WARMUP_KEY, srvconfig.Warmup) - urlMap.Set(constant.RETRIES_KEY, srvconfig.Retries) - urlMap.Set(constant.GROUP_KEY, srvconfig.Group) - urlMap.Set(constant.VERSION_KEY, srvconfig.Version) + urlMap.Set(constant.CLUSTER_KEY, c.Cluster) + urlMap.Set(constant.LOADBALANCE_KEY, c.Loadbalance) + urlMap.Set(constant.WARMUP_KEY, c.Warmup) + urlMap.Set(constant.RETRIES_KEY, c.Retries) + urlMap.Set(constant.GROUP_KEY, c.Group) + urlMap.Set(constant.VERSION_KEY, c.Version) urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) // application info urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name) @@ -195,22 +207,26 @@ func (srvconfig *ServiceConfig) getUrlMap() url.Values { urlMap.Set(constant.ENVIRONMENT_KEY, providerConfig.ApplicationConfig.Environment) // filter - urlMap.Set(constant.SERVICE_FILTER_KEY, mergeValue(providerConfig.Filter, srvconfig.Filter, constant.DEFAULT_SERVICE_FILTERS)) + urlMap.Set(constant.SERVICE_FILTER_KEY, mergeValue(providerConfig.Filter, c.Filter, constant.DEFAULT_SERVICE_FILTERS)) // filter special config - urlMap.Set(constant.ACCESS_LOG_KEY, srvconfig.AccessLog) + urlMap.Set(constant.ACCESS_LOG_KEY, c.AccessLog) // tps limiter - urlMap.Set(constant.TPS_LIMIT_STRATEGY_KEY, srvconfig.TpsLimitStrategy) - urlMap.Set(constant.TPS_LIMIT_INTERVAL_KEY, srvconfig.TpsLimitInterval) - urlMap.Set(constant.TPS_LIMIT_RATE_KEY, srvconfig.TpsLimitRate) - urlMap.Set(constant.TPS_LIMITER_KEY, srvconfig.TpsLimiter) - urlMap.Set(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, srvconfig.TpsLimitRejectedHandler) + urlMap.Set(constant.TPS_LIMIT_STRATEGY_KEY, c.TpsLimitStrategy) + urlMap.Set(constant.TPS_LIMIT_INTERVAL_KEY, c.TpsLimitInterval) + urlMap.Set(constant.TPS_LIMIT_RATE_KEY, c.TpsLimitRate) + urlMap.Set(constant.TPS_LIMITER_KEY, c.TpsLimiter) + urlMap.Set(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, c.TpsLimitRejectedHandler) // execute limit filter - urlMap.Set(constant.EXECUTE_LIMIT_KEY, srvconfig.ExecuteLimit) - urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, srvconfig.ExecuteLimitRejectedHandler) + urlMap.Set(constant.EXECUTE_LIMIT_KEY, c.ExecuteLimit) + urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, c.ExecuteLimitRejectedHandler) + + // auth filter + urlMap.Set(constant.SERVICE_AUTH_KEY, c.Auth) + urlMap.Set(constant.PARAMTER_SIGNATURE_ENABLE_KEY, c.ParamSign) - for _, v := range srvconfig.Methods { + for _, v := range c.Methods { prefix := "methods." + v.Name + "." urlMap.Set(prefix+constant.LOADBALANCE_KEY, v.Loadbalance) urlMap.Set(prefix+constant.RETRIES_KEY, v.Retries) diff --git a/config/service_config_test.go b/config/service_config_test.go index 8ae67533bd1cdd1f9170efd762de51d371d0ad38..6f3230890348e77ea26c9c0eaf9165090c8cd09f 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -93,6 +93,30 @@ func doInitProvider() { }, }, }, + "MockServiceNoRightProtocol": { + InterfaceName: "com.MockService", + Protocol: "mock1", + Registry: "shanghai_reg1,shanghai_reg2,hangzhou_reg1,hangzhou_reg2", + Cluster: "failover", + Loadbalance: "random", + Retries: "3", + Group: "huadong_idc", + Version: "1.0.0", + Methods: []*MethodConfig{ + { + Name: "GetUser", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + { + Name: "GetUser1", + Retries: "2", + Loadbalance: "random", + Weight: 200, + }, + }, + }, }, Protocols: map[string]*ProtocolConfig{ "mock": { diff --git a/config/testdata/consumer_config.yml b/config/testdata/consumer_config.yml index f44ea449fd16235050f6a7ba7823a87e24791780..2034186c0fa0ccf21c3f6fb9df0f5cfd69315113 100644 --- a/config/testdata/consumer_config.yml +++ b/config/testdata/consumer_config.yml @@ -41,9 +41,11 @@ references: interface : "com.ikurento.user.UserProvider" url: "dubbo://127.0.0.1:20000/UserProvider" cluster: "failover" + timeout: "3s" methods : - name: "GetUser" retries: "3" + timeout: "5s" params: "serviceid": "soa.com.ikurento.user.UserProvider" @@ -54,12 +56,21 @@ shutdown_conf: step_timeout: 10s protocol_conf: + # when you choose the Dubbo protocol, the following configuration takes effect dubbo: reconnect_interval: 0 + # reconnect_interval is the actual number of connections a session can use connection_number: 2 - heartbeat_period: "5s" - session_timeout: "20s" - pool_size: 64 + # heartbeat_period is heartbeat interval between server and client connection. + # Effective by client configuration + heartbeat_period: "30s" + # when the session is inactive for more than session_timeout, the session may be closed + session_timeout: "30s" + # a reference has the size of the session connection pool + # that is the maximum number of sessions it may have + pool_size: 4 + # dubbo-go uses getty as the network connection library. + # The following is the relevant configuration of getty pool_ttl: 600 # gr_pool_size is recommended to be set to [cpu core number] * 100 gr_pool_size: 1200 @@ -67,6 +78,8 @@ protocol_conf: queue_len: 64 # queue_number is recommended to be set to gr_pool_size / 20 queue_number: 60 + # dubbo-go uses getty as the network connection library. + # The following is the relevant configuration of getty getty_session_param: compress_encoding: false tcp_no_delay: true @@ -78,5 +91,7 @@ protocol_conf: tcp_read_timeout: "1s" tcp_write_timeout: "5s" wait_timeout: "1s" - max_msg_len: 1024 + # maximum len of data per request + # this refers to the total amount of data requested or returned + max_msg_len: 102400 session_name: "client" diff --git a/config_center/apollo/factory.go b/config_center/apollo/factory.go index 47011be4a3e0e421ca7a314620a3547d665111c8..a5a69e121598bea4194398423775a99f04b61ced 100644 --- a/config_center/apollo/factory.go +++ b/config_center/apollo/factory.go @@ -20,7 +20,7 @@ package apollo import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/extension" - . "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/config_center/parser" ) @@ -28,13 +28,13 @@ func init() { extension.SetConfigCenterFactory("apollo", createDynamicConfigurationFactory) } -func createDynamicConfigurationFactory() DynamicConfigurationFactory { +func createDynamicConfigurationFactory() config_center.DynamicConfigurationFactory { return &apolloConfigurationFactory{} } type apolloConfigurationFactory struct{} -func (f *apolloConfigurationFactory) GetDynamicConfiguration(url *common.URL) (DynamicConfiguration, error) { +func (f *apolloConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, error) { dynamicConfiguration, err := newApolloConfiguration(url) if err != nil { return nil, err diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index ed46d4f9635d4d480a21d09fce0ec4ec84d47a66..85dff14a1ec9ba3905890bf37dc1e1827d59d80f 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -32,7 +32,7 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" - . "github.com/apache/dubbo-go/config_center" + cc "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/config_center/parser" "github.com/apache/dubbo-go/remoting" ) @@ -58,7 +58,7 @@ func newApolloConfiguration(url *common.URL) (*apolloConfiguration, error) { configCluster := url.GetParam(constant.CONFIG_CLUSTER_KEY, "") appId := url.GetParam(constant.CONFIG_APP_ID_KEY, "") - namespaces := url.GetParam(constant.CONFIG_NAMESPACE_KEY, getProperties(DEFAULT_GROUP)) + namespaces := getProperties(url.GetParam(constant.CONFIG_NAMESPACE_KEY, cc.DEFAULT_GROUP)) c.appConf = &agollo.AppConfig{ AppId: appId, Cluster: configCluster, @@ -84,8 +84,8 @@ func getChangeType(change agollo.ConfigChangeType) remoting.EventType { } } -func (c *apolloConfiguration) AddListener(key string, listener ConfigurationListener, opts ...Option) { - k := &Options{} +func (c *apolloConfiguration) AddListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) { + k := &cc.Options{} for _, opt := range opts { opt(k) } @@ -95,8 +95,8 @@ func (c *apolloConfiguration) AddListener(key string, listener ConfigurationList l.(*apolloListener).AddListener(listener) } -func (c *apolloConfiguration) RemoveListener(key string, listener ConfigurationListener, opts ...Option) { - k := &Options{} +func (c *apolloConfiguration) RemoveListener(key string, listener cc.ConfigurationListener, opts ...cc.Option) { + k := &cc.Options{} for _, opt := range opts { opt(k) } @@ -116,7 +116,7 @@ func getNamespaceName(namespace string, configFileFormat agollo.ConfigFileFormat return fmt.Sprintf(apolloConfigFormat, namespace, configFileFormat) } -func (c *apolloConfiguration) GetInternalProperty(key string, opts ...Option) (string, error) { +func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) { config := agollo.GetConfig(c.appConf.NamespaceName) if config == nil { return "", errors.New(fmt.Sprintf("nothing in namespace:%s ", key)) @@ -124,11 +124,11 @@ func (c *apolloConfiguration) GetInternalProperty(key string, opts ...Option) (s return config.GetStringValue(key, ""), nil } -func (c *apolloConfiguration) GetRule(key string, opts ...Option) (string, error) { +func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, error) { return c.GetInternalProperty(key, opts...) } -func (c *apolloConfiguration) GetProperties(key string, opts ...Option) (string, error) { +func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (string, error) { /** * when group is not null, we are getting startup configs(config file) from Config Center, for example: * key=dubbo.propertie diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go index e898be91ee356180f5967f9dd5a02df0dbcfb311..a95524b41b887313993aad4e774ed6d96b24c08f 100644 --- a/config_center/apollo/impl_test.go +++ b/config_center/apollo/impl_test.go @@ -17,13 +17,14 @@ package apollo import ( - "context" "fmt" "net/http" "net/http/httptest" + "os" "strings" "sync" "testing" + "time" ) import ( @@ -139,7 +140,7 @@ func serviceConfigResponse(rw http.ResponseWriter, req *http.Request) { fmt.Fprintf(rw, "%s", result) } -//run mock config server +// run mock config server func runMockConfigServer(handlerMap map[string]func(http.ResponseWriter, *http.Request), notifyHandler func(http.ResponseWriter, *http.Request)) *httptest.Server { uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 0) @@ -171,6 +172,7 @@ func Test_GetConfig(t *testing.T) { mapContent, err := configuration.Parser().Parse(configs) assert.NoError(t, err) assert.Equal(t, "ikurento.com", mapContent["application.organization"]) + deleteMockJson(t) } func Test_GetConfigItem(t *testing.T) { @@ -180,6 +182,7 @@ func Test_GetConfigItem(t *testing.T) { configuration.SetParser(&parser.DefaultConfigurationParser{}) assert.NoError(t, err) assert.Equal(t, "ikurento.com", configs) + deleteMockJson(t) } func initMockApollo(t *testing.T) *apolloConfiguration { @@ -188,11 +191,11 @@ func initMockApollo(t *testing.T) *apolloConfiguration { Address: "106.12.25.204:8080", AppId: "testApplication_yang", Cluster: "dev", - Namespace: "mockDubbog.properties", + Namespace: "mockDubbog", }} apollo := initApollo() apolloUrl := strings.ReplaceAll(apollo.URL, "http", "apollo") - url, err := common.NewURL(context.TODO(), apolloUrl, common.WithParams(c.ConfigCenterConfig.GetUrlMap())) + url, err := common.NewURL(apolloUrl, common.WithParams(c.ConfigCenterConfig.GetUrlMap())) assert.NoError(t, err) configuration, err := newApolloConfiguration(&url) assert.NoError(t, err) @@ -216,6 +219,7 @@ func TestAddListener(t *testing.T) { listener.wg.Wait() assert.Equal(t, "registries.hangzhouzk.username", listener.event) assert.Greater(t, listener.count, 0) + deleteMockJson(t) } func TestRemoveListener(t *testing.T) { @@ -244,6 +248,7 @@ func TestRemoveListener(t *testing.T) { }) assert.Equal(t, listenerCount, 0) assert.Equal(t, listener.count, 0) + deleteMockJson(t) } type apolloDataListener struct { @@ -260,3 +265,10 @@ func (l *apolloDataListener) Process(configType *config_center.ConfigChangeEvent l.count++ l.event = configType.Key } + +func deleteMockJson(t *testing.T) { + // because the file write in another goroutine,so have a break ... + time.Sleep(100 * time.Millisecond) + remove := os.Remove("mockDubbog.properties.json") + t.Log("remove result:", remove) +} diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go index d81e1cbf34e405f7d2974e29558414029308b36b..820d02fb48e2204c3f1eb74fd5624132a63d367e 100644 --- a/config_center/apollo/listener.go +++ b/config_center/apollo/listener.go @@ -29,6 +29,14 @@ type apolloListener struct { listeners map[config_center.ConfigurationListener]struct{} } +// NewApolloListener ... +func NewApolloListener() *apolloListener { + return &apolloListener{ + listeners: make(map[config_center.ConfigurationListener]struct{}, 0), + } +} + +// OnChange ... func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) { for key, change := range changeEvent.Changes { for listener := range a.listeners { @@ -41,19 +49,15 @@ func (a *apolloListener) OnChange(changeEvent *agollo.ChangeEvent) { } } -func NewApolloListener() *apolloListener { - return &apolloListener{ - listeners: make(map[config_center.ConfigurationListener]struct{}, 0), - } -} - -func (al *apolloListener) AddListener(l config_center.ConfigurationListener) { - if _, ok := al.listeners[l]; !ok { - al.listeners[l] = struct{}{} - agollo.AddChangeListener(al) +// AddListener ... +func (a *apolloListener) AddListener(l config_center.ConfigurationListener) { + if _, ok := a.listeners[l]; !ok { + a.listeners[l] = struct{}{} + agollo.AddChangeListener(a) } } -func (al *apolloListener) RemoveListener(l config_center.ConfigurationListener) { - delete(al.listeners, l) +// RemoveListener ... +func (a *apolloListener) RemoveListener(l config_center.ConfigurationListener) { + delete(a.listeners, l) } diff --git a/config_center/configuration_listener.go b/config_center/configuration_listener.go index 1419bcdd0ce10ec15d0c24c2439bb02747ce5391..e70e4f68075c51c33f1110ef44a7b703e36fb78d 100644 --- a/config_center/configuration_listener.go +++ b/config_center/configuration_listener.go @@ -25,10 +25,12 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// ConfigurationListener ... type ConfigurationListener interface { Process(*ConfigChangeEvent) } +// ConfigChangeEvent ... type ConfigChangeEvent struct { Key string Value interface{} diff --git a/config_center/configurator.go b/config_center/configurator.go index 3ba293ec60302b76becce357f49b2baa543f69cd..ffa9034e05c4c3d4cc254886e2ed19576f155dec 100644 --- a/config_center/configurator.go +++ b/config_center/configurator.go @@ -21,6 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) +// Configurator ... type Configurator interface { GetUrl() *common.URL Configure(url *common.URL) diff --git a/config_center/configurator/mock.go b/config_center/configurator/mock.go index 1f03d107c8f588cfd4c23c9086bb0fbe42e05fff..d294b9195db9cfe60056bc29ec26816f740ea396 100644 --- a/config_center/configurator/mock.go +++ b/config_center/configurator/mock.go @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package configurator import ( @@ -22,6 +23,7 @@ import ( "github.com/apache/dubbo-go/config_center" ) +// NewMockConfigurator ... func NewMockConfigurator(url *common.URL) config_center.Configurator { return &mockConfigurator{configuratorUrl: url} } @@ -30,10 +32,12 @@ type mockConfigurator struct { configuratorUrl *common.URL } +// GetUrl ... func (c *mockConfigurator) GetUrl() *common.URL { return c.configuratorUrl } +// Configure ... func (c *mockConfigurator) Configure(url *common.URL) { if cluster := c.GetUrl().GetParam(constant.CLUSTER_KEY, ""); cluster != "" { url.SetParam(constant.CLUSTER_KEY, cluster) diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go index e85b4d3ec9d5e6f9f7163cefce3f328f8dcc225a..d0b23ef2f20d065135547536c2cebcec3eec0ce1 100644 --- a/config_center/configurator/override.go +++ b/config_center/configurator/override.go @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package configurator import ( @@ -21,7 +22,7 @@ import ( ) import ( - "github.com/dubbogo/gost/container/gxset" + gxset "github.com/dubbogo/gost/container/set" gxnet "github.com/dubbogo/gost/net" ) diff --git a/config_center/configurator/override_test.go b/config_center/configurator/override_test.go index a585f4217f81a5d600ec9a48c12b3b47ff2d5322..329c598efe8ef79d7fc1b79ae182c59b238283ac 100644 --- a/config_center/configurator/override_test.go +++ b/config_center/configurator/override_test.go @@ -17,7 +17,6 @@ package configurator import ( - "context" "testing" ) @@ -32,45 +31,49 @@ import ( ) func Test_configureVerison2p6(t *testing.T) { - url, err := common.NewURL(context.Background(), "override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") + url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) configurator := extension.GetConfigurator("default", &url) assert.Equal(t, "override", configurator.GetUrl().Protocol) - providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + assert.NoError(t, err) configurator.Configure(&providerUrl) assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) } func Test_configureVerisonOverrideAddr(t *testing.T) { - url, err := common.NewURL(context.Background(), "override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001") + url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001") assert.NoError(t, err) configurator := extension.GetConfigurator("default", &url) assert.Equal(t, "override", configurator.GetUrl().Protocol) - providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + assert.NoError(t, err) configurator.Configure(&providerUrl) assert.Equal(t, "failover", providerUrl.GetParam(constant.CLUSTER_KEY, "")) } func Test_configureVerison2p6WithIp(t *testing.T) { - url, err := common.NewURL(context.Background(), "override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") + url, err := common.NewURL("override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) configurator := extension.GetConfigurator("default", &url) assert.Equal(t, "override", configurator.GetUrl().Protocol) - providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + assert.NoError(t, err) configurator.Configure(&providerUrl) assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) } func Test_configureVerison2p7(t *testing.T) { - url, err := common.NewURL(context.Background(), "jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider") + url, err := common.NewURL("jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider") assert.NoError(t, err) configurator := extension.GetConfigurator("default", &url) - providerUrl, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") + assert.NoError(t, err) configurator.Configure(&providerUrl) assert.Equal(t, "failfast", providerUrl.GetParam(constant.CLUSTER_KEY, "")) diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index 0546d39732deaa83ace948275a0d4448b1b24cf8..90cd3bbb1d502a0e9ceb8fed5c94a4091bc0578e 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -28,9 +28,14 @@ import ( ////////////////////////////////////////// // DynamicConfiguration ////////////////////////////////////////// -const DEFAULT_GROUP = "dubbo" -const DEFAULT_CONFIG_TIMEOUT = "10s" +const ( + // DEFAULT_GROUP: default group + DEFAULT_GROUP = "dubbo" + // DEFAULT_CONFIG_TIMEOUT: default config timeout + DEFAULT_CONFIG_TIMEOUT = "10s" +) +// DynamicConfiguration ... type DynamicConfiguration interface { Parser() parser.ConfigurationParser SetParser(parser.ConfigurationParser) @@ -46,19 +51,23 @@ type DynamicConfiguration interface { GetInternalProperty(string, ...Option) (string, error) } +// Options ... type Options struct { Group string Timeout time.Duration } +// Option ... type Option func(*Options) +// WithGroup ... func WithGroup(group string) Option { return func(opt *Options) { opt.Group = group } } +// WithTimeout ... func WithTimeout(time time.Duration) Option { return func(opt *Options) { opt.Timeout = time diff --git a/config_center/dynamic_configuration_factory.go b/config_center/dynamic_configuration_factory.go index 0720896fb615f8639c20a46d2078c3dfcd112c32..9f9b13227f6623a02b0261c46d8d1e43624005f8 100644 --- a/config_center/dynamic_configuration_factory.go +++ b/config_center/dynamic_configuration_factory.go @@ -21,6 +21,7 @@ import ( "github.com/apache/dubbo-go/common" ) +// DynamicConfigurationFactory ... type DynamicConfigurationFactory interface { GetDynamicConfiguration(*common.URL) (DynamicConfiguration, error) } diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 79c7c171945400a52563e0b66ef29c2896db0b99..4d972b629abb7abd7cc0d0018026e4ccc04a1e4f 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -32,6 +32,7 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// MockDynamicConfigurationFactory ... type MockDynamicConfigurationFactory struct { Content string } @@ -41,7 +42,8 @@ var ( dynamicConfiguration *MockDynamicConfiguration ) -func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (DynamicConfiguration, error) { +// GetDynamicConfiguration ... +func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(_ *common.URL) (DynamicConfiguration, error) { var err error once.Do(func() { dynamicConfiguration = &MockDynamicConfiguration{listener: map[string]ConfigurationListener{}} @@ -79,48 +81,59 @@ func (f *MockDynamicConfigurationFactory) GetDynamicConfiguration(url *common.UR } +// MockDynamicConfiguration ... type MockDynamicConfiguration struct { parser parser.ConfigurationParser content string listener map[string]ConfigurationListener } -func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, opions ...Option) { +// AddListener ... +func (c *MockDynamicConfiguration) AddListener(key string, listener ConfigurationListener, _ ...Option) { c.listener[key] = listener } -func (c *MockDynamicConfiguration) RemoveListener(key string, listener ConfigurationListener, opions ...Option) { +// RemoveListener ... +func (c *MockDynamicConfiguration) RemoveListener(_ string, _ ConfigurationListener, _ ...Option) { } -func (c *MockDynamicConfiguration) GetConfig(key string, opts ...Option) (string, error) { +// GetConfig ... +func (c *MockDynamicConfiguration) GetConfig(_ string, _ ...Option) (string, error) { return c.content, nil } -//For zookeeper, getConfig and getConfigs have the same meaning. +// GetConfigs For zookeeper, getConfig and getConfigs have the same meaning. func (c *MockDynamicConfiguration) GetConfigs(key string, opts ...Option) (string, error) { return c.GetConfig(key, opts...) } +// Parser ... func (c *MockDynamicConfiguration) Parser() parser.ConfigurationParser { return c.parser } + +// SetParser ... func (c *MockDynamicConfiguration) SetParser(p parser.ConfigurationParser) { c.parser = p } -func (c *MockDynamicConfiguration) GetProperties(key string, opts ...Option) (string, error) { + +// GetProperties ... +func (c *MockDynamicConfiguration) GetProperties(_ string, _ ...Option) (string, error) { return c.content, nil } -//For zookeeper, getConfig and getConfigs have the same meaning. +// GetInternalProperty For zookeeper, getConfig and getConfigs have the same meaning. func (c *MockDynamicConfiguration) GetInternalProperty(key string, opts ...Option) (string, error) { return c.GetProperties(key, opts...) } +// GetRule ... func (c *MockDynamicConfiguration) GetRule(key string, opts ...Option) (string, error) { return c.GetProperties(key, opts...) } +// MockServiceConfigEvent ... func (c *MockDynamicConfiguration) MockServiceConfigEvent() { config := &parser.ConfiguratorConfig{ ConfigVersion: "2.7.1", @@ -142,6 +155,7 @@ func (c *MockDynamicConfiguration) MockServiceConfigEvent() { c.listener[key].Process(&ConfigChangeEvent{Key: key, Value: string(value), ConfigType: remoting.EventTypeAdd}) } +// MockApplicationConfigEvent ... func (c *MockDynamicConfiguration) MockApplicationConfigEvent() { config := &parser.ConfiguratorConfig{ ConfigVersion: "2.7.1", diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go index 85033ce97f3e9038d57b6156e6dc68139363e8c3..58fcdb49dad2c53270894a65bf4ebd9595dc420e 100644 --- a/config_center/parser/configuration_parser.go +++ b/config_center/parser/configuration_parser.go @@ -18,7 +18,6 @@ package parser import ( - "context" "strconv" "strings" ) @@ -36,18 +35,22 @@ import ( ) const ( + // ScopeApplication ... ScopeApplication = "application" - GeneralType = "general" + // GeneralType ... + GeneralType = "general" ) +// ConfigurationParser ... type ConfigurationParser interface { Parse(string) (map[string]string, error) ParseToUrls(content string) ([]*common.URL, error) } -//for support properties file in config center +// DefaultConfigurationParser for support properties file in config center type DefaultConfigurationParser struct{} +// ConfiguratorConfig ... type ConfiguratorConfig struct { ConfigVersion string `yaml:"configVersion"` Scope string `yaml:"scope"` @@ -56,6 +59,7 @@ type ConfiguratorConfig struct { Configs []ConfigItem `yaml:"configs"` } +// ConfigItem ... type ConfigItem struct { Type string `yaml:"type"` Enabled bool `yaml:"enabled"` @@ -67,15 +71,17 @@ type ConfigItem struct { Side string `yaml:"side"` } +// Parse ... func (parser *DefaultConfigurationParser) Parse(content string) (map[string]string, error) { - properties, err := properties.LoadString(content) + pps, err := properties.LoadString(content) if err != nil { logger.Errorf("Parse the content {%v} in DefaultConfigurationParser error ,error message is {%v}", content, err) return nil, err } - return properties.Map(), nil + return pps.Map(), nil } +// ParseToUrls ... func (parser *DefaultConfigurationParser) ParseToUrls(content string) ([]*common.URL, error) { config := ConfiguratorConfig{} if err := yaml.Unmarshal([]byte(content), &config); err != nil { @@ -132,14 +138,14 @@ func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.UR newUrlStr := urlStr newUrlStr = newUrlStr + "&application" newUrlStr = newUrlStr + v - url, err := common.NewURL(context.Background(), newUrlStr) + url, err := common.NewURL(newUrlStr) if err != nil { return nil, perrors.WithStack(err) } urls = append(urls, &url) } } else { - url, err := common.NewURL(context.Background(), urlStr) + url, err := common.NewURL(urlStr) if err != nil { return nil, perrors.WithStack(err) } @@ -178,7 +184,7 @@ func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, e urlStr = urlStr + constant.APP_DYNAMIC_CONFIGURATORS_CATEGORY urlStr = urlStr + "&configVersion=" urlStr = urlStr + config.ConfigVersion - url, err := common.NewURL(context.Background(), urlStr) + url, err := common.NewURL(urlStr) if err != nil { return nil, perrors.WithStack(err) } diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index 504d4910581aff52afa74b13fdfce61c9170ca48..70fb196a1eedb994eae38576de35d36deb450aaa 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -24,8 +24,8 @@ import ( ) import ( + "github.com/dubbogo/go-zookeeper/zk" perrors "github.com/pkg/errors" - "github.com/samuel/go-zookeeper/zk" ) import ( @@ -37,7 +37,11 @@ import ( "github.com/apache/dubbo-go/remoting/zookeeper" ) -const ZkClient = "zk config_center" +const ( + // ZkClient + //zookeeper client name + ZkClient = "zk config_center" +) type zookeeperDynamicConfiguration struct { url *common.URL @@ -134,10 +138,9 @@ func (c *zookeeperDynamicConfiguration) GetProperties(key string, opts ...config content, _, err := c.client.GetContent(c.rootPath + "/" + key) if err != nil { return "", perrors.WithStack(err) - } else { - return string(content), nil } + return string(content), nil } //For zookeeper, getConfig and getConfigs have the same meaning. @@ -156,57 +159,57 @@ func (c *zookeeperDynamicConfiguration) SetParser(p parser.ConfigurationParser) c.parser = p } -func (r *zookeeperDynamicConfiguration) ZkClient() *zookeeper.ZookeeperClient { - return r.client +func (c *zookeeperDynamicConfiguration) ZkClient() *zookeeper.ZookeeperClient { + return c.client } -func (r *zookeeperDynamicConfiguration) SetZkClient(client *zookeeper.ZookeeperClient) { - r.client = client +func (c *zookeeperDynamicConfiguration) SetZkClient(client *zookeeper.ZookeeperClient) { + c.client = client } -func (r *zookeeperDynamicConfiguration) ZkClientLock() *sync.Mutex { - return &r.cltLock +func (c *zookeeperDynamicConfiguration) ZkClientLock() *sync.Mutex { + return &c.cltLock } -func (r *zookeeperDynamicConfiguration) WaitGroup() *sync.WaitGroup { - return &r.wg +func (c *zookeeperDynamicConfiguration) WaitGroup() *sync.WaitGroup { + return &c.wg } -func (r *zookeeperDynamicConfiguration) GetDone() chan struct{} { - return r.done +func (c *zookeeperDynamicConfiguration) Done() chan struct{} { + return c.done } -func (r *zookeeperDynamicConfiguration) GetUrl() common.URL { - return *r.url +func (c *zookeeperDynamicConfiguration) GetUrl() common.URL { + return *c.url } -func (r *zookeeperDynamicConfiguration) Destroy() { - if r.listener != nil { - r.listener.Close() +func (c *zookeeperDynamicConfiguration) Destroy() { + if c.listener != nil { + c.listener.Close() } - close(r.done) - r.wg.Wait() - r.closeConfigs() + close(c.done) + c.wg.Wait() + c.closeConfigs() } -func (r *zookeeperDynamicConfiguration) IsAvailable() bool { +func (c *zookeeperDynamicConfiguration) IsAvailable() bool { select { - case <-r.done: + case <-c.done: return false default: return true } } -func (r *zookeeperDynamicConfiguration) closeConfigs() { - r.cltLock.Lock() - defer r.cltLock.Unlock() +func (c *zookeeperDynamicConfiguration) closeConfigs() { + c.cltLock.Lock() + defer c.cltLock.Unlock() logger.Infof("begin to close provider zk client") // Close the old client first to close the tmp node - r.client.Close() - r.client = nil + c.client.Close() + c.client = nil } -func (r *zookeeperDynamicConfiguration) RestartCallBack() bool { +func (c *zookeeperDynamicConfiguration) RestartCallBack() bool { return true } diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go index e614009faa5b32873c6245dea5c85cc2747e19ea..1d62f3df86f5706823cab7c9ed0bc1a7d9b380f3 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -17,14 +17,13 @@ package zookeeper import ( - "context" "fmt" "sync" "testing" ) import ( - "github.com/samuel/go-zookeeper/zk" + "github.com/dubbogo/go-zookeeper/zk" "github.com/stretchr/testify/assert" ) @@ -35,7 +34,7 @@ import ( ) func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicConfiguration) { - regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111") + regurl, _ := common.NewURL("registry://127.0.0.1:1111") ts, reg, err := newMockZookeeperDynamicConfiguration(®url) reg.SetParser(&parser.DefaultConfigurationParser{}) diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go index 7128b6f5a39e243840a1076f9fc506d94c7ed2ed..122dfaf4f268a706151de6acdaa78bb46e59f8fb 100644 --- a/config_center/zookeeper/listener.go +++ b/config_center/zookeeper/listener.go @@ -27,25 +27,30 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// CacheListener ... type CacheListener struct { keyListeners sync.Map rootPath string } +// NewCacheListener ... func NewCacheListener(rootPath string) *CacheListener { return &CacheListener{rootPath: rootPath} } + +// AddListener ... func (l *CacheListener) AddListener(key string, listener config_center.ConfigurationListener) { // reference from https://stackoverflow.com/questions/34018908/golang-why-dont-we-have-a-set-datastructure // make a map[your type]struct{} like set in java - listeners, loaded := l.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{listener: struct{}{}}) + listeners, loaded := l.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{listener: {}}) if loaded { listeners.(map[config_center.ConfigurationListener]struct{})[listener] = struct{}{} l.keyListeners.Store(key, listeners) } } +// RemoveListener ... func (l *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) { listeners, loaded := l.keyListeners.Load(key) if loaded { @@ -53,6 +58,7 @@ func (l *CacheListener) RemoveListener(key string, listener config_center.Config } } +// DataChange ... func (l *CacheListener) DataChange(event remoting.Event) bool { if event.Content == "" { //meanings new node diff --git a/contributing.md b/contributing.md index b1265c2351789d4929d81556d72234806aed6afa..9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51 100644 --- a/contributing.md +++ b/contributing.md @@ -28,4 +28,14 @@ The title format of the pull request `MUST` follow the following rules: ### 3.1 log -> 1 when logging the function's input parameter, you should add '@' before input parameter name. +>- 1 when logging the function's input parameter, you should add '@' before input parameter name. + +### 3.2 naming + +>- 1 do not use an underscore in package name, such as `filter_impl`. +>- 2 do not use an underscore in constants, such as `DUBBO_PROTOCOL`. use 'DubboProtocol' instead. + +### 3.3 comment + +>- 1 there should be comment for every export func/var. +>- 2 the comment should begin with function name/var name. \ No newline at end of file diff --git a/filter/access_key.go b/filter/access_key.go new file mode 100644 index 0000000000000000000000000000000000000000..c9bdd4ff8993d51e4d5002a1216225e2da074df5 --- /dev/null +++ b/filter/access_key.go @@ -0,0 +1,22 @@ +package filter + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +type AccessKeyPair struct { + AccessKey string `yaml:"accessKey" json:"accessKey,omitempty" property:"accessKey"` + SecretKey string `yaml:"secretKey" json:"secretKey,omitempty" property:"secretKey"` + ConsumerSide string `yaml:"consumerSide" json:"ConsumerSide,consumerSide" property:"consumerSide"` + ProviderSide string `yaml:"providerSide" json:"providerSide,omitempty" property:"providerSide"` + Creator string `yaml:"creator" json:"creator,omitempty" property:"creator"` + Options string `yaml:"options" json:"options,omitempty" property:"options"` +} + +// AccessKeyStorage +// This SPI Extension support 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 new file mode 100644 index 0000000000000000000000000000000000000000..ce0547b36b03b7078784a6c05c08cd3f89611ca4 --- /dev/null +++ b/filter/authenticator.go @@ -0,0 +1,18 @@ +package filter + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +// Authenticator +type Authenticator interface { + + // Sign + // give a sign to request + Sign(protocol.Invocation, *common.URL) error + + // Authenticate + // verify the signature of the request is valid or not + Authenticate(protocol.Invocation, *common.URL) error +} diff --git a/filter/filter.go b/filter/filter.go index 5bd78998a76a1b0e8af99b0b3f0d7e6c103bb794..c069510498c7ac68b2bb2169dfe7132a4ef63229 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -17,12 +17,16 @@ package filter +import ( + "context" +) import ( "github.com/apache/dubbo-go/protocol" ) +// Filter // Extension - Filter type Filter interface { - Invoke(protocol.Invoker, protocol.Invocation) protocol.Result - OnResponse(protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result + Invoke(context.Context, protocol.Invoker, protocol.Invocation) protocol.Result + OnResponse(context.Context, protocol.Result, protocol.Invoker, protocol.Invocation) protocol.Result } diff --git a/filter/impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go similarity index 89% rename from filter/impl/access_log_filter.go rename to filter/filter_impl/access_log_filter.go index 89fa34952f99057f1d8bb35794a57f9905f5f169..fbfe7565170c7df468f755a4bd1aadde166a79c1 100644 --- a/filter/impl/access_log_filter.go +++ b/filter/filter_impl/access_log_filter.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "os" "reflect" "strings" @@ -34,13 +35,21 @@ import ( const ( //used in URL. - FileDateFormat = "2006-01-02" + + // FileDateFormat ... + FileDateFormat = "2006-01-02" + // MessageDateLayout ... MessageDateLayout = "2006-01-02 15:04:05" - LogMaxBuffer = 5000 - LogFileMode = 0600 + // LogMaxBuffer ... + LogMaxBuffer = 5000 + // LogFileMode ... + LogFileMode = 0600 // those fields are the data collected by this filter - Types = "types" + + // Types ... + Types = "types" + // Arguments ... Arguments = "arguments" ) @@ -49,6 +58,7 @@ func init() { } /* + * AccessLogFilter * Although the access log filter is a default filter, * you should config "accesslog" in service's config to tell the filter where store the access log. * for example: @@ -66,13 +76,14 @@ type AccessLogFilter struct { logChan chan AccessLogData } -func (ef *AccessLogFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +func (ef *AccessLogFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { accessLog := invoker.GetUrl().GetParam(constant.ACCESS_LOG_KEY, "") if len(accessLog) > 0 { accessLogData := AccessLogData{data: ef.buildAccessLogData(invoker, invocation), accessLog: accessLog} ef.logIntoChannel(accessLogData) } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } // it won't block the invocation @@ -86,7 +97,7 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { } } -func (ef *AccessLogFilter) buildAccessLogData(invoker protocol.Invoker, invocation protocol.Invocation) map[string]string { +func (ef *AccessLogFilter) buildAccessLogData(_ protocol.Invoker, invocation protocol.Invocation) map[string]string { dataMap := make(map[string]string, 16) attachments := invocation.Attachments() dataMap[constant.INTERFACE_KEY] = attachments[constant.INTERFACE_KEY] @@ -119,7 +130,8 @@ func (ef *AccessLogFilter) buildAccessLogData(invoker protocol.Invoker, invocati return dataMap } -func (ef *AccessLogFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (ef *AccessLogFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } @@ -172,6 +184,7 @@ func isDefault(accessLog string) bool { return strings.EqualFold("true", accessLog) || strings.EqualFold("default", accessLog) } +// GetAccessLogFilter ... func GetAccessLogFilter() filter.Filter { accessLogFilter := &AccessLogFilter{logChan: make(chan AccessLogData, LogMaxBuffer)} go func() { @@ -182,6 +195,7 @@ func GetAccessLogFilter() filter.Filter { return accessLogFilter } +// AccessLogData ... type AccessLogData struct { accessLog string data map[string]string diff --git a/filter/impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go similarity index 84% rename from filter/impl/access_log_filter_test.go rename to filter/filter_impl/access_log_filter_test.go index 834d531f05f952c41abfe8e1c56c20c0285926b8..f0de24d2a89f35876a32763eeb75495e8919ecd9 100644 --- a/filter/impl/access_log_filter_test.go +++ b/filter/filter_impl/access_log_filter_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package filter_impl import ( "context" @@ -37,11 +37,11 @@ import ( func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - url, _ := common.NewURL(context.Background(), - "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+ - "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+ - "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+ - "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&"+ + url, _ := common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&" + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") invoker := protocol.NewBaseInvoker(url) @@ -49,18 +49,18 @@ func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) { inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) accessLogFilter := GetAccessLogFilter() - result := accessLogFilter.Invoke(invoker, inv) + result := accessLogFilter.Invoke(context.Background(), invoker, inv) assert.Nil(t, result.Error()) } func TestAccessLogFilter_Invoke_Default_Config(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - url, _ := common.NewURL(context.Background(), - "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider"+ - "&cluster=failover&accesslog=true&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser."+ - "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name="+ - "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&"+ + url, _ := common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&accesslog=true&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&" + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") invoker := protocol.NewBaseInvoker(url) @@ -70,13 +70,13 @@ func TestAccessLogFilter_Invoke_Default_Config(t *testing.T) { inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) accessLogFilter := GetAccessLogFilter() - result := accessLogFilter.Invoke(invoker, inv) + result := accessLogFilter.Invoke(context.Background(), invoker, inv) assert.Nil(t, result.Error()) } func TestAccessLogFilter_OnResponse(t *testing.T) { result := &protocol.RPCResult{} accessLogFilter := GetAccessLogFilter() - response := accessLogFilter.OnResponse(result, nil, nil) + response := accessLogFilter.OnResponse(nil, result, nil, nil) assert.Equal(t, result, response) } diff --git a/filter/impl/active_filter.go b/filter/filter_impl/active_filter.go similarity index 53% rename from filter/impl/active_filter.go rename to filter/filter_impl/active_filter.go index 36a4e1a767ab7170ce8e5bebf2cfa4403f6ad4ff..23f2c8e25609dff89392107251715fe6f5175f09 100644 --- a/filter/impl/active_filter.go +++ b/filter/filter_impl/active_filter.go @@ -15,37 +15,56 @@ * limitations under the License. */ -package impl +package filter_impl + +import ( + "context" + "strconv" +) import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/filter" "github.com/apache/dubbo-go/protocol" + invocation2 "github.com/apache/dubbo-go/protocol/invocation" ) -const active = "active" +const ( + active = "active" + dubboInvokeStartTime = "dubboInvokeStartTime" +) func init() { extension.SetFilter(active, GetActiveFilter) } +// ActiveFilter ... type ActiveFilter struct { } -func (ef *ActiveFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +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)) protocol.BeginCount(invoker.GetUrl(), invocation.MethodName()) - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (ef *ActiveFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { - - protocol.EndCount(invoker.GetUrl(), invocation.MethodName()) +// OnResponse ... +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 { + result.SetError(err) + logger.Errorf("parse dubbo_invoke_start_time to int64 failed") + return result + } + elapsed := protocol.CurrentTimeMillis() - startTime + protocol.EndCount(invoker.GetUrl(), invocation.MethodName(), elapsed, result.Error() == nil) return result } +// GetActiveFilter ... func GetActiveFilter() filter.Filter { return &ActiveFilter{} } diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8917e9141cad4f22ea201a9a07c2873b584c1f92 --- /dev/null +++ b/filter/filter_impl/active_filter_test.go @@ -0,0 +1,66 @@ +package filter_impl + +import ( + "context" + "errors" + "strconv" + "testing" +) + +import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/mock" +) + +func TestActiveFilter_Invoke(t *testing.T) { + invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]string, 0)) + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + filter := ActiveFilter{} + ctrl := gomock.NewController(t) + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + invoker.EXPECT().Invoke(gomock.Any()).Return(nil) + invoker.EXPECT().GetUrl().Return(url).Times(1) + filter.Invoke(context.Background(), invoker, invoc) + assert.True(t, invoc.AttachmentsByKey(dubboInvokeStartTime, "") != "") + +} + +func TestActiveFilter_OnResponse(t *testing.T) { + c := protocol.CurrentTimeMillis() + elapsed := 100 + invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{ + dubboInvokeStartTime: strconv.FormatInt(c-int64(elapsed), 10), + }) + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + filter := ActiveFilter{} + ctrl := gomock.NewController(t) + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + invoker.EXPECT().GetUrl().Return(url).Times(1) + result := &protocol.RPCResult{ + Err: errors.New("test"), + } + filter.OnResponse(nil, result, invoker, invoc) + methodStatus := protocol.GetMethodStatus(url, "test") + urlStatus := protocol.GetURLStatus(url) + + assert.Equal(t, int32(1), methodStatus.GetTotal()) + assert.Equal(t, int32(1), urlStatus.GetTotal()) + assert.Equal(t, int32(1), methodStatus.GetFailed()) + assert.Equal(t, int32(1), urlStatus.GetFailed()) + assert.Equal(t, int32(1), methodStatus.GetSuccessiveRequestFailureCount()) + assert.Equal(t, int32(1), urlStatus.GetSuccessiveRequestFailureCount()) + assert.True(t, methodStatus.GetFailedElapsed() >= int64(elapsed)) + assert.True(t, urlStatus.GetFailedElapsed() >= int64(elapsed)) + assert.True(t, urlStatus.GetLastRequestFailedTimestamp() != int64(0)) + assert.True(t, methodStatus.GetLastRequestFailedTimestamp() != int64(0)) + +} diff --git a/filter/filter_impl/auth/accesskey_storage.go b/filter/filter_impl/auth/accesskey_storage.go new file mode 100644 index 0000000000000000000000000000000000000000..0a2bf47cbd377899ba8a0edf4a67026dd827d41f --- /dev/null +++ b/filter/filter_impl/auth/accesskey_storage.go @@ -0,0 +1,31 @@ +package auth + +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/filter" + "github.com/apache/dubbo-go/protocol" +) + +// DefaultAccesskeyStorage +// The default implementation of AccesskeyStorage +type DefaultAccesskeyStorage struct { +} + +// GetAccessKeyPair +// get 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, ""), + SecretKey: url.GetParam(constant.SECRET_ACCESS_KEY_KEY, ""), + } +} + +func init() { + extension.SetAccesskeyStorages(constant.DEFAULT_ACCESS_KEY_STORAGE, GetDefaultAccesskeyStorage) +} + +func GetDefaultAccesskeyStorage() filter.AccessKeyStorage { + return &DefaultAccesskeyStorage{} +} diff --git a/filter/filter_impl/auth/accesskey_storage_test.go b/filter/filter_impl/auth/accesskey_storage_test.go new file mode 100644 index 0000000000000000000000000000000000000000..6ab861a8673b191be0a8063980e1dc53e4e70f60 --- /dev/null +++ b/filter/filter_impl/auth/accesskey_storage_test.go @@ -0,0 +1,28 @@ +package auth + +import ( + "net/url" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + invocation2 "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestDefaultAccesskeyStorage_GetAccesskeyPair(t *testing.T) { + url := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"), + common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey")) + invocation := &invocation2.RPCInvocation{} + storage := GetDefaultAccesskeyStorage() + accesskeyPair := storage.GetAccessKeyPair(invocation, url) + assert.Equal(t, "skey", accesskeyPair.SecretKey) + assert.Equal(t, "akey", accesskeyPair.AccessKey) +} diff --git a/filter/filter_impl/auth/consumer_sign.go b/filter/filter_impl/auth/consumer_sign.go new file mode 100644 index 0000000000000000000000000000000000000000..be86b5c74bb9fd02b96483edb18571d47d205ee7 --- /dev/null +++ b/filter/filter_impl/auth/consumer_sign.go @@ -0,0 +1,43 @@ +package auth + +import ( + "context" + "fmt" +) +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/filter" + "github.com/apache/dubbo-go/protocol" +) + +// ConsumerSignFilter +// This filter is working for signing the request on consumer side +type ConsumerSignFilter struct { +} + +func init() { + extension.SetFilter(constant.CONSUMER_SIGN_FILTER, getConsumerSignFilter) +} + +func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + logger.Infof("invoking ConsumerSign filter.") + url := invoker.GetUrl() + + err := doAuthWork(&url, func(authenticator filter.Authenticator) error { + return authenticator.Sign(invocation, &url) + }) + if err != nil { + panic(fmt.Sprintf("Sign for invocation %s # %s failed", url.ServiceKey(), invocation.MethodName())) + + } + return invoker.Invoke(ctx, invocation) +} + +func (csf *ConsumerSignFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + return result +} +func getConsumerSignFilter() filter.Filter { + return &ConsumerSignFilter{} +} diff --git a/filter/filter_impl/auth/consumer_sign_test.go b/filter/filter_impl/auth/consumer_sign_test.go new file mode 100644 index 0000000000000000000000000000000000000000..9a90970b898b75f3c3f1b195062538e62505a082 --- /dev/null +++ b/filter/filter_impl/auth/consumer_sign_test.go @@ -0,0 +1,37 @@ +package auth + +import ( + "context" + "testing" +) + +import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +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" + "github.com/apache/dubbo-go/protocol/mock" +) + +func TestConsumerSignFilter_Invoke(t *testing.T) { + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + url.SetParam(constant.SECRET_ACCESS_KEY_KEY, "sk") + url.SetParam(constant.ACCESS_KEY_ID_KEY, "ak") + inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil) + filter := &ConsumerSignFilter{} + ctrl := gomock.NewController(t) + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + result := &protocol.RPCResult{} + invoker.EXPECT().Invoke(inv).Return(result).Times(2) + invoker.EXPECT().GetUrl().Return(url).Times(2) + assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv)) + + url.SetParam(constant.SERVICE_AUTH_KEY, "true") + assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv)) +} diff --git a/filter/filter_impl/auth/default_authenticator.go b/filter/filter_impl/auth/default_authenticator.go new file mode 100644 index 0000000000000000000000000000000000000000..73eb9cddc0e1b7b4747da4b0f3e883075e349226 --- /dev/null +++ b/filter/filter_impl/auth/default_authenticator.go @@ -0,0 +1,120 @@ +package auth + +import ( + "errors" + "fmt" + "github.com/apache/dubbo-go/filter" + "strconv" + "time" +) + +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" + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" +) + +func init() { + extension.SetAuthenticator(constant.DEFAULT_AUTHENTICATOR, GetDefaultAuthenticator) +} + +// DefaultAuthenticator +// The default implemetation of Authenticator +type DefaultAuthenticator struct { +} + +// Sign +// add the signature for the invocation +func (authenticator *DefaultAuthenticator) Sign(invocation protocol.Invocation, url *common.URL) error { + currentTimeMillis := strconv.Itoa(int(time.Now().Unix() * 1000)) + + consumer := url.GetParam(constant.APPLICATION_KEY, "") + accessKeyPair, err := getAccessKeyPair(invocation, url) + if err != nil { + return errors.New("get accesskey pair failed, cause: " + err.Error()) + } + inv := invocation.(*invocation_impl.RPCInvocation) + signature, err := getSignature(url, invocation, accessKeyPair.SecretKey, currentTimeMillis) + if err != nil { + return err + } + inv.SetAttachments(constant.REQUEST_SIGNATURE_KEY, signature) + inv.SetAttachments(constant.REQUEST_TIMESTAMP_KEY, currentTimeMillis) + inv.SetAttachments(constant.AK_KEY, accessKeyPair.AccessKey) + inv.SetAttachments(constant.CONSUMER, consumer) + return nil +} + +// getSignature +// get signature by the metadata and params of the invocation +func getSignature(url *common.URL, invocation protocol.Invocation, secrectKey string, currentTime string) (string, error) { + + requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, + url.ColonSeparatedKey(), invocation.MethodName(), secrectKey, currentTime) + var signature string + if parameterEncrypt := url.GetParamBool(constant.PARAMTER_SIGNATURE_ENABLE_KEY, false); parameterEncrypt { + var err error + if signature, err = SignWithParams(invocation.Arguments(), requestString, secrectKey); err != nil { + // TODO + return "", errors.New("sign the request with params failed, cause:" + err.Error()) + } + } else { + signature = Sign(requestString, secrectKey) + } + + return signature, nil +} + +// Authenticate +// This method 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, "") + + requestTimestamp := invocation.AttachmentsByKey(constant.REQUEST_TIMESTAMP_KEY, "") + originSignature := invocation.AttachmentsByKey(constant.REQUEST_SIGNATURE_KEY, "") + consumer := invocation.AttachmentsByKey(constant.CONSUMER, "") + if IsEmpty(accessKeyId, false) || IsEmpty(consumer, false) || + IsEmpty(requestTimestamp, false) || IsEmpty(originSignature, false) { + return errors.New("failed to authenticate your ak/sk, maybe the consumer has not enabled the auth") + } + + accessKeyPair, err := getAccessKeyPair(invocation, url) + if err != nil { + return errors.New("failed to authenticate , can't load the accessKeyPair") + } + + computeSignature, err := getSignature(url, invocation, accessKeyPair.SecretKey, requestTimestamp) + if err != nil { + return err + } + if success := computeSignature == originSignature; !success { + return errors.New("failed to authenticate, signature is not correct") + } + return nil +} + +func getAccessKeyPair(invocation protocol.Invocation, url *common.URL) (*filter.AccessKeyPair, error) { + accesskeyStorage := extension.GetAccesskeyStorages(url.GetParam(constant.ACCESS_KEY_STORAGE_KEY, constant.DEFAULT_ACCESS_KEY_STORAGE)) + accessKeyPair := accesskeyStorage.GetAccessKeyPair(invocation, url) + if accessKeyPair == nil || IsEmpty(accessKeyPair.AccessKey, false) || IsEmpty(accessKeyPair.SecretKey, true) { + return nil, errors.New("accessKeyId or secretAccessKey not found") + } else { + return accessKeyPair, nil + } +} + +func GetDefaultAuthenticator() filter.Authenticator { + return &DefaultAuthenticator{} +} + +func doAuthWork(url *common.URL, do func(filter.Authenticator) error) error { + + shouldAuth := url.GetParamBool(constant.SERVICE_AUTH_KEY, false) + if shouldAuth { + authenticator := extension.GetAuthenticator(url.GetParam(constant.AUTHENTICATOR_KEY, constant.DEFAULT_AUTHENTICATOR)) + return do(authenticator) + } + return nil +} diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go new file mode 100644 index 0000000000000000000000000000000000000000..72220eec99533b73cc2c9159e3443d6f566471fa --- /dev/null +++ b/filter/filter_impl/auth/default_authenticator_test.go @@ -0,0 +1,130 @@ +package auth + +import ( + "fmt" + "net/url" + "strconv" + "testing" + "time" +) + +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/protocol/invocation" +) + +func TestDefaultAuthenticator_Authenticate(t *testing.T) { + secret := "dubbo-sk" + access := "dubbo-ak" + testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true") + testurl.SetParam(constant.ACCESS_KEY_ID_KEY, access) + testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, secret) + parmas := []interface{}{"OK", struct { + Name string + Id int64 + }{"YUYU", 1}} + inv := invocation.NewRPCInvocation("test", parmas, nil) + requestTime := strconv.Itoa(int(time.Now().Unix() * 1000)) + signature, _ := getSignature(&testurl, inv, secret, requestTime) + + var authenticator = &DefaultAuthenticator{} + + invcation := invocation.NewRPCInvocation("test", parmas, map[string]string{ + constant.REQUEST_SIGNATURE_KEY: signature, + constant.CONSUMER: "test", + constant.REQUEST_TIMESTAMP_KEY: requestTime, + constant.AK_KEY: access, + }) + err := authenticator.Authenticate(invcation, &testurl) + assert.Nil(t, err) + // modify the params + invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]string{ + constant.REQUEST_SIGNATURE_KEY: signature, + constant.CONSUMER: "test", + constant.REQUEST_TIMESTAMP_KEY: requestTime, + constant.AK_KEY: access, + }) + err = authenticator.Authenticate(invcation, &testurl) + assert.NotNil(t, err) + +} + +func TestDefaultAuthenticator_Sign(t *testing.T) { + authenticator := &DefaultAuthenticator{} + testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?application=test&interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + testurl.SetParam(constant.ACCESS_KEY_ID_KEY, "akey") + testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, "skey") + testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false") + inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil) + _ = authenticator.Sign(inv, &testurl) + assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_SIGNATURE_KEY, ""), "") + assert.NotEqual(t, inv.AttachmentsByKey(constant.CONSUMER, ""), "") + assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_TIMESTAMP_KEY, ""), "") + assert.Equal(t, inv.AttachmentsByKey(constant.AK_KEY, ""), "akey") + +} + +func Test_getAccessKeyPairSuccess(t *testing.T) { + testurl := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"), + common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey")) + invcation := invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil) + _, e := getAccessKeyPair(invcation, testurl) + assert.Nil(t, e) +} + +func Test_getAccessKeyPairFailed(t *testing.T) { + defer func() { + e := recover() + assert.NotNil(t, e) + }() + testurl := common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey")) + invcation := invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil) + _, e := getAccessKeyPair(invcation, testurl) + assert.NotNil(t, e) + testurl = common.NewURLWithOptions( + common.WithParams(url.Values{}), + common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"), + common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"), common.WithParamsValue(constant.ACCESS_KEY_STORAGE_KEY, "dubbo")) + _, e = getAccessKeyPair(invcation, testurl) + +} + +func Test_getSignatureWithinParams(t *testing.T) { + testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true") + inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{ + "": "", + }) + secret := "dubbo" + current := strconv.Itoa(int(time.Now().Unix() * 1000)) + signature, _ := getSignature(&testurl, inv, secret, current) + requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, + testurl.ColonSeparatedKey(), inv.MethodName(), secret, current) + s, _ := SignWithParams(inv.Arguments(), requestString, secret) + assert.False(t, IsEmpty(signature, false)) + assert.Equal(t, s, signature) +} + +func Test_getSignature(t *testing.T) { + testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false") + inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil) + secret := "dubbo" + current := strconv.Itoa(int(time.Now().Unix() * 1000)) + signature, _ := getSignature(&testurl, inv, secret, current) + requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, + testurl.ColonSeparatedKey(), inv.MethodName(), secret, current) + s := Sign(requestString, secret) + assert.False(t, IsEmpty(signature, false)) + assert.Equal(t, s, signature) +} diff --git a/filter/filter_impl/auth/provider_auth.go b/filter/filter_impl/auth/provider_auth.go new file mode 100644 index 0000000000000000000000000000000000000000..90804934f6b01a61f021f61f4ee549d744ccee72 --- /dev/null +++ b/filter/filter_impl/auth/provider_auth.go @@ -0,0 +1,43 @@ +package auth + +import ( + "context" + "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/filter" + "github.com/apache/dubbo-go/protocol" +) + +// ProviderAuthFilter +// This filter is used to verify the correctness of the signature on provider side +type ProviderAuthFilter struct { +} + +func init() { + extension.SetFilter(constant.PROVIDER_AUTH_FILTER, getProviderAuthFilter) +} + +func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + logger.Infof("invoking providerAuth filter.") + url := invoker.GetUrl() + + err := doAuthWork(&url, func(authenticator filter.Authenticator) error { + return authenticator.Authenticate(invocation, &url) + }) + if err != nil { + logger.Infof("auth the request: %v occur exception, cause: %s", invocation, err.Error()) + return &protocol.RPCResult{ + Err: err, + } + } + + return invoker.Invoke(ctx, invocation) +} + +func (paf *ProviderAuthFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + return result +} +func getProviderAuthFilter() filter.Filter { + return &ProviderAuthFilter{} +} diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go new file mode 100644 index 0000000000000000000000000000000000000000..88d6423458d5534f18da4316ffe1bca0b374e43c --- /dev/null +++ b/filter/filter_impl/auth/provider_auth_test.go @@ -0,0 +1,57 @@ +package auth + +import ( + "context" + "strconv" + "testing" + "time" +) + +import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +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" + "github.com/apache/dubbo-go/protocol/mock" +) + +func TestProviderAuthFilter_Invoke(t *testing.T) { + secret := "dubbo-sk" + access := "dubbo-ak" + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") + url.SetParam(constant.ACCESS_KEY_ID_KEY, access) + url.SetParam(constant.SECRET_ACCESS_KEY_KEY, secret) + parmas := []interface{}{ + "OK", + struct { + Name string + Id int64 + }{"YUYU", 1}, + } + inv := invocation.NewRPCInvocation("test", parmas, nil) + requestTime := strconv.Itoa(int(time.Now().Unix() * 1000)) + signature, _ := getSignature(&url, inv, secret, requestTime) + + inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{ + constant.REQUEST_SIGNATURE_KEY: signature, + constant.CONSUMER: "test", + constant.REQUEST_TIMESTAMP_KEY: requestTime, + constant.AK_KEY: access, + }) + ctrl := gomock.NewController(t) + filter := &ProviderAuthFilter{} + defer ctrl.Finish() + invoker := mock.NewMockInvoker(ctrl) + result := &protocol.RPCResult{} + invoker.EXPECT().Invoke(inv).Return(result).Times(2) + invoker.EXPECT().GetUrl().Return(url).Times(2) + assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv)) + url.SetParam(constant.SERVICE_AUTH_KEY, "true") + assert.Equal(t, result, filter.Invoke(context.Background(), invoker, inv)) + +} diff --git a/filter/filter_impl/auth/sign_util.go b/filter/filter_impl/auth/sign_util.go new file mode 100644 index 0000000000000000000000000000000000000000..60698439c5abc1ff0cc555b2ceec77bf2e0e53d5 --- /dev/null +++ b/filter/filter_impl/auth/sign_util.go @@ -0,0 +1,55 @@ +package auth + +import ( + "crypto/hmac" + "crypto/sha256" + "encoding/base64" + "encoding/json" + "errors" + "strings" +) + +// Sign +// get a signature string with given information, such as metadata or parameters +func Sign(metadata, key string) string { + return doSign([]byte(metadata), key) +} + +func SignWithParams(params []interface{}, metadata, key string) (string, error) { + if params == nil || len(params) == 0 { + return Sign(metadata, key), nil + } + + data := append(params, metadata) + if bytes, err := toBytes(data); err != nil { + // TODO + return "", errors.New("data convert to bytes failed") + } else { + return doSign(bytes, key), nil + } +} + +func toBytes(data []interface{}) ([]byte, error) { + if bytes, err := json.Marshal(data); err != nil { + return nil, errors.New("") + } else { + return bytes, nil + } +} + +func doSign(bytes []byte, key string) string { + mac := hmac.New(sha256.New, []byte(key)) + mac.Write(bytes) + signature := mac.Sum(nil) + return base64.URLEncoding.EncodeToString(signature) +} + +func IsEmpty(s string, allowSpace bool) bool { + if len(s) == 0 { + return true + } + if !allowSpace { + return strings.TrimSpace(s) == "" + } + return false +} diff --git a/filter/filter_impl/auth/sign_util_test.go b/filter/filter_impl/auth/sign_util_test.go new file mode 100644 index 0000000000000000000000000000000000000000..de6154e8854af99f8e862d94ee45aefcbf26b12b --- /dev/null +++ b/filter/filter_impl/auth/sign_util_test.go @@ -0,0 +1,84 @@ +package auth + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestIsEmpty(t *testing.T) { + type args struct { + s string + allowSpace bool + } + tests := []struct { + name string + args args + want bool + }{ + // TODO: Add test cases. + {"test1", args{s: " ", allowSpace: false}, true}, + {"test2", args{s: " ", allowSpace: true}, false}, + {"test3", args{s: "hello,dubbo", allowSpace: false}, false}, + {"test4", args{s: "hello,dubbo", allowSpace: true}, false}, + {"test5", args{s: "", allowSpace: true}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := IsEmpty(tt.args.s, tt.args.allowSpace); got != tt.want { + t.Errorf("IsEmpty() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSign(t *testing.T) { + metadata := "com.ikurento.user.UserProvider::sayHi" + key := "key" + signature := Sign(metadata, key) + assert.NotNil(t, signature) + +} + +func TestSignWithParams(t *testing.T) { + metadata := "com.ikurento.user.UserProvider::sayHi" + key := "key" + params := []interface{}{ + "a", 1, struct { + Name string + Id int64 + }{"YuYu", 1}, + } + signature, _ := SignWithParams(params, metadata, key) + assert.False(t, IsEmpty(signature, false)) +} + +func Test_doSign(t *testing.T) { + sign := doSign([]byte("DubboGo"), "key") + sign1 := doSign([]byte("DubboGo"), "key") + sign2 := doSign([]byte("DubboGo"), "key2") + assert.NotNil(t, sign) + assert.Equal(t, sign1, sign) + assert.NotEqual(t, sign1, sign2) +} + +func Test_toBytes(t *testing.T) { + params := []interface{}{ + "a", 1, struct { + Name string + Id int64 + }{"YuYu", 1}, + } + params2 := []interface{}{ + "a", 1, struct { + Name string + Id int64 + }{"YuYu", 1}, + } + jsonBytes, _ := toBytes(params) + jsonBytes2, _ := toBytes(params2) + assert.NotNil(t, jsonBytes) + assert.Equal(t, jsonBytes, jsonBytes2) +} diff --git a/filter/impl/echo_filter.go b/filter/filter_impl/echo_filter.go similarity index 79% rename from filter/impl/echo_filter.go rename to filter/filter_impl/echo_filter.go index 18e42c8cb2b15acb27573c5e24f11a8b69e0d496..a12800a21a8ebe4545b4a8b5bd0f8a30c1462105 100644 --- a/filter/impl/echo_filter.go +++ b/filter/filter_impl/echo_filter.go @@ -15,7 +15,11 @@ * limitations under the License. */ -package impl +package filter_impl + +import ( + "context" +) import ( "github.com/apache/dubbo-go/common/constant" @@ -26,6 +30,7 @@ import ( ) const ( + // ECHO echo module name ECHO = "echo" ) @@ -33,12 +38,14 @@ func init() { extension.SetFilter(ECHO, GetFilter) } +// EchoFilter // 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{} -func (ef *EchoFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +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())) if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 { @@ -48,13 +55,17 @@ func (ef *EchoFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invoc } } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (ef *EchoFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (ef *EchoFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, + _ protocol.Invocation) protocol.Result { + return result } +// GetFilter ... func GetFilter() filter.Filter { return &EchoFilter{} } diff --git a/filter/impl/echo_filter_test.go b/filter/filter_impl/echo_filter_test.go similarity index 77% rename from filter/impl/echo_filter_test.go rename to filter/filter_impl/echo_filter_test.go index e2e592974701ad18c5b01e884485c022ee2320b8..fc09bdce696c6be3c9e11d0ac864b187d1d85cde 100644 --- a/filter/impl/echo_filter_test.go +++ b/filter/filter_impl/echo_filter_test.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "testing" ) @@ -33,12 +34,10 @@ import ( func TestEchoFilter_Invoke(t *testing.T) { filter := GetFilter() - result := filter.Invoke(protocol.NewBaseInvoker(common.URL{}), - invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil)) assert.Equal(t, "OK", result.Result()) - result = filter.Invoke(protocol.NewBaseInvoker(common.URL{}), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)) + result = filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } diff --git a/filter/impl/execute_limit_filter.go b/filter/filter_impl/execute_limit_filter.go similarity index 78% rename from filter/impl/execute_limit_filter.go rename to filter/filter_impl/execute_limit_filter.go index 156af1b140283dd76c4867ca26e9b42ce8eb25c0..434c378045456eb13317e0a48630ebd33f244c05 100644 --- a/filter/impl/execute_limit_filter.go +++ b/filter/filter_impl/execute_limit_filter.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "strconv" "sync" "sync/atomic" @@ -32,17 +33,20 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/filter" - _ "github.com/apache/dubbo-go/filter/common/impl" + _ "github.com/apache/dubbo-go/filter/handler" "github.com/apache/dubbo-go/protocol" ) -const name = "execute" +const ( + name = "execute" +) func init() { extension.SetFilter(name, GetExecuteLimitFilter) } /** + * ExecuteLimitFilter * The filter will limit the number of in-progress request and it's thread-safe. * example: * "UserProvider": @@ -71,23 +75,25 @@ type ExecuteLimitFilter struct { executeState *concurrent.Map } +// ExecuteState ... type ExecuteState struct { concurrentCount int64 } -func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +func (ef *ExecuteLimitFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { methodConfigPrefix := "methods." + invocation.MethodName() + "." - url := invoker.GetUrl() - limitTarget := url.ServiceKey() + ivkURL := invoker.GetUrl() + limitTarget := ivkURL.ServiceKey() limitRateConfig := constant.DEFAULT_EXECUTE_LIMIT - methodLevelConfig := url.GetParam(methodConfigPrefix+constant.EXECUTE_LIMIT_KEY, "") + methodLevelConfig := ivkURL.GetParam(methodConfigPrefix+constant.EXECUTE_LIMIT_KEY, "") if len(methodLevelConfig) > 0 { // we have the method-level configuration limitTarget = limitTarget + "#" + invocation.MethodName() limitRateConfig = methodLevelConfig } else { - limitRateConfig = url.GetParam(constant.EXECUTE_LIMIT_KEY, constant.DEFAULT_EXECUTE_LIMIT) + limitRateConfig = ivkURL.GetParam(constant.EXECUTE_LIMIT_KEY, constant.DEFAULT_EXECUTE_LIMIT) } limitRate, err := strconv.ParseInt(limitRateConfig, 0, 0) @@ -97,7 +103,7 @@ func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protoc } if limitRate < 0 { - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } state, _ := ef.executeState.LoadOrStore(limitTarget, &ExecuteState{ @@ -107,16 +113,17 @@ func (ef *ExecuteLimitFilter) Invoke(invoker protocol.Invoker, invocation protoc concurrentCount := state.(*ExecuteState).increase() defer state.(*ExecuteState).decrease() if concurrentCount > limitRate { - logger.Errorf("The invocation was rejected due to over the execute limitation, url: %s ", url.String()) - rejectedHandlerConfig := url.GetParam(methodConfigPrefix+constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, - url.GetParam(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY)) - return extension.GetRejectedExecutionHandler(rejectedHandlerConfig).RejectedExecution(url, invocation) + logger.Errorf("The invocation was rejected due to over the execute limitation, url: %s ", ivkURL.String()) + rejectedHandlerConfig := ivkURL.GetParam(methodConfigPrefix+constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, + ivkURL.GetParam(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY)) + return extension.GetRejectedExecutionHandler(rejectedHandlerConfig).RejectedExecution(ivkURL, invocation) } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (ef *ExecuteLimitFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (ef *ExecuteLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { return result } @@ -131,6 +138,7 @@ func (state *ExecuteState) decrease() { var executeLimitOnce sync.Once var executeLimitFilter *ExecuteLimitFilter +// GetExecuteLimitFilter ... func GetExecuteLimitFilter() filter.Filter { executeLimitOnce.Do(func() { executeLimitFilter = &ExecuteLimitFilter{ diff --git a/filter/impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go similarity index 88% rename from filter/impl/execute_limit_filter_test.go rename to filter/filter_impl/execute_limit_filter_test.go index 5d729c0e6a1205902856eccfa6aa96b0bee0e790..ae8641f2db0b98b59f9939cfc85f3ad096b1bc7f 100644 --- a/filter/impl/execute_limit_filter_test.go +++ b/filter/filter_impl/execute_limit_filter_test.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "net/url" "testing" ) @@ -43,7 +44,7 @@ func TestExecuteLimitFilter_Invoke_Ignored(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } @@ -60,7 +61,7 @@ func TestExecuteLimitFilter_Invoke_Configure_Error(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } @@ -77,7 +78,7 @@ func TestExecuteLimitFilter_Invoke(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } diff --git a/filter/impl/generic_filter.go b/filter/filter_impl/generic_filter.go similarity index 80% rename from filter/impl/generic_filter.go rename to filter/filter_impl/generic_filter.go index 35aadb11a444bda56109e238b17267f71ec2606b..e8ff2679b0294d6519aecd1cc1fe37bdeab89e46 100644 --- a/filter/impl/generic_filter.go +++ b/filter/filter_impl/generic_filter.go @@ -15,15 +15,18 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "reflect" "strings" ) + import ( hessian "github.com/apache/dubbo-go-hessian2" ) + import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -33,6 +36,8 @@ import ( ) const ( + // GENERIC + //generic module name GENERIC = "generic" ) @@ -42,35 +47,39 @@ func init() { // when do a generic invoke, struct need to be map +// GenericFilter ... type GenericFilter struct{} -func (ef *GenericFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +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() - var newParams []hessian.Object + if oldParams, ok := oldArguments[2].([]interface{}); ok { + newParams := make([]hessian.Object, 0, len(oldParams)) for i := range oldParams { newParams = append(newParams, hessian.Object(struct2MapAll(oldParams[i]))) } - } else { - return invoker.Invoke(invocation) - } - newArguments := []interface{}{ - oldArguments[0], - oldArguments[1], - newParams, + newArguments := []interface{}{ + oldArguments[0], + oldArguments[1], + newParams, + } + newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments()) + newInvocation.SetReply(invocation.Reply()) + return invoker.Invoke(ctx, newInvocation) } - newInvocation := invocation2.NewRPCInvocation(invocation.MethodName(), newArguments, invocation.Attachments()) - newInvocation.SetReply(invocation.Reply()) - return invoker.Invoke(newInvocation) } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (ef *GenericFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (ef *GenericFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, + _ protocol.Invocation) protocol.Result { return result } +// GetGenericFilter ... func GetGenericFilter() filter.Filter { return &GenericFilter{} } diff --git a/filter/impl/generic_filter_test.go b/filter/filter_impl/generic_filter_test.go similarity index 99% rename from filter/impl/generic_filter_test.go rename to filter/filter_impl/generic_filter_test.go index 9797c40df1f57017241675013620a53320e475ad..22948353fc16a99696a85489ce5df7dc9b18a7ba 100644 --- a/filter/impl/generic_filter_test.go +++ b/filter/filter_impl/generic_filter_test.go @@ -15,12 +15,13 @@ * limitations under the License. */ -package impl +package filter_impl import ( "reflect" "testing" ) + import ( "github.com/stretchr/testify/assert" ) diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..6272df6b39b0c18a77721f3a8c9e92618133aa6c --- /dev/null +++ b/filter/filter_impl/generic_service_filter.go @@ -0,0 +1,133 @@ +/* + * 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 filter_impl + +import ( + "context" + "reflect" + "strings" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/mitchellh/mapstructure" + 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/filter" + "github.com/apache/dubbo-go/protocol" + invocation2 "github.com/apache/dubbo-go/protocol/invocation" +) + +const ( + // GENERIC_SERVICE ... + GENERIC_SERVICE = "generic_service" + // GENERIC_SERIALIZATION_DEFAULT ... + GENERIC_SERIALIZATION_DEFAULT = "true" +) + +func init() { + extension.SetFilter(GENERIC_SERVICE, GetGenericServiceFilter) +} + +// GenericServiceFilter ... +type GenericServiceFilter struct{} + +// Invoke ... +func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + logger.Infof("invoking generic service filter.") + logger.Debugf("generic service filter methodName:%v,args:%v", invocation.MethodName(), len(invocation.Arguments())) + + if invocation.MethodName() != constant.GENERIC || len(invocation.Arguments()) != 3 { + return invoker.Invoke(ctx, invocation) + } + + var ( + ok bool + err error + methodName string + newParams []interface{} + genericKey string + argsType []reflect.Type + oldParams []hessian.Object + ) + + url := invoker.GetUrl() + methodName = invocation.Arguments()[0].(string) + // get service + svc := common.ServiceMap.GetService(url.Protocol, strings.TrimPrefix(url.Path, "/")) + // get method + method := svc.Method()[methodName] + if method == nil { + logger.Errorf("[Generic Service Filter] Don't have this method: %s", methodName) + return &protocol.RPCResult{} + } + argsType = method.ArgsType() + genericKey = invocation.AttachmentsByKey(constant.GENERIC_KEY, GENERIC_SERIALIZATION_DEFAULT) + if genericKey == GENERIC_SERIALIZATION_DEFAULT { + oldParams, ok = invocation.Arguments()[2].([]hessian.Object) + } else { + logger.Errorf("[Generic Service Filter] Don't support this generic: %s", genericKey) + return &protocol.RPCResult{} + } + if !ok { + logger.Errorf("[Generic Service Filter] wrong serialization") + return &protocol.RPCResult{} + } + if len(oldParams) != len(argsType) { + logger.Errorf("[Generic Service Filter] method:%s invocation arguments number was wrong", methodName) + return &protocol.RPCResult{} + } + // oldParams convert to newParams + newParams = make([]interface{}, len(oldParams)) + for i := range argsType { + newParam := reflect.New(argsType[i]).Interface() + err = mapstructure.Decode(oldParams[i], newParam) + newParam = reflect.ValueOf(newParam).Elem().Interface() + if err != nil { + logger.Errorf("[Generic Service Filter] decode arguments map to struct wrong: error{%v}", perrors.WithStack(err)) + return &protocol.RPCResult{} + } + newParams[i] = newParam + } + newInvocation := invocation2.NewRPCInvocation(methodName, newParams, invocation.Attachments()) + newInvocation.SetReply(invocation.Reply()) + return invoker.Invoke(ctx, newInvocation) +} + +// OnResponse ... +func (ef *GenericServiceFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + if invocation.MethodName() == constant.GENERIC && len(invocation.Arguments()) == 3 && result.Result() != nil { + v := reflect.ValueOf(result.Result()) + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + result.SetResult(struct2MapAll(v.Interface())) + } + return result +} + +// GetGenericServiceFilter ... +func GetGenericServiceFilter() filter.Filter { + return &GenericServiceFilter{} +} diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..37c6af7450a75449fce51182684be2f619eda9d8 --- /dev/null +++ b/filter/filter_impl/generic_service_filter_test.go @@ -0,0 +1,149 @@ +/* + * 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 filter_impl + +import ( + "context" + "errors" + "reflect" + "testing" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +type TestStruct struct { + AaAa string + BaBa string `m:"baBa"` + XxYy struct { + xxXx string `m:"xxXx"` + Xx string `m:"xx"` + } `m:"xxYy"` +} + +func (c *TestStruct) JavaClassName() string { + return "com.test.testStruct" +} + +type TestService struct{} + +// MethodOne ... +func (ts *TestService) MethodOne(_ context.Context, test1 *TestStruct, test2 []TestStruct, + test3 interface{}, test4 []interface{}, test5 *string) (*TestStruct, error) { + if test1 == nil { + return nil, errors.New("param test1 is nil") + } + if test2 == nil { + return nil, errors.New("param test2 is nil") + } + if test3 == nil { + return nil, errors.New("param test3 is nil") + } + if test4 == nil { + return nil, errors.New("param test4 is nil") + } + if test5 == nil { + return nil, errors.New("param test5 is nil") + } + return &TestStruct{}, nil +} + +// Reference ... +func (*TestService) Reference() string { + return "com.test.Path" +} + +func TestGenericServiceFilter_Invoke(t *testing.T) { + hessian.RegisterPOJO(&TestStruct{}) + methodName := "$invoke" + m := make(map[string]interface{}) + m["AaAa"] = "nihao" + x := make(map[string]interface{}) + x["xxXX"] = "nihaoxxx" + m["XxYy"] = x + aurguments := []interface{}{ + "MethodOne", + nil, + []hessian.Object{ + hessian.Object(m), + hessian.Object(append(make([]map[string]interface{}, 1), m)), + hessian.Object("111"), + hessian.Object(append(make([]map[string]interface{}, 1), m)), + hessian.Object("222")}, + } + s := &TestService{} + _, _ = common.ServiceMap.Register("testprotocol", s) + rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) + filter := GetGenericServiceFilter() + url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path") + result := filter.Invoke(context.Background(), &proxy_factory.ProxyInvoker{BaseInvoker: *protocol.NewBaseInvoker(url)}, rpcInvocation) + assert.NotNil(t, result) + assert.Nil(t, result.Error()) +} + +func TestGenericServiceFilter_ResponseTestStruct(t *testing.T) { + ts := &TestStruct{ + AaAa: "aaa", + BaBa: "bbb", + XxYy: struct { + xxXx string `m:"xxXx"` + Xx string `m:"xx"` + }{}, + } + result := &protocol.RPCResult{ + Rest: ts, + } + aurguments := []interface{}{ + "MethodOne", + nil, + []hessian.Object{nil}, + } + filter := GetGenericServiceFilter() + methodName := "$invoke" + rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) + r := filter.OnResponse(nil, result, nil, rpcInvocation) + assert.NotNil(t, r.Result()) + assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.Map) +} + +func TestGenericServiceFilter_ResponseString(t *testing.T) { + str := "111" + result := &protocol.RPCResult{ + Rest: str, + } + aurguments := []interface{}{ + "MethodOne", + nil, + []hessian.Object{nil}, + } + filter := GetGenericServiceFilter() + methodName := "$invoke" + rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) + r := filter.OnResponse(nil, result, nil, rpcInvocation) + assert.NotNil(t, r.Result()) + assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.String) +} diff --git a/filter/impl/graceful_shutdown_filter.go b/filter/filter_impl/graceful_shutdown_filter.go similarity index 85% rename from filter/impl/graceful_shutdown_filter.go rename to filter/filter_impl/graceful_shutdown_filter.go index b912ea88e4ba4741b7d7fe36b8bbd3ba158abe63..95e625b2d56895a4d57823e4e0e2e7d1d5e90a08 100644 --- a/filter/impl/graceful_shutdown_filter.go +++ b/filter/filter_impl/graceful_shutdown_filter.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "sync/atomic" ) @@ -27,7 +28,6 @@ import ( "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/filter" - "github.com/apache/dubbo-go/filter/common" "github.com/apache/dubbo-go/protocol" ) @@ -53,16 +53,16 @@ type gracefulShutdownFilter struct { shutdownConfig *config.ShutdownConfig } -func (gf *gracefulShutdownFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +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.") return gf.getRejectHandler().RejectedExecution(invoker.GetUrl(), invocation) } atomic.AddInt32(&gf.activeCount, 1) - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (gf *gracefulShutdownFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.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. if gf.shutdownConfig != nil && gf.activeCount <= 0 { @@ -78,7 +78,7 @@ func (gf *gracefulShutdownFilter) rejectNewRequest() bool { return gf.shutdownConfig.RejectRequest } -func (gf *gracefulShutdownFilter) getRejectHandler() common.RejectedExecutionHandler { +func (gf *gracefulShutdownFilter) getRejectHandler() filter.RejectedExecutionHandler { handler := constant.DEFAULT_KEY if gf.shutdownConfig != nil && len(gf.shutdownConfig.RejectRequestHandler) > 0 { handler = gf.shutdownConfig.RejectRequestHandler diff --git a/filter/impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go similarity index 82% rename from filter/impl/graceful_shutdown_filter_test.go rename to filter/filter_impl/graceful_shutdown_filter_test.go index 21da167ea0f201ea357c51cab0ecb4f8ebec0957..4c670933e3dcec29ad9ae7bfef250b4236ae7c54 100644 --- a/filter/impl/graceful_shutdown_filter_test.go +++ b/filter/filter_impl/graceful_shutdown_filter_test.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "net/url" "testing" ) @@ -31,8 +32,8 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config" - filterCommon "github.com/apache/dubbo-go/filter/common" - "github.com/apache/dubbo-go/filter/common/impl" + "github.com/apache/dubbo-go/filter" + common2 "github.com/apache/dubbo-go/filter/handler" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -53,7 +54,7 @@ func TestGenericFilter_Invoke(t *testing.T) { assert.Equal(t, extension.GetRejectedExecutionHandler(constant.DEFAULT_KEY), shutdownFilter.getRejectHandler()) - result := shutdownFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), invoc) + result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) @@ -64,10 +65,10 @@ func TestGenericFilter_Invoke(t *testing.T) { shutdownFilter.shutdownConfig = providerConfig.ShutdownConfig assert.True(t, shutdownFilter.rejectNewRequest()) - result = shutdownFilter.OnResponse(nil, protocol.NewBaseInvoker(*invokeUrl), invoc) + result = shutdownFilter.OnResponse(nil, nil, protocol.NewBaseInvoker(*invokeUrl), invoc) - rejectHandler := &impl.OnlyLogRejectedExecutionHandler{} - extension.SetRejectedExecutionHandler("mock", func() filterCommon.RejectedExecutionHandler { + rejectHandler := &common2.OnlyLogRejectedExecutionHandler{} + extension.SetRejectedExecutionHandler("mock", func() filter.RejectedExecutionHandler { return rejectHandler }) assert.True(t, providerConfig.ShutdownConfig.RequestsFinished) diff --git a/filter/impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go similarity index 91% rename from filter/impl/hystrix_filter.go rename to filter/filter_impl/hystrix_filter.go index 3fd9f87168616b69d5ec72460767890d6956c154..9fd97b57b677c9aa8ec492151df9aace6dc78b62 100644 --- a/filter/impl/hystrix_filter.go +++ b/filter/filter_impl/hystrix_filter.go @@ -14,9 +14,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package impl + +package filter_impl import ( + "context" "fmt" "regexp" "sync" @@ -35,9 +37,12 @@ import ( ) const ( + // HYSTRIX_CONSUMER ... HYSTRIX_CONSUMER = "hystrix_consumer" + // HYSTRIX_PROVIDER ... HYSTRIX_PROVIDER = "hystrix_provider" - HYSTRIX = "hystrix" + // HYSTRIX ... + HYSTRIX = "hystrix" ) var ( @@ -57,6 +62,7 @@ func init() { extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider) } +// HystrixFilterError ... type HystrixFilterError struct { err error failByHystrix bool @@ -66,9 +72,12 @@ func (hfError *HystrixFilterError) Error() string { return hfError.err.Error() } +// FailByHystrix ... func (hfError *HystrixFilterError) FailByHystrix() bool { return hfError.failByHystrix } + +// NewHystrixFilterError ... func NewHystrixFilterError(err error, failByHystrix bool) error { return &HystrixFilterError{ err: err, @@ -76,14 +85,15 @@ func NewHystrixFilterError(err error, failByHystrix bool) error { } } +// HystrixFilter ... type HystrixFilter struct { COrP bool //true for consumer res map[string][]*regexp.Regexp ifNewMap sync.Map } -func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { - +// Invoke ... +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()) // Do the configuration if the circuit breaker is created for the first time @@ -115,12 +125,12 @@ func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.In configLoadMutex.RUnlock() if err != nil { logger.Errorf("[Hystrix Filter]Errors occurred getting circuit for %s , will invoke without hystrix, error is: ", cmdName, err) - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } logger.Infof("[Hystrix Filter]Using hystrix filter: %s", cmdName) var result protocol.Result _ = hystrix.Do(cmdName, func() error { - result = invoker.Invoke(invocation) + result = invoker.Invoke(ctx, invocation) err := result.Error() if err != nil { result.SetError(NewHystrixFilterError(err, false)) @@ -144,9 +154,12 @@ func (hf *HystrixFilter) Invoke(invoker protocol.Invoker, invocation protocol.In return result } -func (hf *HystrixFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (hf *HystrixFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } + +// GetHystrixFilterConsumer ... func GetHystrixFilterConsumer() filter.Filter { //When first called, load the config in consumerConfigOnce.Do(func() { @@ -157,6 +170,7 @@ func GetHystrixFilterConsumer() filter.Filter { return &HystrixFilter{COrP: true} } +// GetHystrixFilterProvider ... func GetHystrixFilterProvider() filter.Filter { providerConfigOnce.Do(func() { if err := initHystrixConfigProvider(); err != nil { @@ -215,6 +229,7 @@ func initHystrixConfigConsumer() error { } return nil } + func initHystrixConfigProvider() error { if config.GetProviderConfig().FilterConf == nil { return perrors.Errorf("no config for hystrix") @@ -241,6 +256,7 @@ func initHystrixConfigProvider() error { // return initHystrixConfig() //} +// CommandConfigWithError ... type CommandConfigWithError struct { Timeout int `yaml:"timeout"` MaxConcurrentRequests int `yaml:"max_concurrent_requests"` @@ -258,11 +274,14 @@ type CommandConfigWithError struct { //- ErrorPercentThreshold: it causes circuits to open once the rolling measure of errors exceeds this percent of requests //See hystrix doc +// HystrixFilterConfig ... type HystrixFilterConfig struct { Configs map[string]*CommandConfigWithError Default string Services map[string]ServiceHystrixConfig } + +// ServiceHystrixConfig ... type ServiceHystrixConfig struct { ServiceConfig string `yaml:"service_config"` Methods map[string]string diff --git a/filter/impl/hystrix_filter_test.go b/filter/filter_impl/hystrix_filter_test.go similarity index 90% rename from filter/impl/hystrix_filter_test.go rename to filter/filter_impl/hystrix_filter_test.go index d3a5183ede25d8a325bb1c73020edddd2ffbc638..66c17d920c14e23f1562773c152e99955a48bfb9 100644 --- a/filter/impl/hystrix_filter_test.go +++ b/filter/filter_impl/hystrix_filter_test.go @@ -14,17 +14,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package impl +package filter_impl import ( + "context" "regexp" "testing" ) + import ( "github.com/afex/hystrix-go/hystrix" "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" @@ -125,9 +128,9 @@ type testMockSuccessInvoker struct { protocol.BaseInvoker } -func (iv *testMockSuccessInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (iv *testMockSuccessInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result { return &protocol.RPCResult{ - Rest: "Sucess", + Rest: "Success", Err: nil, } } @@ -136,7 +139,7 @@ type testMockFailInvoker struct { protocol.BaseInvoker } -func (iv *testMockFailInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +func (iv *testMockFailInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result { return &protocol.RPCResult{ Err: errors.Errorf("exception"), } @@ -144,7 +147,7 @@ func (iv *testMockFailInvoker) Invoke(invocation protocol.Invocation) protocol.R func TestHystrixFilter_Invoke_Success(t *testing.T) { hf := &HystrixFilter{} - result := hf.Invoke(&testMockSuccessInvoker{}, &invocation.RPCInvocation{}) + result := hf.Invoke(context.Background(), &testMockSuccessInvoker{}, &invocation.RPCInvocation{}) assert.NotNil(t, result) assert.NoError(t, result.Error()) assert.NotNil(t, result.Result()) @@ -152,7 +155,7 @@ func TestHystrixFilter_Invoke_Success(t *testing.T) { func TestHystrixFilter_Invoke_Fail(t *testing.T) { hf := &HystrixFilter{} - result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{}) + result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) assert.NotNil(t, result) assert.Error(t, result.Error()) } @@ -164,7 +167,7 @@ func TestHystricFilter_Invoke_CircuitBreak(t *testing.T) { resChan := make(chan protocol.Result, 50) for i := 0; i < 50; i++ { go func() { - result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{}) + result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) resChan <- result }() } @@ -189,7 +192,7 @@ func TestHystricFilter_Invoke_CircuitBreak_Omit_Exception(t *testing.T) { resChan := make(chan protocol.Result, 50) for i := 0; i < 50; i++ { go func() { - result := hf.Invoke(&testMockFailInvoker{}, &invocation.RPCInvocation{}) + result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) resChan <- result }() } diff --git a/filter/filter_impl/metrics_filter.go b/filter/filter_impl/metrics_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..f4734172b74c8bbcdac5c9a9743acb4df5fcb6b5 --- /dev/null +++ b/filter/filter_impl/metrics_filter.go @@ -0,0 +1,93 @@ +/* + * 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 filter_impl + +import ( + "context" + "time" +) + +import ( + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/filter" + "github.com/apache/dubbo-go/metrics" + "github.com/apache/dubbo-go/protocol" +) + +const ( + metricFilterName = "metrics" +) + +var ( + metricFilterInstance filter.Filter +) + +// must initialized before using the filter and after loading configuration +func init() { + extension.SetFilter(metricFilterName, newMetricsFilter) +} + +// metricFilter will calculate the invocation's duration and the report to the reporters +// If you want to use this filter to collect the metrics, +// Adding this into your configuration file, like: +// filter: "metrics" +// metrics: +// reporter: +// - "your reporter" # here you should specify the reporter, for example 'prometheus' +// more info please take a look at dubbo-samples projects +type metricsFilter struct { + reporters []metrics.Reporter +} + +// Invoke collect the duration of invocation and then report the duration by using goroutine +func (p *metricsFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + start := time.Now() + res := invoker.Invoke(ctx, invocation) + end := time.Now() + duration := end.Sub(start) + go func() { + for _, reporter := range p.reporters { + reporter.Report(ctx, invoker, invocation, duration, res) + } + }() + return res +} + +// OnResponse do nothing and return the result +func (p *metricsFilter) OnResponse(ctx context.Context, res protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + return res +} + +// newMetricsFilter the metricsFilter is singleton. +// it's lazy initialization +// make sure that the configuration had been loaded before invoking this method. +func newMetricsFilter() filter.Filter { + if metricFilterInstance == nil { + reporterNames := config.GetMetricConfig().Reporters + reporters := make([]metrics.Reporter, 0, len(reporterNames)) + for _, name := range reporterNames { + reporters = append(reporters, extension.GetMetricReporter(name)) + } + metricFilterInstance = &metricsFilter{ + reporters: reporters, + } + } + + return metricFilterInstance +} diff --git a/filter/filter_impl/metrics_filter_test.go b/filter/filter_impl/metrics_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..709404a2af4f4df0dbf625dbbbd673e34975c0db --- /dev/null +++ b/filter/filter_impl/metrics_filter_test.go @@ -0,0 +1,84 @@ +/* + * 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 filter_impl + +import ( + "context" + "sync" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +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/metrics" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestMetricsFilter_Invoke(t *testing.T) { + + // prepare the mock reporter + config.GetMetricConfig().Reporters = []string{"mock"} + mk := &mockReporter{} + extension.SetMetricReporter("mock", func() metrics.Reporter { + return mk + }) + + instance := extension.GetFilter(metricFilterName) + + url, _ := common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&" + + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker := protocol.NewBaseInvoker(url) + + attach := make(map[string]string, 10) + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + + ctx := context.Background() + + mk.On("Report", ctx, invoker, inv).Return(true, nil) + + mk.wg.Add(1) + result := instance.Invoke(ctx, invoker, inv) + assert.NotNil(t, result) + mk.AssertNotCalled(t, "Report", 1) + // it will do nothing + result = instance.OnResponse(ctx, nil, invoker, inv) + assert.Nil(t, result) +} + +type mockReporter struct { + mock.Mock + wg sync.WaitGroup +} + +func (m *mockReporter) Report(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, cost time.Duration, res protocol.Result) { + m.Called(ctx, invoker, invocation) + m.wg.Done() +} diff --git a/filter/impl/token_filter.go b/filter/filter_impl/token_filter.go similarity index 76% rename from filter/impl/token_filter.go rename to filter/filter_impl/token_filter.go index d10dff5b761d0fbe40ff3a14a93ee8962d000e02..4605416c40a616361868c313881ae784257e6742 100644 --- a/filter/impl/token_filter.go +++ b/filter/filter_impl/token_filter.go @@ -15,9 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package impl +package filter_impl import ( + "context" "strings" ) @@ -33,6 +34,7 @@ import ( ) const ( + // TOKEN ... TOKEN = "token" ) @@ -40,27 +42,31 @@ func init() { extension.SetFilter(TOKEN, GetTokenFilter) } +// TokenFilter ... type TokenFilter struct{} -func (tf *TokenFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +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 { attachs := invocation.Attachments() remoteTkn, exist := attachs[constant.TOKEN_KEY] if exist && strings.EqualFold(invokerTkn, remoteTkn) { - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ", invoker, invocation.MethodName())} } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (tf *TokenFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (tf *TokenFilter) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } +// GetTokenFilter ... func GetTokenFilter() filter.Filter { return &TokenFilter{} } diff --git a/filter/impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go similarity index 77% rename from filter/impl/token_filter_test.go rename to filter/filter_impl/token_filter_test.go index 1473f274037699260725ff9ebb1b3d1377efb326..672082c729bc371a40573a66d13bc57a7024186b 100644 --- a/filter/impl/token_filter_test.go +++ b/filter/filter_impl/token_filter_test.go @@ -15,9 +15,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -package impl +package filter_impl import ( + "context" "net/url" "testing" ) @@ -41,8 +42,10 @@ func TestTokenFilter_Invoke(t *testing.T) { common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "ori_key" - result := filter.Invoke(protocol.NewBaseInvoker(*url), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), + protocol.NewBaseInvoker(*url), + invocation.NewRPCInvocation("MethodName", + []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -53,8 +56,7 @@ func TestTokenFilter_InvokeEmptyToken(t *testing.T) { url := common.URL{} attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "ori_key" - result := filter.Invoke(protocol.NewBaseInvoker(url), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -66,8 +68,7 @@ func TestTokenFilter_InvokeEmptyAttach(t *testing.T) { common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) - result := filter.Invoke(protocol.NewBaseInvoker(*url), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } @@ -79,7 +80,7 @@ func TestTokenFilter_InvokeNotEqual(t *testing.T) { common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) attch := make(map[string]string, 0) attch[constant.TOKEN_KEY] = "err_key" - result := filter.Invoke(protocol.NewBaseInvoker(*url), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), + protocol.NewBaseInvoker(*url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } diff --git a/filter/impl/tps/impl/tps_limit_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go similarity index 94% rename from filter/impl/tps/impl/tps_limit_fix_window_strategy.go rename to filter/filter_impl/tps/tps_limit_fix_window_strategy.go index 285ecfa658cf838cc1140ba716bd72e1976b86fe..a9c2ac15a417ffa6ff8f5b8d78d5c6a94877db30 100644 --- a/filter/impl/tps/impl/tps_limit_fix_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "sync/atomic" @@ -25,10 +25,11 @@ import ( import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/filter/impl/tps" + "github.com/apache/dubbo-go/filter" ) const ( + // FixedWindowKey ... FixedWindowKey = "fixedWindow" ) @@ -39,6 +40,7 @@ func init() { } /** + * 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 @@ -63,6 +65,7 @@ type FixedWindowTpsLimitStrategyImpl struct { timestamp int64 } +// IsAllowable ... func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool { current := time.Now().UnixNano() @@ -79,7 +82,7 @@ func (impl *FixedWindowTpsLimitStrategyImpl) IsAllowable() bool { type fixedWindowStrategyCreator struct{} -func (creator *fixedWindowStrategyCreator) Create(rate int, interval int) tps.TpsLimitStrategy { +func (creator *fixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { return &FixedWindowTpsLimitStrategyImpl{ rate: int32(rate), interval: int64(interval) * int64(time.Millisecond), // convert to ns diff --git a/filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go similarity index 99% rename from filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go rename to filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go index 7ef539ed3b2b93da5c56a05f606e75282226d1ef..5eaf2f707dcc9dd6cf325988242623dd5161c1a8 100644 --- a/filter/impl/tps/impl/tps_limit_fix_window_strategy_test.go +++ b/filter/filter_impl/tps/tps_limit_fix_window_strategy_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "testing" diff --git a/filter/impl/tps/impl/tps_limit_sliding_window_strategy.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go similarity index 94% rename from filter/impl/tps/impl/tps_limit_sliding_window_strategy.go rename to filter/filter_impl/tps/tps_limit_sliding_window_strategy.go index d1a5db6e259ffa63282065f881f6cc8360c8d25b..a781cc7bfbf297d0b9cf84ca0aa9dcfbbef7e14b 100644 --- a/filter/impl/tps/impl/tps_limit_sliding_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "container/list" @@ -25,7 +25,7 @@ import ( import ( "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/filter/impl/tps" + "github.com/apache/dubbo-go/filter" ) func init() { @@ -33,6 +33,7 @@ func init() { } /** + * SlidingWindowTpsLimitStrategyImpl * it's thread-safe. * "UserProvider": * registry: "hangzhouzk" @@ -53,6 +54,7 @@ type SlidingWindowTpsLimitStrategyImpl struct { queue *list.List } +// IsAllowable ... func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool { impl.mutex.Lock() defer impl.mutex.Unlock() @@ -82,7 +84,7 @@ func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool { type slidingWindowStrategyCreator struct{} -func (creator *slidingWindowStrategyCreator) Create(rate int, interval int) tps.TpsLimitStrategy { +func (creator *slidingWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { return &SlidingWindowTpsLimitStrategyImpl{ rate: rate, interval: int64(interval) * int64(time.Millisecond), diff --git a/filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go similarity index 99% rename from filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go rename to filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go index 075f1d9d2be2d18edfee7dc8691b71da65f5da45..57342d1c443993c49c6124f0ef28dae5ebb203e8 100644 --- a/filter/impl/tps/impl/tps_limit_sliding_window_strategy_test.go +++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "testing" diff --git a/filter/impl/tps/impl/tps_limit_strategy_mock.go b/filter/filter_impl/tps/tps_limit_strategy_mock.go similarity index 99% rename from filter/impl/tps/impl/tps_limit_strategy_mock.go rename to filter/filter_impl/tps/tps_limit_strategy_mock.go index a653fb287a2d89d8c6151889ca14b4b7b4832505..72c658fb9a5d48b6080900a4645d318dfd2b0c21 100644 --- a/filter/impl/tps/impl/tps_limit_strategy_mock.go +++ b/filter/filter_impl/tps/tps_limit_strategy_mock.go @@ -18,7 +18,7 @@ // Source: tps_limit_strategy.go // Package filter is a generated GoMock package. -package impl +package tps import ( gomock "github.com/golang/mock/gomock" diff --git a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go similarity index 93% rename from filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go rename to filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go index 9a1b21a3349845e32cb0fe38b07a7f932ec4f454..16624836e6397df5adda3f2aa5a80966721a97fb 100644 --- a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "sync" @@ -23,7 +23,7 @@ import ( import ( "github.com/apache/dubbo-go/common/extension" - "github.com/apache/dubbo-go/filter/impl/tps" + "github.com/apache/dubbo-go/filter" ) func init() { @@ -33,6 +33,7 @@ func init() { } /** + * ThreadSafeFixedWindowTpsLimitStrategyImpl * it's the thread-safe implementation. * Also, it's a thread-safe decorator of FixedWindowTpsLimitStrategyImpl * "UserProvider": @@ -52,6 +53,7 @@ type ThreadSafeFixedWindowTpsLimitStrategyImpl struct { fixedWindow *FixedWindowTpsLimitStrategyImpl } +// IsAllowable ... func (impl *ThreadSafeFixedWindowTpsLimitStrategyImpl) IsAllowable() bool { impl.mutex.Lock() defer impl.mutex.Unlock() @@ -62,7 +64,7 @@ type threadSafeFixedWindowStrategyCreator struct { fixedWindowStrategyCreator *fixedWindowStrategyCreator } -func (creator *threadSafeFixedWindowStrategyCreator) Create(rate int, interval int) tps.TpsLimitStrategy { +func (creator *threadSafeFixedWindowStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { fixedWindowStrategy := creator.fixedWindowStrategyCreator.Create(rate, interval).(*FixedWindowTpsLimitStrategyImpl) return &ThreadSafeFixedWindowTpsLimitStrategyImpl{ fixedWindow: fixedWindowStrategy, diff --git a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go similarity index 99% rename from filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go rename to filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go index 129493962403e0028b09f9646054fda236c99ff7..90cd15201cd71aafcc50a1dfb801ece7a5dee26a 100644 --- a/filter/impl/tps/impl/tps_limit_thread_safe_fix_window_strategy_test.go +++ b/filter/filter_impl/tps/tps_limit_thread_safe_fix_window_strategy_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package tps import ( "testing" diff --git a/filter/impl/tps/impl/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go similarity index 94% rename from filter/impl/tps/impl/tps_limiter_method_service.go rename to filter/filter_impl/tps/tps_limiter_method_service.go index 426ae5994867c5a09653641870ebcef531c0d43c..7fe8de9237b82415a09083c2be59df5e232ecaf0 100644 --- a/filter/impl/tps/impl/tps_limiter_method_service.go +++ b/filter/filter_impl/tps/tps_limiter_method_service.go @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package impl + +package tps import ( "fmt" @@ -30,11 +31,13 @@ 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/filter/impl/tps" + "github.com/apache/dubbo-go/filter" "github.com/apache/dubbo-go/protocol" ) -const name = "method-service" +const ( + name = "method-service" +) func init() { extension.SetTpsLimiter(constant.DEFAULT_KEY, GetMethodServiceTpsLimiter) @@ -42,6 +45,7 @@ func init() { } /** + * MethodServiceTpsLimiterImpl * This implementation allows developer to config both method-level and service-level tps limiter. * for example: * "UserProvider": @@ -111,6 +115,7 @@ type MethodServiceTpsLimiterImpl struct { tpsState *concurrent.Map } +// IsAllowable ... func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool { methodConfigPrefix := "methods." + invocation.MethodName() + "." @@ -127,7 +132,7 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio limitState, found := limiter.tpsState.Load(limitTarget) if found { - return limitState.(tps.TpsLimitStrategy).IsAllowable() + return limitState.(filter.TpsLimitStrategy).IsAllowable() } limitRate := getLimitConfig(methodLimitRateConfig, url, invocation, @@ -149,7 +154,7 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio url.GetParam(constant.TPS_LIMIT_STRATEGY_KEY, constant.DEFAULT_KEY)) limitStateCreator := extension.GetTpsLimitStrategyCreator(limitStrategyConfig) limitState, _ = limiter.tpsState.LoadOrStore(limitTarget, limitStateCreator.Create(int(limitRate), int(limitInterval))) - return limitState.(tps.TpsLimitStrategy).IsAllowable() + return limitState.(filter.TpsLimitStrategy).IsAllowable() } func getLimitConfig(methodLevelConfig string, @@ -178,7 +183,8 @@ func getLimitConfig(methodLevelConfig string, var methodServiceTpsLimiterInstance *MethodServiceTpsLimiterImpl var methodServiceTpsLimiterOnce sync.Once -func GetMethodServiceTpsLimiter() tps.TpsLimiter { +// GetMethodServiceTpsLimiter ... +func GetMethodServiceTpsLimiter() filter.TpsLimiter { methodServiceTpsLimiterOnce.Do(func() { methodServiceTpsLimiterInstance = &MethodServiceTpsLimiterImpl{ tpsState: concurrent.NewMap(), diff --git a/filter/impl/tps/impl/tps_limiter_method_service_test.go b/filter/filter_impl/tps/tps_limiter_method_service_test.go similarity index 97% rename from filter/impl/tps/impl/tps_limiter_method_service_test.go rename to filter/filter_impl/tps/tps_limiter_method_service_test.go index e747d4682d0a8bdee03da6f012fb76b7bd1e02af..441224a3e35147b85c3553871dcaa1fefd09db04 100644 --- a/filter/impl/tps/impl/tps_limiter_method_service_test.go +++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go @@ -15,13 +15,14 @@ * limitations under the License. */ -package impl +package tps import ( "net/url" "testing" ) import ( + "github.com/apache/dubbo-go/filter" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -30,7 +31,6 @@ 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/filter/impl/tps" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -144,10 +144,10 @@ type mockStrategyCreator struct { rate int interval int t *testing.T - strategy tps.TpsLimitStrategy + strategy filter.TpsLimitStrategy } -func (creator *mockStrategyCreator) Create(rate int, interval int) tps.TpsLimitStrategy { +func (creator *mockStrategyCreator) Create(rate int, interval int) filter.TpsLimitStrategy { assert.Equal(creator.t, creator.rate, rate) assert.Equal(creator.t, creator.interval, interval) return creator.strategy diff --git a/filter/impl/tps/impl/tps_limiter_mock.go b/filter/filter_impl/tps/tps_limiter_mock.go similarity index 99% rename from filter/impl/tps/impl/tps_limiter_mock.go rename to filter/filter_impl/tps/tps_limiter_mock.go index acd3a15d18baf10838faf57e141afe1711f0aebb..463b0988acbeb17a967c9803337a61c4914bce42 100644 --- a/filter/impl/tps/impl/tps_limiter_mock.go +++ b/filter/filter_impl/tps/tps_limiter_mock.go @@ -18,7 +18,7 @@ // Source: tps_limiter.go // Package filter is a generated GoMock package. -package impl +package tps import ( reflect "reflect" diff --git a/filter/impl/tps_limit_filter.go b/filter/filter_impl/tps_limit_filter.go similarity index 80% rename from filter/impl/tps_limit_filter.go rename to filter/filter_impl/tps_limit_filter.go index 3cb7381c8616abd61fe2ac306b59694a92715dda..fa78288f9678d67d0eb0d025a83b75493f7fda80 100644 --- a/filter/impl/tps_limit_filter.go +++ b/filter/filter_impl/tps_limit_filter.go @@ -15,19 +15,23 @@ * limitations under the License. */ -package impl +package filter_impl +import ( + "context" +) 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/filter" - _ "github.com/apache/dubbo-go/filter/common/impl" - _ "github.com/apache/dubbo-go/filter/impl/tps/impl" + _ "github.com/apache/dubbo-go/filter/filter_impl/tps" + _ "github.com/apache/dubbo-go/filter/handler" "github.com/apache/dubbo-go/protocol" ) const ( + // TpsLimitFilterKey key TpsLimitFilterKey = "tps" ) @@ -36,6 +40,7 @@ func init() { } /** + * TpsLimitFilter * if you wish to use the TpsLimiter, please add the configuration into your service provider configuration: * for example: * "UserProvider": @@ -51,25 +56,29 @@ func init() { type TpsLimitFilter struct { } -func (t TpsLimitFilter) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// Invoke ... +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, "") rejectedExeHandler := url.GetParam(constant.TPS_REJECTED_EXECUTION_HANDLER_KEY, constant.DEFAULT_KEY) if len(tpsLimiter) > 0 { allow := extension.GetTpsLimiter(tpsLimiter).IsAllowable(invoker.GetUrl(), invocation) if allow { - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } logger.Errorf("The invocation was rejected due to over the tps limitation, url: %s ", url.String()) return extension.GetRejectedExecutionHandler(rejectedExeHandler).RejectedExecution(url, invocation) } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (t TpsLimitFilter) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +// OnResponse ... +func (t TpsLimitFilter) OnResponse(_ context.Context, result protocol.Result, _ protocol.Invoker, + _ protocol.Invocation) protocol.Result { return result } +// GetTpsLimitFilter ... func GetTpsLimitFilter() filter.Filter { return &TpsLimitFilter{} } diff --git a/filter/impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go similarity index 73% rename from filter/impl/tps_limit_filter_test.go rename to filter/filter_impl/tps_limit_filter_test.go index debdbd00dec97ed67d789bfc45103993c014ab4a..cc423ae1e5f3589dd60b0c8655f1123c290f0ffc 100644 --- a/filter/impl/tps_limit_filter_test.go +++ b/filter/filter_impl/tps_limit_filter_test.go @@ -15,14 +15,18 @@ * limitations under the License. */ -package impl +package filter_impl import ( + "context" "net/url" "testing" ) import ( + "github.com/apache/dubbo-go/filter" + "github.com/apache/dubbo-go/filter/filter_impl/tps" + common2 "github.com/apache/dubbo-go/filter/handler" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -31,10 +35,6 @@ import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" - filterCommon "github.com/apache/dubbo-go/filter/common" - filterCommonImpl "github.com/apache/dubbo-go/filter/common/impl" - "github.com/apache/dubbo-go/filter/impl/tps" - "github.com/apache/dubbo-go/filter/impl/tps/impl" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -46,8 +46,10 @@ func TestTpsLimitFilter_Invoke_With_No_TpsLimiter(t *testing.T) { common.WithParamsValue(constant.TPS_LIMITER_KEY, "")) attch := make(map[string]string, 0) - result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := tpsFilter.Invoke(context.Background(), + protocol.NewBaseInvoker(*invokeUrl), + invocation.NewRPCInvocation("MethodName", + []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) @@ -56,9 +58,9 @@ func TestTpsLimitFilter_Invoke_With_No_TpsLimiter(t *testing.T) { func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockLimiter := impl.NewMockTpsLimiter(ctrl) + mockLimiter := tps.NewMockTpsLimiter(ctrl) mockLimiter.EXPECT().IsAllowable(gomock.Any(), gomock.Any()).Return(true).Times(1) - extension.SetTpsLimiter(constant.DEFAULT_KEY, func() tps.TpsLimiter { + extension.SetTpsLimiter(constant.DEFAULT_KEY, func() filter.TpsLimiter { return mockLimiter }) @@ -68,8 +70,10 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) { common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY)) attch := make(map[string]string, 0) - result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := tpsFilter.Invoke(context.Background(), + protocol.NewBaseInvoker(*invokeUrl), + invocation.NewRPCInvocation("MethodName", + []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -77,17 +81,17 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter(t *testing.T) { func TestGenericFilter_Invoke_With_Default_TpsLimiter_Not_Allow(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockLimiter := impl.NewMockTpsLimiter(ctrl) + mockLimiter := tps.NewMockTpsLimiter(ctrl) mockLimiter.EXPECT().IsAllowable(gomock.Any(), gomock.Any()).Return(false).Times(1) - extension.SetTpsLimiter(constant.DEFAULT_KEY, func() tps.TpsLimiter { + extension.SetTpsLimiter(constant.DEFAULT_KEY, func() filter.TpsLimiter { return mockLimiter }) mockResult := &protocol.RPCResult{} - mockRejectedHandler := filterCommonImpl.NewMockRejectedExecutionHandler(ctrl) + mockRejectedHandler := common2.NewMockRejectedExecutionHandler(ctrl) mockRejectedHandler.EXPECT().RejectedExecution(gomock.Any(), gomock.Any()).Return(mockResult).Times(1) - extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, func() filterCommon.RejectedExecutionHandler { + extension.SetRejectedExecutionHandler(constant.DEFAULT_KEY, func() filter.RejectedExecutionHandler { return mockRejectedHandler }) @@ -97,8 +101,8 @@ func TestGenericFilter_Invoke_With_Default_TpsLimiter_Not_Allow(t *testing.T) { common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY)) attch := make(map[string]string, 0) - result := tpsFilter.Invoke(protocol.NewBaseInvoker(*invokeUrl), - invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := tpsFilter.Invoke(context.Background(), + protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } diff --git a/filter/filter_impl/tracing_filter.go b/filter/filter_impl/tracing_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..b8058aa601af98b5416da882321546675459c413 --- /dev/null +++ b/filter/filter_impl/tracing_filter.go @@ -0,0 +1,107 @@ +/* + * 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 filter_impl + +import ( + "context" +) + +import ( + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/log" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/filter" + "github.com/apache/dubbo-go/protocol" +) + +const ( + tracingFilterName = "tracing" +) + +// this should be executed before users set their own Tracer +func init() { + extension.SetFilter(tracingFilterName, newTracingFilter) + opentracing.SetGlobalTracer(opentracing.NoopTracer{}) +} + +var ( + errorKey = "ErrorMsg" + successKey = "Success" +) + +// if you wish to using opentracing, please add the this filter into your filter attribute in your configure file. +// notice that this could be used in both client-side and server-side. +type tracingFilter struct { +} + +func (tf *tracingFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + var ( + spanCtx context.Context + span opentracing.Span + ) + operationName := invoker.GetUrl().ServiceKey() + "#" + invocation.MethodName() + + wiredCtx := ctx.Value(constant.TRACING_REMOTE_SPAN_CTX) + preSpan := opentracing.SpanFromContext(ctx) + + if preSpan != nil { + // it means that someone already create a span to trace, so we use the span to be the parent span + span = opentracing.StartSpan(operationName, opentracing.ChildOf(preSpan.Context())) + spanCtx = opentracing.ContextWithSpan(ctx, span) + + } else if wiredCtx != nil { + + // it means that there has a remote span, usually from client side. so we use this as the parent + span = opentracing.StartSpan(operationName, opentracing.ChildOf(wiredCtx.(opentracing.SpanContext))) + spanCtx = opentracing.ContextWithSpan(ctx, span) + } else { + // it means that there is not any span, so we create a span as the root span. + span, spanCtx = opentracing.StartSpanFromContext(ctx, operationName) + } + + defer func() { + span.Finish() + }() + + result := invoker.Invoke(spanCtx, invocation) + span.SetTag(successKey, result.Error() != nil) + if result.Error() != nil { + span.LogFields(log.String(errorKey, result.Error().Error())) + } + return result +} + +func (tf *tracingFilter) OnResponse(ctx context.Context, result protocol.Result, + invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + return result +} + +var ( + tracingFilterInstance *tracingFilter +) + +func newTracingFilter() filter.Filter { + if tracingFilterInstance == nil { + tracingFilterInstance = &tracingFilter{} + } + return tracingFilterInstance +} diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a51692dddcc3400032650f4953eb1e28fb047709 --- /dev/null +++ b/filter/filter_impl/tracing_filter_test.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 filter_impl + +import ( + "context" + "testing" +) + +import ( + "github.com/opentracing/opentracing-go" +) + +import ( + "github.com/apache/dubbo-go/common/constant" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestTracingFilter_Invoke(t *testing.T) { + url, _ := common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&" + + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker := protocol.NewBaseInvoker(url) + + attach := make(map[string]string, 10) + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + ctx := context.Background() + tf := newTracingFilter() + + // do not has any span + tf.Invoke(ctx, invoker, inv) + + span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Operation") + defer span.Finish() + + // has previous span + tf.Invoke(ctx, invoker, inv) + + // has remote ctx + ctx = context.WithValue(context.Background(), constant.TRACING_REMOTE_SPAN_CTX, span.Context()) + tf.Invoke(ctx, invoker, inv) +} diff --git a/filter/common/impl/rejected_execution_handler_mock.go b/filter/handler/rejected_execution_handler_mock.go similarity index 99% rename from filter/common/impl/rejected_execution_handler_mock.go rename to filter/handler/rejected_execution_handler_mock.go index dace1894668d3a4a154a87bfbdbcc860a97a11ec..a5bef63b3729a7b04d911c9844320aa778ac357a 100644 --- a/filter/common/impl/rejected_execution_handler_mock.go +++ b/filter/handler/rejected_execution_handler_mock.go @@ -18,7 +18,7 @@ // Source: rejected_execution_handler.go // Package filter is a generated GoMock package. -package impl +package handler import ( reflect "reflect" diff --git a/filter/common/impl/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go similarity index 85% rename from filter/common/impl/rejected_execution_handler_only_log.go rename to filter/handler/rejected_execution_handler_only_log.go index 8943433af1ba908fc834740163df78e3b2b6443a..0f9003c7df2165a2f3a364a5afc47f578db1d243 100644 --- a/filter/common/impl/rejected_execution_handler_only_log.go +++ b/filter/handler/rejected_execution_handler_only_log.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package impl +package handler import ( + "github.com/apache/dubbo-go/filter" "sync" ) @@ -26,11 +27,13 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" - filterCommon "github.com/apache/dubbo-go/filter/common" "github.com/apache/dubbo-go/protocol" ) -const HandlerName = "log" +const ( + // HandlerName handler name + HandlerName = "log" +) func init() { extension.SetRejectedExecutionHandler(HandlerName, GetOnlyLogRejectedExecutionHandler) @@ -41,6 +44,7 @@ var onlyLogHandlerInstance *OnlyLogRejectedExecutionHandler var onlyLogHandlerOnce sync.Once /** + * OnlyLogRejectedExecutionHandler * This implementation only logs the invocation info. * it always return en error inside the result. * "UserProvider": @@ -56,12 +60,16 @@ var onlyLogHandlerOnce sync.Once type OnlyLogRejectedExecutionHandler struct { } -func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result { +// RejectedExecution ... +func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, + _ protocol.Invocation) protocol.Result { + logger.Errorf("The invocation was rejected. url: %s", url.String()) return &protocol.RPCResult{} } -func GetOnlyLogRejectedExecutionHandler() filterCommon.RejectedExecutionHandler { +// GetOnlyLogRejectedExecutionHandler ... +func GetOnlyLogRejectedExecutionHandler() filter.RejectedExecutionHandler { onlyLogHandlerOnce.Do(func() { onlyLogHandlerInstance = &OnlyLogRejectedExecutionHandler{} }) diff --git a/filter/common/impl/rejected_execution_handler_only_log_test.go b/filter/handler/rejected_execution_handler_only_log_test.go similarity index 98% rename from filter/common/impl/rejected_execution_handler_only_log_test.go rename to filter/handler/rejected_execution_handler_only_log_test.go index da54d8a106338dd4f21f9b01e66b031e3c311e01..409f09f61bd958992749231fca045b54601fc627 100644 --- a/filter/common/impl/rejected_execution_handler_only_log_test.go +++ b/filter/handler/rejected_execution_handler_only_log_test.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package impl +package handler import ( "net/url" diff --git a/filter/common/rejected_execution_handler.go b/filter/rejected_execution_handler.go similarity index 97% rename from filter/common/rejected_execution_handler.go rename to filter/rejected_execution_handler.go index b993b8444c14c13ce9a8861c113dc02ca5fd335a..caeea1db6631d0968fd58f59f9577ee9272f3ca0 100644 --- a/filter/common/rejected_execution_handler.go +++ b/filter/rejected_execution_handler.go @@ -14,7 +14,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package common + +package filter import ( "github.com/apache/dubbo-go/common" @@ -22,6 +23,7 @@ import ( ) /** + * RejectedExecutionHandler * If the invocation cannot pass any validation in filter, like ExecuteLimitFilter and TpsLimitFilter, * the implementation will be used. * The common case is that sometimes you want to return the default value when the request was rejected. diff --git a/filter/impl/tps/tps_limit_strategy.go b/filter/tps_limit_strategy.go similarity index 95% rename from filter/impl/tps/tps_limit_strategy.go rename to filter/tps_limit_strategy.go index c55f008a09b3743f728ab0506c6b0095cbfd181c..5edf32ce1912642c7ad0ea0b3f6144b45c267eb4 100644 --- a/filter/impl/tps/tps_limit_strategy.go +++ b/filter/tps_limit_strategy.go @@ -15,9 +15,10 @@ * limitations under the License. */ -package tps +package filter /* + * TpsLimitStrategy * please register your implementation by invoking SetTpsLimitStrategy * "UserProvider": * registry: "hangzhouzk" @@ -35,6 +36,7 @@ type TpsLimitStrategy interface { IsAllowable() bool } +// TpsLimitStrategyCreator ... type TpsLimitStrategyCreator interface { Create(rate int, interval int) TpsLimitStrategy } diff --git a/filter/impl/tps/tps_limiter.go b/filter/tps_limiter.go similarity index 97% rename from filter/impl/tps/tps_limiter.go rename to filter/tps_limiter.go index 0622a957a8ba14fdebb52ff44ecf72da17703163..dbc9f76838a4406b4788e7757453098613253d58 100644 --- a/filter/impl/tps/tps_limiter.go +++ b/filter/tps_limiter.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package tps +package filter import ( "github.com/apache/dubbo-go/common" @@ -23,6 +23,7 @@ import ( ) /* + * TpsLimiter * please register your implementation by invoking SetTpsLimiter * The usage, for example: * "UserProvider": diff --git a/go.mod b/go.mod index c2a61f2db1484338bba7dd1bf00a9ff9de2125df..54b39d322e83e05c4c54ffdf166dd4f0aa039249 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ require ( github.com/Workiva/go-datastructures v1.0.50 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e // indirect - github.com/apache/dubbo-go-hessian2 v1.2.5-0.20191029001541-894e45c9aaaa + github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200111150223-4ce8c8d0d7ac github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 // indirect github.com/coreos/bbolt v1.3.3 // indirect github.com/coreos/etcd v3.3.13+incompatible @@ -12,12 +12,14 @@ require ( github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/creasty/defaults v1.3.0 - github.com/dubbogo/getty v1.3.1 - github.com/dubbogo/gost v1.3.0 + github.com/dubbogo/getty v1.3.2 + github.com/dubbogo/go-zookeeper v1.0.0 + github.com/dubbogo/gost v1.5.2 github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect github.com/golang/mock v1.3.1 + github.com/golang/protobuf v1.3.2 github.com/google/btree v1.0.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect @@ -31,11 +33,12 @@ require ( github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f // indirect github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 // 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.0.0-20190723125407-0242d42e3dbb + github.com/opentracing/opentracing-go v1.1.0 github.com/pkg/errors v0.8.1 - github.com/prometheus/client_golang v1.1.0 // indirect - github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec + github.com/prometheus/client_golang v1.1.0 github.com/satori/go.uuid v1.2.0 github.com/smartystreets/goconvey v0.0.0-20190710185942-9d28bd7c0945 // indirect github.com/soheilhy/cmux v0.1.4 // indirect diff --git a/go.sum b/go.sum index 9855250a90f72eca314bf54cd9bea03a619b6a5e..0461f29653568944637306d65a6addbd79df664f 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e h1:MSuLXx/mveDbpDNhVrcWTMeV4lbYWKcyO4rH+jAxmX0= github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190802083043-4cd0c391755e/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= -github.com/apache/dubbo-go-hessian2 v1.2.5-0.20191029001541-894e45c9aaaa h1:11TO1wiM5bvGAVrmfN5atD8gZqUSPE1TBoIs8sI6Abk= -github.com/apache/dubbo-go-hessian2 v1.2.5-0.20191029001541-894e45c9aaaa/go.mod h1:LWnndnrFXZmJLAzoyNAPNHSIJ1KOHVkTSsHgC3YYWlo= +github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200111150223-4ce8c8d0d7ac h1:QKRMidg/RbdI5oaQWMb8Lxo63S+fLmsgMxsFoOCftKw= +github.com/apache/dubbo-go-hessian2 v1.3.1-0.20200111150223-4ce8c8d0d7ac/go.mod h1:VwEnsOMidkM1usya2uPfGpSLO9XUF//WQcWn3y+jFz8= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -102,12 +102,14 @@ github.com/docker/go-connections v0.3.0 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/dubbogo/getty v1.3.1 h1:9fehwTo/D6+z6/+kADMbhbKeMkP80o/3g+XwV5lFLTY= -github.com/dubbogo/getty v1.3.1/go.mod h1:dtLOEb1v6EMHsQNYRWEACiRLmTWB2kJGUAj1aXayPOg= -github.com/dubbogo/gost v1.1.1 h1:JCM7vx5edPIjDA5ovJTuzEEXuw2t7xLyrlgi2mi5jHI= -github.com/dubbogo/gost v1.1.1/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg= -github.com/dubbogo/gost v1.3.0 h1:n90mIUWCPD69BqW8wJ43NDy0RgNxx02aAG4QJcJ785U= -github.com/dubbogo/gost v1.3.0/go.mod h1:R7wZm1DrmrKGr50mBZVcg6C9ekG8aL5hP+sgWcIDwQg= +github.com/dubbogo/getty v1.3.2 h1:l1KVSs/1CtTKbIPTrkTtBT6S9ddvmswDGoAnnl2CDpM= +github.com/dubbogo/getty v1.3.2/go.mod h1:ANbVQ9tbpZ2b0xdR8nRrgS/oXIsZAeRxzvPSOn/7mbk= +github.com/dubbogo/go-zookeeper v1.0.0 h1:RsYdlGwhDW+iKXM3eIIcvt34P2swLdmQfuIJxsHlGoM= +github.com/dubbogo/go-zookeeper v1.0.0/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= +github.com/dubbogo/gost v1.5.1 h1:oG5dzaWf1KYynBaBoUIOkgT+YD0niHV6xxI0Odq7hDg= +github.com/dubbogo/gost v1.5.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/dubbogo/gost v1.5.2 h1:ri/03971hdpnn3QeCU+4UZgnRNGDXLDGDucR/iozZm8= +github.com/dubbogo/gost v1.5.2/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74 h1:2MIhn2R6oXQbgW5yHfS+d6YqyMfXiu2L55rFZC4UD/M= github.com/duosecurity/duo_api_golang v0.0.0-20190308151101-6c680f768e74/go.mod h1:UqXY1lYT/ERa4OEAywUqdok1T4RCRdArkhic1Opuavo= github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y= @@ -369,6 +371,8 @@ github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVo github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/ory/dockertest v3.3.4+incompatible h1:VrpM6Gqg7CrPm3bL4Wm1skO+zFWLbh7/Xb5kGEbJRh8= github.com/ory/dockertest v3.3.4+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE= @@ -410,8 +414,6 @@ 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/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY= -github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= @@ -449,6 +451,7 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= 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 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0= github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go new file mode 100644 index 0000000000000000000000000000000000000000..1636b14da2fe5ab714853aa662eaa774ddbc1791 --- /dev/null +++ b/metrics/prometheus/reporter.go @@ -0,0 +1,184 @@ +/* + * 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 prometheus + +import ( + "context" + "strconv" + "strings" + "sync" + "time" +) +import ( + "github.com/prometheus/client_golang/prometheus" +) + +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/metrics" + "github.com/apache/dubbo-go/protocol" +) + +const ( + reporterName = "prometheus" + serviceKey = constant.SERVICE_KEY + groupKey = constant.GROUP_KEY + versionKey = constant.VERSION_KEY + methodKey = constant.METHOD_KEY + timeoutKey = constant.TIMEOUT_KEY + + providerKey = "provider" + consumerKey = "consumer" + + // to identify the metric's type + histogramSuffix = "_histogram" + // to identify the metric's type + summarySuffix = "_summary" +) + +var ( + labelNames = []string{serviceKey, groupKey, versionKey, methodKey, timeoutKey} + namespace = config.GetApplicationConfig().Name + reporterInstance *PrometheusReporter + reporterInitOnce sync.Once +) + +// should initialize after loading configuration +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. +// https://prometheus.io/docs/guides/go-application/ +type PrometheusReporter struct { + + // report the consumer-side's summary data + consumerSummaryVec *prometheus.SummaryVec + // report the provider-side's summary data + providerSummaryVec *prometheus.SummaryVec + + // report the provider-side's histogram data + providerHistogramVec *prometheus.HistogramVec + // report the consumer-side's histogram data + consumerHistogramVec *prometheus.HistogramVec +} + +// Report report 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) { + url := invoker.GetUrl() + var sumVec *prometheus.SummaryVec + var hisVec *prometheus.HistogramVec + if isProvider(url) { + sumVec = reporter.providerSummaryVec + hisVec = reporter.providerHistogramVec + } else if isConsumer(url) { + sumVec = reporter.consumerSummaryVec + hisVec = reporter.consumerHistogramVec + } else { + logger.Warnf("The url is not the consumer's or provider's, "+ + "so the invocation will be ignored. url: %s", url.String()) + return + } + + labels := prometheus.Labels{ + serviceKey: url.Service(), + groupKey: url.GetParam(groupKey, ""), + versionKey: url.GetParam(versionKey, ""), + methodKey: invocation.MethodName(), + timeoutKey: url.GetParam(timeoutKey, ""), + } + + costMs := float64(cost.Nanoseconds() / constant.MsToNanoRate) + sumVec.With(labels).Observe(costMs) + hisVec.With(labels).Observe(costMs) +} + +func newHistogramVec(side string) *prometheus.HistogramVec { + mc := config.GetMetricConfig() + return prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: side, + Name: serviceKey + histogramSuffix, + Help: "This is the dubbo's histogram metrics", + Buckets: mc.GetHistogramBucket(), + }, + labelNames) +} + +// whether this url represents the application received the request as server +func isProvider(url common.URL) bool { + role := url.GetParam(constant.ROLE_KEY, "") + return strings.EqualFold(role, strconv.Itoa(common.PROVIDER)) +} + +// whether this url represents the application sent then request as client +func isConsumer(url common.URL) bool { + role := url.GetParam(constant.ROLE_KEY, "") + return strings.EqualFold(role, strconv.Itoa(common.CONSUMER)) +} + +// newSummaryVec create SummaryVec, the Namespace is dubbo +// the objectives is from my experience. +func newSummaryVec(side string) *prometheus.SummaryVec { + return prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: namespace, + Help: "This is the dubbo's summary metrics", + Subsystem: side, + Name: serviceKey + summarySuffix, + Objectives: map[float64]float64{ + 0.5: 0.01, + 0.75: 0.01, + 0.90: 0.005, + 0.98: 0.002, + 0.99: 0.001, + 0.999: 0.0001, + }, + }, + labelNames, + ) +} + +// newPrometheusReporter create new prometheusReporter +// it will register the metrics into prometheus +func newPrometheusReporter() metrics.Reporter { + if reporterInstance == nil { + reporterInitOnce.Do(func() { + reporterInstance = &PrometheusReporter{ + consumerSummaryVec: newSummaryVec(consumerKey), + providerSummaryVec: newSummaryVec(providerKey), + + consumerHistogramVec: newHistogramVec(consumerKey), + providerHistogramVec: newHistogramVec(providerKey), + } + prometheus.MustRegister(reporterInstance.consumerSummaryVec, reporterInstance.providerSummaryVec, + reporterInstance.consumerHistogramVec, reporterInstance.providerHistogramVec) + }) + } + return reporterInstance +} diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0cb7d09a2c8e71fb88b54789c8eb3ee2cf967fbf --- /dev/null +++ b/metrics/prometheus/reporter_test.go @@ -0,0 +1,72 @@ +/* + * 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 prometheus + +import ( + "context" + "testing" + "time" +) + +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/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestPrometheusReporter_Report(t *testing.T) { + reporter := extension.GetMetricReporter(reporterName) + url, _ := common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&" + + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker := protocol.NewBaseInvoker(url) + + attach := make(map[string]string, 10) + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + + assert.False(t, isConsumer(url)) + ctx := context.Background() + reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) + + // consumer side + url, _ = common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=0&retries=&" + + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker = protocol.NewBaseInvoker(url) + reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) + + // invalid role + url, _ = common.NewURL( + "dubbo://:20000/UserProvider?app.version=0.0.1&application=BDTService&bean.name=UserProvider" + + "&cluster=failover&environment=dev&group=&interface=com.ikurento.user.UserProvider&loadbalance=random&methods.GetUser." + + "loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=" + + "BDTService&organization=ikurento.com&owner=ZX®istry.role=9&retries=&" + + "service.filter=echo%2Ctoken%2Caccesslog×tamp=1569153406&token=934804bf-b007-4174-94eb-96e3e1d60cc7&version=&warmup=100") + invoker = protocol.NewBaseInvoker(url) + reporter.Report(ctx, invoker, inv, 100*time.Millisecond, nil) +} diff --git a/metrics/reporter.go b/metrics/reporter.go new file mode 100644 index 0000000000000000000000000000000000000000..85ef1dcdf0dad275edecc1f3a85502c1493c1395 --- /dev/null +++ b/metrics/reporter.go @@ -0,0 +1,37 @@ +/* + * 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 metrics + +import ( + "context" + "time" +) +import ( + "github.com/apache/dubbo-go/protocol" +) + +const ( + NameSpace = "dubbo" +) + +// it will be use 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, + cost time.Duration, res protocol.Result) +} diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go index 81f392565f701d990dc1783d5d467814a0fba5bf..3923b7e4e7e543f4c60a89aaebf67f6238916722 100644 --- a/protocol/dubbo/client.go +++ b/protocol/dubbo/client.go @@ -85,6 +85,7 @@ func init() { setClientGrpool() } +// SetClientConf ... func SetClientConf(c ClientConfig) { clientConf = &c err := clientConf.CheckValidity() @@ -95,6 +96,7 @@ func SetClientConf(c ClientConfig) { setClientGrpool() } +// GetClientConf ... func GetClientConf() ClientConfig { return *clientConf } @@ -106,6 +108,7 @@ func setClientGrpool() { } } +// Options ... type Options struct { // connect timeout ConnectTimeout time.Duration @@ -123,6 +126,7 @@ type AsyncCallbackResponse struct { Reply interface{} } +// Client ... type Client struct { opts Options conf ClientConfig @@ -132,6 +136,7 @@ type Client struct { pendingResponses *sync.Map } +// NewClient ... func NewClient(opt Options) *Client { switch { @@ -152,6 +157,7 @@ func NewClient(opt Options) *Client { return c } +// Request ... type Request struct { addr string svcUrl common.URL @@ -160,6 +166,7 @@ type Request struct { atta map[string]string } +// NewRequest ... func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request { return &Request{ addr: addr, @@ -170,11 +177,13 @@ func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, } } +// Response ... type Response struct { reply interface{} atta map[string]string } +// NewResponse ... func NewResponse(reply interface{}, atta map[string]string) *Response { return &Response{ reply: reply, @@ -182,13 +191,13 @@ func NewResponse(reply interface{}, atta map[string]string) *Response { } } -// call one way +// CallOneway call one way func (c *Client) CallOneway(request *Request) error { return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) } -// if @response is nil, the transport layer will get the response without notify the invoker. +// Call if @response is nil, the transport layer will get the response without notify the invoker. func (c *Client) Call(request *Request, response *Response) error { ct := CT_TwoWay @@ -199,6 +208,7 @@ func (c *Client) Call(request *Request, response *Response) error { return perrors.WithStack(c.call(ct, request, response, nil)) } +// AsyncCall ... func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error { return perrors.WithStack(c.call(CT_TwoWay, request, response, callback)) @@ -212,7 +222,15 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "") p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "") p.Service.Method = request.method + p.Service.Timeout = c.opts.RequestTimeout + var timeout = request.svcUrl.GetParam(strings.Join([]string{constant.METHOD_KEYS, request.method + constant.RETRIES_KEY}, "."), "") + if len(timeout) != 0 { + if t, err := time.ParseDuration(timeout); err == nil { + p.Service.Timeout = t + } + } + p.Header.SerialID = byte(S_Dubbo) p.Body = hessian.NewRequest(request.args, request.atta) @@ -238,7 +256,13 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac if session == nil { return errSessionNotExist } - defer c.pool.release(conn, err) + defer func() { + if err == nil { + c.pool.put(conn) + return + } + conn.close() + }() if err = c.transfer(session, p, rsp); err != nil { return perrors.WithStack(err) @@ -259,6 +283,7 @@ func (c *Client) call(ct CallType, request *Request, response *Response, callbac return perrors.WithStack(err) } +// Close ... func (c *Client) Close() { if c.pool != nil { c.pool.close() diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go index 3f8a8ee98c3b2d8b87e2d5469a18d1792578d1d6..1e0a73fac1a6cf6d4d102e5f4f6f1ba60fc4102a 100644 --- a/protocol/dubbo/client_test.go +++ b/protocol/dubbo/client_test.go @@ -210,10 +210,10 @@ func InitTest(t *testing.T) (protocol.Protocol, common.URL) { // Export proto := GetProtocol() - url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider") assert.NoError(t, err) proto.Export(&proxy_factory.ProxyInvoker{ diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go index 758363117f1720a7fe89eb9745b415e506315db8..3e50eb901dbe1e549aea4ea7414d9617851b5363 100644 --- a/protocol/dubbo/codec.go +++ b/protocol/dubbo/codec.go @@ -30,28 +30,34 @@ import ( perrors "github.com/pkg/errors" ) -// serial ID +//SerialID serial ID type SerialID byte const ( + // S_Dubbo dubbo serial id S_Dubbo SerialID = 2 ) -// call type +//CallType call type type CallType int32 const ( + // CT_UNKNOWN unknown call type CT_UNKNOWN CallType = 0 - CT_OneWay CallType = 1 - CT_TwoWay CallType = 2 + // CT_OneWay call one way + CT_OneWay CallType = 1 + // CT_TwoWay call in request/response + CT_TwoWay CallType = 2 ) //////////////////////////////////////////// // dubbo package //////////////////////////////////////////// +// SequenceType ... type SequenceType int64 +// DubboPackage ... type DubboPackage struct { Header hessian.DubboHeader Service hessian.Service @@ -63,6 +69,7 @@ func (p DubboPackage) String() string { return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) } +// Marshal ... func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { codec := hessian.NewHessianCodec(nil) @@ -74,6 +81,7 @@ func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { return bytes.NewBuffer(pkg), nil } +// Unmarshal ... func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, buf.Len())) @@ -89,11 +97,17 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { return perrors.Errorf("opts[0] is not of type *Client") } - pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID)) - if !ok { - return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID) + if p.Header.Type&hessian.PackageRequest != 0x00 { + // size of this array must be '7' + // https://github.com/apache/dubbo-go-hessian2/blob/master/request.go#L272 + p.Body = make([]interface{}, 7) + } else { + pendingRsp, ok := client.pendingResponses.Load(SequenceType(p.Header.ID)) + if !ok { + return perrors.Errorf("client.GetPendingResponse(%v) = nil", p.Header.ID) + } + p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply} } - p.Body = &hessian.Response{RspObj: pendingRsp.(*PendingResponse).response.reply} } // read body @@ -105,6 +119,7 @@ func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { // PendingResponse //////////////////////////////////////////// +// PendingResponse ... type PendingResponse struct { seq uint64 err error @@ -115,6 +130,7 @@ type PendingResponse struct { done chan struct{} } +// NewPendingResponse ... func NewPendingResponse() *PendingResponse { return &PendingResponse{ start: time.Now(), @@ -123,6 +139,7 @@ func NewPendingResponse() *PendingResponse { } } +// GetCallResponse ... func (r PendingResponse) GetCallResponse() common.CallbackResponse { return AsyncCallbackResponse{ Cause: r.err, diff --git a/protocol/dubbo/config.go b/protocol/dubbo/config.go index 5371c587478ce7d064858108055c3f00f3ff4496..dbc6989c54780afacef717f1d110833d92967f9f 100644 --- a/protocol/dubbo/config.go +++ b/protocol/dubbo/config.go @@ -22,10 +22,12 @@ import ( ) import ( + "github.com/dubbogo/getty" perrors "github.com/pkg/errors" ) type ( + // GettySessionParam ... 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"` @@ -45,7 +47,8 @@ type ( SessionName string `default:"rpc" yaml:"session_name" json:"session_name,omitempty"` } - // Config holds supported types by the multiconfig package + // ServerConfig + //Config holds supported types by the multiconfig package ServerConfig struct { // session SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` @@ -61,7 +64,8 @@ type ( GettySessionParam GettySessionParam `required:"true" yaml:"getty_session_param" json:"getty_session_param,omitempty"` } - // Config holds supported types by the multiconfig package + // ClientConfig + //Config holds supported types by the multiconfig package ClientConfig struct { ReconnectInterval int `default:"0" yaml:"reconnect_interval" json:"reconnect_interval,omitempty"` @@ -90,13 +94,14 @@ type ( } ) +// GetDefaultClientConfig ... func GetDefaultClientConfig() ClientConfig { return ClientConfig{ ReconnectInterval: 0, ConnectionNum: 16, - HeartbeatPeriod: "5s", - SessionTimeout: "20s", - PoolSize: 64, + HeartbeatPeriod: "30s", + SessionTimeout: "180s", + PoolSize: 4, PoolTTL: 600, GrPoolSize: 200, QueueLen: 64, @@ -105,7 +110,7 @@ func GetDefaultClientConfig() ClientConfig { CompressEncoding: false, TcpNoDelay: true, TcpKeepAlive: true, - KeepAlivePeriod: "120s", + KeepAlivePeriod: "180s", TcpRBufSize: 262144, TcpWBufSize: 65536, PkgWQSize: 512, @@ -117,9 +122,10 @@ func GetDefaultClientConfig() ClientConfig { }} } +// GetDefaultServerConfig ... func GetDefaultServerConfig() ServerConfig { return ServerConfig{ - SessionTimeout: "20s", + SessionTimeout: "180s", SessionNumber: 700, GrPoolSize: 120, QueueNumber: 6, @@ -128,7 +134,7 @@ func GetDefaultServerConfig() ServerConfig { CompressEncoding: false, TcpNoDelay: true, TcpKeepAlive: true, - KeepAlivePeriod: "120s", + KeepAlivePeriod: "180s", TcpRBufSize: 262144, TcpWBufSize: 65536, PkgWQSize: 512, @@ -141,6 +147,7 @@ func GetDefaultServerConfig() ServerConfig { } } +// CheckValidity ... func (c *GettySessionParam) CheckValidity() error { var err error @@ -163,6 +170,7 @@ func (c *GettySessionParam) CheckValidity() error { return nil } +// CheckValidity ... func (c *ClientConfig) CheckValidity() error { var err error @@ -172,6 +180,11 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) } + if c.heartbeatPeriod >= time.Duration(getty.MaxWheelTimeSpan) { + return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s", + c.HeartbeatPeriod, time.Duration(getty.MaxWheelTimeSpan)) + } + if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) } @@ -179,6 +192,7 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithStack(c.GettySessionParam.CheckValidity()) } +// CheckValidity ... func (c *ServerConfig) CheckValidity() error { var err error @@ -186,5 +200,10 @@ func (c *ServerConfig) CheckValidity() error { return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) } + if c.sessionTimeout >= time.Duration(getty.MaxWheelTimeSpan) { + return perrors.WithMessagef(err, "session_timeout %s should be less than %s", + c.SessionTimeout, time.Duration(getty.MaxWheelTimeSpan)) + } + return perrors.WithStack(c.GettySessionParam.CheckValidity()) } diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go index cb06b6b69c9d0873342af5ea49fae054f029608c..f4cd0cc1234f71bdcf6ce746f01ff3618d820fc5 100644 --- a/protocol/dubbo/dubbo_exporter.go +++ b/protocol/dubbo/dubbo_exporter.go @@ -28,16 +28,19 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// DubboExporter ... type DubboExporter struct { protocol.BaseExporter } +// NewDubboExporter ... func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *DubboExporter { return &DubboExporter{ BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap), } } +// Unexport ... func (de *DubboExporter) Unexport() { serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") de.BaseExporter.Unexport() diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 6dcf2568fa8c88a864c567486a501c2ad7feb3f7..67d1d1e7f1710aef71f63063374a848e2981b828 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -18,11 +18,13 @@ package dubbo import ( + "context" "strconv" "sync" ) import ( + "github.com/opentracing/opentracing-go" perrors "github.com/pkg/errors" ) @@ -34,18 +36,23 @@ import ( invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) -var Err_No_Reply = perrors.New("request need @response") +var ( + // ErrNoReply ... + ErrNoReply = perrors.New("request need @response") +) var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} ) +// DubboInvoker ... type DubboInvoker struct { protocol.BaseInvoker client *Client quitOnce sync.Once } +// NewDubboInvoker ... func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { return &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), @@ -53,8 +60,8 @@ func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { } } -func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result { - +// Invoke ... +func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( err error result protocol.RPCResult @@ -66,6 +73,10 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result { inv.SetAttachments(k, v) } } + + // put the ctx into attachment + di.appendCtx(ctx, inv) + url := di.GetUrl() // async async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false")) @@ -82,7 +93,7 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result { } } else { if inv.Reply() == nil { - result.Err = Err_No_Reply + result.Err = ErrNoReply } else { result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response) } @@ -96,6 +107,7 @@ func (di *DubboInvoker) Invoke(invocation protocol.Invocation) protocol.Result { return &result } +// Destroy ... func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { di.BaseInvoker.Destroy() @@ -105,3 +117,17 @@ func (di *DubboInvoker) Destroy() { } }) } + +// Finally, I made the decision that I don't provide a general way to transfer the whole context +// because it could be misused. If the context contains to many key-value pairs, the performance will be much lower. +func (di *DubboInvoker) appendCtx(ctx context.Context, inv *invocation_impl.RPCInvocation) { + // inject opentracing ctx + currentSpan := opentracing.SpanFromContext(ctx) + if currentSpan != nil { + carrier := opentracing.TextMapCarrier(inv.Attachments()) + err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier) + if err != nil { + logger.Errorf("Could not inject the span context into attachments: %v", err) + } + } +} diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 7d60090e2d81bcb750d1e6d79a08059687c7937d..1a64301f8200a4264001284cca1af3f0f1e07814 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -18,12 +18,14 @@ package dubbo import ( + "context" "sync" "testing" "time" ) import ( + "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" ) @@ -40,8 +42,8 @@ func TestDubboInvoker_Invoke(t *testing.T) { pendingResponses: new(sync.Map), conf: *clientConf, opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 6e9, + ConnectTimeout: 3 * time.Second, + RequestTimeout: 6 * time.Second, }, } c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) @@ -53,14 +55,14 @@ func TestDubboInvoker_Invoke(t *testing.T) { invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"})) // Call - res := invoker.Invoke(inv) + res := invoker.Invoke(context.Background(), inv) assert.NoError(t, res.Error()) assert.Equal(t, User{Id: "1", Name: "username"}, *res.Result().(*User)) assert.Equal(t, "test_value", res.Attachments()["test_key"]) // test attachments for request/response // CallOneway inv.SetAttachments(constant.ASYNC_KEY, "true") - res = invoker.Invoke(inv) + res = invoker.Invoke(context.Background(), inv) assert.NoError(t, res.Error()) // AsyncCall @@ -71,15 +73,20 @@ func TestDubboInvoker_Invoke(t *testing.T) { assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) lock.Unlock() }) - res = invoker.Invoke(inv) + res = invoker.Invoke(context.Background(), inv) assert.NoError(t, res.Error()) // Err_No_Reply inv.SetAttachments(constant.ASYNC_KEY, "false") inv.SetReply(nil) - res = invoker.Invoke(inv) + res = invoker.Invoke(context.Background(), inv) assert.EqualError(t, res.Error(), "request need @response") + // testing appendCtx + span, ctx := opentracing.StartSpanFromContext(context.Background(), "TestOperation") + invoker.Invoke(ctx, inv) + span.Finish() + // destroy lock.Lock() proto.Destroy() diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 59d1ea05160696754b46dfead5713684aa7a94f7..355dbc802488338ef4dbdd7290166038b312f183 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -19,17 +19,21 @@ package dubbo import ( "sync" + "time" ) 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/protocol" ) +// dubbo protocol constant const ( + // DUBBO ... DUBBO = "dubbo" ) @@ -41,12 +45,14 @@ var ( dubboProtocol *DubboProtocol ) +// DubboProtocol ... type DubboProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } +// NewDubboProtocol ... func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -54,6 +60,7 @@ func NewDubboProtocol() *DubboProtocol { } } +// Export ... func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() @@ -66,16 +73,26 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } +// Refer ... func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { + //default requestTimeout + var requestTimeout = config.GetConsumerConfig().RequestTimeout + + requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) + if t, err := time.ParseDuration(requestTimeoutStr); err == nil { + requestTimeout = t + } + invoker := NewDubboInvoker(url, NewClient(Options{ ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - RequestTimeout: config.GetConsumerConfig().RequestTimeout, + RequestTimeout: requestTimeout, })) dp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker } +// Destroy ... func (dp *DubboProtocol) Destroy() { logger.Infof("DubboProtocol destroy.") @@ -107,6 +124,7 @@ func (dp *DubboProtocol) openServer(url common.URL) { } } +// GetProtocol ... func GetProtocol() protocol.Protocol { if dubboProtocol == nil { dubboProtocol = NewDubboProtocol() diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index a6b0bc1df3cf2eb46e07c9dab149d04f62f78012..14f6868ad4a7f2bf6f549a2fbbce8234cbb4aa12 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -18,7 +18,6 @@ package dubbo import ( - "context" "testing" ) @@ -36,10 +35,10 @@ func TestDubboProtocol_Export(t *testing.T) { // Export proto := GetProtocol() srvConf = &ServerConfig{} - url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) exporter := proto.Export(protocol.NewBaseInvoker(url)) @@ -49,7 +48,7 @@ func TestDubboProtocol_Export(t *testing.T) { assert.True(t, eq) // second service: the same path and the different version - url2, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ + url2, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ @@ -78,10 +77,10 @@ func TestDubboProtocol_Export(t *testing.T) { func TestDubboProtocol_Refer(t *testing.T) { // Refer proto := GetProtocol() - url, err := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) clientConf = &ClientConfig{} diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go index df9ab28e0e4b896b11b2345a83cae14401a70759..430c4e49d81d4d5d151a545e1a130fd4ac3fbdc5 100644 --- a/protocol/dubbo/listener.go +++ b/protocol/dubbo/listener.go @@ -18,15 +18,18 @@ package dubbo import ( + "context" "fmt" "net/url" "sync" + "sync/atomic" "time" ) import ( "github.com/apache/dubbo-go-hessian2" "github.com/dubbogo/getty" + "github.com/opentracing/opentracing-go" perrors "github.com/pkg/errors" ) @@ -39,7 +42,10 @@ import ( ) // todo: WritePkg_Timeout will entry *.yml -const WritePkg_Timeout = 5 * time.Second +const ( + // WritePkg_Timeout ... + WritePkg_Timeout = 5 * time.Second +) var ( errTooManySessions = perrors.New("too many sessions") @@ -50,33 +56,47 @@ type rpcSession struct { reqNum int32 } -//////////////////////////////////////////// +func (s *rpcSession) AddReqNum(num int32) { + atomic.AddInt32(&s.reqNum, num) +} + +func (s *rpcSession) GetReqNum() int32 { + return atomic.LoadInt32(&s.reqNum) +} + +// ////////////////////////////////////////// // RpcClientHandler -//////////////////////////////////////////// +// ////////////////////////////////////////// +// RpcClientHandler ... type RpcClientHandler struct { conn *gettyRPCClient } +// NewRpcClientHandler ... func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { return &RpcClientHandler{conn: client} } +// OnOpen ... func (h *RpcClientHandler) OnOpen(session getty.Session) error { h.conn.addSession(session) return nil } +// OnError ... func (h *RpcClientHandler) OnError(session getty.Session, err error) { logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.conn.removeSession(session) } +// OnClose ... func (h *RpcClientHandler) OnClose(session getty.Session) { logger.Infof("session{%s} is closing......", session.Stat()) h.conn.removeSession(session) } +// OnMessage ... func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { p, ok := pkg.(*DubboPackage) if !ok { @@ -85,11 +105,17 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { } if p.Header.Type&hessian.PackageHeartbeat != 0x00 { - logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) - if p.Err != nil { - logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) + if p.Header.Type&hessian.PackageResponse != 0x00 { + logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", p.Header, p.Body) + if p.Err != nil { + logger.Errorf("rpc heartbeat response{error: %#v}", p.Err) + } + h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) + } else { + logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) + p.Header.ResponseStatus = hessian.Response_OK + reply(session, p, hessian.PackageHeartbeat) } - h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) return } logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body) @@ -114,6 +140,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { } } +// OnCron ... func (h *RpcClientHandler) OnCron(session getty.Session) { rpcSession, err := h.conn.getClientRpcSession(session) if err != nil { @@ -131,10 +158,11 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { h.conn.pool.rpcClient.heartbeat(session) } -//////////////////////////////////////////// +// ////////////////////////////////////////// // RpcServerHandler -//////////////////////////////////////////// +// ////////////////////////////////////////// +// RpcServerHandler ... type RpcServerHandler struct { maxSessionNum int sessionTimeout time.Duration @@ -142,6 +170,7 @@ type RpcServerHandler struct { rwlock sync.RWMutex } +// NewRpcServerHandler ... func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { return &RpcServerHandler{ maxSessionNum: maxSessionNum, @@ -150,6 +179,7 @@ func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcSe } } +// OnOpen ... func (h *RpcServerHandler) OnOpen(session getty.Session) error { var err error h.rwlock.RLock() @@ -168,6 +198,7 @@ func (h *RpcServerHandler) OnOpen(session getty.Session) error { return nil } +// OnError ... func (h *RpcServerHandler) OnError(session getty.Session, err error) { logger.Infof("session{%s} got error{%v}, will be closed.", session.Stat(), err) h.rwlock.Lock() @@ -175,6 +206,7 @@ func (h *RpcServerHandler) OnError(session getty.Session, err error) { h.rwlock.Unlock() } +// OnClose ... func (h *RpcServerHandler) OnClose(session getty.Session) { logger.Infof("session{%s} is closing......", session.Stat()) h.rwlock.Lock() @@ -182,6 +214,7 @@ func (h *RpcServerHandler) OnClose(session getty.Session) { h.rwlock.Unlock() } +// OnMessage ... func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { h.rwlock.Lock() if _, ok := h.sessionMap[session]; ok { @@ -199,7 +232,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { // heartbeat if p.Header.Type&hessian.PackageHeartbeat != 0x00 { logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", p.Header, p.Service, p.Body) - h.reply(session, p, hessian.PackageHeartbeat) + reply(session, p, hessian.PackageHeartbeat) return } @@ -226,7 +259,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { if !twoway { return } - h.reply(session, p, hessian.PackageResponse) + reply(session, p, hessian.PackageResponse) } }() @@ -241,7 +274,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { logger.Errorf(err.Error()) p.Header.ResponseStatus = hessian.Response_OK p.Body = err - h.reply(session, p, hessian.PackageResponse) + reply(session, p, hessian.PackageResponse) return } invoker := exporter.(protocol.Exporter).GetInvoker() @@ -252,7 +285,10 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { args := p.Body.(map[string]interface{})["args"].([]interface{}) inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) - result := invoker.Invoke(inv) + + ctx := rebuildCtx(inv) + + result := invoker.Invoke(ctx, inv) if err := result.Error(); err != nil { p.Header.ResponseStatus = hessian.Response_OK p.Body = hessian.NewResponse(nil, err, result.Attachments()) @@ -266,9 +302,10 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { if !twoway { return } - h.reply(session, p, hessian.PackageResponse) + reply(session, p, hessian.PackageResponse) } +// OnCron ... func (h *RpcServerHandler) OnCron(session getty.Session) { var ( flag bool @@ -294,7 +331,22 @@ func (h *RpcServerHandler) OnCron(session getty.Session) { } } -func (h *RpcServerHandler) reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { +// rebuildCtx rebuild the context by attachment. +// Once we decided to transfer more context's key-value, we should change this. +// now we only support rebuild the tracing context +func rebuildCtx(inv *invocation.RPCInvocation) context.Context { + ctx := context.Background() + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} + +func reply(session getty.Session, req *DubboPackage, tp hessian.PackageType) { resp := &DubboPackage{ Header: hessian.DubboHeader{ SerialID: req.Header.SerialID, diff --git a/protocol/dubbo/listener_test.go b/protocol/dubbo/listener_test.go new file mode 100644 index 0000000000000000000000000000000000000000..5f809814607558650e09934019db96dbb2ceeeae --- /dev/null +++ b/protocol/dubbo/listener_test.go @@ -0,0 +1,58 @@ +/* + * 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 dubbo + +import ( + "testing" +) + +import ( + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol/invocation" +) + +// test rebuild the ctx +func TestRebuildCtx(t *testing.T) { + opentracing.SetGlobalTracer(mocktracer.New()) + attach := make(map[string]string, 10) + attach[constant.VERSION_KEY] = "1.0" + attach[constant.GROUP_KEY] = "MyGroup" + inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + + // attachment doesn't contains any tracing key-value pair, + ctx := rebuildCtx(inv) + assert.NotNil(t, ctx) + assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) + + span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") + + opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, + opentracing.TextMapCarrier(inv.Attachments())) + // rebuild the context success + inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) + ctx = rebuildCtx(inv) + span.Finish() + assert.NotNil(t, ctx) + assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) +} diff --git a/protocol/dubbo/pool.go b/protocol/dubbo/pool.go index b5bf040c67c2e0071222466e59db4de67d9e1ca2..9d381585b56ec439f8ebb8938109baf47bb502b2 100644 --- a/protocol/dubbo/pool.go +++ b/protocol/dubbo/pool.go @@ -154,11 +154,11 @@ func (c *gettyRPCClient) addSession(session getty.Session) { } c.lock.Lock() + defer c.lock.Unlock() if c.sessions == nil { c.sessions = make([]*rpcSession, 0, 16) } c.sessions = append(c.sessions, &rpcSession{session: session}) - c.lock.Unlock() } func (c *gettyRPCClient) removeSession(session getty.Session) { @@ -166,21 +166,27 @@ func (c *gettyRPCClient) removeSession(session getty.Session) { return } - c.lock.Lock() - defer c.lock.Unlock() - if c.sessions == nil { - return - } + var removeFlag bool + func() { + c.lock.Lock() + defer c.lock.Unlock() + if c.sessions == nil { + return + } - for i, s := range c.sessions { - if s.session == session { - c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) - logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) - break + for i, s := range c.sessions { + if s.session == session { + c.sessions = append(c.sessions[:i], c.sessions[i+1:]...) + logger.Debugf("delete session{%s}, its index{%d}", session.Stat(), i) + break + } } - } - logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) - if len(c.sessions) == 0 { + logger.Infof("after remove session{%s}, left session number:%d", session.Stat(), len(c.sessions)) + if len(c.sessions) == 0 { + removeFlag = true + } + }() + if removeFlag { c.pool.safeRemove(c) c.close() } @@ -190,17 +196,24 @@ func (c *gettyRPCClient) updateSession(session getty.Session) { if session == nil { return } - c.lock.Lock() - defer c.lock.Unlock() - if c.sessions == nil { - return - } - for i, s := range c.sessions { - if s.session == session { - c.sessions[i].reqNum++ - break + var rs *rpcSession + func() { + c.lock.RLock() + defer c.lock.RUnlock() + if c.sessions == nil { + return + } + + for i, s := range c.sessions { + if s.session == session { + rs = c.sessions[i] + break + } } + }() + if rs != nil { + rs.AddReqNum(1) } } @@ -238,28 +251,42 @@ func (c *gettyRPCClient) isAvailable() bool { func (c *gettyRPCClient) close() error { closeErr := perrors.Errorf("close gettyRPCClient{%#v} again", c) c.once.Do(func() { - c.gettyClient.Close() - c.gettyClient = nil - for _, s := range c.sessions { - logger.Infof("close client session{%s, last active:%s, request number:%d}", - s.session.Stat(), s.session.GetActive().String(), s.reqNum) - s.session.Close() - } - c.sessions = c.sessions[:0] + var ( + gettyClient getty.Client + sessions []*rpcSession + ) + func() { + c.lock.Lock() + defer c.lock.Unlock() + + gettyClient = c.gettyClient + c.gettyClient = nil + + sessions = make([]*rpcSession, 0, len(c.sessions)) + for _, s := range c.sessions { + sessions = append(sessions, s) + } + c.sessions = c.sessions[:0] + }() c.updateActive(0) + + go func() { + if gettyClient != nil { + gettyClient.Close() + } + for _, s := range sessions { + logger.Infof("close client session{%s, last active:%s, request number:%d}", + s.session.Stat(), s.session.GetActive().String(), s.GetReqNum()) + s.session.Close() + } + }() + closeErr = nil }) return closeErr } -func (c *gettyRPCClient) safeClose() error { - c.lock.Lock() - defer c.lock.Unlock() - - return c.close() -} - type gettyRPCClientPool struct { rpcClient *Client size int // size of []*gettyRPCClient @@ -284,37 +311,35 @@ func (p *gettyRPCClientPool) close() { p.conns = nil p.Unlock() for _, conn := range conns { - conn.safeClose() + conn.close() } } func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { - var ( - conn *gettyRPCClient - err error - ) - if conn, err = p.selectGettyRpcClient(protocol, addr); err == nil && conn == nil { + conn, err := p.get() + if err == nil && conn == nil { // create new conn return newGettyRPCClientConn(p, protocol, addr) } return conn, err } -func (p *gettyRPCClientPool) selectGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { + +func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { + now := time.Now().Unix() + p.Lock() defer p.Unlock() if p.conns == nil { return nil, errClientPoolClosed } - now := time.Now().Unix() - for len(p.conns) > 0 { conn := p.conns[len(p.conns)-1] p.conns = p.conns[:len(p.conns)-1] if d := now - conn.getActive(); d > p.ttl { p.remove(conn) - conn.safeClose() + go conn.close() continue } conn.updateActive(now) //update active time @@ -322,13 +347,9 @@ func (p *gettyRPCClientPool) selectGettyRpcClient(protocol, addr string) (*getty } return nil, nil } -func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) { - if conn == nil || conn.getActive() == 0 { - return - } - if err != nil { - conn.safeClose() +func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { + if conn == nil || conn.getActive() == 0 { return } @@ -339,10 +360,17 @@ func (p *gettyRPCClientPool) release(conn *gettyRPCClient, err error) { return } + // check whether @conn has existed in p.conns or not. + for i := range p.conns { + if p.conns[i] == conn { + return + } + } + if len(p.conns) >= p.size { // delete @conn from client pool - p.remove(conn) - conn.safeClose() + // p.remove(conn) + conn.close() return } p.conns = append(p.conns, conn) diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go index 930382cca8bac6955b516a88e93ce26d73e235fe..b5c4f509190dbdc85825ad424656240b234786df 100644 --- a/protocol/dubbo/readwriter.go +++ b/protocol/dubbo/readwriter.go @@ -38,10 +38,12 @@ import ( // RpcClientPackageHandler //////////////////////////////////////////// +// RpcClientPackageHandler ... type RpcClientPackageHandler struct { client *Client } +// NewRpcClientPackageHandler ... func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { return &RpcClientPackageHandler{client: client} } @@ -62,8 +64,10 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface return nil, 0, perrors.WithStack(err) } - pkg.Err = pkg.Body.(*hessian.Response).Exception - pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) + if pkg.Header.Type&hessian.PackageRequest == 0x00 { + pkg.Err = pkg.Body.(*hessian.Response).Exception + pkg.Body = NewResponse(pkg.Body.(*hessian.Response).RspObj, pkg.Body.(*hessian.Response).Attachments) + } return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil } @@ -92,6 +96,7 @@ var ( rpcServerPkgHandler = &RpcServerPackageHandler{} ) +// RpcServerPackageHandler ... type RpcServerPackageHandler struct{} func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { diff --git a/protocol/dubbo/server.go b/protocol/dubbo/server.go index 648c9f8aa8d24a321bfda85279a6470c745dbfa1..bd2b37b7a9f055745e183524d19a442af03360f4 100644 --- a/protocol/dubbo/server.go +++ b/protocol/dubbo/server.go @@ -74,6 +74,7 @@ func init() { SetServerGrpool() } +// SetServerConfig ... func SetServerConfig(s ServerConfig) { srvConf = &s err := srvConf.CheckValidity() @@ -84,10 +85,12 @@ func SetServerConfig(s ServerConfig) { SetServerGrpool() } +// GetServerConfig ... func GetServerConfig() ServerConfig { return *srvConf } +// SetServerGrpool ... func SetServerGrpool() { if srvConf.GrPoolSize > 1 { srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), @@ -95,12 +98,14 @@ func SetServerGrpool() { } } +// Server ... type Server struct { conf ServerConfig tcpServer getty.Server rpcHandler *RpcServerHandler } +// NewServer ... func NewServer() *Server { s := &Server{ @@ -151,6 +156,7 @@ func (s *Server) newSession(session getty.Session) error { return nil } +// Start ... func (s *Server) Start(url common.URL) { var ( addr string @@ -167,6 +173,7 @@ func (s *Server) Start(url common.URL) { } +// Stop ... func (s *Server) Stop() { s.tcpServer.Close() } diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go new file mode 100644 index 0000000000000000000000000000000000000000..d35a2c770cd8b9bda805715889791ccf53c562db --- /dev/null +++ b/protocol/grpc/client.go @@ -0,0 +1,63 @@ +/* +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 ( + "reflect" +) + +import ( + "google.golang.org/grpc" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config" +) + +// Client ... +type Client struct { + *grpc.ClientConn + invoker reflect.Value +} + +// NewClient ... +func NewClient(url common.URL) *Client { + conn, err := grpc.Dial(url.Location, grpc.WithInsecure(), grpc.WithBlock()) + if err != nil { + panic(err) + } + + key := url.GetParam(constant.BEAN_NAME_KEY, "") + impl := config.GetConsumerService(key) + invoker := getInvoker(impl, conn) + + return &Client{ + ClientConn: conn, + invoker: reflect.ValueOf(invoker), + } +} + +func getInvoker(impl interface{}, conn *grpc.ClientConn) interface{} { + in := []reflect.Value{} + in = append(in, reflect.ValueOf(conn)) + method := reflect.ValueOf(impl).MethodByName("GetDubboStub") + res := method.Call(in) + return res[0].Interface() +} diff --git a/protocol/grpc/client_test.go b/protocol/grpc/client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..56ec766f70da93bcddbcff13667a34c39deffe06 --- /dev/null +++ b/protocol/grpc/client_test.go @@ -0,0 +1,53 @@ +/* +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 ( + "reflect" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" + "google.golang.org/grpc" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol/grpc/internal" +) + +func TestGetInvoker(t *testing.T) { + var conn *grpc.ClientConn + var impl *internal.GrpcGreeterImpl + invoker := getInvoker(impl, conn) + + i := reflect.TypeOf(invoker) + expected := reflect.TypeOf(internal.NewGreeterClient(nil)) + assert.Equal(t, i, expected) +} + +func TestNewClient(t *testing.T) { + go internal.InitGrpcServer() + defer internal.ShutdownGrpcServer() + + url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!") + assert.Nil(t, err) + cli := NewClient(url) + assert.NotNil(t, cli) +} diff --git a/protocol/grpc/common_test.go b/protocol/grpc/common_test.go new file mode 100644 index 0000000000000000000000000000000000000000..3d0823b1061a61cfa391358e270c8b9081e9031c --- /dev/null +++ b/protocol/grpc/common_test.go @@ -0,0 +1,114 @@ +/* +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 ( + "context" + "fmt" +) + +import ( + native_grpc "google.golang.org/grpc" +) + +import ( + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/grpc/internal" + "github.com/apache/dubbo-go/protocol/invocation" +) + +// userd grpc-dubbo biz service +func addService() { + config.SetProviderService(newGreeterProvider()) +} + +type greeterProvider struct { + *greeterProviderBase +} + +func newGreeterProvider() *greeterProvider { + return &greeterProvider{ + greeterProviderBase: &greeterProviderBase{}, + } +} + +func (g *greeterProvider) SayHello(ctx context.Context, req *internal.HelloRequest) (reply *internal.HelloReply, err error) { + fmt.Printf("req: %v", req) + return &internal.HelloReply{Message: "this is message from reply"}, nil +} + +func (g *greeterProvider) Reference() string { + return "GrpcGreeterImpl" +} + +// code generated by greeter.go +type greeterProviderBase struct { + proxyImpl protocol.Invoker +} + +func (g *greeterProviderBase) SetProxyImpl(impl protocol.Invoker) { + g.proxyImpl = impl +} + +func (g *greeterProviderBase) GetProxyImpl() protocol.Invoker { + return g.proxyImpl +} + +func (g *greeterProviderBase) ServiceDesc() *native_grpc.ServiceDesc { + return &native_grpc.ServiceDesc{ + ServiceName: "helloworld.Greeter", + HandlerType: (*internal.GreeterServer)(nil), + Methods: []native_grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: dubboGreeterSayHelloHandler, + }, + }, + Streams: []native_grpc.StreamDesc{}, + Metadata: "helloworld.proto", + } +} + +func dubboGreeterSayHelloHandler(srv interface{}, ctx context.Context, + dec func(interface{}) error, interceptor native_grpc.UnaryServerInterceptor) (interface{}, error) { + + in := new(internal.HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + base := srv.(DubboGrpcService) + + args := []interface{}{} + args = append(args, in) + invo := invocation.NewRPCInvocation("SayHello", args, nil) + + if interceptor == nil { + result := base.GetProxyImpl().Invoke(context.Background(), invo) + return result.Result(), result.Error() + } + info := &native_grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + result := base.GetProxyImpl().Invoke(context.Background(), invo) + return result.Result(), result.Error() + } + return interceptor(ctx, in, info, handler) +} diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go new file mode 100644 index 0000000000000000000000000000000000000000..3c38ef974ca22a582ce83102718d01a8edd4258f --- /dev/null +++ b/protocol/grpc/grpc_exporter.go @@ -0,0 +1,51 @@ +/* +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 ( + "sync" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" +) + +// GrpcExporter ... +type GrpcExporter struct { + *protocol.BaseExporter +} + +// NewGrpcExporter ... +func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *GrpcExporter { + return &GrpcExporter{ + BaseExporter: protocol.NewBaseExporter(key, invoker, exporterMap), + } +} + +// Unexport ... +func (gg *GrpcExporter) Unexport() { + serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") + gg.BaseExporter.Unexport() + err := common.ServiceMap.UnRegister(GRPC, serviceId) + if err != nil { + logger.Errorf("[GrpcExporter.Unexport] error: %v", err) + } +} diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go new file mode 100644 index 0000000000000000000000000000000000000000..26bc86f3aa46c8048b16284bd61cb5d9fb4664f9 --- /dev/null +++ b/protocol/grpc/grpc_invoker.go @@ -0,0 +1,104 @@ +/* +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 ( + "context" + "reflect" + "sync" +) + +import ( + "github.com/pkg/errors" + "google.golang.org/grpc/connectivity" +) + +import ( + hessian2 "github.com/apache/dubbo-go-hessian2" + + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +// ErrNoReply ... +var ErrNoReply = errors.New("request need @response") + +// GrpcInvoker ... +type GrpcInvoker struct { + protocol.BaseInvoker + quitOnce sync.Once + client *Client +} + +// NewGrpcInvoker ... +func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker { + return &GrpcInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + client: client, + } +} + +// Invoke ... +func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { + var ( + result protocol.RPCResult + ) + + if invocation.Reply() == nil { + result.Err = ErrNoReply + } + + in := []reflect.Value{} + in = append(in, reflect.ValueOf(context.Background())) + in = append(in, invocation.ParameterValues()...) + + methodName := invocation.MethodName() + method := gi.client.invoker.MethodByName(methodName) + res := method.Call(in) + + result.Rest = res[0] + // check err + if !res[1].IsNil() { + result.Err = res[1].Interface().(error) + } else { + _ = hessian2.ReflectResponse(res[0], invocation.Reply()) + } + + return &result +} + +// IsAvailable ... +func (gi *GrpcInvoker) IsAvailable() bool { + return gi.BaseInvoker.IsAvailable() && gi.client.GetState() != connectivity.Shutdown +} + +// IsDestroyed ... +func (gi *GrpcInvoker) IsDestroyed() bool { + return gi.BaseInvoker.IsDestroyed() && gi.client.GetState() == connectivity.Shutdown +} + +// Destroy ... +func (gi *GrpcInvoker) Destroy() { + gi.quitOnce.Do(func() { + gi.BaseInvoker.Destroy() + + if gi.client != nil { + _ = gi.client.Close() + } + }) +} diff --git a/protocol/grpc/grpc_invoker_test.go b/protocol/grpc/grpc_invoker_test.go new file mode 100644 index 0000000000000000000000000000000000000000..368c1392ec03af310f93e8fc2173b8354975c99e --- /dev/null +++ b/protocol/grpc/grpc_invoker_test.go @@ -0,0 +1,56 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package grpc + +import ( + "context" + "reflect" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol/grpc/internal" + "github.com/apache/dubbo-go/protocol/invocation" +) + +func TestInvoke(t *testing.T) { + go internal.InitGrpcServer() + defer internal.ShutdownGrpcServer() + + url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!") + assert.Nil(t, err) + + cli := NewClient(url) + + invoker := NewGrpcInvoker(url, cli) + + args := []reflect.Value{} + args = append(args, reflect.ValueOf(&internal.HelloRequest{Name: "request name"})) + bizReply := &internal.HelloReply{} + invo := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("SayHello"), + invocation.WithParameterValues(args), invocation.WithReply(bizReply)) + res := invoker.Invoke(context.Background(), invo) + assert.Nil(t, res.Error()) + assert.NotNil(t, res.Result()) + assert.Equal(t, "Hello request name", bizReply.Message) +} diff --git a/protocol/grpc/grpc_protocol.go b/protocol/grpc/grpc_protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..0f5625c152cc366289143b8a29d11cafb513b2f2 --- /dev/null +++ b/protocol/grpc/grpc_protocol.go @@ -0,0 +1,113 @@ +/* +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 ( + "sync" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" +) + +const ( + // GRPC module name + GRPC = "grpc" +) + +func init() { + extension.SetProtocol(GRPC, GetProtocol) +} + +var grpcProtocol *GrpcProtocol + +// GrpcProtocol ... +type GrpcProtocol struct { + protocol.BaseProtocol + serverMap map[string]*Server + serverLock sync.Mutex +} + +// NewGRPCProtocol ... +func NewGRPCProtocol() *GrpcProtocol { + return &GrpcProtocol{ + BaseProtocol: protocol.NewBaseProtocol(), + serverMap: make(map[string]*Server), + } +} + +// Export ... +func (gp *GrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { + url := invoker.GetUrl() + serviceKey := url.ServiceKey() + exporter := NewGrpcExporter(serviceKey, invoker, gp.ExporterMap()) + gp.SetExporterMap(serviceKey, exporter) + logger.Infof("Export service: %s", url.String()) + gp.openServer(url) + return exporter +} + +func (gp *GrpcProtocol) openServer(url common.URL) { + _, ok := gp.serverMap[url.Location] + if !ok { + _, ok := gp.ExporterMap().Load(url.ServiceKey()) + if !ok { + panic("[GrpcProtocol]" + url.Key() + "is not existing") + } + + gp.serverLock.Lock() + _, ok = gp.serverMap[url.Location] + if !ok { + srv := NewServer() + gp.serverMap[url.Location] = srv + srv.Start(url) + } + gp.serverLock.Unlock() + } +} + +// Refer ... +func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker { + invoker := NewGrpcInvoker(url, NewClient(url)) + gp.SetInvokers(invoker) + logger.Infof("Refer service: %s", url.String()) + return invoker +} + +// Destroy ... +func (gp *GrpcProtocol) Destroy() { + logger.Infof("GrpcProtocol destroy.") + + gp.BaseProtocol.Destroy() + + for key, server := range gp.serverMap { + delete(gp.serverMap, key) + server.Stop() + } +} + +// GetProtocol ... +func GetProtocol() protocol.Protocol { + if grpcProtocol == nil { + grpcProtocol = NewGRPCProtocol() + } + return grpcProtocol +} diff --git a/protocol/grpc/grpc_protocol_test.go b/protocol/grpc/grpc_protocol_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d0206a0fd953e40a478c26a2298f4889d8f72771 --- /dev/null +++ b/protocol/grpc/grpc_protocol_test.go @@ -0,0 +1,84 @@ +/* +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 ( + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/grpc/internal" +) + +func TestGrpcProtocol_Export(t *testing.T) { + // Export + addService() + + proto := GetProtocol() + url, err := common.NewURL("grpc://127.0.0.1:40000/GrpcGreeterImpl?accesslog=&app.version=0.0.1&application=BDTService&bean.name=GrpcGreeterImpl&cluster=failover&environment=dev&execute.limit=&execute.limit.rejected.handler=&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX®istry.role=3&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown×tamp=1576923717&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100") + assert.NoError(t, err) + exporter := proto.Export(protocol.NewBaseInvoker(url)) + time.Sleep(time.Second) + + // make sure url + eq := exporter.GetInvoker().GetUrl().URLEqual(url) + assert.True(t, eq) + + // make sure exporterMap after 'Unexport' + _, ok := proto.(*GrpcProtocol).ExporterMap().Load(url.ServiceKey()) + assert.True(t, ok) + exporter.Unexport() + _, ok = proto.(*GrpcProtocol).ExporterMap().Load(url.ServiceKey()) + assert.False(t, ok) + + // make sure serverMap after 'Destroy' + _, ok = proto.(*GrpcProtocol).serverMap[url.Location] + assert.True(t, ok) + proto.Destroy() + _, ok = proto.(*GrpcProtocol).serverMap[url.Location] + assert.False(t, ok) +} + +func TestGrpcProtocol_Refer(t *testing.T) { + go internal.InitGrpcServer() + defer internal.ShutdownGrpcServer() + time.Sleep(time.Second) + + proto := GetProtocol() + url, err := common.NewURL("grpc://127.0.0.1:30000/GrpcGreeterImpl?accesslog=&anyhost=true&app.version=0.0.1&application=BDTService&async=false&bean.name=GrpcGreeterImpl&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&execute.limit=&execute.limit.rejected.handler=&generic=false&group=&interface=io.grpc.examples.helloworld.GreeterGrpc%24IGreeter&ip=192.168.1.106&loadbalance=random&methods.SayHello.loadbalance=random&methods.SayHello.retries=1&methods.SayHello.tps.limit.interval=&methods.SayHello.tps.limit.rate=&methods.SayHello.tps.limit.strategy=&methods.SayHello.weight=0&module=dubbogo+say-hello+client&name=BDTService&organization=ikurento.com&owner=ZX&pid=49427&reference.filter=cshutdown®istry.role=3&remote.timestamp=1576923717&retries=&service.filter=echo%2Ctoken%2Caccesslog%2Ctps%2Cexecute%2Cpshutdown&side=provider×tamp=1576923740&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&version=&warmup=100!") + assert.NoError(t, err) + invoker := proto.Refer(url) + + // make sure url + eq := invoker.GetUrl().URLEqual(url) + assert.True(t, eq) + + // make sure invokers after 'Destroy' + invokersLen := len(proto.(*GrpcProtocol).Invokers()) + assert.Equal(t, 1, invokersLen) + proto.Destroy() + invokersLen = len(proto.(*GrpcProtocol).Invokers()) + assert.Equal(t, 0, invokersLen) +} diff --git a/protocol/grpc/internal/client.go b/protocol/grpc/internal/client.go new file mode 100644 index 0000000000000000000000000000000000000000..d236e3046a90e9179fba07a0be5edb07f8c2a3e8 --- /dev/null +++ b/protocol/grpc/internal/client.go @@ -0,0 +1,50 @@ +/* +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 internal + +import ( + "context" +) + +import ( + "google.golang.org/grpc" +) + +import ( + "github.com/apache/dubbo-go/config" +) + +func init() { + config.SetConsumerService(&GrpcGreeterImpl{}) +} + +// GrpcGreeterImpl +//used for dubbo-grpc biz client +type GrpcGreeterImpl struct { + SayHello func(ctx context.Context, in *HelloRequest, out *HelloReply) error +} + +// Reference ... +func (u *GrpcGreeterImpl) Reference() string { + return "GrpcGreeterImpl" +} + +// GetDubboStub ... +func (u *GrpcGreeterImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient { + return NewGreeterClient(cc) +} diff --git a/protocol/grpc/internal/doc.go b/protocol/grpc/internal/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..f2ef2ebd5e41980e1e1f1b0071ca7bb3885539f7 --- /dev/null +++ b/protocol/grpc/internal/doc.go @@ -0,0 +1,19 @@ +/* +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. +*/ + +// just for test, never use internal for production. +package internal diff --git a/protocol/grpc/internal/helloworld.pb.go b/protocol/grpc/internal/helloworld.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..79b74ac65011208ae74f989cf86e4e6f9f446015 --- /dev/null +++ b/protocol/grpc/internal/helloworld.pb.go @@ -0,0 +1,227 @@ +/* +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. +*/ + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: helloworld.proto + +package internal + +import ( + "context" + "fmt" + "math" + + "github.com/golang/protobuf/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// The request message containing the user's name. +type HelloRequest struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HelloRequest) Reset() { *m = HelloRequest{} } +func (m *HelloRequest) String() string { return proto.CompactTextString(m) } +func (*HelloRequest) ProtoMessage() {} +func (*HelloRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_17b8c58d586b62f2, []int{0} +} + +func (m *HelloRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HelloRequest.Unmarshal(m, b) +} +func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) +} +func (m *HelloRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HelloRequest.Merge(m, src) +} +func (m *HelloRequest) XXX_Size() int { + return xxx_messageInfo_HelloRequest.Size(m) +} +func (m *HelloRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HelloRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HelloRequest proto.InternalMessageInfo + +func (m *HelloRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// The response message containing the greetings +type HelloReply struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HelloReply) Reset() { *m = HelloReply{} } +func (m *HelloReply) String() string { return proto.CompactTextString(m) } +func (*HelloReply) ProtoMessage() {} +func (*HelloReply) Descriptor() ([]byte, []int) { + return fileDescriptor_17b8c58d586b62f2, []int{1} +} + +func (m *HelloReply) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HelloReply.Unmarshal(m, b) +} +func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) +} +func (m *HelloReply) XXX_Merge(src proto.Message) { + xxx_messageInfo_HelloReply.Merge(m, src) +} +func (m *HelloReply) XXX_Size() int { + return xxx_messageInfo_HelloReply.Size(m) +} +func (m *HelloReply) XXX_DiscardUnknown() { + xxx_messageInfo_HelloReply.DiscardUnknown(m) +} + +var xxx_messageInfo_HelloReply proto.InternalMessageInfo + +func (m *HelloReply) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func init() { + proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") + proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") +} + +func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) } + +var fileDescriptor_17b8c58d586b62f2 = []byte{ + // 175 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, + 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x42, 0x88, + 0x28, 0x29, 0x71, 0xf1, 0x78, 0x80, 0x78, 0x41, 0xa9, 0x85, 0xa5, 0xa9, 0xc5, 0x25, 0x42, 0x42, + 0x5c, 0x2c, 0x79, 0x89, 0xb9, 0xa9, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, 0x60, 0xb6, 0x92, + 0x1a, 0x17, 0x17, 0x54, 0x4d, 0x41, 0x4e, 0xa5, 0x90, 0x04, 0x17, 0x7b, 0x6e, 0x6a, 0x71, 0x71, + 0x62, 0x3a, 0x4c, 0x11, 0x8c, 0x6b, 0xe4, 0xc9, 0xc5, 0xee, 0x5e, 0x94, 0x9a, 0x5a, 0x92, 0x5a, + 0x24, 0x64, 0xc7, 0xc5, 0x11, 0x9c, 0x58, 0x09, 0xd6, 0x25, 0x24, 0xa1, 0x87, 0xe4, 0x02, 0x64, + 0xcb, 0xa4, 0xc4, 0xb0, 0xc8, 0x14, 0xe4, 0x54, 0x2a, 0x31, 0x38, 0x19, 0x70, 0x49, 0x67, 0xe6, + 0xeb, 0xa5, 0x17, 0x15, 0x24, 0xeb, 0xa5, 0x56, 0x24, 0xe6, 0x16, 0xe4, 0xa4, 0x16, 0x23, 0xa9, + 0x75, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x5e, 0x0a, 0x60, 0x4c, 0x62, 0x03, 0xfb, + 0xcd, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x0f, 0xb7, 0xcd, 0xf2, 0xef, 0x00, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// GreeterClient is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GreeterClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) +} + +type greeterClient struct { + cc *grpc.ClientConn +} + +func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +type GreeterServer interface { + // Sends a greeting + SayHello(context.Context, *HelloRequest) (*HelloReply, error) +} + +// UnimplementedGreeterServer can be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} + +func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/helloworld.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "helloworld.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "helloworld.proto", +} diff --git a/protocol/grpc/internal/server.go b/protocol/grpc/internal/server.go new file mode 100644 index 0000000000000000000000000000000000000000..a0759f757dc44153e7f09b726db5e66176796c96 --- /dev/null +++ b/protocol/grpc/internal/server.go @@ -0,0 +1,66 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package internal + +import ( + "context" + "log" + "net" +) + +import ( + "google.golang.org/grpc" +) + +var ( + s *grpc.Server +) + +// server is used to implement helloworld.GreeterServer. +type server struct { + UnimplementedGreeterServer +} + +// SayHello implements helloworld.GreeterServer +func (s *server) SayHello(ctx context.Context, in *HelloRequest) (*HelloReply, error) { + log.Printf("Received: %v", in.GetName()) + return &HelloReply{Message: "Hello " + in.GetName()}, nil +} + +// InitGrpcServer ... +func InitGrpcServer() { + port := ":30000" + + lis, err := net.Listen("tcp", port) + if err != nil { + log.Fatalf("failed to listen: %v", err) + } + s = grpc.NewServer() + RegisterGreeterServer(s, &server{}) + if err := s.Serve(lis); err != nil { + log.Fatalf("failed to serve: %v", err) + } +} + +// ShutdownGrpcServer ... +func ShutdownGrpcServer() { + if s == nil { + return + } + s.GracefulStop() +} diff --git a/protocol/grpc/protoc-gen-dubbo/examples/Makefile b/protocol/grpc/protoc-gen-dubbo/examples/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7893bbc51aa436f711bfb653dc81d9ec66b7e5c0 --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/examples/Makefile @@ -0,0 +1,19 @@ +# 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. + +grpc-gen: + protoc -I ./ helloworld.proto --go_out=plugins=grpc:. +dubbo-gen: + protoc -I ./ helloworld.proto --dubbo_out=plugins=grpc+dubbo:. diff --git a/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go new file mode 100644 index 0000000000000000000000000000000000000000..f5d3a49b0916050fc6b2e6373fde0b70df0a1c31 --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.pb.go @@ -0,0 +1,301 @@ +/* +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. +*/ + +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: helloworld.proto + +package main + +import ( + "context" + "fmt" + "math" + + "github.com/golang/protobuf/proto" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +import ( + "github.com/apache/dubbo-go/protocol" + dgrpc "github.com/apache/dubbo-go/protocol/grpc" + "github.com/apache/dubbo-go/protocol/invocation" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +// The request message containing the user's name. +type HelloRequest struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HelloRequest) Reset() { *m = HelloRequest{} } +func (m *HelloRequest) String() string { return proto.CompactTextString(m) } +func (*HelloRequest) ProtoMessage() {} +func (*HelloRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_17b8c58d586b62f2, []int{0} +} + +func (m *HelloRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HelloRequest.Unmarshal(m, b) +} +func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) +} +func (m *HelloRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_HelloRequest.Merge(m, src) +} +func (m *HelloRequest) XXX_Size() int { + return xxx_messageInfo_HelloRequest.Size(m) +} +func (m *HelloRequest) XXX_DiscardUnknown() { + xxx_messageInfo_HelloRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_HelloRequest proto.InternalMessageInfo + +func (m *HelloRequest) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +// The response message containing the greetings +type HelloReply struct { + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *HelloReply) Reset() { *m = HelloReply{} } +func (m *HelloReply) String() string { return proto.CompactTextString(m) } +func (*HelloReply) ProtoMessage() {} +func (*HelloReply) Descriptor() ([]byte, []int) { + return fileDescriptor_17b8c58d586b62f2, []int{1} +} + +func (m *HelloReply) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_HelloReply.Unmarshal(m, b) +} +func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) +} +func (m *HelloReply) XXX_Merge(src proto.Message) { + xxx_messageInfo_HelloReply.Merge(m, src) +} +func (m *HelloReply) XXX_Size() int { + return xxx_messageInfo_HelloReply.Size(m) +} +func (m *HelloReply) XXX_DiscardUnknown() { + xxx_messageInfo_HelloReply.DiscardUnknown(m) +} + +var xxx_messageInfo_HelloReply proto.InternalMessageInfo + +func (m *HelloReply) GetMessage() string { + if m != nil { + return m.Message + } + return "" +} + +func init() { + proto.RegisterType((*HelloRequest)(nil), "main.HelloRequest") + proto.RegisterType((*HelloReply)(nil), "main.HelloReply") +} + +func init() { proto.RegisterFile("helloworld.proto", fileDescriptor_17b8c58d586b62f2) } + +var fileDescriptor_17b8c58d586b62f2 = []byte{ + // 185 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xc8, 0x48, 0xcd, 0xc9, + 0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, + 0xcc, 0xcc, 0x53, 0x52, 0xe2, 0xe2, 0xf1, 0x00, 0xc9, 0x04, 0xa5, 0x16, 0x96, 0xa6, 0x16, 0x97, + 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x06, 0x81, + 0xd9, 0x4a, 0x6a, 0x5c, 0x5c, 0x50, 0x35, 0x05, 0x39, 0x95, 0x42, 0x12, 0x5c, 0xec, 0xb9, 0xa9, + 0xc5, 0xc5, 0x89, 0xe9, 0x30, 0x45, 0x30, 0xae, 0x91, 0x2d, 0x17, 0xbb, 0x7b, 0x51, 0x6a, 0x6a, + 0x49, 0x6a, 0x91, 0x90, 0x11, 0x17, 0x47, 0x70, 0x62, 0x25, 0x58, 0x97, 0x90, 0x90, 0x1e, 0xc8, + 0x26, 0x3d, 0x64, 0x6b, 0xa4, 0x04, 0x50, 0xc4, 0x0a, 0x72, 0x2a, 0x95, 0x18, 0x9c, 0xcc, 0xb8, + 0xa4, 0x33, 0xf3, 0xf5, 0xd2, 0x8b, 0x0a, 0x92, 0xf5, 0x52, 0x2b, 0x12, 0x73, 0x0b, 0x72, 0x52, + 0x8b, 0xf5, 0x10, 0xae, 0x76, 0xe2, 0x07, 0x2b, 0x0e, 0x07, 0xb1, 0x03, 0x40, 0x1e, 0x08, 0x60, + 0x5c, 0xc4, 0xc4, 0xec, 0xe1, 0x13, 0x9e, 0xc4, 0x06, 0xf6, 0x8f, 0x31, 0x20, 0x00, 0x00, 0xff, + 0xff, 0xd2, 0x16, 0x5f, 0x34, 0xe3, 0x00, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// GreeterClient is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GreeterClient interface { + // Sends a greeting + SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) +} + +type greeterClient struct { + cc *grpc.ClientConn +} + +func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { + return &greeterClient{cc} +} + +func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { + out := new(HelloReply) + err := c.cc.Invoke(ctx, "/main.Greeter/SayHello", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// GreeterServer is the server API for Greeter service. +type GreeterServer interface { + // Sends a greeting + SayHello(context.Context, *HelloRequest) (*HelloReply, error) +} + +// UnimplementedGreeterServer can be embedded to have forward compatible implementations. +type UnimplementedGreeterServer struct { +} + +func (*UnimplementedGreeterServer) SayHello(ctx context.Context, req *HelloRequest) (*HelloReply, error) { + return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented") +} + +func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { + s.RegisterService(&_Greeter_serviceDesc, srv) +} + +func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(GreeterServer).SayHello(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/main.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Greeter_serviceDesc = grpc.ServiceDesc{ + ServiceName: "main.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "helloworld.proto", +} + +// GreeterClientImpl is the client API for Greeter service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type GreeterClientImpl struct { + // Sends a greeting + SayHello func(ctx context.Context, in *HelloRequest, out *HelloReply) error +} + +func (c *GreeterClientImpl) Reference() string { + return "greeterImpl" +} + +func (c *GreeterClientImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient { + return NewGreeterClient(cc) +} + +type GreeterProviderBase struct { + proxyImpl protocol.Invoker +} + +func (s *GreeterProviderBase) SetProxyImpl(impl protocol.Invoker) { + s.proxyImpl = impl +} + +func (s *GreeterProviderBase) GetProxyImpl() protocol.Invoker { + return s.proxyImpl +} + +func _DUBBO_Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(HelloRequest) + if err := dec(in); err != nil { + return nil, err + } + base := srv.(dgrpc.DubboGrpcService) + args := []interface{}{} + args = append(args, in) + invo := invocation.NewRPCInvocation("SayHello", args, nil) + if interceptor == nil { + result := base.GetProxyImpl().Invoke(context.Background(), invo) + return result.Result(), result.Error() + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/main.Greeter/SayHello", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + result := base.GetProxyImpl().Invoke(context.Background(), invo) + return result.Result(), result.Error() + } + return interceptor(ctx, in, info, handler) +} + +func (s *GreeterProviderBase) ServiceDesc() *grpc.ServiceDesc { + return &grpc.ServiceDesc{ + ServiceName: "main.Greeter", + HandlerType: (*GreeterServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "SayHello", + Handler: _DUBBO_Greeter_SayHello_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "helloworld.proto", + } +} diff --git a/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto new file mode 100644 index 0000000000000000000000000000000000000000..d68e1dd37b3276760623d214662854e931bbdd09 --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/examples/helloworld.proto @@ -0,0 +1,37 @@ +// Copyright 2015 The gRPC Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "io.grpc.examples.helloworld"; +option java_outer_classname = "HelloWorldProto"; +option objc_class_prefix = "HLW"; + +package main; + +// The greeting service definition. +service Greeter { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} diff --git a/protocol/grpc/protoc-gen-dubbo/main.go b/protocol/grpc/protoc-gen-dubbo/main.go new file mode 100644 index 0000000000000000000000000000000000000000..b2f0e82f74a4d3c1a7013714cd18d83562baff71 --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/main.go @@ -0,0 +1,74 @@ +/* +Licensed to the Apache Software Foundation (ASF) under one or more +contributor license agreements. See the NOTICE file distributed with +this work for additional information regarding copyright ownership. +The ASF licenses this file to You under the Apache License, Version 2.0 +(the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "io/ioutil" + "os" +) + +import ( + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/protoc-gen-go/generator" + _ "github.com/golang/protobuf/protoc-gen-go/grpc" +) + +import ( + _ "github.com/apache/dubbo-go/protocol/grpc/protoc-gen-dubbo/plugin/dubbo" +) + +func main() { + // Begin by allocating a generate. The request and response structures are stored there + // so we can do error handling easily - the response structure contains the field to + // report failure. + g := generator.New() + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + g.Error(err, "reading input") + } + + if err := proto.Unmarshal(data, g.Request); err != nil { + g.Error(err, "parsing input proto") + } + + if len(g.Request.FileToGenerate) == 0 { + g.Fail("no files to generate") + } + + g.CommandLineParameters(g.Request.GetParameter()) + + // Create a wrapped version of the Descriptors and EnumDescriptors that + // point to the file that defines them. + g.WrapTypes() + + g.SetPackageNames() + g.BuildTypeNameMap() + + g.GenerateAllFiles() + + // Send back the results. + data, err = proto.Marshal(g.Response) + if err != nil { + g.Error(err, "failed to marshal output proto") + } + _, err = os.Stdout.Write(data) + if err != nil { + g.Error(err, "failed to write output proto") + } +} diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go new file mode 100644 index 0000000000000000000000000000000000000000..064c738a53d2200223b0ca81aca77358afad032b --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/doc.go @@ -0,0 +1,19 @@ +/* +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 dubbo plugin for protobuf. +package dubbo diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go new file mode 100644 index 0000000000000000000000000000000000000000..e84a7d0cc96887cf728f499c28c26f061ed1ccdf --- /dev/null +++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go @@ -0,0 +1,346 @@ +/* +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 dubbo + +import ( + "fmt" + "strconv" + "strings" +) + +import ( + pb "github.com/golang/protobuf/protoc-gen-go/descriptor" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +// generatedCodeVersion indicates a version of the generated code. +// It is incremented whenever an incompatibility between the generated code and +// the grpc package is introduced; the generated code references +// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion). +const generatedCodeVersion = 4 + +// Paths for packages used by code generated in this file, +// relative to the import_prefix of the generator.Generator. +const ( + contextPkgPath = "context" + grpcPkgPath = "google.golang.org/grpc" + codePkgPath = "google.golang.org/grpc/codes" + statusPkgPath = "google.golang.org/grpc/status" +) + +func init() { + generator.RegisterPlugin(new(dubboGrpc)) +} + +// grpc is an implementation of the Go protocol buffer compiler's +// plugin architecture. It generates bindings for gRPC-dubbo support. +type dubboGrpc struct { + gen *generator.Generator +} + +// Name returns the name of this plugin, "grpc". +func (g *dubboGrpc) Name() string { + return "dubbo" +} + +// The names for packages imported in the generated code. +// They may vary from the final path component of the import path +// if the name is used by other packages. +var ( + contextPkg string + grpcPkg string +) + +// Init initializes the plugin. +func (g *dubboGrpc) Init(gen *generator.Generator) { + g.gen = gen +} + +// Given a type name defined in a .proto, return its object. +// Also record that we're using it, to guarantee the associated import. +func (g *dubboGrpc) objectNamed(name string) generator.Object { + g.gen.RecordTypeUse(name) + return g.gen.ObjectNamed(name) +} + +// Given a type name defined in a .proto, return its name as we will print it. +func (g *dubboGrpc) typeName(str string) string { + return g.gen.TypeName(g.objectNamed(str)) +} + +// P forwards to g.gen.P. +func (g *dubboGrpc) P(args ...interface{}) { g.gen.P(args...) } + +// Generate generates code for the services in the given file. +// be consistent with grpc plugin +func (g *dubboGrpc) Generate(file *generator.FileDescriptor) { + if len(file.FileDescriptorProto.Service) == 0 { + return + } + + contextPkg = string(g.gen.AddImport(contextPkgPath)) + grpcPkg = string(g.gen.AddImport(grpcPkgPath)) + + for i, service := range file.FileDescriptorProto.Service { + g.generateService(file, service, i) + } +} + +// GenerateImports generates the import declaration for this file. +func (g *dubboGrpc) GenerateImports(file *generator.FileDescriptor) { + g.P("import (") + 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(` ) `) +} + +func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] } + +// deprecationComment is the standard comment added to deprecated +// messages, fields, enums, and enum values. +var deprecationComment = "// Deprecated: Do not use." + +// generateService generates all the code for the named service. +func (g *dubboGrpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) { + path := fmt.Sprintf("6,%d", index) // 6 means service. + + origServName := service.GetName() + fullServName := origServName + if pkg := file.GetPackage(); pkg != "" { + fullServName = pkg + "." + fullServName + } + servName := generator.CamelCase(origServName) + deprecated := service.GetOptions().GetDeprecated() + + g.P() + g.P(fmt.Sprintf(`// %sClientImpl is the client API for %s service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.`, servName, servName)) + + // Client interface. + if deprecated { + g.P("//") + g.P(deprecationComment) + } + dubboSrvName := servName + "ClientImpl" + g.P("type ", dubboSrvName, " struct {") + for i, method := range service.Method { + g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service. + if method.GetOptions().GetDeprecated() { + g.P("//") + g.P(deprecationComment) + } + g.P(g.generateClientSignature(servName, method)) + } + g.P("}") + g.P() + + // NewClient factory. + if deprecated { + g.P(deprecationComment) + } + + // add Reference method + //func (u *GrpcGreeterImpl) Reference() string { + // return "GrpcGreeterImpl" + //} + g.P("func (c *", dubboSrvName, ") ", " Reference() string ", "{") + g.P(`return "`, unexport(servName), `Impl"`) + g.P("}") + g.P() + + // add GetDubboStub method + // func (u *GrpcGreeterImpl) GetDubboStub(cc *grpc.ClientConn) GreeterClient { + // return NewGreeterClient(cc) + //} + g.P("func (c *", dubboSrvName, ") ", " GetDubboStub(cc *grpc.ClientConn) ", servName, "Client {") + g.P(`return New`, servName, `Client(cc)`) + g.P("}") + g.P() + + // Server interface. + serverType := servName + "ProviderBase" + g.P("type ", serverType, " struct {") + g.P("proxyImpl protocol.Invoker") + g.P("}") + g.P() + + // add set method + //func (g *GreeterProviderBase) SetProxyImpl(impl protocol.Invoker) { + // g.proxyImpl = impl + //} + g.P("func (s *", serverType, ") SetProxyImpl(impl protocol.Invoker) {") + g.P(`s.proxyImpl = impl`) + g.P("}") + g.P() + + // return get method + g.P("func (s *", serverType, ") GetProxyImpl() protocol.Invoker {") + g.P(`return s.proxyImpl`) + g.P("}") + g.P() + + // add handler + var handlerNames []string + for _, method := range service.Method { + hname := g.generateServerMethod(servName, fullServName, method) + handlerNames = append(handlerNames, hname) + } + + grpcserverType := servName + "Server" + // return service desc + g.P("func (s *", serverType, ") ServiceDesc() *grpc.ServiceDesc {") + g.P(`return &grpc.ServiceDesc{`) + g.P("ServiceName: ", strconv.Quote(fullServName), ",") + g.P("HandlerType: (*", grpcserverType, ")(nil),") + g.P("Methods: []", grpcPkg, ".MethodDesc{") + for i, method := range service.Method { + if method.GetServerStreaming() || method.GetClientStreaming() { + continue + } + g.P("{") + g.P("MethodName: ", strconv.Quote(method.GetName()), ",") + g.P("Handler: ", handlerNames[i], ",") + g.P("},") + } + g.P("},") + g.P("Streams: []", grpcPkg, ".StreamDesc{},") + g.P("Metadata: \"", file.GetName(), "\",") + g.P("}") + g.P("}") + g.P() +} + +// generateClientSignature returns the client-side signature for a method. +func (g *dubboGrpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string { + origMethName := method.GetName() + methName := generator.CamelCase(origMethName) + //if reservedClientName[methName] { + // methName += "_" + //} + reqArg := ", in *" + g.typeName(method.GetInputType()) + if method.GetClientStreaming() { + reqArg = "" + } + respName := "out *" + g.typeName(method.GetOutputType()) + if method.GetServerStreaming() || method.GetClientStreaming() { + respName = servName + "_" + generator.CamelCase(origMethName) + "Client" + } + return fmt.Sprintf("%s func(ctx %s.Context%s, %s) error", methName, contextPkg, reqArg, respName) +} + +func (g *dubboGrpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { +} + +func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string { + methName := generator.CamelCase(method.GetName()) + hname := fmt.Sprintf("_DUBBO_%s_%s_Handler", servName, methName) + inType := g.typeName(method.GetInputType()) + outType := g.typeName(method.GetOutputType()) + + if !method.GetServerStreaming() && !method.GetClientStreaming() { + g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {") + g.P("in := new(", inType, ")") + g.P("if err := dec(in); err != nil { return nil, err }") + + g.P("base := srv.(dgrpc.DubboGrpcService)") + g.P("args := []interface{}{}") + g.P("args = append(args, in)") + g.P(`invo := invocation.NewRPCInvocation("`, methName, `", args, nil)`) + + g.P("if interceptor == nil {") + g.P("result := base.GetProxyImpl().Invoke(invo)") + g.P("return result.Result(), result.Error()") + g.P("}") + + g.P("info := &", grpcPkg, ".UnaryServerInfo{") + g.P("Server: srv,") + g.P("FullMethod: ", strconv.Quote(fmt.Sprintf("/%s/%s", fullServName, methName)), ",") + g.P("}") + + g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {") + g.P("result := base.GetProxyImpl().Invoke(invo)") + g.P("return result.Result(), result.Error()") + g.P("}") + + g.P("return interceptor(ctx, in, info, handler)") + g.P("}") + g.P() + return hname + } + streamType := unexport(servName) + methName + "Server" + g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {") + if !method.GetClientStreaming() { + g.P("m := new(", inType, ")") + g.P("if err := stream.RecvMsg(m); err != nil { return err }") + g.P("return srv.(", servName, "Server).", methName, "(m, &", streamType, "{stream})") + } else { + g.P("return srv.(", servName, "Server).", methName, "(&", streamType, "{stream})") + } + g.P("}") + g.P() + + genSend := method.GetServerStreaming() + genSendAndClose := !method.GetServerStreaming() + genRecv := method.GetClientStreaming() + + // Stream auxiliary types and methods. + g.P("type ", servName, "_", methName, "Server interface {") + if genSend { + g.P("Send(*", outType, ") error") + } + if genSendAndClose { + g.P("SendAndClose(*", outType, ") error") + } + if genRecv { + g.P("Recv() (*", inType, ", error)") + } + g.P(grpcPkg, ".ServerStream") + g.P("}") + g.P() + + g.P("type ", streamType, " struct {") + g.P(grpcPkg, ".ServerStream") + g.P("}") + g.P() + + if genSend { + g.P("func (x *", streamType, ") Send(m *", outType, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genSendAndClose { + g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {") + g.P("return x.ServerStream.SendMsg(m)") + g.P("}") + g.P() + } + if genRecv { + g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {") + g.P("m := new(", inType, ")") + g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }") + g.P("return m, nil") + g.P("}") + g.P() + } + + return hname +} diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go new file mode 100644 index 0000000000000000000000000000000000000000..19b9db4ac743ceefcf035d399c0bbcdd99f1fa80 --- /dev/null +++ b/protocol/grpc/server.go @@ -0,0 +1,106 @@ +/* +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 ( + "fmt" + "net" + "reflect" +) + +import ( + "google.golang.org/grpc" +) + +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" + "github.com/apache/dubbo-go/protocol" +) + +// Server ... +type Server struct { + grpcServer *grpc.Server +} + +// NewServer ... +func NewServer() *Server { + return &Server{} +} + +// DubboGrpcService ... +type DubboGrpcService interface { + SetProxyImpl(impl protocol.Invoker) + GetProxyImpl() protocol.Invoker + ServiceDesc() *grpc.ServiceDesc +} + +// Start ... +func (s *Server) Start(url common.URL) { + var ( + addr string + err error + ) + addr = url.Location + lis, err := net.Listen("tcp", addr) + if err != nil { + panic(err) + } + server := grpc.NewServer() + + key := url.GetParam(constant.BEAN_NAME_KEY, "") + service := config.GetProviderService(key) + + ds, ok := service.(DubboGrpcService) + if !ok { + panic("illegal service type registered") + } + + m, ok := reflect.TypeOf(service).MethodByName("SetProxyImpl") + if !ok { + panic("method SetProxyImpl is necessary for grpc service") + } + + exporter, _ := grpcProtocol.ExporterMap().Load(url.ServiceKey()) + if exporter == nil { + panic(fmt.Sprintf("no exporter found for servicekey: %v", url.ServiceKey())) + } + invoker := exporter.(protocol.Exporter).GetInvoker() + if invoker == nil { + panic(fmt.Sprintf("no invoker found for servicekey: %v", url.ServiceKey())) + } + in := []reflect.Value{reflect.ValueOf(service)} + in = append(in, reflect.ValueOf(invoker)) + m.Func.Call(in) + + server.RegisterService(ds.ServiceDesc(), service) + + s.grpcServer = server + go func() { + if err = server.Serve(lis); err != nil { + logger.Errorf("server serve failed with err: %v", err) + } + }() +} + +// Stop ... +func (s *Server) Stop() { + s.grpcServer.Stop() +} diff --git a/protocol/invocation.go b/protocol/invocation.go index 055e7a4cd18707772d6ba75303053f15dc55dbe3..f32f2c3449ac063ecb89952bd4653312a07a3df4 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -21,9 +21,11 @@ import ( "reflect" ) +// Invocation ... type Invocation interface { MethodName() string ParameterTypes() []reflect.Type + ParameterValues() []reflect.Value Arguments() []interface{} Reply() interface{} Attachments() map[string]string diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index 2124a22f1611b24d7f4370de64b117c58c4f7e7b..b207fd0b0cc4eb7de8409a8c46c6fc9ef0baa5c7 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -19,26 +19,32 @@ package invocation import ( "reflect" + "sync" ) import ( "github.com/apache/dubbo-go/protocol" ) -///////////////////////////// +// /////////////////////////// // Invocation Impletment of RPC -///////////////////////////// +// /////////////////////////// + // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation +// RPCInvocation ... type RPCInvocation struct { - methodName string - parameterTypes []reflect.Type - arguments []interface{} - reply interface{} - callBack interface{} - attachments map[string]string - invoker protocol.Invoker -} - + methodName string + parameterTypes []reflect.Type + parameterValues []reflect.Value + arguments []interface{} + reply interface{} + callBack interface{} + attachments map[string]string + invoker protocol.Invoker + lock sync.RWMutex +} + +// NewRPCInvocation ... func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation { return &RPCInvocation{ methodName: methodName, @@ -47,6 +53,7 @@ func NewRPCInvocation(methodName string, arguments []interface{}, attachments ma } } +// NewRPCInvocationWithOptions ... func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { invo := &RPCInvocation{} for _, opt := range opts { @@ -55,31 +62,45 @@ func NewRPCInvocationWithOptions(opts ...option) *RPCInvocation { return invo } +// MethodName ... func (r *RPCInvocation) MethodName() string { return r.methodName } +// ParameterTypes ... func (r *RPCInvocation) ParameterTypes() []reflect.Type { return r.parameterTypes } +// ParameterValues ... +func (r *RPCInvocation) ParameterValues() []reflect.Value { + return r.parameterValues +} + +// Arguments ... func (r *RPCInvocation) Arguments() []interface{} { return r.arguments } +// Reply ... func (r *RPCInvocation) Reply() interface{} { return r.reply } +// SetReply ... func (r *RPCInvocation) SetReply(reply interface{}) { r.reply = reply } +// Attachments ... func (r *RPCInvocation) Attachments() map[string]string { return r.attachments } +// AttachmentsByKey ... func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string { + r.lock.RLock() + defer r.lock.RUnlock() if r.attachments == nil { return defaultValue } @@ -90,71 +111,92 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string return defaultValue } +// SetAttachments ... func (r *RPCInvocation) SetAttachments(key string, value string) { + r.lock.Lock() + defer r.lock.Unlock() if r.attachments == nil { r.attachments = make(map[string]string) } r.attachments[key] = value } +// Invoker ... func (r *RPCInvocation) Invoker() protocol.Invoker { return r.invoker } +// SetInvoker ... func (r *RPCInvocation) SetInvoker() protocol.Invoker { return r.invoker } +// CallBack ... func (r *RPCInvocation) CallBack() interface{} { return r.callBack } +// SetCallBack ... func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } -/////////////////////////// +// ///////////////////////// // option -/////////////////////////// +// ///////////////////////// type option func(invo *RPCInvocation) +// WithMethodName ... func WithMethodName(methodName string) option { return func(invo *RPCInvocation) { invo.methodName = methodName } } +// WithParameterTypes ... func WithParameterTypes(parameterTypes []reflect.Type) option { return func(invo *RPCInvocation) { invo.parameterTypes = parameterTypes } } +// WithParameterValues ... +func WithParameterValues(parameterValues []reflect.Value) option { + return func(invo *RPCInvocation) { + invo.parameterValues = parameterValues + } +} + +// WithArguments ... func WithArguments(arguments []interface{}) option { return func(invo *RPCInvocation) { invo.arguments = arguments } } +// WithReply ... func WithReply(reply interface{}) option { return func(invo *RPCInvocation) { invo.reply = reply } } +// WithCallBack ... func WithCallBack(callBack interface{}) option { return func(invo *RPCInvocation) { invo.callBack = callBack } } +// WithAttachments ... func WithAttachments(attachments map[string]string) option { return func(invo *RPCInvocation) { invo.attachments = attachments } } +// WithInvoker ... func WithInvoker(invoker protocol.Invoker) option { return func(invo *RPCInvocation) { invo.invoker = invoker diff --git a/protocol/invoker.go b/protocol/invoker.go index f5d41a09ad2778c12c7e5e68167a4d0acc9e3f4c..bb71bab1cfa2ede7fb035912ae996f9adb7411e0 100644 --- a/protocol/invoker.go +++ b/protocol/invoker.go @@ -17,28 +17,35 @@ package protocol +import ( + "context" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" ) +// Invoker ... //go:generate mockgen -source invoker.go -destination mock/mock_invoker.go -self_package github.com/apache/dubbo-go/protocol/mock --package mock Invoker // Extension - Invoker type Invoker interface { common.Node - Invoke(Invocation) Result + Invoke(context.Context, Invocation) Result } ///////////////////////////// // base invoker ///////////////////////////// +// BaseInvoker ... type BaseInvoker struct { url common.URL available bool destroyed bool } +// NewBaseInvoker ... func NewBaseInvoker(url common.URL) *BaseInvoker { return &BaseInvoker{ url: url, @@ -47,22 +54,27 @@ func NewBaseInvoker(url common.URL) *BaseInvoker { } } +// GetUrl ... func (bi *BaseInvoker) GetUrl() common.URL { return bi.url } +// IsAvailable ... func (bi *BaseInvoker) IsAvailable() bool { return bi.available } +// IsDestroyed ... func (bi *BaseInvoker) IsDestroyed() bool { return bi.destroyed } -func (bi *BaseInvoker) Invoke(invocation Invocation) Result { +// Invoke ... +func (bi *BaseInvoker) Invoke(context context.Context, invocation Invocation) Result { return &RPCResult{} } +// Destroy ... 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 3d99786624c71818cc5f787c8695d1c116c35707..ba7197dbc857c2ed7acda1a9f246a5b826e86915 100644 --- a/protocol/jsonrpc/http.go +++ b/protocol/jsonrpc/http.go @@ -33,18 +33,21 @@ import ( ) import ( + "github.com/opentracing/opentracing-go" 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/logger" ) -////////////////////////////////////////////// +// //////////////////////////////////////////// // Request -////////////////////////////////////////////// +// //////////////////////////////////////////// +// Request ... type Request struct { ID int64 group string @@ -56,10 +59,11 @@ type Request struct { contentType string } -////////////////////////////////////////////// +// //////////////////////////////////////////// // HTTP Client -////////////////////////////////////////////// +// //////////////////////////////////////////// +// HTTPOptions ... type HTTPOptions struct { HandshakeTimeout time.Duration HTTPTimeout time.Duration @@ -70,11 +74,13 @@ var defaultHTTPOptions = HTTPOptions{ HTTPTimeout: 3 * time.Second, } +// HTTPClient ... type HTTPClient struct { ID int64 options HTTPOptions } +// NewHTTPClient ... func NewHTTPClient(opt *HTTPOptions) *HTTPClient { if opt == nil { opt = &defaultHTTPOptions @@ -94,6 +100,7 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient { } } +// NewRequest ... func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request { return &Request{ @@ -107,6 +114,7 @@ func (c *HTTPClient) NewRequest(service common.URL, method string, args interfac } } +// Call ... func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error { // header httpHeader := http.Header{} @@ -124,6 +132,13 @@ func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, } } + if span := opentracing.SpanFromContext(ctx); span != nil { + err := opentracing.GlobalTracer().Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(httpHeader)) + if err != nil { + logger.Error("Could not inject the Context into http header.") + } + } + // body codec := newJsonClientCodec() codecData := CodecData{ @@ -144,6 +159,7 @@ func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, return perrors.WithStack(codec.Read(rspBody, rsp)) } +// Do // !!The high level of complexity and the likelihood that the fasthttp client has not been extensively used // in production means that you would need to expect a very large benefit to justify the adoption of fasthttp today. func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) ([]byte, error) { diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index 9be55e247a730460a3adee5622fa978ef2defbfb..0cb88b36a8f330059906eb70417b6d4841020c38 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -25,6 +25,7 @@ import ( ) import ( + "github.com/opentracing/opentracing-go" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -55,10 +56,10 @@ func TestHTTPClient_Call(t *testing.T) { // Export proto := GetProtocol() - url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider") assert.NoError(t, err) proto.Export(&proxy_factory.ProxyInvoker{ @@ -74,6 +75,7 @@ func TestHTTPClient_Call(t *testing.T) { "X-Services": url.Path, "X-Method": "GetUser", }) + req := client.NewRequest(url, "GetUser", []interface{}{"1", "username"}) reply := &User{} err = client.Call(ctx, url, req, reply) @@ -147,6 +149,10 @@ func TestHTTPClient_Call(t *testing.T) { "X-Services": url.Path, "X-Method": "GetUser4", }) + + span := opentracing.StartSpan("Test-Inject-Tracing-ID") + ctx = opentracing.ContextWithSpan(ctx, span) + req = client.NewRequest(url, "GetUser4", []interface{}{1}) reply = &User{} err = client.Call(ctx, url, req, reply) diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go index 7ee454e8ad16d2ee96ed08e7e5f55b2209a81054..d1c2a858b4e4223ac32fc1160b56f6ee1862c8ce 100644 --- a/protocol/jsonrpc/json.go +++ b/protocol/jsonrpc/json.go @@ -31,10 +31,13 @@ import ( ) const ( + // MAX_JSONRPC_ID max jsonrpc request/response id MAX_JSONRPC_ID = 0x7FFFFFFF - VERSION = "2.0" + // VERSION jsonrpc version + VERSION = "2.0" ) +// CodecData ... type CodecData struct { ID int64 Method string @@ -54,7 +57,7 @@ const ( codeServerErrorEnd = -32000 ) -// rsponse Error +// Error response Error type Error struct { Code int `json:"code"` Message string `json:"message"` @@ -278,12 +281,14 @@ type serverResponse struct { Error interface{} `json:"error,omitempty"` } +// ServerCodec ... type ServerCodec struct { req serverRequest } var ( - null = json.RawMessage([]byte("null")) + null = json.RawMessage([]byte("null")) + // Version ... Version = "2.0" ) @@ -291,6 +296,7 @@ func newServerCodec() *ServerCodec { return &ServerCodec{} } +// ReadHeader ... func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error { if header["HttpMethod"] != "POST" { return &Error{Code: -32601, Message: "Method not found"} @@ -322,6 +328,7 @@ func (c *ServerCodec) ReadHeader(header map[string]string, body []byte) error { return nil } +// ReadBody ... func (c *ServerCodec) ReadBody(x interface{}) error { // If x!=nil and return error e: // - Write() will be called with e.Error() in r.Error @@ -355,6 +362,7 @@ func (c *ServerCodec) ReadBody(x interface{}) error { return nil } +// NewError ... func NewError(code int, message string) *Error { return &Error{Code: code, Message: message} } diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go index 6720330494a3b833d4a67d8b2408377ce62b1ddf..7f8fd491854f1ab25e63410a22ef5664db92f614 100644 --- a/protocol/jsonrpc/jsonrpc_exporter.go +++ b/protocol/jsonrpc/jsonrpc_exporter.go @@ -28,16 +28,19 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// JsonrpcExporter ... type JsonrpcExporter struct { protocol.BaseExporter } +// NewJsonrpcExporter ... func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map) *JsonrpcExporter { return &JsonrpcExporter{ BaseExporter: *protocol.NewBaseExporter(key, invoker, exporterMap), } } +// Unexport ... func (je *JsonrpcExporter) Unexport() { serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") je.BaseExporter.Unexport() diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index 2c130e0d7617e96a1724edc5b63f8e66f251446e..b6e194ce0e93e84c164eccf8574e5eb20430f6e8 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -29,11 +29,13 @@ import ( invocation_impl "github.com/apache/dubbo-go/protocol/invocation" ) +// JsonrpcInvoker ... type JsonrpcInvoker struct { protocol.BaseInvoker client *HTTPClient } +// NewJsonrpcInvoker ... func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { return &JsonrpcInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), @@ -41,7 +43,8 @@ func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { } } -func (ji *JsonrpcInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +// Invoke ... +func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( result protocol.RPCResult @@ -50,12 +53,12 @@ func (ji *JsonrpcInvoker) Invoke(invocation protocol.Invocation) protocol.Result inv := invocation.(*invocation_impl.RPCInvocation) url := ji.GetUrl() req := ji.client.NewRequest(url, inv.MethodName(), inv.Arguments()) - ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ + ctxNew := context.WithValue(ctx, constant.DUBBOGO_CTX_KEY, map[string]string{ "X-Proxy-Id": "dubbogo", "X-Services": url.Path, "X-Method": inv.MethodName(), }) - result.Err = ji.client.Call(ctx, url, req, inv.Reply()) + result.Err = ji.client.Call(ctxNew, url, req, inv.Reply()) if result.Err == nil { result.Rest = inv.Reply() } diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go index 8c910339858f4960ad0e394ae6271863d7654adc..9e08eed2b4c61e686073a9039a605c4f73aa08c5 100644 --- a/protocol/jsonrpc/jsonrpc_invoker_test.go +++ b/protocol/jsonrpc/jsonrpc_invoker_test.go @@ -42,10 +42,10 @@ func TestJsonrpcInvoker_Invoke(t *testing.T) { // Export proto := GetProtocol() - url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245&bean.name=UserProvider") assert.NoError(t, err) proto.Export(&proxy_factory.ProxyInvoker{ @@ -60,7 +60,7 @@ func TestJsonrpcInvoker_Invoke(t *testing.T) { jsonInvoker := NewJsonrpcInvoker(url, client) user := &User{} - res := jsonInvoker.Invoke(invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}), + res := jsonInvoker.Invoke(context.Background(), invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}), invocation.WithReply(user))) assert.NoError(t, res.Error()) diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index c18345d413edb2d263f1acaef1741514b665f042..bed7099ab60a6c05c3799f993c0bb348a4b00f02 100644 --- a/protocol/jsonrpc/jsonrpc_protocol.go +++ b/protocol/jsonrpc/jsonrpc_protocol.go @@ -20,17 +20,23 @@ package jsonrpc import ( "strings" "sync" + "time" ) 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/protocol" ) -const JSONRPC = "jsonrpc" +const ( + // JSONRPC + //module name + JSONRPC = "jsonrpc" +) func init() { extension.SetProtocol(JSONRPC, GetProtocol) @@ -38,12 +44,14 @@ func init() { var jsonrpcProtocol *JsonrpcProtocol +// JsonrpcProtocol ... type JsonrpcProtocol struct { protocol.BaseProtocol serverMap map[string]*Server serverLock sync.Mutex } +// NewJsonrpcProtocol ... func NewJsonrpcProtocol() *JsonrpcProtocol { return &JsonrpcProtocol{ BaseProtocol: protocol.NewBaseProtocol(), @@ -51,6 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol { } } +// Export ... func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := strings.TrimPrefix(url.Path, "/") @@ -65,16 +74,26 @@ func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } +// Refer ... func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker { + //default requestTimeout + var requestTimeout = config.GetConsumerConfig().RequestTimeout + + requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) + if t, err := time.ParseDuration(requestTimeoutStr); err == nil { + requestTimeout = t + } + invoker := NewJsonrpcInvoker(url, NewHTTPClient(&HTTPOptions{ HandshakeTimeout: config.GetConsumerConfig().ConnectTimeout, - HTTPTimeout: config.GetConsumerConfig().RequestTimeout, + HTTPTimeout: requestTimeout, })) jp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker } +// Destroy ... func (jp *JsonrpcProtocol) Destroy() { logger.Infof("jsonrpcProtocol destroy.") @@ -106,6 +125,7 @@ func (jp *JsonrpcProtocol) openServer(url common.URL) { } } +// GetProtocol ... func GetProtocol() protocol.Protocol { if jsonrpcProtocol == nil { jsonrpcProtocol = NewJsonrpcProtocol() diff --git a/protocol/jsonrpc/jsonrpc_protocol_test.go b/protocol/jsonrpc/jsonrpc_protocol_test.go index 253ab830dd85e5424811b7fd4e7e7e848adad415..c00bed12fe9fbb4937f21810cee548a25e3b1c05 100644 --- a/protocol/jsonrpc/jsonrpc_protocol_test.go +++ b/protocol/jsonrpc/jsonrpc_protocol_test.go @@ -18,7 +18,6 @@ package jsonrpc import ( - "context" "fmt" "strings" "testing" @@ -38,10 +37,10 @@ import ( func TestJsonrpcProtocol_Export(t *testing.T) { // Export proto := GetProtocol() - url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) exporter := proto.Export(protocol.NewBaseInvoker(url)) @@ -69,10 +68,10 @@ func TestJsonrpcProtocol_Export(t *testing.T) { func TestJsonrpcProtocol_Refer(t *testing.T) { // Refer proto := GetProtocol() - url, err := common.NewURL(context.Background(), "jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&"+ - "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&"+ - "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&"+ - "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&"+ + url, err := common.NewURL("jsonrpc://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) con := config.ConsumerConfig{ diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go index dc85e0f5e76fd07dbcd11646ae529c98e5323a15..8600f02dad3d32d797613823de0bbe40261d2e71 100644 --- a/protocol/jsonrpc/server.go +++ b/protocol/jsonrpc/server.go @@ -32,6 +32,7 @@ import ( ) import ( + "github.com/opentracing/opentracing-go" perrors "github.com/pkg/errors" ) @@ -50,11 +51,15 @@ var ( ) const ( - DefaultMaxSleepTime = 1 * time.Second // accept中间最大sleep interval + // DefaultMaxSleepTime max sleep interval in accept + DefaultMaxSleepTime = 1 * time.Second + // DefaultHTTPRspBufferSize ... DefaultHTTPRspBufferSize = 1024 - PathPrefix = byte('/') + // PathPrefix ... + PathPrefix = byte('/') ) +// Server ... type Server struct { done chan struct{} once sync.Once @@ -64,6 +69,7 @@ type Server struct { timeout time.Duration } +// NewServer ... func NewServer() *Server { return &Server{ done: make(chan struct{}), @@ -93,6 +99,8 @@ func (s *Server) handlePkg(conn net.Conn) { rsp := &http.Response{ Header: header, StatusCode: 500, + ProtoMajor: 1, + ProtoMinor: 1, ContentLength: int64(len(body)), Body: ioutil.NopCloser(bytes.NewReader(body)), } @@ -147,6 +155,13 @@ func (s *Server) handlePkg(conn net.Conn) { } ctx := context.Background() + + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, + opentracing.HTTPHeadersCarrier(r.Header)) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + if len(reqHeader["Timeout"]) > 0 { timeout, err := time.ParseDuration(reqHeader["Timeout"]) if err == nil { @@ -213,6 +228,7 @@ func accept(listener net.Listener, fn func(net.Conn)) error { } } +// Start ... func (s *Server) Start(url common.URL) { listener, err := net.Listen("tcp", url.Location) if err != nil { @@ -239,6 +255,7 @@ func (s *Server) Start(url common.URL) { }() } +// Stop ... func (s *Server) Stop() { s.once.Do(func() { close(s.done) @@ -252,6 +269,8 @@ func serveRequest(ctx context.Context, rsp := &http.Response{ Header: make(http.Header), StatusCode: 500, + ProtoMajor: 1, + ProtoMinor: 1, ContentLength: int64(len(body)), Body: ioutil.NopCloser(bytes.NewReader(body)), } @@ -276,6 +295,8 @@ func serveRequest(ctx context.Context, rsp := &http.Response{ Header: make(http.Header), StatusCode: 200, + ProtoMajor: 1, + ProtoMinor: 1, ContentLength: int64(len(body)), Body: ioutil.NopCloser(bytes.NewReader(body)), } @@ -324,10 +345,9 @@ func serveRequest(ctx context.Context, exporter, _ := jsonrpcProtocol.ExporterMap().Load(path) invoker := exporter.(*JsonrpcExporter).GetInvoker() if invoker != nil { - result := invoker.Invoke(invocation.NewRPCInvocation(methodName, args, map[string]string{ + result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]string{ constant.PATH_KEY: path, - constant.VERSION_KEY: codec.req.Version, - })) + constant.VERSION_KEY: codec.req.Version})) if err := result.Error(); err != nil { rspStream, err := codec.Write(err.Error(), invalidRequest) if err != nil { diff --git a/protocol/mock/mock_invoker.go b/protocol/mock/mock_invoker.go index c509cef054f5a23fe504486e01d7cc0e8772711d..5c5b476b7b07f6c41a74a7ec8f51648aff84b1a3 100644 --- a/protocol/mock/mock_invoker.go +++ b/protocol/mock/mock_invoker.go @@ -21,6 +21,7 @@ package mock import ( + "context" "reflect" ) @@ -91,7 +92,7 @@ func (mr *MockInvokerMockRecorder) Destroy() *gomock.Call { } // Invoke mocks base method -func (m *MockInvoker) Invoke(arg0 protocol.Invocation) protocol.Result { +func (m *MockInvoker) Invoke(ctx context.Context, arg0 protocol.Invocation) protocol.Result { ret := m.ctrl.Call(m, "Invoke", arg0) ret0, _ := ret[0].(protocol.Result) return ret0 diff --git a/protocol/protocol.go b/protocol/protocol.go index 814a85163a99aa3b161b5eafbfed5f13ac4e3eb4..a873469a8ba361c9dfc922b071ffbf256c6a8b98 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -26,6 +26,7 @@ import ( "github.com/apache/dubbo-go/common/logger" ) +// Protocol // Extension - protocol type Protocol interface { Export(invoker Invoker) Exporter @@ -33,6 +34,7 @@ type Protocol interface { Destroy() } +// Exporter // wrapping invoker type Exporter interface { GetInvoker() Invoker @@ -43,37 +45,45 @@ type Exporter interface { // base protocol ///////////////////////////// +// BaseProtocol ... type BaseProtocol struct { exporterMap *sync.Map invokers []Invoker } +// NewBaseProtocol ... func NewBaseProtocol() BaseProtocol { return BaseProtocol{ exporterMap: new(sync.Map), } } +// SetExporterMap ... func (bp *BaseProtocol) SetExporterMap(key string, exporter Exporter) { bp.exporterMap.Store(key, exporter) } +// ExporterMap ... func (bp *BaseProtocol) ExporterMap() *sync.Map { return bp.exporterMap } +// SetInvokers ... func (bp *BaseProtocol) SetInvokers(invoker Invoker) { bp.invokers = append(bp.invokers, invoker) } +// Invokers ... func (bp *BaseProtocol) Invokers() []Invoker { return bp.invokers } +// Export ... func (bp *BaseProtocol) Export(invoker Invoker) Exporter { return NewBaseExporter("base", invoker, bp.exporterMap) } +// Refer ... func (bp *BaseProtocol) Refer(url common.URL) Invoker { return NewBaseInvoker(url) } @@ -103,12 +113,14 @@ func (bp *BaseProtocol) Destroy() { // base exporter ///////////////////////////// +// BaseExporter ... type BaseExporter struct { key string invoker Invoker exporterMap *sync.Map } +// NewBaseExporter ... func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseExporter { return &BaseExporter{ key: key, @@ -117,11 +129,13 @@ func NewBaseExporter(key string, invoker Invoker, exporterMap *sync.Map) *BaseEx } } +// GetInvoker ... func (de *BaseExporter) GetInvoker() Invoker { return de.invoker } +// Unexport ... 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 2efc34da4469cf369d4bbeb871ccfbdb73123f6a..dedf8aa64b6ae1b7b4782350e2625b02171aac44 100644 --- a/protocol/protocolwrapper/mock_protocol_filter.go +++ b/protocol/protocolwrapper/mock_protocol_filter.go @@ -28,6 +28,7 @@ import ( type mockProtocolFilter struct{} +// NewMockProtocolFilter ... func NewMockProtocolFilter() protocol.Protocol { return &mockProtocolFilter{} } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go index 7c58fabea3cccf5a39e1622fedd4a3a297e05983..70d2da0faed3bc9797eb23cec653bea05d445d91 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper.go @@ -18,6 +18,7 @@ package protocolwrapper import ( + "context" "strings" ) @@ -30,6 +31,7 @@ import ( ) const ( + // FILTER ... FILTER = "filter" ) @@ -37,11 +39,13 @@ func init() { extension.SetProtocol(FILTER, GetProtocol) } +// ProtocolFilterWrapper // protocol in url decide who ProtocolFilterWrapper.protocol is type ProtocolFilterWrapper struct { protocol protocol.Protocol } +// Export ... func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Exporter { if pfw.protocol == nil { pfw.protocol = extension.GetProtocol(invoker.GetUrl().Protocol) @@ -50,6 +54,7 @@ func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Expo return pfw.protocol.Export(invoker) } +// Refer ... func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker { if pfw.protocol == nil { pfw.protocol = extension.GetProtocol(url.Protocol) @@ -57,6 +62,7 @@ func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker { return buildInvokerChain(pfw.protocol.Refer(url), constant.REFERENCE_FILTER_KEY) } +// Destroy ... func (pfw *ProtocolFilterWrapper) Destroy() { pfw.protocol.Destroy() } @@ -72,14 +78,15 @@ func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker { // The order of filters is from left to right, so loading from right to left for i := len(filtNames) - 1; i >= 0; i-- { - filter := extension.GetFilter(filtNames[i]) - fi := &FilterInvoker{next: next, invoker: invoker, filter: filter} + flt := extension.GetFilter(filtNames[i]) + fi := &FilterInvoker{next: next, invoker: invoker, filter: flt} next = fi } return next } +// GetProtocol ... func GetProtocol() protocol.Protocol { return &ProtocolFilterWrapper{} } @@ -88,25 +95,30 @@ func GetProtocol() protocol.Protocol { // filter invoker /////////////////////////// +// FilterInvoker ... type FilterInvoker struct { next protocol.Invoker invoker protocol.Invoker filter filter.Filter } +// GetUrl ... func (fi *FilterInvoker) GetUrl() common.URL { return fi.invoker.GetUrl() } +// IsAvailable ... func (fi *FilterInvoker) IsAvailable() bool { return fi.invoker.IsAvailable() } -func (fi *FilterInvoker) Invoke(invocation protocol.Invocation) protocol.Result { - result := fi.filter.Invoke(fi.next, invocation) - return fi.filter.OnResponse(result, fi.invoker, invocation) +// Invoke ... +func (fi *FilterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { + result := fi.filter.Invoke(ctx, fi.next, invocation) + return fi.filter.OnResponse(ctx, result, fi.invoker, invocation) } +// Destroy ... func (fi *FilterInvoker) Destroy() { fi.invoker.Destroy() } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go index dc376313549c24da1cc6cb64a42e8445ef4fe346..8491d57462d47d6af72040d41b78dcb30e6da697 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go @@ -18,6 +18,7 @@ package protocolwrapper import ( + "context" "net/url" "testing" ) @@ -66,7 +67,7 @@ func init() { type EchoFilterForTest struct{} -func (ef *EchoFilterForTest) Invoke(invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +func (ef *EchoFilterForTest) 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())) if invocation.MethodName() == constant.ECHO && len(invocation.Arguments()) == 1 { @@ -75,10 +76,10 @@ func (ef *EchoFilterForTest) Invoke(invoker protocol.Invoker, invocation protoco } } - return invoker.Invoke(invocation) + return invoker.Invoke(ctx, invocation) } -func (ef *EchoFilterForTest) OnResponse(result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { +func (ef *EchoFilterForTest) OnResponse(ctx context.Context, result protocol.Result, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { return result } diff --git a/protocol/result.go b/protocol/result.go index dcdb62310d359d441067395ea92f8460df97eb22..34e76d2dddbaed33b2e2c015631443565cfaea87 100644 --- a/protocol/result.go +++ b/protocol/result.go @@ -17,6 +17,7 @@ package protocol +// Result ... type Result interface { SetError(error) Error() error @@ -32,12 +33,14 @@ type Result interface { // Result Impletment of RPC ///////////////////////////// +// RPCResult ... type RPCResult struct { Attrs map[string]string Err error Rest interface{} } +// SetError ... func (r *RPCResult) SetError(err error) { r.Err = err } @@ -46,26 +49,32 @@ func (r *RPCResult) Error() error { return r.Err } +// SetResult ... func (r *RPCResult) SetResult(rest interface{}) { r.Rest = rest } +// Result ... func (r *RPCResult) Result() interface{} { return r.Rest } +// SetAttachments ... func (r *RPCResult) SetAttachments(attr map[string]string) { r.Attrs = attr } +// Attachments ... func (r *RPCResult) Attachments() map[string]string { return r.Attrs } +// AddAttachment ... func (r *RPCResult) AddAttachment(key, value string) { r.Attrs[key] = value } +// Attachment ... 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 3a8bfbc87f285e0e86269d44c47d6771566d97b1..639fd559aa16689a249d035895fc037dc3bc3f8b 100644 --- a/protocol/rpc_status.go +++ b/protocol/rpc_status.go @@ -20,6 +20,7 @@ package protocol import ( "sync" "sync/atomic" + "time" ) import ( @@ -27,18 +28,82 @@ import ( ) var ( - methodStatistics sync.Map // url -> { methodName : RpcStatus} + methodStatistics sync.Map // url -> { methodName : RPCStatus} + serviceStatistic sync.Map // url -> RPCStatus ) -type RpcStatus struct { - active int32 +// RPCStatus ... +type RPCStatus struct { + active int32 + failed int32 + total int32 + totalElapsed int64 + failedElapsed int64 + maxElapsed int64 + failedMaxElapsed int64 + succeededMaxElapsed int64 + successiveRequestFailureCount int32 + lastRequestFailedTimestamp int64 } -func (rpc *RpcStatus) GetActive() int32 { +// GetActive ... +func (rpc *RPCStatus) GetActive() int32 { return atomic.LoadInt32(&rpc.active) } -func GetStatus(url common.URL, methodName string) *RpcStatus { +// GetFailed ... +func (rpc *RPCStatus) GetFailed() int32 { + return atomic.LoadInt32(&rpc.failed) +} + +// GetTotal ... +func (rpc *RPCStatus) GetTotal() int32 { + return atomic.LoadInt32(&rpc.total) +} + +// GetTotalElapsed ... +func (rpc *RPCStatus) GetTotalElapsed() int64 { + return atomic.LoadInt64(&rpc.totalElapsed) +} + +// GetFailedElapsed ... +func (rpc *RPCStatus) GetFailedElapsed() int64 { + return atomic.LoadInt64(&rpc.failedElapsed) +} + +// GetMaxElapsed ... +func (rpc *RPCStatus) GetMaxElapsed() int64 { + return atomic.LoadInt64(&rpc.maxElapsed) +} + +// GetFailedMaxElapsed ... +func (rpc *RPCStatus) GetFailedMaxElapsed() int64 { + return atomic.LoadInt64(&rpc.failedMaxElapsed) +} + +// GetSucceededMaxElapsed ... +func (rpc *RPCStatus) GetSucceededMaxElapsed() int64 { + return atomic.LoadInt64(&rpc.succeededMaxElapsed) +} + +// GetLastRequestFailedTimestamp ... +func (rpc *RPCStatus) GetLastRequestFailedTimestamp() int64 { + return atomic.LoadInt64(&rpc.lastRequestFailedTimestamp) +} + +// GetSuccessiveRequestFailureCount ... +func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 { + return atomic.LoadInt32(&rpc.successiveRequestFailureCount) +} + +// GetURLStatus ... +func GetURLStatus(url common.URL) *RPCStatus { + rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{}) + return rpcStatus.(*RPCStatus) +} + +// GetMethodStatus ... +func GetMethodStatus(url common.URL, methodName string) *RPCStatus { identifier := url.Key() methodMap, found := methodStatistics.Load(identifier) if !found { @@ -49,27 +114,56 @@ func GetStatus(url common.URL, methodName string) *RpcStatus { methodActive := methodMap.(*sync.Map) rpcStatus, found := methodActive.Load(methodName) if !found { - rpcStatus = &RpcStatus{} + rpcStatus = &RPCStatus{} methodActive.Store(methodName, rpcStatus) } - status := rpcStatus.(*RpcStatus) + status := rpcStatus.(*RPCStatus) return status } +// BeginCount ... func BeginCount(url common.URL, methodName string) { - beginCount0(GetStatus(url, methodName)) + beginCount0(GetURLStatus(url)) + beginCount0(GetMethodStatus(url, methodName)) } -func EndCount(url common.URL, methodName string) { - endCount0(GetStatus(url, methodName)) +// EndCount ... +func EndCount(url common.URL, methodName string, elapsed int64, succeeded bool) { + endCount0(GetURLStatus(url), elapsed, succeeded) + endCount0(GetMethodStatus(url, methodName), elapsed, succeeded) } // private methods -func beginCount0(rpcStatus *RpcStatus) { +func beginCount0(rpcStatus *RPCStatus) { atomic.AddInt32(&rpcStatus.active, 1) } -func endCount0(rpcStatus *RpcStatus) { +func endCount0(rpcStatus *RPCStatus, elapsed int64, succeeded bool) { atomic.AddInt32(&rpcStatus.active, -1) + atomic.AddInt32(&rpcStatus.total, 1) + atomic.AddInt64(&rpcStatus.totalElapsed, elapsed) + + if rpcStatus.maxElapsed < elapsed { + atomic.StoreInt64(&rpcStatus.maxElapsed, elapsed) + } + if succeeded { + if rpcStatus.succeededMaxElapsed < elapsed { + atomic.StoreInt64(&rpcStatus.succeededMaxElapsed, elapsed) + } + atomic.StoreInt32(&rpcStatus.successiveRequestFailureCount, 0) + } else { + atomic.StoreInt64(&rpcStatus.lastRequestFailedTimestamp, time.Now().Unix()) + atomic.AddInt32(&rpcStatus.successiveRequestFailureCount, 1) + atomic.AddInt32(&rpcStatus.failed, 1) + atomic.AddInt64(&rpcStatus.failedElapsed, elapsed) + if rpcStatus.failedMaxElapsed < elapsed { + atomic.StoreInt64(&rpcStatus.failedMaxElapsed, elapsed) + } + } +} + +// CurrentTimeMillis ... +func CurrentTimeMillis() int64 { + return time.Now().UnixNano() / int64(time.Millisecond) } diff --git a/protocol/rpc_status_test.go b/protocol/rpc_status_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ffdb3b535667f32e96c3af2be84851655abf5954 --- /dev/null +++ b/protocol/rpc_status_test.go @@ -0,0 +1,151 @@ +package protocol + +import ( + "strconv" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" +) + +func TestBeginCount(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + BeginCount(url, "test") + urlStatus := GetURLStatus(url) + methodStatus := GetMethodStatus(url, "test") + methodStatus1 := GetMethodStatus(url, "test1") + assert.Equal(t, int32(1), methodStatus.active) + assert.Equal(t, int32(1), urlStatus.active) + assert.Equal(t, int32(0), methodStatus1.active) + +} + +func TestEndCount(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + EndCount(url, "test", 100, true) + urlStatus := GetURLStatus(url) + methodStatus := GetMethodStatus(url, "test") + assert.Equal(t, int32(-1), methodStatus.active) + assert.Equal(t, int32(-1), urlStatus.active) + assert.Equal(t, int32(1), methodStatus.total) + assert.Equal(t, int32(1), urlStatus.total) +} + +func TestGetMethodStatus(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + status := GetMethodStatus(url, "test") + assert.NotNil(t, status) + assert.Equal(t, int32(0), status.total) +} + +func TestGetUrlStatus(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + status := GetURLStatus(url) + assert.NotNil(t, status) + assert.Equal(t, int32(0), status.total) +} + +func Test_beginCount0(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + status := GetURLStatus(url) + beginCount0(status) + assert.Equal(t, int32(1), status.active) +} + +func Test_All(t *testing.T) { + defer destroy() + + url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") + request(url, "test", 100, false, true) + urlStatus := GetURLStatus(url) + methodStatus := GetMethodStatus(url, "test") + assert.Equal(t, int32(1), methodStatus.total) + assert.Equal(t, int32(1), urlStatus.total) + assert.Equal(t, int32(0), methodStatus.active) + assert.Equal(t, int32(0), urlStatus.active) + assert.Equal(t, int32(0), methodStatus.failed) + assert.Equal(t, int32(0), urlStatus.failed) + assert.Equal(t, int32(0), methodStatus.successiveRequestFailureCount) + assert.Equal(t, int32(0), urlStatus.successiveRequestFailureCount) + assert.Equal(t, int64(100), methodStatus.totalElapsed) + assert.Equal(t, int64(100), urlStatus.totalElapsed) + request(url, "test", 100, false, false) + request(url, "test", 100, false, false) + request(url, "test", 100, false, false) + request(url, "test", 100, false, false) + request(url, "test", 100, false, false) + assert.Equal(t, int32(6), methodStatus.total) + assert.Equal(t, int32(6), urlStatus.total) + assert.Equal(t, int32(5), methodStatus.failed) + assert.Equal(t, int32(5), urlStatus.failed) + assert.Equal(t, int32(5), methodStatus.successiveRequestFailureCount) + assert.Equal(t, int32(5), urlStatus.successiveRequestFailureCount) + assert.Equal(t, int64(600), methodStatus.totalElapsed) + assert.Equal(t, int64(600), urlStatus.totalElapsed) + assert.Equal(t, int64(500), methodStatus.failedElapsed) + assert.Equal(t, int64(500), urlStatus.failedElapsed) + + request(url, "test", 100, false, true) + assert.Equal(t, int32(0), methodStatus.successiveRequestFailureCount) + assert.Equal(t, int32(0), urlStatus.successiveRequestFailureCount) + + request(url, "test", 200, false, false) + request(url, "test", 200, false, false) + assert.Equal(t, int32(2), methodStatus.successiveRequestFailureCount) + assert.Equal(t, int32(2), urlStatus.successiveRequestFailureCount) + assert.Equal(t, int64(200), methodStatus.maxElapsed) + assert.Equal(t, int64(200), urlStatus.maxElapsed) + + request(url, "test1", 200, false, false) + request(url, "test1", 200, false, false) + request(url, "test1", 200, false, false) + assert.Equal(t, int32(5), urlStatus.successiveRequestFailureCount) + methodStatus1 := GetMethodStatus(url, "test1") + assert.Equal(t, int32(2), methodStatus.successiveRequestFailureCount) + assert.Equal(t, int32(3), methodStatus1.successiveRequestFailureCount) + +} + +func request(url common.URL, method string, elapsed int64, active, succeeded bool) { + BeginCount(url, method) + if !active { + EndCount(url, method, elapsed, succeeded) + } +} + +func TestCurrentTimeMillis(t *testing.T) { + defer destroy() + c := CurrentTimeMillis() + assert.NotNil(t, c) + str := strconv.FormatInt(c, 10) + i, _ := strconv.ParseInt(str, 10, 64) + assert.Equal(t, c, i) +} + +func destroy() { + delete1 := func(key interface{}, value interface{}) bool { + methodStatistics.Delete(key) + return true + } + methodStatistics.Range(delete1) + delete2 := func(key interface{}, value interface{}) bool { + serviceStatistic.Delete(key) + return true + } + serviceStatistic.Range(delete2) +} diff --git a/registry/base_configuration_listener.go b/registry/base_configuration_listener.go index 056a93aaff1ec657db89f21b4a6b28efc354b49b..55418318dfc52ed9f17f1ec6a18ad9ef9d8163bf 100644 --- a/registry/base_configuration_listener.go +++ b/registry/base_configuration_listener.go @@ -29,15 +29,19 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// BaseConfigurationListener ... type BaseConfigurationListener struct { configurators []config_center.Configurator dynamicConfiguration config_center.DynamicConfiguration defaultConfiguratorFunc func(url *common.URL) config_center.Configurator } +// Configurators ... func (bcl *BaseConfigurationListener) Configurators() []config_center.Configurator { return bcl.configurators } + +// InitWith ... 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 { @@ -56,6 +60,7 @@ func (bcl *BaseConfigurationListener) InitWith(key string, listener config_cente } } +// Process ... 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 { @@ -76,12 +81,15 @@ func (bcl *BaseConfigurationListener) genConfiguratorFromRawRule(rawConfig strin bcl.configurators = ToConfigurators(urls, bcl.defaultConfiguratorFunc) return nil } + +// OverrideUrl ... func (bcl *BaseConfigurationListener) OverrideUrl(url *common.URL) { for _, v := range bcl.configurators { v.Configure(url) } } +// ToConfigurators ... 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 new file mode 100644 index 0000000000000000000000000000000000000000..5b9aef82928d491d4b8f4dbe3caa4bd64a185dad --- /dev/null +++ b/registry/base_registry.go @@ -0,0 +1,375 @@ +/* + * 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 registry + +import ( + "context" + "fmt" + "net/url" + "os" + "strconv" + "strings" + "sync" + "time" +) + +import ( + gxnet "github.com/dubbogo/gost/net" + 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/logger" +) + +const ( + // RegistryConnDelay connection delay + RegistryConnDelay = 3 + // MaxWaitInterval max wait interval + MaxWaitInterval = 3 * time.Second +) + +var ( + processID = "" + localIP = "" +) + +func init() { + processID = fmt.Sprintf("%d", os.Getpid()) + localIP, _ = gxnet.GetLocalIP() +} + +/* + * -----------------------------------NOTICE--------------------------------------------- + * If there is no special case, you'd better inherit BaseRegistry and implement the + * FacadeBasedRegistry interface instead of directly implementing the Registry interface. + * -------------------------------------------------------------------------------------- + */ + +/* + * FacadeBasedRegistry interface is subclass of Registry, and it is designed for registry who want to inherit BaseRegistry. + * You have to implement the interface to inherit BaseRegistry. + */ +type FacadeBasedRegistry interface { + Registry + CreatePath(string) error + DoRegister(string, string) error + DoSubscribe(conf *common.URL) (Listener, error) + CloseAndNilClient() + CloseListener() + InitListeners() +} + +// BaseRegistry is a common logic abstract for registry. It implement Registry interface. +type BaseRegistry struct { + context context.Context + facadeBasedRegistry FacadeBasedRegistry + *common.URL + birth int64 // time of file birth, seconds since Epoch; 0 if unknown + wg sync.WaitGroup // wg+done for zk restart + done chan struct{} + cltLock sync.Mutex //ctl lock is a lock for services map + services map[string]common.URL // service name + protocol -> service config, for store the service registered +} + +// InitBaseRegistry for init some local variables and set BaseRegistry's subclass to it +func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBasedRegistry) Registry { + r.URL = url + r.birth = time.Now().UnixNano() + r.done = make(chan struct{}) + r.services = make(map[string]common.URL) + r.facadeBasedRegistry = facadeRegistry + return r +} + +// GetUrl for get registry's url +func (r *BaseRegistry) GetUrl() common.URL { + return *r.URL +} + +// Destroy for graceful down +func (r *BaseRegistry) Destroy() { + //first step close registry's all listeners + r.facadeBasedRegistry.CloseListener() + // then close r.done to notify other program who listen to it + close(r.done) + // wait waitgroup done (wait listeners outside close over) + r.wg.Wait() + //close registry client + r.closeRegisters() +} + +// Register implement interface registry to register +func (r *BaseRegistry) Register(conf common.URL) error { + var ( + ok bool + err error + ) + role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) + // Check if the service has been registered + r.cltLock.Lock() + _, ok = r.services[conf.Key()] + r.cltLock.Unlock() + if ok { + return perrors.Errorf("Path{%s} has been registered", conf.Key()) + } + + err = r.register(conf) + if err != nil { + return perrors.WithMessagef(err, "register(conf:%+v)", conf) + } + + r.cltLock.Lock() + r.services[conf.Key()] = conf + r.cltLock.Unlock() + logger.Debugf("(%sRegistry)Register(conf{%#v})", common.DubboRole[role], conf) + + return nil +} + +// service is for getting service path stored in url +func (r *BaseRegistry) service(c common.URL) string { + return url.QueryEscape(c.Service()) +} + +// RestartCallBack for reregister when reconnect +func (r *BaseRegistry) RestartCallBack() bool { + + // copy r.services + services := []common.URL{} + for _, confIf := range r.services { + services = append(services, confIf) + } + + flag := true + for _, confIf := range services { + err := r.register(confIf) + if err != nil { + logger.Errorf("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}", + confIf, perrors.WithStack(err)) + flag = false + break + } + logger.Infof("success to re-register service :%v", confIf.Key()) + } + r.facadeBasedRegistry.InitListeners() + + return flag +} + +// register for register url to registry, include init params +func (r *BaseRegistry) register(c common.URL) error { + var ( + err error + //revision string + params url.Values + rawURL string + encodedURL string + dubboPath string + //conf config.URL + ) + params = url.Values{} + + c.RangeParams(func(key, value string) bool { + params.Add(key, value) + return true + }) + + params.Add("pid", processID) + params.Add("ip", localIP) + //params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6)) + + role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) + switch role { + + case common.PROVIDER: + dubboPath, rawURL, err = r.providerRegistry(c, params) + case common.CONSUMER: + dubboPath, rawURL, err = r.consumerRegistry(c, params) + default: + return perrors.Errorf("@c{%v} type is not referencer or provider", c) + } + encodedURL = url.QueryEscape(rawURL) + dubboPath = strings.ReplaceAll(dubboPath, "$", "%24") + err = r.facadeBasedRegistry.DoRegister(dubboPath, encodedURL) + + if err != nil { + return perrors.WithMessagef(err, "register Node(path:%s, url:%s)", dubboPath, rawURL) + } + return nil +} + +// providerRegistry for provider role do +func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values) (string, string, error) { + var ( + dubboPath string + rawURL string + err error + ) + if c.Path == "" || len(c.Methods) == 0 { + return "", "", perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods) + } + dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER]) + r.cltLock.Lock() + err = r.facadeBasedRegistry.CreatePath(dubboPath) + r.cltLock.Unlock() + if err != nil { + logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err)) + return "", "", perrors.WithMessagef(err, "facadeBasedRegistry.CreatePath(path:%s)", dubboPath) + } + params.Add("anyhost", "true") + + // Dubbo java consumer to start looking for the provider url,because the category does not match, + // the provider will not find, causing the consumer can not start, so we use consumers. + // DubboRole = [...]string{"consumer", "", "", "provider"} + // params.Add("category", (RoleType(PROVIDER)).Role()) + params.Add("category", (common.RoleType(common.PROVIDER)).String()) + params.Add("dubbo", "dubbo-provider-golang-"+constant.Version) + + params.Add("side", (common.RoleType(common.PROVIDER)).Role()) + + if len(c.Methods) == 0 { + params.Add("methods", strings.Join(c.Methods, ",")) + } + logger.Debugf("provider url params:%#v", params) + var host string + if c.Ip == "" { + host = localIP + ":" + c.Port + } else { + host = c.Ip + ":" + c.Port + } + + rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, params.Encode()) + // Print your own registration service providers. + dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.PROVIDER)).String()) + logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL) + return dubboPath, rawURL, nil +} + +// consumerRegistry for consumer role do +func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values) (string, string, error) { + var ( + dubboPath string + rawURL string + err error + ) + dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.CONSUMER]) + r.cltLock.Lock() + err = r.facadeBasedRegistry.CreatePath(dubboPath) + r.cltLock.Unlock() + if err != nil { + logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err)) + return "", "", perrors.WithStack(err) + } + dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER]) + r.cltLock.Lock() + err = r.facadeBasedRegistry.CreatePath(dubboPath) + r.cltLock.Unlock() + if err != nil { + logger.Errorf("facadeBasedRegistry.CreatePath(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err)) + return "", "", perrors.WithStack(err) + } + + params.Add("protocol", c.Protocol) + params.Add("category", (common.RoleType(common.CONSUMER)).String()) + params.Add("dubbo", "dubbogo-consumer-"+constant.Version) + + rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode()) + dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String()) + + logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL) + return dubboPath, rawURL, nil +} + +// sleepWait... +func sleepWait(n int) { + wait := time.Duration((n + 1) * 2e8) + if wait > MaxWaitInterval { + wait = MaxWaitInterval + } + time.Sleep(wait) +} + +// Subscribe :subscribe from registry, event will notify by notifyListener +func (r *BaseRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) { + n := 0 + for { + n++ + if !r.IsAvailable() { + logger.Warnf("event listener game over.") + return + } + + listener, err := r.facadeBasedRegistry.DoSubscribe(url) + if err != nil { + if !r.IsAvailable() { + logger.Warnf("event listener game over.") + return + } + logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) + time.Sleep(time.Duration(RegistryConnDelay) * time.Second) + continue + } + + for { + if serviceEvent, err := listener.Next(); err != nil { + logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) + listener.Close() + break + } else { + logger.Infof("update begin, service event: %v", serviceEvent.String()) + notifyListener.Notify(serviceEvent) + } + + } + sleepWait(n) + } +} + +// closeRegisters close and remove registry client and reset services map +func (r *BaseRegistry) closeRegisters() { + r.cltLock.Lock() + defer r.cltLock.Unlock() + logger.Infof("begin to close provider client") + // Close and remove(set to nil) the registry client + r.facadeBasedRegistry.CloseAndNilClient() + // reset the services map + r.services = nil +} + +// IsAvailable judge to is registry not closed by chan r.done +func (r *BaseRegistry) IsAvailable() bool { + select { + case <-r.done: + return false + default: + return true + } +} + +// WaitGroup open for outside add the waitgroup to add some logic before registry destroyed over(graceful down) +func (r *BaseRegistry) WaitGroup() *sync.WaitGroup { + return &r.wg +} + +// Done open for outside to listen the event of registry Destroy() called. +func (r *BaseRegistry) Done() chan struct{} { + return r.done +} diff --git a/registry/consul/registry.go b/registry/consul/registry.go index 73bf3975bc7c73f4a7748f46280ffb1aa5525ca8..c5b8510a6c87068a5b4f1ce52203d401a896a6c2 100644 --- a/registry/consul/registry.go +++ b/registry/consul/registry.go @@ -36,6 +36,7 @@ import ( ) const ( + // RegistryConnDelay ... RegistryConnDelay = 3 ) @@ -137,14 +138,15 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti } for { - if serviceEvent, err := listener.Next(); err != nil { + serviceEvent, err := listener.Next() + if err != nil { logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) listener.Close() return - } else { - logger.Infof("update begin, service event: %v", serviceEvent.String()) - notifyListener.Notify(serviceEvent) } + + logger.Infof("update begin, service event: %v", serviceEvent.String()) + notifyListener.Notify(serviceEvent) } } } diff --git a/registry/consul/utils.go b/registry/consul/utils.go index d295f644631ae63b6bdf035f71f5f104a64083e2..05ac5e76e292a2b574bd5e661bbbcca4f419fc22 100644 --- a/registry/consul/utils.go +++ b/registry/consul/utils.go @@ -18,7 +18,6 @@ package consul import ( - "context" "crypto/md5" "encoding/hex" "fmt" @@ -100,7 +99,7 @@ func retrieveURL(service *consul.ServiceEntry) (common.URL, error) { if !ok { return common.URL{}, perrors.New("retrieve url fails with no url key in service meta") } - url1, err := common.NewURL(context.Background(), url) + url1, err := common.NewURL(url) if err != nil { return common.URL{}, perrors.WithStack(err) } diff --git a/registry/directory/directory.go b/registry/directory/directory.go index e88c611f6f20bc182c3630e328caab848affc08b..f9670af7ea566ce35e89d9173b54752fd16f92bf 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -41,10 +41,12 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// Options ... type Options struct { serviceTTL time.Duration } +// Option ... type Option func(*Options) type registryDirectory struct { @@ -61,6 +63,7 @@ type registryDirectory struct { Options } +// NewRegistryDirectory ... func NewRegistryDirectory(url *common.URL, registry registry.Registry, opts ...Option) (*registryDirectory, error) { options := Options{ //default 300s @@ -222,13 +225,14 @@ func (dir *registryDirectory) List(invocation protocol.Invocation) []protocol.In func (dir *registryDirectory) IsAvailable() bool { if !dir.BaseDirectory.IsAvailable() { return dir.BaseDirectory.IsAvailable() - } else { - for _, ivk := range dir.cacheInvokers { - if ivk.IsAvailable() { - return true - } + } + + for _, ivk := range dir.cacheInvokers { + if ivk.IsAvailable() { + return true } } + return false } diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go index b3c1d35aaa66b3437ff89807fba2df0a383921cb..8ebd130d7d1797a9d8707628d7e5920be758e389 100644 --- a/registry/directory/directory_test.go +++ b/registry/directory/directory_test.go @@ -18,7 +18,6 @@ package directory import ( - "context" "net/url" "strconv" "testing" @@ -62,7 +61,7 @@ func TestSubscribe(t *testing.T) { //} func TestSubscribe_InvalidUrl(t *testing.T) { - url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url, _ := common.NewURL("mock://127.0.0.1:1111") mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) _, err := NewRegistryDirectory(&url, mockRegistry) assert.Error(t, err) @@ -72,8 +71,8 @@ func TestSubscribe_Group(t *testing.T) { extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) extension.SetCluster("mock", cluster_impl.NewMockCluster) - regurl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") - suburl, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000") + regurl, _ := common.NewURL("mock://127.0.0.1:1111") + suburl, _ := common.NewURL("dubbo://127.0.0.1:20000") suburl.SetParam(constant.CLUSTER_KEY, "mock") regurl.SubURL = &suburl mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) @@ -124,7 +123,7 @@ func Test_List(t *testing.T) { } func Test_MergeProviderUrl(t *testing.T) { registryDirectory, mockRegistry := normalRegistryDir(true) - providerUrl, _ := common.NewURL(context.TODO(), "dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService", + providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock1"), common.WithParamsValue(constant.GROUP_KEY, "group"), common.WithParamsValue(constant.VERSION_KEY, "1.0.0")) @@ -139,7 +138,7 @@ func Test_MergeProviderUrl(t *testing.T) { func Test_MergeOverrideUrl(t *testing.T) { registryDirectory, mockRegistry := normalRegistryDir(true) - providerUrl, _ := common.NewURL(context.TODO(), "dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService", + providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue(constant.GROUP_KEY, "group"), common.WithParamsValue(constant.VERSION_KEY, "1.0.0")) @@ -147,7 +146,7 @@ func Test_MergeOverrideUrl(t *testing.T) { Loop1: for { if len(registryDirectory.cacheInvokers) > 0 { - overrideUrl, _ := common.NewURL(context.TODO(), "override://0.0.0.0:20000/org.apache.dubbo-go.mockService", + overrideUrl, _ := common.NewURL("override://0.0.0.0:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock1"), common.WithParamsValue(constant.GROUP_KEY, "group"), common.WithParamsValue(constant.VERSION_KEY, "1.0.0")) @@ -173,9 +172,8 @@ Loop1: func normalRegistryDir(noMockEvent ...bool) (*registryDirectory, *registry.MockRegistry) { extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) - url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url, _ := common.NewURL("mock://127.0.0.1:1111") suburl, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue(constant.GROUP_KEY, "group"), diff --git a/registry/etcdv3/listener.go b/registry/etcdv3/listener.go index 31d62fa916e5659cf424839cedf8f063fabedaa0..79e3ad514584937e742db4bbc993202dd6a9f5b9 100644 --- a/registry/etcdv3/listener.go +++ b/registry/etcdv3/listener.go @@ -18,7 +18,6 @@ package etcdv3 import ( - "context" "strings" ) @@ -39,6 +38,7 @@ type dataListener struct { listener config_center.ConfigurationListener } +// NewRegistryDataListener ... func NewRegistryDataListener(listener config_center.ConfigurationListener) *dataListener { return &dataListener{listener: listener, interestedURL: []*common.URL{}} } @@ -50,7 +50,7 @@ func (l *dataListener) AddInterestedURL(url *common.URL) { func (l *dataListener) DataChange(eventType remoting.Event) bool { url := eventType.Path[strings.Index(eventType.Path, "/providers/")+len("/providers/"):] - serviceURL, err := common.NewURL(context.Background(), url) + serviceURL, err := common.NewURL(url) if err != nil { logger.Warnf("Listen NewURL(r{%s}) = error{%v}", eventType.Path, err) return false @@ -77,9 +77,10 @@ type configurationListener struct { events chan *config_center.ConfigChangeEvent } +// NewConfigurationListener for listening the event of etcdv3. func NewConfigurationListener(reg *etcdV3Registry) *configurationListener { // add a new waiter - reg.wg.Add(1) + reg.WaitGroup().Add(1) return &configurationListener{registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32)} } func (l *configurationListener) Process(configType *config_center.ConfigChangeEvent) { @@ -89,7 +90,7 @@ func (l *configurationListener) Process(configType *config_center.ConfigChangeEv func (l *configurationListener) Next() (*registry.ServiceEvent, error) { for { select { - case <-l.registry.done: + case <-l.registry.Done(): logger.Warnf("listener's etcd client connection is broken, so etcd event listener exit now.") return nil, perrors.New("listener stopped") @@ -97,7 +98,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { logger.Infof("got etcd event %#v", e) if e.ConfigType == remoting.EventTypeDel { select { - case <-l.registry.done: + case <-l.registry.Done(): logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.Value) default: } @@ -108,5 +109,5 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { } } func (l *configurationListener) Close() { - l.registry.wg.Done() + l.registry.WaitGroup().Done() } diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index c064f99c6c4b447a6c81093b87d99e1d1ba6d17a..928e3fa83d4a19869903d3aaee1691c298b031b2 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -18,10 +18,10 @@ package etcdv3 import ( - "context" - "github.com/apache/dubbo-go/config_center" "testing" "time" + + "github.com/apache/dubbo-go/config_center" ) import ( @@ -73,7 +73,7 @@ func (suite *RegistryTestSuite) TestDataChange() { t := suite.T() listener := NewRegistryDataListener(&MockDataListener{}) - url, _ := common.NewURL(context.Background(), "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") + 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") listener.AddInterestedURL(&url) if !listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/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"}) { t.Fatal("data change not ok") diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go index b058113c69b8007803a8a18c1b5e0c3af8c184f4..e1c25768119ea7d7122b9aa22a5f881db44bafd9 100644 --- a/registry/etcdv3/registry.go +++ b/registry/etcdv3/registry.go @@ -19,17 +19,13 @@ package etcdv3 import ( "fmt" - "net/url" - "os" "path" - "strconv" "strings" "sync" "time" ) import ( - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) @@ -42,74 +38,39 @@ import ( "github.com/apache/dubbo-go/remoting/etcdv3" ) -var ( - processID = "" - localIP = "" -) - const ( - Name = "etcdv3" - RegistryConnDelay = 3 + // Name module name + Name = "etcdv3" ) func init() { - processID = fmt.Sprintf("%d", os.Getpid()) - localIP, _ = gxnet.GetLocalIP() extension.SetRegistry(Name, newETCDV3Registry) } type etcdV3Registry struct { - *common.URL - birth int64 // time of file birth, seconds since Epoch; 0 if unknown - - cltLock sync.Mutex - client *etcdv3.Client - services map[string]common.URL // service name + protocol -> service config - + registry.BaseRegistry + cltLock sync.Mutex + client *etcdv3.Client listenerLock sync.Mutex listener *etcdv3.EventListener dataListener *dataListener configListener *configurationListener - - wg sync.WaitGroup // wg+done for etcd client restart - done chan struct{} } +// Client get the etcdv3 client func (r *etcdV3Registry) Client() *etcdv3.Client { return r.client } + +//SetClient set the etcdv3 client func (r *etcdV3Registry) SetClient(client *etcdv3.Client) { r.client = client } + +// func (r *etcdV3Registry) ClientLock() *sync.Mutex { return &r.cltLock } -func (r *etcdV3Registry) WaitGroup() *sync.WaitGroup { - return &r.wg -} -func (r *etcdV3Registry) GetDone() chan struct{} { - return r.done -} -func (r *etcdV3Registry) RestartCallBack() bool { - - services := []common.URL{} - for _, confIf := range r.services { - services = append(services, confIf) - } - - flag := true - for _, confIf := range services { - err := r.Register(confIf) - if err != nil { - logger.Errorf("(etcdV3ProviderRegistry)register(conf{%#v}) = error{%#v}", - confIf, perrors.WithStack(err)) - flag = false - break - } - logger.Infof("success to re-register service :%v", confIf.Key()) - } - return flag -} func newETCDV3Registry(url *common.URL) (registry.Registry, error) { @@ -122,12 +83,9 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) { logger.Infof("etcd address is: %v, timeout is: %s", url.Location, timeout.String()) - r := &etcdV3Registry{ - URL: url, - birth: time.Now().UnixNano(), - done: make(chan struct{}), - services: make(map[string]common.URL), - } + r := &etcdV3Registry{} + + r.InitBaseRegistry(url, r) if err := etcdv3.ValidateClient( r, @@ -137,89 +95,37 @@ func newETCDV3Registry(url *common.URL) (registry.Registry, error) { ); err != nil { return nil, err } + r.WaitGroup().Add(1) //etcdv3 client start successful, then wg +1 - r.wg.Add(1) go etcdv3.HandleClientRestart(r) - r.listener = etcdv3.NewEventListener(r.client) - r.configListener = NewConfigurationListener(r) - r.dataListener = NewRegistryDataListener(r.configListener) + r.InitListeners() return r, nil } -func (r *etcdV3Registry) GetUrl() common.URL { - return *r.URL -} - -func (r *etcdV3Registry) IsAvailable() bool { - - select { - case <-r.done: - return false - default: - return true - } +func (r *etcdV3Registry) InitListeners() { + r.listener = etcdv3.NewEventListener(r.client) + r.configListener = NewConfigurationListener(r) + r.dataListener = NewRegistryDataListener(r.configListener) } -func (r *etcdV3Registry) Destroy() { - - if r.configListener != nil { - r.configListener.Close() - } - r.stop() +func (r *etcdV3Registry) DoRegister(root string, node string) error { + return r.client.Create(path.Join(root, node), "") } -func (r *etcdV3Registry) stop() { - - close(r.done) - - // close current client +func (r *etcdV3Registry) CloseAndNilClient() { r.client.Close() - - r.cltLock.Lock() r.client = nil - r.services = nil - r.cltLock.Unlock() } -func (r *etcdV3Registry) Register(svc common.URL) error { - - role, err := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) - if err != nil { - return perrors.WithMessage(err, "get registry role") - } - - r.cltLock.Lock() - if _, ok := r.services[svc.Key()]; ok { - r.cltLock.Unlock() - return perrors.New(fmt.Sprintf("Path{%s} has been registered", svc.Path)) - } - r.cltLock.Unlock() - - switch role { - case common.PROVIDER: - logger.Debugf("(provider register )Register(conf{%#v})", svc) - if err := r.registerProvider(svc); err != nil { - return perrors.WithMessage(err, "register provider") - } - case common.CONSUMER: - logger.Debugf("(consumer register )Register(conf{%#v})", svc) - if err := r.registerConsumer(svc); err != nil { - return perrors.WithMessage(err, "register consumer") - } - default: - return perrors.New(fmt.Sprintf("unknown role %d", role)) +func (r *etcdV3Registry) CloseListener() { + if r.configListener != nil { + r.configListener.Close() } - - r.cltLock.Lock() - r.services[svc.Key()] = svc - r.cltLock.Unlock() - return nil } -func (r *etcdV3Registry) createDirIfNotExist(k string) error { - +func (r *etcdV3Registry) CreatePath(k string) error { var tmpPath string for _, str := range strings.Split(k, "/")[1:] { tmpPath = path.Join(tmpPath, "/", str) @@ -231,89 +137,7 @@ func (r *etcdV3Registry) createDirIfNotExist(k string) error { return nil } -func (r *etcdV3Registry) registerConsumer(svc common.URL) error { - - consumersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.CONSUMER]) - if err := r.createDirIfNotExist(consumersNode); err != nil { - logger.Errorf("etcd client create path %s: %v", consumersNode, err) - return perrors.WithMessage(err, "etcd create consumer nodes") - } - providersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.PROVIDER]) - if err := r.createDirIfNotExist(providersNode); err != nil { - return perrors.WithMessage(err, "create provider node") - } - - params := url.Values{} - - params.Add("protocol", svc.Protocol) - - params.Add("category", (common.RoleType(common.CONSUMER)).String()) - params.Add("dubbo", "dubbogo-consumer-"+constant.Version) - - encodedURL := url.QueryEscape(fmt.Sprintf("consumer://%s%s?%s", localIP, svc.Path, params.Encode())) - dubboPath := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), (common.RoleType(common.CONSUMER)).String()) - if err := r.client.Create(path.Join(dubboPath, encodedURL), ""); err != nil { - return perrors.WithMessagef(err, "create k/v in etcd (path:%s, url:%s)", dubboPath, encodedURL) - } - - return nil -} - -func (r *etcdV3Registry) registerProvider(svc common.URL) error { - - if len(svc.Path) == 0 || len(svc.Methods) == 0 { - return perrors.New(fmt.Sprintf("service path %s or service method %s", svc.Path, svc.Methods)) - } - - var ( - urlPath string - encodedURL string - dubboPath string - ) - - providersNode := fmt.Sprintf("/dubbo/%s/%s", svc.Service(), common.DubboNodes[common.PROVIDER]) - if err := r.createDirIfNotExist(providersNode); err != nil { - return perrors.WithMessage(err, "create provider node") - } - - params := url.Values{} - - svc.RangeParams(func(key, value string) bool { - params[key] = []string{value} - return true - }) - params.Add("pid", processID) - params.Add("ip", localIP) - params.Add("anyhost", "true") - params.Add("category", (common.RoleType(common.PROVIDER)).String()) - params.Add("dubbo", "dubbo-provider-golang-"+constant.Version) - params.Add("side", (common.RoleType(common.PROVIDER)).Role()) - - if len(svc.Methods) == 0 { - params.Add("methods", strings.Join(svc.Methods, ",")) - } - - logger.Debugf("provider url params:%#v", params) - var host string - if len(svc.Ip) == 0 { - host = localIP + ":" + svc.Port - } else { - host = svc.Ip + ":" + svc.Port - } - - urlPath = svc.Path - - encodedURL = url.QueryEscape(fmt.Sprintf("%s://%s%s?%s", svc.Protocol, host, urlPath, params.Encode())) - dubboPath = fmt.Sprintf("/dubbo/%s/%s", svc.Service(), (common.RoleType(common.PROVIDER)).String()) - - if err := r.client.Create(path.Join(dubboPath, encodedURL), ""); err != nil { - return perrors.WithMessagef(err, "create k/v in etcd (path:%s, url:%s)", dubboPath, encodedURL) - } - - return nil -} - -func (r *etcdV3Registry) subscribe(svc *common.URL) (registry.Listener, error) { +func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) { var ( configListener *configurationListener @@ -346,37 +170,3 @@ func (r *etcdV3Registry) subscribe(svc *common.URL) (registry.Listener, error) { return configListener, nil } - -//subscribe from registry -func (r *etcdV3Registry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) { - for { - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - return - } - - listener, err := r.subscribe(url) - if err != nil { - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - return - } - logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) - time.Sleep(time.Duration(RegistryConnDelay) * time.Second) - continue - } - - for { - if serviceEvent, err := listener.Next(); err != nil { - logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) - listener.Close() - return - } else { - logger.Infof("update begin, service event: %v", serviceEvent.String()) - notifyListener.Notify(serviceEvent) - } - - } - - } -} diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go index 3f8c0f4cfccc2bcc68fc1e55fa69d74e9f0f8c0f..6e26a8f3fcbbf50592520a44b253e5abbaedb061 100644 --- a/registry/etcdv3/registry_test.go +++ b/registry/etcdv3/registry_test.go @@ -18,7 +18,6 @@ package etcdv3 import ( - "context" "strconv" "testing" "time" @@ -35,7 +34,7 @@ import ( func initRegistry(t *testing.T) *etcdV3Registry { - regurl, err := common.NewURL(context.Background(), "registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurl, err := common.NewURL("registry://127.0.0.1:2379", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) if err != nil { t.Fatal(err) } @@ -46,7 +45,8 @@ func initRegistry(t *testing.T) *etcdV3Registry { } out := reg.(*etcdV3Registry) - out.client.CleanKV() + err = out.client.CleanKV() + assert.NoError(t, err) return out } @@ -54,23 +54,24 @@ func (suite *RegistryTestSuite) TestRegister() { t := suite.T() - url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) reg := initRegistry(t) err := reg.Register(url) + assert.NoError(t, err) children, _, err := reg.client.GetChildrenKVList("/dubbo/com.ikurento.user.UserProvider/providers") if err != nil { t.Fatal(err) } - assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-2.6.0%26.*provider", children) + assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*provider", children) assert.NoError(t, err) } func (suite *RegistryTestSuite) TestSubscribe() { t := suite.T() - regurl, _ := common.NewURL(context.Background(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) reg := initRegistry(t) //provider register @@ -83,8 +84,9 @@ func (suite *RegistryTestSuite) TestSubscribe() { regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) reg2 := initRegistry(t) - reg2.Register(url) - listener, err := reg2.subscribe(&url) + err = reg2.Register(url) + assert.NoError(t, err) + listener, err := reg2.DoSubscribe(&url) if err != nil { t.Fatal(err) } @@ -99,10 +101,10 @@ func (suite *RegistryTestSuite) TestSubscribe() { func (suite *RegistryTestSuite) TestConsumerDestory() { t := suite.T() - url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) reg := initRegistry(t) - _, err := reg.subscribe(&url) + _, err := reg.DoSubscribe(&url) if err != nil { t.Fatal(err) } @@ -119,8 +121,9 @@ func (suite *RegistryTestSuite) TestProviderDestory() { t := suite.T() reg := initRegistry(t) - url, _ := common.NewURL(context.Background(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - reg.Register(url) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + err := reg.Register(url) + assert.NoError(t, err) //listener.Close() time.Sleep(1e9) diff --git a/registry/event.go b/registry/event.go index 24f5b72e8b27d4dc727e72d641d8bae3e00ff165..37d863d2162cb3b9d6a9f7eba8823286eb99441c 100644 --- a/registry/event.go +++ b/registry/event.go @@ -36,6 +36,7 @@ func init() { // service event ////////////////////////////////////////// +// ServiceEvent ... type ServiceEvent struct { Action remoting.EventType Service common.URL diff --git a/registry/mock_registry.go b/registry/mock_registry.go index 512c452e39082d619ffceae7f82d28127fbe2975..9591928eebd22bf2a99ec9dcfeb285c4519a3b90 100644 --- a/registry/mock_registry.go +++ b/registry/mock_registry.go @@ -30,11 +30,13 @@ import ( "github.com/apache/dubbo-go/common/logger" ) +// MockRegistry ... type MockRegistry struct { listener *listener destroyed *atomic.Bool } +// NewMockRegistry ... func NewMockRegistry(url *common.URL) (Registry, error) { registry := &MockRegistry{ destroyed: atomic.NewBool(false), @@ -43,17 +45,24 @@ func NewMockRegistry(url *common.URL) (Registry, error) { registry.listener = listener return registry, nil } + +// Register ... func (*MockRegistry) Register(url common.URL) error { return nil } +// Destroy ... func (r *MockRegistry) Destroy() { if r.destroyed.CAS(false, true) { } } + +// IsAvailable ... func (r *MockRegistry) IsAvailable() bool { return !r.destroyed.Load() } + +// GetUrl ... func (r *MockRegistry) GetUrl() common.URL { return common.URL{} } @@ -61,6 +70,8 @@ func (r *MockRegistry) GetUrl() common.URL { func (r *MockRegistry) subscribe(*common.URL) (Listener, error) { return r.listener, nil } + +// Subscribe ... func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) { go func() { for { @@ -81,17 +92,16 @@ func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) } for { - if serviceEvent, err := listener.Next(); err != nil { + serviceEvent, err := listener.Next() + if err != nil { listener.Close() time.Sleep(time.Duration(3) * time.Second) return - } else { - logger.Infof("update begin, service event: %v", serviceEvent.String()) - notifyListener.Notify(serviceEvent) } + logger.Infof("update begin, service event: %v", serviceEvent.String()) + notifyListener.Notify(serviceEvent) } - } }() } @@ -113,6 +123,7 @@ func (*listener) Close() { } +// MockEvent ... func (r *MockRegistry) MockEvent(event *ServiceEvent) { r.listener.listenChan <- event } diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go index 25cd3d09b5711e4e7db56cd8e40f3283f3252e10..a2237dca265f25b07b19a8e1f4fe5a5f6ea9183e 100644 --- a/registry/nacos/listener.go +++ b/registry/nacos/listener.go @@ -51,6 +51,7 @@ type nacosListener struct { subscribeParam *vo.SubscribeParam } +// NewNacosListener ... func NewNacosListener(url common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) { listener := &nacosListener{ namingClient: namingClient, diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go index a8b9fa83fa73858064e570722341c14f974f5c9e..965e91e894ac61562bfd25c8f564f789afd6c8a1 100644 --- a/registry/nacos/registry.go +++ b/registry/nacos/registry.go @@ -47,6 +47,7 @@ var ( ) const ( + //RegistryConnDelay registry connection delay RegistryConnDelay = 3 ) @@ -209,15 +210,15 @@ func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.Noti } for { - if serviceEvent, err := listener.Next(); err != nil { + serviceEvent, err := listener.Next() + if err != nil { logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) listener.Close() return - } else { - logger.Infof("update begin, service event: %v", serviceEvent.String()) - notifyListener.Notify(serviceEvent) } + logger.Infof("update begin, service event: %v", serviceEvent.String()) + notifyListener.Notify(serviceEvent) } } diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go index e6ab693cd3f5432fe30c2b83011cd56e44ac509f..7475b455c0dda09da65012465711ece264bb3dd5 100644 --- a/registry/nacos/registry_test.go +++ b/registry/nacos/registry_test.go @@ -18,7 +18,6 @@ package nacos import ( - "context" "encoding/json" "net/url" "strconv" @@ -36,14 +35,14 @@ import ( ) func TestNacosRegistry_Register(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) urlMap.Set(constant.INTERFACE_KEY, "com.ikurento.user.UserProvider") urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, err := newNacosRegistry(®url) assert.Nil(t, err) @@ -65,7 +64,7 @@ func TestNacosRegistry_Register(t *testing.T) { } func TestNacosRegistry_Subscribe(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -73,7 +72,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) { urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, _ := newNacosRegistry(®url) err := reg.Register(url) @@ -103,7 +102,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) { } func TestNacosRegistry_Subscribe_del(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -111,8 +110,8 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { urlMap.Set(constant.VERSION_KEY, "2.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") - url1, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) - url2, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + url2, _ := common.NewURL("dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, _ := newNacosRegistry(®url) err := reg.Register(url1) @@ -169,7 +168,7 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { } func TestNacosListener_Close(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -177,7 +176,7 @@ func TestNacosListener_Close(t *testing.T) { urlMap.Set(constant.VERSION_KEY, "1.0.0") urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") - url1, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) + url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) reg, _ := newNacosRegistry(®url) listener, err := reg.(*nacosRegistry).subscribe(&url1) assert.Nil(t, err) diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index 534a4b945965f332e49ff343557fa20355921454..748b8204d97e60c9803821290184fc5717c41025 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -18,12 +18,13 @@ package protocol import ( + "context" "strings" "sync" ) import ( - "github.com/dubbogo/gost/container/gxset" + gxset "github.com/dubbogo/gost/container/set" ) import ( @@ -337,6 +338,7 @@ func setProviderUrl(regURL *common.URL, providerURL *common.URL) { regURL.SubURL = providerURL } +// GetProtocol ... func GetProtocol() protocol.Protocol { if regProtocol != nil { return regProtocol @@ -356,10 +358,10 @@ func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoke } } -func (ivk *wrappedInvoker) Invoke(invocation protocol.Invocation) protocol.Result { +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()) - return ivk.invoker.Invoke(invocation) + return ivk.invoker.Invoke(ctx, invocation) } type providerConfigurationListener struct { diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go index 0c19da59df6e4fd2f663f9e8d541165fe26c3ffa..de57a0afa7529dd5c77c1fe5440b336cdd212fca 100644 --- a/registry/protocol/protocol_test.go +++ b/registry/protocol/protocol_test.go @@ -18,7 +18,6 @@ package protocol import ( - "context" "testing" "time" ) @@ -51,9 +50,8 @@ func referNormal(t *testing.T, regProtocol *registryProtocol) { extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) extension.SetCluster("mock", cluster.NewMockCluster) - url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url, _ := common.NewURL("mock://127.0.0.1:1111") suburl, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) @@ -76,9 +74,8 @@ func TestRefer(t *testing.T) { func TestMultiRegRefer(t *testing.T) { regProtocol := newRegistryProtocol() referNormal(t, regProtocol) - url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222") + url2, _ := common.NewURL("mock://127.0.0.1:2222") suburl2, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) @@ -98,9 +95,8 @@ func TestOneRegRefer(t *testing.T) { regProtocol := newRegistryProtocol() referNormal(t, regProtocol) - url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url2, _ := common.NewURL("mock://127.0.0.1:1111") suburl2, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) @@ -120,9 +116,8 @@ func exporterNormal(t *testing.T, regProtocol *registryProtocol) *common.URL { extension.SetProtocol("registry", GetProtocol) extension.SetRegistry("mock", registry.NewMockRegistry) extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) - url, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url, _ := common.NewURL("mock://127.0.0.1:1111") suburl, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue(constant.GROUP_KEY, "group"), @@ -148,9 +143,8 @@ func TestMultiRegAndMultiProtoExporter(t *testing.T) { regProtocol := newRegistryProtocol() exporterNormal(t, regProtocol) - url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:2222") + url2, _ := common.NewURL("mock://127.0.0.1:2222") suburl2, _ := common.NewURL( - context.TODO(), "jsonrpc://127.0.0.1:20000//", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) @@ -178,9 +172,8 @@ func TestOneRegAndProtoExporter(t *testing.T) { regProtocol := newRegistryProtocol() exporterNormal(t, regProtocol) - url2, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + url2, _ := common.NewURL("mock://127.0.0.1:1111") suburl2, _ := common.NewURL( - context.TODO(), "dubbo://127.0.0.1:20000/org.apache.dubbo-go.mockService", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue(constant.GROUP_KEY, "group"), @@ -242,7 +235,6 @@ func TestExportWithOverrideListener(t *testing.T) { return } overrideUrl, _ := common.NewURL( - context.Background(), "override://0:0:0:0/org.apache.dubbo-go.mockService?cluster=mock1&&group=group&&version=1.0.0", ) event := ®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: overrideUrl} @@ -256,7 +248,7 @@ func TestExportWithOverrideListener(t *testing.T) { func TestExportWithServiceConfig(t *testing.T) { extension.SetDefaultConfigurator(configurator.NewMockConfigurator) - ccUrl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + ccUrl, _ := common.NewURL("mock://127.0.0.1:1111") dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl) common_cfg.GetEnvInstance().SetDynamicConfiguration(dc) regProtocol := newRegistryProtocol() @@ -275,7 +267,7 @@ func TestExportWithServiceConfig(t *testing.T) { func TestExportWithApplicationConfig(t *testing.T) { extension.SetDefaultConfigurator(configurator.NewMockConfigurator) - ccUrl, _ := common.NewURL(context.TODO(), "mock://127.0.0.1:1111") + ccUrl, _ := common.NewURL("mock://127.0.0.1:1111") dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl) common_cfg.GetEnvInstance().SetDynamicConfiguration(dc) regProtocol := newRegistryProtocol() diff --git a/registry/registry.go b/registry/registry.go index c7279a29e1f423ca200aa2bf9390c127efcf10cb..d673864700e6ba99e8f0283247d53760b85598aa 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -21,7 +21,13 @@ import ( "github.com/apache/dubbo-go/common" ) -// Extension - Registry +/* + * -----------------------------------NOTICE--------------------------------------------- + * If there is no special case, you'd better inherit BaseRegistry and implement the + * FacadeBasedRegistry interface instead of directly implementing the Registry interface. + * -------------------------------------------------------------------------------------- + */ +// Registry Extension - Registry type Registry interface { common.Node //used for service provider calling , register services to registry @@ -38,11 +44,13 @@ type Registry interface { //mode2 : callback mode, subscribe with notify(notify listener). Subscribe(*common.URL, NotifyListener) } + +// NotifyListener ... type NotifyListener interface { Notify(*ServiceEvent) } -//Deprecated! +// Listener Deprecated! type Listener interface { Next() (*ServiceEvent, error) Close() diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go index 53a592609153003d7d6c24881bccde0dfe6cdde6..588e0c519288ed32a2453fac87d226b41d4a5194 100644 --- a/registry/zookeeper/listener.go +++ b/registry/zookeeper/listener.go @@ -18,7 +18,6 @@ package zookeeper import ( - "context" "strings" "sync" ) @@ -36,18 +35,23 @@ import ( zk "github.com/apache/dubbo-go/remoting/zookeeper" ) +// RegistryDataListener ... type RegistryDataListener struct { interestedURL []*common.URL listener config_center.ConfigurationListener } +// NewRegistryDataListener ... func NewRegistryDataListener(listener config_center.ConfigurationListener) *RegistryDataListener { return &RegistryDataListener{listener: listener, interestedURL: []*common.URL{}} } + +// AddInterestedURL ... func (l *RegistryDataListener) AddInterestedURL(url *common.URL) { l.interestedURL = append(l.interestedURL, url) } +// DataChange ... func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { // Intercept the last bit index := strings.Index(eventType.Path, "/providers/") @@ -56,7 +60,7 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { return false } url := eventType.Path[index+len("/providers/"):] - serviceURL, err := common.NewURL(context.TODO(), url) + serviceURL, err := common.NewURL(url) if err != nil { logger.Errorf("Listen NewURL(r{%s}) = error{%v} eventType.Path={%v}", url, err, eventType.Path) return false @@ -71,6 +75,7 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { return false } +// RegistryConfigurationListener ... type RegistryConfigurationListener struct { client *zk.ZookeeperClient registry *zkRegistry @@ -79,14 +84,18 @@ type RegistryConfigurationListener struct { closeOnce sync.Once } +// NewRegistryConfigurationListener for listening the event of zk. func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry) *RegistryConfigurationListener { - reg.wg.Add(1) + reg.WaitGroup().Add(1) return &RegistryConfigurationListener{client: client, registry: reg, events: make(chan *config_center.ConfigChangeEvent, 32), isClosed: false} } + +// Process ... func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) { l.events <- configType } +// Next ... func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { for { select { @@ -94,7 +103,7 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { logger.Warnf("listener's zk client connection is broken, so zk event listener exit now.") return nil, perrors.New("listener stopped") - case <-l.registry.done: + case <-l.registry.Done(): logger.Warnf("zk consumer register has quit, so zk event listener exit now.") return nil, perrors.New("listener stopped") @@ -111,11 +120,13 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { } } } + +// Close ... func (l *RegistryConfigurationListener) Close() { // ensure that the listener will be closed at most once. l.closeOnce.Do(func() { l.isClosed = true - l.registry.wg.Done() + l.registry.WaitGroup().Done() }) } diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go index 910d47b7e4e3d27c6f7245777cba1f46adc8e318..1a76b29a6f64e0329b289ce50218032a25f6f5cd 100644 --- a/registry/zookeeper/listener_test.go +++ b/registry/zookeeper/listener_test.go @@ -18,7 +18,6 @@ package zookeeper import ( - "context" "testing" ) @@ -34,9 +33,9 @@ import ( func Test_DataChange(t *testing.T) { listener := NewRegistryDataListener(&MockDataListener{}) - url, _ := common.NewURL(context.TODO(), "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") + 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-1.3.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") listener.AddInterestedURL(&url) - int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/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"}) + int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/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-1.3.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"}) assert.Equal(t, true, int) } diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go index 1defedc28a2d42183be8c2e5d77441d8831c1d30..f4e53dcc4219d947fea93a10bccc420811afd2b9 100644 --- a/registry/zookeeper/registry.go +++ b/registry/zookeeper/registry.go @@ -18,20 +18,16 @@ package zookeeper import ( - "context" "fmt" "net/url" - "os" - "strconv" "strings" "sync" "time" ) import ( - gxnet "github.com/dubbogo/gost/net" + "github.com/dubbogo/go-zookeeper/zk" perrors "github.com/pkg/errors" - "github.com/samuel/go-zookeeper/zk" ) import ( @@ -44,20 +40,11 @@ import ( ) const ( - RegistryZkClient = "zk registry" - RegistryConnDelay = 3 - MaxWaitInterval = time.Duration(3e9) -) - -var ( - processID = "" - localIP = "" + // RegistryZkClient zk client name + RegistryZkClient = "zk registry" ) func init() { - processID = fmt.Sprintf("%d", os.Getpid()) - localIP, _ = gxnet.GetLocalIP() - //plugins.PluggableRegistries["zookeeper"] = newZkRegistry extension.SetRegistry("zookeeper", newZkRegistry) } @@ -66,20 +53,13 @@ func init() { ///////////////////////////////////// type zkRegistry struct { - context context.Context - *common.URL - birth int64 // time of file birth, seconds since Epoch; 0 if unknown - wg sync.WaitGroup // wg+done for zk restart - done chan struct{} - - cltLock sync.Mutex - client *zookeeper.ZookeeperClient - services map[string]common.URL // service name + protocol -> service config - + registry.BaseRegistry + client *zookeeper.ZookeeperClient listenerLock sync.Mutex listener *zookeeper.ZkEventListener dataListener *RegistryDataListener configListener *RegistryConfigurationListener + cltLock sync.Mutex //for provider zkPath map[string]int // key = protocol://ip:port/interface } @@ -89,21 +69,17 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { err error r *zkRegistry ) - r = &zkRegistry{ - URL: url, - birth: time.Now().UnixNano(), - done: make(chan struct{}), - services: make(map[string]common.URL), - zkPath: make(map[string]int), + zkPath: make(map[string]int), } + r.InitBaseRegistry(url, r) err = zookeeper.ValidateZookeeperClient(r, zookeeper.WithZkName(RegistryZkClient)) if err != nil { return nil, err } + r.WaitGroup().Add(1) //zk client start successful, then wg +1 - r.wg.Add(1) go zookeeper.HandleClientRestart(r) r.listener = zookeeper.NewZkEventListener(r.client) @@ -113,10 +89,12 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { return r, nil } +// Options ... type Options struct { client *zookeeper.ZookeeperClient } +// Option ... type Option func(*Options) func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, *zkRegistry, error) { @@ -128,27 +106,41 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust ) r = &zkRegistry{ - URL: url, - birth: time.Now().UnixNano(), - done: make(chan struct{}), - services: make(map[string]common.URL), - zkPath: make(map[string]int), + zkPath: make(map[string]int), } - + r.InitBaseRegistry(url, r) c, r.client, _, err = zookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) if err != nil { return nil, nil, err } - r.wg.Add(1) + r.WaitGroup().Add(1) //zk client start successful, then wg +1 go zookeeper.HandleClientRestart(r) + r.InitListeners() + return c, r, nil +} +func (r *zkRegistry) InitListeners() { r.listener = zookeeper.NewZkEventListener(r.client) r.configListener = NewRegistryConfigurationListener(r.client, r) r.dataListener = NewRegistryDataListener(r.configListener) +} - return c, r, nil +func (r *zkRegistry) CreatePath(path string) error { + return r.ZkClient().Create(path) +} + +func (r *zkRegistry) DoRegister(root string, node string) error { + return r.registerTempZookeeperNode(root, node) +} + +func (r *zkRegistry) DoSubscribe(conf *common.URL) (registry.Listener, error) { + return r.getListener(conf) } +func (r *zkRegistry) CloseAndNilClient() { + r.client.Close() + r.client = nil +} func (r *zkRegistry) ZkClient() *zookeeper.ZookeeperClient { return r.client } @@ -161,221 +153,10 @@ func (r *zkRegistry) ZkClientLock() *sync.Mutex { return &r.cltLock } -func (r *zkRegistry) WaitGroup() *sync.WaitGroup { - return &r.wg -} - -func (r *zkRegistry) GetDone() chan struct{} { - return r.done -} - -func (r *zkRegistry) GetUrl() common.URL { - return *r.URL -} - -func (r *zkRegistry) Destroy() { +func (r *zkRegistry) CloseListener() { if r.configListener != nil { r.configListener.Close() } - close(r.done) - r.wg.Wait() - r.closeRegisters() -} - -func (r *zkRegistry) RestartCallBack() bool { - - // copy r.services - services := []common.URL{} - for _, confIf := range r.services { - services = append(services, confIf) - } - - flag := true - for _, confIf := range services { - err := r.register(confIf) - if err != nil { - logger.Errorf("(ZkProviderRegistry)register(conf{%#v}) = error{%#v}", - confIf, perrors.WithStack(err)) - flag = false - break - } - logger.Infof("success to re-register service :%v", confIf.Key()) - } - r.listener = zookeeper.NewZkEventListener(r.client) - r.configListener = NewRegistryConfigurationListener(r.client, r) - r.dataListener = NewRegistryDataListener(r.configListener) - - return flag -} - -func (r *zkRegistry) Register(conf common.URL) error { - var ( - ok bool - err error - ) - role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) - switch role { - case common.CONSUMER: - r.cltLock.Lock() - _, ok = r.services[conf.Key()] - r.cltLock.Unlock() - if ok { - return perrors.Errorf("Path{%s} has been registered", conf.Path) - } - - err = r.register(conf) - if err != nil { - return perrors.WithStack(err) - } - - r.cltLock.Lock() - r.services[conf.Key()] = conf - r.cltLock.Unlock() - logger.Debugf("(consumerZkConsumerRegistry)Register(conf{%#v})", conf) - - case common.PROVIDER: - - // Check if the service has been registered - r.cltLock.Lock() - // Note the difference between consumer and consumerZookeeperRegistry (consumer use conf.Path). - // Because the consumer wants to provide monitoring functions for the selector, - // the provider allows multiple groups or versions of the same service to be registered. - _, ok = r.services[conf.Key()] - r.cltLock.Unlock() - if ok { - return perrors.Errorf("Path{%s} has been registered", conf.Key()) - } - - err = r.register(conf) - if err != nil { - return perrors.WithMessagef(err, "register(conf:%+v)", conf) - } - - r.cltLock.Lock() - r.services[conf.Key()] = conf - r.cltLock.Unlock() - - logger.Debugf("(ZkProviderRegistry)Register(conf{%#v})", conf) - } - - return nil -} - -func (r *zkRegistry) service(c common.URL) string { - return url.QueryEscape(c.Service()) -} - -func (r *zkRegistry) register(c common.URL) error { - var ( - err error - //revision string - params url.Values - rawURL string - encodedURL string - dubboPath string - //conf config.URL - ) - - err = zookeeper.ValidateZookeeperClient(r, zookeeper.WithZkName(RegistryZkClient)) - if err != nil { - return perrors.WithStack(err) - } - params = url.Values{} - - c.RangeParams(func(key, value string) bool { - params.Add(key, value) - return true - }) - - params.Add("pid", processID) - params.Add("ip", localIP) - //params.Add("timeout", fmt.Sprintf("%d", int64(r.Timeout)/1e6)) - - role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) - switch role { - - case common.PROVIDER: - - if c.Path == "" || len(c.Methods) == 0 { - return perrors.Errorf("conf{Path:%s, Methods:%s}", c.Path, c.Methods) - } - // 先创建服务下面的provider node - dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER]) - r.cltLock.Lock() - err = r.client.Create(dubboPath) - r.cltLock.Unlock() - if err != nil { - logger.Errorf("zkClient.create(path{%s}) = error{%#v}", dubboPath, perrors.WithStack(err)) - return perrors.WithMessagef(err, "zkclient.Create(path:%s)", dubboPath) - } - params.Add("anyhost", "true") - - // Dubbo java consumer to start looking for the provider url,because the category does not match, - // the provider will not find, causing the consumer can not start, so we use consumers. - // DubboRole = [...]string{"consumer", "", "", "provider"} - // params.Add("category", (RoleType(PROVIDER)).Role()) - params.Add("category", (common.RoleType(common.PROVIDER)).String()) - params.Add("dubbo", "dubbo-provider-golang-"+constant.Version) - - params.Add("side", (common.RoleType(common.PROVIDER)).Role()) - - if len(c.Methods) == 0 { - params.Add("methods", strings.Join(c.Methods, ",")) - } - logger.Debugf("provider zk url params:%#v", params) - var host string - if c.Ip == "" { - host = localIP + ":" + c.Port - } else { - host = c.Ip + ":" + c.Port - } - - rawURL = fmt.Sprintf("%s://%s%s?%s", c.Protocol, host, c.Path, params.Encode()) - encodedURL = url.QueryEscape(rawURL) - - // Print your own registration service providers. - dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.PROVIDER)).String()) - logger.Debugf("provider path:%s, url:%s", dubboPath, rawURL) - - case common.CONSUMER: - dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.CONSUMER]) - r.cltLock.Lock() - err = r.client.Create(dubboPath) - r.cltLock.Unlock() - if err != nil { - logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err)) - return perrors.WithStack(err) - } - dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), common.DubboNodes[common.PROVIDER]) - r.cltLock.Lock() - err = r.client.Create(dubboPath) - r.cltLock.Unlock() - if err != nil { - logger.Errorf("zkClient.create(path{%s}) = error{%v}", dubboPath, perrors.WithStack(err)) - return perrors.WithStack(err) - } - - params.Add("protocol", c.Protocol) - - params.Add("category", (common.RoleType(common.CONSUMER)).String()) - params.Add("dubbo", "dubbogo-consumer-"+constant.Version) - - rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode()) - encodedURL = url.QueryEscape(rawURL) - - dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String()) - logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL) - - default: - return perrors.Errorf("@c{%v} type is not referencer or provider", c) - } - - err = r.registerTempZookeeperNode(dubboPath, encodedURL) - - if err != nil { - return perrors.WithMessagef(err, "registerTempZookeeperNode(path:%s, url:%s)", dubboPath, rawURL) - } - return nil } func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { @@ -405,53 +186,6 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { return nil } -func (r *zkRegistry) subscribe(conf *common.URL) (registry.Listener, error) { - return r.getListener(conf) -} -func sleepWait(n int) { - wait := time.Duration((n + 1) * 2e8) - if wait > MaxWaitInterval { - wait = MaxWaitInterval - } - time.Sleep(wait) -} - -//subscribe from registry -func (r *zkRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) { - n := 0 - for { - n++ - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - return - } - - listener, err := r.subscribe(url) - if err != nil { - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - return - } - logger.Warnf("getListener() = err:%v", perrors.WithStack(err)) - time.Sleep(time.Duration(RegistryConnDelay) * time.Second) - continue - } - - for { - if serviceEvent, err := listener.Next(); err != nil { - logger.Warnf("Selector.watch() = error{%v}", perrors.WithStack(err)) - listener.Close() - break - } else { - logger.Infof("update begin, service event: %v", serviceEvent.String()) - notifyListener.Notify(serviceEvent) - } - - } - sleepWait(n) - } -} - func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListener, error) { var ( zkListener *RegistryConfigurationListener @@ -488,22 +222,3 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen return zkListener, nil } - -func (r *zkRegistry) closeRegisters() { - r.cltLock.Lock() - defer r.cltLock.Unlock() - logger.Infof("begin to close provider zk client") - // Close the old client first to close the tmp node. - r.client.Close() - r.client = nil - r.services = nil -} - -func (r *zkRegistry) IsAvailable() bool { - select { - case <-r.done: - return false - default: - return true - } -} diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go index 841c38da7fbf1830b6f7c55809fc50d52468ef46..0d7623ca12a9b4e49f84ec988c796f2e913d537f 100644 --- a/registry/zookeeper/registry_test.go +++ b/registry/zookeeper/registry_test.go @@ -18,7 +18,6 @@ package zookeeper import ( - "context" "strconv" "testing" "time" @@ -35,20 +34,20 @@ import ( ) func Test_Register(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"})) ts, reg, _ := newMockZkRegistry(®url) defer ts.Stop() err := reg.Register(url) children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers") - assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-2.6.0%26.*.serviceid%3Dsoa.mock%26.*provider", children) + assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26category%3Dproviders%26cluster%3Dmock%26dubbo%3Ddubbo-provider-golang-1.3.0%26.*.serviceid%3Dsoa.mock%26.*provider", children) assert.NoError(t, err) } func Test_Subscribe(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) ts, reg, _ := newMockZkRegistry(®url) //provider register @@ -64,7 +63,7 @@ func Test_Subscribe(t *testing.T) { _, reg2, _ := newMockZkRegistry(®url, zookeeper.WithTestCluster(ts)) reg2.Register(url) - listener, _ := reg2.subscribe(&url) + listener, _ := reg2.DoSubscribe(&url) serviceEvent, _ := listener.Next() assert.NoError(t, err) @@ -76,8 +75,8 @@ func Test_Subscribe(t *testing.T) { } func Test_ConsumerDestory(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))) - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) ts, reg, err := newMockZkRegistry(®url) defer ts.Stop() @@ -85,7 +84,7 @@ func Test_ConsumerDestory(t *testing.T) { assert.NoError(t, err) err = reg.Register(url) assert.NoError(t, err) - _, err = reg.subscribe(&url) + _, err = reg.DoSubscribe(&url) assert.NoError(t, err) //listener.Close() @@ -96,8 +95,8 @@ func Test_ConsumerDestory(t *testing.T) { } func Test_ProviderDestory(t *testing.T) { - regurl, _ := common.NewURL(context.TODO(), "registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - url, _ := common.NewURL(context.TODO(), "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) + regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) ts, reg, err := newMockZkRegistry(®url) defer ts.Stop() diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go index 050968565387fd31871b0aa8e9969496d39f6534..ba3ea6e864923b1e70cc4a0d31ee98415807699c 100644 --- a/remoting/etcdv3/client.go +++ b/remoting/etcdv3/client.go @@ -36,16 +36,22 @@ import ( ) const ( - ConnDelay = 3 - MaxFailTimes = 15 + // ConnDelay connection dalay + ConnDelay = 3 + // MaxFailTimes max failure times + MaxFailTimes = 15 + // RegistryETCDV3Client client name RegistryETCDV3Client = "etcd registry" ) var ( + // ErrNilETCDV3Client ... ErrNilETCDV3Client = perrors.New("etcd raw client is nil") // full describe the ERR - ErrKVPairNotFound = perrors.New("k/v pair not found") + // ErrKVPairNotFound ... + ErrKVPairNotFound = perrors.New("k/v pair not found") ) +// Options ... type Options struct { name string endpoints []string @@ -54,30 +60,38 @@ type Options struct { heartbeat int // heartbeat second } +// Option ... type Option func(*Options) +// WithEndpoints ... func WithEndpoints(endpoints ...string) Option { return func(opt *Options) { opt.endpoints = endpoints } } + +// WithName ... func WithName(name string) Option { return func(opt *Options) { opt.name = name } } + +// WithTimeout ... func WithTimeout(timeout time.Duration) Option { return func(opt *Options) { opt.timeout = timeout } } +// WithHeartbeat ... func WithHeartbeat(heartbeat int) Option { return func(opt *Options) { opt.heartbeat = heartbeat } } +// ValidateClient ... func ValidateClient(container clientFacade, opts ...Option) error { options := &Options{ @@ -117,6 +131,7 @@ func ValidateClient(container clientFacade, opts ...Option) error { return nil } +// Client ... type Client struct { lock sync.RWMutex @@ -191,6 +206,7 @@ func (c *Client) stop() bool { return false } +// Close ... func (c *Client) Close() { if c == nil { @@ -309,6 +325,7 @@ func (c *Client) get(k string) (string, error) { return string(resp.Kvs[0].Value), nil } +// CleanKV ... func (c *Client) CleanKV() error { c.lock.RLock() @@ -408,10 +425,12 @@ func (c *Client) keepAliveKV(k string, v string) error { return nil } +// Done ... func (c *Client) Done() <-chan struct{} { return c.exit } +// Valid ... func (c *Client) Valid() bool { select { case <-c.exit: @@ -428,6 +447,7 @@ func (c *Client) Valid() bool { return true } +// Create ... func (c *Client) Create(k string, v string) error { err := c.put(k, v) @@ -437,6 +457,7 @@ func (c *Client) Create(k string, v string) error { return nil } +// Delete ... func (c *Client) Delete(k string) error { err := c.delete(k) @@ -447,6 +468,7 @@ func (c *Client) Delete(k string) error { return nil } +// RegisterTemp ... func (c *Client) RegisterTemp(basePath string, node string) (string, error) { completeKey := path.Join(basePath, node) @@ -459,6 +481,7 @@ func (c *Client) RegisterTemp(basePath string, node string) (string, error) { return completeKey, nil } +// GetChildrenKVList ... func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) { kList, vList, err := c.getChildren(k) @@ -468,6 +491,7 @@ func (c *Client) GetChildrenKVList(k string) ([]string, []string, error) { return kList, vList, nil } +// Get ... func (c *Client) Get(k string) (string, error) { v, err := c.get(k) @@ -478,6 +502,7 @@ func (c *Client) Get(k string) (string, error) { return v, nil } +// Watch ... func (c *Client) Watch(k string) (clientv3.WatchChan, error) { wc, err := c.watch(k) @@ -487,6 +512,7 @@ func (c *Client) Watch(k string) (clientv3.WatchChan, error) { return wc, nil } +// WatchWithPrefix ... func (c *Client) WatchWithPrefix(prefix string) (clientv3.WatchChan, error) { wc, err := c.watchWithPrefix(prefix) diff --git a/remoting/etcdv3/facade.go b/remoting/etcdv3/facade.go index 499044b8d77d3dcd8d32b0cb70cb78f84fae8ec4..35befc85e449ec02a6377faec300aa6b46bcc8bf 100644 --- a/remoting/etcdv3/facade.go +++ b/remoting/etcdv3/facade.go @@ -38,11 +38,12 @@ type clientFacade interface { SetClient(*Client) ClientLock() *sync.Mutex WaitGroup() *sync.WaitGroup //for wait group control, etcd client listener & etcd client container - GetDone() chan struct{} //for etcd client control + Done() chan struct{} //for etcd client control RestartCallBack() bool common.Node } +// HandleClientRestart ... func HandleClientRestart(r clientFacade) { var ( @@ -54,7 +55,7 @@ func HandleClientRestart(r clientFacade) { LOOP: for { select { - case <-r.GetDone(): + case <-r.Done(): logger.Warnf("(ETCDV3ProviderRegistry)reconnectETCDV3 goroutine exit now...") break LOOP // re-register all services @@ -71,7 +72,7 @@ LOOP: failTimes = 0 for { select { - case <-r.GetDone(): + case <-r.Done(): logger.Warnf("(ETCDV3ProviderRegistry)reconnectETCDRegistry goroutine exit now...") break LOOP case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // avoid connect frequent diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go index a4d5805a6dbf3c76f43cb6085653c791b33ab119..a51a68bce78f4f24658f96dac5dc8778a07a6d9a 100644 --- a/remoting/etcdv3/listener.go +++ b/remoting/etcdv3/listener.go @@ -33,6 +33,7 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// EventListener ... type EventListener struct { client *Client keyMapLock sync.Mutex @@ -40,6 +41,7 @@ type EventListener struct { wg sync.WaitGroup } +// NewEventListener ... func NewEventListener(client *Client) *EventListener { return &EventListener{ client: client, @@ -47,7 +49,7 @@ func NewEventListener(client *Client) *EventListener { } } -// Listen on a spec key +// ListenServiceNodeEvent Listen on a spec key // this method will return true when spec key deleted, // this method will return false when deep layer connection lose func (l *EventListener) ListenServiceNodeEvent(key string, listener ...remoting.DataListener) bool { @@ -134,7 +136,7 @@ func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remotin panic("unreachable") } -// Listen on a set of key with spec prefix +// ListenServiceNodeEventWithPrefix Listen on a set of key with spec prefix func (l *EventListener) ListenServiceNodeEventWithPrefix(prefix string, listener ...remoting.DataListener) { l.wg.Add(1) @@ -180,7 +182,7 @@ func timeSecondDuration(sec int) time.Duration { return time.Duration(sec) * time.Second } -// this func is invoked by etcdv3 ConsumerRegistry::Registe/ etcdv3 ConsumerRegistry::get/etcdv3 ConsumerRegistry::getListener +// ListenServiceEvent is invoked by etcdv3 ConsumerRegistry::Registe/ etcdv3 ConsumerRegistry::get/etcdv3 ConsumerRegistry::getListener // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent // | // --------> ListenServiceNodeEvent @@ -229,6 +231,7 @@ func (l *EventListener) ListenServiceEvent(key string, listener remoting.DataLis }(key) } +// Close ... func (l *EventListener) Close() { l.wg.Wait() } diff --git a/remoting/listener.go b/remoting/listener.go index 8d1e357d37ff92e7bf60121133998dc1745c9af8..3713ba0ccf9d98d4470741785a9490e657cf051c 100644 --- a/remoting/listener.go +++ b/remoting/listener.go @@ -21,6 +21,7 @@ import ( "fmt" ) +// DataListener ... type DataListener interface { DataChange(eventType Event) bool //bool is return for interface implement is interesting } @@ -29,11 +30,15 @@ type DataListener interface { // event type ////////////////////////////////////////// +// EventType ... type EventType int const ( + // EventTypeAdd ... EventTypeAdd = iota + // EventTypeDel ... EventTypeDel + // EventTypeUpdate ... EventTypeUpdate ) @@ -51,6 +56,7 @@ func (t EventType) String() string { // service event ////////////////////////////////////////// +// Event ... type Event struct { Path string Action EventType diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index a7fc568f567d720448d0be63c592fae5f8df9bbf..f95231b374230c93036e0fbd74aeca4ecfe57f46 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -25,8 +25,8 @@ import ( ) import ( + "github.com/dubbogo/go-zookeeper/zk" perrors "github.com/pkg/errors" - "github.com/samuel/go-zookeeper/zk" ) import ( @@ -35,14 +35,19 @@ import ( ) const ( - ConnDelay = 3 + // ConnDelay connection delay interval + ConnDelay = 3 + // MaxFailTimes max fail times MaxFailTimes = 15 ) var ( errNilZkClientConn = perrors.New("zookeeperclient{conn} is nil") + errNilChildren = perrors.Errorf("has none children") + errNilNode = perrors.Errorf("node does not exist") ) +// ZookeeperClient ... type ZookeeperClient struct { name string ZkAddrs []string @@ -54,6 +59,7 @@ type ZookeeperClient struct { eventRegistry map[string][]*chan struct{} } +// StateToString ... func StateToString(state zk.State) string { switch state { case zk.StateDisconnected: @@ -85,6 +91,7 @@ func StateToString(state zk.State) string { return "zookeeper unknown state" } +// Options ... type Options struct { zkName string client *ZookeeperClient @@ -92,14 +99,17 @@ type Options struct { ts *zk.TestCluster } +// Option ... type Option func(*Options) +// WithZkName ... func WithZkName(name string) Option { return func(opt *Options) { opt.zkName = name } } +// ValidateZookeeperClient ... func ValidateZookeeperClient(container zkClientFacade, opts ...Option) error { var ( err error @@ -173,12 +183,14 @@ func newZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (* return z, nil } +// WithTestCluster ... func WithTestCluster(ts *zk.TestCluster) Option { return func(opt *Options) { opt.ts = ts } } +// NewMockZookeeperClient ... func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event, error) { var ( err error @@ -224,6 +236,7 @@ func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) return ts, z, event, nil } +// HandleZkEvent ... func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) { var ( state int @@ -248,11 +261,13 @@ LOOP: logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.ZkAddrs, z.name) z.stop() z.Lock() - if z.Conn != nil { - z.Conn.Close() - z.Conn = nil - } + conn := z.Conn + z.Conn = nil z.Unlock() + if conn != nil { + conn.Close() + } + break LOOP case (int)(zk.EventNodeDataChanged), (int)(zk.EventNodeChildrenChanged): logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path) @@ -282,6 +297,7 @@ LOOP: } } +// RegisterEvent ... func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" || event == nil { return @@ -290,11 +306,13 @@ func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { z.Lock() a := z.eventRegistry[zkPath] a = append(a, event) + z.eventRegistry[zkPath] = a logger.Debugf("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event) z.Unlock() } +// UnregisterEvent ... func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { if zkPath == "" { return @@ -321,6 +339,7 @@ func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { } } +// Done ... func (z *ZookeeperClient) Done() <-chan struct{} { return z.exit } @@ -336,6 +355,7 @@ func (z *ZookeeperClient) stop() bool { return false } +// ZkConnValid ... func (z *ZookeeperClient) ZkConnValid() bool { select { case <-z.exit: @@ -353,6 +373,7 @@ func (z *ZookeeperClient) ZkConnValid() bool { return valid } +// Close ... func (z *ZookeeperClient) Close() { if z == nil { return @@ -361,14 +382,17 @@ func (z *ZookeeperClient) Close() { z.stop() z.Wait.Wait() z.Lock() - if z.Conn != nil { - z.Conn.Close() - z.Conn = nil - } + conn := z.Conn + z.Conn = nil z.Unlock() + if conn != nil { + conn.Close() + } + logger.Warnf("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs) } +// Create ... func (z *ZookeeperClient) Create(basePath string) error { var ( err error @@ -380,10 +404,12 @@ func (z *ZookeeperClient) Create(basePath string) error { tmpPath = path.Join(tmpPath, "/", str) err = errNilZkClientConn z.Lock() - if z.Conn != nil { - _, err = z.Conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll)) - } + conn := z.Conn z.Unlock() + if conn != nil { + _, err = conn.Create(tmpPath, []byte(""), 0, zk.WorldACL(zk.PermAll)) + } + if err != nil { if err == zk.ErrNodeExists { logger.Infof("zk.create(\"%s\") exists\n", tmpPath) @@ -397,6 +423,7 @@ func (z *ZookeeperClient) Create(basePath string) error { return nil } +// Delete ... func (z *ZookeeperClient) Delete(basePath string) error { var ( err error @@ -404,14 +431,16 @@ func (z *ZookeeperClient) Delete(basePath string) error { err = errNilZkClientConn z.Lock() - if z.Conn != nil { - err = z.Conn.Delete(basePath, -1) - } + conn := z.Conn z.Unlock() + if conn != nil { + err = conn.Delete(basePath, -1) + } return perrors.WithMessagef(err, "Delete(basePath:%s)", basePath) } +// RegisterTemp ... func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, error) { var ( err error @@ -424,10 +453,12 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er data = []byte("") zkPath = path.Join(basePath) + "/" + node z.Lock() - if z.Conn != nil { - tmpPath, err = z.Conn.Create(zkPath, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) - } + conn := z.Conn z.Unlock() + if conn != nil { + tmpPath, err = conn.Create(zkPath, data, zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) + } + //if err != nil && err != zk.ErrNodeExists { if err != nil { logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)\n", zkPath, perrors.WithStack(err)) @@ -438,6 +469,7 @@ func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, er return tmpPath, nil } +// RegisterTempSeq ... func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, error) { var ( err error @@ -446,15 +478,17 @@ func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, err = errNilZkClientConn z.Lock() - if z.Conn != nil { - tmpPath, err = z.Conn.Create( + conn := z.Conn + z.Unlock() + if conn != nil { + tmpPath, err = conn.Create( path.Join(basePath)+"/", data, zk.FlagEphemeral|zk.FlagSequence, zk.WorldACL(zk.PermAll), ) } - z.Unlock() + logger.Debugf("zookeeperClient.RegisterTempSeq(basePath{%s}) = tempPath{%s}", basePath, tmpPath) if err != nil && err != zk.ErrNodeExists { logger.Errorf("zkClient{%s} conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)\n", @@ -466,37 +500,44 @@ func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, return tmpPath, nil } +// GetChildrenW ... func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { var ( err error children []string stat *zk.Stat - event <-chan zk.Event + watcher *zk.Watcher ) err = errNilZkClientConn z.Lock() - if z.Conn != nil { - children, stat, event, err = z.Conn.ChildrenW(path) - } + conn := z.Conn z.Unlock() + if conn != nil { + children, stat, watcher, err = conn.ChildrenW(path) + } + if err != nil { + if err == zk.ErrNoChildrenForEphemerals { + return nil, nil, errNilChildren + } if err == zk.ErrNoNode { - return nil, nil, perrors.Errorf("path{%s} has none children", path) + return nil, nil, errNilNode } logger.Errorf("zk.ChildrenW(path{%s}) = error(%v)", path, err) return nil, nil, perrors.WithMessagef(err, "zk.ChildrenW(path:%s)", path) } if stat == nil { - return nil, nil, perrors.Errorf("path{%s} has none children", path) + return nil, nil, perrors.Errorf("path{%s} get stat is nil", path) } if len(children) == 0 { - return nil, nil, perrors.Errorf("path{%s} has none children", path) + return nil, nil, errNilChildren } - return children, event, nil + return children, watcher.EvtCh, nil } +// GetChildren ... func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { var ( err error @@ -506,10 +547,12 @@ func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { err = errNilZkClientConn z.Lock() - if z.Conn != nil { - children, stat, err = z.Conn.Children(path) - } + conn := z.Conn z.Unlock() + if conn != nil { + children, stat, err = conn.Children(path) + } + if err != nil { if err == zk.ErrNoNode { return nil, perrors.Errorf("path{%s} has none children", path) @@ -521,25 +564,28 @@ func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { return nil, perrors.Errorf("path{%s} has none children", path) } if len(children) == 0 { - return nil, perrors.Errorf("path{%s} has none children", path) + return nil, errNilChildren } return children, nil } +// ExistW ... func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { var ( - exist bool - err error - event <-chan zk.Event + exist bool + err error + watcher *zk.Watcher ) err = errNilZkClientConn z.Lock() - if z.Conn != nil { - exist, _, event, err = z.Conn.ExistsW(zkPath) - } + conn := z.Conn z.Unlock() + if conn != nil { + exist, _, watcher, err = conn.ExistsW(zkPath) + } + if err != nil { logger.Warnf("zkClient{%s}.ExistsW(path{%s}) = error{%v}.", z.name, zkPath, perrors.WithStack(err)) return nil, perrors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath) @@ -549,9 +595,10 @@ func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { return nil, perrors.Errorf("zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath) } - return event, nil + return watcher.EvtCh, nil } +// GetContent ... 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 f1bd0c2cb38669ad968bd83efae166a4432c6e2d..cb41eb326be95470e39694fc5df233fdf073b905 100644 --- a/remoting/zookeeper/client_test.go +++ b/remoting/zookeeper/client_test.go @@ -24,7 +24,7 @@ import ( ) import ( - "github.com/samuel/go-zookeeper/zk" + "github.com/dubbogo/go-zookeeper/zk" "github.com/stretchr/testify/assert" ) @@ -133,3 +133,12 @@ func TestRegisterTempSeq(t *testing.T) { states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} verifyEventStateOrder(t, event, states, "event channel") } + +func Test_UnregisterEvent(t *testing.T) { + client := &ZookeeperClient{} + client.eventRegistry = make(map[string][]*chan struct{}) + array := []*chan struct{}{} + array = append(array, new(chan struct{})) + client.eventRegistry["test"] = array + client.UnregisterEvent("test", new(chan struct{})) +} diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index cdc7ead61226906a629fdb99b6b966ada5ee5253..055db4f716a914354d1bada653fbc0a850b615b5 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -35,11 +35,12 @@ type zkClientFacade interface { SetZkClient(*ZookeeperClient) ZkClientLock() *sync.Mutex WaitGroup() *sync.WaitGroup //for wait group control, zk client listener & zk client container - GetDone() chan struct{} //for zk client control + Done() chan struct{} //for zk client control RestartCallBack() bool common.Node } +// HandleClientRestart ... func HandleClientRestart(r zkClientFacade) { var ( err error @@ -51,7 +52,7 @@ func HandleClientRestart(r zkClientFacade) { LOOP: for { select { - case <-r.GetDone(): + case <-r.Done(): logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") break LOOP // re-register all services @@ -67,7 +68,7 @@ LOOP: failTimes = 0 for { select { - case <-r.GetDone(): + case <-r.Done(): logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") break LOOP case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // Prevent crazy reconnection zk. diff --git a/remoting/zookeeper/facade_test.go b/remoting/zookeeper/facade_test.go index 58e0d69dcfd9bf645c147f6e920e56ed5f3951eb..97ea775652cf82ce86388fb376832ccb7e07a205 100644 --- a/remoting/zookeeper/facade_test.go +++ b/remoting/zookeeper/facade_test.go @@ -18,13 +18,12 @@ package zookeeper import ( - "context" "sync" "testing" "time" ) import ( - "github.com/samuel/go-zookeeper/zk" + "github.com/dubbogo/go-zookeeper/zk" "github.com/stretchr/testify/assert" ) import ( @@ -55,7 +54,7 @@ func (r *mockFacade) WaitGroup() *sync.WaitGroup { return &r.wg } -func (r *mockFacade) GetDone() chan struct{} { +func (r *mockFacade) Done() chan struct{} { return r.done } @@ -79,7 +78,7 @@ func Test_Facade(t *testing.T) { ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) defer ts.Stop() - url, _ := common.NewURL(context.Background(), "mock://127.0.0.1") + url, _ := common.NewURL("mock://127.0.0.1") mock := &mockFacade{client: z, URL: &url} go HandleClientRestart(mock) states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index 9521ea749027582c015ac998a6f6f68d350cc3bc..4493c06dc3f13d59b9388268613fe9e08a14033e 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -25,8 +25,8 @@ import ( import ( "github.com/dubbogo/getty" + "github.com/dubbogo/go-zookeeper/zk" perrors "github.com/pkg/errors" - "github.com/samuel/go-zookeeper/zk" ) import ( @@ -34,6 +34,7 @@ import ( "github.com/apache/dubbo-go/remoting" ) +// ZkEventListener ... type ZkEventListener struct { client *ZookeeperClient pathMapLock sync.Mutex @@ -41,6 +42,7 @@ type ZkEventListener struct { wg sync.WaitGroup } +// NewZkEventListener ... func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { return &ZkEventListener{ client: client, @@ -48,10 +50,12 @@ func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { } } +// SetClient ... func (l *ZkEventListener) SetClient(client *ZookeeperClient) { l.client = client } +// ListenServiceNodeEvent ... func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener ...remoting.DataListener) bool { l.wg.Add(1) defer l.wg.Done() @@ -106,8 +110,17 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li newChildren, err := l.client.GetChildren(zkPath) if err != nil { - logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err)) - return + if err == errNilChildren { + content, _, err := l.client.Conn.Get(zkPath) + if err != nil { + logger.Errorf("Get new node path {%v} 's content error,message is {%v}", zkPath, perrors.WithStack(err)) + } else { + listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeUpdate, Content: string(content)}) + } + + } else { + logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err)) + } } // a node was added -- listen the new node @@ -177,7 +190,7 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi if MaxFailTimes <= failTimes { failTimes = MaxFailTimes } - logger.Warnf("listenDirEvent(path{%s}) = error{%v}", zkPath, err) + logger.Infof("listenDirEvent(path{%s}) = error{%v}", zkPath, err) // clear the event channel CLEAR: for { @@ -188,6 +201,11 @@ func (l *ZkEventListener) listenDirEvent(zkPath string, listener remoting.DataLi } } l.client.RegisterEvent(zkPath, &event) + if err == errNilNode { + logger.Warnf("listenDirEvent(path{%s}) got errNilNode,so exit listen", zkPath) + l.client.UnregisterEvent(zkPath, &event) + return + } select { case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): l.client.UnregisterEvent(zkPath, &event) @@ -262,55 +280,11 @@ func timeSecondDuration(sec int) time.Duration { return time.Duration(sec) * time.Second } -// this func is invoked by ZkConsumerRegistry::Register/ZkConsumerRegistry::get/ZkConsumerRegistry::getListener +// ListenServiceEvent is invoked by ZkConsumerRegistry::Register/ZkConsumerRegistry::get/ZkConsumerRegistry::getListener // registry.go:Listen -> listenServiceEvent -> listenDirEvent -> ListenServiceNodeEvent // | // --------> ListenServiceNodeEvent func (l *ZkEventListener) ListenServiceEvent(zkPath string, listener remoting.DataListener) { - var ( - err error - dubboPath string - children []string - ) - - l.pathMapLock.Lock() - _, ok := l.pathMap[zkPath] - l.pathMapLock.Unlock() - if ok { - logger.Warnf("@zkPath %s has already been listened.", zkPath) - return - } - - l.pathMapLock.Lock() - l.pathMap[zkPath] = struct{}{} - l.pathMapLock.Unlock() - - logger.Infof("listen dubbo provider path{%s} event and wait to get all provider zk nodes", zkPath) - children, err = l.client.GetChildren(zkPath) - if err != nil { - children = nil - logger.Warnf("fail to get children of zk path{%s}", zkPath) - } - - for _, c := range children { - // listen l service node - dubboPath = path.Join(zkPath, c) - content, _, err := l.client.Conn.Get(dubboPath) - if err != nil { - logger.Errorf("Get new node path {%v} 's content error,message is {%v}", dubboPath, perrors.WithStack(err)) - } - if !listener.DataChange(remoting.Event{Path: dubboPath, Action: remoting.EventTypeAdd, Content: string(content)}) { - continue - } - logger.Infof("listen dubbo service key{%s}", dubboPath) - go func(zkPath string, listener remoting.DataListener) { - if l.ListenServiceNodeEvent(zkPath) { - listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) - } - logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath) - }(dubboPath, listener) - } - logger.Infof("listen dubbo path{%s}", zkPath) go func(zkPath string, listener remoting.DataListener) { l.listenDirEvent(zkPath, listener) @@ -322,6 +296,7 @@ func (l *ZkEventListener) valid() bool { return l.client.ZkConnValid() } +// Close ... func (l *ZkEventListener) Close() { l.wg.Wait() } diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go index aa627c7e8a53ef87fb39446b05d4001bcf18cf3f..43e9aca3f44470873c3c97ec2447bebcc57e5545 100644 --- a/remoting/zookeeper/listener_test.go +++ b/remoting/zookeeper/listener_test.go @@ -24,7 +24,7 @@ import ( "time" ) import ( - "github.com/samuel/go-zookeeper/zk" + "github.com/dubbogo/go-zookeeper/zk" "github.com/stretchr/testify/assert" ) import ( @@ -97,12 +97,11 @@ func TestListener(t *testing.T) { listener := NewZkEventListener(client) dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait} listener.ListenServiceEvent("/dubbo", dataListener) - + time.Sleep(1 * time.Second) _, err := client.Conn.Set("/dubbo/dubbo.properties", []byte(changedData), 1) assert.NoError(t, err) wait.Wait() assert.Equal(t, changedData, dataListener.eventList[1].Content) - client.Close() }