diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 0000000000000000000000000000000000000000..732e426fec2a7b3efa8899ee6b1536e660b142bb --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,112 @@ +name: CI + +on: + push: + branches: [master, develop] + pull_request: + branches: "*" + +jobs: + + build: + name: ${{ matrix.os }} - Go ${{ matrix.go_version }} + runs-on: ${{ matrix.os }} + strategy: + # If you want to matrix build , you can append the following list. + matrix: + go_version: + - 1.13 + os: + - ubuntu-latest + + env: + DING_TOKEN: "6374f1bf8d4f23cde81d4a4b8c1f0bc98cc92b5151ca938ab938d3d7f4230fc4" + DING_SIGN: "SECa98677289194bb0e5caec3051301d06515750ff1bd2f932a4704298afb2e0ae6" + + steps: + + - name: Set up Go 1.x + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go_version }} + id: go + + - name: Check out code into the Go module directory + uses: actions/checkout@v2 + + - name: Cache dependencies + uses: actions/cache@v2 + with: + # Cache + path: ~/go/pkg/mod + # Cache key + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + # An ordered list of keys to use for restoring the cache if no cache hit occurred for key + restore-keys: | + ${{ runner.os }}-go- + + - name: Get dependencies + run: | + if [ -f Gopkg.toml ]; then + curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh + dep ensure + else + go get -v -t -d ./... + fi + + - name: License Check + run: | + go fmt ./... && [[ -z `git status -s` ]] + sh before_validate_license.sh + chmod u+x /tmp/tools/license/license-header-checker + /tmp/tools/license/license-header-checker -v -a -r -i vendor /tmp/tools/license/license.txt . go && [[ -z `git status -s` ]] + + - name: Test + run: | + chmod u+x before_ut.sh && ./before_ut.sh + go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic + chmod +x integrate_test.sh && ./integrate_test.sh + + - name: Coverage + run: bash <(curl -s https://codecov.io/bash) + + # Because the contexts of push and PR are different, there are two Notify. + # Notifications are triggered only in the apache/dubbo-go repository. + - name: DingTalk Message Notify only Push + uses: zcong1993/actions-ding@v3.0.1 + # Whether job is successful or not, always () is always true. + if: | + always() && + github.event_name == 'push' && + github.repository == 'apache/dubbo-go' + with: + # DingDing bot token + dingToken: ${{ env.DING_TOKEN }} + secret: ${{ env.DING_SIGN }} + # Post Body to send + body: | + { + "msgtype": "markdown", + "markdown": { + "title": "Github Actions", + "text": "## Github Actions \n - name: CI \n - repository: ${{ github.repository }} \n - trigger: ${{ github.actor }} \n - event: ${{ github.event_name }} \n - ref: ${{ github.ref }} \n - status: [${{ job.status }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) \n - environment: ${{ runner.os }} \n - SHA: [${{ github.sha }}](${{ github.event.compare }})" + } + } + + - name: DingTalk Message Notify only PR + uses: zcong1993/actions-ding@v3.0.1 + if: | + always() && + github.event_name == 'pull_request' && + github.repository == 'apache/dubbo-go' + with: + dingToken: ${{ env.DING_TOKEN }} + secret: ${{ env.DING_SIGN }} + body: | + { + "msgtype": "markdown", + "markdown": { + "title": "Github Actions", + "text": "## Github Actions \n - name: CI \n - repository: ${{ github.repository }} \n - pr_title: ${{ github.event.pull_request.title }} \n - trigger: ${{ github.actor }} \n - event: ${{ github.event_name }} \n - ref: [${{ github.ref }}](${{ github.event.pull_request._links.html.href }}) \n - status: [${{ job.status }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}) \n - environment: ${{ runner.os }} \n > SHA: [${{ github.sha }}](${{ github.event.pull_request._links.html.href }})" + } + } diff --git a/.gitignore b/.gitignore index fabff68b874df4c2a7de15ce91798e9bb963b358..8158b497e3620a15fc5a52e91020e1fcebd5be41 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,8 @@ config_center/zookeeper/zookeeper-4unittest/ registry/zookeeper/zookeeper-4unittest/ metadata/report/zookeeper/zookeeper-4unittest/ registry/consul/agent* +metadata/report/consul/agent* +remoting/consul/agent* config_center/apollo/mockDubbog.properties.json # vim stuff diff --git a/CHANGE.md b/CHANGE.md index 90cb5a1443e8062125cbb9f2b3cc0ac1cf759d06..bf62511f8037e17ab7ef2d300ea4f2ae7d6344f6 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,6 +1,35 @@ # Release Notes --- +## 1.5.2 + +### New Features +- [Add consul service discovery](https://github.com/apache/dubbo-go/pull/701) [@zhangshen023](https://github.com/zhangshen023) +- [Add File system service discovery](https://github.com/apache/dubbo-go/pull/732) [@DogBaoBao](https://github.com/DogBaoBao) +- [Migrate travis Ci to Github Actions](https://github.com/apache/dubbo-go/pull/752) [@sdttttt](https://github.com/sdttttt) +- [Add sentinel-golang flow control/circuit breaker](https://github.com/apache/dubbo-go/pull/748) [@louyuting](https://github.com/louyuting) +- [Add dubbo-go docs and blog into doc directory](https://github.com/apache/dubbo-go/pull/767) [@oaoit](https://github.com/oaoit) + +### Enhancement +- [Add address notification batch mode](https://github.com/apache/dubbo-go/pull/741) [@beiwei30](https://github.com/beiwei30) +- [Refactor network and codec model](https://github.com/apache/dubbo-go/pull/673) [@fangyincheng](https://github.com/fangyincheng) [@georgehao](https://github.com/georgehao) +- [Remove unnecessary return and judgement](https://github.com/apache/dubbo-go/pull/730) [@YongHaoWu](https://github.com/YongHaoWu) +- [Improve exporter append method](https://github.com/apache/dubbo-go/pull/722) [@gaoxinge](https://github.com/gaoxinge) +- [Refactor for proxyInvoker cannot be extended](https://github.com/apache/dubbo-go/pull/747) [@cvictory](https://github.com/cvictory) +- [Refactor attachment type from map\[string\]stiring to map\[string\]interface{}](https://github.com/apache/dubbo-go/pull/713) [@cvictory](https://github.com/cvictory) +- [Improve map access concurrency](https://github.com/apache/dubbo-go/pull/739) [@skyao](https://github.com/skyao) +- [Improve code quantity](https://github.com/apache/dubbo-go/pull/763) [@gaoxinge](https://github.com/gaoxinge) + +### Bugfixes +- [Fix etcdv3 lease](https://github.com/apache/dubbo-go/pull/738) [@zhangshen023](https://github.com/zhangshen023) +- [Fix rename SethealthChecker to SetHealthChecker](https://github.com/apache/dubbo-go/pull/746) [@watermelo](https://github.com/watermelo) +- [Fix init config problem in HystrixFilter](https://github.com/apache/dubbo-go/pull/731) [@YGrylls](https://github.com/YGrylls) +- [Fix zookeeper listener report error after started](https://github.com/apache/dubbo-go/pull/735) [@wenxuwan](https://github.com/wenxuwan) + +Milestone: [https://github.com/apache/dubbo-go/milestone/4](https://github.com/apache/dubbo-go/milestone/4?closed=1) + +Project: [https://github.com/apache/dubbo-go/projects/10](https://github.com/apache/dubbo-go/projects/10) + ## 1.5.1 ### New Features diff --git a/README.md b/README.md index 9e1edd3af1cd8957b1daa9b9fe2cadf121bc2d6d..492e7d00e515a88bc2c580cbfd43effa1d1811ac 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,8 @@ Apache License, Version 2.0 ## Release note ## +[v1.5.2 - Sep 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.2) + [v1.5.1 - Aug 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.1) [v1.5.0 - July 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0) diff --git a/README_CN.md b/README_CN.md index b76d8983deae427f9317c4f930f0e06da479f484..b3e375e1f7e690ee6d943036c0ad1dad10abe398 100644 --- a/README_CN.md +++ b/README_CN.md @@ -15,6 +15,10 @@ Apache License, Version 2.0 ## 鍙戝竷鏃ュ織 ## +[v1.5.2 - 2020骞�9鏈�23鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.2) + +[v1.5.1 - 2020骞�8鏈�23鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.1) + [v1.5.0 - 2020骞�7鏈�24鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.0) [v1.4.0 - 2020骞�3鏈�17鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.0) diff --git a/cluster/cluster_impl/zone_aware_cluster_invoker_test.go b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go index cd201a42c759354ca536ea3e9e77116d89ea8b4b..7f77f33166de293836c15391f5eedd5a18084dbe 100644 --- a/cluster/cluster_impl/zone_aware_cluster_invoker_test.go +++ b/cluster/cluster_impl/zone_aware_cluster_invoker_test.go @@ -44,7 +44,7 @@ func TestZoneWareInvokerWithPreferredSuccess(t *testing.T) { //defer ctrl.Finish() mockResult := &protocol.RPCResult{ - Attrs: map[string]string{constant.PREFERRED_KEY: "true"}, + Attrs: map[string]interface{}{constant.PREFERRED_KEY: "true"}, Rest: rest{tried: 0, success: true}} var invokers []protocol.Invoker @@ -99,7 +99,7 @@ func TestZoneWareInvokerWithWeightSuccess(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( func(invocation protocol.Invocation) protocol.Result { return &protocol.RPCResult{ - Attrs: map[string]string{constant.WEIGHT_KEY: w1}, + Attrs: map[string]interface{}{constant.WEIGHT_KEY: w1}, Rest: rest{tried: 0, success: true}} }).MaxTimes(100) } else { @@ -107,7 +107,7 @@ func TestZoneWareInvokerWithWeightSuccess(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( func(invocation protocol.Invocation) protocol.Result { return &protocol.RPCResult{ - Attrs: map[string]string{constant.WEIGHT_KEY: w2}, + Attrs: map[string]interface{}{constant.WEIGHT_KEY: w2}, Rest: rest{tried: 0, success: true}} }).MaxTimes(100) } @@ -154,7 +154,7 @@ func TestZoneWareInvokerWithZoneSuccess(t *testing.T) { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn( func(invocation protocol.Invocation) protocol.Result { return &protocol.RPCResult{ - Attrs: map[string]string{constant.ZONE_KEY: zoneValue}, + Attrs: map[string]interface{}{constant.ZONE_KEY: zoneValue}, Rest: rest{tried: 0, success: true}} }) invokers = append(invokers, invoker) diff --git a/cluster/router/chan.go b/cluster/router/chan.go index 6904e1734a7cbdaa00afa1b30797d19ca502453c..e3e84b81f3f812fcb0bcfbcddc200b4c54e31213 100644 --- a/cluster/router/chan.go +++ b/cluster/router/chan.go @@ -17,9 +17,16 @@ package router +import ( + "github.com/apache/dubbo-go/protocol" +) + // Chain type Chain interface { router // AddRouters Add routers AddRouters([]PriorityRouter) + + // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change. + SetInvokers(invokers []protocol.Invoker) } diff --git a/cluster/router/healthcheck/default_health_check.go b/cluster/router/healthcheck/default_health_check.go index c693b86ecdab7b32936185fe4bd614bd0f83fbeb..c522bdf0f98244f5f8bdfba6085cb45dd89c1004 100644 --- a/cluster/router/healthcheck/default_health_check.go +++ b/cluster/router/healthcheck/default_health_check.go @@ -31,7 +31,7 @@ import ( ) func init() { - extension.SethealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker) + extension.SetHealthChecker(constant.DEFAULT_HEALTH_CHECKER, NewDefaultHealthChecker) } // DefaultHealthChecker is the default implementation of HealthChecker, which determines the health status of @@ -85,7 +85,7 @@ func (c *DefaultHealthChecker) getCircuitBreakerSleepWindowTime(status *protocol } else if diff > constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF { diff = constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF } - sleepWindow := (1 << diff) * c.GetCircuitTrippedTimeoutFactor() + sleepWindow := (1 << uint(diff)) * c.GetCircuitTrippedTimeoutFactor() if sleepWindow > constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS { sleepWindow = constant.MAX_CIRCUIT_TRIPPED_TIMEOUT_IN_MS } diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go index a5f1dc13d9385fe6bc230e79337230e674b97e96..fafed68c76414ed24b40f8d691858da8d3622e44 100644 --- a/cluster/router/tag/tag_router.go +++ b/cluster/router/tag/tag_router.go @@ -69,6 +69,7 @@ func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocati var ( result []protocol.Invoker addresses []string + tag string ) if !c.isEnabled() || len(invokers) == 0 { @@ -83,9 +84,14 @@ func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocati // since the rule can be changed by config center, we should copy one to use. tagRouterRuleCopy := new(RouterRule) _ = copier.Copy(tagRouterRuleCopy, c.tagRouterRule) - tag, ok := invocation.Attachments()[constant.Tagkey] + tagValue, ok := invocation.Attachments()[constant.Tagkey] if !ok { tag = url.GetParam(constant.Tagkey, "") + } else { + tag, ok = tagValue.(string) + if !ok { + tag = url.GetParam(constant.Tagkey, "") + } } // if we are requesting for a Provider with a specific tag diff --git a/common/constant/default.go b/common/constant/default.go index 629aa32392a0151046eaaea67287618eae02158d..4165942a615e220f6384a898b07c04bafd39c3b0 100644 --- a/common/constant/default.go +++ b/common/constant/default.go @@ -45,6 +45,7 @@ const ( DEFAULT_REST_CLIENT = "resty" DEFAULT_REST_SERVER = "go-restful" DEFAULT_PORT = 20000 + DEFAULT_SERIALIZATION = HESSIAN2_SERIALIZATION ) const ( diff --git a/common/constant/key.go b/common/constant/key.go index 7c45a1397d8767510f1f8b92f4e82f0ece05c810..943338f8e6f13512d96cfed4cbc4f275d6aab2cb 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -22,31 +22,32 @@ const ( ) const ( - PORT_KEY = "port" - GROUP_KEY = "group" - VERSION_KEY = "version" - INTERFACE_KEY = "interface" - PATH_KEY = "path" - PROTOCOL_KEY = "protocol" - SERVICE_KEY = "service" - METHODS_KEY = "methods" - TIMEOUT_KEY = "timeout" - CATEGORY_KEY = "category" - CHECK_KEY = "check" - ENABLED_KEY = "enabled" - SIDE_KEY = "side" - OVERRIDE_PROVIDERS_KEY = "providerAddresses" - BEAN_NAME_KEY = "bean.name" - GENERIC_KEY = "generic" - CLASSIFIER_KEY = "classifier" - TOKEN_KEY = "token" - LOCAL_ADDR = "local-addr" - REMOTE_ADDR = "remote-addr" - PATH_SEPARATOR = "/" - DUBBO_KEY = "dubbo" - RELEASE_KEY = "release" - ANYHOST_KEY = "anyhost" - SSL_ENABLED_KEY = "ssl-enabled" + GROUP_KEY = "group" + VERSION_KEY = "version" + INTERFACE_KEY = "interface" + PATH_KEY = "path" + SERVICE_KEY = "service" + METHODS_KEY = "methods" + TIMEOUT_KEY = "timeout" + CATEGORY_KEY = "category" + CHECK_KEY = "check" + ENABLED_KEY = "enabled" + SIDE_KEY = "side" + OVERRIDE_PROVIDERS_KEY = "providerAddresses" + BEAN_NAME_KEY = "bean.name" + GENERIC_KEY = "generic" + CLASSIFIER_KEY = "classifier" + TOKEN_KEY = "token" + LOCAL_ADDR = "local-addr" + REMOTE_ADDR = "remote-addr" + DEFAULT_REMOTING_TIMEOUT = 3000 + RELEASE_KEY = "release" + ANYHOST_KEY = "anyhost" + PORT_KEY = "port" + PROTOCOL_KEY = "protocol" + PATH_SEPARATOR = "/" + DUBBO_KEY = "dubbo" + SSL_ENABLED_KEY = "ssl-enabled" ) const ( @@ -81,6 +82,7 @@ const ( EXECUTE_REJECTED_EXECUTION_HANDLER_KEY = "execute.limit.rejected.handler" PROVIDER_SHUTDOWN_FILTER = "pshutdown" CONSUMER_SHUTDOWN_FILTER = "cshutdown" + SERIALIZATION_KEY = "serialization" PID_KEY = "pid" SYNC_REPORT_KEY = "sync.report" RETRY_PERIOD_KEY = "retry.period" @@ -169,6 +171,10 @@ const ( NACOS_USERNAME = "username" ) +const ( + FILE_KEY = "file" +) + const ( ZOOKEEPER_KEY = "zookeeper" ) @@ -177,6 +183,18 @@ const ( ETCDV3_KEY = "etcdv3" ) +const ( + CONSUL_KEY = "consul" + CHECK_PASS_INTERVAL = "consul-check-pass-interval" + // default time-to-live in millisecond + DEFAULT_CHECK_PASS_INTERVAL = 16000 + QUERY_TAG = "consul_query_tag" + ACL_TOKEN = "acl-token" + // default deregister critical server after + DEFAULT_DEREGISTER_TIME = "20s" + DEREGISTER_AFTER = "consul-deregister-critical-service-after" +) + const ( TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx" ) diff --git a/common/constant/serializtion.go b/common/constant/serializtion.go new file mode 100644 index 0000000000000000000000000000000000000000..f27598ccf5cf04a72d14d4ef97ae9298076efe1a --- /dev/null +++ b/common/constant/serializtion.go @@ -0,0 +1,28 @@ +/* + * 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 + +const ( + S_Hessian2 byte = 2 + S_Proto byte = 21 +) + +const ( + HESSIAN2_SERIALIZATION = "hessian2" + PROTOBUF_SERIALIZATION = "protobuf" +) diff --git a/common/extension/health_checker.go b/common/extension/health_checker.go index 8def727614dad8393eeef9ced5e30a056fa65461..cec4c2defc291c617a0549c3296e07851b2ec128 100644 --- a/common/extension/health_checker.go +++ b/common/extension/health_checker.go @@ -26,8 +26,8 @@ var ( healthCheckers = make(map[string]func(url *common.URL) router.HealthChecker) ) -// SethealthChecker sets the HealthChecker with @name -func SethealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) { +// SetHealthChecker sets the HealthChecker with @name +func SetHealthChecker(name string, fcn func(_ *common.URL) router.HealthChecker) { healthCheckers[name] = fcn } diff --git a/common/extension/health_checker_test.go b/common/extension/health_checker_test.go index 4e83a6f6e1ed8a57b6e6374377d08eabfb56c604..af6b114a612a465d4397be7a599ddfc9ff7edab9 100644 --- a/common/extension/health_checker_test.go +++ b/common/extension/health_checker_test.go @@ -32,7 +32,7 @@ import ( ) func TestGetHealthChecker(t *testing.T) { - SethealthChecker("mock", newMockhealthCheck) + SetHealthChecker("mock", newMockHealthCheck) checker := GetHealthChecker("mock", common.NewURLWithOptions()) assert.NotNil(t, checker) } @@ -44,6 +44,6 @@ func (m mockHealthChecker) IsHealthy(invoker protocol.Invoker) bool { return true } -func newMockhealthCheck(_ *common.URL) router.HealthChecker { +func newMockHealthCheck(_ *common.URL) router.HealthChecker { return &mockHealthChecker{} } diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go index ce0f4d1d3f4dc8b93467aaeede40ea03b53c6e66..d51ce1cc1bf40a8ad25804c797eeed3b88e7d132 100644 --- a/common/proxy/proxy.go +++ b/common/proxy/proxy.go @@ -145,12 +145,17 @@ func (p *Proxy) Implement(v common.RPCService) { inv.SetAttachments(k, value) } - // add user setAttachment + // add user setAttachment. It is compatibility with previous versions. atm := invCtx.Value(constant.AttachmentKey) if m, ok := atm.(map[string]string); ok { for k, value := range m { inv.SetAttachments(k, value) } + } else if m2, ok2 := atm.(map[string]interface{}); ok2 { + // it is support to transfer map[string]interface{}. It refers to dubbo-java 2.7. + for k, value := range m2 { + inv.SetAttachments(k, value) + } } result := p.invoke.Invoke(invCtx, inv) diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go index 1b8ca222011292040c57c3e86df0438943a5b464..752f3ea38193ca46a5c9dbcb8ec4b05811d19159 100644 --- a/common/proxy/proxy_factory/default.go +++ b/common/proxy/proxy_factory/default.go @@ -89,6 +89,8 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati result.SetAttachments(invocation.Attachments()) url := pi.GetUrl() + //get providerUrl. The origin url may be is registry URL. + url = *getProviderURL(&url) methodName := invocation.MethodName() proto := url.Protocol @@ -159,3 +161,10 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati } return result } + +func getProviderURL(url *common.URL) *common.URL { + if url.SubURL == nil { + return url + } + return url.SubURL +} diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go index 14b2befbc47242d9cc9a2f88e9070b84828062c0..c6066157fd8d2147fd3a319c8d48fdd910752711 100644 --- a/common/proxy/proxy_test.go +++ b/common/proxy/proxy_test.go @@ -24,6 +24,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/protocol/invocation" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -32,6 +33,7 @@ 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/dubbo/hessian2" ) type TestService struct { @@ -40,6 +42,7 @@ type TestService struct { MethodThree func(int, bool) (interface{}, error) MethodFour func(int, bool) (*interface{}, error) `dubbo:"methodFour"` MethodFive func() error + MethodSix func(context.Context, string) (interface{}, error) Echo func(interface{}, *interface{}) error } @@ -120,3 +123,34 @@ func TestProxyImplement(t *testing.T) { assert.Nil(t, s3.MethodOne) } + +func TestProxyImplementForContext(t *testing.T) { + invoker := &TestProxyInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(common.URL{}), + } + p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"}) + s := &TestService{} + p.Implement(s) + attahments1 := make(map[string]interface{}, 4) + attahments1["k1"] = "v1" + attahments1["k2"] = "v2" + context := context.WithValue(context.Background(), constant.AttachmentKey, attahments1) + r, err := p.Get().(*TestService).MethodSix(context, "xxx") + v1 := r.(map[string]interface{}) + assert.NoError(t, err) + assert.Equal(t, v1["TestProxyInvoker"], "TestProxyInvokerValue") +} + +type TestProxyInvoker struct { + protocol.BaseInvoker +} + +func (bi *TestProxyInvoker) Invoke(context context.Context, inv protocol.Invocation) protocol.Result { + rpcInv := inv.(*invocation.RPCInvocation) + mapV := inv.Attachments() + mapV["TestProxyInvoker"] = "TestProxyInvokerValue" + hessian2.ReflectResponse(mapV, rpcInv.Reply()) + return &protocol.RPCResult{ + Rest: inv.Arguments(), + } +} diff --git a/common/rpc_service.go b/common/rpc_service.go index 9ef2b956aa955f4fc79c6f75bd060ccfee2d02ca..5ed4df6d717db31021d8585a88d5576f59756f8a 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -169,7 +169,7 @@ func (sm *serviceMap) GetService(protocol, name string) *Service { return nil } -// GetInterface gets an interface defination by interface name +// GetInterface gets an interface definition by interface name func (sm *serviceMap) GetInterface(interfaceName string) []*Service { sm.mutex.RLock() defer sm.mutex.RUnlock() @@ -271,7 +271,7 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro sm.mutex.Lock() defer sm.mutex.Unlock() sm.interfaceMap[interfaceName] = make([]*Service, 0, len(svrs)) - for i, _ := range svrs { + for i := range svrs { if i != index { sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i]) } diff --git a/common/url.go b/common/url.go index ec6dce9175596e4f1774614f8f0cb978d181f300..5a3e57406bf221a341d9a3ea120943144a35aca0 100644 --- a/common/url.go +++ b/common/url.go @@ -18,6 +18,7 @@ package common import ( + "bytes" "encoding/base64" "fmt" "math" @@ -222,7 +223,7 @@ func NewURL(urlString string, opts ...option) (URL, error) { } // rawUrlString = "//" + rawUrlString - if strings.Index(rawUrlString, "//") < 0 { + if !strings.Contains(rawUrlString, "//") { t := URL{baseUrl: baseUrl{}} for _, opt := range opts { opt(&t) @@ -325,12 +326,15 @@ func (c URL) Key() string { // ServiceKey gets a unique key of a service. func (c URL) ServiceKey() string { - intf := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) + return ServiceKey(c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")), + c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, "")) +} + +func ServiceKey(intf string, group string, version string) string { if intf == "" { return "" } - var buf strings.Builder - group := c.GetParam(constant.GROUP_KEY, "") + buf := &bytes.Buffer{} if group != "" { buf.WriteString(group) buf.WriteString("/") @@ -338,7 +342,6 @@ func (c URL) ServiceKey() string { buf.WriteString(intf) - version := c.GetParam(constant.VERSION_KEY, "") if version != "" && version != "0.0.0" { buf.WriteString(":") buf.WriteString(version) @@ -396,6 +399,17 @@ func (c *URL) AddParam(key string, value string) { c.params.Add(key, value) } +// AddParamAvoidNil will add key-value pair +// Not thread-safe +// think twice before using it. +func (c *URL) AddParamAvoidNil(key string, value string) { + if c.params == nil { + c.params = url.Values{} + } + + c.params.Add(key, value) +} + // SetParam will put the key-value pair into url // it's not thread safe. // think twice before you want to use this method @@ -643,6 +657,34 @@ func (c *URL) CloneWithParams(reserveParams []string) *URL { ) } +// IsEquals compares if two URLs equals with each other. Excludes are all parameter keys which should ignored. +func IsEquals(left URL, right URL, excludes ...string) bool { + if left.Ip != right.Ip || left.Port != right.Port { + return false + } + + leftMap := left.ToMap() + rightMap := right.ToMap() + for _, exclude := range excludes { + delete(leftMap, exclude) + delete(rightMap, exclude) + } + + if len(leftMap) != len(rightMap) { + return false + } + + for lk, lv := range leftMap { + if rv, ok := rightMap[lk]; !ok { + return false + } else if lv != rv { + return false + } + } + + return true +} + func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) { methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys)) for _, paramKey := range paramKeys { diff --git a/config/base_config.go b/config/base_config.go index 22a0832731daff6c9957d4913a3784c9b268b11f..336bb03c7b61ad8aad8465bb3c7754abeb9e9f5a 100644 --- a/config/base_config.go +++ b/config/base_config.go @@ -78,15 +78,8 @@ func getKeyPrefix(val reflect.Value) []string { } else { prefix = val.MethodByName(configPrefixMethod).Call(nil)[0].String() } - var retPrefixes []string - - for _, pfx := range strings.Split(prefix, "|") { - - retPrefixes = append(retPrefixes, pfx) - - } - return retPrefixes + return strings.Split(prefix, "|") } func getPtrElement(v reflect.Value) reflect.Value { @@ -216,12 +209,9 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC prefix := s.MethodByName("Prefix").Call(nil)[0].String() for _, pfx := range strings.Split(prefix, "|") { m := config.GetSubProperty(pfx) - if m != nil { - for k := range m { - f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem())) - } + for k := range m { + f.SetMapIndex(reflect.ValueOf(k), reflect.New(f.Type().Elem().Elem())) } - } } diff --git a/config/config_loader.go b/config/config_loader.go index 75b82628d68a23e575cfa637b3603d09e09ea9d6..c66e526921e7e5ab017105e2f4ea2baa62563205 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -141,19 +141,18 @@ func loadConsumerConfig() { // wait for invoker is available, if wait over default 3s, then panic var count int - checkok := true for { + checkok := true for _, refconfig := range consumerConfig.References { if (refconfig.Check != nil && *refconfig.Check) || (refconfig.Check == nil && consumerConfig.Check != nil && *consumerConfig.Check) || (refconfig.Check == nil && consumerConfig.Check == nil) { // default to true - if refconfig.invoker != nil && - !refconfig.invoker.IsAvailable() { + if refconfig.invoker != nil && !refconfig.invoker.IsAvailable() { checkok = false count++ if count > maxWait { - errMsg := fmt.Sprintf("Failed to check the status of the service %v . No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) + errMsg := fmt.Sprintf("Failed to check the status of the service %v. No provider available for the service to the consumer use dubbo version %v", refconfig.InterfaceName, constant.Version) logger.Error(errMsg) panic(errMsg) } @@ -161,14 +160,13 @@ func loadConsumerConfig() { break } if refconfig.invoker == nil { - logger.Warnf("The interface %s invoker not exist , may you should check your interface config.", refconfig.InterfaceName) + logger.Warnf("The interface %s invoker not exist, may you should check your interface config.", refconfig.InterfaceName) } } } if checkok { break } - checkok = true } } diff --git a/config/consumer_config.go b/config/consumer_config.go index 9d283eeca7bbaf5a82f71357853c6b53560b2fe4..c8083603e12570a4492dd63a749adb4aa89663c8 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -60,8 +60,8 @@ type ConsumerConfig struct { References map[string]*ReferenceConfig `yaml:"references" json:"references,omitempty" property:"references"` ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"` - FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` - ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` + FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"` + ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"` ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` } diff --git a/config/provider_config.go b/config/provider_config.go index c710e48dc233a62837b31a89828e9c612eaff093..fcb429b6400936f72c09035968b4d4c72387246c 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -43,9 +43,9 @@ type ProviderConfig struct { ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` Services map[string]*ServiceConfig `yaml:"services" json:"services,omitempty" property:"services"` Protocols map[string]*ProtocolConfig `yaml:"protocols" json:"protocols,omitempty" property:"protocols"` - ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf" ` - FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf" ` - ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf" ` + ProtocolConf interface{} `yaml:"protocol_conf" json:"protocol_conf,omitempty" property:"protocol_conf"` + FilterConf interface{} `yaml:"filter_conf" json:"filter_conf,omitempty" property:"filter_conf"` + ShutdownConfig *ShutdownConfig `yaml:"shutdown_conf" json:"shutdown_conf,omitempty" property:"shutdown_conf"` ConfigType map[string]string `yaml:"config_type" json:"config_type,omitempty" property:"config_type"` Registry *RegistryConfig `yaml:"registry" json:"registry,omitempty" property:"registry"` diff --git a/config/reference_config.go b/config/reference_config.go index bbc875192c7a87354ccc81e28ea05bbc3bb71149..cd10f89eb7773e16ed953623c6fb38dcb98b01b4 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -251,5 +251,4 @@ func (c *ReferenceConfig) GenericLoad(id string) { c.id = id c.Refer(genericService) c.Implement(genericService) - return } diff --git a/config/remote_config.go b/config/remote_config.go index 55380dd5a05b47b5b4677b32daf73b37376673d0..0f0c3e5cb7991e19ea0ad722fc4d40da01c1fad7 100644 --- a/config/remote_config.go +++ b/config/remote_config.go @@ -40,7 +40,7 @@ type RemoteConfig struct { TimeoutStr string `default:"5s" yaml:"timeout" json:"timeout,omitempty"` Username string `yaml:"username" json:"username,omitempty" property:"username"` Password string `yaml:"password" json:"password,omitempty" property:"password"` - Params map[string]string `yaml:"params" json:"address,omitempty"` + Params map[string]string `yaml:"params" json:"params,omitempty"` } // Timeout return timeout duration. diff --git a/config/service_config.go b/config/service_config.go index 54383e4791bf0d0749aea08d7ba9a613e4cfe70b..48632a1b1e295eb5ec3027bd495ef8d19e978ec4 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -59,6 +59,7 @@ type ServiceConfig struct { Methods []*MethodConfig `yaml:"methods" json:"methods,omitempty" property:"methods"` Warmup string `yaml:"warmup" json:"warmup,omitempty" property:"warmup"` Retries string `yaml:"retries" json:"retries,omitempty" property:"retries"` + Serialization string `yaml:"serialization" json:"serialization" property:"serialization"` Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` Token string `yaml:"token" json:"token,omitempty" property:"token"` AccessLog string `yaml:"accesslog" json:"accesslog,omitempty" property:"accesslog"` @@ -133,7 +134,7 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List { tcp, err := gxnet.ListenOnTCPRandomPort(proto.Ip) if err != nil { - panic(perrors.New(fmt.Sprintf("Get tcp port error,err is {%v}", err))) + panic(perrors.New(fmt.Sprintf("Get tcp port error, err is {%v}", err))) } defer tcp.Close() ports.PushBack(strings.Split(tcp.Addr().String(), ":")[1]) @@ -145,14 +146,14 @@ func getRandomPort(protocolConfigs []*ProtocolConfig) *list.List { func (c *ServiceConfig) Export() error { // TODO: config center start here - // TODO:delay export + // TODO: delay export if c.unexported != nil && c.unexported.Load() { - err := perrors.Errorf("The service %v has already unexported! ", c.InterfaceName) + err := perrors.Errorf("The service %v has already unexported!", c.InterfaceName) logger.Errorf(err.Error()) return err } if c.unexported != nil && c.exported.Load() { - logger.Warnf("The service %v has already exported! ", c.InterfaceName) + logger.Warnf("The service %v has already exported!", c.InterfaceName) return nil } @@ -160,23 +161,23 @@ func (c *ServiceConfig) Export() error { urlMap := c.getUrlMap() protocolConfigs := loadProtocol(c.Protocol, c.Protocols) if len(protocolConfigs) == 0 { - logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs ", c.InterfaceName, c.Protocol) + logger.Warnf("The service %v's '%v' protocols don't has right protocolConfigs", c.InterfaceName, c.Protocol) return nil } ports := getRandomPort(protocolConfigs) nextPort := ports.Front() + proxyFactory := extension.GetProxyFactory(providerConfig.ProxyFactory) for _, proto := range protocolConfigs { // registry the service reflect methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService) if err != nil { - formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v .", c.InterfaceName, proto.Name, err.Error()) + formatErr := perrors.Errorf("The service %v export the protocol %v error! Error message is %v.", c.InterfaceName, proto.Name, err.Error()) logger.Errorf(formatErr.Error()) return formatErr } port := proto.Port - if len(proto.Port) == 0 { port = nextPort.Value.(string) nextPort = nextPort.Next() @@ -196,33 +197,31 @@ func (c *ServiceConfig) Export() error { ivkURL.AddParam(constant.Tagkey, c.Tag) } - var exporter protocol.Exporter - if len(regUrls) > 0 { + 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") + } + c.cacheMutex.Unlock() + for _, regUrl := range regUrls { regUrl.SubURL = ivkURL - - 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") - } - c.cacheMutex.Unlock() - - invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*regUrl) - exporter = c.cacheProtocol.Export(invoker) + invoker := proxyFactory.GetInvoker(*regUrl) + 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, ivkURL))) + return perrors.New(fmt.Sprintf("Registry protocol new exporter error, registry is {%v}, url is {%v}", regUrl, ivkURL)) } + c.exporters = append(c.exporters, exporter) } } else { - invoker := extension.GetProxyFactory(providerConfig.ProxyFactory).GetInvoker(*ivkURL) - exporter = extension.GetProtocol(protocolwrapper.FILTER).Export(invoker) + invoker := 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}", ivkURL))) + return perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error, url is {%v}", ivkURL)) } + c.exporters = append(c.exporters, exporter) } - c.exporters = append(c.exporters, exporter) } c.exported.Store(true) return nil @@ -272,7 +271,8 @@ func (c *ServiceConfig) getUrlMap() url.Values { urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) urlMap.Set(constant.RELEASE_KEY, "dubbo-golang-"+constant.Version) urlMap.Set(constant.SIDE_KEY, (common.RoleType(common.PROVIDER)).Role()) - + // todo: move + urlMap.Set(constant.SERIALIZATION_KEY, c.Serialization) // application info urlMap.Set(constant.APPLICATION_KEY, providerConfig.ApplicationConfig.Name) urlMap.Set(constant.ORGANIZATION_KEY, providerConfig.ApplicationConfig.Organization) @@ -314,7 +314,6 @@ func (c *ServiceConfig) getUrlMap() url.Values { urlMap.Set(constant.EXECUTE_LIMIT_KEY, v.ExecuteLimit) urlMap.Set(constant.EXECUTE_REJECTED_EXECUTION_HANDLER_KEY, v.ExecuteLimitRejectedHandler) - } return urlMap diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index 8030a2c800c67d47a27e7aaa5d6f1bb39a83cdc9..ac5328c27a95425333276be58e6dd614e23bb5ac 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -45,6 +45,7 @@ const ( ) type apolloConfiguration struct { + cc.BaseDynamicConfiguration url *common.URL listeners sync.Map diff --git a/config_center/base_dynamic_configuration.go b/config_center/base_dynamic_configuration.go new file mode 100644 index 0000000000000000000000000000000000000000..3d6757852ad83d54338b721d0cb617772f40b6b7 --- /dev/null +++ b/config_center/base_dynamic_configuration.go @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package config_center + +// BaseDynamicConfiguration will default implementation DynamicConfiguration some method +type BaseDynamicConfiguration struct { +} + +// RemoveConfig +func (bdc *BaseDynamicConfiguration) RemoveConfig(string, string) error { + return nil +} diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go index 294a60ebb2e4e18cfc47cd90aedeaa615b5626d2..ec4e606e0dff300729e2a2bc56f510db94ea9f26 100644 --- a/config_center/configurator/override.go +++ b/config_center/configurator/override.go @@ -110,7 +110,7 @@ func (c *overrideConfigurator) configureIfMatchInternal(url *common.URL) { func (c *overrideConfigurator) configureIfMatch(host string, url *common.URL) { if constant.ANYHOST_VALUE == c.configuratorUrl.Ip || host == c.configuratorUrl.Ip { providers := c.configuratorUrl.GetParam(constant.OVERRIDE_PROVIDERS_KEY, "") - if len(providers) == 0 || strings.Index(providers, url.Location) >= 0 || strings.Index(providers, constant.ANYHOST_VALUE) >= 0 { + if len(providers) == 0 || strings.Contains(providers, url.Location) || strings.Contains(providers, constant.ANYHOST_VALUE) { c.configureIfMatchInternal(url) } } diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index 540febc9d38e164afcc62538478df140b7d671c7..cbf8e8cf031ed20d6e69c5a3d7556f22ad4dc223 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -58,6 +58,9 @@ type DynamicConfiguration interface { // PublishConfig will publish the config with the (key, group, value) pair PublishConfig(string, string, string) error + // RemoveConfig will remove the config white the (key, group) pair + RemoveConfig(string, string) error + // GetConfigKeysByGroup will return all keys with the group GetConfigKeysByGroup(group string) (*gxset.HashSet, error) } diff --git a/config_center/file/factory.go b/config_center/file/factory.go new file mode 100644 index 0000000000000000000000000000000000000000..2dda900e20cb7476b1d8da95e4b2b26fcb9dcefd --- /dev/null +++ b/config_center/file/factory.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 file + +import ( + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/config_center/parser" +) + +func init() { + extension.SetConfigCenterFactory(constant.FILE_KEY, func() config_center.DynamicConfigurationFactory { + return &fileDynamicConfigurationFactory{} + }) +} + +type fileDynamicConfigurationFactory struct { +} + +// GetDynamicConfiguration Get Configuration with URL +func (f *fileDynamicConfigurationFactory) GetDynamicConfiguration(url *common.URL) (config_center.DynamicConfiguration, + error) { + dynamicConfiguration, err := newFileSystemDynamicConfiguration(url) + if err != nil { + return nil, perrors.WithStack(err) + } + + dynamicConfiguration.SetParser(&parser.DefaultConfigurationParser{}) + return dynamicConfiguration, err +} diff --git a/config_center/file/impl.go b/config_center/file/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..9d8254026bf3740631798bb0906436029be15abd --- /dev/null +++ b/config_center/file/impl.go @@ -0,0 +1,312 @@ +/* + * 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 file + +import ( + "bytes" + "errors" + "io/ioutil" + "os" + "os/exec" + "os/user" + "path" + "path/filepath" + "runtime" + "strings" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/config_center/parser" +) + +const ( + PARAM_NAME_PREFIX = "dubbo.config-center." + CONFIG_CENTER_DIR_PARAM_NAME = PARAM_NAME_PREFIX + "dir" + CONFIG_CENTER_ENCODING_PARAM_NAME = PARAM_NAME_PREFIX + "encoding" + DEFAULT_CONFIG_CENTER_ENCODING = "UTF-8" +) + +// FileSystemDynamicConfiguration +type FileSystemDynamicConfiguration struct { + config_center.BaseDynamicConfiguration + url *common.URL + rootPath string + encoding string + cacheListener *CacheListener + parser parser.ConfigurationParser +} + +func newFileSystemDynamicConfiguration(url *common.URL) (*FileSystemDynamicConfiguration, error) { + encode := url.GetParam(CONFIG_CENTER_ENCODING_PARAM_NAME, DEFAULT_CONFIG_CENTER_ENCODING) + + root := url.GetParam(CONFIG_CENTER_DIR_PARAM_NAME, "") + var c *FileSystemDynamicConfiguration + if _, err := os.Stat(root); err != nil { + // not exist, use default, /XXX/xx/.dubbo/config-center + if rp, err := Home(); err != nil { + return nil, perrors.WithStack(err) + } else { + root = path.Join(rp, ".dubbo", "config-center") + } + } + + if _, err := os.Stat(root); err != nil { + // it must be dir, if not exist, will create + if err = createDir(root); err != nil { + return nil, perrors.WithStack(err) + } + } + + c = &FileSystemDynamicConfiguration{ + url: url, + rootPath: root, + encoding: encode, + } + + c.cacheListener = NewCacheListener(c.rootPath) + + return c, nil +} + +// RootPath get root path +func (fsdc *FileSystemDynamicConfiguration) RootPath() string { + return fsdc.rootPath +} + +// Parser Get Parser +func (fsdc *FileSystemDynamicConfiguration) Parser() parser.ConfigurationParser { + return fsdc.parser +} + +// SetParser Set Parser +func (fsdc *FileSystemDynamicConfiguration) SetParser(p parser.ConfigurationParser) { + fsdc.parser = p +} + +// AddListener Add listener +func (fsdc *FileSystemDynamicConfiguration) AddListener(key string, listener config_center.ConfigurationListener, + opts ...config_center.Option) { + tmpOpts := &config_center.Options{} + for _, opt := range opts { + opt(tmpOpts) + } + + path := fsdc.GetPath(key, tmpOpts.Group) + + fsdc.cacheListener.AddListener(path, listener) +} + +// RemoveListener Remove listener +func (fsdc *FileSystemDynamicConfiguration) RemoveListener(key string, listener config_center.ConfigurationListener, + opts ...config_center.Option) { + tmpOpts := &config_center.Options{} + for _, opt := range opts { + opt(tmpOpts) + } + + path := fsdc.GetPath(key, tmpOpts.Group) + + fsdc.cacheListener.RemoveListener(path, listener) +} + +// GetProperties get properties file +func (fsdc *FileSystemDynamicConfiguration) GetProperties(key string, opts ...config_center.Option) (string, error) { + tmpOpts := &config_center.Options{} + for _, opt := range opts { + opt(tmpOpts) + } + + path := fsdc.GetPath(key, tmpOpts.Group) + file, err := ioutil.ReadFile(path) + if err != nil { + return "", perrors.WithStack(err) + } + + return string(file), nil +} + +// GetRule get Router rule properties file +func (fsdc *FileSystemDynamicConfiguration) GetRule(key string, opts ...config_center.Option) (string, error) { + return fsdc.GetProperties(key, opts...) +} + +// GetInternalProperty get value by key in Default properties file(dubbo.properties) +func (fsdc *FileSystemDynamicConfiguration) GetInternalProperty(key string, opts ...config_center.Option) (string, + error) { + return fsdc.GetProperties(key, opts...) +} + +// PublishConfig will publish the config with the (key, group, value) pair +func (fsdc *FileSystemDynamicConfiguration) PublishConfig(key string, group string, value string) error { + path := fsdc.GetPath(key, group) + return fsdc.write2File(path, value) +} + +// GetConfigKeysByGroup will return all keys with the group +func (fsdc *FileSystemDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { + path := fsdc.GetPath("", group) + r := gxset.NewSet() + + fileInfo, _ := ioutil.ReadDir(path) + + for _, file := range fileInfo { + // list file + if file.IsDir() { + continue + } + + r.Add(file.Name()) + } + + return r, nil +} + +// RemoveConfig will remove the config whit hte (key, group) +func (fsdc *FileSystemDynamicConfiguration) RemoveConfig(key string, group string) error { + path := fsdc.GetPath(key, group) + _, err := fsdc.deleteDelay(path) + return err +} + +// Close close file watcher +func (fsdc *FileSystemDynamicConfiguration) Close() error { + return fsdc.cacheListener.Close() +} + +// GetPath get path +func (fsdc *FileSystemDynamicConfiguration) GetPath(key string, group string) string { + if len(key) == 0 { + return path.Join(fsdc.rootPath, group) + } + + if len(group) == 0 { + group = config_center.DEFAULT_GROUP + } + + return path.Join(fsdc.rootPath, group, key) +} + +func (fsdc *FileSystemDynamicConfiguration) deleteDelay(path string) (bool, error) { + if path == "" { + return false, nil + } + + if err := os.RemoveAll(path); err != nil { + return false, err + } + + return true, nil +} + +func (fsdc *FileSystemDynamicConfiguration) write2File(fp string, value string) error { + if err := forceMkdirParent(fp); err != nil { + return perrors.WithStack(err) + } + + return ioutil.WriteFile(fp, []byte(value), os.ModePerm) +} + +func forceMkdirParent(fp string) error { + pd := getParentDirectory(fp) + + return createDir(pd) +} + +func createDir(path string) error { + // create dir, chmod is drwxrwxrwx(0777) + if err := os.MkdirAll(path, os.ModePerm); err != nil { + return err + } + + return nil +} + +func getParentDirectory(fp string) string { + return substr(fp, 0, strings.LastIndex(fp, string(filepath.Separator))) +} + +func substr(s string, pos, length int) string { + runes := []rune(s) + l := pos + length + if l > len(runes) { + l = len(runes) + } + return string(runes[pos:l]) +} + +// Home returns the home directory for the executing user. +// +// This uses an OS-specific method for discovering the home directory. +// An error is returned if a home directory cannot be detected. +func Home() (string, error) { + user, err := user.Current() + if nil == err { + return user.HomeDir, nil + } + + // cross compile support + if "windows" == runtime.GOOS { + return homeWindows() + } + + // Unix-like system, so just assume Unix + return homeUnix() +} + +func homeUnix() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + return home, nil + } + + // If that fails, try the shell + var stdout bytes.Buffer + cmd := exec.Command("sh", "-c", "eval echo ~$USER") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output when reading home directory") + } + + return result, nil +} + +func homeWindows() (string, error) { + drive := os.Getenv("HOMEDRIVE") + path := os.Getenv("HOMEPATH") + home := drive + path + if drive == "" || path == "" { + home = os.Getenv("USERPROFILE") + } + if home == "" { + return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") + } + + return home, nil +} diff --git a/config_center/file/impl_test.go b/config_center/file/impl_test.go new file mode 100644 index 0000000000000000000000000000000000000000..58892953d556512a88689baa5110995091d75f7b --- /dev/null +++ b/config_center/file/impl_test.go @@ -0,0 +1,156 @@ +/* + * 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 file + +import ( + "fmt" + "os" + "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/config_center" +) + +const ( + key = "com.dubbo.go" +) + +func initFileData(t *testing.T) (*FileSystemDynamicConfiguration, error) { + urlString := "registry://127.0.0.1:2181" + regurl, err := common.NewURL(urlString) + assert.NoError(t, err) + dc, err := extension.GetConfigCenterFactory("file").GetDynamicConfiguration(®url) + assert.NoError(t, err) + + return dc.(*FileSystemDynamicConfiguration), err +} + +func TestPublishAndGetConfig(t *testing.T) { + file, err := initFileData(t) + assert.NoError(t, err) + err = file.PublishConfig(key, "", "A") + assert.NoError(t, err) + + prop, err := file.GetProperties(key) + assert.NoError(t, err) + assert.Equal(t, "A", prop) + + defer destroy(file.rootPath, file) +} + +func TestAddListener(t *testing.T) { + file, err := initFileData(t) + group := "dubbogo" + value := "Test Value" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + + listener := &mockDataListener{} + file.AddListener(key, listener, config_center.WithGroup(group)) + + value = "Test Value 2" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + // remove need wait a moment + time.Sleep(time.Second) + defer destroy(file.rootPath, file) +} + +func TestRemoveListener(t *testing.T) { + file, err := initFileData(t) + group := "dubbogo" + value := "Test Value" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + + listener := &mockDataListener{} + file.AddListener(key, listener, config_center.WithGroup(group)) + + value = "Test Value 2" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + + // make sure callback before RemoveListener + time.Sleep(time.Second) + file.RemoveListener(key, listener, config_center.WithGroup(group)) + value = "Test Value 3" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + // remove need wait a moment + time.Sleep(time.Second) + defer destroy(file.rootPath, file) +} + +func TestGetConfigKeysByGroup(t *testing.T) { + file, err := initFileData(t) + group := "dubbogo" + value := "Test Value" + err = file.PublishConfig(key, group, value) + gs, err := file.GetConfigKeysByGroup(group) + assert.NoError(t, err) + assert.Equal(t, 1, gs.Size()) + assert.Equal(t, key, gs.Values()[0]) + // remove need wait a moment + time.Sleep(time.Second) + defer destroy(file.rootPath, file) +} + +func TestGetConfig(t *testing.T) { + file, err := initFileData(t) + assert.NoError(t, err) + group := "dubbogo" + value := "Test Value" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + prop, err := file.GetProperties(key, config_center.WithGroup(group)) + assert.NoError(t, err) + assert.Equal(t, value, prop) + defer destroy(file.rootPath, file) +} + +func TestPublishConfig(t *testing.T) { + file, err := initFileData(t) + assert.NoError(t, err) + group := "dubbogo" + value := "Test Value" + err = file.PublishConfig(key, group, value) + assert.NoError(t, err) + prop, err := file.GetInternalProperty(key, config_center.WithGroup(group)) + assert.NoError(t, err) + assert.Equal(t, value, prop) + defer destroy(file.rootPath, file) +} + +func destroy(path string, fdc *FileSystemDynamicConfiguration) { + fdc.Close() + os.RemoveAll(path) +} + +type mockDataListener struct{} + +func (l *mockDataListener) Process(configType *config_center.ConfigChangeEvent) { + fmt.Printf("process!!!!! %v", configType) +} diff --git a/config_center/file/listener.go b/config_center/file/listener.go new file mode 100644 index 0000000000000000000000000000000000000000..d569030e5ac6a127a862c4d22d180f674cadce2d --- /dev/null +++ b/config_center/file/listener.go @@ -0,0 +1,162 @@ +/* + * 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 file + +import ( + "io/ioutil" + "sync" +) + +import ( + "github.com/fsnotify/fsnotify" +) + +import ( + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/remoting" +) + +// CacheListener is file watcher +type CacheListener struct { + watch *fsnotify.Watcher + keyListeners sync.Map + rootPath string +} + +// NewCacheListener creates a new CacheListener +func NewCacheListener(rootPath string) *CacheListener { + cl := &CacheListener{rootPath: rootPath} + // start watcher + watch, err := fsnotify.NewWatcher() + if err != nil { + logger.Errorf("file : listen config fail, error:%v ", err) + } + go func() { + for { + select { + case event := <-watch.Events: + key := event.Name + logger.Debugf("watcher %s, event %v", cl.rootPath, event) + if event.Op&fsnotify.Write == fsnotify.Write { + if l, ok := cl.keyListeners.Load(key); ok { + dataChangeCallback(l.(map[config_center.ConfigurationListener]struct{}), key, + remoting.EventTypeUpdate) + } + } + if event.Op&fsnotify.Create == fsnotify.Create { + if l, ok := cl.keyListeners.Load(key); ok { + dataChangeCallback(l.(map[config_center.ConfigurationListener]struct{}), key, + remoting.EventTypeAdd) + } + } + if event.Op&fsnotify.Remove == fsnotify.Remove { + if l, ok := cl.keyListeners.Load(key); ok { + removeCallback(l.(map[config_center.ConfigurationListener]struct{}), key, remoting.EventTypeDel) + } + } + case err := <-watch.Errors: + // err may be nil, ignore + if err != nil { + logger.Warnf("file : listen watch fail:%+v", err) + } + } + } + }() + cl.watch = watch + + extension.AddCustomShutdownCallback(func() { + cl.watch.Close() + }) + + return cl +} + +func removeCallback(lmap map[config_center.ConfigurationListener]struct{}, key string, event remoting.EventType) { + if len(lmap) == 0 { + logger.Warnf("file watch callback but configuration listener is empty, key:%s, event:%v", key, event) + return + } + for l := range lmap { + callback(l, key, "", event) + } +} + +func dataChangeCallback(lmap map[config_center.ConfigurationListener]struct{}, key string, event remoting.EventType) { + if len(lmap) == 0 { + logger.Warnf("file watch callback but configuration listener is empty, key:%s, event:%v", key, event) + return + } + c := getFileContent(key) + for l := range lmap { + callback(l, key, c, event) + } +} + +func callback(listener config_center.ConfigurationListener, path, data string, event remoting.EventType) { + listener.Process(&config_center.ConfigChangeEvent{Key: path, Value: data, ConfigType: event}) +} + +// Close will remove key listener and close watcher +func (cl *CacheListener) Close() error { + cl.keyListeners.Range(func(key, value interface{}) bool { + cl.keyListeners.Delete(key) + return true + }) + return cl.watch.Close() +} + +// AddListener will add a listener if loaded +// if you watcher a file or directory not exist, will error with no such file or directory +func (cl *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 := cl.keyListeners.LoadOrStore(key, map[config_center.ConfigurationListener]struct{}{ + listener: {}}) + if loaded { + listeners.(map[config_center.ConfigurationListener]struct{})[listener] = struct{}{} + cl.keyListeners.Store(key, listeners) + return + } + if err := cl.watch.Add(key); err != nil { + logger.Errorf("watcher add path:%s err:%v", key, err) + } +} + +// RemoveListener will delete a listener if loaded +func (cl *CacheListener) RemoveListener(key string, listener config_center.ConfigurationListener) { + listeners, loaded := cl.keyListeners.Load(key) + if !loaded { + return + } + delete(listeners.(map[config_center.ConfigurationListener]struct{}), listener) + if err := cl.watch.Remove(key); err != nil { + logger.Errorf("watcher remove path:%s err:%v", key, err) + } +} + +func getFileContent(path string) string { + c, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("read file path:%s err:%v", path, err) + return "" + } + + return string(c) +} diff --git a/config_center/mock_dynamic_config.go b/config_center/mock_dynamic_config.go index 8fe0a251239f7bfc6a3f70c3834da1b3af8484ba..9bebd600c6ba9e09f172f9260a920b6572fa694c 100644 --- a/config_center/mock_dynamic_config.go +++ b/config_center/mock_dynamic_config.go @@ -98,6 +98,7 @@ func (c *MockDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.Ha // MockDynamicConfiguration uses to parse content and defines listener type MockDynamicConfiguration struct { + BaseDynamicConfiguration parser parser.ConfigurationParser content string listener map[string]ConfigurationListener diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go index bbf707b93811663d0a259c6704e1008bfa91c5c1..be94b9a2e356b797b580ef4861895259ca7a4315 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -47,6 +47,7 @@ const ( // nacosDynamicConfiguration is the implementation of DynamicConfiguration based on nacos type nacosDynamicConfiguration struct { + config_center.BaseDynamicConfiguration url *common.URL rootPath string wg sync.WaitGroup diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index ef579eb2d11cf5f5bafb132c3e201c12ee7845c0..485abcb5f0403050f0ed4797d8cce408a23eed62 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -44,6 +44,7 @@ const ( ) type zookeeperDynamicConfiguration struct { + config_center.BaseDynamicConfiguration url *common.URL rootPath string wg sync.WaitGroup diff --git a/contributing.md b/contributing.md index 9ee2dae32fad6caaf9e19c5e98e8b99b61c26a51..51301511e01c12e388edf574e5c8660ad4a542a1 100644 --- a/contributing.md +++ b/contributing.md @@ -38,4 +38,27 @@ The title format of the pull request `MUST` follow the following rules: ### 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 +>- 2 the comment should begin with function name/var name. + +### 3.4 import + +We dubbogo import blocks should be splited into 3 blocks. + +```Go +// block 1: the go internal package +import ( + "fmt" +) + +// block 2: the third package +import ( + "github.com/dubbogo/xxx" + + "github.com/RoaringBitmap/roaring" +) + +// block 3: the dubbo-go package +import ( + "github.com/apache/dubbo-go/common" +) +``` \ No newline at end of file diff --git a/doc/md/arch/dubbo-go-design-implement-and-featrues.md b/doc/md/arch/dubbo-go-design-implement-and-featrues.md new file mode 100644 index 0000000000000000000000000000000000000000..c0600ef21fb578225646c9acc94a63d109494ee1 --- /dev/null +++ b/doc/md/arch/dubbo-go-design-implement-and-featrues.md @@ -0,0 +1,129 @@ +# [dubbo-go 鐨勫紑鍙戙€佽璁′笌鍔熻兘浠嬬粛](https://www.infoq.cn/article/7JIDIi7pfwDDk47EpaXZ) + +## dubbo-go 鐨勫墠涓栦粖鐢� + + + +dubbo-go 鏄洰鍓� Dubbo 澶氳瑷€鐢熸€佹渶鐏儹鐨勯」鐩€俤ubbo-go 鏈€鏃╃殑鐗堟湰搴旇瑕佽拷婧埌 2016 骞达紝鐢辩ぞ鍖轰簬闆ㄥ悓瀛︾紪鍐� dubbo-go 鐨勫垵鐗堛€傚綋鏃跺緢澶氫笢瑗挎病鏈夌幇鎴愮殑杞瓙锛屽 Go 璇█娌℃湁鍍� netty 涓€鏍风殑鍩轰簬浜嬩欢鐨勭綉缁滃鐞嗗紩鎿庛€� hessian2 鍗忚娌℃湁 Go 璇█鐗堟湰瀹炵幇锛屽姞涓婂綋鏃� Dubbo 涔熸病鏈夊紑濮嬮噸鏂扮淮鎶ゃ€傛墍浠ヤ粠鍗忚搴撳埌缃戠粶寮曟搸锛屽啀鍒颁笂灞� dubbo-go 锛屽叾瀹為兘鏄粠闆跺紑濮嬪啓鐨勩€� + +鍦� 2018 骞达紝鎼虹▼寮€濮嬪仛 Go 璇█鐨勪竴浜涗腑闂翠欢浠ユ惌寤哄唴閮ㄧ殑 Go 璇█鐢熸€侊紝闇€瑕佹湁涓€涓� Go 鐨勬湇鍔℃鏋跺彲浠ヤ笌鎼虹▼鐨勭幇鏈� dubbo soa 鐢熸€佷簰閫氥€傛墍浠ョ敱鎴戣礋璐i噸鏋勪簡 dubbo锛峠o 骞跺紑婧愬嚭杩欎釜鐗堟湰銆傚綋鏃惰皟鐮斾簡寰堝寮€婧愮殑 Go 璇█鏈嶅姟妗嗘灦锛屽綋鏃惰兘澶熸敮鎸� hessian2 鍗忚鐨勫苟璺� Dubbo 鍙互鎵撻€氱殑浠呮壘鍒颁簡褰撴椂浜庨洦鍐欑殑 dubbo-go 鏃╂湡鐗堟湰銆傜敱浜庢惡绋嬪绀惧尯鐗堟湰鐨� Dubbo 鍋氫簡鎸哄鐨勬墿灞曪紝婧愪簬瀵规墿灞曟€х殑闇€姹傛垜浠� Go 璇█鐗堟湰闇€瑕佷竴涓洿鏄撲簬鎵╁睍鐨勭増鏈紝鍔犱笂褰撴椂杩欎釜鐗堟湰鏈韩鐨勫姛鑳戒篃姣旇緝绠€鍗曪紝鎵€浠ユ垜浠壘鍒颁簡浣滆€呭悎浣滈噸鏋勪簡涓€涓洿濂界殑鐗堟湰銆傜粡杩囦簡澶у崐骞存椂闂达紝鍦ㄤ笂鍥剧涓夐樁娈� 19 骞� 6 鏈堢殑鏃跺€欙紝鍩烘湰涓婂凡缁忔妸 dubbo-go 閲嶆瀯浜嗕竴閬嶏紝鎬讳綋鐨勬€濊矾鏄弬鑰冪殑 Dubbo 鏁翠綋鐨勪唬鐮佹灦鏋勶紝鐢� Go 璇█瀹屽叏閲嶅啓浜嗕竴涓畬鏁寸殑鍏峰鏈嶅姟绔窡娑堣垂绔殑 Golang rpc/ 寰湇鍔℃鏋躲€� + +鍚庢潵鎴戜滑灏嗛噸鏋勫悗鐨勭増鏈� dubbo-go 1.0 璐$尞缁� Apache 鍩洪噾浼氾紝鍒扮幇鍦ㄥ凡缁忚繃鍘讳簡涓や釜澶氭湀鐨勬椂闂达紝杩戞湡绀惧尯鍙戝竷浜� 1.1 鐗堟湰銆傜洰鍓嶄负姝紝宸茬粡鏈夊寘鎷惡绋嬪湪鍐呯殑鍏徃宸茬粡鍦ㄧ敓浜х幆澧冨紑濮嬩簡璇曠敤鍜屾帹骞裤€� + +## Start dubbo-go + +鐜板湪鐨� dubbo-go 宸茬粡鑳藉璺� Java 鐗堟湰鍋氭瘮杈冨ソ鐨勮瀺鍚堜簰閫氾紝鍚屾椂 dubbo-go 鑷韩涔熸槸涓€涓畬鎴愮殑 Go 璇█ rpc/ 寰湇鍔℃鏋讹紝瀹冧篃鍙互鑴辩 java dubbo 鏉ョ嫭绔嬩娇鐢ㄣ€� + +杩欒竟绠€鍗曚粙缁嶄竴涓嬬敤娉曪紝鍐欎竴涓� hello world 鐨勪緥瀛愩€� + + + +涓婂浘鏄竴涓畝鍗曠殑 java service 锛屾敞鍐屼负涓€涓� Dubbo 鏈嶅姟锛屾槸涓€涓畝鍗曠殑鑾峰彇鐢ㄦ埛淇℃伅鐨勪緥瀛愩€� + + + +涓婂浘鏄� dubbo-go 鐨勫鎴风锛屾潵璁㈤槄鍜岃皟鐢ㄨ繖涓� Java 鐨� Dubbo 鏈嶅姟銆侴o 璇█瀹㈡埛绔渶瑕佹樉寮忚皟鐢� SetConsumerService 鏉ユ敞鍐岄渶瑕佽闃呯殑鏈嶅姟锛岀劧鍚庨€氳繃璋冪敤 dubbo-go-hessian2 搴撶殑 registerPOJO 鏂规硶鏉ユ敞鍐� user 瀵硅薄锛屽仛 Java 鍜� Go 璇█涔嬮棿鐨勮嚜瀹氫箟 pojo 绫诲瀷杞崲銆傚叿浣撶殑鏈嶅姟璋冪敤鏂规硶灏辨槸澹版槑涓€涓殑 GetUser 闂寘锛屼究鍙洿鎺ヨ皟鐢ㄣ€� + + + +涓婂浘锛屽悓鏍风殑鍙互鍩轰簬 dubbo-go 鍙戝竷涓€涓� GetUser 鐨勬湇鍔$锛屼娇鐢ㄦ柟寮忕被浼硷紝鍙戝竷瀹屽悗鍙互琚� dubbo java 鐨勫鎴风璋冪敤銆� + + + +濡備笂鍥炬墍绀猴紝鐜板湪宸茬粡鍋氬埌浜嗚繖鏍蜂竴涓▼搴︼紝鍚屾牱涓€浠� dubbo-go 瀹㈡埛绔唬鐮侊紝鍙互鍘昏皟鐢� dubbo-go 鐨勬湇鍔$锛屼篃鍙互鍘昏皟鐢� Dubbo Java 鐨勬湇鍔$锛涘悓鏍蜂竴浠� dubbo-go 鐨勬湇鍔$浠g爜锛屽彲浠ヨ dubbo-go 瀹㈡埛绔拰 Java 瀹㈡埛绔皟鐢紝鎵€浠ュ熀鏈笂浣跨敤 Dubbo 浣滀负 PPC 妗嗘灦鐨� Go 璇█搴旂敤璺� Java 搴旂敤鏄病鏈変粈涔堥樆纰嶇殑锛屾槸瀹屽叏鐨勮法璇█ RPC 璋冪敤銆傛洿閲嶈鐨勬槸 dubbo-go 缁ф壙浜� Dubbo 鐨勮澶氫紭鐐癸紝濡傛槗浜庢墿灞曘€佹湇鍔℃不鐞嗗姛鑳藉己澶э紝澶у鍦ㄧ敤 Go 璇█寮€鍙戝簲鐢ㄧ殑杩囩▼涓紝濡傛灉涔熼亣鍒扮被浼奸渶瑕佷笌 Dubbo Java 鎵撻€氱殑闇€姹傦紝鎴栬€呴渶瑕佹壘涓€涓湇鍔℃不鐞嗗姛鑳藉畬澶囩殑 Go 寰湇鍔℃鏋讹紝鍙互鐪嬩笅鎴戜滑 dubbo-go 椤圭洰銆� + +## dubbo-go 鐨勭粍鎴愰」鐩� + +涓嬮潰浠嬬粛涓€涓� dubbo-go 鐨勭粍鎴愰」鐩紝涓轰簡鏂逛究鍙互琚叾浠栭」鐩洿鎺ュ鐢紝 dubbo-go 鎷嗗垎鎴愪簡澶氫釜椤圭洰锛屽苟鍏ㄩ儴浠� Apache 鍗忚寮€婧愩€� + +**apache/dubbo-go** + +dubbo-go 涓婚」鐩紝 Dubbo 鏈嶅姟绔€佸鎴风瀹屾暣 Go 璇█瀹炵幇銆� + +**apache/dubbo-go-hession2** + +鐩墠搴旂敤鏈€骞挎硾锛屼笌 Java 鐗堟湰鍏煎绋嬪害鏈€楂樼殑 hessian2 鍗忚 Go 璇█瀹炵幇锛屽凡缁忚澶氫釜 GolangRPC & Service Mesh 椤圭洰浣跨敤銆� + +**dubbo-go/getty** + +dubbo-go 寮傛缃戠粶 I/O 搴擄紝灏嗙綉缁滃鐞嗗眰瑙h€︺€� + +**dubbo-go/gost** + +鍩烘湰绫诲簱锛屽畾涔変簡 timeWheel銆乭ashSet銆乼askPool 绛夈€� + +**dubbo-go/dubbo-go-benchmark** + +鐢ㄤ簬瀵� dubbo-go 杩涜绠€鍗曠殑鍘嬪姏娴嬭瘯锛屾€ц兘娴嬭瘯銆� + +**apache/dubbo-go-hessian2** + + + +鍏堢畝鍗曚粙缁嶄竴涓� dubbo-go-hessian2 椤圭洰銆傝椤圭洰灏辨槸 hessian2 鍗忚鐨� Go 璇█瀹炵幇锛屾渶鍩烘湰鐨勫彲浠ュ皢 Java 鐨勫熀鏈暟鎹被鍨嬪拰澶嶆潅鏁版嵁绫诲瀷锛堝涓€浜涘寘瑁呯被鍜� list 鎺ュ彛瀹炵幇绫伙級涓� golang 杩欒竟瀵瑰簲銆� + +璇︽儏鍙互鍙傝€冿細 [https://github.com/hessian-group/hessian-type-mapping](https://github.com/hessian-group/hessian-type-mapping) + +鍙﹀ Dubbo Java 鏈嶅姟绔彲浠ヤ笉鎹曡幏寮傚父锛屽皢寮傚父绫婚€氳繃 hession2 鍗忚搴忓垪鍖栭€氳繃缃戠粶浼犺緭缁欐秷璐圭锛屾秷璐圭杩涜鍙嶅簭鍒楀寲瀵硅寮傚父瀵硅薄骞惰繘琛屾崟鑾枫€傛垜浠粡杩囦竴娈垫椂闂寸殑鏁寸悊锛岀洰鍓嶅凡缁忔敮鎸佸湪 Go 娑堣垂绔畾涔夊搴� Java 鐨勮秴杩� 40 绉� exception 绫伙紝鏉ュ疄鐜板 Java 寮傚父鐨勬崟鑾凤紝鍗充娇鐢� dubbo-go 涔熷彲浠ュ仛鍒扮洿鎺ユ崟鑾� Java 鏈嶅姟绔姏鍑虹殑寮傚父銆� + +鍙﹀瀵逛簬 Java 绔� BigDecimal 楂樼簿搴﹁绠楃被鐨勬敮鎸併€傛秹鍙婂埌涓€浜涢噾铻嶇浉鍏崇殑璁$畻浼氭湁绫讳技鐨勯渶姹傦紝鎵€浠ヤ篃瀵硅繖涓被杩涜浜嗘敮鎸併€� + +鍏朵粬鐨勶紝杩樻湁鏄犲皠 java 绔殑鏂规硶鍒悕锛屼富瑕佺殑鍘熷洜鏄� Go 杩欒竟璇█鐨勮绾︼紝闇€瑕佽搴忓垪鍖栫殑鏂规硶鍚嶅繀椤绘槸棣栧瓧姣嶅ぇ鍐欍€傝€� Java 杩欒竟娌℃湁杩欑瑙勮寖锛屾墍浠ユ垜浠姞浜嗕竴涓� hessian 鏍囩鐨勬敮鎸侊紝鍙互鍏佽鐢ㄦ埛鎵嬪姩鏄犲皠 Java 绔殑鏂规硶鍚嶇О銆� + +鍩烘湰涓婄幇鍦ㄧ殑 dubbo-go 宸茬粡婊¤冻缁濆ぇ澶氭暟涓� Java 鐨勭被鍨嬩簰閫氶渶姹傦紝鎴戜滑杩戞湡涔熷湪瀹炵幇瀵� Java 娉涘瀷鐨勬敮鎸併€� + +**dubbo-go/getty** + + + +Go 璇█澶╃敓灏辨槸涓€涓紓姝ョ綉缁� I/O 妯″瀷锛屽湪 linux 涓� Go 璇█鍐欑殑缃戠粶鏈嶅姟鍣ㄤ篃鏄噰鐢ㄧ殑 epoll 浣滀负鏈€搴曞眰鐨勬暟鎹敹鍙戦┍鍔�, 杩欒窡 java 鍦� linux 鐨� nio 瀹炵幇鏄竴鏍风殑銆傛墍浠� Go 璇█鐨勭綉缁滃鐞嗗ぉ鐢熷氨鏄紓姝ョ殑銆傛垜浠渶瑕佸皝瑁呯殑鍏跺疄鏄熀浜� Go 鐨勫紓姝ョ綉缁滆鍐欎互鍙婁箣鍚庣殑澶勭悊涓棿灞傘€俫etty 灏嗙綉缁滄暟鎹鐞嗗垎涓轰笁灞傦紝鍏ュ悜鏂瑰悜鍒嗗埆缁忚繃瀵圭綉缁� i/o 灏佽鐨� streaming 灞傘€佹牴鎹笉鍚屽崗璁鏁版嵁杩涜搴忓垪鍖栧弽搴忓垪鍖栫殑 codec 灞傦紝浠ュ強鏈€鍚庢暟鎹笂鍗囧埌闇€瑕佷笂灞傛秷璐圭殑 handler 灞傘€傚嚭鍚戞柟鍚戝熀鏈笌鍏ュ悜缁忚繃鐨勭浉鍙嶃€傛瘡涓摼鎺ョ殑 IO 鍗忕▼鏄垚瀵瑰嚭鐜扮殑锛屾瘮濡傝鍗忕▼璐熻矗璇诲彇銆� codec 閫昏緫鐒跺悗鏁版嵁鍒� listener 灞傦紝鐒跺悗鏈€鍚庣殑浜嬩欢鐢变笟鍔″崗绋嬫睜鏉ュ鐞嗐€� + +璇ラ」鐩洰鍓嶆槸涓� dubbo-go 瑙h€﹀嚭鏉ョ殑锛屾墍浠ュぇ瀹跺鏋滄湁绫讳技闇€姹傚彲浠ョ洿鎺ユ嬁鏉ョ敤锛岀洰鍓嶅凡缁忔湁瀵逛簬 tcp/udp/websocket 鐨勬敮鎸併€� + +**Apache / dubbo-go** + + + +dubbo-go 涓婚」鐩紝鎴戜滑閲嶆瀯鐨勮繖涓€鐗堜富瑕佹槸鍩轰簬 Dubbo 鐨勫垎灞備唬鐮佽璁★紝涓婂浘鏄� dubbo-go 鐨勪唬鐮佸垎灞傘€傚熀鏈笂涓� Java 鐗堟湰 Dubbo 鐜版湁鐨勫垎灞備竴鑷达紝鎵€浠� dubbo锛峠o 涔熺户鎵夸簡 Dubbo 鐨勪竴浜涗紭鑹壒鎬э紝姣斿鏁存磥鐨勪唬鐮佹灦鏋勩€佹槗浜庢墿灞曘€佸畬鍠勭殑鏈嶅姟娌荤悊鍔熻兘銆� + +鎴戜滑鎼虹▼杩欒竟锛屼娇鐢ㄧ殑鏄嚜宸辩殑娉ㄥ唽涓績锛屽彲浠ュ湪 dubbo-go 鎵╁睍鏈哄埗鐨勫熀纭€涓婄伒娲绘墿灞曡€屾棤闇€鍘绘敼鍔� dubbo-go 鐨勬簮浠g爜銆� + +## dubbo-go 鐨勫姛鑳戒粙缁� + +**dubbo-go 宸插疄鐜板姛鑳�** + +鐩墠 dubbo-go 宸茬粡瀹炵幇浜� Dubbo 鐨勫父鐢ㄥ姛鑳斤紙濡傝礋璐e潎琛°€侀泦缇ょ瓥鐣ャ€佹湇鍔″鐗堟湰澶氬疄鐜般€佹湇鍔″娉ㄥ唽涓績澶氬崗璁彂甯冦€佹硾鍖栬皟鐢ㄣ€佹湇鍔¢檷绾х啍鏂瓑锛夛紝鍏朵腑鏈嶅姟娉ㄥ唽鍙戠幇宸茬粡鏀寔 zookeeper/etcd/consul/nacos 涓绘祦娉ㄥ唽涓績銆傝繖閲屼笉灞曞紑璇︾粏浠嬬粛锛岀洰鍓� dubbo-go 鏀寔鐨勫姛鑳藉彲浠ユ煡鐪嬮」鐩� readme 涓殑 feature list 锛岃鎯呭弬鑰冿細 [https://github.com/apache/dubbo-go#feature-list](https://github.com/apache/dubbo-go#feature-list) + +鐩墠绀惧尯姝e湪寮€鍙戜腑鐨勫姛鑳斤紝涓昏鏄棭鏈熺敤鎴蜂娇鐢ㄨ繃绋嬩腑鎻愬嚭鐨勪竴浜涢渶姹傦紝涔熸槸鐢熶骇钀藉湴涓€浜涘繀闇€鐨勯渶姹傦紝濡傜洃鎺с€佽皟鐢ㄩ摼璺熻釜浠ュ強鏈嶅姟璺敱銆佸姩鎬侀厤缃腑蹇冪瓑鏇撮珮绾х殑鏈嶅姟娌荤悊闇€姹傘€� + +**dubbo-go 鍔熻兘浠嬬粛涔嬫硾鍖栬皟鐢�** + + + +杩欓噷璇︾粏鍋氬嚑涓噸鐐瑰姛鑳界殑浠嬬粛銆傞鍏堟槸娉涘寲璋冪敤锛屽涓婂浘锛岃繖涓篃鏄ぞ鍖哄悓瀛︽彁鐨勯渶姹傘€傝鍚屽鍏徃鍐呴儴鏈夊緢澶� Dubbo 鏈嶅姟锛屼粬浠敤 Go 鍋氫簡涓€涓� api gateway 缃戝叧锛屾兂瑕佹妸 Dubbo 鏈嶅姟鏆撮湶鎴愬缃� http 鎺ュ彛銆傚洜涓哄唴閮ㄧ殑 Dubbo 鏈嶅姟姣旇緝澶氾紝涓嶅彲鑳芥瘡涓€涓� Dubbo 鏈嶅姟閮藉幓鍋氫竴涓秷璐圭鎺ュ彛鍘诲仛閫傞厤锛岃繖鏍风殑璇濅竴鏃︽湇鍔$鏀瑰姩锛屽鎴风涔熻鏀广€傛墍浠ヤ粬杩欒竟鐨勬€濊矾鏄仛鍩轰簬 dubbo-go 鍋氭硾鍖栬皟鐢紝 api-gateway 瑙f瀽鍑哄缃戣姹傜殑鍦板潃锛岃В鏋愬嚭鎯宠璋冪敤鐨� Dubbo 鏈嶅姟鐨勭洰鏍囥€傚熀浜� dubbo-go consumer 娉涘寲璋冪敤鎸囧畾 service銆乵ethod 锛屼互鍙婅皟鐢ㄥ弬鏁般€� + +鍏蜂綋鐨勫師鐞嗘槸锛� dubbo-go 杩欒竟浣滀负娑堣垂绔紝瀹為檯浼氶€氳繃鏈湴 genericService.invoke 鏂规硶鍋氫唬鐞嗭紝鍙傛暟閲岄潰鍖呭惈浜� service name锛宮ethod name 锛岃繕鍖呭惈琚皟鐢ㄧ洰鏍� service 闇€瑕佺殑鍙傛暟绫诲瀷銆佸€肩瓑鏁版嵁锛岃繖浜涙暟鎹悗闈細閫氳繃 dubbo-go-hession2 鍋氳浆鎹紝浼氬皢鍐呭杞寲鎴� map 绫诲瀷锛岀粡杩囩綉缁滃彂閫佸埌瀵瑰簲鐨� Java 鏈嶅姟绔紝鐒跺悗 Java 閭h竟鏄帴鏀剁殑 map 绫诲瀷鐨勫弬鏁帮紝浼氳嚜鍔ㄥ弽搴忓垪鍖栨垚鑷繁鐨� pojo 绫诲瀷銆傝繖鏍峰氨瀹炵幇浜� dubbo-go 浣滀负瀹㈡埛绔紝娉涘寲璋冪敤 Dubbo 鏈嶅姟绔殑鐩殑銆� + +**dubbo-go 鍔熻兘浠嬬粛涔嬮檷绾х啍鏂�** + + + +闄嶇骇鐔旀柇杩欒竟鏄熀浜庣殑鏄ぇ瀹舵瘮杈冪啛鎮夌殑 hystrix 鐨� Go 璇█鐗堟湰锛屽熀浜� hystrix 锛岀敤鎴峰彲浠ュ畾涔夌啍鏂鍒欏拰闄嶇骇瑙﹀彂鐨勪唬鐮佹銆傞檷绾х啍鏂敮鎸佹槸浣滀负涓€涓嫭绔嬬殑 dubbo-go filter 锛屽彲浠ョ伒娲婚€夋嫨鏄惁鍚敤锛屽鏋滀笉鍚敤灏卞彲浠ュ湪鎵撳寘鐨勬椂鍊欎笉灏嗕緷璧栧紩鍏ャ€侳ilter 灞傛槸 dubbo-go 涓浜庤姹傞摼璺殑涓€涓矗浠婚摼妯″紡鎶借薄锛岀洰鍓嶆湁璁稿鍔熻兘閮芥槸鍩轰簬鍔ㄦ€佹墿灞� filter 閾炬潵瀹炵幇鐨勶紝鍖呮嫭 trace銆乴eastactive load balacne銆乴og 绛夈€傞檷绾х啍鏂璁℃垚涓€涓湇鍔¤皟鐢ㄧ鐙珛鐨� filter 鍙互鐏垫椿婊¤冻璋冪敤绔瑙掑浜庡井鏈嶅姟鏋舵瀯涓€滈槻闆穿鈥滅殑鏈嶅姟娌荤悊闇€姹傘€� + +**dubbo-go 鍔熻兘浠嬬粛涔嬪姩鎬侀厤缃�** + +鍏充簬鍔ㄦ€侀厤缃腑蹇冿紝 Dubbo 鐨� 2.6 鍒� 2.7 鐗堟湰鍋氫簡涓€涓瘮杈冨ぇ鐨勫彉鍖栵紝浠庝箣鍓嶇殑 url 閰嶇疆褰㈠紡杩囨浮鍒颁簡鏀寔閰嶇疆涓績 yaml 鏍煎紡閰嶇疆鐨勫舰寮忥紝娌荤悊绮掑害涔熶粠鍗曟湇鍔$骇鍒殑閰嶇疆鏀寔鍒颁簡搴旂敤绾у埆鐨勯厤缃紝涓嶈繃鍦� 2.7 鐗堟湰涓繕鏄吋瀹� 2.6 鐗堟湰 url 褰㈠紡杩涜鏈嶅姟閰嶇疆鐨勩€俤ubbo-go 杩欒竟鑰冭檻鍒拌窡 Dubbo2.6 鍜� 2.7 鐨勪簰閫氭€э紝鍚屾牱鏀寔 url 鍜岄厤缃枃浠舵柟寮忕殑鏈嶅姟閰嶇疆锛屽悓鏃跺吋瀹瑰簲鐢ㄧ骇鍒拰鏈嶅姟绾у埆鐨勯厤缃紝璺� dubbo 淇濇寔涓€鑷达紝鐩墠宸茬粡瀹炵幇浜� zookeeper 鍜� apollo 浣滀负閰嶇疆涓績鐨勬敮鎸併€� + +## dubbo-go roadmap 2019-2020 + + + +鏈€鍚庢槸澶у姣旇緝鍏虫敞鐨勶紝绀惧尯鍏充簬 dubbo-go 2019 骞翠笅鍗婂勾鐨勮鍒掞紝鐩墠鏉ョ湅涓昏杩樻槸鐜版湁鍔熻兘鐨勮ˉ榻愬拰涓€浜涢棶棰樼殑淇锛屾垜浠殑鐩爣灏辨槸棣栧厛鍋氬埌 Java 鍜� Go 鍦ㄨ繍琛屾椂鐨勫吋瀹逛簰閫氬拰鍔熻兘鐨勪竴鑷达紝鍏舵鏄煡婕忚ˉ缂� dubbo-go 浣滀负涓€涓畬鏁� Go 璇█寰湇鍔℃鏋跺湪鍔熻兘涓婄殑鍙互鏀硅繘涔嬪銆� + +鍙﹀鍊煎緱鍏虫敞鐨勪竴鐐规槸锛岄璁′粖骞村勾搴曪紝 dubbo-go 浼氬彂甯冧竴涓敮鎸� kubernetes 浣滀负娉ㄥ唽涓績鐨勬墿灞曪紝绉瀬鎷ユ姳浜戝師鐢熺敓鎬併€傚叧浜庝簯鍘熺敓鐨勬敮鎸侊紝绀惧尯鍓嶆湡鍋氫簡绉瀬鐨勫伐浣滐紝鍖呮嫭璁ㄨ鍏充簬 dubbo-go 涓� Service Mesh 鐨勫叧绯讳互鍙婂湪鍏朵腑鐨勫畾浣嶏紝鍙互鑲畾鐨勬槸锛� dubbo-go 灏嗕細閰嶅悎 Dubbo 绀惧尯鍦� Service Mesh 鏂瑰悜鐨勮鍒掑苟鎵紨閲嶈瑙掕壊锛屾垜浠垵姝ラ璁′細鍦ㄦ槑骞寸粰鍑轰笌 Service Mesh 寮€婧愮ぞ鍖洪」鐩泦鎴愮殑鏂规锛岃澶у鏈熷緟銆� + +dubbo-go 绀惧尯鐩墠灞炰簬蹇€熷仴搴锋垚闀跨姸鎬侊紝浠庢崘璧犵粰 Apache 鍚庣殑涓嶅埌 3 涓湀鐨勬椂闂撮噷锛屽惛寮曚簡澶ф壒閲忕殑娲昏穬寮€鍙戣€呭拰鎰熷叴瓒g殑鐢ㄦ埛锛屾杩庡悇浣嶅悓閬撳湪浣跨敤鎴栬€呭涔犱腑閬囧埌闂鑳藉鏉ョぞ鍖鸿璁烘垨鑰呯粰浜堟寚姝o紝涔熸杩庡 dubbo-go 鏈夋綔鍦ㄩ渶姹傛垨鑰呭 dubbo-go 鎰熷叴瓒g殑鍚岄亾鑳藉姞鍏ュ埌绀惧尯涓€� + +**浣滆€呬粙缁�**锛� + +浣曢懌閾紝鐩墠灏辫亴浜庢惡绋嬶紝鍩虹涓彴鐮斿彂閮ㄦ妧鏈笓瀹讹紝dubbo-go 鍏卞悓鍙戣捣浜恒€佷富瑕佷綔鑰咃紝Apache Dubbo committer锛屽叧娉ㄤ簰鑱旂綉涓彴浠ュ強涓棿浠堕鍩熴€� \ No newline at end of file diff --git a/doc/md/arch/dubbo-go-review-and-future.md b/doc/md/arch/dubbo-go-review-and-future.md new file mode 100644 index 0000000000000000000000000000000000000000..9fc1df668b664a8790b9edc1c1123f2386cb6212 --- /dev/null +++ b/doc/md/arch/dubbo-go-review-and-future.md @@ -0,0 +1,246 @@ +# [dubbogo 鍥為【涓庡睍鏈沒(https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/103856203/) + +2020-01-06 08:30:00 + +Dubbo 鏄樋閲屼簬 2011 骞村紑婧愮殑涓€娆鹃珮鎬ц兘 RPC 妗嗘灦锛屽湪 Java 鐢熸€佷腑鍏锋湁涓嶅皬鐨勫奖鍝嶅姏銆�2019骞�5鏈�21鏃ワ紝Dubbo 浠� Apache 杞欢鍩洪噾浼氭瘯涓氾紝鎴愪负 Apache 椤剁骇椤圭洰銆傜洰鍓嶏紝姣曚笟鍚庣殑 Dubbo 椤圭洰鐨勭敓鎬佷腑宸茬粡姝e紡瀹樺寮曞叆浜� Go 璇█锛屽彂甯冧簡 Dubbogo 椤圭洰銆傛湰鏂囧嵆鏄 Dubbogo 杩欎竴椤圭洰鐨勫畬鏁村洖椤句笌鐪熷疄灞曟湜銆傜敱铓傝殎閲戞湇涓棿浠舵妧鏈笓瀹朵簬闆ㄥ拰鎼虹▼鍩虹涓彴鐮斿彂閮ㄥ伐绋嬪笀鏂归摱鍩庡悎浣滃畬鎴愩€� + +**01** + +**Dubbogo聽鏁翠綋妗嗘灦** + +鍏堜粙缁嶄竴涓� dubbogo 鐨勭紭璧凤紝鍏堢湅涓嬮潰杩欏箙鍥撅細 + +聽 聽 聽 聽 聽 聽 聽 + +鏈€鍙宠竟鐨� service0 鍜� service1 鏄� Dubbo 鐨勬湇鍔$锛屽乏杈圭殑 gateway 鏄綉鍏筹紝HTTP 聽璇锋眰浠庣綉鍏宠繘鏉ワ紝蹇呴』杞寲鎴� Dubbo 鐨勫崗璁墠鑳藉埌鍚庨潰鐨勬湇鍔★紝鎵€浠ヤ腑闂村姞浜嗕竴灞俻roxy 瀹屾垚鐩稿叧鍔熻兘銆傚熀鏈笂姣忎釜 service 閮介渶瑕佷竴涓� proxy 鍘昏浆鍖栧崗璁拰璇锋眰锛屾墍浠ヨ繖涓椂鍊� dubbogo 鐨勯」鐩渶姹傚氨鍑烘潵浜嗐€傛渶鍒濈殑瀹炵幇灏辨槸浠� Dubbo 鐨� Go 鐗堟湰浣滀负鐩爣锛屽疄鐜颁笌 Java 鐗堟湰 Dubbo 鐨勪簰璋冦€� + +**Dubbogo 鐩爣** + +聽聽 聽聽聽 聽 聽 聽聽 + +鐒跺悗杩欎釜鍥炬槸 dubbogo 鐨勭幇鍦ㄨ揪鍒扮殑鐩爣锛氱敤涓€浠� Go 瀹㈡埛绔殑浠g爜鑳藉鍦ㄦ病鏈変换浣曚唬鐞嗗拰鍏朵粬涓棿浠剁殑鎯呭喌涓嬬洿鎺ヨ皟鐢ㄥ叾浠栬瑷€绔紝涓昏鏄疛ava 鏈嶅姟绔殑鏈嶅姟鍜� Go 鏈嶅姟绔殑鏈嶅姟锛岃€� Go 浣滀负鏈嶅姟绔殑鏃跺€欙紝Java 瀹㈡埛绔篃鍙互鐩存帴璋冪敤 Go 鏈嶅姟绔殑鏈嶅姟銆� + +**Dubbogo 鍙戝睍鍘嗙▼** + +聽 聽 聽 聽 聽 聽 聽 + +涓嬮潰浠嬬粛 dubbogo 鐨勬暣涓彂灞曞巻绋嬶紝鍦�2016骞�8鏈堜唤鐨勬椂鍊欐槸浜庨洦鏋勫缓浜� dubbogo 椤圭洰锛岄偅涓椂鍊欑殑 dubbogo 鍙敮鎸侀€氳繃 jsonrpc 2.0 鍗忚 杩涜 HTTP 閫氫俊锛屽埌 2018 骞�2 鏈堜唤鏀寔 hessian2 鍗忚杩涜 TCP 閫氫俊锛屽埌 2018 骞� 5 鏈堥」鐩 dubbo 瀹樻柟鍏虫敞鍚庡紑濮嬩粠闆堕噸鏋勶紝浜庨洦 2018 骞� 8 鏈堜唤鍒濇閲嶆瀯鍑轰竴涓� 0.1 鐗堟湰銆傜敱浜庢垜浠惡绋嬭繖杈圭殑涓€浜涢渶姹傦紝2019 骞村垵鎴戝拰鎴戠殑鍚屼簨浣曢懌閾篃寮€濮嬪弬涓庝簡 dubbogo 椤圭洰鐨勯噸鏋勶紝鍚屾椂鍜屼簬闆ㄤ竴璧峰紑濮嬬粍寤虹ぞ鍖猴紝鍦� 2019 骞� 6 鏈堜唤鐨勬椂鍊� dubbogo1.0 鐗堟湰涓婄嚎锛岃繖涓増鏈殑閲嶆瀯鏄弬鐓т簡 Dubbo 鐨勬暣浣撹璁★紝涓讳綋鍔熻兘閮藉湪杩欎釜鐗堟湰閲岄潰鏈夊憟鐜帮紝鍚屾湡璇ラ」鐩繘鍏ヤ簡 Apache 缁勭粐銆備粖骞� 8 鏈堜唤鐢辩ぞ鍖哄悓瀛︽湜鍝ヨ礋璐g殑 Dubbo-go-hessian2 鐨勯」鐩篃杩涗簡 Apache 缁勭粐銆傚埌鐩墠涓烘鎴戜滑绀惧尯鏈変簺宸ヤ綔宸茬粡鍜� dubbo 榻愬ご骞惰繘锛屼緥濡傚 grpc 鍜� k8s 鐨勬敮鎸侊紝鐩稿叧浠g爜姝e湪 review 涓紝骞村簳鍙戝竷鐨� v1.3 鐗堟湰浼氬寘鍚� grpc 鏀寔銆傞璁″埌2020骞达紝涔熸槸鏄庡勾鐨勭洰鏍囷紝甯屾湜椤圭洰鑳戒互鍏ㄦ柊濮挎€佽瀺鍏ヤ簯鍘熺敓鏃朵唬銆� + +**Dubbogo 鏁翠綋璁捐** + +聽 聽 聽 聽 聽 聽 聽 + +杩欎釜鍥惧ぇ瀹舵槸涓嶆槸鐪嬬潃寰堢啛鎮夛紝鏄� Dubbo 鐨勬暣涓垎灞傝璁″浘锛屼絾鏄皯浜� Dubbo 閲岄潰鐨勫緢澶氫笢瑗匡紝鍥犱负鎴戜滑鏄€熼壌浜� Dubbo 鐨勫垎灞傝璁″拰鏄撴嫇灞曟€х殑鎬濇兂锛屼絾鏄敱浜� Go 璇█鍜� Java 璇█鐨勬湰璐ㄥ樊鍒喅瀹氫簡鎴戜滑椤圭洰璁捐涓嶅彲鑳藉畬鍏ㄧ収鎼畠锛屾湁涓€浜涗笢瑗垮氨缁欏畠绠€鍖栦簡锛岀壒鍒槸鍗忚灞傝繖涓€鍧椼€傛瘮濡傝 Dubbo 閲岄潰 SPI 鐨勬嫇灞曪紝鍦� Go 閲岄潰鎴戜滑鏄€熺敤浜� Go 鐨勯潪渚靛叆寮忔帴鍙g殑鏂瑰紡鍘诲疄鐜扮殑锛岀敱浜� Go 绂佹 package 寰幆寮曠敤锛屾墍浠� dubbogo 鍦ㄤ唬鐮佺殑鍒嗗寘鍒嗗眰涓婇潰涔熸槸鏈変弗鏍肩殑瑙勫畾锛岃繖姝eソ璺熷畠鐨勬槗鎷撳睍鎬х殑鐗规€х粨鍚堜簡璧锋潵銆� + +鍏充簬浠g悊閮ㄥ垎锛屽洜涓� Java 鏈夊姩鎬佷唬鐞嗭紝Go 鐨勫弽灏勫氨娌℃湁 Java 鐨勫弽灏勯偅涔堝己澶э紝鎵€浠ユ垜浠繖杈逛唬鐞嗙殑瀹炵幇鏂瑰紡涔熻窡瀹冩槸涓嶄竴鏍风殑銆� + +**Dubbogo 鑳藉姏澶у浘** + +聽 聽 聽 聽 聽 聽 聽 + +涓婇潰鐨勫浘鏄垜浠綋鍓� dubbogo 椤圭洰瀹炵幇鐨勮兘鍔涘ぇ鍥撅紝鏈€涓婂眰鏄綋鍓嶅疄鐜扮殑涓€浜涙敞鍐屼腑蹇冩湁 zk銆乪tcd銆乶acos銆乧onsul锛岀幇鍦ㄤ笌 k8s 鍏宠仈鐨勫姛鑳芥鍦ㄥ紑鍙戜腑銆傞厤缃腑蹇冪洰鍓嶆槸鏀寔 Apollo 鍜� zookeeper銆傚乏杈规槸娑堣垂绔紝娑堣垂绔繖杈瑰疄鐜扮殑鏄湁 cluster 鐨勶紝绛栫暐涓婂熀鏈笂瀹炵幇浜� dubbo 鏀寔鐨勬墍鏈夌瓥鐣ャ€傜劧鍚庤繕鏈夎礋杞藉潎琛$瓥鐣ワ紝fillter 涓昏鏄湁涓€涓� tps 鐨勯檺娴佽繕鏈夋硾鍖栬皟鐢紝杩欎袱涓悗闈細璁插埌銆傜紪鐮佸眰鐜板湪灏辨槸 jsonrpc 2.0 鍜� hessian2锛宲rotobuf v3 姝e湪鍔犵揣 review 涓€傜洰鍓嶇ぞ鍖烘鍦ㄥ紑鍙戜腑鐨勬敮鎸侊紝鍖呮嫭 trace銆乬rpc銆乲8s娉ㄥ唽涓績锛屼互鍙婂 restful 鐨勬敮鎸併€� + +**鍏抽敭椤圭洰** + +聽 聽 聽 聽 聽 聽 聽 + +鐩墠 dubbogo 椤圭洰鏁翠綋鐢� 4 涓粍鎴愰儴鍒嗐€傜涓€涓槸 getty锛屼竴涓紓姝ョ綉缁� IO 搴擄紝鏄疄鐜� tcp 閫氫俊鍗忚鏈€鍧氬疄鐨勫熀纭€锛涚浜屼釜鏄� dubbo-go-hessian2锛岃繖涓槸涓庡綋鍓� java hessian2 楂樺害鍏煎鐨勯」鐩紱绗笁涓槸 gost锛屾槸 dubbogo 鐨� 鍩虹搴擄紱鏈€鍚庢槸 dubbogo 鐨勭ず渚嬪簱锛岀洰鍓嶅凡缁忚縼绉诲埌 https://github.com/apache/dubbo-samples锛屽拰 Java 绀轰緥鍚堝苟浜嗐€傝繖浜涢兘鏄綋鍓� dubbogo 涓昏鐨勭粍鎴愰」鐩€� + +**02** + +**鍗忚瀹炵幇** + +聽 聽 聽 聽 聽 聽 聽 + +鎺ヤ笅鏉ヨ涓€浜涘叿浣撶殑瀹炵幇鍜岄儴鍒嗙殑鍔熻兘锛屼笂鍥炬槸 dubbo-go-hessian2 瀹炵幇锛屽垪鍑烘潵鏄竴浜涗富瑕佺殑鍔熻兘鍒楄〃锛岀涓€涓槸 Java 鐨� JDK Exceptions 鐨勫疄鐜帮紝閲岄潰瀹炵幇浜� 40 澶氱鐨� Java JDK 涓昏鐨勫紓甯革紝鍙互涓� Java 鐨� hessian2 鐗堟湰浜掔浉瑙g紪鐮佺殑鏀寔锛屾敮鎸佽嚜鍔ㄦ墿灞曡嚜宸卞疄鐜� exceptions锛屾垨鑰呮槸涓嶅父瑙佺殑 Exceptions锛涚浜屼釜鏄敮鎸佸瓧娈靛悕鐨勮仈鍚嶏紝Go 鍙簭鍒楀寲鐨勫瓧娈垫槸澶у啓瀛楁瘝寮€澶达紝浣嗘槸 Java 榛樿鏄皬鍐欏紑澶寸殑锛屾墍浠ユ湁缂栫爜鐨勫瓧娈靛悕涓嶄竴鑷寸殑闂锛岃繖灏辨湁鍒悕璇嗗埆鍜屾敮鎸佽嚜瀹氫箟鍛藉悕銆� + +go-hessian2 杩樻敮鎸� Java 鐨� bigdecimal銆丏ate銆乀ime銆佸熀鏈被鍨嬬殑鍖呰绫诲瀷銆丟eneric Invocation銆丏ubbo Attachements锛岀敋鑷虫敮鎸� emoji 琛ㄦ儏銆� + +go-hessian2 閲岄潰濡傛灉瑕佽В鐮佸拰缂栫爜鐢ㄦ埛鑷畾涔夌被鍨嬶紝鐢ㄦ埛闇€瑕佽嚜宸辨妸瀹冩敞鍐岃繘鍘伙紝鍓嶆彁鏄敮鎸� go-hessian2 鐨� POJO interface锛屾墠鑳借窡 JAVA 瀵瑰簲绫讳簰鐩歌В缂栫爜銆� + +聽 聽 聽 聽 聽 聽 聽 + +涓婇潰鏄� go-hessian2 鐨勭被鍨嬪搴旇〃锛岄渶瑕佺壒鍒偣鍑虹殑鏄� int锛実o 杩欒竟鐨� int 绫诲瀷鍦ㄤ笉鍚屽瓧闀跨郴缁熶笅鏄湁涓嶅悓鐨勫ぇ灏忥紝鍙兘鏄� 32 浣嶄篃鍙兘 64浣嶇殑锛岃€� Java 鐨� int 鏄� 32 浣嶇殑锛屾墍浠ユ垜浠互 go 璇█鐨� int32 绫诲瀷瀵瑰簲 Java int 绫诲瀷銆� + +鍒氭墠鎻愬埌浜� Java 鐨� Class 鍜� go struct 鐨勫搴斻€備笂鍥炬湁涓€涓� go-hessian2 鐨� POJO 鎺ュ彛瀹氫箟锛屾瘡涓� Java class 瀵瑰簲鍒� go struct锛屽垯 struct 闇€瑕佺粰鍑� Java ClassName銆� + +聽 聽 聽 聽 聽 聽 聽 + +浣犱篃鍙互鍔� hessian 鏍囩锛岃В鏋愮殑鏃跺€欎細鎶婅繖涓瓧娈靛悕鐢ㄥ埆鍚嶅啓杩涘幓锛屽疄鐜拌嚜瀹氫箟 fieldName銆傞粯璁ゆ儏鍐典笅锛実o-hessian2 涓細鑷姩鎶� struct field 棣栧瓧姣嶅彉鎴愬皬鍐欎綔涓哄叾 fieldName銆� + +聽 聽 聽 聽 聽 聽 聽 + +娉涘寲寮曠敤锛屾槸 dubbogo 閲岄潰姣旇緝閲嶈鐨勫姛鑳姐€傜ぞ鍖轰竴浣嶅悓瀛﹂渶瑕佸熀浜� dubbogo 瀹炵幇缃戝叧锛屾敹闆嗗閮ㄧ殑璇锋眰锛岀劧鍚庨€氳繃娉涘寲寮曠敤鐨勫舰寮忚皟鐢ㄥ叾浠� Dubbo 鏈嶅姟锛屾渶鍚庤嚜宸卞姩鎵嬫妸瀹冨疄鐜颁簡銆備娇鐢ㄦ椂锛岄鍏堥渶瑕佸湪椤圭洰閲屽唴缃竴涓� GenericService 鏈嶅姟锛岃皟鐢↙oad锛岀劧鍚庡氨鍍忔甯哥殑璋冪敤鏈嶅姟涓€鏍风洿鎺ヨ皟鐢紝璺� Java 鏄被浼肩殑锛孏o 瀹㈡埛绔彲浠ヤ笉鐭ラ亾 Java 鐨勬帴鍙e畾涔夊拰绫诲畾涔夛紝鎶婃柟娉曞悕銆佸弬鏁扮被鍨嬨€佸弬鏁版暟缁勪互涓€涓� map 鐨勫舰寮忎紶杈撳埌 Java 鏈嶅姟绔紝Java 鏈嶅姟绔敹鍒拌姹傚悗杩涜璇嗗埆锛屾妸瀹冭浆鎹㈡垚 POJO 绫汇€� + +浠ヤ笂鏄� go-hessian2 涓€浜涚粏鑺傘€備笂鏂囪鍒扮殑娉涘寲寮曠敤锛屾槸鎶婄綉鍏充綔涓烘暣涓唴缃� Dubbo 鏈嶅姟鐨勫叕鍏辨秷璐圭锛屼娇鐢ㄧ殑鏃跺€欏彧闇€瑕佺煡閬撹姹傜殑鏂规硶銆佸弬鏁扮被鍒紝鐒跺悗灏辫兘澶熻皟鐢� Dubbo 鐨勬湇鍔°€傚悗闈富瑕佸垎浜笁閮ㄥ垎鍐呭锛氶鍏堟槸缃戠粶寮曟搸銆佸簳灞傜綉缁滃簱杩欏潡锛涘叾娆℃槸鏈嶅姟娌荤悊鏂归潰鐨勫唴瀹癸紝鍏朵腑鍖呭惈浠� k8s 浣滀负娉ㄥ唽涓績鐨勪竴涓垵姝ョ殑瑙e喅鏂规锛涚涓夐儴鍒嗘槸浜掕仈浜掗€氾紝涓昏鏄拰 grpc 鎵撻€氥€傛渶鍚庣粰鍑轰竴涓睍鏈涳紝鍖呭惈 dubbogo 绀惧尯鏄庡勾鐨勫伐浣滃唴瀹广€� + +**03** + +**缃戠粶寮曟搸** + +dubbogo 鐨勭綉缁滃紩鎿庨噷闈㈠垎涓轰笁灞傦紝 濡備笅鍥炬墍绀猴細 + + + +鏈€搴曞眰 streaming 澶勭悊浜岃繘鍒舵祦锛岀浜屽眰 codec灞傦紝杩涜鍗忚鐨勫簭鍒楀寲鍜屽弽搴忓垪鍖栵紝绗笁灞傛槸 Eventlistener锛屾彁渚涘簲鐢ㄤ娇鐢ㄦ帴鍙c€俿treaming 灞傝兘鏀寔 websocket銆乀CP銆乁DP 涓夌缃戠粶閫氳鍗忚锛岃繖灞傚叿鏈変竴瀹氱殑鐏垫椿鎬э紝浠婂勾骞村垵涓婃捣鏈変竴涓悓瀛︿粖骞存妸 KCP 涔熷姞杩涘幓浜嗭紝褰撴椂璇磋寮€婧愯础鐚嚭鏉ワ紝鎴戣繕鍦ㄦ湡寰呬腑銆俢odec 灞傚彲浠ラ€傜敤涓嶅悓鍗忚锛岀敤鎴疯嚜瀹氫箟鍗冲彲銆� + +聽 聽 聽 聽 + +EventListener 瀵逛笂灞傛毚闇蹭簡 4 涓洖璋冩帴鍙c€傜涓€涓槸 OnOpen锛岀綉缁滆繛鎺ュ垵寤烘垚鍔熸椂琚皟鐢紝搴旂敤灞傚鏋滃垽瀹氬叾涓烘甯歌繛鎺ワ紝鍒欏彲浠ユ妸杩炴帴 session 瀛樺偍涓嬫潵锛屽鏋滅敤鎴峰垽鏂綋鍓嶈繛鎺ヨ繃澶氬垯杩斿洖涓€涓潪绌虹殑 error锛屽垯杩欎釜杩炴帴浼氳 dubbogo 鍏抽棴銆傚叾娆℃槸 OnError 浜嬩欢锛屽綋缃戠粶杩炴帴鍑洪敊锛屽氨浼氬洖璋冨埌杩欎釜鎺ュ彛锛屽湪 dubbogo 鍏抽棴杩欎釜杩炴帴涔嬪墠鍏佽鐢ㄦ埛鍋氱浉搴斿鐞嗭紝濡傛妸缃戠粶杩炴帴 session 浠庡簲鐢ㄥ眰鐨� session 姹犱腑鍒犻櫎銆傜涓変釜鏄� OnCron锛屽鐞嗗畾鏃朵换鍔★紝濡傚績璺筹紝dubbogo 閽堝 websocket 鍗忚鍦ㄥ簳灞傜洿鎺ユ妸蹇冭烦鐑换鍔″鐞嗕簡锛岄拡瀵� tcp 鍜� udp 鍗忚闇€瑕佺敤鎴峰湪杩欎釜鍥炶皟鍑芥暟涓嚜宸卞疄鐜般€傜鍥涗釜鎺ュ彛鏄� OnMessage锛岀敤浣滃鐞嗕竴涓畬鏁寸殑缃戠粶鍖呫€傚彲浠ョ湅鍒版暣涓洖璋冩帴鍙i鏍艰窡 websocket 鐨勬帴鍙e緢鍍忋€� + +聽 聽 聽 聽 聽 聽 聽 + +**鍗忕▼姹�** + +dubbogo 鐨� goroutine pool 閲屾湁 worker channel 銆愭暟閲忎负 M銆戝拰閫昏緫澶勭悊 goroutine 銆愭暟閲忎负 N銆戝拰缃戠粶浠诲姟銆愮綉缁滃寘銆戜笁绉嶈鑹诧紝缃戠粶瑙e寘鍚庢妸鎶婂寘鎸夌収鏌愮瑙勫垯鏀惧叆鏌愪釜 worker pool锛岀劧鍚庨€昏緫澶勭悊 goroutine 浠� channel 涓鍙栨暟鎹寘骞舵墽琛岄€昏緫澶勭悊锛屽叾鐩殑鏄槸涓轰簡鎶婄綉缁� I/O 涓庨€昏緫澶勭悊鍒嗗紑銆備笉鍚岀殑 goroutine pool 璁捐涓紝鏈夌殑 N 澶у皬浼氬彉鍖栵紝鏈夌殑涓嶅彉锛屽垎鍒彲绉颁箣涓哄彲浼哥缉 goroutine pool 鍜屼笉鍙几缂� goroutine pool锛屽彲浼哥缉 goroutine pool 鍙互瀵规満鍣ㄨ祫婧愮殑浣跨敤涓嶅彲棰勮銆俤ubbogo 閲囩敤浜嗕笉鍙几缂� goroutine pool锛屽叾鑰冮噺鏄檺瀹氬叾缃戠粶璧勬簮浣跨敤鐨勪笂闄愩€� + +鍙﹀锛宒ubbogo 鐨� goroutine pool 涓嶈€冭檻鏀跺寘鍚庣殑澶勭悊椤哄簭銆傝濡傦紝dubbogo 鏈嶅姟绔敹鍒颁簡 A 鍜� B 涓や釜缃戠粶鍖咃紝dubbogo 鏈夊彲鑳藉厛澶勭悊缃戠粶鍖� B锛屽悗澶勭悊缃戠粶鍖� A銆傚鏋滃鎴风鐨勬瘡娆¤姹傞兘鏄嫭绔嬬殑锛屾病鏈夊墠鍚庨『搴忓叧绯伙紝鍒欏甫鏈変笉鑰冭檻缃戠粶鍖呭鐞嗛『搴忔槸娌℃湁闂鐨勩€傚鏋滄湁寮洪『搴忚姹傦紝璀涓婂眰鐢ㄦ埛鍏虫敞 A 鍜� B 璇锋眰澶勭悊鐨勫墠鍚庨『搴忥紝鍒欏彲浠ユ妸 A 鍜� B 涓や釜璇锋眰鍚堝苟涓轰竴涓姹傦紝鎴栬€呮妸 dubbogo 鐨� goroutine pool 鐗规€у叧闂€� + +涓€鑸儏鍐典笅锛屼笉寤鸿澶у鑷繁鍐� goroutine pool锛屽洜涓� Go 璇█瀵� goroutine 璧勬簮鐨勭鐞嗗凡缁忛潪甯稿厛杩涳紝姣斿閲婃斁涓€涓崗绋嬶紝Go 涓嶄細椹笂閿€姣佹帀鐩稿叧鐨勮祫婧愶紝涓€鏃︽湁鍒涘缓 goroutine 鐨勯渶瑕侊紝椹笂灏卞彲澶嶇敤杩欎釜鎴愭湰鏄緢浣庣殑銆備粈涔堟儏鍐典笅浣跨敤 Goroutine Pool 鍛紵涓汉瑙夊緱鍍忕綉缁滃簱閫昏緫澶勭悊杩欑被鍦烘櫙涓嬫墽琛屽悓鏍风被鍨嬩换鍔″満鏅笅纭畾 goroutine 浼氳杩呴€熼噸澶嶄娇鐢ㄦ椂鍙互灏濊瘯浣跨敤锛屼絾鏄€庝箞鐢ㄥソ杩樻槸闇€瑕佷粩缁嗚€冮噺锛屽嵆闇€瑕佷粩缁嗚€冮噺 M 涓� N 鐨勬瘮渚嬪叧绯汇€� + +鍋囪澶勭悊鏌愮缃戠粶浠诲姟璇锋眰锛屾湁鐨勮姹�1绉掑氨澶勭悊瀹屼簡锛屾湁鐨勫彲鑳�10姣澶勭悊瀹屼簡锛岃缃� M 涓� N 姣斾緥涓� 1:1锛岃繖鏍� 1 瀵� 1 閫犳垚鐨勫悗鏋滃彲鑳芥槸楗ラタ锛屽氨鏄湁涓€浜涢槦鍒楀鐞嗙殑寰堝揩锛屾湁鐨勫鐞嗗緢鎱紝鏁翠綋璐熻浇涓嶅潎琛★紝杩欑鎯呭喌涓嬪氨涓嶆帹鑽愪綘鐢ㄥ崗鎴愭睜浜嗐€� + +杩樻湁涓€涓瘮渚嬫ā鍨嬫槸鏄�1锛歂鐨勶紝涓€鍐欏璇伙紝姣斿璇存墍鏈夌殑璇锋眰閮戒氦缁欎竴涓槦鍒楋紝鎵€鏈夐€昏緫澶勭悊 goroutine pool 閮芥秷璐硅繖涓槦鍒楋紝閫犳垚鐨勭粨鏋滄槸浠€涔堝憿锛熷洜涓轰綘鍙湁涓€涓敓浜ц€咃紝閭d綘灏卞彧鏈変竴涓槦鍒楋紝澶氫釜娑堣垂鑰呮秷璐硅繖涓€涓槦鍒楋紝閫犳垚鐨勭粨鏋滄槸浠€涔堝憿锛熷洜涓� go channel 鐨勪綆鏁堢巼銆愭暣浣撲娇鐢ㄤ竴涓� mutex lock銆戦€犳垚娑堣垂鑰� goroutine hang 鍦ㄩ攣绔炰簤涓婏紝褰撶劧鍏剁綉缁滃寘澶勭悊椤哄簭鏇存棤浠庝繚璇併€� + +姣旇緝鍧囪 鐨勬晥鏋滃氨鏄� M 鍜� N 閮藉ぇ浜� 1锛宒ubbogo 鐨勭殑 goroutine pool 妯″瀷涓� M 鍜� N 鐨勫彇鍊煎彲浠ヨ嚜琛岄厤缃紝鍏舵晥鏋滄槸姣忎釜 channel 琚� N/M 涓� goroutine 娑堣垂锛岃繖绉嶆ā鍨嬬被浼间簬 kafka 鐨� consumer group锛屽叾浼樼偣鏄吋椤惧鐞嗘晥鐜囧拰閿佸帇鍔涘钩琛★紝鍙互鍋氬埌鎬讳綋灞傞潰鐨勪换鍔″鐞嗗潎琛°€� + +## + +**浼樺寲鏀硅繘** + +浼樺寲鏀硅繘涓昏浠庝笁涓柟闈㈠叆鎵嬶紝 濡備笅鍥炬墍绀猴細 + + + +聽 聽 聽 + +1\. 鍐呭瓨姹犮€俫oroutine pool 鏄鐞嗗 CPU 璧勬簮鐨勫垎閰嶏紝鍐呭瓨姹犲氨鏄鐞嗗唴瀛樿祫婧愮殑鍒嗛厤銆傛垜涓汉鍙嶅绾补涓轰簡鐐妧娌℃湁鐩殑鍦板啓鍐呭瓨姹狅紝鍏跺疄 Go 鐨勫唴瀛樼鐞嗚繖鍧楃洰鍓嶄紭鍖栫殑寰堝ソ浜嗐€侴o 璇█鍒濆鐗堟湰鐨勫唴瀛樼鐞嗕娇鐢ㄤ簡璋锋瓕鑷鐨� tcmalloc 搴擄紝杩欎釜搴撴妸搴旂敤閲婃斁鐨勫唴瀛樿嚜宸卞厛缂撳瓨浣忥紝寰呭け鏁堟湡鍚庢墠閲婃斁锛岄偅杩欐牱閫犳垚鐨勭粨鏋滄槸浠€涔堝憿锛熷氨鏄棭鏈熺殑 Go 绋嬪簭鐨勫唴瀛樻垚鏈緢楂樸€傚亣璁剧▼搴忎竴涓� sidecar 绋嬪簭鐨勮祫婧愰檺鍒舵槸鍐呭瓨2G锛孋PU 鏍告暟鏄� 2 鏍革紝鐢ㄨ繖鏍蜂竴涓唴瀛樼鐞嗗簱锛屽唴瀛樼敤瀹屼笉閲婃斁缁欐搷浣滅郴缁燂紝閭d箞娌′汉鏁㈢敤杩欎釜椤圭洰锛屽綋鐒舵渶鏂扮殑 Go 鍐呭瓨绠$悊鍣ㄦ槸缁忚繃瀹屽叏閲嶆瀯鐨勶紝铏界劧涔熷尯鍒嗕笉鍚屽ぇ灏� span 鐨勫唴瀛樺湪 P 绾у埆鍜屽叏灞€绾у埆杩涜缂撳瓨锛屼絾鏄熀鏈笂涓嶇敤鑰冭檻杩欑鍐呭瓨鑶ㄨ儉涓嶅彲鎺х殑闂浜嗐€傞偅涔堜粈涔堟儏鍐典笅浣跨敤鍐呭瓨姹犲憿锛熶綘纭畾浣犵殑涓氬姟鏈変竴浜涘璞℃槸棰戠箒鐨勫鐢ㄥ垯鍙互灏濊瘯浣跨敤銆� 鐩墠澶ч儴鍒嗗唴瀛樻睜鎶€鏈簳灞備緷璧栫殑搴曞骇閮芥槸 sync.Pool锛岃嚜宸卞啓涓€涓篃涓嶉毦銆傝€屼笖 Go 1.13 涔嬪悗鐨� sync.Pool 宸茬粡鍙互鍋氬埌璺� GC span 涓嶉噴鏀剧紦瀛樺璞★紝闈炲父涔嬪ソ銆偮犅� + +2\. 瀹氭椂鍣ㄣ€侴o 璇█鏃╂湡瀹氭椂鍣ㄥ洜涓烘暣浣撲娇鐢ㄤ竴鎶婂ぇ閿佺殑缂樻晠鏁堢巼鏋佸樊锛屽綋鐒舵渶鏂扮殑灏辩浉褰撳ソ浜嗭紝閫氳繃姣忎釜 CPU 鏍镐笅涓€涓畾鏃跺櫒鐨勬柟娉曘€愮被浼间簬鍒嗙墖閿併€戝垎鏁d簡绔炰簤鍘嬪姏锛屼絾鏄緢澶氭儏鍐典笅杩樻槸鏈夌珵浜夊帇鍔涳紝濡傛灉瀵规椂闂寸簿搴﹁姹備笉楂樹釜浜哄缓璁湪鑷繁鐨勫簲鐢ㄤ腑鑷繁鍐欎竴涓畝鍗曠殑鏃堕棿杞疄鐜颁竴涓畾鏃跺櫒锛岄噴鏀� CPU 鍘嬪姏銆� + +3\. 缃戠粶鍐� buffer 鍚堝苟銆傚啓 buffer 鍚堝苟涓€鑸噰鐢� writev锛屼絾鏄� Go 璇█鐨� writev 鏈夊唴瀛樻硠闇查棶棰橈紝鎴戣繖杈逛竴涓礋璐� MOSN 寮€鍙戠殑鍚屼簨鍏冩€诲彂鐜扮殑銆備粬鍏堢粰 Go 璇█瀹樻柟鎻愪氦浜� PR锛岀劧鍚庡湪 MOSN 涓妸 writev 鎵旀帀鑷繁鍐欎簡涓€涓畝鍗曞ソ鐢ㄧ殑鍐� buffer 鍚堝苟鍙戦€佸疄鐜帮細閫氳繃 for 寰幆 10 娆′粠鍙戦€� channel 涓妸缃戠粶鍖呰鍙栧嚭鏉ョ劧鍚庡悎骞跺彂閫侊紝褰撶劧寰幆涓棿缃戠粶鍙戦€� channel 娌℃湁瓒冲鐨勭綉缁滃寘灏遍€氳繃 \`select-default\` 鍒嗘敮绔嬪嵆閫€鍑哄惊鐜€� + +**channel 浣跨敤** + +Go 璇█鏄竴涓€傚悎澶勭悊 IO 瀵嗛泦鍨嬩换鍔$殑璇█锛屼笉鎿呴暱澶勭悊 CPU 瀵嗛泦鍨嬩换鍔★紝鍏跺唴瀛橀€氫俊鐨勫熀纭€灏辨槸 channel銆俢hannel 鏁翠綋鐨勫唴瀛樺熀纭€鏄竴涓� ring buffer 鏁扮粍鍜屼竴涓� lock锛屽鍔犲叾浠栦竴浜涜鍐欓€氱煡闃熷垪绛夛紝涔熸槸鍥犱负涓€鎶婂ぇ閿佺殑缂樻晠锛屽垯 buffer 鍨� channel 濡傛灉浣跨敤涓嶅綋鍒欐晥鐜囦笉浼氬緢楂橈紝濡傛瘡涓� channel element 鐨勫唴瀛樹娇鐢ㄨ繃澶с€俢hannel 杩樻湁涓€涓� closed 瀛楁锛岀敤浜庡垽瀹� channel 鐨勫啓鏄惁琚叧闂帀锛孏o 璇█瀵瑰叾鎿嶄綔鏄互鍘熷瓙閿佹柟寮忚繘琛岀殑锛屽緢澶氫汉浠ヨ繖涓瓧娈典负鍩虹杩涜淇″彿閫氱煡锛屽鏋滀娇鐢ㄤ笉褰撳緢鍙兘閫犳垚 for 寰幆 CPU 100% 鐨勯棶棰橈紝鎵€浠ュ湪 for-select 寰幆涓壒鍒璋ㄦ厧浣跨敤锛宒ubbogo 鍦ㄨ繖鏂归潰韪╄繃鍧戙€� + +**04** + +**鏈嶅姟娌荤悊** + +涓嬮潰涓哄ぇ瀹惰涓€涓嬫湇鍔℃不鐞嗭紝璇村埌鏈嶅姟娌荤悊锛屽叾瀹炴渶閲嶈鐨勮繕鏄湇鍔″彂鐜板拰鏈嶅姟娉ㄥ唽锛岃繖鍧楅€昏緫璺� Dubbo 绫讳技锛岃繖娆′笉浣滃睍寮€銆備笅闈富瑕佸寘鍚袱鏂归潰鐨勫唴瀹癸紝鍒嗗埆鏄檺娴佺畻娉曞拰浼橀泤閫€鍑恒€� + +**闄愭祦绠楁硶** + +闄愭祦绠楁硶棣栧厛闇€瑕佽€冭檻闄愭祦鐨勫璞★紝dubbogo 闇€瑕佽€冭檻 interface 鍜� method銆傚叾娆℃槸闄愭祦鏂规硶锛岄鍏堥渶瑕佽€冭檻鐨勬槸鍗曟満闄愭祦杩樻槸闆嗙兢闄愭祦锛屽崟鏈洪檺娴佺畻娉曞緢澶氾紝璀甯哥敤鐨勫浐瀹氱獥鍙g畻娉曞拰婊戝姩绐楀彛绠楁硶锛屼互鍙婃洿杩涗竴姝ョ殑鑷€傚簲闄愭祦銆傞檺娴佹椂涓€涓噸瑕侀棶棰樺氨鏄檺娴佸弬鏁版槸寰堥毦閰嶇殑锛岃濡傜嚎涓婃湇鍔″埌搴曢渶瑕佷娇鐢ㄥ灏戞満鍣ㄨ祫婧愬悎鐞嗭紝闄愭祦绐楀彛鐨勬椂闂寸獥鍙f椂闀垮簲璇ュ闀垮悎閫傦紝鍏� qps 鍊艰缃灏戝悎閫傚憿锛熻繖閮芥槸 dubbogo 闇€瑕佽В鍐崇殑闂銆傚厛杩涘璋锋瓕鐨� BBR 绠楁硶锛屽彲浠ュ湪褰撳墠鐨勭綉缁滅幆澧冩伓鍖栧墠涓嶆柇灏濊瘯鏀硅繘鐩稿叧鍙傛暟锛岀洿鍒板皾璇曞嚭涓€娈垫椂闂村唴鐨勬渶浣冲弬鏁般€傝繕鏈変竴浜涗笟鍔″舰鎬佷笅鐨勯檺娴侊紝濡傞拡瀵逛細鍛樺拰闈炰細鍛樺垎鍒璁′笉鍚岀殑闄愭祦閾捐矾銆� + +Dubbo 鐨勯檺娴佹帴鍙f簮鐮佸涓嬶細 + +聽 聽 聽 聽 聽 聽 聽 + +杩欎釜鎺ュ彛鎶借薄鏄潪甯告紓浜殑锛岀涓€涓槸闄愭祦 url锛岀浜屼釜鏈嶅姟璋冪敤銆備笅闈㈡槸 Dubbo 鐨勫浐瀹氱獥鍙i檺娴佹簮鐮侊細 + +聽 聽 聽 聽 聽 聽 聽 + +涓婇潰鐨勪唬鐮佸緢鏄庢樉锛�"private final" 鍐冲畾浜� Dubbo 浣跨敤鑰呭彧鑳戒娇鐢ㄦ湡缁欏畾鐨勫浐瀹氱獥鍙i檺娴侀檺绠楁硶锛屾棤娉曟墿灞曘€� + +浠ヤ笅鏄� dubbogo 鐨勯檺娴佹帴鍙o細 + +聽 聽 聽 聽 聽 聽 聽 + +TpsLimiter 鏄檺娴佸璞★紝TpsLimitStrategy 鏄檺娴佺畻娉曪紝RejectedExecutionHandle 鏄檺娴佸姩浣溿€� + +鎺ヤ笅鏉ユ槸涓€涓浐瀹氱獥鍙g畻娉曞疄鐜帮細 + +聽 聽 聽 聽聽 聽 聽 聽 + +涓婂浘鏄� dubbogo 鐨勫浐瀹氱獥鍙g畻娉曞疄鐜帮紝鍏堕潪绾跨▼瀹夊叏锛屽ぇ瀹剁湅涓€涓嬩唬鐮佸氨鍙互浜嗭紝涓嶆帹鑽愬ぇ瀹剁敤銆備笅鍥炬槸 dubbogo 鐨勬粦鍔ㄧ獥鍙g畻娉曞疄鐜帮細 + +聽 聽 聽 聽聽 聽 聽 聽聽 + +鍏跺熀鏈師鐞嗘槸鐢ㄤ竴涓槦鍒楀瓨鍌ㄤ竴娈垫椂闂村唴鐨勮姹傦紝鐒跺悗鏍规嵁闃熷垪闀垮害鍒ゅ畾鍗冲彲銆� + +涓嶇鏄浐瀹氱獥鍙h繕鏄粦鍔ㄧ獥鍙o紝鍏跺垽瀹氱畻娉曠畝鍗曪紝楹荤儲鐨勬槸鍏跺弬鏁拌缃紝濡備笅鍥撅細 + +聽 聽 聽 聽聽 聽 聽 聽 + +鍥哄畾绐楀彛鏃堕暱绮惧害寰堥毦鎺у埗銆傛瘮濡傝闄愭祦涓€绉� QPS 鍊� 1000锛屽墠 100 姣鏉ヤ簡涓€鍗冧釜璇锋眰锛岀劧鍚庡垽瀹氱畻娉曟妸璇锋眰鏀捐繃浜嗭紝鑰屽叾鍚� 900 姣 浠讳綍璇锋眰閮芥棤娉曞鐞嗐€備竴鑸殑澶勭悊鏂规硶鏄妸鏃堕棿绮掑害鏇寸簿缁嗕竴浜涳紝dubbogo 鐨勬椂闂寸獥鍙f渶灏忓崟浣嶆槸涓€姣锛屽垯鐢ㄦ埛鍙互鎶婃椂闂寸獥鍙h瀹氫负 100 姣锛屾€讳綋鏉ヨ涓€娈垫椂闂村唴鏄緢骞崇ǔ鐨勩€備笅闈㈣繖涓浘鏄垜浠ぞ鍖虹殑 commiter 閭撴槑鍐欏畬鍗氬鍙戝嚭鏉ワ紝琛屼笟澶т浆寰俊璇勮濡備笅锛� + +聽 聽 聽 聽 聽 聽 聽 + +鍥句腑绗竴涓棶棰樻槸 qps 鍜� tps 姣忎釜璇锋眰鎴愭湰涓嶅悓锛岃繖涓棶棰樻€庝箞澶勭悊鍛紵涓汉瑙夊緱杩欐槸涓€涓垎绾ч檺娴侀棶棰橈紝鍦ㄥ悓涓€涓湇鍔′笅閽堝涓嶅悓鐨勮姹傚仛涓嶅悓鐨勫垎绾у鐞嗐€傜浜屼釜闂 鈥濋厤缃簡 qps 1000锛屼絾鏄姹傝繃鏉ユ槸10涓囦綘杩樻槸姝烩€滐紝杩欎釜灏遍渶瑕佹洿涓婂眰鐨勮繍缁磋兘鍔涜繘琛屽簲瀵癸紝璀鍒ゅ畾涓烘伓鎰忔祦閲忔敾鍑诲氨搴旇鍦ㄧ綉鍏冲眰鎷︽埅鎺夛紝濡傛灉鏄湇鍔¤兘鍔涗笉琛屽氨鎵╁銆� + +閽堝鍒嗙骇闄愭祦锛宒ubbogo 鐩墠灏氭棤娉曞湪鍚屼竴涓繘绋嬪唴瀹屾垚锛岃繖闇€瑕� dubbogo 鐨勯厤缃腑蹇冩洿瀹屽杽浠ュ悗杩涜澶勭悊锛岀敤鎴峰彲浠ラ€氳繃鎼缓涓嶅悓鐨勬湇鍔¢摼璺鐞嗕箣銆傝濡備細鍛�/闈炰細鍛樺垎绾э紝鍚屼竴涓湇鍔¢拡瀵逛笉鍚岀殑浼氬憳绛夌骇鎼缓鐩稿簲鐨勯摼璺紝鍦ㄧ綉鍏冲眰灏卞垽瀹氫竴涓� userID 鏄惁鏄細鍛橈紝鐒跺悗鍙戦€佷笉鍚岀殑閾捐矾銆� + +dubbogo 鐨勫崟鏈虹啍鏂槸鍩轰簬 hystrix-go 瀹炵幇鐨勶紝鍏跺垽瀹氬弬鏁版湁鏈€澶у苟鍙戣姹傛暟銆佽秴鏃舵椂闂淬€侀敊璇巼锛涘叾娆℃槸淇濇姢绐楀彛锛屾槸鐔旀柇鏃堕暱锛岀啍鏂涔呭悗杩涜鏈嶅姟鎭㈠锛涚涓変釜鏄繚鎶ゆ€у姩浣滐紝灏辨槸鍦ㄤ繚鎶ゆ椂闂寸獥鍙d箣鍐呮墽琛屼粈涔堟牱鐨勫姩浣滐紝鍏蜂綋瀹炵幇鐢ㄦ埛鑷畾涔夈€� + +聽 聽 聽 聽聽 聽 聽 聽 + +**浼橀泤閫€鍑�** + +浼橀泤閫€鍑轰篃鏄倱鏄庡悓瀛︾殑澶т綔锛屽彲浠ュ湪缃戠粶涓婃悳鍒扮浉鍏冲崥瀹€€傚疄鐜颁紭闆呴€€鍑虹殑姝ラ鏈夛細 + +1. 鍛婄煡娉ㄥ唽涓績锛屾湇鍔″嵆灏嗗叧闂紝姝ゆ椂绛夊緟骞跺鐞嗚姹傦紱 + +2. 娉ㄥ唽涓績閫氱煡鍒殑瀹㈡埛绔紝鍒殑瀹㈡埛绔仠姝㈠彂閫佹柊璇锋眰锛岀瓑寰呭凡鍙戣姹傜殑鍝嶅簲锛� + +3. 鑺傜偣澶勭悊瀹屾墍鏈夋帴鏀跺埌鐨勮姹傚苟涓旇繑鍥炲搷搴斿悗锛岄噴鏀句綔涓烘湇鍔$鐩稿叧鐨勭粍浠跺拰璧勬簮锛� + +4. 鑺傜偣閲婃斁浣滀负瀹㈡埛绔殑缁勪欢鍜岃祫婧愩€� + + +聽聽 聽 聽 聽聽 聽 聽 聽 + +鎵€浠ユ瘡涓€姝ュ熀鏈笂閮借缁欑▼搴忎竴瀹氱殑鏃堕棿杩涜绛夊緟锛屾墍浠ョ瓑鐨勬椂闂寸獥鍙f槸澶氬皯鍛紵dubbogo 榛樿姣忎釜姝ラ澶ф鑺�2绉掞紝鎬讳綋涓€涓椂闂寸獥鍙f槸10绉掋€� + +聽 聽 聽 聽 聽 聽 聽 + +鍩烘湰涓婂湪鍒殑 RPC 妗嗘灦閲岄潰锛屽彲鑳戒笉澶父瑙佸埌杩欑澶勭悊銆� + +**05** + +**Dubbogo 涓婁簯 +** + +聽聽 + +dubbogo 浣滀负寰湇鍔℃鏋跺浣曢€傞厤 k8s锛屽浣曢儴缃诧紵dubbogo 鏈韩鏄竴涓� RPC 妗嗘灦锛屼絾鏄叾鍙堟湁浜嗘湇鍔℃不鐞嗚兘鍔涳紝杩欓儴鍒嗚兘鍔涗笌 k8s 鐨勯儴鍒嗚兘鍔涙湁浜涢噸鍚堬紝涓嶅彲鑳戒负浜嗛€傞厤 k8s 灏卞交搴曟姏寮冦€傜洰鍓� Dubbo 瀹樻柟涔熸病鏈夊緢濂界殑瑙e喅鏂规渚涙垜浠弬鑰冿紝鎵€浠ヨ繖閲屾垜浠� dubbogo 鍏堢粰鍑轰竴涓畝鍗曠殑甯歌瘑鎬х殑瀹炶返鏂规銆備笅闈㈠厛鍒嗘瀽涓� dubbogo 鐨� interface/service 鍜� k8s service 涓よ€呯洿鎺ョ殑宸埆銆� + + + +聽 聽 聽 + +k8s service 鏄澶氬叿鏈夌浉鍚屾湇鍔¤兘鍔� pod 璧勬簮鐨勮仛鍚堬紝瀹冭嚜宸辩殑璐熻浇鍧囪 绠楁硶浠ュ強鍋ュ悍妫€鏌ョ瓑鍔熻兘銆傝€� Dubbo 閲岄潰鐨� interface/service 浠呬粎鏄湇鍔� provider 闆嗗悎锛屾湇鍔℃不鐞嗚兘鍔涗緷璧� dubbo 鐨� directory銆乺outer 鍜� loadbalace 绛夐澶栫殑鍔熻兘妯″潡銆傚苟涓擠ubbo 鏈嶅姟鍖哄垎 group/version锛岃繕鏈� provider銆乧onsumer 瑙掕壊绛夌瓑銆侱ubbo interface/service 鏃犳硶涓� k8s service 瀵规爣锛孌ubbo interface/service 鍜屽叾鏁翠綋鏈嶅姟娌荤悊鑳藉姏鎵嶈兘瀵规爣鎴� k8s service銆備簩鑰呭樊寮傝繖涔堝ぇ锛屽浣曞皢 dubbo 闆嗘垚鍒� k8s 涓憿锛� + +k8s 鎻愪緵浜� pod/endpoint/service 涓夊眰缁村害鐨勮祫婧愩€傜畝鍗曠殑鍋氭硶锛屽彲浠ラ€氳繃鐩戝惉pod/endpoint/service 涓夊眰缁村害璧勬簮鐨勪簨浠讹紝浣滃嚭鍚堢悊鐨勫鐞嗕互杈惧埌鏈嶅姟娌荤悊鐨勭洰鐨勩€傜洰鍓嶆垜浠ぞ鍖烘垚鍛樼帇缈旀彁浜や簡涓€涓熀浜庣洃鍚� pod 浜嬩欢鏉ュ疄鐜版湇鍔℃不鐞嗙殑 pr锛屼紭鐐瑰氨鏄笉闇€瑕佸紩鍏ラ澶栫粍浠讹紝閫氳繃鐩戝惉 k8s 涓渶缁嗙矑搴﹁祫婧� pod 鐨勪簨浠讹紝閫氳繃 k8s apiserver 鑾峰彇 pod 鍒楄〃锛屽彧鏄€氳繃 apiserver 浣跨敤 etcd 鐨勬湇鍔℃敞鍐屽拰鏈嶅姟閫氱煡鑳藉姏锛屽叾浠栫户缁娇鐢� Dubbo 鐨勬湇鍔℃不鐞嗚兘鍔涖€傚叾浼樼偣鏄ā鍨嬬畝鍗曪紝涓嶉渶瑕佸疄鐜伴澶栫殑妯″潡锛屽嚑涔庝笉闇€瑕佸 Dubbo 浣滃嚭鏀瑰姩锛岀己鐐瑰氨鏄叾瀹炴棤娉曚娇鐢� k8s 鑷繁鐨勫仴搴锋鏌ヨ兘鍔涳紝闇€瑕佽嚜宸辩洃鍚緢缁嗙矑搴︾殑 pod 浜嬩欢鏉ョ患鍚堝鐞嗘湇鍔″仴搴枫€佹湇鍔′笂涓嬬嚎绛夋儏鍐碉紝鑰屼笖杩樺瓨鍦ㄦ病鏈変娇鐢� k8s service 鐨勪簨浠剁洃鍚兘鍔涳紝姣忎釜 consumer 鍐椾綑鐩戝惉涓€浜涗笉蹇呰鐩戝惉鐨勪簨浠讹紝鍔犲ぇ apiserver 鐨勭綉缁滃帇鍔涖€傛墍浠ュ叾瀹炵幇鐩墠鏉ョ湅鍙兘杩樹笉鏄渶浼樿В锛屼笌 k8s 寤鸿鐨刼perator 鏂瑰紡涔熸湁涓€瀹氱殑鑳岀銆傜ぞ鍖虹洰鍓嶈繕鍦ㄨ璁烘柊鏂规锛屽姹� k8s 鏈€浼樿В锛屽ぇ閮ㄥ垎浜哄€惧悜浜庨噰鐢� k8s 绀惧尯鎺ㄨ崘鐨� operator 鏂规锛屼絾鏄叾寮€鍙戝拰绾夸笂缁存姢鎴愭湰灏变笂鍗囦簡銆傚悗闈袱绉嶆柟寮忎細鍏卞瓨锛屼娇鐢ㄨ€呰浠佽鏅恒€� + +**06** + +**浜掕瀺浜掗€�** + +聽聽 + +鍏充簬浜掕瀺浜掗€氾紝Dubbo 鏄庡勾鏈変釜涓変釜閲嶈鐩爣锛屽叾涓竴涓洰鏍囨槸涓庡闈㈢殑寰湇鍔$敓鎬佽繘琛屼簰鑱斾簰閫氾紝姣斿璇磋窡 grpc 浜掗€氥€傜洰鍓� dubbo 鐨� grpc 鐨勮В鍐虫柟妗堝凡缁忓紑鏀惧嚭鏉ワ紝dubbogo 涓� grpc 浜掗€氱殑寮€鍙戝伐浣滀篃鍑犺繎瀹屾垚銆� + +涓嬮潰宸﹁竟 dubbogo 鐨勪唬鐮佺敓鎴愬櫒宸ュ叿鏍规嵁 grpc 鐨� pb 鏈嶅姟瀹氫箟鏂囦欢鑷姩鐢熸垚鐨勯€傞厤 dubbogo 鐨勪唬鐮侊紝鍙宠竟鏄搴旂殑浣跨敤绀轰緥銆備笉鍚屼簬 k8s service 鐨勫鏉傛€э紝grpc 鏁翠綋浠呬粎鍏锋湁 rpc 鑳藉姏锛屾病鏈夋湇鍔℃不鐞嗚兘鍔涳紝鎵€浠ュ師濮嬬殑 grpc 灏卞彲浠ュ緢濂界殑宓屽叆鍒� dubbogo 閲岄潰锛実rpc server 鐨� methodhandler 瀵规垜浠� dubbogo 鏉ヨ灏辨槸 dubbo invoker锛実rpc 鐨勪竴浜涚浉鍏崇殑鎺ュ彛鐩存帴璺熸垜浠殑鎺ュ彛宓屽璧锋潵锛屼袱涓敓鎬佸氨瀵规帴璧锋潵浜嗐€� + +聽聽 聽 聽 + +**07** + +**灞曟湜鏈潵 +** + + + +鏈€鍚庡氨鏄睍鏈涙湭鏉ワ紝涔熷氨鏄槑骞寸殑瑙勫垝銆� + + + +聽 聽 聽 + +鏄庡勾鎴戜滑灏嗕細寰堝揩瀹炵幇 dubbo router銆傜ぞ鍖哄湪 8鏈堜唤宸茬粡瀹炵幇浜� router 鍔熻兘闇€瑕佺殑搴曞眰鐨勭畻娉曟ā鍧楋紝浣嗘槸褰撴椂閰嶇疆涓績涓嬪彂鐩稿叧鐨勫弬鏁扮殑鑳藉姏杩樹笉鏄緢寮猴紝鎵€浠ユ病鏈夋渶缁堝畬鎴愩€傛渶杩戞湇鍔℃不鐞嗛厤缃垰鍒氭敮鎸佷簡 zookeeper 鍜� apollo锛岄璁″緢蹇氨鍙互灏� router 鐨勫弬鏁伴€氳繃閰嶇疆涓績涓嬪彂鐨勫舰寮忔敮鎸佹帀銆傚彟澶栵紝杩樻湁 tracing锛屾垜浠皢浼氬紩鍏ョぞ鍖轰富娴佺殑 tracing 鏂规锛屼互 opentracing 涓烘爣鍑嗭紝鍘婚泦鎴� opentracing 寮€婧愮敓鎬佺殑鐩稿叧鑳藉姏銆傜涓変釜鏄� kubernetes operator锛岃繖涓氨鏄垰鎵嶈鐨� K8s 鐨勬湇鍔¤皟鐢紝鎴戜滑浼氬熀浜� operator 鐨勬柟妗堝仛涓€鐗堟柊鐨勫熀浜� k8s 鐨勬敞鍐屼腑蹇冨疄鐜般€傛渶鍚庡氨鏄簯鍘熺敓鐢熸€佺殑铻嶅叆锛屽嵆涓� istio 鐨勯泦鎴愶紝dubbogo 灏嗕細鎴愪负 dubbo 鍦� service mesh 鐢熸€佷腑鐨勯噸瑕佽鑹层€� + +鐩墠 dubbogo 椤圭洰锛屼粖骞存槸鑳� run 璧锋潵锛岃川閲忔柟闈㈣繕鏈夊緢澶氬伐浣滆鍋氾紝鍔熻兘鍩烘湰涓婂埌鏄庡勾鍙笌 dubbo 2.7 琛ラ綈锛岀洰鍓嶅凡缁忓熀鏈鐢ㄣ€傜洰鍓嶈惤鍦板疄璺电殑鏄� 3 涓瘮杈冨吀鍨嬬殑鍏徃锛屼竴涓槸鎼虹▼锛岃繕鏈変竴涓槸娑傞甫鏅鸿兘銆� + +dubbogo 鏈韩鏄竴涓� go 璇█椤圭洰锛屼篃鏈熷緟涓庡叾浠� go 绀惧尯鐨勬寚姝f垨鑰呴渶姹傦紝涓€璧锋垚闀裤€� \ No newline at end of file diff --git a/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md b/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md new file mode 100644 index 0000000000000000000000000000000000000000..55356f286dcc903635321f18df1ce779d934167e --- /dev/null +++ b/doc/md/config-center/how-to-implement-remote-configuration-management-in-dubbo-go.md @@ -0,0 +1,274 @@ +# [dubbo-go 涓浣曞疄鐜拌繙绋嬮厤缃鐞嗭紵](https://blog.csdn.net/weixin_39860915/article/details/104548947) + +2020-02-27 20:00:00 + +涔嬪墠鍦� Apache/dubbo-go锛堜互涓嬬畝绉� dubbo-go 锛夌ぞ鍖轰腑锛屾湁鍚屽甯屾湜閰嶇疆鏂囦欢涓嶄粎鍙互鏀句簬鏈湴锛岃繕鍙互鏀句簬閰嶇疆绠$悊涓績閲屻€傞偅涔堬紝鏀惧湪鏈湴鍜岄厤缃鐞嗕腑蹇冪┒绔熸湁鍝簺涓嶄竴鏍峰憿锛� + +鏀惧湪鏈湴锛屾瘡娆℃洿鏂伴渶瑕侀噸鍚紝閰嶇疆鏂囦欢绠$悊鍥伴毦锛屾棤娉曞仛鍒板疄鏃舵洿鏂板嵆鍒荤敓鏁堛€傛澶栵紝鏈湴鏂囦欢杩樹緷璧栦汉宸ョ増鏈帶鍒讹紝鍦ㄥ井鏈嶅姟鐨勫満鏅笅锛屽ぇ澶х殑澧炲姞浜嗚繍缁寸殑鎴愭湰涓庨毦搴︺€� + +鑰岄厤缃鐞嗕腑蹇冩彁渚涗簡缁熶竴鐨勯厤缃枃浠剁鐞嗭紝鏀寔鏂囦欢鏇存柊銆佸疄鏃跺悓姝ャ€佺粺涓€鐗堟湰鎺у埗銆佹潈闄愮鐞嗙瓑鍔熻兘銆� + +# **鐩爣** + +* * * + +鍩轰簬浠ヤ笂鍑犱釜鑳屾櫙锛屽彲浠ユ€荤粨鍑轰互涓�**鐩爣** + +* 涓� Dubbo 鐜版湁鐨勯厤缃腑蹇冨唴鐨勯厤缃枃浠跺吋瀹癸紝闄嶄綆鏂板璇█鏍堢殑瀛︿範鎴愭湰锛� + +* 鏀寔澶氱閰嶇疆鏂囦欢鏍煎紡锛� + +* 鏀寔涓绘祦閰嶇疆涓績锛岄€傚簲涓嶄竴鏍风殑浣跨敤鍦烘櫙锛屽疄鐜伴珮鎵╁睍鐨勯厤缃笅鍙戯紱 + + +# **閰嶇疆涓績** + +* * * + +閰嶇疆涓績鍦� dubbo-go 涓富瑕佹壙鎷呬互涓嬪満鏅殑鑱岃矗锛� + +1銆佷綔涓哄閮ㄥ寲閰嶇疆涓績锛屽嵆瀛樺偍 dubbo.properties 閰嶇疆鏂囦欢锛屾鏃讹紝key 鍊奸€氬父涓烘枃浠跺悕濡� dubbo.properties , value 鍒欎负閰嶇疆鏂囦欢鍐呭銆� + +2銆佸瓨鍌ㄥ崟涓厤缃」锛屽鍚勭寮€鍏抽」銆佸父閲忓€肩瓑銆� + +3銆佸瓨鍌ㄦ湇鍔℃不鐞嗚鍒欙紝姝ゆ椂 key 閫氬父鎸夌収 鈥滄湇鍔″悕 + 瑙勫垯绫诲瀷鈥� 鐨勬牸寮忔潵缁勭粐锛岃€� value 鍒欎负鍏蜂綋鐨勬不鐞嗚鍒欍€� + +灏辩洰鍓嶈€岃█锛宒ubbo-go 棣栬鏀寔鐨勬槸 Dubbo 涓敮鎸佺殑寮€婧愰厤缃腑蹇冿紝鍖呮嫭锛� + +1銆丄pollo 锛氭惡绋嬫鏋堕儴闂ㄧ爺鍙戠殑鍒嗗竷寮忛厤缃腑蹇冿紝鑳藉闆嗕腑鍖栫鐞嗗簲鐢ㄤ笉鍚岀幆澧冦€佷笉鍚岄泦缇ょ殑閰嶇疆锛岄厤缃慨鏀瑰悗鑳藉瀹炴椂鎺ㄩ€佸埌搴旂敤绔紝骞朵笖鍏峰瑙勮寖鐨勬潈闄愩€佹祦绋嬫不鐞嗙瓑鐗规€э紝閫傜敤浜庡井鏈嶅姟閰嶇疆绠$悊鍦烘櫙銆� + +2銆乑ooKeeper 锛氫竴涓垎甯冨紡鐨勶紝寮€鏀炬簮鐮佺殑鍒嗗竷寮忓簲鐢ㄧ▼搴忓崗璋冩湇鍔★紝鏄� Google 鐨� Chubby 涓€涓紑婧愮殑瀹炵幇锛屾槸 Hadoop 鍜� Hbase 鐨勯噸瑕佺粍浠躲€傚畠鏄竴涓负鍒嗗竷寮忓簲鐢ㄦ彁渚涗竴鑷存€ф湇鍔$殑杞欢锛屾彁渚涚殑鍔熻兘鍖呮嫭锛氶厤缃淮鎶ゃ€佸煙鍚嶆湇鍔°€佸垎甯冨紡鍚屾銆佺粍鏈嶅姟绛夈€� + +3銆丯acos : Alibaba 寮€婧愮殑閰嶇疆绠$悊缁勪欢锛屾彁渚涗簡涓€缁勭畝鍗曟槗鐢ㄧ殑鐗规€ч泦锛屽府鍔╂偍瀹炵幇鍔ㄦ€佹湇鍔″彂鐜般€佹湇鍔¢厤缃鐞嗐€佹湇鍔″強娴侀噺绠$悊銆� + +鑰岃€冭檻鍒版煇浜涘叕鍙稿唴閮ㄦ湁鑷韩鐨勭爺鍙戠殑閰嶇疆涓績锛屽張鎴栬€呭綋鍓嶆祦琛岃€� Dubbo 灏氭湭鏀寔鐨勯厤缃腑蹇冿紝濡� etcd锛屾垜浠殑鏍稿績鍦ㄤ簬璁捐涓€濂楁満鍒讹紝鍏佽鎴戜滑锛屼篃鍖呮嫭鐢ㄦ埛锛屽彲浠ラ€氳繃鎵╁睍鎺ュ彛鏂扮殑瀹炵幇锛屾潵蹇€熸帴鍏ヤ笉鍚岀殑閰嶇疆涓績銆� + +閭e湪 dubbo-go 涓┒绔熸€庝箞瀹炵幇鍛紵鎴戜滑鐨勭瓟妗堟槸锛�**鍩轰簬鍔ㄦ€佺殑鎻掍欢鏈哄埗鍦ㄥ惎鍔ㄦ椂鎸夐渶鍔犺浇閰嶇疆涓績鐨勪笉鍚屽疄鐜般€�** + +瀹炵幇璇ラ儴鍒嗗姛鑳芥斁缃簬涓€涓嫭绔嬬殑瀛愰」鐩腑锛岃锛� + +_https://github.com/apache/dubbo-go/tree/master/config\_center_ + +## **dubbo-go 璁捐** + +鍘熼€昏緫涓猴細鍚姩鏃惰鍙栨湰鍦伴厤缃枃浠讹紝灏嗗叾鍔犺浇杩涘唴瀛橈紝閫氳繃閰嶇疆鏂囦欢涓殑閰嶇疆璇诲彇娉ㄥ唽涓績鐨勪俊鎭幏鍙栨湇鍔℃彁渚涜€咃紝娉ㄥ唽鏈嶅姟娑堣垂鑰呫€� + +鏈変簺璇昏€呬細鏈夌偣鍥版儜锛屼笉鏄濂戒簡浣跨敤閰嶇疆涓績鐨勶紝涓轰粈涔堢幇鍦ㄥ張瑕佽鍙栨湰鍦伴厤缃憿锛熺瓟妗堝氨鏄紝璇诲彇鐨勮繖閮ㄥ垎淇℃伅鍒嗘垚涓ら儴鍒嗭細 + +* 浣跨敤浠€涔堜綔涓洪厤缃腑蹇冿紱 + +* 璇ラ厤缃腑蹇冪殑鍏冩暟鎹紝姣斿璇翠娇鐢� zookeeper 浣滀负閰嶇疆涓績锛岄偅涔� zookeeper 鐨勯摼鎺ヤ俊鎭氨鏄厓鏁版嵁锛屾瘯绔熸垜浠彧鏈夊湪鐭ラ亾浜嗛摼鎺ヤ俊鎭箣鍚庢墠鑳借繛涓� zookeeper锛� + + +鍦ㄦ敼閫犵殑鏃跺€欙紝闇€瑕佽€冭檻浠ヤ笅鐨勯棶棰橈細 + +**1銆佸浣曞疄鐜版敮鎸佸涓厤缃腑蹇冿紵濡備綍瀹炵幇鎸夐渶鍔犺浇锛�** + +閫氳繃鎶借薄 DynamicConfiguration 璁╁紑鍙戣€呭彲浠ュ揩閫熸敮鎸佸涓厤缃腑蹇冦€備娇鐢ㄨ€呭鍏ユ寚瀹氱殑缁勪欢鍖呭悗锛屽湪鍚姩闃舵灏嗛渶瑕佺殑缁勪欢鍔犺浇杩涘唴瀛樹腑锛屼互渚跨粰绋嬪簭鎸夐渶璋冪敤锛屽涓嬪浘缁胯壊閮ㄥ垎銆� + +**2銆侀厤缃腑蹇冪殑閰嶇疆鍔犺浇闃舵鍦ㄤ粈涔堟椂鍊欙紵** + +搴斿湪璇诲彇閰嶇疆鏂囦欢闃舵鍚庯紝璇诲彇骞惰В鏋愭湰鍦伴厤缃枃浠朵腑閰嶇疆涓績淇℃伅銆傚垵濮嬪寲閰嶇疆涓績閾炬帴锛岃鍙柭�/dubbo/config/dubbo/dubbo.properties 涓� /dubbo/config/dubbo/搴旂敤鍚�/dubbo.properties 锛屽苟灏嗗叾鍔犺浇鍒板唴瀛樹箣涓鐩栧師鏈夐厤缃紝鐩戝惉鍏跺彉鏇达紝瀹炴椂鏇存柊鑷冲唴瀛橈紝濡備笅鍥捐摑鑹查儴鍒�: + + + +### **ConfigCenterFactory** + +浣跨敤鑰呭姞杞藉搴旈厤缃腑蹇冩ā鍧楀悗锛屽湪鍒濆鍖栭樁娈靛姞鍏ュ悇閰嶇疆涓績妯″潡寰€鍏朵腑娉ㄥ唽鍏跺垵濮嬪寲绫汇€� + + + +### **DynamicConfigurationFactory** + +鏁翠釜鍔ㄦ€侀厤缃腑蹇冪殑鍏抽敭鐐瑰氨鍦� DynamicConfigurationFactory 涓婏紝鍏朵腑閫氳繃瑙f瀽鍐呴儴鑷畾涔夌殑 URL 锛岃幏鍙栧叾鍗忚绫诲瀷锛屽弽灏勫叾鍙傛暟锛岀敤浜庡垱寤洪厤缃腑蹇冪殑閾炬帴銆� + + + +濡傦細 + +閰嶇疆鏂囦欢涓厤缃細 + +```css +config_center: + protocol: zookeeper + address: 127.0.0.1:2181 + namespace: test +``` + +dubbo-go 鍐呴儴浼氳В鏋愪负锛� + +```javascript +zookeeper://127.0.0.1:2181?namespace=test +``` + +鍦ㄥ唴閮ㄤ紶閫掞紝鐢ㄤ簬鍒濆鍖栭厤缃腑蹇冮摼鎺ャ€� + +**PS锛�**鍦� dubbo-go 涓埌澶勫彲瑙佽繖绉嶅唴閮ㄥ崗璁紝閫忓交鐞嗚В杩欎釜鍐呴儴鍗忚瀵归槄璇� dubbo-go 浠g爜寰堟湁甯姪銆� + +### **DynamicConfiguration** + +璇ユ帴鍙h瀹氫簡鍚勪釜閰嶇疆涓績闇€瑕佸疄鐜扮殑鍔熻兘锛� + +* 閰嶇疆鏁版嵁鍙嶅簭鍒楀寲鏂瑰紡锛氱洰鍓嶅彧鏈� Properties 杞崲鍣紝鍙傝锛欴efaultConfigurationParser 銆� + +* 澧炲姞鐩戝惉鍣細鐢ㄤ簬澧炲姞鐩戝惉鏁版嵁鍙樺寲鍚庡鍔犵壒瀹氶€昏緫锛堝彈闄愪簬閰嶇疆涓績 client 绔疄鐜帮級銆� + +* 鍒犻櫎鐩戝惉鍣細鍒犻櫎宸叉湁鐩戝惉鍣紙鍙楅檺浜庨厤缃腑蹇� client 绔疄鐜帮紝鐩墠鎵€鐭� nacos client 娌℃湁鎻愪緵璇ユ柟娉曪級銆� + +* 鑾峰彇璺敱閰嶇疆锛氳幏鍙栬矾鐢辫〃閰嶇疆銆� + +* 鑾峰彇搴旂敤绾ч厤缃細鑾峰彇搴旂敤灞傜骇閰嶇疆锛屽锛氬崗璁被鍨嬮厤缃瓑銆� + + + + +## **瀹炵幇** + +* * * + + + +浼樺厛鑰冭檻涓庣幇鏈� Dubbo 璁捐鍏煎锛屼粠鑰岄檷浣庝娇鐢ㄨ€呯殑瀛︿範鎴愭湰锛宒ubbo-admin 浣滀负鏈嶅姟鎻愪緵鑰呭疄鐜板簲鐢ㄧ骇閰嶇疆绠$悊锛� dubbo-go 浣滀负娑堣垂绔疄鐜伴厤缃笅鍙戠鐞嗗姛鑳姐€備笅闈互 ZooKeeper 涓轰緥锛屽鏈嶅姟鎻愪緵鑰呬笌鏈嶅姟娑堣垂鑰呰繘琛屾暣浣撴祦绋嬪垎鏋愩€� + +### **濡備綍瀛樺偍閰嶇疆绠$悊** + +dubbo-admin 閰嶇疆绠$悊涓鍔� global 閰嶇疆锛孼ooKeeper 涓細鑷姩鐢熸垚鍏跺搴旈厤缃妭鐐癸紝鍐呭鍧囦负 dubbo-admin 涓缃殑閰嶇疆銆� + +1銆�/dubbo/config/dubbo/dubbo.properties 瀵瑰簲鍏ㄥ眬閰嶇疆鏂囦欢銆� + +2銆�/dubbo/config/dubbo/ 搴旂敤鍚� /dubbo.properties 瀵瑰簲鎸囧畾搴旂敤閰嶇疆鏂囦欢銆� + +#### **鑺傜偣璺緞** + +####  + +涓婂浘灞曠ず浜� dubbo.properties 鏂囦欢鍦� ZooKeeper 鍜� Apollo 涓殑瀛樺偍缁撴瀯锛� + +**ZooKeeper** + +* 鍛藉悕绌洪棿 namespace 閮戒负锛欴ubbo + +* 鍒嗙粍 group 锛氬叏灞€绾у埆涓� dubbo , 鎵€鏈夊簲鐢ㄥ叡浜紱搴旂敤绾у埆涓哄簲鐢ㄥ悕 demo-provider 锛屽彧瀵硅搴旂敤鐢熸晥 + +* key : dubbo.properties + + +**Apollo** + +* app\_id : 鑷敱鎸囧畾锛岄粯璁わ細dubbo 锛屾渶濂戒笌 zookeeper 聽namespace 涓€鑷� + +* cluster : 鑷敱鎸囧畾锛屾渶濂戒笌 zookeeper group 涓€鑷� + +* 鍛藉悕绌洪棿 namespace : dubbo.properties + + +ZooKeeper 涓� Apollo 鏈€澶х殑涓嶄竴鏍峰氨鍦ㄤ簬 dubbo.properties 鎵€鍦ㄧ殑鑺傜偣銆� + +### **瀹炵幇閰嶇疆绠$悊涓績鏀寔** + +浠� Apollo 涓轰緥锛岀畝鍗曠殑浠嬬粛锛屽浣曞疄鐜版敮鎸佷竴涓柊鐨勯厤缃鐞嗕腑蹇冦€� + +#### **閫夋嫨閰嶇疆绠$悊涓績 Client / SDK** + +鏈緥涓娇鐢ㄧ殑 Apollo Go Client 涓猴細https://github.com/zouyx/agollo 銆� + +**PS:** 濡傛病鎵惧埌锛岃嚜宸卞疄鐜颁篃鏄彲浠ョ殑鍝︺€� + +#### **鑺傜偣璺緞** + +鍥犱负姣忎釜閰嶇疆绠$悊涓績鐨勫瓨鍌ㄧ粨鏋勫悇鏈夌壒鐐癸紝瀵艰嚧 Dubbo 鍦ㄤ娇鐢ㄥ閮ㄩ厤缃鐞嗕腑蹇冩椂锛屽瓨鍌ㄩ厤缃妭鐐圭殑缁撴瀯涓嶄竴鏍枫€傚湪 dubbo-configcenter 鎵惧埌甯屾湜鏀寔鐨勯厤缃鐞嗕腑蹇冿紝鑰屾湰渚嬩腑 Apollo 鍒欏湪 ApolloDynamicConfiguration.java 銆� + +娉ㄩ噴涓〃鏄庯紝Apollo namespace = governance (governance .properties) 鐢ㄤ簬娌荤悊瑙勫垯锛宯amespace = dubbo (dubbo.properties) 鐢ㄤ簬閰嶇疆鏂囦欢銆� + +#### **瀹炵幇聽DynamicConfiguration** + +鏂板缓鍒涘缓瀹㈡埛绔柟娉曪紝鏈€濂藉鎴风淇濇寔涓哄崟渚嬨€� + + + +浠ヤ笅涓哄繀椤诲疄鐜扮殑鏂规硶锛屼互涓嬫柟娉曠敤浜庤幏鍙栭厤缃腑蹇冮厤缃€� + +* GetInternalProperty锛氬湪閰嶇疆鏂囦欢锛圓pollo 涓� namespace锛変腑锛屾牴鎹� key 鑾峰彇瀵瑰簲 value锛� + +* GetRule锛氳幏鍙栨不鐞嗛厤缃枃浠讹紙Apollo 涓� namespace锛夛紱 + +* GetProperties锛氳幏鍙栨暣涓厤缃枃浠讹紙Apollo 涓� namespace锛夛紱 + + +鍙€夋嫨瀹炵幇鐨勬柟娉曪紝濡備笉瀹炵幇锛屽垯涓嶈兘鍔ㄦ€佹洿鏂� dubbo-go 涓厤缃俊鎭€� + +* RemoveListener + +* AddListener + + +鑰� Parser & SetParser 浣跨敤榛樿瀹炵幇鍗冲彲锛岄粯璁や负 Properties 杞崲鍣ㄣ€� + +鏇村淇℃伅锛屽弬鑰冿細dubbo-go-apollo 锛岃鎯呭弬鑰冿細 + +_https://github.com/apache/dubbo-go/tree/master/config\_center/apollo_ + +## **浣跨敤鏂规硶** + +浠庝笂闈㈢殑璁捐閲岄潰锛屼篃鑳藉ぇ姒傜寽鍒版€庝箞浣跨敤浜嗭細 + + + +寰堟樉鐒讹紝浣跨敤閰嶇疆涓績骞朵笉澶嶆潅锛屽彧闇€瑕佹妸瀵瑰簲鐨勪緷璧栧紩鍏ヨ繘鏉ャ€傚湪鍖呭垵濮嬪寲鐨勬椂鍊欙紝浼氬垱寤哄嚭鏉ュ搴旂殑閰嶇疆涓績鐨勫疄鐜般€傛瘮濡傝鍔犺浇 ZooKeeper 鎴栬€� Apollo 浣滀负閰嶇疆涓績锛� + +**ZooKeeper** + +```nginx +_ "github.com/apache/dubbo-go/config_center/zookeeper" +``` + +**Apollo** + +```nginx +_ "github.com/apache/dubbo-go/config_center/apollo" +``` + +褰撶劧浠呬粎鍔犺浇杩樹笉澶燂紝姣斿璇磋櫧鐒舵垜鍔犺浇浜� zookeeper锛屼絾鏄垜杩橀渶瑕佺煡閬撴€庝箞杩炰笂杩欎釜閰嶇疆涓績锛屽嵆鍓嶉潰鎻愬埌鐨勯厤缃腑蹇冪殑鍏冩暟鎹紝杩欓儴鍒嗕俊鎭槸闇€瑕佸湪鏈湴閰嶇疆鍑烘潵鐨勩€傛瘮濡傝锛� + +**ZooKeeper** + +```css +config_center: + protocol: "zookeeper" + address: "127.0.0.1:2181" +``` + +**Apollo** + +濡傛灉闇€瑕佷娇鐢� Apollo 浣滀负閰嶇疆涓績锛岃鎻愬墠鍒涘缓 namespace: dubbo.properties锛岀敤浜庨厤缃鐞嗐€� + +```properties +config_center: + protocol: "apollo" + address: "127.0.0.1:8070" + app_id: test_app + cluster: dev +``` + +# **鎬荤粨** + +* * * + +鏇村姞鍏蜂綋鐨勫疄鐜帮紝鎴戝氨涓嶈缁嗚杩帮紝澶у鍙互鍘荤湅婧愮爜锛屾杩庡ぇ瀹舵寔缁叧娉紝鎴栬€呰础鐚唬鐮併€� + +鏁翠釜閰嶇疆涓績鐨勫姛鑳斤紝楹婚泙铏藉皬锛屼絾浜旇剰淇卞叏銆傜洰鍓嶅苟涓嶇畻鏄崄鍒嗗畬鍠勶紝浣嗘槸鏁翠釜妗嗘灦灞傞潰涓婃潵璇达紝鏄蛋鍦ㄤ簡姝g‘鐨勮矾涓娿€備粠鎵╁睍鎬ф潵璇达紝鏄瘮杈冧究鍒┿€傜洰鍓嶆敮鎸佺殑閰嶇疆涓績杩樹笉澶熶赴瀵岋紝鍙湁 ZooKeeper 涓� Apollo 锛屾敮鎸佺殑閰嶇疆鏂囦欢鏍煎紡涔熷彧鏈� properties 锛岃櫧鐒惰兘婊¤冻鍩烘湰浣跨敤鍦烘櫙锛岃窛绂诲畬鍠勮繕鏈夎繕闀胯繙鐨勮矾銆� + +**鏈潵璁″垝锛�** + +* Nacos锛堢瓑寰呭彂甯� 锛� + +* etcd锛堟鍦ㄥ紑鍙戯級 + +* consul锛堟湭鏀寔锛� + +* 涓板瘜鐨勬枃浠堕厤缃牸寮忥紝濡傦細yml , xml 绛� + + +**鏈枃浣滆€咃細**閭规瘏璐わ紝Github ID @zouyx锛屽紑婧愮埍濂借€咃紝灏辫亴浜� SheIn 渚涘簲閾鹃儴闂紝璐熻矗渚涘簲閾惧紑鏀惧钩鍙般€� + +鏈枃缂╃暐鍥撅細icon by 鐢ㄦ埛7388492991 \ No newline at end of file diff --git a/doc/md/course/the-5th-years-of-dubbo-go.md b/doc/md/course/the-5th-years-of-dubbo-go.md new file mode 100644 index 0000000000000000000000000000000000000000..288c8ab0fba01f2101b737dd8841680367512d6f --- /dev/null +++ b/doc/md/course/the-5th-years-of-dubbo-go.md @@ -0,0 +1,165 @@ +# [鍐欏湪 Dubbo go 鐨勭浜斿勾](https://my.oschina.net/u/3874284/blog/4577120) + +09/16 14:33 + + + +浣滆€� |聽浜庨洦 + +**闃块噷宸村反浜戝師鐢熷叕浼楀彿鍚庡彴鍥炲鈥�915鈥濆嵆鍙煡鐪� dubbogo聽v1.5.1聽椤圭洰绠$悊鍥炬竻鏅板ぇ鍥撅紒** + +# 寮曡 + +dubbogo 椤圭洰宸茶繘鍏ョ浜斾釜骞村ご銆� + +椤圭洰鍙戝睍鐨勫墠涓ゅ勾锛屾垜浠妸 hessian2 鍗忚搴撱€佺綉缁滃簱鍜屾暣浣撳熀纭€妗嗘灦鎼缓涓€鐣€備粠 2018 骞撮」鐩 Dubbo 瀹樻柟鎺ョ撼寮€濮嬶紝渚濇墭闃块噷骞冲彴锛岀ぞ鍖哄紑濮嬪舰鎴愬苟蹇€熷彂灞曘€備笌绀惧尯鍚屽浠綈蹇冨悎鍔涗箣涓嬶紝濡備粖鍏ㄩ潰鍏煎 Dubbo v2.7.x 鐨� Dubbo-go v1.5.1 宸茬粡鍙戝竷銆� + +# 绔嬮」 + +涓€涓」鐩暣浣撳繀椤绘彁鐐煎嚭鏍稿績鐩爣锛屾寚鏄庡叾瀛樺湪鐨勬剰涔夊拰浠峰€笺€傛湁浜嗗垵蹇冿紝椤圭洰鍙戝睍杩囩▼涓骇鐢熷洶鎯戞椂锛屾墠鑳芥槑纭瓟澶� 鈥滄垜鏄皝锛熶粠鍝噷鏉ワ紵鍒板摢閲屽幓鈥濄€� + +## 1\. dubbogo + +dubbogo 椤圭洰鏈夊叾鑷韩鐨� milestone 瑕佹眰锛屽ぇ鑷磋鍒掍簡姣忎釜闃舵鐨勫叧閿噷绋嬬锛屽湪椤圭洰鍙戝睍鍒濇湡浠呬粎鏄疄鐜� Dubbo 鐨勬煇涓姛鑳斤紝浣嗗湪鍙戝睍杩囩▼涓細涓嶆柇缁撳悎褰撲笅鐨勬妧鏈彂灞曟疆娴侊紝涓嶆柇淇鍏舵湭鏉ュ彂灞曟柟鍚戙€� + +鍏跺彂鐗堣鍒掓槸閫氳繃鈥滃紑鍙戝綋鍓嶇増鏈€佽鍒掓柊鐗堟湰銆佹牴鎹弽棣堜慨姝f柊鐗堟湰鈥濈殑妯″紡瀹氫箟褰撳墠鐗堟湰鐨勫紑鍙戝唴瀹瑰拰涓嬩竴涓増鏈殑鍙戝睍鏂瑰悜銆傛瘡娆″彂鐗堝悗浼氭牴鎹ぞ鍖轰娇鐢ㄥ弽棣堝涓嬩竴浠g殑鍙戝睍鐩爣杩涜淇銆� + +绔欏湪鍚冪摐浜虹殑瑙掑害锛屾垨璁稿彲浠ヨ鍑� 鈥渄ubbogo 涓嶅氨鏄� dubbo 鐨� Go 璇█鐗堟湰鍢涳紝鐓х潃鎶勫氨鏄簡鈥� 涔嬬被鐨勮璋冦€傝€屽弬涓庤繃 dubbogo 椤圭洰璺熺潃绀惧尯涓€璺蛋鏉ョ殑浜猴紝灏辩煡閬� dubbogo 骞朵笉绠€鍗曞畾浣嶄簬 Dubbo 椤圭洰鐨� Go 璇█鐗堟湰銆� + +dubbogo 鍒濆績涓嶅彉锛屼笉鍚屾椂闂村鑷韩瀹氫綅鍧囨湁鍗囩骇銆傛垜璁や负褰撳墠 dubbogo 鐨勫畾浣嶆槸锛� + +* 鍏ㄩ潰鍏煎 Dubbo锛� +* 涓€涓� Go 璇█搴旂敤閫氫俊妗嗘灦锛屽厖鍒嗗埄鐢ㄤ綔涓轰簯鍘熺敓鏃朵唬绗竴璇█---Go 璇█鐨勪紭鍔匡紝鎵╁睍 dubbo 鐨勮兘鍔涖€� + +## 2\. dubbo-go-proxy + +dubbogo 椤圭洰鍒濇湡鐩殑鏄緷闈� Dubbo 瀹炵幇 "bridge the gap between Java and Go" 锛岀洰鍓� dubbogo 姝d笌 Dubbo 榻愬ご骞惰繘锛屽凡缁忚揪鍒伴」鐩珛椤圭殑鐩爣銆傛湁闀挎湡鐢熷懡鐨勯€氫俊妗嗘灦锛屽ぇ姒傛湁 5 骞寸殑鎴愰暱鏈熷拰 5 骞寸殑绋冲畾鎴愮啛鏈熴€傜洰鍓嶇殑 dubbogo 澶勫湪鎴愰暱鏈熷拰绋冲畾鎴愮啛鏈熺殑杩囨浮鏈燂紝杩欐剰鍛崇潃绀惧尯濡傛灉鎯充繚鎸佸彂灞曟€佸娍锛屽氨蹇呴』寮€濮嬭蛋澶氬厓鍖栭亾璺紝鍙戝睍鑷繁鐨勭敓鎬佷簡銆� + +鐪间笅 dubbogo 绀惧尯姝e湪闆嗕腑绮惧姏瀛靛寲涓€涓柊鐨勯」鐩�---瀹炵幇涓€涓熀浜� dubbogo 鐨� [HTTP 缃戝叧](https://github.com/dubbogo/dubbo-go-proxy)锛岄」鐩殑鎰忎箟鏄細dubbogo 鑷韩鏄竴涓祦閲忔帶鍒朵腑闂翠欢锛屽湪鍏朵笂鎵╁睍椤圭洰锛屽叾鏂瑰悜寰堣嚜鐒跺氨鏄仛涓€涓� proxy/sidecar or gateway锛屼笖绀惧尯涓€鐩存湁缃戝叧杩欐柟闈㈢殑闇€姹傘€� + +椤圭洰鐩殑濡備笅锛� + +* 鍋氫竴涓叿鏈夌敓浜т娇鐢ㄦ剰涔夌殑缃戝叧锛� +* dubbo-go-proxy 楠岃瘉 dubbogo 鐨勮兘鍔涳紝瀵� dubbogo 鏈潵鐨勮繘鍖栨寚鍑烘柊鏂瑰悜锛屽叡鍚岃繘鍖栵紱 +* 浼樺寲 dubbogo 鐨勭ǔ瀹氭€у拰鎬ц兘銆� + +# 鍥㈤槦 + +椤圭洰绔嬮」瀹屾瘯鍚庯紝灏辫繘鍏ユ嫑鍏典拱椹樁娈典簡銆� + +## 1\. 鏉ユ簮 + +dubbogo 绀惧尯鍙戝睍鍒濇湡锛屽叾鍏抽敭鎴愬憳閮芥槸閫氳繃鎻愪氦 issue 鎴栬€� pr 鐨勫悓瀛︽挬鏉ョ殑銆傞€氳繃杩欑鏂瑰紡鎾╂潵鐨勫悓瀛﹀洜涓哄織鍚岄亾鍚堬紝鏈夋瀬楂樼殑姒傜巼鍚岀ぞ鍖轰竴璧疯蛋涓嬫潵銆俤ubbogo 绀惧尯鐨� core member 灏辨槸杩欐牱鏉ョ殑銆� + +鍏舵鏄笌鍏朵粬鍏徃鐨勫悎浣溿€俤ubbogo 鏈韩鏄竴涓湁鐫€鏋侀珮鐢熶骇鐜闇€姹傜殑椤圭洰锛屽湪鍙戝睍杩囩▼涓緷娆′笌鎼虹▼銆佹秱楦︺€佹枟楸笺€佽檸鐗欍€佽殏铓侀噾鏈嶅拰闃块噷闆嗗洟鏈夎繃鏋佹繁鐨勫悎浣滐紝鍏堕棿涓庢惡绋嬬殑鍚堜綔瀵� dubbogo 鎴愬瀷鑰岃█鏋佷负鍏抽敭銆� + +鍙︿竴涓€斿緞鏄笌鍏朵粬绀惧尯鍚堜綔銆俤ubbogo 椤圭洰鍙戝睍杩囩▼涓紝涓庝互涓嬬ぞ鍖哄悎浣滆繃锛� + +* 涓� MOSN 绀惧尯鍚堜綔瀹炵幇 Dubbo Mesh锛� +* 涓� sentinel 绀惧尯鍚堜綔锛屽湪 Dubbo/Dubbo-go 瀹屾暣鎺ュ叆 sentinel 鐨勯檷绾у拰闄愭祦鏂规锛� +* 涓� Apollo 绀惧尯鍚堜綔锛屽湪 Dubbo-go 涓疄鐜拌繙绋嬮厤缃笅鍙戯紱 +* 涓� Nacos 绀惧尯鍚堜綔锛屽疄鐜板熀浜� Nacos 鐨勬湇鍔″彂鐜般€� + +涓庡叾浠栫ぞ鍖哄悎浣滅殑濂藉鏄娇寰楀弻鏂圭殑椤圭洰閮藉彈鐩婏細鎵╁睍鍙屾柟鐨勮兘鍔涘拰浣跨敤鍦烘櫙锛屽叾娆℃槸绀惧尯闂翠汉鍛樼殑娴佸姩銆傚湪鍚堜綔杩囩▼涓紝dubbogo 鑷韩鍙楃泭鏋佸ぇ锛岀洰鍓嶆湁 4 涓ぞ鍖� committer 鏉ヨ嚜浜庡叾瀹冪ぞ鍖恒€傚悎浣滃畬鎴愬悗骞朵笉鎰忓懗鐫€缁撴潫锛岃€屾槸涓€涓柊鐨勫弻璧㈢殑寮€濮嬶細绀惧尯椤圭洰涔熸槸鍙戝睍鐨勶紝褰撲竴涓」鐩湁鏂扮壒鎬ф椂鍙互鍚屾椂蹇€熻鍙︿竴涓」鐩噰鐢ㄩ獙璇侊紝瀵规墿灞曞紑鍙戣€呬滑鐨勬妧鏈兘鍔涘拰浜鸿剦涔熸槸鏋佷负鏈夊埄鐨勶紝dubbogo 绀惧尯鐩墠鐨勫ソ鍑犱釜鍚屽鍚屾椂娲昏穬鍦ㄥ涓ぞ鍖恒€� + +dubbogo 椤圭洰宸茬粡杩囦簡鑽夎幗闃舵锛屽舰鎴愪簡涓€涓殑 800 澶氫汉鐨勭ぞ鍖虹兢锛屾墍浠� dubbogo-proxy 椤圭洰绔嬮」鍚庯紝寰堝揩灏卞湪绀惧尯缇ゅ唴鎵惧埌寰堝椤圭洰鐖卞ソ鑰呫€� + +## 2\. 鎴愬憳鐨� qualification + +椤圭洰鍙戝睍鍒濇湡鏈夊緢澶氬悓瀛︿細 Java 涓嶆噦 Dubbo 涓嶄細 Go锛屾渶鍚庨兘閫氳繃鍙備笌椤圭洰鎻愬崌浜嗚嚜鎴戠殑鑳藉姏銆傚綋鐒舵湁浜涗汉浼氭媴蹇冮」鐩唬鐮佺殑璐ㄩ噺锛屼絾鍙绉夋寔 "Community Over Code" 杩欎釜 "Apache Way"锛屽湪鍙戝睍杩囩▼涓繖浜涢棶棰橀兘涓嶅ぇ銆� + +2019 骞存椂锛屽弬涓� dubbogo 椤圭洰鐨勬垚鍛樹腑涓€閮ㄥ垎鍚屽骞虫椂鐨勫伐浣滄槸杩涜涓氬姟寮€鍙戯紝绉夋壙鐫€瀵逛腑闂翠欢閫氫俊鎶€鏈� 鈥滄垜鏄皝锛熸垜浠庡摢閲屾潵锛熻鍒伴偅閲屽幓鈥� 鐨勫垵蹇冨弬涓� dubbogo 鐨勫紑鍙戯紝鏃犺鏄 dubbogo 鎶戞垨鏄鍏惰嚜韬妧鏈按骞虫彁鍗囬兘浜х敓浜嗙Н鏋佺殑褰卞搷銆� + +dubbogo 绀惧尯瀵� dubbogo 鍙戠増鏃堕棿鏈変竴瀹氭湡闄愯姹傦紝鎵€浠ュ鍙備笌浜哄憳鐨勬椂闂存姇鍏ヤ篃鏈変竴瀹氱殑瑕佹眰銆傛瘡涓増鏈殑鏍稿績鍔熻兘鐨� owner锛岄渶瑕佷繚璇佸湪 deadline 鏈熼檺鍐呭畬鎴愬紑鍙戜换鍔°€� + +dubbogo 姣忎釜鐗堟湰閮芥湁涓€涓彂鐗堜汉锛岃礋璐g浉搴旂増鏈殑浠诲姟鎷嗗垎銆佸彂灞曡窡韪€佷唬鐮� Review 鍜屾渶鍚庣殑娴嬭瘯楠屾敹锛岃繖灏辫姹傚彂鐗堜汉鑷韩鐨勬妧鏈按骞冲拰鏃堕棿鎶曞叆鏋侀珮銆傜洰鍓� dubbogo 姣忎釜澶х増鏈殑鍙戠増浜洪兘涓嶆槸鍚屼竴涓汉锛屾瘡娆� dubbogo 鍙戠増锛岄兘鎰忓懗鐫€姣忎釜鍙戠増浜虹殑浣撳姏鍜岀簿鍔涚殑鏋佸ぇ浠樺嚭銆備簬鏌愬湪姝よ嚧鏁巻灞婂彂鐗堜汉锛� + +# 绠$悊 + +椤圭洰绔嬮」鍚庯紝灏遍渶瑕佹槑纭彂灞曞ぇ鏂瑰悜銆佸彂灞� milestone銆佺増鏈鍒掋€佷互鍙婁竴娈垫椂闂村唴鐨勫叿浣撶殑寮€鍙戣鍒掋€傞」鐩彂灞曞垵鏈燂紝Roadmap 鍙互涓嶆竻鏅帮紝鍏堟懜鐫€鐭冲ご杩囨渤锛屽湪鍙戝睍杩囩▼涓€愭鏄庣‘鍏跺唴瀹广€� + +## 1\. 闇€姹傛敹闆� + +dubbogo 椤圭洰鍙戝睍鍒濇湡锛屽叾鐩爣浠呬粎鏄疄鐜� dubbo 鏌愪釜鐗堟湰鐨勫姛鑳斤紝 鎵€浠ュ叾闇€姹傛敹闆嗗苟涓嶇敤鑺辫垂寰堜箙鏃堕棿銆傞殢鐫€ 2019 骞� 8 鏈堜唤鍙戝竷 v1.0 鍚庯紝dubbogo 瓒婃潵瓒婂鍦拌澶氬鐢熶骇鍘傚晢鎶曞叆鐢熶骇浣跨敤鐜涓紝鐩墠鍏堕渶姹傛柟鏉ユ簮濡備笅锛� + +* 瀹炵幇 dubbo 鏌愪釜鐗堟湰鐨勫姛鑳斤紱 +* 瀹為檯浣跨敤鏂圭殑鐢熶骇闇€姹傦紱 +* 涓虹揣璺熷綋涓嬫渶杩戞妧鏈彂灞曟柟鍚戣€岃繘琛岀殑鎶€鏈婕斻€� + +dubbogo 褰撳墠鐨� K8s 娉ㄥ唽涓績鎶€鏈柟妗堝氨鏄揣璺熸渶鏂版妧鏈彂灞曟柟鍚戣€岃繘琛岄婕旂殑鏋佸ソ渚嬭瘉锛屽叾鍙戝睍鏃堕棿绾垮涓嬶細 + +* 2019 骞� 7 鏈堬紝闅忕潃闃块噷闆嗗洟鍜岃殏铓侀噾鏈嶅湪浜戝師鐢熸柟鍚戠殑鎺ㄦ尝鍔╂緶锛岄樋閲� dubbo 寮€鍙戝洟闃熷苟鏈粰鍑哄彲闈犵殑鎶€鏈柟鍚戯紝dubbogo 绀惧尯 core members 涔熼兘娌℃湁浜戝師鐢熸柟鍚戠殑鎶€鏈Н绱拰鐩稿簲浜烘墠锛屽喅瀹氬厛寮€鍙戜竴涓熀浜� kube-apiserver 鐨勬敞鍐屼腑蹇冿紝浠ヨ繘琛屾妧鏈偍澶囷紱 + +* 2019 骞� 8 鏈堬紝 璋冪爺 dubbo 鐨� K8s 娉ㄥ唽涓績鏂规鍚庯紝dubbogo 绀惧尯鍐冲畾鎶涘紑瀹冪嫭绔嬪疄鐜颁竴涓紝浜夊彇浠ユ渶浣庝唬浠锋妸 dubbo 搴旂敤杩佺Щ鍒� K8s 鐜涓繍琛岃捣鏉ワ紝鍚屾椂鍐冲畾鏈潵鐨勫彂灞曟柟鍚戞槸 dubbogo operator锛� + +* 2019 骞� 11 鏈堬紝绀惧尯鐨勭帇缈斿悓瀛︾粰鍑轰簡鍒濇瀹炵幇锛屽湪 2020 骞� 1 鏈堥殢 dubbogo v1.2 鐗堟湰鍙戝竷锛� + +* 2020 骞� 4 鏈堬紝鏈夌敤鎴疯姹傚湪 K8s 鐜涓� consumer 鍙法 namespace 璁块棶 provider锛岀浉搴斿疄鐜板湪 2020 骞� 7 鏈堥殢鐫€ dubbogo v1.5 鐗堟湰鍙戝竷锛� + +* 2020 骞� 5 鏈堬紝dubbogo 绀惧尯鍜� mosn 绀惧尯鍚堜綔瀹炵幇浜� dubbo mesh锛� + +* 2020 骞� 6 鏈堬紝绀惧尯鎰忚瘑鍒� kube-apiserver 鏄郴缁熺殑杩愮淮鎬� IaaS 灞傜殑鏍稿績缁勪欢锛屼笉搴旇璺ㄨ繃 PaaS 灞傜洿鎺ユ毚闇茬粰搴旂敤灞傦紝鍚﹀垯搴旂敤灞備娇鐢ㄤ笉褰撴垨鑰呮鏋惰嚜韬殑娴侀噺鏂归潰鐨� bug 鎶� kube-apiserver 鎵撳灝鍚庡皢閫犳垚鏁翠釜绯荤粺鐨� P0 绾ф晠闅滐紝dubbogo v1.6 搴斿綋缁欏嚭 dubbogo operator 鐨勫叿浣撳疄鐜帮紱 + +* 2020 骞� 7 鏈堬紝dubbogo v1.5 鍙戝竷鍚庯紝绀惧尯宸茬粡鐭ラ亾瀹屽叏鍙互鎶婄洰鍓嶇殑 kube-apiserver 娉ㄥ唽涓績鐨勫疄鐜扮嫭绔嬫垚涓轰竴涓� dubbogo operator锛屾湭鏉ョ殑鏂瑰悜鏄粨鍚� Istio 鎷撳睍鍏剁伆搴﹀彂甯冦€侀檺娴併€佹晠闅滄敞鍏ュ拰閰嶇疆鍔ㄦ€佷笅鍙戣兘鍔涖€� + + +鑷充簬 dubbo-go-proxy 锛宒ubbogo 绀惧尯骞朵笉鎵撶畻鍊熼壌鍏朵粬椤圭洰锛屽畬鍏ㄤ緷闈犵ぞ鍖哄悓瀛﹁础鐚悇鑷兂娉曞悗锛岃繘琛岄」鐩渶姹傛敹闆嗐€傜洰鍓� dubbogo 绀惧尯宸茬粡鏀堕泦瀹屾瘯 dubbo-go-proxy 鐨刐椤圭洰闇€姹傛柟鐨勬剰瑙乚(https://github.com/apache/dubbo-go/issues/540)锛岀ぞ鍖哄凡鏈� 5 浣嶅悓瀛﹀弬涓庨」鐩竴鏈熷紑鍙戯紝棰勮 10 鏈堜唤鍙戝竷鍒濈増銆� + +## 2\. 椤圭洰绠$悊 + +闇€姹傛敹闆嗗畬姣曪紝瀹氫箟杩戞湡涓€娈垫椂闂村唴鐨勫紑鍙戠洰鏍囧悗锛屽氨杩涘叆浜嗛」鐩换鍔℃媶瑙e拰椤圭洰寮€鍙戠鐞嗛樁娈点€傚儚 dubbogo 鍜� dubbo-go-proxy 杩欑鍚庢潵鑰呴」鐩紝澶勪簬杩借刀闃舵锛屼釜浜虹悊瑙e畠浠苟娌℃湁鎵€璋撶殑鍚庡彂浼樺娍锛屾洿娌℃湁鐗瑰畾鐨勬妧鏈紭鍔匡紝鑳藉鍋氱殑灏辨槸蹇€熻凯浠o紝缂╃煭涓庣珵鍝佺殑宸窛銆� + +dubbogo 瑕佹眰鎺ュ彈寮€鍙戜换鍔$殑鍚勪釜 feature owner 蹇呴』鍦ㄦ煇涓� deadline 鍓嶅畬鎴愮浉搴旂殑寮€鍙戜换鍔°€傚綋鐒讹紝feature 鐨勭瓑绾т笉鍚岋紝闈炴牳蹇� feature 銆愬鎶€鏈婕旀€х殑 feature銆戝厑璁� delay锛岄『寤跺彂甯冧篃鏃犱笉鍙€� + +鎴戜滑鍦ㄩ」鐩瘡涓増鏈殑闇€姹傛敹闆嗛樁娈碉紝浼氭妸鐖卞ソ鑰呯粺涓€鎷夊叆涓€涓皬缇よ繘琛屾矡閫氫氦娴併€傝繘鍏ュ紑鍙戦樁娈垫椂锛岀敱浜庨」鐩椂闂� deadline 闄愬畾浠ュ強鎶€鏈尮閰嶅害涓ら」瑕佹眰锛屾瘡涓増鏈氨寰堣嚜鐒惰兘閫夊嚭鑳藉鐣欎笅鏉ュ弬涓庨」鐩紑鍙戠殑浜恒€傛渶缁堝弬涓庢瘡涓増鏈紑鍙戠殑浜哄敖閲忎笉瑕佽秴杩� 7 涓汉锛屽惁鍒欐矡閫氭垚鏈氨浼氬墽澧炪€� + +涓嬪浘鏄� dubbogo v1.5.1 鐨勯」鐩鐞嗗浘\*\*锛堥樋閲屽反宸翠簯鍘熺敓鍏紬鍙峰悗鍙板洖澶嶁€�915鈥濆嵆鍙煡鐪嬫竻鏅板ぇ鍥撅級\*\*锛� + + + +鍏舵湁浠诲姟鍒嗚В銆佹妧鏈闄╀互鍙婇闄╅妗堛€� + + + +宸ュ叿鏄敓浜у姏锛岀洰鍓嶄互 dubbogo 椤圭洰寮€鍙戣繘搴﹁窡韪伐鍏蜂娇鐢� Github Projects銆傚涓婂浘锛屾瘡涓瓙浠诲姟杩涘害锛岃繖涓伐鍏烽兘浼氬強鏃舵樉绀猴紝鐩稿簲鐨勪换鍔� owner 鍙互鍙婃椂鏍规嵁浠诲姟杩涘害鍜� deadline 锛岃皟鏁村紑鍙戣鍒掋€傛洿杩涗竴姝ョ殑濂藉鏄紝娌℃湁浜轰細瀵瑰伐鍏蜂骇鐢熸剰瑙侊紝鎽嗚劚鈥滀氦閫氬熀鏈潬璧帮紝閫氳鍩烘湰闈犲惣鈥濈殑娌熼€氭ā寮忥紝鍑忓皯鐗堟湰鍙戠増浜哄拰 feature owner 涔嬮棿鐨勬埦姘斻€� + +## 3\. 浠g爜璐ㄩ噺 + +寮€婧愰」鐩殑寮€鍙戜换鍔′笉浠呬粎鏄紑鍙戜唬鐮侊紝涔熶笉鎰忓懗鐫€鍥犱负鍚勪釜 owner 浠呬粎鏄笟浣欐椂闂村弬涓庡紑婧愰」鐩氨鍙互闄嶄綆瀵逛唬鐮佽川閲忚姹傘€� + +宸ュ叿灏辨槸鐢熶骇鍔涳紝鍚堥€傜殑宸ュ叿鑳藉鍑忓皯浜哄伐宸ヤ綔閲忓拰鎻愬崌浠g爜璐ㄩ噺銆俤ubbogo 鍦ㄩ」鐩紑鍙戣繃绋嬩腑鐨勫悇涓樁娈甸兘鐢ㄥ埌浜嗗涓嬪伐鍏凤細 + +* auto-comment锛歝ontributor 鎻愬嚭 issue 鎴栬€� pr 鍚庯紝鍙緢鏂逛究鍦板彂鍑洪鍏堝畾鍒剁殑璇勮锛� + +* hound锛氫竴涓� Go 璇█椤圭洰闈欐€佷唬鐮佹娴嬪伐鍏凤紝鑷姩 Review 浠g爜锛� + +* travis锛氫竴涓� Github 椤圭洰鑷姩鍖栨祴璇曞伐鍏凤紝鍙嚜鍔ㄦ墽琛屼唬鐮佸崟娴嬪拰鐢ㄦ埛鑷畾涔夌殑闆嗘垚娴嬭瘯锛屽苟鍙戝嚭閽夐拤閫氱煡锛� + +* 浜哄伐 Review锛歞ubbogo 绀惧尯瑕佹眰姣忎釜 pr 鑷冲皯鏈変笁涓� committer 绾у埆鐨勪汉 Review 閫氳繃锛� + +* goreportcard锛氫竴涓緢濂界殑 Github 椤圭洰闈欐€佷唬鐮佹娴嬪伐鍏凤紱 + +* gitee锛氫綔涓哄浗鍐呬竴涓瘮杈冨己澶х殑浠g爜鎵樼缃戠珯锛屽厤璐逛负椤圭洰鎻愪緵浜嗕竴浜涗唬鐮佸畨鍏ㄥ拰闈欐€佷唬鐮佽川閲忔娴嬪伐鍏凤紝dubbogo 涔熺粡甯镐娇鐢紝鍙楃泭鑹锛� + +* 浠g爜瑙勮寖锛岀ぞ鍖哄唴閮ㄦ湁涓€浠界畝鍗曠殑浠g爜瑙勮寖锛屽苟闅忕潃椤圭洰鍙戝睍涓嶆柇瀹屽杽銆� + + +# 灞曟湜 + +<br />dubbogo 椤圭洰姣忔鍙戝畬鐗堟湰锛屽彂鐗堜汉閮戒細棣栧厛鍙戝嚭涓€浠� "What's New"锛岄櫎浜嗘€荤粨鏈€鏂扮増鏈殑鐗规€у锛岃繕浼氭€荤粨鍏惰繎鏈熻繘灞曞苟瀵规湭鏉ュ彂灞曡繘琛岃鍒掞紝浠ュ府鍔╅」鐩埍濂借€呭拰浣跨敤鑰呬簡瑙e叾瀹炵幇鎬濊矾鍜屼娇鐢ㄦ柟娉曘€� + +dubbogo 鑷韩杩樻湁寰堝缂虹偣锛屽锛� + +* 缃戠珯寤鸿鍜屾枃妗h川閲忛兘鏈夊緟鏀硅繘锛� +* API 鐢ㄦ埛鍙嬪ソ搴︿笉澶燂紱 +* 閰嶇疆鏂囦欢杩囧涓旀病鏈夊悎鐞嗙殑鏂囨。璇存槑瀵艰嚧鍏ラ棬闂ㄦ楂橈紱 +* 鏁翠綋鎬ц兘鏀硅繘锛屽緢澶氬湴鏂瑰彲娣诲姞璋冪敤閾剧殑缂撳瓨浠ュ噺灏忛攣绔炰簤锛� +* 娣诲姞 prometheus 鎸囨爣锛岀户缁彁楂� 鍙娴嬫€э紱 +* 鍦ㄥ崗璁眰闈㈡敮鎸佸叾浠栧井鏈嶅姟妗嗘灦锛屽疄鐜板師鐢熼€氫俊锛屼互缁х画鎻愬崌鍏朵簰鑱斾簰閫氭€э紱 +* dubbo-samples 鐢ㄤ緥涓嶅涓板瘜锛岀户缁坊鍔犳祴璇曠敤渚嬶紝浠ュ噺灏忓叆闂ㄩ棬妲涳紱 + +甯屾湜鍊熷姪绀惧尯涔嬪姏锛屽湪 dubbogo 鍙戝睍杩囩▼涓秷鍖栧苟瑙e喅鎺夎繖浜涢棶棰橈紝dubbogo 绀惧尯銆愰拤閽夌兢鍙� 23331795銆戜笌 dubbogo 鍚屽湪銆� + +### 浣滆€呯畝浠� + +浜庨洦锛屼竴涓湁鍗佸骞存湇鍔$鍩虹鏋舵瀯鐮斿彂涓€绾垮伐浣滅粡楠岀殑绋嬪簭鍛橈紝鐩墠鍦ㄨ殏铓侀噾鏈嶅彲淇″師鐢熼儴浠庝簨瀹瑰櫒缂栨帓鍜� service mesh 宸ヤ綔銆傜儹鐖卞紑婧愶紝浠� 2015 骞寸粰 Redis 璐$尞浠g爜寮€濮嬶紝闄嗙画鏀硅繘杩� Muduo/Pika/Dubbo/Dubbo-go 绛夌煡鍚嶉」鐩€� \ No newline at end of file diff --git a/doc/md/getty/getty-development-log.md b/doc/md/getty/getty-development-log.md new file mode 100644 index 0000000000000000000000000000000000000000..c98be503adcda4ce51a2635a14cf25ef721b0e26 --- /dev/null +++ b/doc/md/getty/getty-development-log.md @@ -0,0 +1,224 @@ +## [getty 寮€鍙戞棩蹇梋(http://alexstocks.github.io/html/getty.html) + +* * * + +_written by Alex Stocks on 2018/03/19锛岀増鏉冩墍鏈夛紝鏃犳巿鏉冧笉寰楄浆杞絖 + +### 0 璇存槑 + +* * * + +[getty](https://github.com/alexstocks/getty)鏄竴涓猤o璇█瀹炵幇鐨勭綉缁滃眰寮曟搸锛屽彲浠ュ鐞員CP/UDP/websocket涓夌缃戠粶鍗忚銆� + +2016骞�6鏈堟垜鍦ㄤ笂娴峰仛涓€涓嵆鏃堕€氳椤圭洰鏃讹紝鎺ュ彛灞傜殑搴曞眰缃戠粶椹卞姩鏄�褰撴椂鐨勫悓浜媅sanbit](https://github.com/sanbit)鍐欑殑锛�鍘熷缃戠粶灞傚疄鐜颁簡TCP Server锛屽叾鍛藉悕瑙勮寖瀛︿範浜嗚憲鍚嶇殑netty銆傚綋鏃惰繖涓紩鎿庢瘮杈冪畝娲侊紝闅忕潃鎴戝杩欎釜椤圭洰鐨勬敼杩涜繖涓綉缁滃眰寮曟搸涔熷氨闅忎箣杩涘寲浜嗭紙娣诲姞浜員CP Client銆佹娊璞″嚭浜� TCP connection 鍜� TCP session锛夛紝鑷�2016骞�8鏈堜唤锛堝張娣诲姞浜唚ebsocket锛夊叾涓庡師濮嬪疄鐜板凡缁忓ぇ寮傚叾瓒d簡锛屽緛寰楀師浣滆€呭拰鐩稿叧棰嗗鍚屾剰鍚庡氨鏀惧埌浜唃ithub涓娿€� + +灏嗚繎涓ゅ勾鐨勬椂闂存垜涓嶉棿鏂湴瀵瑰叾杩涜鏀硅繘锛屽勾榻挎笎澧炰絾璁板繂閫熻“锛岃寰楁湁蹇呰璁板綍涓嬩竴浜涘紑鍙戣繃绋嬩腑閬囧埌鐨勯棶棰�浠ュ強瑙e喅鏂规硶锛屼互澶囧皢鏉ュ洖蹇嗕箣鍙傝€冦€� + +### 1 UDP connection + +* * * + +2018骞�3鏈�5鏃� 璧风粰 getty 娣诲姞浜哢DP鏀寔銆� + +#### 1.1 UDP connect + +* * * + +UDP鑷韩鍒嗕负unconnected UDP鍜宑onnected UDP涓ょ锛宑onnected UDP鐨勫簳灞傚師鐞嗚涓嬪浘銆� + + + +褰撲竴绔殑UDP endpoint璋冪敤connect涔嬪悗锛宱s灏变細鍦ㄥ唴閮ㄧ殑routing table涓婃妸udp socket鍜屽彟涓€涓猠ndpoint鐨勫湴鍧€鍏宠仈璧锋潵锛屽湪鍙戣捣connect鐨剈dp endpoint绔缓绔嬭捣涓€涓崟鍚戠殑杩炴帴鍥涘厓缁勶細鍙戝嚭鐨刣atagram packet鍙兘鍙戝線杩欎釜endpoint锛堜笉绠endto鐨勬椂鍊欐槸鍚︽寚瀹氫簡鍦板潃锛変笖鍙兘鎺ユ敹杩欎釜endpoint鍙戞潵鐨剈dp datagram packet锛堝鍥�???鍙戞潵鐨勫寘浼氳OS涓㈠純锛夈€� + +UDP endpoint鍙戣捣connect鍚庯紝OS骞朵笉浼氳繘琛孴CP寮忕殑涓夋鎻℃墜锛屾搷浣滅郴缁熷叡浠呬粎璁板綍涓�UDP socket鐨刾eer udp endpoint 鍦板潃鍚庡氨鐞嗚В杩斿洖锛屼粎浠呬細鏍告煡瀵圭鍦板潃鏄惁瀛樺湪缃戠粶涓€� + +鑷充簬鍙︿竴涓猽dp endpoint鏄惁涓篶onnected udp鍒欐棤鍏崇揣瑕侊紝鎵€浠ョОudp connection鏄崟鍚戠殑杩炴帴銆傚鏋渃onnect鐨勫绔笉瀛樺湪鎴栬€呭绔鍙f病鏈夎繘绋嬬洃鍚紝鍒欏彂鍖呭悗瀵圭浼氳繑鍥濱CMP 鈥減ort unreachable鈥� 閿欒銆� + +濡傛灉涓€涓狿OSIX绯荤粺鐨勮繘绋�鍙戣捣UDP write鏃舵病鏈夋寚瀹歱eer UDP address锛屽垯浼氭敹鍒癊NOTCONN閿欒锛岃€岄潪EDESTADDRREQ銆� + + + +涓€鑸彂璧穋onnect鐨勪负 UDP client锛屽吀鍨嬬殑鍦烘櫙鏄疍NS绯荤粺锛�DNS client鏍规嵁/etc/resolv.conf閲岄潰鎸囧畾鐨凞NS server杩涜connect鍔ㄤ綔銆� + +鑷充簬 UDP server 鍙戣捣connect鐨勬儏褰㈡湁 TFTP锛�UDP client 鍜� UDP server 闇€瑕佽繘琛岄暱鏃堕棿鐨勯€氫俊锛� client 鍜� server 閮介渶瑕佽皟鐢� connect 鎴愪负 connected UDP銆� + +濡傛灉涓€涓� connected UDP 闇€瑕佹洿鎹� peer endpoint address锛屽彧闇€瑕侀噸鏂� connect 鍗冲彲銆� + +#### 1.2 connected UDP 鐨勬€ц兘 + +* * * + +connected UDP 鐨勪紭鍔胯瑙佸弬鑰冩枃妗�1銆傚亣璁�鏈変袱涓� datagram 闇€瑕佸彂閫侊紝unconnected UDP 鐨勮繘琛� write 鏃跺彂閫佽繃绋嬪涓嬶細 + +```none +* Connect the socket +* Output the first datagram +* Unconnect the socket +* Connect the socket +* Output the second datagram +* Unconnect the socket +``` + +姣忓彂閫佷竴涓寘閮介渶瑕佽繘琛� connect锛屾搷浣滅郴缁熷埌 routine table cache 涓垽鏂湰娆$洰鐨勫湴鍦板潃鏄惁涓庝笂娆′竴鑷达紝濡傛灉涓嶄竴鑷�杩橀渶瑕佷慨鏀� routine table銆� + +connected UDP 鐨勪袱娆″彂閫佽繃绋嬪涓嬶細 + +```none +* Connect the socket +* Output first datagram +* Output second datagram +``` + +杩欎釜 case 涓嬶紝鍐呮牳鍙湪绗竴娆¤瀹氫笅铏氭嫙閾炬帴鐨� peer address锛屽悗闈㈣繘琛岃繛缁彂閫佸嵆鍙€傛墍浠� connected UDP 鐨勫彂閫�杩囩▼鍑忓皯浜� 1/3 鐨勭瓑寰呮椂闂淬€� + +2017骞�5鏈�7鏃� 鎴戞浘鐢� [python 绋嬪簭](https://github.com/alexStocks/python-practice/blob/master/tcp_udp_http_ws/udp/client.py) 瀵逛簩鑰呬箣闂寸殑鎬ц兘鍋氳繃娴嬭瘯锛屽鏋� client 鍜� server 閮介儴缃插湪鏈満锛屾祴璇曠粨鏋滄樉绀哄彂閫� 100 000 閲忕殑 UDP datagram packet 鏃讹紝connected UDP 姣� unconnected UDP 灏戠敤浜� 2 / 13 鐨勬椂闂淬€� + +杩欎釜娴嬭瘯鐨勫彟涓€涓粨璁烘槸锛氫笉绠℃槸 connected UDP 杩樻槸 unconnected UDP锛屽鏋滃惎鐢ㄤ簡 SetTimeout锛屽垯浼氬澶у彂閫佸欢杩熴€� + +#### 1.3 Go UDP + +* * * + +Go 璇█ UDP 缂栫▼涔熷 connected UDP 鍜� unconnected UDP 杩涜浜嗘槑纭尯鍒嗭紝鍙傝€冩枃妗�2 璇︾粏鍦板垪鏄庝簡濡備綍浣跨敤鐩稿叧 API锛屾牴鎹繖绡囨枃妗d釜浜轰篃鍐欎竴涓� [绋嬪簭](https://github.com/alexstocks/go-practice/blob/master/udp-tcp-http/udp/connected-udp.go) 娴嬭瘯杩欎簺 API锛屾祴璇曠粨璁哄涓嬶細 + +```none +* 1 connected UDP 璇诲啓鏂规硶鏄� Read 鍜� Write锛� +* 2 unconnected UDP 璇诲啓鏂规硶鏄� ReadFromUDP 鍜� WriteToUDP锛堜互鍙� ReadFrom 鍜� WriteTo)锛� +* 3 unconnected UDP 鍙互璋冪敤 Read锛屽彧鏄棤娉曡幏鍙� peer addr锛� +* 4 connected UDP 鍙互璋冪敤 ReadFromUDP锛堝~鍐欑殑鍦板潃浼氳蹇界暐锛� +* 5 connected UDP 涓嶈兘璋冪敤 WriteToUDP锛屸€濆嵆浣挎槸鐩稿悓鐨勭洰鏍囧湴鍧€涔熶笉鍙互鈥濓紝鍚﹀垯浼氬緱鍒伴敊璇� 鈥渦se of WriteTo with pre-connected connection鈥濓紱 +* 6 unconnected UDP 涓嶈兘璋冪敤 Write, 鈥滃洜涓轰笉鐭ラ亾鐩爣鍦板潃鈥�, error:鈥漺rite: destination address requiredsmallnestMBP:udp smallnest鈥濓紱 +* 7 connected UDP 鍙互璋冪敤 WriteMsgUDP锛屼絾鏄湴鍧€蹇呴』涓� nil锛� +* 8 unconnected UDP 鍙互璋冪敤 WriteMsgUDP锛屼絾鏄�蹇呴』濉啓 peer endpoint address銆� +``` + +缁间笂缁撹锛岃缁熶竴浣跨敤 ReadFromUDP锛屽啓鍒欑粺涓€浣跨敤 WriteMsgUDP銆� + +#### 1.4 Getty UDP + +* * * + +鐗堟湰 v0.8.1 Getty 涓坊鍔� connected UDP 鏀寔鏃讹紝鍏惰繛鎺ュ嚱鏁� [dialUDP](https://github.com/alexstocks/getty/blob/master/client.go#L141) 杩欐槸绠€鍗曡皟鐢ㄤ簡 net.DialUDP 鍑芥暟锛屽鑷存槰鏃ワ紙20180318 22:19 pm锛夋祴璇曠殑鏃跺€欓亣鍒颁竴涓€幇璞★細鎶� peer UDP endpoint 鍏抽棴锛宭ocal udp endpoint 杩涜 connect 鏃� net.DialUDP 鍑芥暟杩斿洖鎴愬姛锛岀劧鍚� lsof 鍛戒护鏌ラ獙缁撴灉鏃剁湅鍒扮‘瀹炲瓨鍦ㄨ繖涓崟閾炬帴锛� + +```none +COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME +echo_clie 31729 alex 9u IPv4 0xa5d288135c97569d 0t0 UDP localhost:63410->localhost:10000 +``` + +鐒跺悗褰� net.UDPConn 杩涜 read 鍔ㄤ綔鐨勬椂鍊欙紝浼氬緱鍒伴敊璇� 鈥渞ead: connection refused鈥濄€� + +浜庢槸妯′豢C璇█涓 TCP client connect 鎴愬姛涓庡惁鍒ゆ柇鏂规硶锛屽 [dialUDP](https://github.com/alexstocks/getty/blob/master/client.go#L141) 鏀硅繘濡備笅锛� + +```none +* 1 net.DialUDP 鎴愬姛涔嬪悗锛屽垽鏂叾鏄惁鏄嚜杩炴帴锛屾槸鍒欓€€鍑猴紱 +* 2 connected UDP 鍚戝绔彂閫佷竴涓棤鐢ㄧ殑 datagram packet銆愨€漰ing鈥濆瓧绗︿覆锛屽绔細鍥犲叾闈炴纭� datagram 鑰屼涪寮冦€戯紝澶辫触鍒欓€€鍑猴紱 +* 3 connected UDP 鍙戣捣璇绘搷浣滐紝濡傛灉瀵圭杩斿洖 鈥渞ead: connection refused鈥� 鍒欓€€鍑猴紝鍚﹀垯灏卞垽鏂负 connect 鎴愬姛銆� +``` + +### 2 Compression + +* * * + +鍘诲勾缁� getty 娣诲姞浜� TCP/Websocket compression 鏀寔锛學ebsocket 搴撲娇鐢ㄧ殑鏄� [gorilla/websocket](https://github.com/gorilla/websocket/)锛孾Go 瀹樼綉](https://godoc.org/golang.org/x/net/websocket)涔熸帹鑽愯繖涓簱锛屽洜涓鸿嚜 `This package("golang.org/x/net/websocket") currently lacks some features`銆� + +#### 2.1 TCP compression + +* * * + +鏈€杩戝湪瀵� Websocket compression 杩涜娴嬭瘯鐨勬椂鍊欙紝鍙戠幇 CPU 寰堝鏄撳氨璺戝埌 100%锛屼笖绋嬪簭鍚姩鍚庡緢蹇氨 panic 閫€鍑轰簡銆� + +鏍规嵁 panic 淇℃伅鎻愮ず鏌ュ埌 [gorilla/websocket/conn.go:ReadMsg](https://github.com/gorilla/websocket/blob/master/conn.go#L1018) 鍑芥暟璋冪敤 [gorilla/websocket/conn.go:NextReader](https://github.com/gorilla/websocket/blob/master/conn.go#L928) 鍚庡氨绔嬪嵆 panic 閫€鍑轰簡銆俻anic 鐨� `琛ㄥ眰鍘熷洜` 鍒版槸寰堝鏄撴煡鏄庯細 + +* 1 [gorrilla/websocket:Conn::advanceFrame](https://github.com/gorilla/websocket/blob/master/conn.go#L768) 閬囧埌璇昏秴鏃堕敊璇紙io timeout锛�; +* 2 [gorrilla/websocket:ConnConn.readErr](https://github.com/gorilla/websocket/blob/master/conn.go#L941)璁板綍杩欎釜error锛� +* 3 [gorilla/websocket/conn.go:Conn::NextReader](https://github.com/gorilla/websocket/blob/master/conn.go#L959)寮€濮嬭鍙栦箣鍓嶅垯[妫€鏌ヨ繖涓敊璇痌(https://github.com/gorilla/websocket/blob/master/conn.go#L938)锛屽浠ュ墠鍙戠敓杩囬敊璇垯涓嶅啀璇诲彇 websocket frame锛屽苟瀵筟gorrilla/websocket:ConnConn.readErr绱Н璁℃暟](https://github.com/gorilla/websocket/blob/master/conn.go#L957)锛� +* 4 [褰揼orrilla/websocket:ConnConn.readErr鏁板€煎ぇ浜� 1000](https://github.com/gorilla/websocket/blob/master/conn.go#L958) 鐨勬椂鍊欙紝绋嬪簭灏变細panic 閫€鍑恒€� + +浣嗘槸涓轰綍鍙戠敓璇昏秴鏃堕敊璇垯姣棤澶寸华銆� + +2018/03/07 鏃ユ祴璇� TCP compression 鐨勬椂鍊欏彂鐜板惎鍔� compression 鍚庯紝绋嬪簭 CPU 涔熶細寰堝揩璺戝埌 100%锛岃繘涓€姝ヨ拷鏌ュ悗鍙戠幇鍑芥暟 [getty/conn.go:gettyTCPConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L228) 閲岄潰鐨� log 鏈夊緢澶� 鈥渋o timeout鈥� error銆傚綋鏃舵煡鍒拌繖涓敊璇緢鐤戞儜锛屽洜涓烘垜宸茬粡鍦� TCP read 涔嬪墠杩涜浜嗚秴鏃惰缃€怱etReadDeadline銆戯紝闅鹃亾鍚姩 compression 浼氬鑷磋秴鏃惰缃け鏁堜娇寰梥ocket鎴愪簡闈為樆濉炵殑socket锛� + +浜庢槸鍦� [getty/conn.go:gettyTCPConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L228) 涓坊鍔犱簡涓€涓€昏緫锛氬惎鐢� TCP compression 鐨勬椂涓嶅啀璁剧疆瓒呮椂鏃堕棿銆愰粯璁ゆ儏鍐典笅tcp connection鏄案涔呴樆濉炵殑銆戯紝CPU 100% 鐨勯棶棰樺緢蹇氨寰楀埌浜嗚В鍐炽€� + +鑷充簬涓轰綍 `鍚敤 TCP compression 浼氬鑷� SetDeadline 澶辨晥浣垮緱socket鎴愪簡闈為樆濉炵殑socket`锛屽浛浜庝釜浜鸿兘鍔涘拰绮惧姏锛屽緟灏嗘潵杩芥煡鍑虹粨鏋滃悗鍐嶅湪姝よˉ鍏呬箣銆� + +#### 2.2 Websocket compression + +* * * + +TCP compression 鐨勯棶棰樿В鍐冲悗锛屼釜浜虹寽鎯� Websocket compression 绋嬪簭閬囧埌鐨勯棶棰樻垨璁镐篃璺� `鍚敤 TCP compression 浼氬鑷� SetDeadline 澶辨晥浣垮緱socket鎴愪簡闈為樆濉炵殑socket` 鏈夊叧銆� + +浜庢槸鍊熼壌 TCP 鐨勮В鍐虫柟娉曪紝鍦� [getty/conn.go:gettyWSConn::read](https://github.com/alexstocks/getty/blob/master/conn.go#L527) 鐩存帴鎶婅秴鏃惰缃叧闂紝鐒跺悗 CPU 100% 琚В鍐筹紝涓旂▼搴忚繍杞甯搞€� + +### 3 unix socket + +鏈妭涓� getty 鏃犲叧锛屼粎浠呮槸鍦ㄤ娇鐢� unix socket 杩囩▼涓亣鍒颁竴浜� keypoint 鐨勮褰曘€� + +#### 3.1 reliable + +unix socket datagram 褰㈠紡鐨勫寘涔熸槸鍙潬鐨勶紝姣忔鍐欏繀鐒惰姹傚搴斾竴娆¤锛屽惁鍒欏啓鏂逛細琚樆濉炪€傚鏋滄槸 stream 褰㈠紡锛屽垯 buffer 娌℃湁婊′箣鍓嶏紝鍐欒€呮槸涓嶄細琚樆濉炵殑銆俤atagram 鐨勪紭鍔垮湪浜� api 绠€鍗曘€� + +```none +Unix sockets are reliable. If the reader doesn't read, the writer blocks. If the socket is a datagram socket, each write is paired with a read. If the socket is a stream socket, the kernel may buffer some bytes between the writer and the reader, but when the buffer is full, the writer will block. Data is never discarded, except for buffered data if the reader closes the connection before reading the buffer. ---[Do UNIX Domain Sockets Overflow?](https://unix.stackexchange.com/questions/283323/do-unix-domain-sockets-overflow) +``` + +```none +On most UNIX implementations, UNIX domain datagram sockets are always reliable and don't reorder + datagrams. ---[man 7 socketpair](http://www.man7.org/linux/man-pages/man7/unix.7.html) +``` + +鈥� ---[Do UNIX Domain Sockets Overflow?](https://unix.stackexchange.com/questions/283323/do-unix-domain-sockets-overflow) + +#### 3.2 buffer size + +datagram 褰㈠紡鐨� unix socket 鐨勫崟涓� datagram 鍖呮渶澶ч暱搴︽槸 130688 B銆� + +```none +AF_UNIX SOCK_DATAGRAM/SOCK_SEQPACKET datagrams need contiguous memory. Contiguous physical memory is hard to find, and the allocation fails. The max size actually is 130688 B. --- [the max size of AF_UNIX datagram message that can be sent in linux](https://stackoverflow.com/questions/4729315/what-is-the-max-size-of-af-unix-datagram-message-that-can-be-sent-in-linux) +``` + +```none +It looks like AF_UNIX sockets don't support scatter/gather on current Linux. it is a fixed size 130688 B. --- [Difference between UNIX domain STREAM and DATAGRAM sockets?](https://stackoverflow.com/questions/13953912/difference-between-unix-domain-stream-and-datagram-sockets) +``` + +### 4 Goroutine Pool + +闅忕潃 [dubbogo/getty](https://github.com/dubbogo/getty) 琚� [apache/dubbo-go](https://github.com/apache/dubbo-go/) 鐢ㄤ綔搴曞眰 tcp 鐨� transport 寮曟搸锛屽浜庢彁楂樼郴缁熷悶鍚愮殑闇€瑕侊紝[dubbogo/getty](https://github.com/dubbogo/getty) 闈复鐫€涓嬩竴姝ョ殑杩涘寲瑕佹眰锛歔**閽堝 dubbo-go 鍜� Getty 鐨勭綉缁� I/O 涓庣嚎绋嬫淳鍙戣繖涓€閮ㄥ垎杩涜杩涗竴姝ヤ紭鍖�**](https://www.oschina.net/question/3820517_2306822)銆傚叾涓殑鍏抽敭灏辨槸娣诲姞 Goroutine Pool銆愪笅鏂囩畝绉� gr pool銆戯紝浠ュ垎绂荤綉缁� I/O 鍜� 閫昏緫澶勭悊銆� + +Gr Pool 鎴愬憳鏈変换鍔¢槦鍒椼€愬叾鏁扮洰涓� M銆戝拰 Gr 鏁扮粍銆愬叾鏁扮洰涓� N銆戜互鍙婁换鍔°€愭垨鑰呯О涔嬩负娑堟伅銆戯紝鏍规嵁 N 鐨勬暟鐩彉鍖栧叾绫诲瀷鍒嗕负鍙几缂╀笌鍥哄畾澶у皬锛屽彲浼哥缉 Gr Pool 濂藉鏄彲浠ラ殢鐫€浠诲姟鏁扮洰鍙樺寲澧炲噺 N 浠ヨ妭绾� CPU 鍜屽唴瀛樿祫婧愶紝浣嗕竴鑸笉鐢氬父鐢紝姣斾汉浠ュ墠鎾歌繃涓€涓悗灏辫汉鍦ㄦ垜鐨� [github repo](https://github.com/alexstocks/goext/blob/master/sync/pool/worker_pool.go) 閲岄潰浜嗐€� + +[dubbogo/getty](https://github.com/dubbogo/getty) 鍙叧娉� N 鍊煎浐瀹氬ぇ灏忕殑 gr pool锛屼笖涓嶈€冭檻鏀跺埌鍖呭悗鐨勫鐞嗛『搴忋€傝濡傦紝[dubbogo/getty](https://github.com/dubbogo/getty) 鏈嶅姟绔敹鍒颁簡瀹㈡埛绔彂鏉ョ殑 A 鍜� B 涓や釜缃戠粶鍖咃紝涓嶈€冭檻澶勭悊椤哄簭鐨� gr pool 妯″瀷鍙兘閫犳垚瀹㈡埛绔厛鏀跺埌 B 鍖呯殑 response锛屽悗鎵嶆敹鍒� A 鍖呯殑 response銆� + +濡傛灉瀹㈡埛绔殑姣忔璇锋眰閮芥槸鐙珛鐨勶紝娌℃湁鍓嶅悗椤哄簭鍏崇郴锛屽垯甯︽湁 gr pool 鐗规€х殑 [dubbogo/getty](https://github.com/dubbogo/getty) 涓嶈€冭檻椤哄簭鍏崇郴鏄病鏈夐棶棰樼殑銆傚鏋滀笂灞傜敤鎴峰叧娉� A 鍜� B 璇锋眰澶勭悊鐨勫墠鍚庨『搴忥紝鍒欏彲浠ユ妸 A 鍜� B 涓や釜璇锋眰鍚堝苟涓轰竴涓姹傦紝鎴栬€呮妸 gr pool 鐗规€у叧闂€� + +### 4.1 鍥哄畾澶у皬 Gr Pool + +鎸夌収 M 涓� N 鐨勬瘮渚嬶紝鍥哄畾澶у皬 Gr Pool 鍙堝尯鍒嗕负 1:1銆�1:N銆丮:N 涓夌被銆� + +1:N 绫诲瀷鐨� Gr Pool 鏈€鏄撳疄鐜帮紝涓汉 2017 骞村湪椤圭洰 [kafka-connect-elasticsearch](https://github.com/AlexStocks/kafka-connect-elasticsearch) 涓疄鐜拌繃姝ょ被鍨嬬殑 [Gr Pool](https://github.com/AlexStocks/kafka-connect-elasticsearch/blob/master/app/worker.go)锛氫綔涓烘秷璐硅€呬粠 kafka 璇诲彇鏁版嵁鐒跺悗鏀惧叆娑堟伅闃熷垪锛岀劧鍚庡悇涓� worker gr 浠庢闃熷垪涓彇鍑轰换鍔¤繘琛屾秷璐瑰鐞嗐€� + +鍚� [dubbogo/getty](https://github.com/dubbogo/getty) 涓坊鍔� gr pool 鏃朵篃鏇惧疄鐜拌繃杩欎釜鐗堟湰鐨� [gr pool](https://github.com/dubbogo/getty/pull/6/commits/4b32c61e65858b3eea9d88d8f1c154ab730c32f1)銆傝繖绉嶆ā鍨嬬殑 gr pool 鏁翠釜 pool 鍙垱寤轰竴涓� chan锛� 鎵€鏈� gr 鍘昏鍙栬繖涓€涓� chan锛屽叾缂虹偣鏄細闃熷垪璇诲啓妯″瀷鏄� 涓€鍐欏璇伙紝鍥犱负 go channel 鐨勪綆鏁堢巼銆愭暣浣撲娇鐢ㄤ竴涓� mutex lock銆戦€犳垚绔炰簤婵€鐑堬紝褰撶劧鍏剁綉缁滃寘澶勭悊椤哄簭鏇存棤浠庝繚璇併€� + +[dubbogo/getty](https://github.com/dubbogo/getty) 鍒濆鐗堟湰鐨� [gr pool](https://github.com/dubbogo/getty/pull/6/files/c4d06e2a329758a6c65c46abe464a90a3002e428#diff-9922b38d89e2ff9f820f2ce62f254162) 妯″瀷涓� 1:1锛屾瘡涓� gr 澶氭湁鑷繁鐨� chan锛屽叾璇诲啓妯″瀷鏄竴鍐欎竴璇伙紝鍏朵紭鐐规槸鍙繚璇佺綉缁滃寘澶勭悊椤哄簭鎬э紝 +濡傝鍙� kafka 娑堟伅鏃跺€欙紝鎸夌収 kafka message 鐨� key 鐨� hash 鍊间互鍙栦綑鏂瑰紡銆恏ash(message key) % N銆戝皢鍏舵姇閫掑埌鏌愪釜 task queue锛屽垯鍚屼竴 key 鐨勬秷鎭兘鍙互淇濊瘉澶勭悊鏈夊簭銆備絾 [鏈涘摜](http://alexstocks.github.io/html/10) 鎸囧嚭浜嗚繖绉嶆ā鍨嬬殑缂洪櫡锛氭瘡涓猼ask澶勭悊瑕佹湁鏃堕棿锛屾鏂规浼氶€犳垚鏌愪釜 gr 鐨� chan 閲岄潰鏈� task 鍫靛锛屽氨绠楀叾浠� gr 闂茬潃锛屼篃娌″姙娉曞鐞嗕箣銆愪换鍔″鐞嗏€滈ゥ楗库€濄€戙€� + +[wenwei86](https://github.com/wenweihu86) 缁欏嚭浜嗘洿杩涗竴姝ョ殑 1:1 妯″瀷鐨勬敼杩涙柟妗堬細姣忎釜 gr 涓€涓� chan锛屽鏋� gr 鍙戠幇鑷繁鐨� chan 娌℃湁璇锋眰锛屽氨鍘绘壘鍒殑 chan锛屽彂閫佹柟涔熷敖閲忓彂寰€娑堣垂蹇殑鍗忕▼銆傝繖涓柟妗堢被浼间簬 go runtime 鍐呴儴鐨� MPG 璋冨害绠楁硶锛屼絾鏄鎴戜釜浜烘潵璇寸畻娉曞拰瀹炵幇鍧囧お澶嶆潅锛屾晠鑰屾病鏈夐噰鐢ㄣ€� + +[dubbogo/getty](https://github.com/dubbogo/getty) 鐩墠閲囩敤浜� M:N 妯″瀷鐗堟湰鐨� [gr pool](https://github.com/dubbogo/getty/pull/6/commits/1991056b300ba9804de0554dbb49b5eb04560c4b)锛屾瘡涓� task queue 琚� N/M 涓� gr 娑堣垂锛岃繖绉嶆ā鍨嬬殑浼樼偣鏄吋椤惧鐞嗘晥鐜囧拰閿佸帇鍔涘钩琛★紝鍙互鍋氬埌鎬讳綋灞傞潰鐨勪换鍔″鐞嗗潎琛°€傛鐗堟湰涓� Task 娲惧彂閲囩敤 RoundRobin 鏂瑰紡銆� + +## 鎬荤粨 + +* * * + +鏈枃鎬荤粨浜� [getty](https://github.com/alexstocks/getty) 杩戞湡寮€鍙戣繃绋嬩腑閬囧埌鐨勪竴浜涢棶棰橈紝鍥夸簬涓汉姘村钩鍙兘缁欏嚭鐩墠鑷涓烘渶濂界殑瑙e喅鏂规硶銆愬浣曚綘鏈夋洿濂界殑瀹炵幇锛岃鐣欒█銆戙€� + +闅忕潃 [getty](https://github.com/alexstocks/getty) 鑻ユ湁鏂扮殑 improvement 鎴栬€呮柊 feature锛屾垜浼氬強鏃惰ˉ鍔犳鏂囥€� + +姝よ銆� + +## 鍙傝€冩枃妗� + +* * * + +* 1 [connect Function with UDP](http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch08lev1sec11.html) +* 2 [娣卞叆Go UDP缂栫▼](http://colobu.com/2016/10/19/Go-UDP-Programming/) \ No newline at end of file diff --git a/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md b/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md new file mode 100644 index 0000000000000000000000000000000000000000..875ea1589699cfd1167e4802825d273ca6fb2b4e --- /dev/null +++ b/doc/md/hessian2/dubbo-go-hessian2-performance-optimization.md @@ -0,0 +1,233 @@ +# 璁颁竴娆″ dubbo-go-hessian2 鐨勬€ц兘浼樺寲 + +2020骞�05鏈�02鏃� + +鐩綍 + +[dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2) 鏄竴涓敤 Go 瀹炵幇鐨� hessian 鍗忚 v2.0 鐗堟湰鐨勫簭鍒楀寲搴撱€備粠椤圭洰鍚嶇О閲屽彲浠ョ湅鍒颁富瑕佺敤鍦� [dubbo-go](http://github.com/apache/dubbo-go) 杩欎釜椤圭洰閲屻€俬essian 鍗忚浣滀负 dubbo 鐨勯粯璁ゅ崗璁紝鍥犳瀵规€ц兘鏈夋瘮杈冮珮鐨勮姹傘€� + +## 绔嬮」 + +璀鏈夌綉鏂� [鍩轰簬 Go 鐨勯┈铚傜獫鏃呮父缃戝垎甯冨紡 IM 绯荤粺鎶€鏈疄璺礭(https://my.oschina.net/u/4231722/blog/3168223) 鎶� dubbo-go 涓庡叾浠� RPC 妗嗘灦瀵规瘮濡備笅锛� + + + +鏈夐壌浜庢锛岀ぞ鍖轰究寮€濮嬬粍缁囬儴鍒嗕汉鍔涳紝鍚姩浜嗗 dubbo-go 鎬ц兘浼樺寲銆愬悓鏃朵篃娆㈣繋涓婃枃浣滆€呭埌閽夐拤缇� 23331795 涓庢垜浠ぞ鍖轰氦娴併€戙€傝€冨療 dubbo-go 鐨勫悇涓粍浠讹紝澶у涓嶇害鑰屽悓鍦板喅瀹氶鍏堜紭鍖栨瘮杈冪嫭绔嬬殑 [dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2)銆� + +## 璧锋 + +鍦ㄦ渶寮€濮嬬殑鏃跺€欙紝骞舵病鏈夊お鎯虫竻妤氶渶瑕佸仛浠€涔堬紝鏀瑰摢涓湴鏂癸紝瑕佷紭鍖栧埌浣曠绋嬪害锛屾墍浠ユ渶绠€鍗曠殑鍔炴硶灏辨槸鐪嬬湅鐜扮姸銆� + +棣栧厛锛屽啓浜嗕竴涓畝鍗曠殑渚嬪瓙锛屾妸甯歌鐨勭被鍨嬪埌涓€涓粨鏋勪綋閲岋紝鐒跺悗娴嬩竴涓嬭€楁椂銆� + +```go +type Mix struct { + A int + B string + CA time.Time + CB int64 + CC string + CD []float64 + D map[string]interface{} +} + +m := Mix{A: int('a'), B: `hello`} +m.CD = []float64{1, 2, 3} +// 鍐嶅姞涓€灞傦紝浣垮緱鏁版嵁鏄惧緱澶嶆潅涓€浜� +m.D = map[string]interface{}{`floats`: m.CD, `A`: m.A, `m`: m} +``` + +> 鐪嬭捣鏉ヨ繖涓粨鏋勪綋璺熺湡瀹炵幆澧冮噷鍙兘涓嶅お涓€鏍凤紝浣嗘槸鐢ㄦ潵鍒嗘瀽鐡堕搴旇鏄冻澶熶簡銆� + +鐒跺悗鐩存帴闈� Go Test 鍐欎釜娴嬭瘯鐢ㄤ緥锛� + +```go +func BenchmarkEncode(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = encodeTarget(&m) + } +} + +func BenchmarkDecode(b *testing.B) { + for i := 0; i < b.N; i++ { + _, _ = NewDecoder(bytes).Decode() + } +} +``` + +> go test -benchmem -run=^$ github.com/apache/dubbo-go-hessian2 -bench "^B" -vet=off -v + +寰楀埌涓嬮潰缁撴灉锛� + +```plain +BenchmarkEncode-8 89461 11485 ns/op 3168 B/op 122 allocs/op +BenchmarkDecode-8 64914 19595 ns/op 7448 B/op 224 allocs/op +``` + +**_娉細鍩轰簬 MacBook Pro 2018銆愪富棰� Intel Core i7 2.6 GHz銆戞祴璇曘€俖** + +涓嶄笌鍚岀被搴撲綔妯悜姣旇緝锛屼粎浠呬粠杩欎釜娴嬭瘯缁撴灉閲岀殑鏁板瓧涓婃棤娉曞緱鍑轰换浣曠粨璁恒€傚鎴戜滑鏉ヨ鏇撮噸瑕佺殑鏄細瀹冨埌搴曟參鍦ㄥ摢閲屻€傞鍏堟兂鍒扮殑鎵嬫渚挎槸锛氬€熷姪 pprof 鐢熸垚鐏劙鍥撅紝瀹氫綅 CPU 娑堣€椼€� + +pprof 宸ュ叿鐨勭敤娉曞彲浠ュ弬鑰冨畼缃戞枃妗c€傛湰鏂囨祴璇曟椂鐩存帴浣跨敤浜� Goland 鍐呯疆 `CPU Profiler` 鐨勬祴璇曞伐鍏凤細娴嬭瘯鍑芥暟宸﹁竟鐨� `Run xx with 'CPU Profiler'`銆� + + + +娴嬭瘯璺戝畬鍚庯紝 Goland 鐩存帴鏄剧ず鐏劙鍥惧涓嬶細 + + + +浠庤繖涓浘閲屽彲浠ョ湅鍒帮紝娴嬭瘯浠g爜澶ф鍗犵敤浜嗗乏杈圭殑 70%锛屽彸杈� 30% 鏄繍琛屾椂鐨勪竴浜涙秷鑰楋紝杩愯鏃堕儴鍒嗕竴鑸寘鎷� gc銆乻chedule 涓ゅぇ鍧楋紝涓€鑸笉鑳界洿鎺ヤ紭鍖栥€傚浘涓婂乏杈瑰彲浠ユ竻鏅板湴鐪嬪埌 `encObject` 閲� `RegisterPOJO` 鍜� `Encode` 鍚勫崰浜嗗皬涓€鍗娿€� + +瀹屾垚搴忓垪鍖栧姛鑳界殑 `Encode` 娑堣€� CPU 濡傛涔嬪灏氬彲鐞嗚В锛岃€岀洿瑙変笂锛屾妸绫诲璞¤繘琛岃В鏋愬拰娉ㄥ唽 `RegisterPOJO` 鏄笉搴旇鎴愪负娑堣€楀ぇ鎴风殑銆傛墍浠ョ寽娴嬭繖涓湴鏂硅涔堟敞鍐屾湁闂锛岃涔堟湁閲嶅娉ㄥ唽銆� + +涓嬩竴姝ュ垎鏋愶紝鐢ㄤ簡涓€涓畝鍗曠殑鍔炴硶锛氬湪杩欎釜鍑芥暟閲屽姞鏃ュ織銆傜劧鍚庣户缁啀璺戜竴涓� benchmark锛屽彲浠ョ湅鍒版€ц兘鐡堕澶勶細瀹瑰櫒璇诲啓鐨勫湴鏂广€� + +鏃㈢劧鐭ラ亾杩欓噷鍋氫簡璁稿閲嶅鐨勬棤鐢ㄥ姛锛屽氨寰堝鏄撴槑纭紭鍖栨柟娉曪細鍔犵紦瀛樸€傛妸宸茬粡瑙f瀽杩囩殑缁撴灉缂撳瓨涓嬫潵锛屼笅娆¢渶瑕佺殑鏃跺€欑洿鎺ュ彇鍑轰娇鐢ㄣ€傛敼杩涘悗鐨勪唬鐮佺畝鍗曞涓嬶細 + +```go +if goName, ok := pojoRegistry.j2g[o.JavaClassName()]; ok { + return pojoRegistry.registry[goName].index +} +``` + +杩欓噷鍒氬紑濮嬫湁涓枒闂紝涓轰粈涔堣鍒嗕袱姝ュ厛鍙� `JavaClassName` 鍐嶅彇 `GoName` 鑰屼笉鐩存帴鍙栧悗鑰咃紵鐪嬭捣鏉ュソ鍍忔槸澶氭涓€涓句簡锛屼絾鍏跺疄 `JavaClassName` 鏄被鐩存帴瀹氫箟鐨勶紝鑰� `GoName` 鍗翠緷璧栦竴娆″弽灏勩€傜浉杈冧箣涓嬩袱娆¤浆鎹㈢殑娑堣€楀彲浠ュ拷鐣ヤ簡銆傛敼瀹屼箣鍚庡啀璺戜竴涓� benchmark锛� + +```plain +BenchmarkEncode-8 197593 5601 ns/op 1771 B/op 51 allocs/op +``` + +闈炲父鎯婅鍦扮湅鍒帮紝鍚炲悙閲忓ぇ姒傛槸鍘熸潵鐨� 200%銆備笌涓婇潰鐨勭伀鐒板浘瀵规瘮锛屽彲浠ョ矖鐣ョ殑璁$畻锛宍RegiserPOJO` 澶ф鍗犱簡鏁翠綋鐨� 30%锛屾敼杩涘悗搴旇涔熷彧鏈夊師鏉ョ殑 `1 / 0.7 * 100% = 140%` 鎵嶅銆傜瓟妗堜篃鍙互鍦ㄧ伀鐒板浘閲屾壘鍒帮細 + + + +闄や簡 `RegisterPOJO` 琚共鎺変互澶栵紝涓庝笂鍥惧姣旓紝杩樻湁鍝簺鍖哄埆鍛紵鍙互鐪嬪埌锛屽師鏉ュ崰鐢ㄥ皢杩� 20% 鐨� `GC` 涔熷嚑涔庣湅涓嶅埌浜嗐€傛墍浠ョ湡瀹炵殑 CPU 鍒╃敤鐜囦篃瑕佸姞涓婅繖閮ㄥ垎鐨勫闀匡紝澶х害 `1 / 0.5 * 100% = 200%`銆� + +> 闇€瑕佹彁閱掔殑鏄紝benchmark 璺戝嚭鏉ョ殑缁撴灉骞朵笉绠楃ǔ瀹氾紝鎵€浠ヤ綘鑷繁鍘嬪嚭鏉ョ殑缁撴灉璺熸垜鐨勫彲鑳戒笉澶竴鑷达紝鐢氳嚦澶氳窇鍑犳鐨勭粨鏋滀篃涓嶅畬鍏ㄤ竴鏍枫€傚浜庝笂闈㈢殑鏁板瓧浣犲彧瑕佺悊瑙e師鍥犲氨濂斤紝涓婁笅娴姩 10% 涔熼兘鏄甯歌寖鍥淬€� 鍙嶈繃鏉ョ湅锛岃繖涔熺畻鏄� GC 浼樺寲鐨勪竴涓搴︺€傜鍒� GC 鍗犵敤 CPU 杩囬珮锛岄櫎浜嗗幓涓€涓釜鎹㈠璞℃睜锛屼篃鍙互閲嶇偣鐪嬬湅閭d簺琚绻佽皟鐢ㄧ殑妯″潡銆傚綋鐒舵洿绉戝鐨勬柟娉曟槸鐪� `pprof heap` / `memory profiler` 銆� + +閽堝杩欎釜缁撴灉锛屽彲浠ョ湅鍒� `encObject` 浠ヤ笂閮借鍒囧壊鎴愪簡涓嶅悓鐨勫皬鏍煎瓙锛屼笉鍐嶆湁鍍� `RegisterPOJO` 閭f牱鐨勫ぇ鍧楀崰鐢紝涓€鑸儏鍐典笅锛屼紭鍖栧埌杩欓噷灏卞彲浠ヤ簡銆� + +鐪嬪畬浜� `Encode` 锛屽啀鏉ョ湅鐪� `Decode` 锛屾柟娉曠被浼硷紝鐩存帴鐪� Goland 鐢熸垚鐨勭伀鐒板浘锛� + + + +杩欎釜鍥炬湁鐐硅糠鎯戞€э紝濂藉儚涔熻鍒嗘垚宸笉澶氱殑灏忔牸瀛愪簡銆傚彲浠ョ偣寮€ `decObject` 杩欎竴灞傦細 + + + +杩欎釜鏃跺€欏師鏉ュ皬鐨� `...` 浼氭樉绀哄叿浣撳唴瀹癸紝闇€瑕佹敞鎰忕殑鏄噷闈㈡湁涓や釜 `findField` 锛屽湪澶嶆潅鐨勮皟鐢ㄩ噷缁忓父浼氶亣鍒拌繖绉嶆儏鍐碉細涓€涓€楄祫婧愮殑鍑芥暟琚垎鍒颁簡璁稿鍑芥暟閲岋紝瀵艰嚧鍦ㄧ湅鐏劙鍥炬椂骞朵笉鑳界洿瑙傚湴鐪嬪埌瀹冨氨鏄摱棰堛€傛瘮杈冨父瑙佺殑鏈夊簭鍒楀寲銆佹棩蹇椼€佺綉缁滆姹傜瓑姣忎釜妯″潡閮戒細骞蹭竴鐐瑰嵈鍙堟病鏈変竴涓叏灞€鐨勫嚱鏁板彧骞蹭粬涓€浠朵簨銆傝繖涓椂鍊欓櫎浜嗚倝鐪煎幓鎵句互澶栦篃鍙互鍊熷姪浜庡彟澶栦竴涓伐鍏凤細 + + + +鍦ㄨ繖涓� `Method List` 閲屽彲浠ユ槑鏄剧湅鍒� `findField` 宸茬粡琚悎骞跺埌涓€璧蜂簡锛屾€诲崰鐢ㄦ帴杩� CPU 鐨勪竴鍗婏紝鐪嬪埌杩欓噷浣犲ぇ姒傚氨鐭ラ亾瀹冨簲璇ユ槸涓紭鍖栫偣浜嗐€� + +## 杩涗竴姝� + +鍑芥暟 `func findField(name string, typ reflect.Type) ([]int, error)` 鐨勪綔鐢ㄦ槸鍦ㄤ竴涓被鍨嬮噷瀵绘壘鎸囧畾灞炴€х殑浣嶇疆锛圛ndex锛屽弽灏勫寘閲岀敤瀹冩潵琛ㄧず鏄鍑犱釜瀛楁锛夈€傚緢瀹规槗鎯冲埌锛屽浜庝竴涓粨鏋勪綋鏉ヨ锛屾瘡涓瓧娈电殑浣嶇疆浠庝竴寮€濮嬪氨纭畾浜嗭紝鎵€浠ョ敤缂撳瓨涓€鏍峰彲浠ヨВ鍐宠繖涓棶棰樸€備竴涓畝鍗曠殑浼樺寲濡備笅锛� + +```go +func findField(name string, typ reflect.Type) (indexes []int, err error) { + typCache, _ := _findFieldCache.LoadOrStore(typ, &sync.Map{}) + indexes, _ := typCache.(*sync.Map).Load(name) + if len(indexes.([]int)) == 0 { + err = perrors.Errorf("failed to find field %s", name) + } + + return indexes.([]int), err + + // ... +} +``` + +```plain +- BenchmarkDecode-8 57723 17987 ns/op 7448 B/op 224 allocs/op ++ BenchmarkDecode-8 82995 12272 ns/op 7224 B/op 126 allocs/op +``` + +鍙互鐪嬪埌锛岀粨鏋滃苟涓嶅棰勬湡鐨勯偅鏍锋彁鍗囦竴鍊嶆晥鏋溿€傝繖涓唬鐮佷箥鐪嬭捣鏉ワ紝濂藉儚闄や簡鏈変竴浜涘暟鍡︾殑鏂█锛屽ソ鍍忎篃娌″埆鐨勪笢瑗夸簡锛屼负浠€涔堝彧鏈� 60% 鐨勬彁鍗囧憿锛屾垜浠繕鏄€熷姪涓嬪伐鍏� + + + +鍙互鐪嬪埌锛氳缂撳瓨鑰楄垂浜� 7% 鐨勮祫婧愩€傚叾涓紝`sync.(*Map)` 涓嶄究浼樺寲锛屼絾 `newobejct` 鏄摢閲屾潵鐨勫憿锛熶唬鐮侀噷鍙互鐪嬪埌锛屽敮涓€瀹氫箟鏂板璞$殑鍦版柟灏辨槸鍑芥暟绗竴琛岀殑 `&sync.Map` 锛屾垜鎶辩潃璇曚竴璇曠殑蹇冩€佹妸 `LoadOrStore` 鎷嗘垚浜嗕袱姝� + +```go +typCache, ok := _findFieldCache.Load(typ) +if !ok { + typCache = &sync.Map{} + _findFieldCache.Store(typ, typCache) +} +``` + +```plain +- BenchmarkDecode-8 82995 12272 ns/op 7224 B/op 126 allocs/op ++BenchmarkDecode-8 103876 12385 ns/op 6568 B/op 112 allocs/op +``` + +鐪嬬粨鏋滐紝鐫€瀹炲嚭涔庢剰鏂欍€傛兂璧锋潵浠ュ墠鐪� Java 浠g爜鏃剁粡甯哥鍒拌繖鏍风殑浠g爜锛� + +```go +if ( logLevel == `info` ) { + log.Info(...) +} +``` + +浠ュ墠涓€鐩磋寰楄繖涓� `if` 鐪熸槸娴垂鎰熸儏锛岀幇鍦ㄦ兂鏉ワ紝鍒槸涓€鐣鐭ヤ簡銆傚鏋滆兘鎻愪緵涓€涓� `LoadOrStore(key, func() interface{})` 鐨勬柟娉曪紝 浼氫笉浼氭洿濂戒竴浜涳紵 鍒拌繖閲岀殑璇濓紝鎴戜滑鍋氫簡涓や釜姣旇緝澶х殑浼樺寲锛屾暣浣撴€ц兘澶х害鎻愬崌浜嗕竴鍊嶃€傚鏋滀粩缁嗙湅鐏劙鍥撅紝杩樹細鍙戠幇鏈夊緢澶氬皬鐨勪紭鍖栫偣锛屼絾鏄敱浜庢病鏈変粈涔堢壒鍒川鐨勯璺冿紝杩欓噷涓嶅啀璧樿堪銆傛湁鍏磋叮鐨勫皬浼欎即鍙互鍒� [PR Imp: cache in reflection](https://github.com/apache/dubbo-go-hessian2/pull/179) 閲岄槄璇荤浉鍏崇殑璁ㄨ銆� + +## 鏇磋繘涓€姝� + +浼樺寲鍒版锛屼緷鐒惰棌鐫€涓€涓洿娣卞眰娆$殑闂锛氭壘涓€涓彲闈犵殑鍙傝€冨熀鍑嗭紝浠ヨ 閲忕洰鍓嶇殑宸ヤ綔缁撴灉銆愭瘯绔熸病鏈夊姣斿氨娌℃湁浼ゅ銆戙€備竴涓緢瀹规槗鎯冲埌鐨勬瘮杈冨璞℃槸 Go 璇█瀹樻柟鐨� `json` 鏍囧噯搴撱€� + +鎶� [dubbo-go-hessian2](https://github.com/apache/dubbo-go-hessian2) 涓� `json` 鏍囧噯搴撳仛姣旇緝濡備笅锛� + +```shell +$ go test -benchmem -run=^$ github.com/apache/dubbo-go-hessian2 -bench "^B" -vet=off -v -count=5 +goos: darwin +goarch: amd64 +pkg: github.com/apache/dubbo-go-hessian2 +BenchmarkJsonEncode +BenchmarkJsonEncode-8 249114 4719 ns/op 832 B/op 15 allocs/op +BenchmarkJsonEncode-8 252224 4862 ns/op 832 B/op 15 allocs/op +BenchmarkJsonEncode-8 240582 4739 ns/op 832 B/op 15 allocs/op +BenchmarkJsonEncode-8 213283 4784 ns/op 832 B/op 15 allocs/op +BenchmarkJsonEncode-8 227101 4665 ns/op 832 B/op 15 allocs/op +BenchmarkEncode +BenchmarkEncode-8 182184 5615 ns/op 1771 B/op 51 allocs/op +BenchmarkEncode-8 183007 5565 ns/op 1771 B/op 51 allocs/op +BenchmarkEncode-8 218664 5593 ns/op 1771 B/op 51 allocs/op +BenchmarkEncode-8 214704 5886 ns/op 1770 B/op 51 allocs/op +BenchmarkEncode-8 181861 5605 ns/op 1770 B/op 51 allocs/op +BenchmarkJsonDecode +BenchmarkJsonDecode-8 123667 8412 ns/op 1776 B/op 51 allocs/op +BenchmarkJsonDecode-8 122796 8497 ns/op 1776 B/op 51 allocs/op +BenchmarkJsonDecode-8 132103 8471 ns/op 1776 B/op 51 allocs/op +BenchmarkJsonDecode-8 130687 8492 ns/op 1776 B/op 51 allocs/op +BenchmarkJsonDecode-8 127668 8476 ns/op 1776 B/op 51 allocs/op +BenchmarkDecode +BenchmarkDecode-8 107775 10092 ns/op 6424 B/op 98 allocs/op +BenchmarkDecode-8 110996 9950 ns/op 6424 B/op 98 allocs/op +BenchmarkDecode-8 111036 10760 ns/op 6424 B/op 98 allocs/op +BenchmarkDecode-8 113151 10063 ns/op 6424 B/op 98 allocs/op +BenchmarkDecode-8 109197 10002 ns/op 6424 B/op 98 allocs/op +PASS +ok github.com/apache/dubbo-go-hessian2 28.680s +``` + +铏界劧姣忔鐨勭粨鏋滀笉绋冲畾锛屼絾灏辨暣浣撹€岃█锛岀洰鍓嶇殑搴忓垪鍖栧拰鍙嶅簭鍒楀寲鎬ц兘澶ф閮芥槸 JSON 鏍囧噯搴撶殑 85% 宸﹀彸銆傝繖涓垚缁╁苟涓嶈兘璇村ソ锛屼絾鐭湡鍐呰兘鑺� 20 鍒嗙殑绮惧姏寰楀埌涓€涓� 80 鍒嗙殑缁撴灉锛屽簲璇ヤ篃鏄彲浠ユ帴鍙楃殑銆傝嚦浜庡墿涓嬬殑 20%锛屽氨涓嶆槸闈犳敼鍑犺浠g爜灏辫兘鎼炲畾浜嗐€傚唴瀛樺垎閰嶆槸鍚﹀悎鐞嗐€佹墽琛屾祦绋嬫槸鍚︽湁鍐椾綑锛岄兘鏄渶瑕佷竴鐐逛竴婊村湴鍘绘敼杩涖€� + +## 鎬荤粨 + +鏈€鍚庯紝鎴戜滑鏉ユ€荤粨涓€涓嬫湰鏂囦富瑕佺殑浼樺寲姝ラ锛� + +* 鍒╃敤鐏劙鍥� 蹇€熷畾浣嶆秷鑰� CPU 杈冮珮鐨勬ā鍧楋紱 +* 鍒╃敤缂撳瓨鏈哄埗锛屽揩閫熸秷闄ら噸澶嶇殑璁$畻锛� +* 鍒╃敤 CallTree銆丮ethodList 绛夊绉嶅伐鍏峰垎鏋愬皬娈典唬鐮佺殑绮剧‘娑堣€楋紱 +* 閬靛惊浜屽叓瀹氬緥锛屼互鏈€灏忕殑鎴愭湰鍋氬嚭涓€涓晥鏋滄樉钁楃殑鏀剁泭銆� + +### 娆㈣繋鍔犲叆 dubbo-go 绀惧尯 + +鐩墠 dubbo-go 宸茬粡鍒颁簡涓€涓瘮杈冪ǔ瀹氭垚鐔熺殑鐘舵€併€傚湪鎺ヤ笅鏉ョ殑鐗堟湰閲岄潰锛屾垜浠皢闆嗕腑绮惧姏鍦ㄤ簯鍘熺敓涓娿€備笅涓€涓増鏈紝鎴戜滑灏嗛鍏堝疄鐜板簲鐢ㄧ淮搴︾殑鏈嶅姟娉ㄥ唽锛岃繖鏄竴涓拰鐜版湁娉ㄥ唽妯″瀷瀹屽叏涓嶅悓鐨勬柊鐨勬敞鍐屾ā鍨嬨€備篃鏄垜浠湞鐫€浜戝師鐢熷姫鍔涚殑涓€涓叧閿増鏈€� + +dubbo-go 閽夐拤缇� **23331795** 娆㈣繋浣犵殑鍔犲叆銆� + +## 浣滆€呬俊鎭� + +寮犳収浠侊紝github id micln锛屼换鑱� 寰楀埌 APP 鍚庣寮€鍙戙€� \ No newline at end of file diff --git a/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md new file mode 100644 index 0000000000000000000000000000000000000000..61589e6b64a0c3ebf957403243c9d0b6ec56cce1 --- /dev/null +++ b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.6.0.md @@ -0,0 +1,64 @@ +# [What's new in Dubbo-go-hessian2 v1.6.0](https://my.oschina.net/dubbogo/blog/4318016) + +鍙戠増浜猴細[鏈涘摜](https://github.com/wongoo) + +## 1\. 澧炲姞缂撳瓨浼樺寲 + +dubbo-go-hessian2 鍦ㄨВ鏋愭暟鎹殑鏁版嵁澶ч噺浣跨敤鍒颁簡 struct 鐨勭粨鏋勪俊鎭紝杩欓儴鍒嗕俊鎭彲浠ョ紦瀛樿捣鏉ュ弽澶嶅埄鐢紝浣垮緱鎬ц兘鎻愬崌浜嗕竴鍊嶃€備紭鍖栬繃绋嬭褰曞彲浠ヨ缁嗛槄璇籟銆婅涓€娆″ dubbo-go-hessian2 鐨勬€ц兘浼樺寲銆媇(https://mp.weixin.qq.com/s/ouVxldQAt0_4BET7srjJ6Q). + +瀵瑰簲 pr [#179](https://github.com/apache/dubbo-go-hessian2/pull/179)锛屼綔鑰� [micln](https://github.com/micln)銆� + +## 2\. string 瑙f瀽鎬ц兘浼樺寲 + +鐢变簬 hessian 锛� dubbo 搴忓垪鍖栧崗璁紝涓嬬О锛歨essian 锛夊 string 鐨勫畾涔夋槸16 bit 鐨� unicode 鐨� UTF-8 琛ㄧず褰㈠紡锛屽瓧绗﹂暱搴﹁〃绀烘槸16 bit 鐨勫瓧绗︽暟銆傝繖鏄粎閽堝 java 鍒跺畾鐨勮鑼冿紝java 涓竴涓瓧绗︽槸16 bit锛屽搴斿埌 UTF-16. hessian 搴撲篃鏄姣忎竴涓瓧绗﹁繘琛岃浆鐮佸簭鍒楀寲銆備絾 golang 閲岄潰瀛楃鏄拰 UTF-8 瀵瑰簲鐨勶紝dubbo-go-hessian2 閲岄潰鐨� rune 鏄� 32bit锛屽拰 unicode涓€涓€鏄犲皠銆傚浜� U+10000 ~ U+10FFFF 鐨勫瓧绗︼紝闇€鎸夌収 UTF16 鐨勮鑼冿紝灏嗗瓧绗﹁浆鎹负 2 涓瓧鑺傜殑浠g悊瀛楃锛屽啀鍋氳浆鎹紝鎵嶈兘鍜� java 鐨勫簭鍒楀寲鏂瑰紡瀵瑰簲璧锋潵銆� + +鍘熸潵涓嶇鏄紪鐮佽繕鏄В鏋愰兘鏄竴涓瓧绗︿竴涓瓧绗﹀鐞嗭紝鐗瑰埆鏄В鏋愮殑鏃跺€欙紝浠庢祦閲岄潰涓€涓瓧鑺備竴涓瓧鑺傝鍙栧苟缁勮鎴� rune锛岀劧鍚庡啀杞崲涓� string锛岃繖鏍锋晥鐜囩壒鍒綆銆傛垜浠殑浼樺寲鏂规鏄紝鎵规璇诲彇瀛楄妭娴佸埌 buffer 涓紝瀵� buffer 杩涜瑙f瀽杞负 UTF-8 鏁扮粍锛屽苟缁熻瀛楃鏁伴噺銆傚叾涓渶瑕佸浠g悊瀵瑰瓧绗﹀皢鍏惰浆鎹负鏍囧噯 UTF-8 瀛愯妭鏁扮粍銆傚鏋滅粺璁$殑瀛楃鏁伴噺涓嶈冻锛屽啀杩涗竴姝ヨ鍙栨祦绉嶇殑鏁版嵁杩涜瑙f瀽銆傞€氳繃姝ゆ柟寮忔彁鍗囦竴鍊嶇殑瑙f瀽鏁堢巼銆� + +瀵瑰簲 pr [#188](https://github.com/apache/dubbo-go-hessian2/pull/188)锛屼綔鑰� [zonghaishang](https://github.com/zonghaishang)銆� + +## 3\. 瑙f瀽蹇界暐涓嶅瓨鍦ㄧ殑瀛楁 + +hessian 搴撳湪瑙f瀽鏁版嵁鐨勬椂鍊欙紝瀵逛簬涓€涓� class 瀛楁锛屽鏋滀笉瀛樺湪锛屽垯鐩存帴蹇界暐鎺夈€備絾 v1.6.0 鐗堟湰涔嬪墠 dubbo-go-hessian2 瑙f瀽鏁版嵁锛屽鏋滈亣鍒颁笉瀛樺湪鐨勫瓧娈碉紝浼氳繑鍥� error銆備粠 v1.6.0 寮€濮嬶紝涓� hessian 涓€鏍凤紝蹇界暐涓嶅瓨鍦ㄧ殑瀛楁銆�**鍥犱负杩欐槸涓€涓壒鎬х殑鍙樻洿锛屾墍浠ュ崌绾х殑鍚屽涓€瀹氳娉ㄦ剰浜嗐€�** + +瀵瑰簲 pr [#201](https://github.com/apache/dubbo-go-hessian2/pull/201)锛屼綔鑰� [micln](https://github.com/micln) & [fangyincheng](https://github.com/fangyincheng)銆� + +## 4\. 瑙e喅娴偣鏁扮簿搴︿涪澶遍棶棰� + +鍦ㄥ float32 绫诲瀷杩涜搴忓垪鍖栨椂锛屾垜浠竴寰嬪己鍒惰浆鎹负 float64 鍐嶈繘琛屽簭鍒楀寲鎿嶄綔銆傜敱浜庢诞鐐规暟鐨勭簿搴﹂棶棰橈紝鍦ㄨ繖涓浆鎹㈣繃绋嬩腑鍙兘鍑虹幇灏忔暟鐐瑰悗鍑虹幇澶氫綑鐨勫熬鏁帮紝渚嬪 (float32)99.8-->(float64)99.80000305175781銆� + +1.6.0 鐗堟湰瀵� float32 鐨勫簭鍒楀寲杩涜浜嗕紭鍖栵細 + +* 濡傛灉灏忔暟灏炬暟灏忎簬 3 浣嶏紝鏍规嵁 hessian2 鍗忚搴忓垪鍖栦负 double 32-bit 鏍煎紡 +* 鍚﹀垯鍏堣浆鎹负 string 绫诲瀷锛屽啀杞崲涓� float64 绫诲瀷锛岃繖鏍峰仛鍙互閬垮厤鐢变簬娴偣鏁扮簿搴﹂棶棰樹骇鐢熷浣欑殑灏炬暟锛屾渶鍚庡 float64 杩涜搴忓垪鍖栥€� + +铏界劧瀵� float32 绫诲瀷杩涜浜嗕紭鍖栵紝浣嗘槸渚濈劧寤鸿浣跨敤娴偣鏁扮殑鏃跺€欎紭鍏堜娇鐢� float64 绫诲瀷銆� + +瀵瑰簲 pr [#196](https://github.com/apache/dubbo-go-hessian2/pull/196)锛屼綔鑰� [willson-chen](https://github.com/willson-chen)銆� + +## 5\. 瑙e喅 attachment 绌哄€间涪澶遍棶棰� + +dubbo 璇锋眰涓寘鍚� attachment 淇℃伅锛屼箣鍓嶅鏋� attachment 閲岄潰鍚湁濡� `"key1":""`锛岃繖绉� value 涓虹┖鐨勬儏鍐碉紝瑙f瀽鍑烘潵鐨勭粨鏋滀細鐩存帴涓㈠け杩欎釜灞炴€� key1 锛寁1.6.0 淇浜嗘闂锛岀幇鍦ㄨВ鏋愬嚭鏉ョ殑 attachment 浼氭纭В鏋愬嚭绌� value 鐨勫睘鎬с€� + +瀵瑰簲 pr [#191](https://github.com/apache/dubbo-go-hessian2/pull/191)锛屼綔鑰� [champly](https://github.com/champly)銆� + +## 6\. 鏀寔 鈥樼户鎵库€� 鍜屽拷鐣ュ啑浣欏瓧娈� + +鐢变簬 go 娌℃湁缁ф壙鐨勬蹇碉紝鎵€浠ュ湪涔嬪墠鐨勭増鏈紝Java 鐖剁被鐨勫瓧娈典笉琚� dubbo-go-hessian2 鎵€鏀寔銆傛柊鐗堟湰涓紝dubbo-go-hessian2 灏咼ava鏉ヨ嚜鐖剁被鐨勫瓧娈电敤鍖垮悕缁撴瀯浣撳搴旓紝濡傦細 + +```rust +type Dog struct { + Animal + Gender string + DogName string `hessian:"-"` +} +``` + +鍚屾椂锛屽氨鍍� json 缂栫爜涓€氳繃 immediately 鍙互鍦ㄥ簭鍒楀寲涓拷鐣ヨ瀛楁锛屽悓鐞嗭紝閫氳繃 hessian:"-" 鐢ㄦ埛涔熷彲浠ヨ鍐椾綑瀛楁涓嶅弬涓� hessian 搴忓垪鍖栥€� + +瀵瑰簲pr [#154](https://github.com/apache/dubbo-go-hessian2/pull/154)锛屼綔鑰� [micln](https://github.com/micln) + +## 娆㈣繋鍔犲叆 dubbo-go 绀惧尯 + +閽夐拤缇�: **23331795** + +[github](https://www.oschina.net/p/github)[apache](https://www.oschina.net/p/apache+http+server)[java](https://www.oschina.net/p/java) \ No newline at end of file diff --git a/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md new file mode 100644 index 0000000000000000000000000000000000000000..5e8ce0bb47d0b98c37c6b5367f207dcc8c6862f3 --- /dev/null +++ b/doc/md/hessian2/what's-new-in-dubbo-go-hessian2-v1.7.0.md @@ -0,0 +1,268 @@ +# [Dubbo-go-hessian2 v1.7.0 鍙戝竷](https://www.oschina.net/news/118648/dubbogo-hessian2-1-7-0-released) + +Dubbo-go-hessian2 v1.7.0宸插彂甯冿紝璇﹁聽[https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0锛宂(https://github.com/apache/dubbo-go-hessian2/releases/tag/v1.7.0%EF%BC%8C)聽浠ヤ笅瀵硅繖娆℃洿鏂板唴瀹硅繘琛岃缁嗘暣鐞嗐€� + +鍙﹀v1.6.3 灏� attachment 绫诲瀷鐢� map\[string\]stiring 鏀逛负map\[string\]interface{} 瀵艰嚧鐗堟湰涓嶅吋瀹归棶棰橈紝杩欓儴鍒嗗凡杩樺師锛屽悗缁殑璁″垝鏄皢dubbo鍗忚鐨剅equest/response瀵硅薄鏁翠綋杩佺Щ鍒癲ubbogo椤圭洰涓繘琛岃凯浠d慨鏀癸紝 hessian2涓皢涓嶅啀鏀瑰姩鍒皉equest/response瀵硅薄銆� + +## 1\. New Features + +### 1.1 add GetStackTrace method into Throwabler and its implements.聽[#207](https://github.com/apache/dubbo-go-hessian2/pull/207) + +> contributed by聽[https://github.com/cvictory](https://github.com/cvictory) + +go璇█client璇锋眰java璇█鏈嶅姟鏃讹紝濡傛灉java璇█鎶涘嚭浜嗗紓甯革紝寮傚父瀵瑰簲鐨勫爢鏍堜俊鎭槸琚繚瀛樺湪StackTraceElement涓€� + +杩欎釜寮傚父淇℃伅鍦ㄦ棩蹇椾腑鏈€濂借兘琚墦鍗板嚭鏉ワ紝浠ユ柟渚垮鎴风鎺掓煡闂锛屾墍浠ュ湪Throwabler鍜屽搴斿瓙绫讳腑澧炲姞浜哠tackTraceElement鐨勮幏鍙栥€� + +娉細鍏跺疄杩樻湁涓€绉嶆洿濂界殑鏂规硶锛屾墍鏈夌殑鍏蜂綋鐨勫紓甯哥被鍨嬮兘鍖呭惈java\_exception/exception.go鐨凾hrowable struct銆傝繖鏍峰彧闇€瑕佸湪Throwable涓鍔燝etStackTrace鏂规硶灏卞彲浠ヤ簡銆備絾鏄繖绉嶆柟寮忛渶瑕佹洿澶氱殑娴嬭瘯楠岃瘉锛屾敼鍔ㄧ殑閫昏緫鐩稿浼氬鏉備竴浜涖€備絾鏄唬鐮佷細鏇存暣娲併€� 杩欓噷鍏堜笉鐢ㄨ繖绉嶆柟娉曘€� + +### 1.2 catch user defined exceptions.聽[#208](https://github.com/apache/dubbo-go-hessian2/pull/208) + +> contributed by聽[https://github.com/cvictory](https://github.com/cvictory) + +golang涓鍔犱竴涓猨ava涓璄xception瀵硅薄鐨勫簭鍒楀寲杈撳嚭鏂规硶锛� + +```css +func JavaException() []byte { + e := hessian.NewEncoder() + exception := java_exception.NewException("java_exception") + e.Encode(exception) + return e.Buffer() +} +``` + +鍦╫utput/output.go 鎻愪緵璋冪敤鍏ュ彛:娣诲姞濡備笅鍑芥暟鍒濆鍖栧0鏄� + +```plain +func init() { + funcMap["JavaException"] = testfuncs.JavaException +} +``` + +java浠g爜涓鍔犺皟鐢╣o鏂规硶搴忓垪鍖栫粨鏋�:聽**璇存槑**: Assert.assertEquals 涓嶈兘鐩存帴姣旇緝Exception瀵硅薄鏄惁鐩哥瓑 + +```php + /** + * test java java.lang.Exception object and go java_exception Exception struct + */ + @Test + public void testException() { + Exception exception = new Exception("java_exception"); + Object javaException = GoTestUtil.readGoObject("JavaException"); + if (javaException instanceof Exception) { + Assert.assertEquals(exception.getMessage(), ((Exception) javaException).getMessage()); + } + } +``` + +### 1.3 support java8 time object.聽[#212](https://github.com/apache/dubbo-go-hessian2/pull/212),聽[#221](https://github.com/apache/dubbo-go-hessian2/pull/221) + +> contributed by聽[https://github.com/willson-chen](https://github.com/willson-chen),聽[https://github.com/cyb-code](https://github.com/cyb-code) + +golang涓鍔犱竴涓猨ava8瀵硅薄鐨勫簭鍒楀寲杈撳嚭鏂规硶锛� + +```go +// test java8 java.time.Year +func Java8TimeYear() []byte { + e := hessian.NewEncoder() + year := java8_time.Year{Year: 2020} + e.Encode(year) + return e.Buffer() +} + +// test java8 java.time.LocalDate +func Java8LocalDate() []byte { + e := hessian.NewEncoder() + date := java8_time.LocalDate{Year: 2020, Month: 9, Day: 12} + e.Encode(date) + return e.Buffer() +} +``` + +鍦╫utput/output.go 鎻愪緵璋冪敤鍏ュ彛:娣诲姞鍑芥暟鍒濆鍖栧0鏄� + +```plain +func init() { + funcMap["Java8TimeYear"] = testfuncs.Java8TimeYear + funcMap["Java8LocalDate"] = testfuncs.Java8LocalDate +} +``` + +java浠g爜涓鍔犺皟鐢╣o鏂规硶搴忓垪鍖栫粨鏋�: + +```java +/** + * test java8 java.time.* object and go java8_time/* struct + */ +@Test +public void testJava8Year() { + Year year = Year.of(2020); + Assert.assertEquals(year + , GoTestUtil.readGoObject("Java8TimeYear")); + LocalDate localDate = LocalDate.of(2020, 9, 12); + Assert.assertEquals(localDate, GoTestUtil.readGoObject("Java8LocalDate")); +} +``` + +### 1.4 support test golang encoding data in java.聽[#213](https://github.com/apache/dubbo-go-hessian2/pull/213) + +> contributed by聽[https://github.com/wongoo](https://github.com/wongoo) + +涓轰簡鏇村ソ鐨勬祴璇曢獙璇乭essian搴擄紝鍘熸潵宸茬粡鏀寔鍦╣olang涓祴璇昷ava鐨勫簭鍒楀寲鏁版嵁锛岀幇鍦ㄥ鍔犲湪java涓祴璇昰olang鐨勫簭鍒楀寲鏁版嵁锛屽疄鐜板弻鍚戞祴璇曢獙璇併€� + +golang涓鍔犲簭鍒楀寲杈撳嚭鏂规硶: + +```css +func HelloWorldString() []byte { + e := hessian.NewEncoder() + e.Encode("hello world") + return e.Buffer() +} +``` + +灏嗚鏂规硶娉ㄥ唽鍒皁utput/output.go涓� + +```plain + // add all output func here + func init() { + funcMap["HelloWorldString"] = testfuncs.HelloWorldString +} +``` + +output/output.go 鎻愪緵璋冪敤鍏ュ彛: + +```go +func main() { + flag.Parse() + + if *funcName == "" { + _, _ = fmt.Fprintln(os.Stderr, "func name required") + os.Exit(1) + } + f, exist := funcMap[*funcName] + if !exist { + _, _ = fmt.Fprintln(os.Stderr, "func name not exist: ", *funcName) + os.Exit(1) + } + + defer func() { + if err := recover(); err != nil { + _, _ = fmt.Fprintln(os.Stderr, "error: ", err) + os.Exit(1) + } + }() + if _, err := os.Stdout.Write(f()); err != nil { + _, _ = fmt.Fprintln(os.Stderr, "call error: ", err) + os.Exit(1) + } + os.Exit(0) +} +``` + +java浠g爜涓鍔犺皟鐢╣o鏂规硶搴忓垪鍖栫粨鏋�: + +```plain +public class GoTestUtil { + + public static Object readGoObject(String func) { + System.out.println("read go data: " + func); + try { + Process process = Runtime.getRuntime() + .exec("go run output/output.go -func_name=" + func, + null, + new File("..")); + + int exitValue = process.waitFor(); + if (exitValue != 0) { + Assert.fail(readString(process.getErrorStream())); + return null; + } + + InputStream is = process.getInputStream(); + Hessian2Input input = new Hessian2Input(is); + return input.readObject(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + private static String readString(InputStream in) throws IOException { + StringBuilder out = new StringBuilder(); + InputStreamReader reader = new InputStreamReader(in, StandardCharsets.UTF_8); + char[] buffer = new char[4096]; + + int bytesRead; + while ((bytesRead = reader.read(buffer)) != -1) { + out.append(buffer, 0, bytesRead); + } + + return out.toString(); + } +} +``` + +澧炲姞java娴嬭瘯浠g爜: + +```java +@Test +public void testHelloWordString() { + Assert.assertEquals("hello world" + , GoTestUtil.readGoObject("HelloWorldString")); +} +``` + +### 1.5 support java.sql.Time & java.sql.Date.聽[#219](https://github.com/apache/dubbo-go-hessian2/pull/219) + +> contributed by聽[https://github.com/zhangshen023](https://github.com/zhangshen023) + +澧炲姞浜� java 绫� java.sql.Time, java.sql.Date 鏀寔锛屽垎鍒搴斿埌hessian.Time 鍜� hessian.Date锛� 璇﹁聽[https://github.com/apache/dubbo-go-hessian2/pull/219/files銆俔(https://github.com/apache/dubbo-go-hessian2/pull/219/files%E3%80%82) + +## 2\. Enhancement + +### 2.1 Export function EncNull.聽[#225](https://github.com/apache/dubbo-go-hessian2/pull/225) + +> contributed by聽[https://github.com/cvictory](https://github.com/cvictory) + +寮€鏀� hessian.EncNull 鏂规硶锛屼互渚跨敤鎴风壒瀹氭儏鍐典笅浣跨敤銆� + +## 3\. Bugfixes + +### 3.1 fix enum encode error in request.聽[#203](https://github.com/apache/dubbo-go-hessian2/pull/203) + +> contributed by聽[https://github.com/pantianying](https://github.com/pantianying) + +鍘熸潵鍦� dubbo request 瀵硅薄涓病鏈夊垽鏂� enum 绫诲瀷鐨勬儏鍐碉紝姝r澧炲姞浜嗗垽鏂槸涓嶆槸POJOEnum绫诲瀷銆傝瑙伮燵https://github.com/apache/dubbo-go-hessian2/pull/203/files](https://github.com/apache/dubbo-go-hessian2/pull/203/files) + +### 3.2 fix \[\]byte field decoding issue.聽[#216](https://github.com/apache/dubbo-go-hessian2/pull/216) + +> contributed by聽[https://github.com/wongoo](https://github.com/wongoo) + +v1.7.0 涔嬪墠濡傛灉 struct涓寘鍚玕[\]byte瀛楁鏃舵棤娉曞弽搴忓垪鍖�, 鎶ラ敊鈥渆rror list tag: 0x29鈥濓紝涓昏鍘熷洜鏄褰撳仛list杩涜澶勭悊锛屽浜庤繖绉嶆儏鍐靛簲璇ユ寜鐓inary鏁版嵁杩涜澶勭悊鍗冲彲銆� + +```go +type Circular struct { + Num int + Previous *Circular + Next *Circular + ResponseDataBytes []byte // <---- +} + +func (Circular) JavaClassName() string { + return "com.company.Circular" +} +``` + +### 3.3 fix decoding error for map in map.聽[#229](https://github.com/apache/dubbo-go-hessian2/pull/229) + +> contributed by聽[https://github.com/wongoo](https://github.com/wongoo) + +v1.7.0 涔嬪墠宓屽map鏃犳硶姝g‘瑙f瀽锛屼富瑕佸師鍥犳槸瀵瑰簲鐨刴ap瀵硅薄琚綋鍋氫竴涓暟鎹被鍨嬪嵈鏈鑷姩鍔犲埌绫诲紩鐢ㄥ垪琛ㄤ腑锛岃€屽祵濂梞ap绫讳俊鎭槸鍚屼竴绫诲瀷鐨勫紩鐢紝鍘荤被寮曠敤鍒楄〃鎵撅紝鎵句笉鍒板氨鎶ラ敊浜嗐€� 瑙e喅杩欎釜闂鐨勬柟娉曞氨鏄亣鍒癿ap绫诲璞★紝涔熷皢鍏跺姞鍏ュ埌绫诲紩鐢ㄥ垪琛ㄤ腑鍗冲彲銆� 闂璇︾粏鍙傝€兟燵#119](https://github.com/apache/dubbo-go-hessian2/issues/119). + +### 3.4 fix fields name mismatch in Duration class.聽[#234](https://github.com/apache/dubbo-go-hessian2/pull/234) + +> contributed by聽[https://github.com/skyao](https://github.com/skyao) + +杩欎釜 PR 瑙e喅浜咲uration瀵硅薄涓瓧娈甸敊璇畾涔夛紝鍘熸潵鏄�"second/nano"锛� 搴旇鏄�"seconds/nanos"銆� + +鍚屾椂鏀瑰杽浜嗘祴璇曢獙璇佹暟鎹€備箣鍓嶄娇鐢�0浣滀负int瀛楁鐨勬祴璇曟暟鎹紝杩欐槸涓嶅噯纭殑锛屽洜涓篿nt绫诲瀷榛樿鍊煎氨鏄�0. \ No newline at end of file diff --git a/doc/md/interview/dubbo-go-published.md b/doc/md/interview/dubbo-go-published.md new file mode 100644 index 0000000000000000000000000000000000000000..cb2848dee732fafe1141b3a3064f1da8d9b662be --- /dev/null +++ b/doc/md/interview/dubbo-go-published.md @@ -0,0 +1,157 @@ +# [鍐蹭笂浜戝師鐢燂紝Dubbo 鍙戝竷 Go 鐗堟湰](https://www.oschina.net/question/3820517_2306822) + +[h4cd](https://my.oschina.net/u/3820517) 鍙戝竷浜� 2019/06/02 22:36 + +5 鏈� 21 鏃ワ紝缁忚繃涓€骞村鐨勫鍖栵紝Apache Dubbo 浠� Apache 杞欢鍩洪噾浼氭瘯涓氾紝鎴愪负 Apache 椤剁骇椤圭洰銆� + + + +Dubbo 鏄樋閲屼簬 2011 骞村紑婧愮殑涓€娆鹃珮鎬ц兘 RPC 妗嗘灦锛屽湪 Java 鐢熸€佷腑鍏锋湁涓嶅皬鐨勫奖鍝嶅姏銆傚綋鍒濈粡鍘嗚繃涓€娈佃澶栫晫璇熺梾鐨勨€滃仠姝㈢淮鎶も€濈伆鏆楁椂鍏夛紝鍚庢潵鍦� 2017 骞� Dubbo 娴瓙鍥炲ご锛屽畼鏂瑰甯冮噸鏂伴噸鐐圭淮鎶ゃ€� + +閲嶆柊鍚埅鐨� Dubbo 灏嗛瑕佺洰鏍囧畾浣嶄簬閲嶆柊婵€娲荤ぞ鍖猴紝璧㈠洖寮€鍙戣€呯殑淇′换锛屽苟涓旈€愭笎灏� Dubbo 鎵撻€犳垚涓€涓浗闄呭寲涓庣幇浠e寲鐨勯」鐩紝鐩墠璺濈瀹e竷閲嶅惎宸茬粡杩囦簡涓€骞村崐鐨勬椂闂淬€� + +鍦ㄨ繖涓繃绋嬩腑锛孌ubbo 鍙戝竷浜嗗涓増鏈紝骞堕€愭笎浠庝竴涓� RPC 妗嗘灦鍚戝井鏈嶅姟鐢熸€佺郴缁熻浆鍙橈紝18 骞村勾鍒� Dubbo 鍏ラ┗ Apache 杞欢鍩洪噾浼氬鍖栧櫒锛屽紑濮嬩互 Apache 涔嬮亾鍙戝睍绀惧尯銆� + +涓€骞翠箣鍚庯紝Dubbo 鍦� Apache 瀛靛寲鍣ㄤ腑鍙戝竷浜嗛噸鍚淮鎶や互鏉ョ殑棣栦釜閲岀▼纰戠増鏈� 2.7.0锛屾坊鍔犱簡绀惧尯鍛煎0寰堥珮鐨勫紓姝ュ寲鏀寔锛屼互鍙婃敞鍐屼腑蹇冧笌閰嶇疆涓績鍒嗙绛夌壒鎬с€� + +杩欐湡闂� Dubbo 3.0 鐨勫紑鍙戝伐浣滀篃琚彁涓婁簡鏃ョ▼锛屼粖骞� 4 鏈堜腑鏃紝瀹樻柟姝e紡鍏竷浜� Dubbo 3.0 鐨勮繘搴︼紝姝ょ増鏈柊鐗规€у寘鎷敮鎸� Filter 閾剧殑寮傛鍖栥€佸搷搴斿紡缂栫▼銆佷簯鍘熺敓/Service Mesh 鏂瑰悜鐨勬帰绱紝浠ュ強涓庨樋閲屽唴澶栬瀺鍚堛€� + +鐒跺悗锛孌ubbo 姣曚笟浜嗐€�**姣曚笟鍚庣殑 Dubbo 杩戞湡鏈変粈涔堟秷鎭憿锛�**鐢熸€佽繕鍦ㄥ彂灞曪紝Dubbo 绀惧尯鍦ㄥ墠鍑犳棩鍏紑浜� [Dubbo Roadmap 2019](https://github.com/dubbo/awesome-dubbo/blob/master/slides/meetup/201905@beijing/DUBBO%20ROADMAP%202019.pdf)锛岃鍒掑湪 2020 骞� 2 鏈堜唤鍙戝竷 Dubbo 3.0 姝e紡鐗堬紝鎰熷叴瓒g殑鍚屽鍙互璇︾粏鏌ラ槄銆� + + + +鑰屾渶杩戝畼鏂瑰張**瀹e竷 Go 璇█鍔犲叆聽Dubbo 鐢熸€�**锛屽彂甯冧簡 [dubbo-go聽椤圭洰](https://github.com/dubbo/go-for-apache-dubbo)銆� + + + +鍦ㄦ涔嬪墠 Dubbo 鐨�**璺ㄨ瑷€鍙墿灞曟€�**宸茬粡鏈変竴浜涘疄鐜帮紝鏀寔鐨勮瑷€鍖呮嫭 PHP銆丯ode.js 涓� Python锛屽悓鏃朵篃鍩轰簬鏍囧噯 Java REST API - JAX-RS 2.0 瀹炵幇浜� REST 鐨勮皟鐢ㄦ敮鎸侊紝鍏蜂綋鎯呭喌濡備笅锛� + +* **PHP**锛歱hp-for-apache-dubbo锛宐y 涔愪俊锛屾彁渚涘鎴风鍜屾湇鍔$ +* **Node.js**锛歞ubbo2.js锛宐y聽鍗冪背缃戯紝鎻愪緵瀹㈡埛绔� +* **Node.js**锛歟gg-dubbo-rpc锛宐y聽铓傝殎閲戞湇 egg 鍥㈤槦锛屾彁渚涘鎴风鍜屾湇鍔$ +* **Python** 锛歱y-client-for-apache-dubbo锛宐y聽鍗冪背缃戯紝鎻愪緵瀹㈡埛绔� + +鐜板湪鍔犲叆浜� dubbo-go锛孏o 寮€鍙戣€呬篃缁堜簬鍙互灏濆埌 Dubbo 鐨勬粙鍛充簡銆傛嵁鎮夛紝dubbo-go 椤圭洰灏嗕簬**鏈懆瀹屾垚寰€ Apache 杞欢鍩洪噾浼氱殑杩佺Щ**锛屼綔涓� Apache Dubbo 椤剁骇椤圭洰鐨勫瓙椤圭洰锛屽眾鏃� dubbo-go 椤圭洰鐨勬柊鍦板潃涔熷皢鍙樹负锛歔https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go)銆� + +鍏充簬椤圭洰鐨勭爺鍙戣儗鏅笌鍏蜂綋鎶€鏈粏鑺傜瓑鐩稿叧鍐呭锛屾垜浠涓€鏃堕棿閲囪浜嗛」鐩叡鍚屽彂璧蜂汉锛岀洰鍓嶅湪鎼虹▼鍩虹涓彴鐮斿彂閮ㄧ殑**浣曢懌閾�**銆� + +**OSCHINA锛�**dubbo-go 鏄粈涔堬紝瀹氫綅鏄粈涔堬紝涓轰粈涔堝仛杩欎釜椤圭洰锛� + +**dubbo-go 浣曢懌閾細** + +**dubbo****\-****go 鏄�** **D****ubbo 鐨勫畬鏁� Go 璇█瀹炵幇銆�** + +鎴戜滑鐭ラ亾 Dubbo 鏈韩鍩轰簬 Java锛屽緢澶氬叕鍙镐篃閮戒互 Java 寮€鍙戜负涓伙紝骞朵笖浣跨敤 Dubbo 浣� RPC 鎴栧井鏈嶅姟寮€鍙戞鏋躲€� + +鑰屾渶杩� Go 璇█鐢熸€佸彂灞曟瘮杈冭繀閫燂紝鍥犲叾璇█浼樺娍锛屾垜浠凡缁忔湁閮ㄩ棬寮€濮嬪皾璇曚娇鐢� Go 寮€鍙戜竴浜涙柊鐨勯」鐩紝灏变細瀛樺湪浜熼渶瑙e喅鐨勯棶棰橈細 + +* 濡備綍瀹炵幇 Go 椤圭洰鍜� Java & Dubbo 椤圭洰鐨勪簰閫氾紵 +* 鍙﹀锛孏o 椤圭洰鏈韩涔熸湁瀵� RPC 涓庡井鏈嶅姟寮€鍙戞鏋剁殑璇夋眰锛屽浣曡В鍐筹紵 + +鍩轰簬杩欎袱涓棶棰橈紝鎴戜滑鎼虹▼鍥㈤槦鍩轰簬 dubbo-go 鐨勬棭鏈熼」鐩紝閲嶆瀯寮€鍙戜簡鏇存槗浜庢墿灞曚笖鍔熻兘鏇村姞瀹屽杽鐨� dubbo-go v1.0.0 鐗堟湰锛屽苟璐$尞鍥炰簡绀惧尯锛屽畠**棣栬鐩殑灏辨槸瑙e喅 Go 椤圭洰涓� Java & Dubbo 椤圭洰鐨勪簰閫氶棶棰橈紝鍚屾椂****涔�****涓� Go 椤圭洰鎻愪緵****浜�****涓€绉� RPC** **涓庡井鏈嶅姟寮€鍙�****妗嗘灦鐨勯€夋嫨**銆� + +dubbo-go 鎻愪緵瀹㈡埛绔笌鏈嶅姟鍣ㄧ锛岀洰鍓� dubbo-go 绀惧尯浣滀负 Dubbo 鐢熸€佹渶娲昏穬鐨勭ぞ鍖轰箣涓€锛屽悗闈㈢殑瀹氫綅闇€瑕侀厤鍚� Dubbo 瀹樻柟鐨勮姹備笌绀惧尯鐢ㄦ埛鐨勯渶姹傘€� + +**OSCHINA锛�**鎴戜滑鐭ラ亾 Dubbo 鍦� Java 鐢熸€佷笂鏄湁闈炲父楂樼殑鎴愬氨鐨勶紝鑰岀洰鍓� Go 鐢熸€佹湰韬篃鏈変竴浜涚煡鍚嶇殑寰湇鍔℃鏋讹紝閭� dubbo-go 涔嬩簬 Go 鐢熸€侊紝鏄惁鏈変笌鍏跺畠妗嗘灦姣旀嫾鐨勮兘鍔涳紵 + +**dubbo-go 浣曢懌閾細** + +鎴戜滑鏈€澶х殑鑳藉姏灏辨槸浣滀负 Dubbo 鐨� Go 璇█鐗堟湰锛屾墦閫氫簡涓ょ璇█涔嬮棿鐨� gap锛�**璁� Dubbo 鏇村姞璐磋繎浜戝師鐢�**锛屼负寮€鍙戣€呬篃鎻愪緵浜嗘渶澶х殑鐏垫椿鎬э紝鏄捐憲闄嶄綆浼佷笟鐜版湁鏈嶅姟涓婁簯鐨勬垚鏈紝璁╀紒涓氬湪浜戝師鐢熸椂浠e浜嗕竴绉嶉€夋嫨銆� + +**OSCHINA锛�**Go 鐨勭壒鎬ф湁娌℃湁鍦� dubbo-go 涓緱鍒扮浉搴旂殑浣撶幇锛燂紙姣斿 Go 鐨勯珮骞跺彂鏄€庝箞浠庡熀浜� Java 鐨� Dubbo 涓敼閫犲埌 dubbo-go 涓殑锛燂級 + +**dubbo****\-****go 浣曢懌閾細** + +鎴戝浜� Go 璇█鐨勮鐭ユ槸锛岄鍏堝涔犳垚鏈瘮杈冨皬锛岀浉姣斾簬 Java 鐨勫涔犳垚鏈紝Go 璇█鏇村鏄撳涔犲拰涓婃墜銆� + +鍏舵 Go 鍦ㄨ瑷€灞傞潰涓婏紝姣斿鍏� CSP 缂栫▼妯″瀷鍦ㄩ珮骞跺彂澶勭悊涓婄殑绠€鍗曢珮鏁堛€佽交閲忕骇鍗忕▼鐨勪紭鍔匡紝鐩告瘮杈冨熀浜� JVM 鐨� Java 绋嬪簭鏉ヨ锛屽熀浜� runtime 鐨� Go 绋嬪簭鐬椂鍚姩鑳藉姏绛夌壒鎬ч兘鍚稿紩鐫€寰堝寮€鍙戣€咃紝杩欓噷灏变笉璇︾粏闃愯堪浜嗐€� + +鏈€鍚庡氨鏄綔涓轰簯鍘熺敓璇█鐨勪紭鍔匡紝闅忕潃 Docker銆乲8s 涓� Istio 绛変紭绉€椤圭洰鐨勫嚭鐜帮紝浜戝師鐢熷簳灞傚熀鏈 Go 璇█缁熶竴浜嗭紝鐩镐俊浼佷笟鍦ㄤ簯鍘熺敓妯″紡涓嬪紑鍙戠殑鏃ュ瓙宸茬粡涓嶈繙浜嗐€傛垜瑙夊緱 Go 璇█鐨勭敓鎬佸簲璇ヤ細瓒婃潵瓒婂ソ锛屼篃浼氭湁瓒婃潵瓒婂鐨勪汉浣跨敤瀹冦€� + +灏嗗熀浜� Java 鐨� Dubbo 寮曞叆鍒� Go 涓紝鍍忓墠杈硅鐨勶紝dubbo-go 甯︽潵鐨勪紭鍔垮氨鏄彲浠ュ揩閫熻瀺鍏ヤ簯鍘熺敓鐨勯鍩熴€傝璇� Go 璇█鐗规€т綋鐜扮殑璇濓紝鍙互鍙傝€冧竴涓� **dubbo****\-****go** **涓�****寮傛缃戠粶 I/O 妯″瀷鐨勮璁★紝杩欓儴鍒嗗皢 Go 璇█杞婚噺绾у崗绋嬬殑浼樺娍浣撶幇浜嗗嚭鏉�**銆� + +杩欓噷涔熻涓€涓� Go 璇█涓嶈冻鐨勫湴鏂癸細 + +* Go 鐩稿 Java 鏉ヨ杩樻槸寰堝勾杞荤殑璇█锛屾病鏈夋ā鏉垮簱鍙敤锛屾墍浠ョぞ鍖哄湪缂栧啓骞剁淮鎶essian 2 鍗忚搴撲笂浠樺嚭浜嗗緢楂樼殑寮€鍙戞垚鏈紱 +* 姣旇捣 Java 鐨� try/catch 閿欒澶勭悊鏂瑰紡锛孏o 鐨� error 澶勭悊鑳藉姏鍋忓急锛� +* 鎬讳綋鐢熸€佽繕鏄笉濡� Java锛屽娌℃湁鍍� Netty 涓€鏍风殑寮烘湁鍔涚綉缁� I/O 搴撱€� + +涓轰粈涔堟彁鍒拌繖涓€鐐瑰憿锛屽洜涓� Dubbo 鑷韩浣跨敤浜� Netty 鍜� Hessian 2 鍗忚瀹樻柟 Java 搴擄紝鑰� dubbo-go 鍦ㄥ紑濮嬪仛鐨勬椂鍊欒繖浜涢兘鏄病鏈夌殑锛岃繖浣垮緱 **dubbo****\-****go** **涓€璺蛋鏉ラ潪甯歌壈杈涳紝浣嗘槸****绀惧尯****鏈€缁堥兘****鍏嬫湇浜嗭紝骞朵笖棰濆璐$尞浜嗗紑婧愮殑 Getty 鍜�** **H****essian2 椤圭洰銆�** + +杩欓噷鐗瑰埆鎰熻阿 dubbo-go 绀惧尯鏃╂湡鐨勭粍缁囪€呬簬闆紝椤圭洰鐨勬棭鏈熺増鏈槸 **2016 骞�**鍦ㄥ叾棰嗗鑳¢暱鍩庡拰鍚屼簨鍒樼晱涓夋敮鎸佷笅寮€鍙戠殑锛屼粬璐$尞鐨� Hessian2 鍜� Getty 椤圭洰锛屼篃涓烘渶鏂扮増鏈殑 dubbo-go 鎵撳ソ浜嗗潥瀹炵殑鍩虹銆� + +**OSCHINA锛�**鍓嶄笉涔� Dubbo 鎵嶅甯冧箣鍚庝細鍦� 3.0 涓己璋� Service Mesh 锛岃繖灏辨槸璇█鏃犲叧鐨勪簡锛岄偅 dubbo-go 杩樻湁蹇呰鍦ㄨ繖鏃跺€欏姞鍏ョ敓鎬佸悧锛� + +**dubbo****\-****go 浣曢懌閾細** + +Service Mesh 纭疄鏄井鏈嶅姟鏈潵鍙戝睍鐨勭殑涓€涓ぇ鏂瑰悜锛屼絾鏄幇闃舵鍦ㄥ浗鍐呭ぇ鍏徃杩樻病鏈夌湅鍒伴潪甯告垚鍔熺殑妗堜緥锛屽緢澶氫腑灏忓叕鍙歌嚜韬井鏈嶅姟杩樻湭鎷嗗垎瀹屾瘯鐢氳嚦浜庤繕鏈紑濮嬶紝鐩墠 dubbo-go 绀惧尯浼樺厛瑙e喅杩欑绫诲瀷浼佷笟寰湇鍔℃妧鏈惤鍦扮幆鑺備腑閬囧埌鐨勯棶棰橈紝涓撴敞浜庤ˉ榻愮浉鍏冲姛鑳姐€佷紭鍖栨暣浣撴€ц兘鍜岃В鍐� bug銆傝嚦浜庢湭鏉ワ紝鎴戠浉淇¢殢鐫€ Dubbo Mesh 鍦� Service Mesh 棰嗗煙鐨勬帰绱紝dubbo-go 鑲畾浼氳窡杩涘苟鎵紨閲嶈瑙掕壊銆� + +**OSCHINA锛�**dubbo-go 涓� Dubbo 鐨勬洿鏂板叧绯绘槸鎬庝箞鏍风殑锛熸槸鍚屾鏇存柊鐗规€ц繕鏄湁鑷繁鐨勪竴浜涘垱鏂帮紵 + +**dubbo****\-****go 浣曢懌閾細** + +鎴戜滑鐜板湪鍙戝竷鐨勬渶鏂扮増鏈槸 v1.0.0锛屾垜浠湪姣忎竴娆� release 鏂扮殑鐗堟湰鍚庯紝閮戒細鏄庣‘璇存槑鍙互鍏煎鐨� Dubbo 鐗堟湰銆傛墍浠ワ紝dubbo-go 闇€瑕佸吋瀹瑰搴� Dubbo 鐗堟湰鍙风殑鍔熻兘锛屼細鍚屾鏇存柊涓€浜� Dubbo 鐗规€с€� + +**OSCHINA锛�**鏂板彂甯冪増鏈甫鏉ヤ粈涔堝€煎緱鍏虫敞鐨勭壒鎬э紵 + +**dubbo****\-****go 浣曢懌閾細** + +褰撳墠鍙戝竷鐨� v1.0.0 鐗堟湰鏀寔鐨勫姛鑳藉涓嬶細 + +* 瑙掕壊锛欳onsumer(鈭�)銆丳rovider(鈭�) +* 浼犺緭鍗忚锛欻TTP(鈭�)銆乀CP(鈭�) +* 搴忓垪鍖栧崗璁細JsonRPC v2(鈭�)銆丠essian v2(鈭�) +* 娉ㄥ唽涓績锛歓ooKeeper(鈭�) +* 闆嗙兢绛栫暐锛欶ailover(鈭�) +* 璐熻浇鍧囪 锛歊andom(鈭�) +* 杩囨护鍣細Echo Health Check(鈭�) +* extension 鎵╁睍鏈哄埗 + +dubbo-go v1.0.0 鐗堟湰锛屼富瑕佺敱鎴戝拰鍚屽湪鎼虹▼鐨勫悓浜媅鏂归摱鍩嶿(https://github.com/fangyincheng)缁存姢锛岀ぞ鍖烘垚鍛榌鍛ㄥ瓙搴哴(https://github.com/u0x01)涓嶽楂樿緵鏍糫(https://github.com/gaoxinge)鍙備笌璐$尞锛岃鐗堟湰**娌跨敤浜�** **D****ubbo 鐨勪唬鐮佸垎灞傝В鑰﹁璁�**銆侱ubbo 2.6.x 鐨勪富瑕佸姛鑳介兘浼氶€愭笎鍦� dubbo-go 涓疄鐜帮紝鍖呮嫭 Dubbo 鍩轰簬 SPI 鐨勪唬鐮佹嫇灞曟満鍒讹紝dubbo-go 涔熸湁瀵瑰簲鐨� extension 鎵╁睍鏈哄埗涓庝箣瀵瑰簲銆� + +鎴戜滑鍦ㄦ湭鏉ュ皢閫愭笎鎺ㄥ嚭鐩墠鍙墿灞曟ā鍧楃殑鏇村瀹炵幇锛屽琛ラ綈鏇村鐨� Loadbalance 璐熻浇鍧囪 銆丆luster Strategy 闆嗙兢绛栫暐瀹炵幇锛堢洰鍓嶈繖浜涗换鍔$敱绀惧尯浼欎即涓诲姩璁ら锛屽笇鏈涙洿澶氱殑 Go 璇█鐖卞ソ鑰呮湅鍙嬪彲浠ュ姞鍏ョぞ鍖鸿础鐚級锛涘張濡備簯鍘熺敓棰嗗煙闈炲父娴佽鐨� k8s锛屾垜浠篃灏嗗悓姝� Dubbo 鐨� roadmap锛岃窡杩� k8s 浣滀负娉ㄥ唽涓績鐨勬敮鎸侊紝鐩墠鐢辩ぞ鍖烘垚鍛榌寮犳捣褰琞(https://github.com/NameHaibinZhang)璐熻矗璺熻繘銆� + +褰撶劧骞垮ぇ寮€鍙戣€呬滑涔熷彲浠ュ杩欎簺妯″潡鎺ュ彛杩涜鏂扮殑瀹炵幇锛岄€氳繃 extension 鎷撳睍锛屼互瀹屾垚鑷繁鐨勭壒娈婇渶姹傝€屾棤闇€淇敼婧愪唬鐮併€傚悓鏃讹紝鎴戜滑闈炲父娆㈣繋寮€鍙戣€呬负绀惧尯璐$尞鏈夌敤鐨勬嫇灞曞疄鐜般€� + +姝ょ増鏈В鍐充簡涓€澶ч噸鐐归棶棰橈細**涓� Dubbo Java 鐗堟湰浜掗€氱殑瑙e喅鏂规銆�**鎴戜滑灏嗚繖閮ㄥ垎鎻愬彇鍑轰簡 [Hessi](https://github.com/dubbogo/hessian2)[a](https://github.com/dubbogo/hessian2)[n2](https://github.com/dubbogo/hessian2) 椤圭洰锛岃椤圭洰婧愯嚜绀惧尯[浜庨洦](https://github.com/AlexStocks)鐨勬棭鏈熻础鐚紝鐜板湪鐢辩ぞ鍖烘垚鍛榌鏈涘摜](https://github.com/wongoo)璐熻矗缁存姢锛孾鍛ㄥ瓙搴哴(https://github.com/u0x01)涓嶽楂樿緵鏍糫(https://github.com/gaoxinge)鍙備笌璐$尞銆傜洰鍓嶈椤圭洰宸茬粡瀹屾垚浜嗗 Java 澶ч儴鍒嗙被鍨嬬殑鍏煎鏀寔銆傚ぇ瀹朵篃鍙互鍗曠嫭灏嗚椤圭洰闆嗘垚鍒拌嚜宸辩殑椤圭洰涓紝瀹冪殑寮€婧愬崗璁槸 Apache-2.0銆� + +鍙﹀涓€涓瘮杈冮噸瑕佺殑灏辨槸 **dubbo****\-****go 鐜板湪浣跨敤鐨� TCP 寮傛缃戠粶 I/O 搴�**锛岃搴撲篃鏄熀浜嶽浜庨洦](https://github.com/AlexStocks)鏃╂湡鍐欑殑 [Getty](https://github.com/dubbogo/getty) 椤圭洰锛岀洰鍓嶇敱绀惧尯鐨刐鏈涘摜](https://github.com/wongoo)涓嶽鏂归摱鍩嶿(https://github.com/fangyincheng)璐熻矗缁存姢锛屽畠鍚屾牱涔熸槸 Apache-2.0 鐨勫紑婧愬崗璁€備笅涓€鐗堟湰鎴戜滑**浼氶拡瀵� dubbo****\-****go 鍜� Getty 鐨勭綉缁� I/O 涓庣嚎绋嬫淳鍙戣繖涓€閮ㄥ垎杩涜杩涗竴姝ヤ紭鍖�****銆�** + +闄ゆ涔嬪锛屾垜浠鍒掍笅涓€姝ユ敮鎸� Dubbo 鐨勫彟澶栧嚑澶ч噸瑕佸姛鑳斤紝濡傦細 + +* routing rule 璺敱瑙勫垯(dubbo v2.6.x) +* dynamic configuration 鍔ㄦ€侀厤缃腑蹇�(dubbo v2.7.x) +* metrics 鎸囨爣涓庣洃鎺�(dubbo v2.7.x)聽 +* trace 閾捐矾鐩戞帶(dubbo ecos)聽 + +**OSCHINA锛�**鐩墠椤圭洰鐨勫簲鐢ㄦ儏鍐靛浣曪紵 + +**dubbo****\-****go 浣曢懌閾細** + +dubbo-go 鐜板湪宸茬粡寮€濮嬭涓€浜涗紒涓氬皾璇曞簲鐢ㄤ簬 Go 璇█搴旂敤铻嶅叆浼佷笟宸叉湁 Java & Dubbo 鎶€鏈爤锛屼互鍙婃惌寤哄叏鏂� Go 璇█鍒嗗竷寮忓簲鐢ㄧ瓑鍦烘櫙銆傛瘮濡備腑閫氬揩閫掑唴閮� Go 璋冪敤 Java Dubbo 鏈嶅姟锛涗綔涓烘惡绋� Go 璇█搴旂敤鐨勬湇鍔℃鏋朵互鍙� Go銆丣ava 搴旂敤浜掗€氥€� + +鍏蜂綋鐨勫簲鐢ㄦ儏鍐靛彲浠ユ煡鐪嬶細 + +* [https://github.com/dubbo/go-for-apache-dubbo/issues/2](https://github.com/dubbo/go-for-apache-dubbo/issues/2) + +**OSCHINA锛�**鎺ヤ笅鏉ョ殑婕旇繘鏂瑰悜鏄€庝箞鏍风殑锛� + +**dubbo****\-****go 浣曢懌閾細** + +鍦� dubbo-go 杩佸線 Apache 杞欢鍩洪噾浼氫綔涓� Apache Dubbo 鐨勫瓙椤圭洰鍚庯紝棣栧厛鏈€閲嶈鐨勬槸**鎬ц兘鐨勮繘涓€姝ヤ紭鍖�**锛岀洰鍓嶆€ц兘涓婅櫧鐒惰兘澶熻揪鍒板簲鐢ㄧ殑鐢熶骇绾у埆瑕佹眰锛屼絾鎴戜滑瑙夊緱杩樻病鏈夊彂鎸ュ嚭 Go 璇█鐨勪紭鍔匡紝杩樻湁姣旇緝澶х殑浼樺寲绌洪棿銆傛瘮濡傚墠杈规彁鍒扮殑 Getty锛屼笅涓€鐗堟湰浼氶拡瀵� dubbo-go 搴旂敤 Getty 鐨勭綉缁� I/O 妯″瀷涓庣嚎绋嬫淳鍙戝仛涓€浜涗紭鍖栥€� + +鍙﹀鍖呭惈涓婇潰鎻愬埌鐨勬垜浠繎鏈熼渶瑕佽ˉ鍏ㄤ竴浜涢噸瑕佸姛鑳斤紝鏈€澶ч檺搴﹀湴鍦�**鍔熻兘瀹屾暣鎬�**涓婅兘澶熻窡 Dubbo 鍏煎銆傚叧浜庢湭鏉� dubbo-go 鐨勫彂灞曪紝涔熶細鍚� Dubbo 2.7.x 鐗堟湰杩欐潯绾夸笂鐨勮矾绾垮浘婕旇繘銆� + +**OSCHINA锛�**璇村埌鎬ц兘锛屽綋鍓嶆€ц兘鎯呭喌鍏蜂綋濡備綍锛� + +**dubbo****\-****go 浣曢懌閾細** + +鎴戜滑鏈夊仛涓€涓� [**dubbo-go-****benchmark**](https://github.com/dubbogo/go-for-apache-dubbo-benchmark) 椤圭洰锛屽湪 CPU 鍨嬪彿涓� Intel(R) Xeon(R) CPU E5-2609 0 @2.40GHz锛孋PU 鏍稿績鏁颁负 4\*8 鐨勭‖浠舵按骞充笅锛屽彂閫� 1k 骞惰繑鍥� 1k 鐨勬暟鎹紝100 骞跺彂鏁帮紝100w 鎬昏姹傛暟锛宷ps 鍙互杈惧埌 1.2 涓囧乏鍙炽€� + +CPU 鎬ц兘鎹㈡垚姣旇緝楂樼殑閰嶇疆濡� Intel Core i9 2.9GHz锛宷ps 鍙互鍒拌揪 2 涓囧乏鍙炽€� + +鎴戜滑鍚庨潰浼氬 Hessian2 搴撳拰 Getty 搴撹繘琛屾寔缁€ц兘浼樺寲锛屼互缁欏箍澶т娇鐢ㄨ€呰妭绾﹁祫婧愩€� + +## **閲囪鍢夊浠嬬粛** + +**浣曢懌閾�**锛屾惡绋嬪熀纭€涓彴鐮斿彂閮ㄦ妧鏈笓瀹讹紝dubbo-go 涓昏浣滆€呫€傜洰鍓嶄笓娉ㄤ簬 Golang & Java銆佷腑鍙版灦鏋勩€佷腑闂翠欢涓庡尯鍧楅摼绛夋妧鏈€� \ No newline at end of file diff --git a/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md b/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md new file mode 100644 index 0000000000000000000000000000000000000000..67dd72d92b3c929ccdcbb5d948e8c086f8fb1d32 --- /dev/null +++ b/doc/md/interview/what's-new-in -dubbo-go-v1.4.0.md @@ -0,0 +1,167 @@ +# [dubbo-go 1.4.0 鐗堟湰鍙戝竷锛屾敮鎸� K8s 娉ㄥ唽涓績銆乺est 鍗忚](https://blog.csdn.net/weixin_45583158/article/details/105132322) + + +2020-03-26 09:30:00 + +寰楃泭浜庣ぞ鍖烘椿璺冪殑鏀寔锛�2020 骞� 3 鏈� 25 鏃� 鎴戜滑鍙戝竷浜嗕竴涓浜哄叴濂嬬殑鐗堟湰鈥斺€攄ubbo-go v1.4.0銆傞櫎浜嗙户缁敮鎸佸凡鏈夌殑 Dubbo 鐨勪竴浜涚壒鎬у锛� dubbo-go 寮€濮嬩簡涓€浜涜嚜宸辩殑鍒涙柊灏濊瘯銆� + +杩欎釜鐗堟湰锛屾渶澶х殑鎰忎箟鍦ㄤ簬锛屽仛浜嗕竴浜涙敮鎸佷簯鍘熺敓鐨勫噯澶囧伐浣溿€傛瘮濡傝锛岀ぞ鍖哄湪鎺㈣浜嗗緢涔呯殑 k8s 钀藉湴涔嬪悗锛岀粓浜庢嬁鍑烘潵浜嗕娇鐢� k8s 浣滀负娉ㄥ唽涓績鐨勮В鍐虫柟妗堛€� + +鍏舵涓€涓瘮杈冨ぇ鐨勬敼杩涙槸--鎴戜滑鍦ㄥ彲瑙傛祴鎬т笂涔熻繄鍑轰簡閲嶈鐨勪竴姝ャ€傚湪杩欎箣鍓嶏紝dubbo-go鍙彁渚涗簡鏃ュ織杩欎箞涓€涓崟涓€鎵嬫锛屽唴閮ㄧ殑淇℃伅姣旇緝涓嶉€忔槑锛岃繖涓増鏈皢鏈夊緢澶х殑鏀瑰杽銆� + +鏈€鍚庝竴涓护浜哄績鍔ㄧ殑鏀硅繘鏄紝鎴戜滑鏀寔浜� REST 鍗忚銆� + +## 1\. K8s 娉ㄥ唽涓績 + +dubbo-go 娉ㄥ唽涓績鐨勬湰璐ㄤ负K/V鍨嬬殑鏁版嵁瀛樺偍銆傚綋鍓嶇増鏈疄鐜颁簡浠� Endpoint 涓虹淮搴﹀湪 k8s API Server 杩涜鏈嶅姟娉ㄥ唽鍜屽彂鐜扮殑鏂规銆愪笅鏂囩畝绉� Endpoint 鏂规銆戯紝鏋舵瀯鍥惧涓嬨€� + + + +Endpoint 鏂规锛岄鍏堝皢姣忎釜 dubbo-go 杩涚▼鑷韩鏈嶅姟淇℃伅搴忓垪鍖栧悗锛岄€氳繃 Kubernetes 鎻愪緵鐨� Patch 鐨勬帴鍙e啓鍏ュ湪鑷韩 Pod 瀵硅薄鐨� Annotation 涓€傚叾娆★紝閫氳繃 Kubernetes 鐨� Watch 鎺ュ彛瑙傚療闆嗙兢涓湰 Namespace 鍐呭甫鏈夋煇浜涘浐瀹歭able \[瑙佷笂鍥綷] Pod 鐨凙nnotation 淇℃伅鐨勬洿鏂帮紝澶勭悊鏈嶅姟鍋ュ悍妫€鏌ャ€佹湇鍔′笂涓嬬嚎绛夋儏鍐靛苟瀹炴椂鏇存柊鏈湴缂撳瓨銆傛暣浣撴祦绋嬩粎浣跨敤 Kubernetes 鍘熺敓 API 瀹屾垚灏� Kubernetes 浣滀负娉ㄥ唽涓績鐨勫姛鑳界壒鎬с€� + +杩欎釜鏂规闈炲父绠€娲侊紝涓嶉渶瑕佸疄鐜伴澶栫殑绗笁鏂规ā鍧楋紝涔熶笉闇€瑕佸 Dubbo 涓氬姟浣滃嚭鏀瑰姩锛屼粎浠呮妸 k8s 褰撳仛閮ㄧ讲骞冲彴锛屼緷璧栧叾瀹瑰櫒绠$悊鑳藉姏锛屾病鏈変娇鐢ㄥ叾 label selector 鍜� service 绛夋湇鍔℃不鐞嗙壒鎬с€傚鏋滅珯鍦� k8s Operator 鐨勮搴︽潵鐪嬶紝Operator 鏂规鐨勪紭鐐瑰嵆 Endpoint 鏂规鐨勭己鐐癸紝Endpoint 鏂规鏃犳硶浣跨敤 k8s 鐨勫仴搴锋鏌ヨ兘鍔涳紝浜︽病鏈変娇鐢� k8s service 鐨勪簨浠剁洃鍚兘鍔涳紝姣忎釜 consumer 鍐椾綑鐩戝惉涓€浜涗笉蹇呰鐩戝惉鐨勪簨浠讹紝褰� Endpoint 杩囧鏃朵細鍔犲ぇ API Server 鐨勭綉缁滃帇鍔涖€� + +鐩墠 dubbo-go 绀惧尯鍏跺疄宸茬粡鏈変簡 operator 鐗堟湰娉ㄥ唽涓績鐨勬妧鏈柟妗堬紝聽鍚庣画鐗堟湰銆愯鍒掔増鏈槸 v1.6銆戠殑 dubbo-go 浼氱粰鍑哄叾瀹炵幇銆傜浉姣斿綋鍓嶅疄鐜帮紝operator 鏂规寮€鍙戝拰绾夸笂缁存姢鎴愭湰褰撶劧涓婂崌寰堝銆備簩鑰呭鍚岀‖甯佺殑涓ら潰锛岀ぞ鍖轰細璁╀袱绉嶆柟寮忎細鍏卞瓨锛屼互婊¤冻涓嶅悓 level 鐨勪娇鐢ㄨ€呫€� + +娉ㄦ剰: 鍥� Pod 琚皟搴﹁€� IP 鍙戠敓鍙樺寲鏃讹紝褰撳墠鐗堟湰鐨� configuration 浠ュ強 router config 妯″潡鏆傛椂鏃犳硶鍔ㄦ€佹洿鏂般€傝繖鏈夊緟浜庢垜浠繘涓€姝ヨВ鍐炽€� + +鍙傝€冭寖渚媆[1\]. + +## 2\. tracing 鍜� metric + +鍙娴嬫€ф槸寰湇鍔¢噸瑕佺殑涓€鐜紝涔熸槸鎴戜滑1.4鐗堟湰鐫€鍔涙敮鎸佺殑閮ㄥ垎銆傚湪1.4鐗堟湰涓紝鎴戜滑涓昏鍦� tracing 鍜� metric 涓や釜鏂瑰悜鎻愪緵浜嗘敮鎸併€� + +涓轰簡鏀寔 tracing 鍜� metric锛屽叧閿殑涓€鐐规槸鏀寔context鍦ㄦ暣涓皟鐢ㄨ繃绋嬩腑浼犻€掋€備负姝ゆ垜浠В鍐充簡context璺ㄧ浼犻€掔殑闂銆傜洰鍓嶇敤鎴峰彲浠ュ湪鎺ュ彛涓0鏄� context 骞朵笖璁剧疆鍊硷紝dubbo-go 鍦ㄥ簳灞傚畬鎴� context 鍐呭浠� client 浼犻€掑埌 server 鐨勪换鍔°€� + + + +鍦� metric 鏂归潰锛宒ubbo-go 寮€濮嬫敮鎸� Prometheus 閲囬泦鏁版嵁浜嗐€傜洰鍓嶆敮鎸� Prometheus涓� 鐨� Histogram 鍜� Summary銆傜敤鎴蜂篃鍙互閫氳繃鎵╁睍 Reporter 鎺ュ彛鏉ヨ嚜瀹氫箟鏁版嵁閲囬泦銆� + +鍦� tracing 鏂归潰锛岀洰鍓� dubbo-go 鐨勮璁℃槸閲囩敤 opentracing 浣滀负缁熶竴鐨� API锛屽湪璇� API 鐨勫熀纭€涓婏紝閫氳繃鍦� client 鍜� server 涔嬩腑浼犻€� context锛屼粠鑰屽皢鏁翠釜閾捐矾涓茶捣鏉ャ€傜敤鎴峰彲浠ラ噰鐢ㄤ换浣曟敮鎸� opentracing API 鐨勭洃鎺ф鏋舵潵浣滀负瀹炵幇锛屼緥濡� zipkin锛宩aeger 绛夈€� + +## 3\. rest鍗忚鏀寔 + +Dubbo 鐢熸€佺殑搴旂敤涓庡叾浠栫敓鎬佺殑搴旂敤浜掕仈浜掗€氾紝涓€鐩存槸 dubbo-go 绀惧尯杩芥眰鐨勭洰鏍囥€俤ubbo-go v1.3 鐗堟湰宸茬粡瀹炵幇浜� dubbo-go 涓� grpc 鐢熸€佸簲鐢ㄧ殑浜掕仈浜掗€氾紝鑻ユ兂涓庡叾浠栫敓鎬佸 Spring 鐢熸€佷簰鑱斾簰閫氾紝鍊熷姪 rest 鍗忚鏃犵枒鏄竴涓緢濂界殑鎶€鏈墜娈点€� + +Rest 鍗忚鏄竴涓緢寮哄ぇ骞朵笖绀惧尯鍛煎0寰堥珮鐨勭壒鎬э紝瀹冭兘澶熸湁鏁堣В鍐� open API锛屽墠绔€氫俊锛屽紓鏋勭郴缁熼€氫俊绛夐棶棰樸€傛瘮濡傦紝濡傛灉浣犵殑鍏徃閲岄潰鏈変竴浜涢檲骞翠唬鐮佹槸閫氳繃 http 鎺ュ彛鏉ユ彁渚涙湇鍔$殑锛岄偅涔堜娇鐢ㄦ垜浠殑 rest 鍗忚灏卞彲浠ユ棤缂濋泦鎴愪簡銆� + +閫氳繃鍦� dubbo-go 涓彂甯� RESTful 鐨勬帴鍙g殑搴旂敤鍙互璋冪敤浠绘剰鐨� RESTful 鐨勬帴鍙o紝涔熷彲浠ヨ浠讳綍瀹㈡埛绔互 http 鐨勫舰寮忚皟鐢紝妗嗘灦鍥惧涓嬶細 + + + + +鍦ㄨ璁¤繃绋嬩腑锛岃€冭檻鍒颁笉鍚岀殑鍏徃鍐呴儴浣跨敤鐨� web 妗嗘灦骞朵笉鐩稿悓锛屾墍浠ユ垜浠厑璁哥敤鎴锋墿灞曡嚜宸� rest server 锛� web 妗嗘灦鍦� dubbo-go鐨勫皝瑁咃級鐨勫疄鐜帮紝褰撶劧锛屼笌 rest server 鐩稿叧鐨勶紝璇稿 filter 绛夛紝閮藉彲浠ュ湪鑷繁鐨� rest server 瀹炵幇鍐呴儴鎵╁睍銆� + +## 4\. 璺敱鍔熻兘澧炲己 + +璺敱瑙勫垯鍦ㄥ彂璧蜂竴娆� RPC 璋冪敤鍓嶈捣鍒拌繃婊ょ洰鏍囨湇鍔″櫒鍦板潃鐨勪綔鐢紝杩囨护鍚庣殑鍦板潃鍒楄〃锛屽皢浣滀负娑堣垂绔渶缁堝彂璧� RPC 璋冪敤鐨勫閫夊湴鍧€銆倂1.4 鐗堟湰鐨� dubbo-go 瀹炵幇浜� Condition Router 鍜� Health Instance First Router锛屽皢鍦ㄥ悗闈㈢増鏈腑闄嗙画缁欏嚭璇稿 Tag Router 绛夊墿浣� Router 鐨勫疄鐜般€� + +### 4.1 鏉′欢璺敱 + +鏉′欢璺敱锛屾槸 dubbo-go 涓涓€涓敮鎸佺殑璺敱瑙勫垯锛屽厑璁哥敤鎴烽€氳繃閰嶇疆鏂囦欢鍙婅繙绔厤缃腑蹇冪鐞嗚矾鐢辫鍒欍€� + +涓庝箣鐩镐技鐨勪竴涓蹇垫槸 dubbo-go 閲岄潰鐨� group 姒傚康锛屼絾鏄潯浠惰矾鐢辨彁渚涗簡鏇村姞缁嗙矑搴︾殑鎺у埗鎵嬫鍜屾洿鍔犱赴瀵岀殑琛ㄨ揪璇箟銆傛瘮杈冨吀鍨嬬殑浣跨敤鍦烘櫙鏄粦鐧藉悕鍗曡缃紝鐏板害浠ュ強娴嬭瘯绛夈€� + +鍙傝€冭寖渚媆[2\]銆� + +### 4.2 鍋ュ悍瀹炰緥浼樺厛璺敱 + +鍦� RPC 璋冪敤涓紝鎴戜滑甯屾湜灏藉彲鑳藉湴灏嗚姹傚懡涓埌閭d簺澶勭悊鑳藉姏蹇€佸浜庡仴搴风姸鎬佺殑瀹炰緥锛岃璺敱鐨勫姛鑳藉氨鏄€氳繃鏌愮绛栫暐鏂畾鏌愪釜瀹炰緥涓嶅仴搴凤紝骞跺皢鍏舵帓闄ゅ湪鍊欓€夎皟鐢ㄥ垪琛紝浼樺厛璋冪敤閭d簺鍋ュ悍鐨勫疄渚嬨€傝繖閲岀殑"鍋ュ悍"鍙互鏄垜浠嚜宸卞畾涔夌殑鐘舵€侊紝榛樿瀹炵幇鍗冲綋閿欒姣斾緥鍒拌揪鏌愪竴涓槇鍊兼椂鎴栬€呰姹傛椿璺冩暟澶т簬涓婇檺鍒欒涓哄叾涓嶅仴搴凤紝鍏佽鐢ㄦ埛鎵╁睍鍋ュ悍妫€娴嬬瓥鐣ャ€� + +鍦ㄦ垜浠湇鍔℃不鐞嗛噷闈紝鏍稿績鐨勯棶棰樺叾瀹炲氨鍦ㄤ簬濡備綍鍒ゆ柇涓€涓疄渚嬫槸鍚﹀彲鐢ㄣ€傛棤璁烘槸璐熻浇鍧囪 銆� + +鐔旀柇杩樻槸闄愭祦锛岄兘鏄杩欎釜闂鐨勮В绛斻€傛墍浠ワ紝杩欎釜 feature 鏄竴涓緢濂界殑灏濊瘯銆傚洜涓烘垜浠帴涓嬫潵璁″垝鎻愪緵鐨勭壒鎬э紝鍩轰簬瑙勫垯鐨勯檺娴佷互鍙婂姩鎬侀檺娴侊紝閮芥槸瑕佽В鍐斥€滃浣曟柇瀹氫竴涓疄渚嬫槸鍚﹀彲鐢ㄢ€濊繖涔堜竴涓棶棰樸€� + +鎵€浠ユ杩庡ぇ瀹朵娇鐢ㄨ繖涓壒鎬э紝骞跺悜绀惧尯鍙嶉鍚勮嚜璁惧畾鐨勫仴搴锋寚鏍囥€傝繖瀵规垜浠帴涓嬫潵鐨勫伐浣滀細鏈夊緢澶х殑甯姪銆� + +## 5\. hessian 鍗忚澧炲己 + +鐩歌緝浜� dubbo 鐨� Java 璇█浠ュ強鍏朵粬澶氳瑷€鐗堟湰锛宒ubbo-go 绀惧尯姣旇緝鑷豹鐨勫湴鏂逛箣涓€灏辨槸锛氭棤璁哄簳灞傜綉缁滃紩鎿庤繕鏄師鐢熶娇鐢ㄧ殑 hessian2 鍗忚锛屼互鍙婃暣浣撴湇鍔℃不鐞嗘鏋讹紝閮界敱 dubbo-go 绀惧尯浠庨浂寮€鍙戝苟缁存姢銆倂1.4 鐗堟湰鐨� dubbo-go 瀵� hessian2 鍗忚鍙堝甫鏉ヤ簡璇稿鏂� feature銆� + +### 5.1 鏀寔 dubbo 鍗忚鐨� attachments + +鍦� dubbo-go涓紝attachments 鏈哄埗鐢ㄤ簬浼犻€掍笟鍔″弬鏁颁箣澶栫殑闄勫姞淇℃伅锛屾槸鍦ㄥ鎴风鍜屾湇鍔$涔嬮棿浼犻€掗潪涓氬姟鍙傛暟淇℃伅鐨勯噸瑕佹柟寮忋€� + +hessian 缂栫爜鍗忚灏嗕箣缂栫爜鍦� body 鍐呭鐨勫悗闈㈣繘琛屼紶杈擄紝dubbo-go-hessian2 涔嬪墠骞朵笉鏀寔璇�/鍐� attachments锛屽湪澶氫釜浣跨敤鏂广€愬铓傝殎閲戞湇銆戠殑瑕佹眰涓嬶紝dubbo-go-hessian2 浠ュ吋瀹瑰凡鏈夌殑浣跨敤鏂瑰紡涓哄墠鎻愶紝鏀寔浜� attachments 鐨勮/鍐欍€� + +Request 鍜� Response 鐨� struct 涓畾涔変簡 attachments 鐨� map锛屽綋闇€瑕佷娇鐢� attachments锛岄渶瑕佺敱浣跨敤鏂规瀯閫犺繖涓ょ绫诲瀷鐨勫弬鏁版垨鑰呰繑鍥炲璞°€傚惁鍒欙紝灏嗘棤娉曞湪hessian鐨勪紶杈撴祦涓幏鍙栧拰鍐欏叆attachments銆� + +鍙﹀锛屽埄鐢� dubbo-go 璋冪敤閾句腑浼犺緭 context 鐨勫姛鑳斤紝鐢ㄦ埛宸茬粡鍙互鍦ㄦ湇鍔℃柟娉曚腑閫氳繃 context 娣诲姞 attachments浜嗐€� + +### 5.2 鏀寔蹇界暐闈炴敞鍐� pojo 鐨勮В鏋愭柟寮� + +鐢变簬 hessian 缂栫爜鍗忚涓� Java 鐨勭被鍨嬮珮搴﹁€﹀悎锛屽湪 golang 鐨勫疄鐜颁腑浼氱浉瀵规瘮杈冮夯鐑︼紝闇€瑕佹湁鎸囨槑鐨勫搴旂被鍨嬨€俤ubbo-go-hessian2 鐨勫疄鐜版柟寮忔槸锛氬畾涔� POJO 鎺ュ彛锛岃姹傚疄鐜� JavaClassName 鏂规硶鏉ヤ緵绋嬪簭鑾峰彇 Java 瀵瑰簲鐨勭被鍚嶃€傝繖瀵艰嚧浜嗘帴鏀跺埌鍖呭惈鏈敞鍐岀被鐨勮姹傛椂锛屽皢浼氭棤娉曡В鏋愯€屾姤閿欙紝杩欎釜闂浠ュ墠鏄棤娉曡В鍐崇殑銆� + +浣嗘槸锛屾湁涓€浜涗娇鐢ㄥ満鏅缃戝叧鎴栬€� service mesh 鐨� sidecar锛岄渶瑕佸湪涓嶅叧蹇� Java 绫荤殑鍏蜂綋瀹氫箟鐨勬儏鍐典笅锛屽儚 http璇诲彇 header 淇℃伅涓€鏍蜂粎浠呰鍙� dubbo 璇锋眰鐨勯檮鍔犱俊鎭紝灏� dubbo/dubbo-go 璇锋眰杞彂銆傞€氳繃璇� feature锛岀綉鍏�/sidecar 骞朵笉鍏虫敞璇锋眰鐨勫叿浣撳唴瀹癸紝鍙互鍦ㄨВ鏋愯姹傜殑鏁版嵁娴佹椂璺宠繃鏃犳硶瑙f瀽鐨勫叿浣撶被鍨嬶紝鐩存帴璇诲彇 attachments 鐨勫唴瀹广€� + +璇ュ疄鐜伴€氳繃鍦� Decoder 涓坊鍔犵殑 skip 瀛楁锛屽姣忎竴涓� object 鍋氬嚭鐗规畩澶勭悊銆� + +### 5.3 鏀寔 java.math.BigInteger 鍜� java.math.BigDecimal + +鍦� Java 鏈嶅姟涓紝java.math.BigInteger 鍜� java.math.BigDecimal 鏄棰戠箒浣跨敤鐨勬暟瀛楃被鍨嬶紝hessian 搴撳皢瀹冧滑鏄犲皠涓� github.com/dubbogo/gost/math/big 涓嬬殑瀵瑰簲绫诲瀷銆� + +### 5.4 鏀寔 鈥樼户鎵库€� 鍜屽拷鐣ュ啑浣欏瓧娈� + +鐢变簬 go 娌℃湁缁ф壙鐨勬蹇碉紝鎵€浠ュ湪涔嬪墠鐨勭増鏈紝Java 鐖剁被鐨勫瓧娈典笉琚� dubbo-go-hessian2 鎵€鏀寔銆傛柊鐗堟湰涓紝dubbo-go-hessian2 灏咼ava鏉ヨ嚜鐖剁被鐨勫瓧娈电敤鍖垮悕缁撴瀯浣撳搴旓紝濡傦細 + +```cpp +type Dog struct { + Animal + Gender string + DogName string `hessian:"-"` +} +``` + +鍚屾椂锛屽氨鍍� json 缂栫爜涓€氳繃聽`immediately`聽鍙互鍦ㄥ簭鍒楀寲涓拷鐣ヨ瀛楁锛屽悓鐞嗭紝閫氳繃聽`hessian:"-"`聽鐢ㄦ埛涔熷彲浠ヨ鍐椾綑瀛楁涓嶅弬涓� hessian 搴忓垪鍖栥€� + +鐩墠锛屼笂杩板洓涓壒鎬у凡琚煇 Go 鐗堟湰鐨� sidecar 闆嗘垚鍒板叾鍟嗕笟鐗堟湰涓彁渚涘晢涓氭湇鍔°€� + +## 6\. Nacos 閰嶇疆涓績 + +閰嶇疆涓績鏄幇浠e井鏈嶅姟鏋舵瀯閲岄潰鐨勬牳蹇冪粍浠讹紝鐜板湪 dubbo-go 鎻愪緵浜嗗閰嶇疆涓績鐨勬敮鎸併€� + + + +Nacos 浣滀负涓€涓槗浜庢瀯寤轰簯鍘熺敓搴旂敤鐨勫姩鎬佹湇鍔″彂鐜般€侀厤缃鐞嗗拰鏈嶅姟绠$悊骞冲彴锛屽湪璇ョ増鏈粓浜庝綔涓洪厤缃腑蹇冭€屽緱鍒颁簡鏀寔銆� + +鍙傝€冭寖渚媆[3\]. + +## 7\. 鎺ュ彛绾х鍚嶈璇� + +Dubbo 閴存潈璁よ瘉鏄负浜嗛伩鍏嶆晱鎰熸帴鍙h鍖垮悕鐢ㄦ埛璋冪敤鑰屽湪 SDK 灞傞潰鎻愪緵鐨勯澶栦繚闅溿€傜敤鎴峰彲浠ュ湪鎺ュ彛绾у埆杩涜瀹氫箟鏄惁鍏佽鍖垮悕璋冪敤锛屽苟瀵硅皟鐢ㄦ柟杩涜楠岀鎿嶄綔锛屽浜庨獙绛句笉閫氳繃鐨勬秷璐圭锛岀姝㈣皟鐢ㄣ€� + + + +濡備笂鍥撅紝鎬讳綋瀹炵幇鍩轰簬 AK/SK 鏈哄埗锛屽簲鐢ㄩ€氳繃 HTTPS 閫氫俊锛屽惎鍔ㄦ椂鍚戦壌鏉冩湇鍔℃媺鍙栵紝瀹氭湡鏇存柊銆備笖鍏佽鐢ㄦ埛鑷畾涔夎幏鍙� AK/SK 鐨勬簮锛屽湪 RPC 灞傞潰淇濋殰瀹夊叏鎬с€� + +## 8\. 鍥為【涓庡睍鏈� + +鐩墠 dubbo-go 宸茬粡鍒颁簡涓€涓瘮杈冪ǔ瀹氭垚鐔熺殑鐘舵€併€傚湪鎺ヤ笅鏉ョ殑鐗堟湰閲岄潰锛屾垜浠皢闆嗕腑绮惧姏鍦ㄤ簯鍘熺敓涓娿€備笅涓€涓増鏈紝鎴戜滑灏嗛鍏堝疄鐜板簲鐢ㄧ淮搴︾殑鏈嶅姟娉ㄥ唽锛岃繖鏄竴涓拰鐜版湁娉ㄥ唽妯″瀷瀹屽叏涓嶅悓鐨勬柊鐨勬敞鍐屾ā鍨嬨€備篃鏄垜浠湞鐫€浜戝師鐢熷姫鍔涚殑涓€涓叧閿増鏈€� + +鍦ㄥ彲瑙傛祴鎬т笂锛屾垜浠鍒掑湪鏁翠釜 dubbo-go 鐨勬鏋跺唴锛屽紩鍏ユ洿澶氱殑鍩嬬偣锛屾敹闆嗘洿鍔犲鐨勫唴閮ㄧ姸鎬併€傝繖闇€瑕佸疄闄呯敓浜х幆澧冪敤鎴风殑浣跨敤鍙嶉锛屼粠鑰岀煡閬撹濡備綍鍩嬬偣锛屾敹闆嗕綍绉嶆暟鎹€� + +鍦ㄩ檺娴佸拰鐔旀柇涓婏紝鍙互杩涗竴姝ユ墿灞曘€傚綋涓嬬殑闄愭祦绠楁硶锛屾槸涓€绉嶉潤鎬佺殑绠楁硶--闄愭祦鍙傛暟骞舵病鏈夊疄鏃舵牴鎹綋鍓嶆湇鍔″櫒鐨勭姸鎬佹潵鎺ㄦ柇鏄惁搴旇闄愭祦锛屽畠鍙兘浠呬粎鏄敤鎴风殑缁忛獙鍊笺€傚叾缂虹偣鍦ㄤ簬锛岀敤鎴烽毦浠ユ妸鎻″簲璇ュ浣曢厤缃紝渚嬪 TPS 绌剁珶搴旇璁剧疆鍦ㄥ澶с€傛墍浠ヨ鍒掑紩鍏ヤ竴绉嶅熀浜庤鍒欑殑闄愭祦鍜岀啍鏂€傝繖绉嶅熀浜庤鍒欑殑闄愭祦鍜岀啍鏂紝灏嗗厑璁哥敤鎴疯缃竴浜涚郴缁熺姸鎬佺殑鐘舵€侊紝濡� CPU 浣跨敤鐜囷紝纾佺洏 IO锛岀綉缁� IO 绛夈€傚綋绯荤粺鐘舵€佺鍚堢敤鎴疯鍒欐椂锛屽皢瑙﹀彂鐔旀柇銆� + +鐩墠杩欎簺瑙勫垝鐨劼犱换鍔℃竻鍗昞[4\]锛岄兘宸茬粡鏀惧叆鍦� dubbo-go 椤圭洰鐨� issue 閲岄潰锛屾杩庢劅鍏磋叮鐨勬湅鍙嬭棰嗗弬涓庡紑鍙戙€俤ubbo-go 绀惧尯鍦�**閽夐拤缇� 23331795**聽娆㈣繋浣犵殑鍔犲叆銆� + +**鏂囦腑閾炬帴锛�** + +\[1\]聽https://github.com/apache/dubbo-samples/tree/master/golang/registry/kubernetes + +\[2\]聽https://github.com/dubbogo/dubbo-samples/tree/master/golang/router/condition + +\[3\]聽https://github.com/dubbogo/dubbo-samples/tree/master/golang/configcenter/nacos + +\[4\]聽https://github.com/apache/dubbo-go/milestone/1 + +**鍙傝€冮槄璇伙細** + +* [浠巐stio鐨勮搴﹁皥寰湇鍔$殑涓€浜涜鍖篯(https://blog.csdn.net/weixin_45583158/article/details/105085686) + +* [Go璇█濡備綍瀹炵幇stop the world锛焆(https://blog.csdn.net/weixin_45583158/article/details/104912555) + +* [鍏充簬Golang GC鐨勪竴浜涜瑙�--鐪熺殑姣擩ava绠楁硶鏇撮鍏堝悧锛焆(https://blog.csdn.net/weixin_45583158/article/details/100143593) + +* [Swift绋嬪簭鍛樺Rust鍗拌薄锛氬唴瀛樼鐞哴(https://blog.csdn.net/weixin_45583158/article/details/104853360) + +* [JDK 14鍙戝竷锛岀┖鎸囬拡閿欒鏀硅繘姝e紡钀藉湴](https://blog.csdn.net/weixin_45583158/article/details/104981073) \ No newline at end of file diff --git a/doc/md/practice/dubbo-go-experience.md b/doc/md/practice/dubbo-go-experience.md new file mode 100644 index 0000000000000000000000000000000000000000..5ee1d6db53dff6445ab6a287b09159540f64434a --- /dev/null +++ b/doc/md/practice/dubbo-go-experience.md @@ -0,0 +1,199 @@ +# [dubbo-go 韪╁潙璁癩(https://dubbogo.github.io/dubbo-go-website/zh-cn/blog/dubbo-go-experience.html) + +## 鎵贰 + +### 鍓嶅皹 + +鐢变簬鎴戠殑涓€涓」鐩渶瑕佸仛鍏徃鐢ㄦ埛閴存潈锛岃€岀粍鍐呭叾浠栧皬浼欎即鍒氬ソ鏈変竴涓� _dubbo_ 鐨勯壌鏉� _rpc_ 锛屼竴寮€濮嬫垜鏄墦绠楃洿鎺ョ殑璇� _redis_ 鏁版嵁鐒跺悗鑷繁鍋氳В瀵嗐€傚伐浣滆繘琛屽埌涓€鍗婏紝鐢变簬鑰冭檻鍒板鏋滀互鍚庤繖涓湇鍔℃湁浠讳綍鍙樺姩锛屾垜杩欒竟瑕佹湁鑱斿姩琛屼负锛屾墍浠ユ敼鐢� _go_ 鏉ヨ皟鐢� _dubbo_ 鐨� _rpc_ 锛屼簬鏄垜鍦� _github_ 涓婃壘鍒颁簡 [闆ㄧ](https://github.com/AlexStocks) 鐨� [dubbogo](https://github.com/AlexStocks/dubbogo) (PS: 杩欎釜鏄� _dubbo-go_ 鍓嶈韩)銆備笉寰椾笉璇达紝闆ㄧ鏄儹蹇冪殑浜哄効鍟婏紝褰撴椂杩樺府鐫€鎴戣皟璇曚唬鐮併€傛渶鍚庝篃鏄帴鍏ヤ簡涓€涓槈鍓茬増鐨勫惂锛屼富瑕佹槸褰撴椂 _hessian2_ 瀵规硾鍨嬫敮鎸佺殑涓嶆€庝箞濂姐€� + +### 鐜板湪 + +鐩墠 [dubbo-go](https://github.com/apache/dubbo-go)闅跺睘浜� _apache_ 绀惧尯锛岀浉姣斾互鍓嶅仛浜嗛儴鍒嗛噸鏋勶紝骞朵笖缁存姢涔熷緢娲昏穬浜嗐€� + +## 鎺ュ叆 + +### 闂 + +鐩墠鏁翠釜椤圭洰鍦ㄥ揩閫熺殑杩唬涓紝寰堝鍔熻兘杩樻病鏈夊畬鍠勶紝缁存姢浜哄憳杩樻病鏈夋椂闂存潵瀹屽杽鏂囨。锛屾墍浠ュ湪鎺ュ叆鐨勬椂鍊欒鑷繁鐪嬫簮鐮佹垨璋冭瘯銆� + +### 璇存槑 + +鐩墠鎴戝徃鍦ㄤ娇鐢� _dubbo_ 鐨勮繃绋嬩娇鐢ㄧ殑 _zookeeper_ 浣滀负娉ㄥ唽涓績锛屽簭鍒楀寲鏄� _hessian2_ 锛屾墍浠ユ垜浠鍋氬涓嬪垵濮嬪寲锛� + +```go + import ( + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "github.com/apache/dubbo-go/filter/impl" + + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" + ) +``` + +### 閰嶇疆 + +鐢变簬鎴戞槸鎺ュ叆瀹㈡埛绔紝鎵€浠ユ垜杩欒竟鍙厤缃簡 _ConsumerConfig_ 銆� + +```yaml +dubbo: + # client + request_timeout: "3s" + # connect timeout + connect_timeout: "3s" + check: true + application: + organization: "dfire.com" + name: "soa.sso.ITokenService" + module: "dubbogo token service client" + version: "1.0.0" + owner: "congbai" + registries: + "hangzhouzk": + protocol: "zookeeper" + timeout: "3s" + address: "zk1.2dfire-daily.com:2181" + username: "" + password: "" + references: + "ITokenService": + registry: "hangzhouzk" + protocol: "dubbo" + interface: "com.dfire.soa.sso.ITokenService" + version: "1.0.0" + methods: + - name: "validate" + retries: "3" +``` + +鎴戣繖閲屾槸鎶� _dubbo-go_ 浣滀负绗笁鏂瑰簱鏉ョ敤锛屾墍浠ユ垜娌′娇鐢ㄥ畼鏂� [dubbo-samples](https://github.com/dubbogo/dubbo-samples/golang) 閭f牱鍦� _init_ 鍑芥暟涓鍏ラ厤缃€� + +閰嶇疆浠g爜濡備笅锛� + +```go + import ( + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol/dubbo" + ) + + type DubboCli struct { + } + + func NewCli(cconf config.ConsumerConfig) *DubboCli { + config.SetConsumerConfig(cconf) + + dubbo.SetClientConf(dubbo.GetDefaultClientConfig()) + + config.Load() + + return &DubboCli{} + } +``` + +### 鎺ュ叆 + +濂戒簡锛岄厤缃姞杞藉畬灏辫鏄庢垜浠殑鍑嗗宸ヤ綔宸茬粡鍋氬ソ浜嗭紝鎺ヤ笅鏉ュ氨瑕佹帴鍏� _rpc_ 鎺ュ彛浜嗐€� + +#### 杩斿洖鍊� + +涓€鑸� _rpc_ 璋冪敤鐨勮繑鍥炲€奸兘鏄嚜瀹氫箟鐨勶紝鎵€浠ユ垜浠篃瑕佸憡璇� _dubbo-go_ 闀夸粈涔堟牱瀛愩€傝繖涓粨鏋勪綋瑕佽窡 _java_ 鐨勭被瀵瑰簲璧锋潵锛岃繖閲屾垜浠槸瑕佸疄鐜� _hessian2_ 鐨� _interface_ : + +```go +// POJO interface +// !!! Pls attention that Every field name should be upper case. +// Otherwise the app may panic. +type POJO interface { + JavaClassName() string // got a go struct's Java Class package name which should be a POJO class. +} +``` + +鎴戠殑瀹炵幇濡備笅锛� + +```go +type Result struct { + Model interface{} `json:"model,omitempty"` + Models []interface{} `json:"models,omitempty"` + ResultCode string `json:"resultCode"` + Success bool `json:"success"` + Message string `json:"message"` + TotalRecord int `json:"totalRecord"` +} + +func (r Result) JavaClassName() string { + return "com.twodfire.share.result.ResultSupport" +} +``` + +杩欓噷鐨� _JavaClassName_ 鎺ュ彛鐨勬剰涔夊氨濡傚嚱鏁扮鍚嶄竴鏍凤紝杩斿洖鐨勫氨鏄� _java_ 鐨勭被鍚嶃€� + +#### 鎺ュ彛 + +瑕佹兂璋冪敤 _dubbo_ 鐨勬帴鍙e氨蹇呴』瀹炵幇涓嬮潰杩欎釜 _interface_ + +```go +// rpc service interface +type RPCService interface { + Reference() string // rpc service id or reference id +} +``` + +鎵€浠ユ垜闇€瑕佹瀯閫犱竴涓� _struct_ 鏉ュ仛杩欎釜浜嬫儏锛屾瘮濡傦細 + +```go +type ITokenService struct { + Validate func(ctx context.Context, req []interface{}, resp *Result) error `dubbo:"validate"` +} + +func (i *ITokenService) Reference() string { + return "ITokenService" +} +``` + +杩欎釜缁撴瀯浣撲竴鑸槸涓嶄細鏈変粈涔堟暟鎹垚鍛樸€� + +杩欓噷鎴戜滑娉ㄦ剰鍒� _Validate_ 鍑芥暟澹版槑鍚庨潰璺熺殑 _dubbo tag_ 锛岃繖涓槸涓哄鏋� _rpc_ 鍚嶇О鐨勯瀛楁瘝鏄皬鍐欙紙姣斿鎴戣璋冪敤鐨� _dubbo_ 鎺ュ彛灏辨槸 _validate_ )鍑嗗鐨� _MethodMapper_ 锛岀被浼间簬 _json_ 鐨勬槧灏� _tag_ 鍔熸晥銆備竴寮€濮嬫垜灏辨槸閬囧埌杩欎釜鍧戯紝鎴戞寜瀹樻柟鐨勪緥瀛愬疄鐜帮紝鏃ュ織涓€鐩磋鎵句笉鍒版帴鍙o紝鍚庢潵鎴戜篃鍦ㄥ畼鏂圭兢閲岃闂ぇ浣墠鐭ラ亾鏈夎繖涓姛鑳姐€� + +#### 娉ㄥ唽 + +濂戒簡锛屼笂闈㈢殑鍑嗗鍏ㄩ儴瀹屾垚鍚庯紝鎴戜滑瑕佸仛鏈€鍚庝竴姝ワ紝閭e氨鏄憡璇� _dubbo-go_ 鎴戜滑鎯宠鐨勬槸浠€涔堛€備唬鐮佸涓嬶細 + +```go + import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" + ) + + var tokenProvider = new(ITokenService) + + func init() { + config.SetConsumerService(tokenProvider) + hessian.RegisterPOJO(&Result{}) + } +``` + +#### 璋冪敤 + +鎺ヤ笅鏉ユ垜浠氨鍙互瀹屾垚鎴戜滑鐨� _DubboCli_ 鎺ュ彛浜嗭紝浠g爜濡備笅锛� + +```go +func (d *DubboCli) CheckUser(token, app string) (bool, error) { + args := []interface{}{token, app} + resp := &Result{} + + if err := tokenProvider.Validate(context.Background(), args, resp); err != nil { + return false, err + } + if resp.Success { + return resp.Success, nil + } + return resp.Success, errors.New(resp.Message) +} +``` + +濂戒簡锛岃嚦姝ゆ垜浠氨瀹屾垚浜� _dubbo-go_ 鐨勫叏閮ㄦ帴鍏ュ伐浣溿€� Happy Coding... + +## 鍐欏湪鏈€鍚� + +鍏跺疄浠g爜鏍煎紡杩欎釜闂锛屾垜鍦ㄦ帴鍏ョ殑鏃跺€欒窡瀹樻柟缇ら噷鐨勭淮鎶よ€呭ぇ浣彁杩囷紝浣跨敤 _go_ 瀹樻柟鐨勪唬鐮佹牸寮忓伐鍏� [goimports](https://github.com/golang/tools/tree/master/cmd/goimports) 鏉ョ粺涓€浠g爜鏍煎紡锛岃繖 鏍峰浜庣淮鎶よ€呬互澶栫殑浜烘彁 _PR_ 涔熸槸鏈夊埄銆傛垜鍦ㄦ帴鍏ョ殑杩囩▼涓亣鍒颁竴涓� _bug_ 锛屾垜鍙嶉缁欓洦绁烇紝浠栧氨璁╂垜鎻愪簡涓� _PR_ 锛屽湪鏁翠釜杩囩▼灏辨槸杩欎釜 浠g爜鏍煎紡鐨勯棶棰橈紝瀵艰嚧鎴戝弽澶嶇殑淇敼浠g爜銆� \ No newline at end of file diff --git a/doc/md/practice/dubbo-go-quick-start.md b/doc/md/practice/dubbo-go-quick-start.md new file mode 100644 index 0000000000000000000000000000000000000000..69482f7d05e07baf28533d01c19ca5e11105c935 --- /dev/null +++ b/doc/md/practice/dubbo-go-quick-start.md @@ -0,0 +1,197 @@ +# [蹇€熷紑濮媇(https://dubbogo.github.io/dubbo-go-website/zh-cn/docs/user/quick-start.html) + +閫氳繃涓€涓� `hellowworld` 渚嬪瓙甯﹂澶у蹇€熶笂鎵婦ubbo-go妗嗘灦銆� + +鍗忚锛欴ubbo +缂栫爜锛欻essian2 +娉ㄥ唽涓績锛歓ookeeper + +## 鐜 + +* Go缂栫▼鐜 +* 鍚姩zookeeper鏈嶅姟锛屼篃鍙互浣跨敤杩滅▼瀹炰緥 + +## 浠庢湇鍔$寮€濮� + +### 绗竴姝ワ細缂栧啓 `Provider` 缁撴瀯浣撳拰鎻愪緵鏈嶅姟鐨勬柟娉� + +> [https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/user.go](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/user.go) + +1. 缂栧啓闇€瑕佽缂栫爜鐨勭粨鏋勪綋锛岀敱浜庝娇鐢� `Hessian2` 浣滀负缂栫爜鍗忚锛宍User` 闇€瑕佸疄鐜� `JavaClassName` 鏂规硶锛屽畠鐨勮繑鍥炲€煎湪dubbo涓搴擴ser绫荤殑绫诲悕銆� + +```go +type User struct { + Id string + Name string + Age int32 + Time time.Time +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} +``` + +2. 缂栧啓涓氬姟閫昏緫锛宍UserProvider` 鐩稿綋浜巇ubbo涓殑涓€涓湇鍔″疄鐜般€傞渶瑕佸疄鐜� `Reference` 鏂规硶锛岃繑鍥炲€兼槸杩欎釜鏈嶅姟鐨勫敮涓€鏍囪瘑锛屽搴攄ubbo鐨� `beans` 鍜� `path` 瀛楁銆� + +```go +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}) (*User, error) { + println("req:%#v", req) + rsp := User{"A001", "hellowworld", 18, time.Now()} + println("rsp:%#v", rsp) + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} +``` + +3. 娉ㄥ唽鏈嶅姟鍜屽璞� + +```go +func init() { + config.SetProviderService(new(UserProvider)) + // ------for hessian2------ + hessian.RegisterPOJO(&User{}) +} +``` + +### 绗簩姝ワ細缂栧啓涓荤▼搴� + +> [https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/server.go](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/app/server.go) + +1. 寮曞叆蹇呴渶鐨刣ubbo-go鍖� + +```go +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/registry/protocol" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + _ "github.com/apache/dubbo-go/filter/impl" + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" + + _ "github.com/apache/dubbo-go/protocol/dubbo" +) +``` + +2. main 鍑芥暟 + +```go +func main() { + config.Load() +} +``` + +### 绗笁姝ワ細缂栧啓閰嶇疆鏂囦欢骞堕厤缃幆澧冨彉閲� + +1. 鍙傝€� [log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/profiles/release/log.yml) 鍜� [server](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-server/profiles/release/server.yml) 缂栬緫閰嶇疆鏂囦欢銆� + +涓昏缂栬緫浠ヤ笅閮ㄥ垎锛� + +* `registries` 缁撶偣涓嬮渶瑕侀厤缃畓k鐨勬暟閲忓拰鍦板潃 + +* `services` 缁撶偣涓嬮厤缃湇鍔$殑鍏蜂綋淇℃伅锛岄渶瑕侀厤缃� `interface` 閰嶇疆锛屼慨鏀逛负瀵瑰簲鏈嶅姟鐨勬帴鍙e悕锛屾湇鍔$殑key瀵瑰簲绗竴姝ヤ腑 `Provider` 鐨� `Reference` 杩斿洖鍊� + + +2. 鎶婁笂闈㈢殑涓や釜閰嶇疆鏂囦欢鍒嗗埆閰嶇疆涓虹幆澧冨彉閲� + +```shell +export CONF_PROVIDER_FILE_PATH="xxx" +export APP_LOG_CONF_FILE="xxx" +``` + +## 鎺ョ潃鏄鎴风 + +### 绗竴姝ワ細缂栧啓瀹㈡埛绔� `Provider` + +> [https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/user.go](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/user.go) + +1. 鍙傝€冩湇鍔$绗竴姝ョ殑绗竴鐐广€� + +2. 涓庢湇鍔$涓嶅悓鐨勬槸锛屾彁渚涙湇鍔$殑鏂规硶浣滀负缁撴瀯浣撶殑鍙傛暟锛屼笉闇€瑕佺紪鍐欏叿浣撲笟鍔¢€昏緫銆傚彟澶栵紝`Provider` 涓嶅搴攄ubbo涓殑鎺ュ彛锛岃€屾槸瀵瑰簲涓€涓疄鐜般€� + + +```go +type UserProvider struct { + GetUser func(ctx context.Context, req []interface{}, rsp *User) error +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} +``` + +3. 娉ㄥ唽鏈嶅姟鍜屽璞� + +```go +func init() { + config.SetConsumerService(userProvider) + hessian.RegisterPOJO(&User{}) +} +``` + +### 绗簩姝ワ細缂栧啓瀹㈡埛绔富绋嬪簭 + +> [https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/client.go](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/app/client.go) + +1. 寮曞叆蹇呴渶鐨刣ubbo-go鍖� + +```go +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/registry/protocol" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + _ "github.com/apache/dubbo-go/filter/impl" + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" + + _ "github.com/apache/dubbo-go/protocol/dubbo" +) +``` + +2. main 鍑芥暟 + +```go +func main() { + config.Load() + time.Sleep(3e9) + + println("\n\n\nstart to test dubbo") + user := &User{} + err := userProvider.GetUser(context.TODO(), []interface{}{"A001"}, user) + if err != nil { + panic(err) + } + println("response result: %v\n", user) +} +func println(format string, args ...interface{}) { + fmt.Printf("\033[32;40m"+format+"\033[0m\n", args...) +} +``` + +### 绗笁姝ワ細缂栧啓閰嶇疆鏂囦欢骞堕厤缃幆澧冨彉閲� + +1. 鍙傝€� [log](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/log.yml) 鍜� [client](https://github.com/dubbogo/dubbo-samples/blob/master/golang/helloworld/dubbo/go-client/profiles/release/client.yml) 缂栬緫閰嶇疆鏂囦欢銆� + +涓昏缂栬緫浠ヤ笅閮ㄥ垎锛� + +* `registries` 缁撶偣涓嬮渶瑕侀厤缃畓k鐨勬暟閲忓拰鍦板潃 + +* `references` 缁撶偣涓嬮厤缃湇鍔$殑鍏蜂綋淇℃伅锛岄渶瑕侀厤缃� `interface` 閰嶇疆锛屼慨鏀逛负瀵瑰簲鏈嶅姟鐨勬帴鍙e悕锛屾湇鍔$殑key瀵瑰簲绗竴姝ヤ腑 `Provider` 鐨� `Reference` 杩斿洖鍊� + + +2. 鎶婁笂闈㈢殑涓や釜閰嶇疆鏂囦欢璐瑰埆閰嶇疆涓虹幆澧冨彉閲忥紝涓洪槻姝og鐨勭幆澧冨彉閲忓拰鏈嶅姟绔殑log鐜鍙橀噺鍐茬獊锛屽缓璁墍鏈夌殑鐜鍙橀噺涓嶈鍋氬叏灞€閰嶇疆锛屽湪褰撳墠璧锋晥鍗冲彲銆� + +```shell +export CONF_CONSUMER_FILE_PATH="xxx" +export APP_LOG_CONF_FILE="xxx" +``` \ No newline at end of file diff --git a/doc/md/registry-center/dubbo-go-registry-center--nacos.md b/doc/md/registry-center/dubbo-go-registry-center--nacos.md new file mode 100644 index 0000000000000000000000000000000000000000..5ee3f77ccc4f88c35891737d3df22fae076229ab --- /dev/null +++ b/doc/md/registry-center/dubbo-go-registry-center--nacos.md @@ -0,0 +1,116 @@ +# [瑙f瀯 Dubbo-go 鐨勬牳蹇冩敞鍐屽紩鎿� Nacos](https://my.oschina.net/dubbogo/blog/4608576) + +杩戝嚑骞达紝闅忕潃Go璇█绀惧尯閫愭笎鍙戝睍鍜屽.澶э紝瓒婃潵瓒婂鐨勫叕鍙稿紑濮嬪皾璇曢噰鐢℅o鎼缓寰湇鍔′綋绯伙紝涔熸秾鐜颁簡涓€鎵笹o鐨勫井鏈嶅姟妗嗘灦锛屽go-micro銆乬o-kit銆丏ubbo-go绛夛紝璺熷井鏈嶅姟娌荤悊鐩稿叧鐨勭粍浠朵篃閫愭笎寮€濮嬪湪Go鐢熸€佸彂鍔涳紝濡係entinel銆丠ystrix绛夐兘鎺ㄥ嚭浜咷o璇█鐗堟湰锛岃€屼綔涓哄井鏈嶅姟妗嗘灦鐨勬牳蹇冨紩鎿�--娉ㄥ唽涓績锛屼篃鏄繀涓嶅彲缂哄皯鐨勭粍浠讹紝甯傞潰宸茬粡鏈夊娆炬敞鍐屼腑蹇冩敮鎸丟o璇█锛屽簲璇ュ浣曢€夋嫨鍛紵鎴戜滑鍙互瀵圭洰鍓嶄富娴佺殑鏀寔Go璇█鐨勬敞鍐屼腑蹇冨仛涓姣斻€� + + + +鏍规嵁涓婅〃鐨勫姣旀垜浠彲浠ヤ粠浠ヤ笅鍑犱釜缁村害寰楀嚭缁撹锛� + +* 鐢熸€�:鍚勬敞鍐屼腑蹇冨Go璇█閮芥湁鏀寔锛屼絾鏄疦acos銆� Consul銆丒tcd 绀惧尯娲昏穬锛寊ookeeper鍜孍ureka绀惧尯娲昏穬搴﹁緝浣庯紱 +* 鏄撶敤鎬э細Nacos銆丒ureka銆丆onsul閮芥湁鐜版垚鐨勭鎺у钩鍙帮紝Etcd銆亃ookeeper鏈韩浣滀负kv瀛樺偍锛屾病鏈夌浉搴旂殑绠℃帶骞冲彴锛孨acos鏀寔涓枃鐣岄潰锛屾瘮杈冪鍚堝浗浜轰娇鐢ㄤ範鎯紱 +* 鍦烘櫙鏀寔锛欳P妯″瀷涓昏閽堝寮轰竴鑷村満鏅紝濡傞噾铻嶇被锛孉P妯″瀷閫傜敤浜庨珮鍙敤鍦烘櫙锛孨acos鍙互鍚屾椂婊¤冻涓ょ鍦烘櫙锛孍ureka涓昏婊¤冻楂樺彲鐢ㄥ満鏅紝Consul銆乑ookeepr銆丒tcd涓昏婊¤冻寮轰竴鑷村満鏅紝姝ゅNacos鏀寔浠庡叾瀹冩敞鍐屼腑蹇冨悓姝ユ暟鎹紝鏂逛究鐢ㄦ埛娉ㄥ唽涓績杩佺Щ锛� +* 鍔熻兘瀹屾暣鎬э細鎵€鏈夋敞鍐屼腑蹇冮兘鏀寔鍋ュ悍妫€鏌ワ紝Nacos銆丆onsul鏀寔鐨勬鏌ユ柟寮忚緝澶氾紝婊¤冻涓嶅悓搴旂敤鍦烘櫙锛孼ookeeper閫氳繃keep alive鏂瑰紡锛岃兘瀹炴椂鎰熺煡瀹炰緥鍙樺寲锛汵acos銆丆onsul鍜孍ureka閮芥敮鎸佽礋杞藉潎琛$瓥鐣ワ紝Nacos閫氳繃Metadata selector鏀寔鏇寸伒娲荤殑绛栫暐锛涙澶栵紝Nacos銆丒ureka閮芥敮鎸侀洩宕╀繚鎶わ紝閬垮厤鍥犱负杩囧鐨勫疄渚嬩笉鍋ュ悍瀵瑰仴搴风殑瀹炰緥閫犳垚闆穿鏁堝簲銆� + +缁煎悎涓婇潰鍚勭淮搴︾殑瀵规瘮锛屽彲浠ヤ簡瑙e埌Nacos浣滀负娉ㄥ唽涓績鏈変竴瀹氱殑浼樺娍锛岄偅涔堝畠瀵笹o寰湇鍔$敓鎬佺殑闆嗘垚鍋氬緱濡備綍锛熸帴涓嬫潵鎴戜滑棣栧厛鎺㈢储涓婲acos鏄浣曚笌Dubbo-go闆嗘垚銆� + +# 寮曡█ + +Dubbo-go鐩墠鏄疍ubbo澶氳瑷€鐢熸€佷腑鏈€鐏儹鐨勪竴涓」鐩紝浠�2016骞村彂甯冭嚦浠婏紝宸茬粡璧拌繃5涓勾澶淬€傛渶杩戯紝Dubbo-go鍙戝竷浜唙1.5鐗堟湰锛屽叏闈㈠吋瀹笵ubbo 2.7.x鐗堟湰锛屾敮鎸佷簡搴旂敤缁村害鐨勬湇鍔℃敞鍐屼笌鍙戠幇锛屽拰涓绘祦鐨勬敞鍐屾ā鍨嬩繚鎸佷竴鑷达紝鏍囧織鐫€Dubbo-go鍚戜簯鍘熺敓杩堝嚭浜嗗叧閿殑涓€姝ャ€備綔涓洪┍鍔ㄦ湇鍔¤繍杞殑鏍稿績寮曟搸--娉ㄥ唽涓績锛屽湪鍒囨崲鍒板簲鐢ㄧ淮搴︾殑娉ㄥ唽妯″瀷鍚庯紝涔熼渶瑕佸仛鐩稿簲鐨勯€傞厤锛屾湰鏂囧皢瑙f瀽濡備綍浠acos涓烘牳蹇冨紩鎿庡疄鐜板簲鐢ㄧ淮搴︾殑鏈嶅姟娉ㄥ唽涓庡彂鐜帮紝骞朵笖缁欏嚭鐩稿簲鐨勫疄璺垫渚嬨€傛澶栵紝鏈枃浠g爜鍩轰簬Dubbo-go v1.5.1锛孨acos-SDK-go v1.0.0鍜孨acos v1.3.2銆� + +# 鏈嶅姟娉ㄥ唽涓庡彂鐜版灦鏋� + +浠庢灦鏋勪腑锛屾垜浠彲浠ョ湅鍒帮紝涓庢帴鍙g骇鍒殑鏈嶅姟娉ㄥ唽鍙戠幇涓嶅悓鐨勬槸锛孌ubbo-go鐨刾rovider鍚姩鍚庝細璋冪敤Nacos-go-sdk鐨凴egisterInstance鎺ュ彛鍚慛acos娉ㄥ唽鏈嶅姟瀹炰緥锛屾敞鍐岀殑鏈嶅姟鍚嶅嵆涓哄簲鐢ㄥ悕绉帮紝鑰屼笉鏄帴鍙e悕绉般€侰onusmer鍚姩鍚庡垯浼氳皟鐢⊿ubscribe鎺ュ彛璁㈤槄璇ュ簲鐢ㄧ殑鏈嶅姟瀹炰緥鍙樺寲锛屽苟瀵圭殑瀹炰緥鍙戣捣鏈嶅姟璋冪敤銆� + + + +# 鏈嶅姟妯″瀷 + +鍥�3鏄垜浠珼ubbo-go鐨勫簲鐢ㄧ淮搴︽湇鍔″彂鐜版ā鍨嬶紝涓昏鏈夋湇鍔″拰瀹炰緥涓や釜灞傜骇鍏崇郴锛屾湇鍔″疄渚嬬殑灞炴€т富瑕佸寘鍚疄渚婭d銆佷富鏈哄湴鍧€銆佹湇鍔$鍙c€佹縺娲荤姸鎬佸拰鍏冩暟鎹€傚浘4涓篘acos鐨勬湇鍔″垎绾у瓨鍌ㄦā鍨嬶紝鍖呭惈鏈嶅姟銆侀泦缇ゅ拰瀹炰緥涓変釜灞傛銆備袱鑰呭姣旓紝澶氫簡涓€涓泦缇ょ淮搴︾殑灞傜骇锛岃€屼笖瀹炰緥灞炴€т俊鎭兘澶熷畬鍏ㄥ尮閰嶃€傛墍浠ュ湪Dubbo-go灏嗗簲鐢ㄦ湇鍔″疄渚嬫敞鍐屽埌Nacos鏃讹紝鎴戜滑鍙渶瑕佸皢闆嗙兢璁剧疆涓洪粯璁ら泦缇わ紝鍐嶅~鍏呮湇鍔″拰瀹炰緥鐨勭浉鍏冲睘鎬э紝鍗冲彲瀹屾垚鏈嶅姟妯″瀷涓婄殑鍖归厤銆傛澶朜acos鍙互灏嗘湇鍔℃敞鍐屽埌涓嶅悓鐨凬amespace涓嬶紝瀹炵幇澶氱鎴风殑闅旂銆�  + + + +# 鏈嶅姟瀹炰緥蹇冭烦缁存寔 + +Dubbo-go鐨凱rovider鍦ㄥ悜Nacos娉ㄥ唽搴旂敤鏈嶅姟瀹炰緥淇℃伅鍚庯紝闇€瑕佷富鍔ㄤ笂鎶ュ績璺筹紝璁㎞acos鏈嶅姟绔劅鐭ュ疄渚嬬殑瀛樻椿涓庡惁锛屼互鍒ゆ柇鏄惁灏嗚鑺傜偣浠庡疄渚嬪垪琛ㄤ腑绉婚櫎銆傜淮鎶ゅ績璺崇殑宸ヤ綔鏄湪Nacos-SDK-go瀹屾垚鐨勶紝浠庡浘5浠g爜涓彲浠ョ湅鍒帮紝褰揇ubbo-go璋冪敤RegisterInstance娉ㄥ唽涓€涓湇鍔″疄渚嬫椂锛孲DK闄や簡璋冪敤Nacos鐨凴egister API涔嬪锛岃繕浼氳皟鐢ˋddBeatInfo锛屽皢鏈嶅姟瀹炰緥淇℃伅娣诲姞鍒版湰鍦扮紦瀛橈紝閫氳繃鍚庡彴鍗忕▼瀹氭湡鍚慛acos鍙戦€佹湇鍔″疄渚嬩俊鎭紝淇濇寔蹇冭烦銆傚綋鏈嶅姟涓嬬嚎鏃讹紝鍙互閫氳繃璋冪敤DeRegisterInstance鎵ц鍙嶆敞鍐岋紝骞剁Щ闄ゆ湰鍦扮殑蹇冭烦淇濇寔浠诲姟锛孨acos瀹炰緥鍒楄〃涓篃浼氬皢璇ュ疄渚嬬Щ闄ゃ€� + + + +# 璁㈤槄鏈嶅姟瀹炰緥鍙樺寲 + +Dubbo-go鐨凜onsumer鍦ㄥ惎鍔ㄧ殑鏃跺€欎細璋冪敤Nacos-SDK-go鐨凷ubscribe鎺ュ彛锛岃鎺ュ彛鍏ュ弬濡傚浘6锛岃闃呯殑鏃跺€欏彧闇€瑕佷紶閫扴erviceName鍗冲簲鐢ㄥ悕鍜屽洖璋冨嚱鏁癝ubscribeCallback锛孨acos鍦ㄦ湇鍔″疄渚嬪彂鐢熷彉鍖栫殑鏃跺€欏嵆鍙€氳繃鍥炶皟鍑芥暟閫氱煡Dubbo-go銆侼acos-SDK-go鏄浣曟劅鐭acos鐨勬湇鍔″疄渚嬪彉鍖栫殑鍛紵涓昏鏈変袱绉嶆柟寮忥細 + +* Nacos鏈嶅姟绔富鍔ㄦ帹閫侊紝Nacos-SDK-go鍦ㄥ惎鍔ㄧ殑鏃跺€欎細鐩戝惉涓€涓猆DP绔彛锛岃绔彛鍦ㄨ皟鐢∟acos Register API鐨勬椂鍊欎綔涓哄弬鏁颁紶閫掞紝Nacos浼氳褰旾p鍜岀鍙o紝褰撴湇鍔″疄渚嬪彂鐢熷彉鍖栨椂锛孨acos浼氬鎵€鏈夌洃鍚鏈嶅姟鐨処p鍜岀鍙e彂閫乁DP璇锋眰锛屾帹閫佸彉鍖栧悗鐨勬湇鍔″疄渚嬩俊鎭€� + +* Nacos-SDK-go瀹氭湡鏌ヨ锛孲DK浼氬璁㈤槄鐨勬湇鍔″疄渚嬪畾鏃惰皟鐢ㄦ煡璇㈡帴鍙o紝濡傛灉鏌ヨ鏈夊彉鍖栧垯閫氳繃鍥炶皟鎺ュ彛閫氱煡Dubbo-go銆備綔涓哄厹搴曠瓥鐣ヤ繚璇丯acos鏈嶅姟绔帹閫佸け璐ュ悗锛屼粛鑳芥劅鐭ュ埌鍙樺寲銆� + +  + + +姝ゅNacos-SDK-go杩樻敮鎸佹帹绌轰繚鎶わ紝褰揘acos鎺ㄩ€佺殑瀹炰緥鍒楄〃涓虹┖鏃讹紝涓嶆洿鏂版湰鍦扮紦瀛橈紝涔熶笉閫氱煡Dubbo-go鍙樻洿锛岄伩鍏岰onsumer鏃犲彲鐢ㄥ疄渚嬭皟鐢紝閫犳垚鏁呴殰銆傚悓鏃讹紝SDK杩樻敮鎸佹湇鍔″疄渚嬩俊鎭湰鍦版寔涔呭寲瀛樺偍锛屽彲浠ヤ繚璇佸湪Nacos鏈嶅姟鏁呴殰杩囩▼涓紝Consumer閲嶅惎涔熻兘鑾峰彇鍒板彲鐢ㄥ疄渚嬶紝鍏峰瀹圭伨鏁堟灉銆� + +# 鑼冧緥瀹炶返 + +## 鐜鍑嗗 + +dubbo-go samples浠g爜涓嬭浇锛歔https://github.com/apache/dubbo-samples/tree/master/golang锛屽熀浜嶯acos娉ㄥ唽涓績鐨勫簲鐢ㄧ骇鏈嶅姟鍙戠幇鐨刪ello](https://github.com/apache/dubbo-samples/tree/master/golang%EF%BC%8C%E5%9F%BA%E4%BA%8ENacos%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E7%9A%84%E5%BA%94%E7%94%A8%E7%BA%A7%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E7%9A%84hello) world浠g爜鐩綍鍦� registry/servicediscovery/nacos銆� + + + +Nacos鏈嶅姟绔惌寤猴紝鍙傝€冨畼鏂规枃妗o細[https://nacos.io/zh-cn/docs/quick-start.html锛屾垨鑰呬娇鐢ㄥ畼鏂规彁渚涚殑鍏叡Nacos鏈嶅姟锛歨ttp://console.nacos.io/nacos(璐﹀彿瀵嗙爜:nacos锛屼粎渚涙祴璇�)锛屾垨鑰呰喘涔伴樋閲屼簯鏈嶅姟锛歨ttps://help.aliyun.com/document\_detail/139460.html?spm=a2c4g.11186623.6.559.d7e264b7bLpZIs](https://nacos.io/zh-cn/docs/quick-start.html%EF%BC%8C%E6%88%96%E8%80%85%E4%BD%BF%E7%94%A8%E5%AE%98%E6%96%B9%E6%8F%90%E4%BE%9B%E7%9A%84%E5%85%AC%E5%85%B1Nacos%E6%9C%8D%E5%8A%A1%EF%BC%9Ahttp://console.nacos.io/nacos(%E8%B4%A6%E5%8F%B7%E5%AF%86%E7%A0%81:nacos%EF%BC%8C%E4%BB%85%E4%BE%9B%E6%B5%8B%E8%AF%95)%EF%BC%8C%E6%88%96%E8%80%85%E8%B4%AD%E4%B9%B0%E9%98%BF%E9%87%8C%E4%BA%91%E6%9C%8D%E5%8A%A1%EF%BC%9Ahttps://help.aliyun.com/document_detail/139460.html?spm=a2c4g.11186623.6.559.d7e264b7bLpZIs) + +## Server绔惌寤� + +杩涘叆registry/servicediscovery/nacos/go-server/profiles鏂囦欢锛屽彲浠ョ湅鍒版湁dev銆乺elease鍜宼est涓変釜鏂囦欢澶癸紝鍒嗗埆瀵瑰簲寮€鍙戙€佹祴璇曞拰鐢熶骇閰嶇疆銆傛垜浠娇鐢╠ev閰嶇疆鏉ユ惌寤哄紑鍙戠幆澧冿紝dev鏂囦欢涓嬫湁log.yml鍜宻erver.yml鏂囦欢锛屼笅闈㈠server.yml閰嶇疆杩涜淇敼銆� + +remote閰嶇疆锛岃繖閲屼娇鐢ㄥ叕鍏辩殑Nacos鏈嶅姟锛宎ddress鏀寔閰嶇疆澶氫釜鍦板潃锛岀敤閫楀彿鍒嗗壊銆俻arams鍙傛暟閰嶇疆nacos-sdk鐨勬棩蹇楃洰褰曘€� + +```Yaml +remote: + nacos: + address: "console.nacos.io:80" + timeout: "5s" + params: + logDir: "/data/nacos-sdk/log" +configCenter閰嶇疆 +config_center: + protocol: "nacos" + address: "console.nacos.io:80" +``` + +閰嶇疆server绔幆澧冨彉閲� + +```Bash +export CONF_PROVIDER_FILE_PATH=server绔殑server.yml鏂囦欢璺緞 +export APP_LOG_CONF_FILE=server绔殑log.yml鏂囦欢璺緞 +``` + +杩涘叆registry/servicediscovery/nacos/go-server/app锛岃繍琛宻erver.go鐨刴ain鏂规硶锛屽彲浠ヤ粠Nacos鐨勬帶鍒跺彴锛圼http://console.nacos.io/nacos/#/serviceManagement?dataId=&group=&appName=&namespace=锛塢(http://console.nacos.io/nacos/#/serviceManagement?dataId=&group=&appName=&namespace=%EF%BC%89) + +鐪嬪埌锛屽簲鐢╱ser-info-server宸茬粡娉ㄥ唽鎴愬姛銆� + + + + + +## Client绔惌寤� + +client鐨勯厤缃枃浠跺湪registry/servicediscovery/nacos/go-server/profiles鐩綍涓嬶紝闇€瑕佷慨鏀圭殑鍦版柟璺焥erver绔竴鏍凤紝杩欓噷涓嶈禈杩般€� + +閰嶇疆client绔幆澧冨彉閲� + +```Bash +export CONF_CONSUMER_FILE_PATH=client绔殑server.yml鏂囦欢璺緞 +export APP_LOG_CONF_FILE=client绔殑log.yml鏂囦欢璺緞 +``` + +杩涘叆registry/servicediscovery/nacos/go-client/app锛岃繍琛宑lient.go鐨刴ain鏂规硶锛岀湅鍒板涓嬫棩蹇楄緭鍑猴紝琛ㄧず璋冪敤server绔垚鍔熴€� + + + +浣滆€咃細鏉庡織楣� + +Github璐﹀彿锛歀zp0412锛孨acos-SDK-go浣滆€咃紝Apache/Dubbo-go Contributor銆傜幇灏辫亴浜庨樋閲屼簯浜戝師鐢熷簲鐢ㄥ钩鍙帮紝涓昏鍙備笌鏈嶅姟鍙戠幇銆丆oreDNS銆丼erviceMesh鐩稿叧宸ヤ綔锛岃礋璐f帹鍔∟acos Go寰湇鍔$敓鎬佸缓璁俱€� + +鐩稿叧閾炬帴 Nacos-SDK-go椤圭洰鍦板潃锛歔https://github.com/nacos-group/nacos-sdk-go](https://github.com/nacos-group/nacos-sdk-go) Nacos golang鐢熸€佷氦娴佺兢锛�23191211 Nacos椤圭洰鍦板潃锛歔https://nacos.io/](https://nacos.io/) Nacos绀惧尯浜ゆ祦缇わ細30438813 + +Dubbo-go 椤圭洰鍦板潃锛歔https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) Dubbo-go绀惧尯浜ゆ祦缇わ細23331795 \ No newline at end of file diff --git a/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md b/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md new file mode 100644 index 0000000000000000000000000000000000000000..14da444755a9c0e9f1fd8e6b7cef6db563b5a746 --- /dev/null +++ b/doc/md/routing-rule/how-to-implement-routing-rule-in-dubbo-go.md @@ -0,0 +1,215 @@ +# [dubbo-go 涓浣曞疄鐜拌矾鐢辫鍒欏姛鑳絔(https://zouyx.github.io/posts/2020/03/30/dubbo-go%20%E4%B8%AD%E5%A6%82%E4%BD%95%E5%AE%9E%E7%8E%B0%E8%B7%AF%E7%94%B1%E8%A7%84%E5%88%99%E5%8A%9F%E8%83%BD.html) + +dubbo-go 涓浣曞疄鐜拌矾鐢辫鍒欏姛鑳� + +# Let鈥榮 Go! + +* * * + +鏈€杩戝湪 Apache/dubbo-go锛堜互涓嬬畝绉� dubbo-go 锛夌ぞ鍖轰腑锛岃矾鐢辫鍒欑獊鐒舵垚浜嗗懠澹版渶楂樼殑鍔熻兘涔嬩竴銆傞偅鍒板簳涓轰粈涔堥渶瑕佽矾鐢辫鍒欙紵 + +鍏堣矾鐢辫鍒欓渶瑕佸疄鐜扮殑鍔熻兘锛� + +璺敱瑙勫垯锛� routing rule 锛夋槸涓轰簡鏀瑰彉缃戠粶娴侀噺鎵€缁忚繃鐨勯€斿緞鑰屼慨鏀硅矾鐢变俊鎭殑鎶€鏈紝涓昏閫氳繃鏀瑰彉璺敱灞炴€э紙鍖呮嫭鍙揪鎬э級鏉ュ疄鐜般€傚湪鍙戣捣涓€娆� RPC 璋冪敤鍓嶈捣鍒拌繃婊ょ洰鏍囨湇鍔″櫒鍦板潃鐨勪綔鐢紝杩囨护鍚庣殑鍦板潃鍒楄〃锛屽皢浣滀负娑堣垂绔渶缁堝彂璧� RPC 璋冪敤鐨勫閫夊湴鍧€銆� + +璇曟兂璇ヤ笅鍦烘櫙锛氫娇鐢� dubbo-go 鍦ㄧ敓浜х幆澧冧笂锛屾帓闄ら鍙戝竷鏈恒€備娇鐢ㄨ矾鐢辫鍒欏疄鐜颁笉鏄緢鍚堥€傚悧锛� + +铏界劧鐭ラ亾浜嗚矾鐢辫鍒欓渶瑕佸疄鐜颁粈涔堝姛鑳斤紝浣嗚繕涓嶈冻浠ュ疄鐜颁竴涓畬鏁寸殑璺敱瑙勫垯鍔熻兘銆傞櫎姝や箣澶栵紝杩橀渶瑕佺煡閬撳浣曟柟渚跨殑绠$悊璺敱瑙勫垯銆� + +# 鐩爣 + +缁间笂鎵€杩帮紝鍙互鎬荤粨鍑轰互涓� **鐩爣** + +* 鏀寔鏂逛究鎵╁睍璺敱瑙勫垯鐨勯厤缃紱 +* 鍙互鏂逛究鐨勭鐞嗚矾鐢辫鍒欓厤缃紝濡傛敮鎸佹湰鍦颁笌杩滅▼閰嶇疆涓績绠$悊锛� +* 涓� Dubbo 鐜版湁鐨勯厤缃腑蹇冨唴鐨勮矾鐢辫鍒欓厤缃枃浠跺吋瀹癸紝闄嶄綆鍦ㄦ柊澧炶瑷€鏍堢殑瀛︿範鍙婁娇鐢ㄦ垚鏈紱 + +# 璺敱瑙勫垯璁捐 + +鍦ㄨ璁′箣鍒濓紝棣栧厛瑕佽€冭檻鐨勬槸璺敱瑙勫垯搴旇鏀惧湪鏁翠釜鏈嶅姟娌荤悊鍛ㄦ湡鐨勫摢涓樁娈靛憿锛� + +鏈変簺璇昏€呭彲鑳戒細鏈夌偣鍥版儜锛屾垜杩炴灦鏋勫浘閮戒笉鐭ラ亾锛屽浣曡€冭檻鍦ㄥ摢涓樁娈碉紵涓嶆€曪紝涓嬪浘椹笂缁欎綘瑙f儜銆� + + + +鍙互鐪嬪埌鍥句腑鐨� Router 灏辨槸璺敱瑙勫垯鎻掑叆鐨勪綅缃紝鐩墠璺敱瑙勫垯涓昏鐢ㄤ簬鎺у埗 Consumer 鍒� Provider 涔嬮棿鐨勭綉缁滄祦閲忕殑璺敱璺緞銆� + +闄ゆ涔嬪锛岃繕鏈夊嚑涓棶棰樻槸闇€瑕佷紭鍏堣€冭檻锛� + +1.闇€瑕佷粈涔堝姛鑳斤紵 + +* 閫氳繃閰嶇疆淇℃伅鐢熸垚璺敱瑙勫垯锛屽寘鎷細璇诲彇骞惰В鏋愭湰鍦伴厤缃枃浠讹紝璇诲彇骞惰В鏋愰厤缃腑蹇冪殑閰嶇疆銆備互璐d换閾炬ā寮忎覆鑱旇捣鏉ャ€� +* 閫氳繃璺敱瑙勫垯锛屽尮閰嶆湰鍦颁俊鎭笌杩滅鏈嶅姟淇℃伅锛岃繃婊ゅ嚭鍙互璋冪敤鐨勮繙绔妭鐐癸紝鍐嶈繘琛岃礋杞藉潎琛°€� + +2.濡備綍璁捐鎺ュ彛锛� + +閫氳繃绗竴鐐癸紝鎴戜滑鑳借璁″嚭浠ヤ笅鎺ュ彛鏉ュ疄鐜版墍闇€鐨勫姛鑳姐€� + +* 璺敱瑙勫垯鎺ュ彛锛氱敤浜庤矾鐢辫鍒欒繃婊ゅ嚭鍙互璋冪敤鐨勮繙绔妭鐐广€� + +* 璺敱瑙勫垯璐d换閾炬帴鍙o細鍏佽鎵ц澶氫釜璺敱瑙勫垯銆� + +* 閰嶇疆淇℃伅鐢熸垚璺敱瑙勫垯鎺ュ彛锛氳В鏋愬唴閮ㄩ厤缃俊鎭紙common.URL锛夌敓鎴愬搴旂殑璺敱瑙勫垯銆� + +* 閰嶇疆鏂囦欢鐢熸垚璺敱瑙勫垯鎺ュ彛锛氳В鏋愰厤缃枃浠剁敓鎴愬搴旂殑璺敱瑙勫垯銆� + + +3.濡備綍瀹炵幇鏈湴涓庤繙绋嬭矾鐢辫鍒欓厤缃姞杞斤紵 + +* 鏈湴璺敱瑙勫垯閰嶇疆锛氬湪鍘熼厤缃姞杞介樁娈碉紝鏂板璇诲彇璺敱閰嶇疆鏂囦欢銆備娇鐢� `FIleRouterFactory` 瑙f瀽鍚庯紝鐢熸垚瀵瑰簲璺敱瑙勫垯锛屾斁缃埌鍐呭瓨涓鐢ㄣ€� +* 杩滅▼璺敱瑙勫垯閰嶇疆锛氬湪 zookeeper 娉ㄥ唽骞剁洃鍚潤鎬佽祫婧愮洰褰曞悗銆傝鍙栭潤鎬佽祫婧愶紝绛涢€夌鍚堣矾鐢辫鍒欓厤缃俊鎭紝閫氳繃 `RouterFactory` 鐢熸垚瀵瑰簲璺敱瑙勫垯锛屾斁缃埌鍐呭瓨涓鐢ㄣ€� + +## Router + +鍖归厤鍙婅繃婊よ繙绋嬪疄渚嬬殑璺敱瑙勫垯銆�  鐩墠宸叉湁瀹炵幇绫诲寘鎷細 + +* listenableRouter: +* AppRouter锛� +* ConditionRouter锛� +* HealthCheckRouter: +* FileConditionRouter: + +## RouterChain + +鎵ц澶氫釜璺敱瑙勫垯鐨勮矗浠婚摼銆�  + +## FIleRouterFactory + +鐢熸垚瑙f瀽閰嶇疆鏂囦欢鐢熸垚璺敱瑙勫垯鐨勫伐鍘傜被銆�  + +## RouterFactory + +閫氳繃閰嶇疆淇℃伅鐢熸垚璺敱瑙勫垯鐨勫伐鍘傜被銆�  + +# 瀹炵幇 + + + +瀹炵幇璺敱瑙勫垯浠ュ吋瀹� dubbo 涓洪瑕佺洰鏍囷紝闄嶄綆浣跨敤鑰呯殑瀛︿範鎴愭湰涓鸿緟鍔╃洰鏍囥€備笌閰嶇疆涓績妯″潡鐩哥粨鍚堬紝瀹炵幇璺敱瑙勫垯杩滅▼缁熶竴绠$悊涓庝笅鍙戙€� + +## 瑙勫垯绫诲瀷 + +涓嬮潰鍏堟潵浠嬬粛涓€涓嬫湁鍝簺鍏蜂綋鐨勮矾鐢辫鍒欏疄鐜般€� + +### **鏉′欢璺敱** + +dubbo-go 涓涓€涓敮鎸佺殑璺敱瑙勫垯锛屽厑璁哥敤鎴烽€氳繃閰嶇疆鏂囦欢鍙婇厤缃腑蹇冪鐞嗚矾鐢辫鍒欍€� + +涓庝箣鐩镐技鐨勪竴涓蹇垫槸 dubbo-go 閲岄潰鐨� group 姒傚康锛屼絾鏄潯浠惰矾鐢辨彁渚涗簡鏇村姞缁嗙矑搴︾殑鎺у埗鎵嬫鍜屾洿鍔犱赴瀵岀殑琛ㄨ揪璇箟銆傛瘮杈冨吀鍨嬬殑浣跨敤鍦烘櫙鏄粦鐧藉悕鍗曡缃紝鐏板害浠ュ強娴嬭瘯绛夈€� + +### **鍋ュ悍妫€鏌ヨ矾鐢�** + +鍦� RPC 璋冪敤涓紝鎴戜滑甯屾湜灏藉彲鑳藉湴灏嗚姹傚懡涓埌閭d簺澶勭悊鑳藉姏蹇€佸浜庡仴搴风姸鎬佺殑瀹炰緥锛岃璺敱鐨勫姛鑳藉氨鏄€氳繃鏌愮绛栫暐鏂畾鏌愪釜瀹炰緥涓嶅仴搴凤紝骞跺皢鍏舵帓闄ゅ湪鍊欓€夎皟鐢ㄥ垪琛紝浼樺厛璋冪敤閭d簺鍋ュ悍鐨勫疄渚嬨€傝繖閲岀殑鈥濆仴搴封€濆彲浠ユ槸鎴戜滑鑷繁瀹氫箟鐨勭姸鎬侊紝榛樿瀹炵幇鍗冲綋閿欒姣斾緥鍒拌揪鏌愪竴涓槇鍊兼椂鎴栬€呰姹傛椿璺冩暟澶т簬涓婇檺鍒欒涓哄叾涓嶅仴搴凤紝鍏佽鐢ㄦ埛鎵╁睍鍋ュ悍妫€娴嬬瓥鐣ャ€� + +鍦ㄦ垜浠湇鍔℃不鐞嗛噷闈紝鏍稿績鐨勯棶棰樺叾瀹炲氨鍦ㄤ簬濡備綍鍒ゆ柇涓€涓疄渚嬫槸鍚﹀彲鐢ㄣ€傛棤璁烘槸璐熻浇鍧囪 銆� + +鐔旀柇杩樻槸闄愭祦锛岄兘鏄杩欎釜闂鐨勮В绛斻€傛墍浠ワ紝杩欎釜 feature 鏄竴涓緢濂界殑灏濊瘯銆傚洜涓烘垜浠帴涓嬫潵璁″垝鎻愪緵鐨勭壒鎬э紝鍩轰簬瑙勫垯鐨勯檺娴佷互鍙婂姩鎬侀檺娴侊紝閮芥槸瑕佽В鍐� 鈥滃浣曟柇瀹氫竴涓疄渚嬫槸鍚﹀彲鐢ㄢ€� 杩欎箞涓€涓棶棰樸€� + +鎵€浠ユ杩庡ぇ瀹朵娇鐢ㄨ繖涓壒鎬э紝骞跺悜绀惧尯鍙嶉鍚勮嚜璁惧畾鐨勫仴搴锋寚鏍囥€傝繖瀵规垜浠帴涓嬫潵鐨勫伐浣滀細鏈夊緢澶х殑甯姪銆� + +### **鏍囩璺敱** + +浠� Provider 涓虹淮搴︼紝閫氳繃灏嗘煇涓€涓垨澶氫釜鏈嶅姟鐨勬彁渚涜€呭垝鍒嗗埌鍚屼竴涓垎缁勶紝绾︽潫娴侀噺鍙湪鎸囧畾鍒嗙粍涓祦杞紝浠庤€屽疄鐜版祦閲忛殧绂荤殑鐩殑锛屽彲浠ヤ綔涓鸿摑缁垮彂甯冦€佺伆搴﹀彂甯冪瓑鍦烘櫙鐨勮兘鍔涘熀纭€銆� + +* 闈欐€佹墦鏍囷細鏍规嵁閰嶇疆鏂囦欢鎵€閰嶇疆鐨勬爣绛撅紝鍥哄畾缁� Provider 璁剧疆鏍囩銆� +* 鍔ㄦ€佹墦鏍囷細鍩轰簬鍋ュ悍妫€鏌ヨ矾鐢憋紝鏍规嵁鏈嶅姟涓嶅悓鏃跺埢锛屼笉鍚岀姸鎬侊紝鍔ㄦ€佸湪 Provider 璁剧疆閫傚悎鐨勬爣绛俱€� + +## 鍒嗘瀽 + +鎺ョ潃锛屼互鏉′欢璺敱鍦� zookeeper 瀹炵幇涓轰緥锛屽鏈嶅姟鎻愪緵鑰呬笌鏈嶅姟娑堣垂鑰呰繘琛屾暣浣撴祦绋嬪垎鏋愩€� + +### 濡備綍閰嶇疆鏉′欢璺敱瑙勫垯 + +閰嶇疆鏉′欢璺敱瑙勫垯鏃㈠彲浠ラ€氳繃鏈湴閰嶇疆鏂囦欢涔熻兘閫氳繃杩滅▼閰嶇疆涓績杩涜閰嶇疆锛岄厤缃敓鏁堟祦绋嬮兘鏄細閰嶇疆鏂囦欢 => dubbo 鍐呴儴鍗忚 => 缂撳瓨鑷冲簲鐢ㄧ骇鍐呭瓨 => 杩囨护鍑哄彲璋冪敤鑺傜偣銆� + +**dubbo-admin** 銆愭湇鍔℃不鐞�/鏉′欢璺敱銆戝鍔犺矾鐢辫鍒欓厤缃紝zookeeper 涓細鑷姩鐢熸垚鍏跺搴旈厤缃妭鐐癸紝鍐呭鍧囦负 **dubbo-admin** 涓缃殑閰嶇疆銆� + +**_鍏ㄥ眬閰嶇疆_** + +瀵瑰簲搴旂敤绾у叏灞€璺敱瑙勫垯閰嶇疆銆� + +```plain +/dubbo/config/dubbo/user-info-server锛堝簲鐢ㄥ悕锛�.condition-router +``` + +搴旂敤鍚嶏細鍙 user-info-server 搴旂敤鐢熸晥 .condition-router: 璺敱绫诲瀷銆傞櫎姝や箣澶栵紝杩樻湁 .tag-router 琛ㄧず鏍囩璺敱銆� + +**_鏈嶅姟閰嶇疆_** + +瀵瑰簲鏈嶅姟绾ф墍鏈夎矾鐢辫鍒欓厤缃€� + +```plain +/dubbo/ com.ikurento.user.UserProvider锛堟湇鍔″悕锛� /routers +``` + +鏈嶅姟鍚嶏細鍙 com.ikurento.user.UserProvider 鏈嶅姟鐢熸晥銆� + +## 瀹炵幇 Router + + + +浠ヤ笅涓哄繀椤诲疄鐜扮殑鏂规硶锛屼互涓嬫柟娉曠敤浜庤幏鍙栬繃婊ゆ湇鍔$鑺傜偣閰嶇疆銆� + +* Route: 鏍规嵁閰嶇疆锛岃皟鐢ㄨ妭鐐逛笌琚皟鐢ㄨ妭鐐癸紝杩囨护鍑哄彲璋冪敤鑺傜偣銆� +* Priority: 璺敱瑙勫垯浼樺厛绾э紝闇€瑕佹槸涓鏁存暟銆� +* URL: 閫氳繃璺敱瑙勫垯杞崲鍑烘潵鐨� dubbo 鍐呴儴鍗忚銆� + +鏇村瀹炵幇鍙傝€冿細 + +璺敱瑙勫垯锛歔https://github.com/apache/dubbo-go/tree/master/cluster/router/condition](https://github.com/apache/dubbo-go/tree/master/cluster/router/condition) + +鍏朵腑鍖呭惈鐩戝惉閰嶇疆涓績瀹炵幇锛歔https://github.com/apache/dubbo-go/blob/master/cluster/router/condition/listenable\_router.go](https://github.com/apache/dubbo-go/blob/master/cluster/router/condition/listenable_router.go) + +# 浣跨敤鏂规硶 + +缁忚繃涓婇潰璁捐涓庡疄鐜扮殑鍒嗘瀽锛屽ぇ姒備篃鑳界寽娴嬪埌濡備綍浣跨敤锛� + + + +濡傚浘鎵€绀猴紝浣跨敤璺敱瑙勫垯骞朵笉澶嶆潅锛屽彧闇€瑕佹妸瀵瑰簲鐨勪緷璧栧紩鍏ヨ繘鏉ャ€傚湪鍖呭垵濮嬪寲鐨勬椂鍊欙紝浼氬垱寤哄嚭鏉ュ搴旂殑璺敱瑙勫垯鐨勫疄鐜般€傛瘮濡傝鍔犺浇鏉′欢璺敱銆佸仴搴锋娴嬭矾鐢辨垨鑰呮爣绛句綔涓鸿矾鐢辫鍒欙細 + +## 鏈湴璺敱瑙勫垯閰嶇疆 + +```plain +_ "github.com/apache/dubbo-go/cluster/router/condition" +``` + +浠呬粎寮曠敤渚濊禆鍖呰繕涓嶇洿鎺ヤ娇鐢紝杩橀渶瑕侀厤缃寚瀹氱殑閰嶇疆鏂囦欢锛� **_router\_config.yml_** 锛屽唴瀹瑰涓�: + +```plain +# dubbo router yaml configure file +priority: 1 +force: true +conditions : ["host = 1.1.1.1 => host = 192.168.199.214"] +``` + +鏇村閰嶇疆鏂瑰紡锛歔鏉′欢璺敱閰嶇疆](http://dubbo.apache.org/zh-cn/docs/user/demos/routing-rule.html) + +## 閰嶇疆涓績閰嶇疆 + +```plain +_ "github.com/apache/dubbo-go/config_center/zookeeper" +``` + +鐩墠浠呮敮鎸� zookeeper 閰嶇疆涓績锛屼笌 dubbo-admin 缁撳悎鍗冲彲浣跨敤銆傞厤缃柟寮忓涓嬶細 + + + + + +# 鎬荤粨 + +鏇村姞鍏蜂綋鐨勫疄鐜帮紝鎴戝氨涓嶈缁嗚杩帮紝澶у鍙互鍘荤湅婧愮爜锛屾杩庡ぇ瀹舵寔缁叧娉紝鎴栬€呰础鐚唬鐮併€� + +鏁翠釜璺敱瑙勫垯鍔熻兘锛屽凡缁忚兘璺熶笂 dubbo 2.7.x 鐗堟湰锛屽凡缁忔敮鎸佹湰鍦板強杩滅璺敱瑙勫垯閰嶇疆绠$悊銆備粠鎵╁睍鎬ф潵璇达紝鏄瘮杈冧究鍒┿€傜洰鍓嶅凡缁忔敮鎸佹潯浠惰矾鐢便€佹爣绛捐矾鐢变笌鍋ュ悍妫€娴嬭矾鐢憋紝铏界劧鑳芥弧瓒冲熀鏈娇鐢ㄥ満鏅紝璺濈瀹屽杽杩樻湁杩橀暱杩滅殑璺€� + +鏈潵璁″垝锛� + +1. 鏇村鐨勯厤缃腑蹇冩敮鎸侊紝鐞嗚涓婂凡缁忔敮鎸侊紝浣嗚繕娌℃祴璇曘€� +2. service-router锛堟湭鏀寔锛� +3. 鏍囩璺敱-閰嶇疆涓績锛堟湭鏀寔锛� +4. 鐩墠璺敱涓庨厤缃腑蹇冪粨鍚堢殑浠g爜锛屽鏂板璺敱瑙勫垯骞朵笉鍙嬪ソ锛屾湁涓€瀹氭帴鍏ユ垚鏈€� + +娆㈣繋澶у鍏虫敞鎴栬€呰础鐚唬鐮侊紝[https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) + +Written on March 31, 2020 \ No newline at end of file diff --git a/doc/md/rpc/dubb-go-adaptive-grpc.md b/doc/md/rpc/dubb-go-adaptive-grpc.md new file mode 100644 index 0000000000000000000000000000000000000000..2105d23ef6d5c41b5a3d15e8309a6c39573cce97 --- /dev/null +++ b/doc/md/rpc/dubb-go-adaptive-grpc.md @@ -0,0 +1,129 @@ +# [鏃犵紳琛旀帴 gRPC 涓� dubbo-go](https://developer.aliyun.com/article/742946) + +[涓棿浠跺皬鍝(https://developer.aliyun.com/profile/g6g63f3lanvck) 2020-01-19 1530娴忚閲� + +**绠€浠嬶細** + +鏈€杩戞垜浠� dubbo-go 绀惧尯閲岄潰锛屽懠澹板緢澶х殑涓€涓� feature 灏辨槸瀵� gRPC 鐨勬敮鎸併€傚湪鏌愪綅澶т浆鐨勪笉鎳堝姫鍔涗箣涓嬶紝缁堜簬寮勫嚭鏉ヤ簡銆� + +浠婂ぉ鎴戝氨缁欏ぇ瀹跺垎鏋愪竴涓嬪ぇ浣槸鎬庝箞杩炴帴 dubbo-go 鍜� gRPC 銆� + +## gRPC + +鍏堟潵绠€鍗曚粙缁嶄竴涓� gRPC 銆傚畠鏄� Google 鎺ㄥ嚭鏉ョ殑涓€涓� RPC 妗嗘灦銆俫RPC鏄€氳繃 IDL ( Interface Definition Language )鈥斺€旀帴鍙e畾涔夎瑷€鈥斺€旂紪璇戞垚涓嶅悓璇█鐨勫鎴风鏉ュ疄鐜扮殑銆傚彲浠ヨ鏄疪PC鐞嗚鐨勪竴涓潪甯搁潪甯告爣鍑嗙殑瀹炵幇銆� + +鍥犺€� gRPC 澶╃劧灏辨敮鎸佸璇█銆傝繖鍑犲勾锛屽畠鍑犱箮鎴愪负浜嗚法璇█ RPC 妗嗘灦鐨勬爣鍑嗗疄鐜版柟寮忎簡锛屽緢澶氫紭绉€鐨剅pc妗嗘灦锛屽 Spring Cloud 鍜� dubbo 锛岄兘鏀寔 gRPC 銆� + +server 绔� + +鍦� Go 閲岄潰锛宻erver 绔殑鐢ㄦ硶鏄細 + + +瀹冪殑鍏抽敭閮ㄥ垎鏄細`s := grpc.NewServer()`鍜宍pb.RegisterGreeterServer(s, &server{})`涓や釜姝ラ銆傜涓€涓楠ゅ緢瀹规槗锛屽敮鐙浜屼釜姝ラ`RegisterGreeterServer`鏈夌偣楹荤儲銆備负浠€涔堝憿锛� + +鍥犱负`pb.RegisterGreeterServer(s, &server{})`杩欎釜鏂规硶鏄€氳繃鐢ㄦ埛瀹氫箟鐨刞protobuf`缂栬瘧鍑烘潵鐨勩€� + +濂藉湪锛岃繖涓紪璇戝嚭鏉ョ殑鏂规硶锛屾湰璐ㄤ笂鏄細 + + + +涔熷氨鏄锛屽鏋滄垜浠湪 dubbo-go 閲岄潰鎷垮埌杩欎釜 \_Greeter\_serviceDesc 锛屽氨鍙互瀹炵幇杩欎釜 server 鐨勬敞鍐屻€傚洜姝わ紝鍙互鐪嬪埌锛屽湪 dubbo-go 閲岄潰锛岃瑙e喅鐨勪竴涓叧閿棶棰樺氨鏄浣曟嬁鍒拌繖涓� serviceDesc 銆� + +## Client 绔� + +Client 绔殑鐢ㄦ硶鏄細 + + +杩欎釜涓滆タ瑕佸鏉備竴鐐癸細 +1銆佸垱寤鸿繛鎺ワ細conn, err := grpc.Dial(address) +2銆佸垱寤篶lient锛歝 := pb.NewGreeterClient(conn) +3銆佽皟鐢ㄦ柟娉曪細r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) + +绗竴涓棶棰樺叾瀹炴尯濂借В鍐崇殑锛屾瘯绔熸垜浠彲浠ヤ粠鐢ㄦ埛鐨勯厤缃噷闈㈣鍑� address 锛� + +绗簩涓棶棰樺氨鏄渶闅剧殑鍦版柟浜嗐€傚鍚� RegisterGreeterServer 鏄缂栬瘧鍑烘潵鐨勯偅鏍凤紝杩欎釜 NewGreeterClient 涔熸槸琚紪璇戝嚭鏉ョ殑銆� + +鑰岀涓変釜闂锛屼箥涓€鐪嬫槸鐢ㄥ弽灏勫氨鑳借В鍐筹紝浣嗘槸鎴戜滑鎵撳紑 SayHello 灏辫兘鐪嬪埌锛� + + +缁撳悎 greetClient 鐨勫畾涔夛紝寰堝鏄撶湅鍒帮紝鎴戜滑鐨勫叧閿氨鍦ㄤ簬 err := c.cc.Invoke ( ctx, "/helloworld.Greeter/SayHello", in, out, opts... )銆傛崲瑷€涔嬶紝鎴戜滑鍙渶瑕佸垱寤哄嚭鏉ヨ繛鎺ワ紝骞朵笖鎷垮埌鏂规硶銆佸弬鏁板氨鑳介€氳繃绫讳技鐨勮皟鐢ㄦ潵妯℃嫙鍑� c.SayHello 銆� + +閫氳繃瀵� gRPC 鐨勭畝鍗曞垎鏋愶紝鎴戜滑澶ф鐭ラ亾瑕佹€庝箞寮勪簡銆傝繕鍓╀笅涓€涓棶棰橈紝灏辨槸鎴戜滑鐨勮В鍐虫柟妗堟€庝箞鍜� dubbo-go 缁撳悎璧锋潵鍛紵 + +## 璁捐 + +鎴戜滑鍏堟潵鐪嬩竴涓� dubbo-go 鐨勬暣浣撹璁★紝鎬濊€冧竴涓嬶紝濡傛灉鎴戜滑瑕佸仛 gRPC 鐨勯€傞厤锛屽簲璇ユ槸鍦ㄥ摢涓眰娆′笂鍋氶€傞厤銆� + + +鎴戜滑鏍规嵁鍓嶉潰浠嬬粛鐨� gRPC 鐨勭浉鍏崇壒鎬у彲浠ョ湅鍑烘潵锛実RPC 宸茬粡瑙e喅浜� codec 鍜� transport 涓ゅ眰鐨勯棶棰樸€� + +鑰屼粠 cluster 寰€涓婏紝鏄剧劧 gRPC 娌℃湁娑夊強銆備簬鏄紝浠庤繖涓浘閲岄潰鎴戜滑灏卞彲浠ョ湅鍑烘潵锛岃鍋氳繖绉嶉€傞厤锛岄偅涔� protocol 杩欎竴灞傛槸鏈€鍚堥€傜殑銆傚嵆锛屾垜浠彲浠ュ鍚� dubbo protocol 閭h埇锛屾墿灞曞嚭鏉ヤ竴涓� grpc protocol 銆� + +杩欎釜 gRPC protocol 澶т綋涓婄浉褰撲簬涓€涓€傞厤鍣紝灏嗗簳灞傜殑 gRPC 鐨勫疄鐜板拰鎴戜滑鑷韩鐨� dubbo-go 杩炴帴鍦ㄤ竴璧枫€� + + + +## 瀹炵幇 + +鍦� dubbo-go 閲岄潰锛屽拰 gRPC 鐩稿叧鐨勪富瑕佹槸锛� + + + +鎴戜滑鐩存帴杩涘幓鐪嬬湅鍦� gRPC 灏忚妭閲岄潰鎻愬埌鐨勮鐐规槸濡備綍瀹炵幇鐨勩€� + +### server绔� + + + +杩欐牱鐪嬭捣鏉ワ紝杩樻槸寰堟竻鏅扮殑銆傚鍚� dubbo- go 鍏跺畠鐨� protocol 涓€鏍凤紝鍏堟嬁鍒� service 锛岃€屽悗閫氳繃 service 鏉ユ嬁鍒� serviceDesc 锛屽畬鎴愭湇鍔$殑娉ㄥ唽銆� + +娉ㄦ剰涓€涓嬩笂鍥炬垜绾㈢嚎鏍囧噯鐨� ds, ok := service.(DubboGrpcService) 杩欎竴鍙ャ€� + +涓轰粈涔堟垜璇磋繖涓湴鏂规湁鐐瑰鎬憿锛熸槸鍥犱负鐞嗚涓婃潵璇达紝鎴戜滑杩欓噷娉ㄥ唽鐨勮繖涓� service 瀹為檯涓婂氨鏄� protobuf 缂栬瘧涔嬪悗鐢熸垚鐨� gRPC 鏈嶅姟绔殑閭d釜 service 鈥斺€斿緢鏄剧劧锛屽崟绾殑缂栬瘧涓€涓� protobuf 鎺ュ彛锛屽畠鑲畾涓嶄細瀹炵幇 DubboGrpcService 鎺ュ彛锛� + + + +閭d箞 ds, ok := service.(DubboGrpcService) 杩欎竴鍙ワ紝绌剁珶鎬庝箞鎵嶈兘璁╁畠鑳藉鎵ц鎴愬姛鍛紵 + +鎴戜細鍦ㄥ悗闈㈢粰澶у鎻檽杩欎釜璋滃簳銆� + +## Client绔� + +dubbo-go 璁捐浜嗚嚜韬殑 Client 锛屼綔涓哄 gRPC 閲岄潰 Client 鐨勪竴绉嶆ā鎷熶笌灏佽锛� + + +娉ㄦ剰鐪嬶紝杩欎釜 Client 鐨勫畾涔変笌鍓嶉潰 greetClient 鐨勫畾涔夊強鍏剁浉浼笺€傚啀鐪嬩笅闈㈢殑 NewClient 鏂规硶锛岄噷闈篃鏃犻潪灏辨槸鍒涘缓浜嗚繛鎺� conn 锛岃€屽悗鍒╃敤 conn 閲屽垱寤轰簡涓€涓� Client 瀹炰緥銆� + +娉ㄦ剰鐨勬槸锛岃繖閲岄潰缁存姢鐨� invoker 瀹為檯涓婃槸涓€涓� stub 銆� + +褰撶湡姝e彂璧疯皟鐢ㄧ殑鏃跺€欙細 + + + +绾㈣壊妗嗘妗嗕綇鐨勫氨鏄叧閿楠ゃ€傚埄鐢ㄥ弽灏勪粠 invoker 鈥斺€斾篃灏辨槸 stub 鈥斺€旈噷闈㈡嬁鍒拌皟鐢ㄧ殑鏂规硶锛岃€屽悗閫氳繃鍙嶅皠璋冪敤銆� + +### 浠g爜鐢熸垚 + +鍓嶉潰鎻愬埌杩� ds, ok := service.(DubboGrpcService) 杩欎竴鍙ワ紝闈复鐨勯棶棰樻槸濡備綍璁� protobuf 缂栬瘧鐢熸垚鐨勪唬鐮佽兘澶熷疄鐜� DubboGrpcService 鎺ュ彛鍛紵 + +鏈変簺灏忎紮浼村彲鑳戒篃娉ㄦ剰鍒帮紝鍦ㄦ垜璐村嚭鏉ョ殑涓€浜涗唬鐮侀噷闈紝鍙嶅皠鎿嶄綔浼氭牴鎹悕瀛楁潵鑾峰彇method瀹炰緥锛屾瘮濡侼ewClient鏂规硶閲岄潰鐨刴ethod := reflect.ValueOf(impl).MethodByName("GetDubboStub")杩欎竴鍙ャ€傝繖涓€鍙ョ殑impl锛屽嵆鎸囨湇鍔$殑瀹炵幇锛屼篃鏄� protobuf 閲岄潰缂栬瘧鍑烘潵鐨勶紝鎬庝箞璁� protobuf 缂栬瘧鍑烘潵鐨勪唬鐮侀噷闈㈠惈鏈夎繖涓� GetDubboStub 鏂规硶鍛紵 + +鍒拌繖閲岋紝绛旀宸茬粡鍛间箣娆插嚭浜嗭細淇敼 protobuf 缂栬瘧鐢熸垚浠g爜鐨勯€昏緫锛� + +搴嗗垢鐨勬槸锛屽湪 protobuf 閲岄潰鍏佽鎴戜滑閫氳繃鎻掍欢鐨勫舰寮忔墿灞曟垜浠嚜宸辩殑浠g爜鐢熸垚鐨勯€昏緫銆� + +鎵€浠ユ垜浠彧闇€瑕佹敞鍐屼竴涓垜浠嚜宸辩殑鎻掍欢锛� + + +鐒跺悗杩欎釜鎻掍欢浼氭妸鎴戜滑鎵€闇€瑕佺殑浠g爜缁欏祵鍏ヨ繘鍘汇€傛瘮濡傝宓屽叆`GetDubboStub`鏂规硶锛� + + +杩樻湁`DubboGrpcService`鎺ュ彛锛� + + + +杩欎釜涓滆タ锛屽睘浜庨毦鑰呬笉浼氫細鑰呬笉闅俱€傚氨鏄鏋滀綘涓嶇煡閬撳彲浠ラ€氳繃`plugin`鐨勫舰寮忔潵淇敼鐢熸垚鐨勪唬鐮侊紝閭e氨鏄湡闅撅紱浣嗘槸濡傛灉鐭ラ亾浜嗭紝杩欎釜涓滆タ灏卞緢绠€鍗曚簡鈥斺€旀棤闈炲氨鏄按纾ㄥ伐澶舰浜嗐€� + +**浣滆€呬俊鎭細**閭撴槑锛屾瘯涓氫簬鍗椾含澶у锛屽氨鑱屼簬 eBay Payment 閮ㄩ棬锛岃礋璐i€€娆句笟鍔″紑鍙戙€� + +缂栬В鐮� 鑷劧璇█澶勭悊 Dubbo Java 搴旂敤鏈嶅姟涓棿浠� Go Spring \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-metrics-design.md b/doc/md/service-governance/dubbo-go-metrics-design.md new file mode 100644 index 0000000000000000000000000000000000000000..d2234440c95e8391a0a9f425541ca3c8050b26b8 --- /dev/null +++ b/doc/md/service-governance/dubbo-go-metrics-design.md @@ -0,0 +1,111 @@ +# [eBay 閭撴槑锛歞ubbo-go 涓� metrics 鐨勮璁(https://mp.weixin.qq.com/s/_ibXd2z1RqjOJwk7jMAwig) + +鍙戝竷浜庯細2020 骞� 4 鏈� 22 鏃� 17:15 + +鏈€杩戝洜涓鸿鍦� Apache/dubbo-go锛堜互涓嬬畝绉� dubbo-go 锛夐噷闈㈠疄鐜扮被浼肩殑杩欎釜 metrics 鍔熻兘锛屼簬鏄姳浜嗗緢澶氭椂闂村幓浜嗚В鐜板湪 Dubbo 閲岄潰鐨� metrics 鏄€庝箞瀹炵幇鐨勩€傝閮ㄥ垎锛屽疄闄呬笂鏄鏀惧湪涓€涓嫭绔嬬殑椤圭洰閲岄潰锛屽嵆 metrics 銆� + +鎬讳綋涓婃潵璇达紝Dubbo 鐨� metrics 鏄竴涓粠璁捐鍒板疄鐜伴兘闈炲父浼樼鐨勬ā鍧楋紝鐞嗚涓婃潵璇达紝澶ч儴鍒嗙殑 Java 椤圭洰鏄彲浠ョ洿鎺ヤ娇鐢� metrics 鐨勩€備絾涔熷洜涓哄吋椤炬€ц兘銆佹墿灞曟€х瓑鍚勭闈炲姛鑳界壒鎬э紝鎵€浠ュ垵鐪嬩唬鐮佷細鏈夌鏃犱粠涓嬫墜鐨勬劅瑙夈€� + +浠婂ぉ杩欑瘒鏂囩珷灏嗕細浠庢瘮杈冨ぇ鐨勬蹇靛拰鎶借薄涓婅璁轰竴涓� dubbo-go 涓殑 metrics 妯″潡鐨勮璁♀€斺€斿疄闄呬笂涔熷氨鏄� Dubbo 涓殑 metrics 鐨勮璁°€傚洜涓烘垜浠呬粎鏄皢 Dubbo 閲岄潰鐨勭浉鍏冲唴瀹瑰湪 dubbo-go 涓鍒朵竴浠姐€� + +鐩墠 dubbo-go 鐨� metrics 鍒氬垰寮€濮嬭捣姝ワ紝绗竴涓� PR 鏄細 + +## 鎬讳綋璁捐 + +**Metric** + +瑕佹兂鐞嗚В metrics 鐨勮璁★紝棣栧厛瑕佺悊瑙o紝鎴戜滑闇€瑕佹敹闆嗕竴浜涗粈涔堟暟鎹€傛垜浠彲浠ヨ交鏄撳垪涓惧嚭鏉ュ湪 RPC 棰嗗煙閲岄潰鎴戜滑鎵€鍏冲績鐨勫悇绉嶆寚鏍囷紝璇稿姣忎釜鏈嶅姟鐨勮皟鐢ㄦ鏁帮紝鍝嶅簲鏃堕棿锛涘鏋滄洿鍔犵粏鑷翠竴鐐癸紝杩樻湁鍚勭鍝嶅簲鏃堕棿鐨勫垎甯冿紝骞冲潎鍝嶅簲鏃堕棿锛�999 绾库€︹€� + +浣嗘槸涓婇潰鍒椾妇鐨勬槸浠庢暟鎹殑鍐呭涓婂垝鍒嗙殑銆� metrics 鍦ㄦ娊璞′笂锛屽垯鏄憭寮冧簡杩欑鍒掑垎鏂瑰紡锛岃€屾槸缁撳悎浜嗘暟鎹殑鐗规€у拰琛ㄧ幇褰㈠紡缁煎悎鍒掑垎鐨勩€� + +浠庢簮鐮侀噷闈㈠緢瀹规槗鎵惧埌杩欑鍒掑垎鐨勬娊璞°€� + +metrics 璁捐浜� Metric 鎺ュ彛浣滀负鎵€鏈夋暟鎹殑椤剁骇鎶借薄锛� + +鍦� Dubbo 閲岄潰锛屽叾姣旇緝鍏抽敭鐨勫瓙鎺ュ彛鏄細 + +涓轰簡澶у鐞嗚В锛岃繖閲屾垜鎶勪竴涓嬭繖浜涙帴鍙g殑鐢ㄩ€旓細 + +* Gauge: 涓€绉嶅疄鏃舵暟鎹殑搴﹂噺锛屽弽鏄犵殑鏄灛鎬佺殑鏁版嵁锛屼笉鍏锋湁绱姞鎬э紝渚嬪褰撳墠 JVM 鐨勭嚎绋嬫暟锛� +* Counter: 璁℃暟鍣ㄥ瀷鎸囨爣锛岄€傜敤浜庤褰曡皟鐢ㄦ€婚噺绛夌被鍨嬬殑鏁版嵁锛� +* Histogram : 鐩存柟鍒嗗竷鎸囨爣锛屼緥濡傦紝鍙互鐢ㄤ簬缁熻鏌愪釜鎺ュ彛鐨勫搷搴旀椂闂达紝鍙互灞曠ず 50%, 70%, 90% 鐨勮姹傚搷搴旀椂闂磋惤鍦ㄥ摢涓尯闂村唴锛� +* Meter: 涓€绉嶇敤浜庡害閲忎竴娈垫椂闂村唴鍚炲悙鐜囩殑璁¢噺鍣ㄣ€備緥濡傦紝涓€鍒嗛挓鍐咃紝浜斿垎閽熷唴锛屽崄浜斿垎閽熷唴鐨� qps 鎸囨爣锛� +* Timer: Timer 鐩稿綋浜� Meter+Histogram 鐨勭粍鍚堬紝鍚屾椂缁熻涓€娈典唬鐮侊紝涓€涓柟娉曠殑 qps锛屼互鍙婃墽琛屾椂闂寸殑鍒嗗竷鎯呭喌锛� + +鐩墠 dubbo-go 鍙疄鐜颁簡 FastCompass 锛屽畠涔熸槸 Metric 鐨勫瓙绫伙細 + +杩欎釜鎺ュ彛鍔熻兘寰堢畝鍗曪紝灏辨槸鐢ㄤ簬鏀堕泦涓€娈垫椂闂翠箣鍐呯殑 subCategory 鎵ц鐨勬鏁板拰鍝嶅簲鏃堕棿銆� subCategory 鏄竴涓瘮杈冨娉涚殑姒傚康锛屾棤璁烘槸鍦� Dubbo 杩樻槸鍦� dubbo-go 閲岄潰锛屼竴涓吀鍨嬬殑 subCategory 灏变細鏄煇涓湇鍔°€� + +杩欓噷鐨勮璁¤鐐瑰湪浜庯紝瀹冩槸浠庝粈涔堣搴︿笂鍘诲仛杩欎簺鏁版嵁鐨勬娊璞$殑銆� + +寰堝浜哄湪寮€鍙戣繖绉嶉噰闆嗘暟鎹殑鐩稿叧绯荤粺鎴栬€呭姛鑳界殑鏃跺€欙紝鏈€瀹规槗闄峰叆鐨勫氨鏄粠鏁版嵁鍐呭涓婂仛鎶借薄锛屼緥濡傛娊璞′竴涓帴鍙o紝閲岄潰鐨勬柟娉曞氨鏄幏寰楁湇鍔$殑璋冪敤娆℃暟鎴栬€呭钩鍧囧搷搴旀椂闂寸瓑銆� + +杩欑鎶借薄骞堕潪涓嶅彲浠ワ紝灏ゅ叾鏄湪绠€鍗曠郴缁熼噷闈紝杩橀潪甯稿ソ鐢ㄣ€傚敮鐙湪閫氱敤鎬у拰鎵╁睍鎬т笂瑕佸樊寰堝銆� + +**MetricManager** + +鍦ㄦ垜浠畾涔変簡 Metric 涔嬪悗锛屽緢瀹规槗灏辨兂鍒帮紝鎴戣鏈変竴涓笢瑗挎潵绠$悊杩欎簺 Metric 銆傝繖灏辨槸 MetricManager 鈥斺€斿搴斿埌 Dubbo 閲岄潰鐨� IMetricManager 鎺ュ彛銆� + +MetricManager 鎺ュ彛鐩墠鍦� dubbo-go 閲岄潰杩樺緢绠€鍗曪細 + +鏈川涓婃潵璇达紝鎴戝湪鍓嶉潰鎻愬埌鐨勯偅浜� Metric 鐨勫瓙绫伙紝閮藉彲浠ヤ粠杩欎釜 MetricManager 閲岄潰鎷垮埌銆傚畠鏄澶栫殑鍞竴鍏ュ彛銆� + +鍥犳鏃犺鏄笂鎶ラ噰闆嗙殑鏁版嵁锛岃繕鏄煇浜涘姛鑳借鐢ㄨ繖浜涢噰闆嗙殑鏁版嵁锛屾渶閲嶈鐨勫氨鏄幏寰椾竴涓� MetricManager 鐨勫疄渚嬨€備緥濡傛垜浠渶杩戞鍦ㄥ紑鍙戠殑鎺ュ叆 Prometheus 灏辨槸鎷垮埌杩欎釜 MetriManger 瀹炰緥锛岃€屽悗浠庨噷闈㈡嬁鍒� FastCompass 鐨勫疄渚嬶紝鑰屽悗閲囬泦杩欎簺鏁版嵁锛� + +**MetricRegistry** + +MetricRegistry 鏄竴涓 Metric 闆嗗悎鐨勬娊璞°€� MetricManager 鐨勯粯璁ゅ疄鐜伴噷闈紝灏辨槸浣跨敤 MetricRegistry 鏉ョ鐞� Metric 鐨�: + +鎵€浠ワ紝鏈川涓婂畠灏辨槸鎻愪緵浜嗕竴浜涙敞鍐� Metric 鐒跺悗鍐嶄粠閲岄潰鎹炲嚭鏉ョ殑鏂规硶銆� + +浜庢槸锛岃繖灏辨湁涓€涓棶棰樹簡锛氫负浠€涔堟垜鍦ㄦ湁浜� MetricManager 涔嬪悗锛岃繕鏈夋湁涓€涓� MetricRegistry锛熶技涔庤繖涓や釜鍔熻兘鏈変簺閲嶅彔锛� + +绛旀澶ф鏄袱涓柟闈細 +1銆侀櫎浜嗙鐞嗘墍鏈夌殑 Metric 涔嬪锛岃繕鎵挎媴鐫€棰濆鐨勫姛鑳斤紝杩欎簺鍔熻兘鍏稿瀷鐨勫氨鏄� IsEnabled 銆傝€屽疄闄呬笂锛屽湪鏈潵鎴戜滑浼氳祴浜堝畠绠$悊鐢熷懡鍛ㄦ湡鐨勮矗浠伙紝姣斿璇村湪 Dubbo 閲岄潰锛岃鎺ュ彛灏辫繕鏈変竴涓� clear 鏂规硶锛� +2銆� metrics 閲岄潰杩樻湁涓€涓� group 鐨勬蹇碉紝鑰岃繖鍙兘鐢� MetricManager 鏉ヨ繘琛岀鐞嗭紝鑷冲皯浜ょ粰 MetricRegistry 鏄笉鍚堥€傜殑銆� + +metrics 鐨� group 璇磋捣鏉ヤ篃寰堢畝鍗曘€傛瘮濡傚湪 Dubbo 妗嗘灦閲岄潰閲囬泦鐨勬暟鎹紝閮戒細褰掑睘浜� Dubbo 杩欎釜 group 銆備篃灏辨槸璇达紝濡傛灉鎴戞兂灏嗛潪妗嗘灦灞傞潰閲囬泦鐨勬暟鎹€斺€旀瘮濡傜函绮圭殑涓氬姟鏁版嵁鈥斺€斿垎闅斿嚭鏉ワ紝灏卞彲浠ュ€熺敤涓€涓� business group 銆傚張鎴栬€呮垜閲囬泦鍒扮殑鏈哄櫒鑷韩鐨勬暟鎹紝鍙互灏嗗叾褰掔被鍒� system 杩欎釜 group 涓嬨€� + +鎵€浠� MetricManger 鍜� MetricRegistry 鐨勫叧绯绘槸锛� + +Clock 鎶借薄鏄竴涓垵鐪嬫病浠€涔堢敤锛屽啀鐪嬩細瑙夊緱鍏舵娊璞$殑寰堝ソ銆侰lock 閲岄潰灏变袱涓柟娉曪細 + +涓€涓槸鑾峰緱鏃堕棿鎴筹紝鍙﹀涓€涓垯鏄幏寰楁椂闂村懆鏈� (Tick)銆傛瘮濡傞€氬父閲囬泦鏁版嵁鍙兘鏄瘡涓€鍒嗛挓閲囬泦涓€娆★紝鎵€浠ヤ綘寰楃煡閬撶幇鍦ㄥ鍦ㄥ摢涓椂闂村懆鏈熼噷闈€€侰lock 灏辨彁渚涗簡杩欑鎶借薄銆� + +寰堝浜哄湪瀹炵幇鑷繁鐨勮繖绉� metrics 鐨勬鏋剁殑鏃跺€欙紝澶у鏁伴兘鏄洿鎺ヤ娇鐢ㄧ郴缁熺殑鏃堕挓锛屼篃灏辨槸绯荤粺鐨勬椂闂存埑銆備簬鏄墍鏈夌殑 Metic 鍦ㄩ噰闆嗘暟鎹垨鑰呬笂鎶ユ暟鎹殑鏃跺€欙紝涓嶅緱涓嶈嚜宸卞幓澶勭悊杩欑鏃堕挓鏂归潰鐨勯棶棰樸€� + +杩欐牱涓嶅悓鐨� Metric 涔嬮棿灏卞緢闅惧仛鍒版椂閽熺殑鍚屾銆傛瘮濡傝鍙兘鍦ㄦ煇涓� Metric1 閲岄潰锛岄噰闆嗗懆鏈熸槸褰撳墠杩欎竴鍒嗛挓锛岃€� Metric2 鏄綋鍓嶈繖涓€鍒嗛挓鐨勭涓夊崄绉掑埌涓嬩竴鍒嗛挓鐨勭涓夊崄绉掋€傝櫧鐒跺畠浠兘鏄竴鍒嗛挓閲囬泦涓€娆★紝浣嗘槸杩欎釜鍛ㄦ湡灏卞涓嶄笂浜嗐€� + +鍙﹀涓€涓湁鎰忔€濈殑鍦版柟鍦ㄤ簬锛孋lock 鎻愪緵鐨勮繖绉嶆娊璞★紝鍏佽鎴戜滑涓嶅繀鐪熺殑鎸夌収鐜板疄鏃堕棿鐨勬椂闂存埑鏉ュ鐞嗐€傛瘮濡傝锛屽彲浠ヨ€冭檻鎸夌収 CPU 鐨勮繍琛屾椂闂存潵璁捐 Clock 鐨勫疄鐜般€� + +## 渚嬪瓙 + +灏辩敤杩欎竴娆� PR 鐨勫唴瀹规潵灞曠ず涓€涓嬭繖涓璁°€� + +鍦� dubbo-go 閲岄潰杩欐瀹炵幇浜� metricsFilter 锛屽畠涓昏灏辨槸鏀堕泦璋冪敤娆℃暟鍜屽搷搴旀椂闂达紝鍏舵牳蹇冩槸锛� + +report 鍏跺疄灏辨槸鎶� metrics reports 缁� MetricManager 锛� + +鎵€浠ワ紝杩欓噷闈㈠彲浠ョ湅鍑烘潵锛屽鏋滄垜浠鏀堕泦浠€涔堟暟鎹紝涔熸槸瑕佸厛鑾峰緱 MetricManager 鐨勫疄渚嬨€� + +FastCompass 鐨勫疄鐜伴噷闈細灏嗚繖涓€娆¤皟鐢ㄧ殑鏈嶅姟鍙婂叾鍝嶅簲鏃堕棿淇濆瓨涓嬫潵銆傝€屽悗鍦ㄩ渶瑕佺殑鏃跺€欏啀鍙栧嚭鏉ャ€� + +鎵€璋撶殑闇€瑕佺殑鏃跺€欙紝閫氬父灏辨槸涓婃姤缁欑洃鎺х郴缁熺殑鏃跺€欍€傛瘮濡傚墠闈㈢殑鎻愬埌鐨勪笂鎶ョ粰 Prometheus銆� + +鎵€浠ヨ繖涓祦绋嬪彲浠ユ娊璞¤〃杈句负锛� + +杩欐槸涓€涓洿鍔犲娉涚殑鎶借薄銆備篃灏辨槸鎰忓懗鐫€锛屾垜浠櫎浜嗗彲浠ヤ粠杩欎釜 metricFilter 閲岄潰鏀堕泦鏁版嵁锛屼篃鍙互浠庤嚜韬殑涓氬姟閲岄潰鍘绘敹闆嗘暟鎹€傛瘮濡傝缁熻鏌愭浠g爜鐨勬墽琛屾椂闂达紝涓€鏍峰彲浠ヤ娇鐢� FastCompass 銆� + +鑰岄櫎浜� Prometheus 锛屽鏋滅敤鎴疯嚜宸辩殑鍏徃閲岄潰鏈夌洃鎺ф鏋讹紝閭d箞浠栦滑鍙互鑷繁瀹炵幇鑷繁鐨勪笂鎶ラ€昏緫銆傝€屼笂鎶ョ殑鏁版嵁鍒欏彧闇€瑕佹嬁鍒� MetricManager 瀹炰緥灏辫兘鎷垮埌銆� + +## 鎬荤粨 + +鏈川涓婃潵璇达紝鏁翠釜 metrics 鍙互鐪嬪仛鏄竴涓法澶ф棤姣旂殑 provider-conumer 妯″瀷銆� + +涓嶅悓鐨勬暟鎹細鍦ㄤ笉鍚岀殑鍦版柟鍜屼笉鍚屾椂闂寸偣涓婅閲囬泦銆傛湁浜涗汉鍦ㄨ杩欎簺婧愮爜鐨勬椂鍊欎細鏈夌偣鍥版儜锛屽氨鏄繖浜涙暟鎹粈涔堟椂闂寸偣浼氳閲囬泦鍛紵 + +瀹冧滑鍙細鍦ㄤ袱绫绘椂闂寸偣閲囬泦锛� +1銆佸疄鏃堕噰闆嗐€傚鎴戜笂闈妇渚嬬殑 metricsFilter 锛屼竴娆¤皟鐢ㄨ繃鏉ワ紝瀹冪殑鏁版嵁灏辫閲囬泦浜嗭紱 +2銆佸彟澶栦竴涓垯鏄鍚� Prometheus 銆傛瘡娆� Prometheus 瑙﹀彂浜� collect 鏂规硶锛岄偅涔堝畠灏变細鎶婃瘡绉嶏紙濡� Meter, Gauge 锛夐噷闈㈢殑鏁版嵁鏀堕泦杩囨潵锛岀劧鍚庝笂鎶ワ紝鍙互绉颁负鏄畾鏃堕噰闆嗭紱 + +杩欎簺鍏蜂綋鐨勫疄鐜帮紝鎴戝氨涓嶄竴涓€璁ㄨ浜嗭紝澶у鏈夊叴瓒e彲浠ュ幓鐪嬬湅婧愮爜銆傝繖浜涙暟鎹紝涔熸槸鎴戜滑 dubbo-go 鍚庨潰瑕侀檰缁疄鐜扮殑涓滆タ锛屾杩庡ぇ瀹舵寔缁叧娉紝鎴栬€呮潵璐$尞浠g爜銆� \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-sentinel.md b/doc/md/service-governance/dubbo-go-sentinel.md new file mode 100644 index 0000000000000000000000000000000000000000..ef9358c460ec002bb2c4dae597fd1d8abda1094f --- /dev/null +++ b/doc/md/service-governance/dubbo-go-sentinel.md @@ -0,0 +1,155 @@ +# [鍦╠ubbo-go涓娇鐢╯entinel](https://dubbogo.github.io/dubbo-go-website/zh-cn/blog/dubbo-go-sentinel.html) + +鏃惰嚦浠婃棩锛孉pache/dubbo-go锛堜互涓嬬畝绉� dubbo-go 锛夐」鐩湪鍔熻兘涓婂凡缁忛€愭瀵归綈java鐗堟湰锛岀ǔ瀹氭€т篃鍦ㄤ笉鍚岀殑鐢熶骇鐜寰楀埌浜嗛獙璇併€傜ぞ鍖轰究寮€濮嬪啀鏈嶅姟娌荤悊銆佺洃鎺х瓑鏂瑰悜鍙戝姏銆傞殢鐫€ 1.2鍜�1.3 鐗堟湰鍙戝竷锛� dubbo-go 鏂板浜嗗ぇ閲忔绫绘柊feature銆� + +浠婂ぉ鎴戜滑鑱婁竴鑱婇檺娴佺浉鍏宠瘽棰橈紝姝ゅ墠dubbo-go宸茬粡鏀寔浜哰tps limit](https://github.com/apache/dubbo-go/pull/237)銆乕execute limit](https://github.com/apache/dubbo-go/pull/246) 銆乕hystrix](https://github.com/apache/dubbo-go/pull/133) 鐨勫唴缃甪ilter锛岀敤鎴峰彧瑕佺畝鍗曢厤缃氨鑳介┈涓婄敤涓娿€備絾鎴戜滑鐭ラ亾锛屽湪 java 鐨� dubbo 鐢熸€佷腑锛屾湁涓€椤归檺娴佸伐鍏疯骞挎硾浣跨敤锛岄偅灏辨槸sentinel銆俿entinel鍥犱负寮哄ぇ鐨勫姩鎬佽鍒掗厤缃€佷紭绉€鐨刣ashboard浠ュ強瀵筪ubbo鐨勮壇濂介€傞厤锛屾垚涓轰紬澶氫娇鐢╠ubbo鐨勪紒涓氶€夌敤闄愭祦宸ュ叿鐨勪笉浜屼箣閫夈€� + +灏卞湪鍓嶄簺鏃ュ瓙锛岀ぞ鍖洪潪甯搁珮鍏村緱鐭� Sentinel Golang 棣栦釜鐗堟湰 0.1.0 姝e紡鍙戝竷锛岃繖浣垮緱 dubbo-go涔熷彲浠ヤ娇鐢� sentinel 浣滀负宸ュ叿杩涜涓€浜涙湇鍔℃不鐞嗐€佺洃鎺х殑宸ヤ綔浜嗐€傞殢鐫€sentinel golang鐨勫仴澹紝鎴戜滑鐩镐俊鐢ㄦ埛椹笂鍙互鍍弒entinel绠$悊java dubbo鏈嶅姟閭f牱绠$悊dubbo-go鐨勬湇鍔′簡銆� + +瀹屾垚sentinel golang鐨刣ubbo-adapter鍏跺疄闈炲父绠€鍗曪紝杩欏緱鐩婁簬dubbo-go鏃╁氨瀹屾垚浜唂ilter閾剧殑鏋勯€狅紝鐢ㄦ埛鍙互鑷畾涔塮ilter锛屽苟涓旂伒娲荤殑瀹夋帓鍏舵墽琛岄『搴忋€傚湪1.3鍙戝竷鍚庯紝澧炲姞浜唂ilter涓殑context浼犻€掞紝鏋勫缓sentinel/adapter/dubbo鏇翠负鏂逛究銆� + +鎴戜滑浠ュ叾涓殑provider filter閫傞厤涓轰緥: + + + +姝� filter 瀹炵幇浜� dubbo-go鐨刦ilter鎺ュ彛锛屽彧瑕佺敤鎴峰湪鏈嶅姟鍚姩鏃跺皢姝ilter鍔犺浇鍒癲ubbo-go涓紝鍗冲彲浣跨敤姝ilter銆�  +sentinel瀹炵幇鍘熺悊涓庡叾浠栭檺娴併€佺啍鏂簱澶у悓灏忓紓锛屽簳灞傛槸鐢ㄧ殑婊戝姩绐楀彛绠楁硶銆備笌hystrix绛夋鏋剁浉姣斾笉鍚岀偣鏄璁$悊蹇碉紝Sentinel 鐨勮璁$悊蹇垫槸璁╂偍鑷敱閫夋嫨鎺у埗鐨勮搴︼紝骞惰繘琛岀伒娲荤粍鍚堬紝浠庤€岃揪鍒版兂瑕佺殑鏁堟灉銆� + +涓嬮潰鎴戞暣鐞嗕簡瀹屾暣鐨勪娇鐢ㄦ祦绋嬶細(娉ㄦ剰锛歞ubbo-go鐗堟湰璇蜂娇鐢�1.3.0-rc3鍙婂叾浠ヤ笂鐗堟湰) + +鍦╠ubbo-go涓娇鐢╯entinel涓昏鍒嗕负浠ヤ笅鍑犳锛� + +1.鍒濆鍖杝entinel + +2.灏唖entinel娉ㄥ叆dubbo-go鐨刦ilter + +3.鍒濆鍖杁ubbo-go + +4.閰嶇疆瑙勫垝 + +## 鍒濆鍖杝entinel + +绀轰緥浠g爜锛� + +```go +import ( + sentinel "github.com/alibaba/sentinel-golang/api" +) + +func initSentinel() { + err := sentinel.InitWithLogDir(confPath, logDir) + if err != nil { + // 鍒濆鍖� Sentinel 澶辫触 + } +} +``` + +## 灏唖entinel娉ㄥ叆dubbo-go鐨刦ilter + +浣犲彲浠ラ€氳繃import鍖呯殑褰㈠紡鎵ц锛屾墽琛屽叾涓殑init()鏉ユ敞鍏ilter + +```go +import ( + _ "github.com/alibaba/sentinel-golang/adapter/dubbo" +) +``` + +涔熷彲浠ユ墜鍔ㄦ墽琛岋紝缁欎綘鐨刦ilter鍙栦笂鑷繁鎯宠鐨勫悕瀛� + +```go +import ( + "github.com/apache/dubbo-go/common/extension" + sd "github.com/alibaba/sentinel-golang/adapter/dubbo" +) + +func main(){ + extension.SetFilter("myConsumerFilter",sd.GetConsumerFilter()) + extension.SetFilter("myProviderFilter",sd.GetConsumerFilter()) +} +``` + +瀹屾垚浠ヤ笂姝ラ锛屼綘灏卞彲浠ュ湪闇€瑕佺殑dubbo鎺ュ彛閰嶇疆閲屽啓鍏entinel鐨刦ilterName,鏋勫缓璧锋帴鍙g殑filter閾炬潯銆傛瘮濡備互涓嬩互consumer.yml閰嶇疆鏂囦欢涓轰緥 + +```yml +references: + "UserProvider": + registry: "hangzhouzk" + protocol : "dubbo" + interface : "com.ikurento.user.UserProvider" + cluster: "failover" + filter: "myConsumerFilter" + methods : + - name: "GetUser" + retries: 3 +``` + +## 鍒濆鍖杁ubbo-go + +鍒拌繖涓€姝ワ紝浣犲彧闇€瑕佹甯稿惎鍔╠ubbo-go绋嬪簭灏卞畬鎴愪簡鏈嶅姟鍚姩銆傜敤浠ヤ笅浠g爜鍋氫竴涓緝涓哄畬鏁翠妇渚� + +```go +import ( + hessian "github.com/apache/dubbo-go-hessian2" + sd "github.com/alibaba/sentinel-golang/adapter/dubbo" +) + +import ( + "github.com/apache/dubbo-go/common/logger" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/impl" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/protocol" + + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + _ "github.com/apache/dubbo-go/registry/zookeeper" + "github.com/apache/dubbo-go/common/extension" +) + +func main() { + + hessian.RegisterPOJO(&User{}) + extension.SetFilter("myConsumerFilter",sd.GetConsumerFilter()) + extension.SetFilter("myProviderFilter",sd.GetConsumerFilter()) + config.Load() + + // init finish, do your work + test() +} +``` + +## 瑙勫垝閰嶇疆 + +sentinel浠ュ己澶х殑瑙勫垝閰嶇疆鍚稿紩浜嗗緢澶氫娇鐢ㄨ€咃紝鍏舵彁渚涘姩鎬佹暟鎹簮鎺ュ彛杩涜鎵╁睍锛岀敤鎴峰彲浠ラ€氳繃鍔ㄦ€佹枃浠舵垨 etcd 绛夐厤缃腑蹇冩潵鍔ㄦ€佸湴閰嶇疆瑙勫垯銆備絾鐩墠sentinel-golang浣滀负鐮磋泲鐗堟湰锛屽姩鎬侀厤缃繕鍦ㄥ紑鍙戜腑 + +### 鍔ㄦ€佹暟鎹簮 + +锛堝紑鍙戜腑锛塖entinel 鎻愪緵鍔ㄦ€佹暟鎹簮鎺ュ彛杩涜鎵╁睍锛岀敤鎴峰彲浠ラ€氳繃鍔ㄦ€佹枃浠舵垨 etcd 绛夐厤缃腑蹇冩潵鍔ㄦ€佸湴閰嶇疆瑙勫垯銆� + +### 纭紪鐮佹柟寮� + +Sentinel 涔熸敮鎸佸師濮嬬殑纭紪鐮佹柟寮忓姞杞借鍒欙紝鍙互閫氳繃鍚勪釜妯″潡鐨� `LoadRules(rules)` 鏂规硶鍔犺浇瑙勫垯銆備互涓嬫槸纭紪鐮佹柟寮忓鏌愪釜method鍦╟onsumer绔殑QPS娴佹帶锛� + +```go +_, err := flow.LoadRules([]*flow.FlowRule{ + { + ID: 666, + Resource: "dubbo:consumer:com.ikurento.user.UserProvider:myGroup:1.0.0:hello()", + MetricType: flow.QPS, + Count: 10, + ControlBehavior: flow.Reject, + }, +}) +if err != nil { + // 鍔犺浇瑙勫垯澶辫触锛岃繘琛岀浉鍏冲鐞� +} +``` + +# 鎬荤粨 + +鏇村姞鍏蜂綋鐨勫疄鐜帮紝鎴戝氨涓嶈缁嗚杩帮紝澶у鍙互鍘荤湅婧愮爜杩涗竴姝ヤ簡瑙c€� + +鏈€鍚庯紝娆㈣繋澶у鎸佺画鍏虫敞锛屾垨鑰呰础鐚唬鐮侊紝鏈熷緟dubbo-go鍦�2020骞村湪浜戝師鐢熼鍩熺户缁獊鐮淬€� + +dubbo-go浠撳簱鍦板潃锛歔https://github.com/apache/dubbo-go](https://github.com/apache/dubbo-go) \ No newline at end of file diff --git a/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md b/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md new file mode 100644 index 0000000000000000000000000000000000000000..962ec98fdf331e9e5ba42482af91b186f770fe30 --- /dev/null +++ b/doc/md/service-governance/dubbo-go-tps-limit-design-and-implement.md @@ -0,0 +1,163 @@ +# [dubbogo涓殑TPS Limit璁捐涓庡疄鐜癩(https://developer.aliyun.com/article/726804) + +# 鍓嶈█ + +[Apache Dubbo](http://dubbo.apache.org)鏄敱闃块噷寮€婧愮殑涓€涓猂PC妗嗘灦锛岄櫎浜嗗熀鏈殑RPC鍔熻兘浠ュ锛岃繕鎻愪緵浜嗕竴鏁村鐨勬湇鍔℃不鐞嗙浉鍏冲姛鑳姐€傜洰鍓嶅畠宸茬粡鏄疉pache鍩洪噾浼氫笅鐨勯《绾ч」鐩€� + +鑰孾dubbogo](https://github.com/apache/dubbo-go)鍒欐槸dubbo鐨刧o璇█瀹炵幇銆� + +鏈€杩戝湪`dubbogo`鐨刞todo list`涓婂彂鐜帮紝瀹冭繕娌℃湁瀹炵幇`TPS Limit`鐨勬ā鍧楋紝浜庢槸灏辨娊绌哄疄鐜颁簡杩欎釜閮ㄥ垎銆� + +`TPS limit`瀹為檯涓婂氨鏄檺娴侊紝姣斿璇撮檺鍒朵竴鍒嗛挓鍐呮煇涓帴鍙e彧鑳借闂�200娆★紝瓒呰繃杩欎釜娆℃暟锛屽垯浼氳鎷掔粷鏈嶅姟銆傚湪`Dubbo`鐨凧ava鐗堟湰涓婏紝鍙湁涓€涓疄鐜帮紝灏辨槸`DefaultTPSLimiter`銆� + +`DefaultTPSLimiter`鏄湪鏈嶅姟绾у埆涓婅繘琛岄檺娴併€傝櫧鐒禶dubbo`鐨勫畼鏂规枃妗i噷闈㈠0绉板彲浠ュ湪`method`绾у埆涓婅繘琛岄檺娴侊紝浣嗘槸鎴戠湅浜嗕竴涓嬪畠鐨勬簮鐮侊紝瀹為檯涓婅繖涓槸鍋氫笉鍒扮殑銆傚綋鐒讹紝濡傛灉鑷繁閫氳繃瀹炵幇`Filter`鎺ュ彛鏉ュ疄鐜癭method`绾у埆鐨勯檺娴侊紝閭d箞鑷劧鏄彲浠ョ殑鈥斺€旇繖鏍锋毚闇蹭簡`dubbo`Java鐗堟湰瀹炵幇鐨勫彟澶栦竴涓棶棰橈紝灏辨槸`dubbo`鐨刞TpsLimitFilter`瀹炵幇锛屾槸涓嶅厑璁告帴鍏ヨ嚜宸盽TpsLimiter`鐨勫疄鐜扮殑銆傝繖浠庡畠鐨勬簮鐮佷篃鍙互鐪嬪嚭鏉ワ細 + + + +瀹冪洿鎺ュ啓姝讳簡`TpsLimiter`鐨勫疄鐜般€� + +杩欎釜瀹炵幇鐨勭洰鍓嶅彧鏄悎骞跺埌浜哷develop`涓婏紝绛変笅娆″彂甯冩寮忕増鏈殑鏃跺€欐墠浼氬彂甯冨嚭鏉ャ€� + +Github: [https://github.com/apache/dubbo-go/pull/237](https://github.com/apache/dubbo-go/pull/237) + +# 璁捐鎬濊矾 + +浜庢槸鎴戝ぇ姒傚弬鑰冧簡涓€涓媊dubbo`宸叉湁鐨勫疄鐜帮紝鍋氫簡涓€鐐规敼杩涖€� + +`dubbo`閲岄潰鐨勬牳蹇冩娊璞℃槸`TpsLimiter`鎺ュ彛銆俙TpsLimitFilter`鍙槸绠€鍗曡皟鐢ㄤ簡涓€涓嬭繖涓帴鍙g殑鏂规硶鑰屽凡锛� + + + +杩欎釜鎶借薄鏄緢妫掔殑銆備絾鏄繕娆犵己浜嗕竴浜涙娊璞°€� + +瀹為檯涓婏紝涓€涓猅PS Limit灏辫瑙e喅涓変釜闂锛� + +1. 瀵逛粈涔堜笢瑗胯繘琛宍limit`銆傛瘮濡傝锛屽鏈嶅姟杩涜闄愭祦锛屾垨鑰呭鏌愪釜鏂规硶杩涜闄愭祦锛屾垨鑰呭IP杩涜闄愭祦锛屾垨鑰呭鐢ㄦ埛杩涜闄愭祦锛� +2. 濡備綍鍒ゆ柇宸茬粡`over limitation`銆傝繖鏄粠绠楁硶灞傞潰涓婅€冭檻锛屽嵆鐢ㄤ粈涔堢畻娉曟潵鍒ゆ柇鏌愪釜璋冪敤杩涙潵鐨勬椂鍊欙紝宸茬粡瓒呰繃閰嶇疆鐨勪笂闄愪簡锛� +3. 琚嫆缁濅箣鍚庤濡備綍澶勭悊銆傚鏋滀竴涓姹傝鏂畾涓哄凡缁廯over limititation`浜嗭紝閭d箞璇ユ€庝箞澶勭悊锛� + +鎵€浠ュ湪`TpsLimiter`鎺ュ彛鐨勫熀纭€涓婏紝鎴戝啀鍔犱簡涓や釜鎶借薄锛� + + + +TpsLimiter + + + +TpsLimitStrategy + + + +RejectedExecutionHandler + +`TpsLimiter`瀵瑰簲鍒癑ava鐨刞TpsLimiter`锛屼袱鑰呮槸宸笉澶氥€傚湪鎴戠殑璁炬兂閲岄潰锛屽畠鏃㈡槸椤剁骇鍏ュ彛锛岃繕闇€瑕佹壙鎷呰В鍐崇涓€涓棶棰樼殑鑱岃矗銆� + +鑰宍TpsLimitStrategy`鍒欐槸绗簩涓棶棰樼殑鎶借薄鐨勬帴鍙e畾涔夈€傚畠浠h〃鐨勬槸绾补鐨勭畻娉曘€傝鎺ュ彛瀹屽叏娌℃湁鍙傛暟锛屽疄闄呬笂锛屾墍鏈夌殑瀹炵幇闇€瑕佺淮鎶よ嚜韬殑鐘舵€佲€斺€斿浜庡ぇ閮ㄥ垎瀹炵幇鑰岃█锛屽畠澶ф鍙渶瑕佽幏鍙栦竴涓嬬郴缁熸椂闂存埑锛屾墍浠ヤ笉闇€瑕佸弬鏁般€� + +鏈€鍚庝竴涓帴鍙RejectedExecutionHandler`浠h〃鐨勬槸鎷掔粷绛栫暐銆傚湪`TpsLimitFilter`閲岄潰锛屽鏋滃畠璋冪敤`TpsLimiter`鐨勫疄鐜帮紝鍙戠幇璇ヨ姹傝鎷掔粷锛岄偅涔堝氨浼氫娇鐢ㄨ鎺ュ彛鐨勫疄鐜版潵鑾峰彇涓€涓繑鍥炲€硷紝杩斿洖缁欏鎴风銆� + +# 瀹炵幇 + +鍏跺疄瀹炵幇娌″お澶氬ソ璋堢殑銆備笉杩囨湁涓€浜涘井濡欑殑鍦版柟锛屾垜铏界劧鍦ㄤ唬鐮侀噷闈㈡敞閲婁簡锛屼絾鏄垜瑙夊緱鍦ㄨ繖閲屽啀澶氳涓€鐐逛篃鏄彲浠ョ殑銆� + +棣栧厛鎻愬強鐨勫氨鏄嫆缁濈瓥鐣RejectedExecutionHandler`锛屾垜灏辨槸鎻愪緵浜嗕竴绉嶅疄鐜帮紝灏辨槸闅忎究log浜嗕竴涓嬶紝浠€涔堥兘娌″仛銆傚洜涓鸿繖涓笢瑗挎槸寮轰笟鍔$浉鍏崇殑锛屾垜涔熶笉鑳芥彁渚涙洿鍔犲鐨勯€氱敤鐨勫疄鐜般€� + +## 鏂规硶涓庢湇鍔″弻閲嶆敮鎸佺殑TpsLimiter + +`TpsLimiter`鎴戝彧鏈変竴涓疄鐜帮紝閭e氨鏄痐MethodServiceTpsLimiterImpl`銆傚畠灏辨槸鏍规嵁閰嶇疆锛屽鏋滄柟娉曠骇鍒厤缃簡鍙傛暟锛岄偅涔堜細鍦ㄦ柟娉曠骇鍒笂杩涜闄愭祦銆傚惁鍒欙紝濡傛灉鍦ㄦ湇鍔$骇鍒紙ServiceKey锛変笂鏈夐厤缃紝閭d箞浼氬湪鏈嶅姟绾у埆杩涜闄愭祦銆� + +涓句釜鏈€澶嶆潅鐨勪緥瀛愶細鏈嶅姟A闄愬埗100锛屾湁鍥涗釜鏂规硶锛屾柟娉昅1閰嶇疆闄愬埗40锛屾柟娉昅2鍜屾柟娉昅3鏃犻厤缃紝鏂规硶M4閰嶇疆闄愬埗-1锛氶偅涔堟柟娉昅1浼氬崟鐙檺娴�40锛汳2鍜孧3鍚堝苟缁熻锛岃闄愬埗鍦�100锛涙柟娉昅4鍒欎細琚拷鐣ャ€� + +鐢ㄦ埛鍙互閰嶇疆鍏蜂綋鐨勭畻娉曘€傛瘮濡傝浣跨敤鎴戞帴涓嬫潵璇寸殑锛屾垜宸茬粡瀹炵幇鐨勪笁绉嶅疄鐜般€� + +## FixedWindow鍜孴hreadSafeFixedWindow + +`FixedWindow`鐩存帴瀵瑰簲鍒癑ava鐨刞DefaultTpsLimiter`銆傚畠閲囩敤鐨勬槸`fixed-window`绠楁硶锛氭瘮濡傝閰嶇疆浜嗕竴鍒嗛挓鍐呭彧鑳借皟鐢�100娆°€傚亣濡備粠00:00寮€濮嬭鏃讹紝閭d箞00:00-01:00鍐咃紝鍙兘璋冪敤100娆°€傚彧鏈夊埌杈�01:00锛屾墠浼氬紑鍚柊鐨勭獥鍙�01:00-02:00銆傚鍥撅細 + + + +Fixed-Window鍥剧ず + + + +Fixed-Window瀹炵幇 + +杩欓噷鏈変竴涓緢鏈夋剰鎬濈殑鍦版柟銆傚氨鏄繖涓疄鐜帮紝鏄竴涓嚑涔庣嚎绋嬪畨鍏ㄤ絾鏄叾瀹炲苟涓嶆槸绾跨▼瀹夊叏鐨勫疄鐜般€� + +鍦ㄦ墍鏈夌殑瀹炵幇閲岄潰锛屽畠鏄渶涓虹畝鍗曪紝鑰屼笖鎬ц兘鏈€楂樼殑銆傛垜鍦ㄨ 閲忎簡涓€鐣箣鍚庯紝杩樻槸娌℃妸瀹冨仛鎴愮嚎绋嬪畨鍏ㄧ殑銆備簨瀹炰笂锛孞ava鐗堟湰鐨勪篃涓嶆槸绾跨▼瀹夊叏鐨勩€� + +瀹冨彧浼氬湪澶氫釜绾跨▼閫氳繃绗�67琛岀殑妫€娴嬩箣鍚庯紝鎵嶄細鍑虹幇骞跺彂闂锛岃繖涓椂鍊欏氨涓嶆槸绾跨▼瀹夊叏浜嗐€備絾鏄湪鏈€鍚庣殑`return`璇彞涓紝閭d竴鏁翠釜鏄嚎绋嬪畨鍏ㄧ殑銆傚畠鍥犱负涓嶆柇璁℃暟寰€涓婂姞锛屾墍浠ュ涓嚎绋嬪悓鏃惰窇鍒拌繖閲岋紝鍏跺疄涓嶄細鏈変粈涔堥棶棰樸€� + +鐜板湪鎴戣鎻湶涓€涓渶涓哄璇$殑鐗规€т簡锛�**骞跺彂瓒婇珮锛岄偅涔堣繖涓猔raise condition`灏辫秺涓ラ噸锛屼篃灏辨槸璇磋秺涓嶅畨鍏ㄣ€�** + +浣嗘槸浠庡疄闄呬娇鐢ㄨ搴﹁€岃█锛屾湁鏋佺TPS鐨勮繕鏄瘮杈冨皯鐨勩€傚浜庨偅浜汿PS鍙湁鍑犵櫨姣忕鐨勶紝鏄病浠€涔堥棶棰樼殑銆� + +**涓轰簡淇濇寔鍜宒ubbo涓€鑷寸殑鐗规€э紝鎴戞妸瀹冧綔涓洪粯璁ょ殑瀹炵幇銆�** + +姝ゅ锛屾垜杩樹负瀹冩悶浜嗕竴涓嚎绋嬪畨鍏ㄧ増鏈紝涔熷氨鏄痐ThreadSafeFixedWindowTpsLimitStrategyImpl`锛屽彧鏄畝鍗曠殑鐢╜sync`灏佽浜嗕竴涓嬶紝鍙互鐪嬪仛鏄竴涓猔Decorator`妯″紡鐨勫簲鐢ㄣ€� + +濡傛灉寮烘眰绾跨▼瀹夊叏锛屽彲浠ヨ€冭檻浣跨敤杩欎釜銆� + +## SlidingWindow + +杩欐槸鎴戞瘮杈冨枩娆㈢殑瀹炵幇銆傚畠璺熺綉缁滃崗璁噷闈㈢殑婊戝姩绐楀彛绠楁硶鍦ㄧ悊蹇典笂鏄瘮杈冩帴杩戠殑銆� + + + +Sliding-Window + +鍏蜂綋鏉ヨ锛屽亣濡傛垜璁剧疆鐨勫悓鏍锋槸涓€鍒嗛挓1000娆★紝瀹冪粺璁$殑姘歌繙鏄粠褰撳墠鏃堕棿鐐瑰線鍓嶅洖婧竴鍒嗛挓鍐咃紝宸茬粡琚皟鐢ㄤ簡澶氬皯娆°€傚鏋滆繖涓€鍒嗛挓鍐咃紝璋冪敤娆℃暟娌¤秴杩�1000锛岃姹備細琚鐞嗭紝濡傛灉宸茬粡瓒呰繃锛岄偅涔堝氨浼氭嫆缁濄€� + +鎴戝啀鏉ユ弿杩颁竴涓嬶紝`SldingWindow`鍜宍FixedWindow`涓ょ绠楁硶鐨勫尯鍒€傝繖涓よ€呭緢澶氫汉浼氭悶娣枫€傚亣濡傚綋鍓嶇殑鏃堕棿鎴虫槸00:00锛屼袱涓畻娉曞悓鏃舵敹鍒颁簡绗竴涓姹傦紝寮€鍚涓€涓椂闂寸獥鍙c€� + +閭d箞`FixedWindow`灏辨槸00:00-01:00鏄涓€涓獥鍙o紝鎺ヤ笅鏉ヤ緷娆℃槸01:00-02:00, 02:00-03:00, ...銆傚綋鐒跺亣濡傝01:00涔嬪悗鐨勪笁鍗佺鍐呴兘娌℃湁璇锋眰锛屽湪01:31鍙堟潵浜嗕竴涓姹傦紝閭d箞鏃堕棿绐楀彛灏辨槸01:31-02:31銆� + +鑰宍SildingWindow`鍒欐病鏈夎繖绉嶆蹇点€傚亣濡傚湪01:30鏀跺埌涓€涓姹傦紝閭d箞`SlidingWindow`缁熻鐨勫垯鏄�00:30-01:30鍐呮湁娌℃湁杈惧埌1000娆°€�**瀹冩案杩滆绠楃殑閮芥槸鎺ユ敹鍒拌姹傜殑閭d竴鍒诲線鍓嶅洖婧竴鍒嗛挓鐨勮姹傛暟閲忋€�** + +濡傛灉杩樻槸瑙夊緱鏈夊洶闅撅紝閭d箞绠€鍗曟潵璇村氨鏄痐FixedWindow`寰€鍚庣湅涓€鍒嗛挓锛宍SlidingWindow`鍥炴函涓€鍒嗛挓銆� + +> 杩欎釜璇存硶骞朵笉涓ヨ皑锛屽彧鏄负浜嗘柟渚跨悊瑙c€� + +鍦ㄧ湡姝e啓杩欎釜瀹炵幇鐨勬椂鍊欙紝鎴戠◢寰敼浜嗕竴鐐圭偣锛� + + + +鎴戠敤浜嗕竴涓槦鍒楁潵淇濆瓨姣忔璁块棶鐨勬椂闂存埑銆備竴鑸殑鍐欐硶锛岄兘鏄姹傝繘鏉ワ紝鍏堟妸宸茬粡涓嶅湪绐楀彛鏃堕棿鍐呯殑鏃堕棿鎴冲垹鎺夛紝鐒跺悗缁熻鍓╀笅鐨勬暟閲忥紝涔熷氨鏄悗闈㈢殑`slow path`鐨勯偅涓€鍫嗛€昏緫銆� + +浣嗘槸鎴戞敼浜嗙殑涓€鐐规槸锛屾垜杩涙潵鐩存帴缁熻闃熷垪閲岄潰鐨勬暟閲忊€斺€斾篃灏辨槸璇锋眰鏁伴噺锛屽鏋滈兘灏忎簬涓婇檺锛岄偅涔堟垜鍙互鐩存帴杩斿洖`true`銆傚嵆`quick path`銆� + +杩欑鏀硅繘鐨勬牳蹇冨氨鏄細鎴戝彧鏈夊湪妫€娴嬪埌褰撳墠闃熷垪閲岄潰鏈夎秴杩囦笂闄愭暟閲忕殑璇锋眰鏁伴噺鏃跺€欙紝鎵嶄細灏濊瘯鍒犻櫎宸茬粡涓嶅湪绐楀彛鍐呯殑鏃堕棿鎴炽€� + +杩欏叾瀹炲氨鏄紝鏄瘡涓姹傝繃鏉ワ紝鎴戦兘娓呯悊涓€涓嬮槦鍒楀憿锛熻繕鏄彧鏈夐槦鍒楀厓绱犺秴鍑烘暟閲忎簡锛屾垜鎵嶆竻鐞嗗憿锛熸垜閫夋嫨鐨勬槸鍚庤€呫€� + +鎴戣涓鸿繖鏄竴绉嶆敼杩涒€︹€﹀綋鐒朵粠鏈川涓婃潵璇达紝鏁翠綋寮€閿€鏄病鏈夊噺灏戠殑鈥斺€斿洜涓篳golang`璇█閲岄潰`List`鐨勫疄鐜帮紝涓€娆″鍒犻櫎鍑犱釜锛屽拰姣忔鍒犻櫎涓€涓紝澶氬垹鍑犳锛屽苟娌℃湁澶氬ぇ鐨勫尯鍒€� + +### 绠楁硶鎬荤粨 + +鏃犺鏄痐FixedWindow`绠楁硶杩樻槸`SlidingWindow`绠楁硶閮芥湁涓€涓浐鏈夌殑缂洪櫡锛屽氨鏄繖涓椂闂寸獥鍙i毦鎺у埗銆� + +鎴戜滑璁炬兂涓€涓嬶紝鍋囧璇存垜浠妸鏃堕棿绐楀彛璁剧疆涓轰竴鍒嗛挓锛屽厑璁�1000娆¤皟鐢ㄣ€傜劧鑰岋紝鍦ㄥ墠鍗佺鐨勬椂鍊欏氨璋冪敤浜�1000娆°€傚湪鍚庨潰鐨勪簲鍗佺锛屾湇鍔″櫒铏界劧灏嗘墍鏈夌殑璇锋眰閮藉鐞嗗畬浜嗭紝鐒舵槸鍥犱负绐楀彛杩樻病鍒版柊绐楀彛锛屾墍浠ヨ繖涓椂闂存杩囨潵鐨勮姹傦紝鍏ㄩ儴浼氳鎷掔粷銆� + + + +瑙e喅鐨勬柟妗堝氨鏄皟灏忔椂闂寸獥鍙o紝姣斿璋冩暣鍒颁竴绉掋€備絾鏄椂闂寸獥鍙g殑缂╁皬锛屼細瀵艰嚧`FixedWindow`绠楁硶鐨刞raise condition`鎯呭喌鍔犲墽銆俙SlidingWindow`涔熶細鍙楀奖鍝嶏紝浣嗘槸褰卞搷瑕佸皬寰堝銆� + +## 閭d簺娌℃湁瀹炵幇鐨� + +### 鍩轰簬鐗瑰畾涓氬姟瀵硅薄鐨勯檺娴� + +涓句緥鏉ヨ锛屾煇浜涚壒娈婁笟鍔$敤鐨勯拡瀵圭敤鎴稩D杩涜闄愭祦鍜岄拡瀵笽P杩涜闄愭祦锛屾垜灏辨病鏈夊湪`dubbogo`閲岄潰瀹炵幇銆傛湁闇€瑕佺殑鍙互閫氳繃瀹炵幇`TpsLimiter`鎺ュ彛鏉ュ畬鎴愩€� + +### 鍏ㄥ眬TPS limit + +杩欑瘒鏂囩珷涔嬪墠璁ㄨ鐨勯兘鏄崟鏈洪檺娴併€傚鏋滃叏灞€闄愭祦锛屾瘮濡傝閽堝鏌愪釜瀹㈡埛锛屽畠璐拱鐨勬湇鍔℃槸姣忓垎閽熻皟鐢�100娆★紝閭d箞灏遍渶瑕佸叏灞€闄愭祦鈥斺€旇櫧鐒惰繖绉峜ase閮戒笉浼氱敤`Filter`鏂规锛岃€屾槸鍙﹀鍋氫竴涓猔API`鎺ュ叆鎺у埗銆� + +姣斿璇达紝寰堝父鐢ㄧ殑浣跨敤Redis杩涜闄愭祦鐨勩€傞拡瀵规煇涓鎴凤紝涓€鍒嗛挓鍙兘璁块棶100娆★紝閭f垜灏辩敤瀹㈡埛ID鍋歬ey锛寁alue璁剧疆鎴怢ist锛屾瘡娆¤皟鐢ㄨ繃鏉ワ紝闅忎究濉炰竴涓€艰繘鍘伙紝璁剧疆杩囨湡鏃堕棿涓€鍒嗛挓銆傞偅涔堟瘡娆$粺璁″彧闇€瑕佺粺璁″綋鍓峩ey鐨勫瓨娲荤殑鍊肩殑鏁伴噺灏卞彲浠ヤ簡銆� + +杩欑鎴戜篃娌″疄鐜帮紝鍥犱负濂藉儚娌′粈涔堥渶姹傘€傚浗鍐呰璁篢PS limit閮芥槸璁ㄨ鍗曟満TPS limit姣旇緝澶氥€� + +杩欎釜鍚屾牱鍙互閫氳繃瀹炵幇`TpsLimiter`鎺ュ彛鏉ュ疄鐜般€� + +### Leaky Bucket绠楁硶 + +杩欎釜鏈潵鍙互鏄痐TpsLimitStrategy`鐨勪竴绉嶅疄鐜扮殑銆傚悗鏉ユ垜瑙夊緱锛屽畠鍏跺疄骞舵病鏈夌壒鍒ぇ鐨勪紭鍔库€斺€旇櫧鐒跺彿绉板彲浠ュ仛鍒板潎鍖€锛屼絾鏄叾瀹炲苟鍋氫笉鍒扮湡姝g殑鍧囧寑銆傞€氳繃璋冩暣`SlidingWindow`鐨勭獥鍙eぇ灏忥紝鏄彲浠ユ帴杩戝畠瀹gО鐨勫潎鍖€娑堣垂鐨勬晥鏋滅殑銆傛瘮濡傝璋冩暣鍒颁竴绉掞紝閭e叾瀹炲氨宸茬粡寰堝潎鍖€浜嗐€傝€岃繖骞朵笉浼氬甫鏉ュ灏戦澶栫殑寮€閿€銆� \ No newline at end of file diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png new file mode 100644 index 0000000000000000000000000000000000000000..0f77e7c7bdd2e7c48d190647e36f17b07c18221f Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-a.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3189a4802785962c2e2dab94d3ed3acacd10cff6 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-b.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png new file mode 100644 index 0000000000000000000000000000000000000000..2fc84afe62b322fdcb85efb4ddef94ea48d19e6a Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-c.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png new file mode 100644 index 0000000000000000000000000000000000000000..fe5f233b6e238dbf5bc2fcfa89d336934d2bf064 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-d.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png new file mode 100644 index 0000000000000000000000000000000000000000..52298189dd0fe82728ed8d8f4525365d4b7b681d Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-e.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png new file mode 100644 index 0000000000000000000000000000000000000000..1368e1c0b718327a74f6028abea4dabaa22c5bcb Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-f.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png new file mode 100644 index 0000000000000000000000000000000000000000..ffde3b56b074e50dd26e158f38d721c40ab5aed4 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-g.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png new file mode 100644 index 0000000000000000000000000000000000000000..6783926994624da6c67902edfad886a3518cef28 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-h.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png new file mode 100644 index 0000000000000000000000000000000000000000..0e2c25b19895593acc72b71f95036526d3969eca Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-i.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png new file mode 100644 index 0000000000000000000000000000000000000000..1ea510561d01d57398e045720d51ac378716dae1 Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-j.png differ diff --git a/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png b/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png new file mode 100644 index 0000000000000000000000000000000000000000..f5ae884bfbed5e07f9a61056ff4f3b8f709b1b4a Binary files /dev/null and b/doc/pic/arch/dubbo-go-design-implement-and-featrues-k.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-0.jpg b/doc/pic/arch/dubbo-go-review-and-future-0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5ec22f175354319c8df25e52f65260bfd8394fb5 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-0.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-a.jpg b/doc/pic/arch/dubbo-go-review-and-future-a.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dc8f1dc5f4b011d6d4a1b90263532d1f222b3278 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-a.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-b.jpg b/doc/pic/arch/dubbo-go-review-and-future-b.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a8234572e3cd043a5f7a022bb5b714c8d637d9d3 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-b.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-c.jpg b/doc/pic/arch/dubbo-go-review-and-future-c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..652bd00dfdb10188bc47115477152baaa7bbb95e Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-c.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-d.jpg b/doc/pic/arch/dubbo-go-review-and-future-d.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5371cbbb76a0143eb463eb8d038e454c9767ace9 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-d.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-e.jpg b/doc/pic/arch/dubbo-go-review-and-future-e.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ad7a3f4c29b35a441d1de5a3d76a5dfcb0c9aec9 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-e.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-f.jpg b/doc/pic/arch/dubbo-go-review-and-future-f.jpg new file mode 100644 index 0000000000000000000000000000000000000000..13812d7eac01f5255fc99ab01f1eca4934d894f8 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-f.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-g.jpg b/doc/pic/arch/dubbo-go-review-and-future-g.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1a174ae11da2adb3d41439e0161310681c049c61 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-g.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-h.jpg b/doc/pic/arch/dubbo-go-review-and-future-h.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fc398af847858a3c46c20763fadbc3d0e92859c3 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-h.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-i.jpg b/doc/pic/arch/dubbo-go-review-and-future-i.jpg new file mode 100644 index 0000000000000000000000000000000000000000..42558f45f033552e6ed44e27265e27ac94fe1e8b Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-i.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-j.jpg b/doc/pic/arch/dubbo-go-review-and-future-j.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8eb3e7fd5baa2380993c10ea5fe19bb56fe0ef36 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-j.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-k.jpg b/doc/pic/arch/dubbo-go-review-and-future-k.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3af59c4f446a683fb4f60f90de6552fd2b73182d Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-k.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-l.jpg b/doc/pic/arch/dubbo-go-review-and-future-l.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7e10f682d09df3a9ad43dcec5a9e65648d9f93f0 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-l.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-m.jpg b/doc/pic/arch/dubbo-go-review-and-future-m.jpg new file mode 100644 index 0000000000000000000000000000000000000000..17d78da72cf769df66e02dda2080b3da82fb3b20 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-m.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-n.jpg b/doc/pic/arch/dubbo-go-review-and-future-n.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b64a975bae7e9281646718fe86ec93f5b2e631b6 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-n.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-o.png b/doc/pic/arch/dubbo-go-review-and-future-o.png new file mode 100644 index 0000000000000000000000000000000000000000..f97468ca767b65265dd868e1faec88612dd41087 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-o.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-p.png b/doc/pic/arch/dubbo-go-review-and-future-p.png new file mode 100644 index 0000000000000000000000000000000000000000..3f9e924e77ca6e84e9cb10334d59e81954059cfc Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-p.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-q.jpg b/doc/pic/arch/dubbo-go-review-and-future-q.jpg new file mode 100644 index 0000000000000000000000000000000000000000..48bb4a943caa06df3c6c6dad8201c4419f35e45e Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-q.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-r.jpg b/doc/pic/arch/dubbo-go-review-and-future-r.jpg new file mode 100644 index 0000000000000000000000000000000000000000..72290335becc150b03aab4d52b800885c1307dd2 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-r.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-s.jpg b/doc/pic/arch/dubbo-go-review-and-future-s.jpg new file mode 100644 index 0000000000000000000000000000000000000000..33b4d7472b8079029a2384e56c8af7e95aec7e04 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-s.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-t.png b/doc/pic/arch/dubbo-go-review-and-future-t.png new file mode 100644 index 0000000000000000000000000000000000000000..7aee88fb00ae89597bbc0f0704e42af2ddad44d5 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-t.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-u.png b/doc/pic/arch/dubbo-go-review-and-future-u.png new file mode 100644 index 0000000000000000000000000000000000000000..99649afbec228c22188d19bd93a6c5d7f1ce69af Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-u.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-v.jpg b/doc/pic/arch/dubbo-go-review-and-future-v.jpg new file mode 100644 index 0000000000000000000000000000000000000000..909738ebae532d4ff02f5332128b1b250d47c870 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-v.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-w.jpg b/doc/pic/arch/dubbo-go-review-and-future-w.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ec8606bc02173a3518e86a0d45154a3128f723fa Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-w.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-x.png b/doc/pic/arch/dubbo-go-review-and-future-x.png new file mode 100644 index 0000000000000000000000000000000000000000..328e046643ebbde623696b4acb1a66ddb850bfab Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-x.png differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-y.jpg b/doc/pic/arch/dubbo-go-review-and-future-y.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c8f1801e0778cbd21480a241071cfbc0d24ab44e Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-y.jpg differ diff --git a/doc/pic/arch/dubbo-go-review-and-future-z.jpg b/doc/pic/arch/dubbo-go-review-and-future-z.jpg new file mode 100644 index 0000000000000000000000000000000000000000..219784c9bc72b42b02b0b6adada688dd1f021b04 Binary files /dev/null and b/doc/pic/arch/dubbo-go-review-and-future-z.jpg differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3fc7600e7618d1d026c5bc41d78556bca801c6fa Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-a.jpg differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png new file mode 100644 index 0000000000000000000000000000000000000000..08b9f62d733a5f4dcae4c38cc9422b60cfa26b24 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-b.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png new file mode 100644 index 0000000000000000000000000000000000000000..046f9be15864c3938e90c77728d5e8743c57465a Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-c.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png new file mode 100644 index 0000000000000000000000000000000000000000..0c0ad73c0836b53675eb7c36f0149aea8839be13 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-d.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png new file mode 100644 index 0000000000000000000000000000000000000000..4c035fd8e5bc31ccb08b41483795e926d06e1fce Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-e.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png new file mode 100644 index 0000000000000000000000000000000000000000..cebcd376722e7e630a0b8969ed8c4b2722b01000 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-f.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png new file mode 100644 index 0000000000000000000000000000000000000000..fdee33ba12ce4d10a4a866b5dc872e6d114c29d5 Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-g.png differ diff --git a/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png new file mode 100644 index 0000000000000000000000000000000000000000..47b9ea64dbe8d1b4d7431baaae74946e37dafa7b Binary files /dev/null and b/doc/pic/config-center/how-to-implement-remote-configuration-management-in-dubbo-go-h.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-a.png b/doc/pic/course/the-5th-years-of-dubbo-go-a.png new file mode 100644 index 0000000000000000000000000000000000000000..6e8fa474f8f791e43770b872499d672383bf05e3 Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-a.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-b.png b/doc/pic/course/the-5th-years-of-dubbo-go-b.png new file mode 100644 index 0000000000000000000000000000000000000000..09bb6917172f19b5f6dfd7dedaa23c98d74a5cc4 Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-b.png differ diff --git a/doc/pic/course/the-5th-years-of-dubbo-go-c.png b/doc/pic/course/the-5th-years-of-dubbo-go-c.png new file mode 100644 index 0000000000000000000000000000000000000000..31b09ae1a7c5866c3f24ff095ba326d9066d66dd Binary files /dev/null and b/doc/pic/course/the-5th-years-of-dubbo-go-c.png differ diff --git a/doc/pic/getty/getty_connected_udp_socket.gif b/doc/pic/getty/getty_connected_udp_socket.gif new file mode 100644 index 0000000000000000000000000000000000000000..011cd2329e874636b881012129cdd8256dd6da10 Binary files /dev/null and b/doc/pic/getty/getty_connected_udp_socket.gif differ diff --git a/doc/pic/getty/getty_dns_udp.gif b/doc/pic/getty/getty_dns_udp.gif new file mode 100644 index 0000000000000000000000000000000000000000..253dddf2395a4624ba4bb8661806851000f46c3d Binary files /dev/null and b/doc/pic/getty/getty_dns_udp.gif differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png new file mode 100644 index 0000000000000000000000000000000000000000..484e07d3acf12da294bc01ed462a752ba8df6261 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-a.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png new file mode 100644 index 0000000000000000000000000000000000000000..2a208c6952177dd56e5c78ddf3afbfd365050803 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-b.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png new file mode 100644 index 0000000000000000000000000000000000000000..f89c20fe3b3c6184a653887c0aae41b2c767e3cf Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-c.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png new file mode 100644 index 0000000000000000000000000000000000000000..5d639b918a5da58e50ac4c1bf65bc959247f7c76 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-d.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png new file mode 100644 index 0000000000000000000000000000000000000000..940950ec290a4a1ff415179156a372296acb0114 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-e.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png new file mode 100644 index 0000000000000000000000000000000000000000..26a543b22739038730542e984b299fea078b318d Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-f.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png new file mode 100644 index 0000000000000000000000000000000000000000..a98c7222d7bf1de7e241015a2aa3088f758d0b89 Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-g.png differ diff --git a/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png new file mode 100644 index 0000000000000000000000000000000000000000..3da98bfe966b5aed47899c090095c567f22f7d3b Binary files /dev/null and b/doc/pic/hessian2/dubbo-go-hessian2-performance-optimization-h.png differ diff --git a/doc/pic/interview/dubbo-go-published-a.jpg b/doc/pic/interview/dubbo-go-published-a.jpg new file mode 100644 index 0000000000000000000000000000000000000000..ce27147a1ad97ba3b227837b2e98464ff813def1 Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-a.jpg differ diff --git a/doc/pic/interview/dubbo-go-published-b.jpg b/doc/pic/interview/dubbo-go-published-b.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9cc7e6486c98ac4e12f7a989524353c3aa3d1178 Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-b.jpg differ diff --git a/doc/pic/interview/dubbo-go-published-c.jpg b/doc/pic/interview/dubbo-go-published-c.jpg new file mode 100644 index 0000000000000000000000000000000000000000..69c70c7d516924e54003bf5bd49793befb09439d Binary files /dev/null and b/doc/pic/interview/dubbo-go-published-c.jpg differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png new file mode 100644 index 0000000000000000000000000000000000000000..97f3caa29e076b651ea4477cdac637c6dc4bee26 Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-a.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png new file mode 100644 index 0000000000000000000000000000000000000000..a491cbbf46fff38962456b85cf0e4ee37b5cf128 Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-b.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png new file mode 100644 index 0000000000000000000000000000000000000000..fa867fc6cf437daf5f67e0a03d34f3daa839b43f Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-c.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png new file mode 100644 index 0000000000000000000000000000000000000000..5fe471a16d412741aa4668f9dbd4135d4281bbc4 Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-d.png differ diff --git a/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png new file mode 100644 index 0000000000000000000000000000000000000000..679e90abfc8e6ebf744902f89de2cf29e9789719 Binary files /dev/null and b/doc/pic/interview/what's-new-in-dubbo-go-v1.4.0-e.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png new file mode 100644 index 0000000000000000000000000000000000000000..dafc1505533b67fd61c26ba284fb13fb70410639 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-a.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png new file mode 100644 index 0000000000000000000000000000000000000000..85b3997c8fb22918d311d160a3154ef4e608c60f Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-b.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png new file mode 100644 index 0000000000000000000000000000000000000000..11ada496e3702c70390b7de1bb37e617cf9c7e12 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-c.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png new file mode 100644 index 0000000000000000000000000000000000000000..e66f3bd3895cfaddb1293e5d54cd45f3859afcfb Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-d.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png new file mode 100644 index 0000000000000000000000000000000000000000..a02d9c7322aa118e6d8eaab014ff41ef9febb49a Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-e.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png new file mode 100644 index 0000000000000000000000000000000000000000..4d97fcf4c20b68723933ba05d9919910a334ab56 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-f.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png new file mode 100644 index 0000000000000000000000000000000000000000..da690449bd3b1be01d50cdab787fdf7d47c62bb2 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-g.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png new file mode 100644 index 0000000000000000000000000000000000000000..1a3e6834fb4e92b4a840e7741ad271adc1a0ff7e Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-h.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png new file mode 100644 index 0000000000000000000000000000000000000000..a5e7503ed3b17933aeb4ca2208b367851feb15f6 Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-i.png differ diff --git a/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png b/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png new file mode 100644 index 0000000000000000000000000000000000000000..a05602b8154e3f710c457f4bf62410236d3547dd Binary files /dev/null and b/doc/pic/registry-center/dubbo-go-registry-center--nacos-j.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png new file mode 100644 index 0000000000000000000000000000000000000000..87726d88484c23d6395023bb10e86009d59a1fd7 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-a.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f2c8dcaf39db8aeb3920dc638e3472bc0fc182 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-b.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png new file mode 100644 index 0000000000000000000000000000000000000000..c55fa2e7af662598b2fea728ac10f8798450ad9c Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-c.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png new file mode 100644 index 0000000000000000000000000000000000000000..3d57d26861d6bfd6fbf43f077a30600df2ce8629 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-d.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png new file mode 100644 index 0000000000000000000000000000000000000000..0474e7c9279fd7619e42db16076436adfae155dd Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-e.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png new file mode 100644 index 0000000000000000000000000000000000000000..f763f7c51d3fa1a679ae015797000f7fb85a8c2d Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-f.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png new file mode 100644 index 0000000000000000000000000000000000000000..5d71cd67b652d03e432c25eca905d880b91e14a3 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-g.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png new file mode 100644 index 0000000000000000000000000000000000000000..710ff688cc792ae2e224c76140dedb8a8e3bdcad Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-h.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png new file mode 100644 index 0000000000000000000000000000000000000000..6112cfe105623963c5d720c9244c4cc0015b63e4 Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-i.png differ diff --git a/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png new file mode 100644 index 0000000000000000000000000000000000000000..10e69939abb5e67139a8478a1b7750ae6066e73b Binary files /dev/null and b/doc/pic/routing-rule/how-to-implement-routing-rule-in-dubbo-go-j.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-a.png b/doc/pic/rpc/dubb-go-adaptive-grpc-a.png new file mode 100644 index 0000000000000000000000000000000000000000..c71a36149815c14eff9f730d1e9dae01df8f20d4 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-a.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-b.png b/doc/pic/rpc/dubb-go-adaptive-grpc-b.png new file mode 100644 index 0000000000000000000000000000000000000000..b401df59e5255cd4263d623658e40fcf0ccf6710 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-b.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-c.png b/doc/pic/rpc/dubb-go-adaptive-grpc-c.png new file mode 100644 index 0000000000000000000000000000000000000000..66fc888c5431d5e53e030bb15dadaa065919149f Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-c.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-d.png b/doc/pic/rpc/dubb-go-adaptive-grpc-d.png new file mode 100644 index 0000000000000000000000000000000000000000..59160482c23c2170624c1f37dd8d3b40414c5565 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-d.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-e.png b/doc/pic/rpc/dubb-go-adaptive-grpc-e.png new file mode 100644 index 0000000000000000000000000000000000000000..9f81f6226db1284f12d5d0b7cd4df3ffe096732e Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-e.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-f.png b/doc/pic/rpc/dubb-go-adaptive-grpc-f.png new file mode 100644 index 0000000000000000000000000000000000000000..d35e11c3f17c60cc02462c67edb1e5dd84e5548f Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-f.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-g.png b/doc/pic/rpc/dubb-go-adaptive-grpc-g.png new file mode 100644 index 0000000000000000000000000000000000000000..9c52e9e88abaa294c81573abade80b00c3bf9ea7 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-g.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-h.png b/doc/pic/rpc/dubb-go-adaptive-grpc-h.png new file mode 100644 index 0000000000000000000000000000000000000000..a1833f0fa1a35b867d4c112773b0c763f3c9f407 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-h.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-i.png b/doc/pic/rpc/dubb-go-adaptive-grpc-i.png new file mode 100644 index 0000000000000000000000000000000000000000..a5d0deb864c6d6cea753def7aac226b320a8b72f Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-i.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-j.png b/doc/pic/rpc/dubb-go-adaptive-grpc-j.png new file mode 100644 index 0000000000000000000000000000000000000000..de21c4fa2d4c2cadfc9a0488bc005db221086713 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-j.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-k.png b/doc/pic/rpc/dubb-go-adaptive-grpc-k.png new file mode 100644 index 0000000000000000000000000000000000000000..813cee6ce0fc54f82634360566b26dd486ffc3bc Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-k.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-l.png b/doc/pic/rpc/dubb-go-adaptive-grpc-l.png new file mode 100644 index 0000000000000000000000000000000000000000..747a5a2059cfb63b05673c43d5d95d921b573e08 Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-l.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-m.png b/doc/pic/rpc/dubb-go-adaptive-grpc-m.png new file mode 100644 index 0000000000000000000000000000000000000000..55a1ae13de227728bf04371e0bd9a1a0e4ec6fba Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-m.png differ diff --git a/doc/pic/rpc/dubb-go-adaptive-grpc-n.png b/doc/pic/rpc/dubb-go-adaptive-grpc-n.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf287145a946768ba0ac3d17d6602f1972a1ffd Binary files /dev/null and b/doc/pic/rpc/dubb-go-adaptive-grpc-n.png differ diff --git a/doc/pic/service-governance/dubbo-go-sentinel-a.png b/doc/pic/service-governance/dubbo-go-sentinel-a.png new file mode 100644 index 0000000000000000000000000000000000000000..3806989c8b578e9cf4eae822e005493de3043ddd Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-sentinel-a.png differ diff --git a/doc/pic/service-governance/dubbo-go-sentinel-b.png b/doc/pic/service-governance/dubbo-go-sentinel-b.png new file mode 100644 index 0000000000000000000000000000000000000000..03df855c491f5b02a371a8e5504e3b6e5a55916b Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-sentinel-b.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png new file mode 100644 index 0000000000000000000000000000000000000000..8a34165318bf0d55a0c43943a082cf1119b8ffdb Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-a.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png new file mode 100644 index 0000000000000000000000000000000000000000..3a6f1f15122dd058727617f2168e3eb364957fe0 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-b.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png new file mode 100644 index 0000000000000000000000000000000000000000..77f10d99a5efbda5da57911884e7d884d1a94167 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-c.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png new file mode 100644 index 0000000000000000000000000000000000000000..65b7185a8a1c306ce1712208bedba951f38b6c40 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-d.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png new file mode 100644 index 0000000000000000000000000000000000000000..07740479b74ca686bed1af5221b49087916ca216 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-e.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png new file mode 100644 index 0000000000000000000000000000000000000000..49b30ef7fe1a0a82030b3489ac9eca9301609c2c Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-f.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png new file mode 100644 index 0000000000000000000000000000000000000000..22d4e6f01c53a790586a70338e455fdaac472d67 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-g.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png new file mode 100644 index 0000000000000000000000000000000000000000..132057b12a8484969ca98b4ec90cecf2c382fc5b Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-h.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png new file mode 100644 index 0000000000000000000000000000000000000000..b39a83787b085b902d50866703b454c4573fdaf3 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-i.png differ diff --git a/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png new file mode 100644 index 0000000000000000000000000000000000000000..e671ff58ce055934a54528696135f42a5e848cd1 Binary files /dev/null and b/doc/pic/service-governance/dubbo-go-tps-limit-design-and-implement-j.png differ diff --git a/doc/ppt/arch/dubbogo20191228-hangzhou.pptx b/doc/ppt/arch/dubbogo20191228-hangzhou.pptx new file mode 100644 index 0000000000000000000000000000000000000000..c6c09b289067b38c147c813318a4bbd9c183c44e Binary files /dev/null and b/doc/ppt/arch/dubbogo20191228-hangzhou.pptx differ diff --git a/doc/ppt/arch/panty_dubbo-go_share.pptx b/doc/ppt/arch/panty_dubbo-go_share.pptx new file mode 100644 index 0000000000000000000000000000000000000000..65c510003724b18c70041b6c164fa733fc6b31d5 Binary files /dev/null and b/doc/ppt/arch/panty_dubbo-go_share.pptx differ diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go index 621012c24c0ad12b3bda397148a3ed9c29d080ed..6eaf9cb00bafe8fb0d4b9f8cda50e6bc7115461b 100644 --- a/filter/filter_impl/access_log_filter.go +++ b/filter/filter_impl/access_log_filter.go @@ -105,13 +105,27 @@ func (ef *AccessLogFilter) logIntoChannel(accessLogData AccessLogData) { 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] - dataMap[constant.METHOD_KEY] = invocation.MethodName() - dataMap[constant.VERSION_KEY] = attachments[constant.VERSION_KEY] - dataMap[constant.GROUP_KEY] = attachments[constant.GROUP_KEY] - dataMap[constant.TIMESTAMP_KEY] = time.Now().Format(MessageDateLayout) - dataMap[constant.LOCAL_ADDR], _ = attachments[constant.LOCAL_ADDR] - dataMap[constant.REMOTE_ADDR], _ = attachments[constant.REMOTE_ADDR] + if v, ok := attachments[constant.INTERFACE_KEY]; ok && v != nil { + dataMap[constant.INTERFACE_KEY] = v.(string) + } + if v, ok := attachments[constant.METHOD_KEY]; ok && v != nil { + dataMap[constant.METHOD_KEY] = v.(string) + } + if v, ok := attachments[constant.VERSION_KEY]; ok && v != nil { + dataMap[constant.VERSION_KEY] = v.(string) + } + if v, ok := attachments[constant.GROUP_KEY]; ok && v != nil { + dataMap[constant.GROUP_KEY] = v.(string) + } + if v, ok := attachments[constant.TIMESTAMP_KEY]; ok && v != nil { + dataMap[constant.TIMESTAMP_KEY] = v.(string) + } + if v, ok := attachments[constant.LOCAL_ADDR]; ok && v != nil { + dataMap[constant.LOCAL_ADDR] = v.(string) + } + if v, ok := attachments[constant.REMOTE_ADDR]; ok && v != nil { + dataMap[constant.REMOTE_ADDR] = v.(string) + } if len(invocation.Arguments()) > 0 { builder := strings.Builder{} diff --git a/filter/filter_impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go index 55c328cc30ae892c603fcc65034e48d2a52403d2..a3a6151aa1b6a933c57543248f3703125fa356d9 100644 --- a/filter/filter_impl/access_log_filter_test.go +++ b/filter/filter_impl/access_log_filter_test.go @@ -45,7 +45,7 @@ func TestAccessLogFilter_Invoke_Not_Config(t *testing.T) { "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) + attach := make(map[string]interface{}, 10) inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) accessLogFilter := GetAccessLogFilter() @@ -64,7 +64,7 @@ func TestAccessLogFilterInvokeDefaultConfig(t *testing.T) { "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) + attach := make(map[string]interface{}, 10) attach[constant.VERSION_KEY] = "1.0" attach[constant.GROUP_KEY] = "MyGroup" inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go index 6b72830e6a1a523b775b9294863ab18f8fe518a2..9837a49c72e28c7a7209f8af6059bdc30c222cc2 100644 --- a/filter/filter_impl/active_filter_test.go +++ b/filter/filter_impl/active_filter_test.go @@ -37,7 +37,7 @@ import ( ) func TestActiveFilterInvoke(t *testing.T) { - invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]interface{}, 0)) url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") filter := ActiveFilter{} ctrl := gomock.NewController(t) @@ -53,7 +53,7 @@ func TestActiveFilterInvoke(t *testing.T) { func TestActiveFilterOnResponse(t *testing.T) { c := protocol.CurrentTimeMillis() elapsed := 100 - invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{ + invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{ dubboInvokeStartTime: strconv.FormatInt(c-int64(elapsed), 10), }) url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go index 5b107b5960ff5adc383d52aa5e393d9fc6e71d14..8b0fb6b0e3de04ccb2c30d2c7e22c32af5733742 100644 --- a/filter/filter_impl/auth/default_authenticator_test.go +++ b/filter/filter_impl/auth/default_authenticator_test.go @@ -52,7 +52,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { var authenticator = &DefaultAuthenticator{} - invcation := invocation.NewRPCInvocation("test", parmas, map[string]string{ + invcation := invocation.NewRPCInvocation("test", parmas, map[string]interface{}{ constant.REQUEST_SIGNATURE_KEY: signature, constant.CONSUMER: "test", constant.REQUEST_TIMESTAMP_KEY: requestTime, @@ -61,7 +61,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { err := authenticator.Authenticate(invcation, &testurl) assert.Nil(t, err) // modify the params - invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]string{ + invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]interface{}{ constant.REQUEST_SIGNATURE_KEY: signature, constant.CONSUMER: "test", constant.REQUEST_TIMESTAMP_KEY: requestTime, @@ -119,7 +119,7 @@ func Test_getAccessKeyPairFailed(t *testing.T) { 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{ + inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{ "": "", }) secret := "dubbo" diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go index 626782ae8390f046f441c1f162700a883e6f22d0..f6ebfcd7abb83a55b4a09c1ec2d6c71b88bf7727 100644 --- a/filter/filter_impl/auth/provider_auth_test.go +++ b/filter/filter_impl/auth/provider_auth_test.go @@ -54,7 +54,7 @@ func TestProviderAuthFilter_Invoke(t *testing.T) { requestTime := strconv.Itoa(int(time.Now().Unix() * 1000)) signature, _ := getSignature(&url, inv, secret, requestTime) - inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]string{ + inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{ constant.REQUEST_SIGNATURE_KEY: signature, constant.CONSUMER: "test", constant.REQUEST_TIMESTAMP_KEY: requestTime, diff --git a/filter/filter_impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go index d36d6edef1ec52c24a9ccd64233b4620b4f10bc7..953f5e1cc8f6ff3299dcac21c5cd2a41de08cdc1 100644 --- a/filter/filter_impl/execute_limit_filter_test.go +++ b/filter/filter_impl/execute_limit_filter_test.go @@ -36,7 +36,7 @@ import ( func TestExecuteLimitFilterInvokeIgnored(t *testing.T) { methodName := "hello" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), @@ -51,7 +51,7 @@ func TestExecuteLimitFilterInvokeIgnored(t *testing.T) { func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), @@ -68,7 +68,7 @@ func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) { func TestExecuteLimitFilterInvoke(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), diff --git a/filter/filter_impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go index 87ac2eac616a20617b7a5e68254a1f47ecb8ac17..220ef6f20857aa06bcc45d3ac0a9bd52b0d65af2 100644 --- a/filter/filter_impl/graceful_shutdown_filter_test.go +++ b/filter/filter_impl/graceful_shutdown_filter_test.go @@ -39,7 +39,7 @@ import ( ) func TestGenericFilterInvoke(t *testing.T) { - invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]interface{}, 0)) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{})) diff --git a/filter/filter_impl/hystrix_filter.go b/filter/filter_impl/hystrix_filter.go index e2275149f1229c87ed4ae3f89dc9ae32d9fe6461..d13e02c06f7b2191e4d01c013a72300e1b095616 100644 --- a/filter/filter_impl/hystrix_filter.go +++ b/filter/filter_impl/hystrix_filter.go @@ -53,10 +53,6 @@ var ( providerConfigOnce sync.Once ) -//The filter in the server end of dubbo-go can't get the invoke result for now, -//this filter ONLY works in CLIENT end (consumer side) temporarily -//Only after the callService logic is integrated into the filter chain of server end then the filter can be used, -//which will be done soon func init() { extension.SetFilter(HYSTRIX_CONSUMER, GetHystrixFilterConsumer) extension.SetFilter(HYSTRIX_PROVIDER, GetHystrixFilterProvider) @@ -85,7 +81,47 @@ func NewHystrixFilterError(err error, failByHystrix bool) error { } } -// nolint +/** + * HystrixFilter + * You should add hystrix related configuration in provider or consumer config or both, according to which side you are to apply HystrixFilter. + * For example: + * filter_conf: + * hystrix: + * configs: + * # =========== Define config here ============ + * "Default": + * timeout : 1000 + * max_concurrent_requests : 25 + * sleep_window : 5000 + * error_percent_threshold : 50 + * request_volume_threshold: 20 + * "userp": + * timeout: 2000 + * max_concurrent_requests: 512 + * sleep_window: 4000 + * error_percent_threshold: 35 + * request_volume_threshold: 6 + * "userp_m": + * timeout : 1200 + * max_concurrent_requests : 512 + * sleep_window : 6000 + * error_percent_threshold : 60 + * request_volume_threshold: 16 + * # =========== Define error whitelist which will be ignored by Hystrix counter ============ + * error_whitelist: [".*exception.*"] + * + * # =========== Apply default config here =========== + * default: "Default" + * + * services: + * "com.ikurento.user.UserProvider": + * # =========== Apply service level config =========== + * service_config: "userp" + * # =========== Apply method level config =========== + * methods: + * "GetUser": "userp_m" + * "GetUser1": "userp_m" + */ type HystrixFilter struct { COrP bool //true for consumer res map[string][]*regexp.Regexp @@ -213,11 +249,11 @@ func getConfig(service string, method string, cOrP bool) CommandConfigWithError func initHystrixConfigConsumer() error { if config.GetConsumerConfig().FilterConf == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_consumer") } filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] if filterConfig == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_consumer") } hystrixConfByte, err := yaml.Marshal(filterConfig) if err != nil { @@ -232,11 +268,11 @@ func initHystrixConfigConsumer() error { func initHystrixConfigProvider() error { if config.GetProviderConfig().FilterConf == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_provider") } - filterConfig := config.GetConsumerConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] + filterConfig := config.GetProviderConfig().FilterConf.(map[interface{}]interface{})[HYSTRIX] if filterConfig == nil { - return perrors.Errorf("no config for hystrix") + return perrors.Errorf("no config for hystrix_provider") } hystrixConfByte, err := yaml.Marshal(filterConfig) if err != nil { diff --git a/filter/filter_impl/metrics_filter_test.go b/filter/filter_impl/metrics_filter_test.go index 881106f4bc5a7890569be347122da5144e440c8b..ac10d52cf3c156e3580760a4409bab49bb4d0c4f 100644 --- a/filter/filter_impl/metrics_filter_test.go +++ b/filter/filter_impl/metrics_filter_test.go @@ -57,7 +57,7 @@ func TestMetricsFilterInvoke(t *testing.T) { "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) + attach := make(map[string]interface{}, 10) inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) ctx := context.Background() diff --git a/filter/filter_impl/seata_filter_test.go b/filter/filter_impl/seata_filter_test.go index 6c39897f7aa3a044490e9eece8f352a9db9fc74b..45817e95cbd2eaa7365adc8a299523af8310f797 100644 --- a/filter/filter_impl/seata_filter_test.go +++ b/filter/filter_impl/seata_filter_test.go @@ -48,8 +48,9 @@ func (iv *testMockSeataInvoker) Invoke(ctx context.Context, _ protocol.Invocatio func TestSeataFilter_Invoke(t *testing.T) { filter := getSeataFilter() - result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, map[string]string{ - SEATA_XID: "10.30.21.227:8091:2000047792", - })) + result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo", + []interface{}{"OK"}, map[string]interface{}{ + SEATA_XID: "10.30.21.227:8091:2000047792", + })) assert.Equal(t, "10.30.21.227:8091:2000047792", result.Result()) } diff --git a/filter/filter_impl/sentinel_filter.go b/filter/filter_impl/sentinel_filter.go new file mode 100644 index 0000000000000000000000000000000000000000..86d6460acd840fb85b821f50c81f944da4c1b926 --- /dev/null +++ b/filter/filter_impl/sentinel_filter.go @@ -0,0 +1,237 @@ +/* + * 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" + "fmt" + "strings" +) + +import ( + sentinel "github.com/alibaba/sentinel-golang/api" + "github.com/alibaba/sentinel-golang/core/base" + "github.com/alibaba/sentinel-golang/logging" +) + +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" +) + +func init() { + extension.SetFilter(SentinelProviderFilterName, GetSentinelProviderFilter) + extension.SetFilter(SentinelConsumerFilterName, GetSentinelConsumerFilter) + + if err := sentinel.InitDefault(); err != nil { + logger.Errorf("[Sentinel Filter] fail to initialize Sentinel") + } + if err := logging.ResetGlobalLogger(DubboLoggerWrapper{Logger: logger.GetLogger()}); err != nil { + logger.Errorf("[Sentinel Filter] fail to ingest dubbo logger into sentinel") + } +} + +type DubboLoggerWrapper struct { + logger.Logger +} + +func (d DubboLoggerWrapper) Fatal(v ...interface{}) { + d.Logger.Error(v...) +} + +func (d DubboLoggerWrapper) Fatalf(format string, v ...interface{}) { + d.Logger.Errorf(format, v...) +} + +func (d DubboLoggerWrapper) Panic(v ...interface{}) { + d.Logger.Error(v...) +} + +func (d DubboLoggerWrapper) Panicf(format string, v ...interface{}) { + d.Logger.Errorf(format, v...) +} + +func GetSentinelConsumerFilter() filter.Filter { + return &SentinelConsumerFilter{} +} + +func GetSentinelProviderFilter() filter.Filter { + return &SentinelProviderFilter{} +} + +func sentinelExit(ctx context.Context, result protocol.Result) { + if methodEntry := ctx.Value(MethodEntryKey); methodEntry != nil { + e := methodEntry.(*base.SentinelEntry) + sentinel.TraceError(e, result.Error()) + e.Exit() + } + if interfaceEntry := ctx.Value(InterfaceEntryKey); interfaceEntry != nil { + e := interfaceEntry.(*base.SentinelEntry) + sentinel.TraceError(e, result.Error()) + e.Exit() + } +} + +type SentinelProviderFilter struct{} + +func (d *SentinelProviderFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getProviderPrefix()) + + var ( + interfaceEntry *base.SentinelEntry + methodEntry *base.SentinelEntry + b *base.BlockError + ) + interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Inbound)) + if b != nil { + // interface blocked + return sentinelDubboProviderFallback(ctx, invoker, invocation, b) + } + ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry) + + methodEntry, b = sentinel.Entry(methodResourceName, + sentinel.WithResourceType(base.ResTypeRPC), + sentinel.WithTrafficType(base.Inbound), + sentinel.WithArgs(invocation.Arguments()...)) + if b != nil { + // method blocked + return sentinelDubboProviderFallback(ctx, invoker, invocation, b) + } + ctx = context.WithValue(ctx, MethodEntryKey, methodEntry) + return invoker.Invoke(ctx, invocation) +} + +func (d *SentinelProviderFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { + sentinelExit(ctx, result) + return result +} + +type SentinelConsumerFilter struct{} + +func (d *SentinelConsumerFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { + interfaceResourceName, methodResourceName := getResourceName(invoker, invocation, getConsumerPrefix()) + var ( + interfaceEntry *base.SentinelEntry + methodEntry *base.SentinelEntry + b *base.BlockError + ) + + interfaceEntry, b = sentinel.Entry(interfaceResourceName, sentinel.WithResourceType(base.ResTypeRPC), sentinel.WithTrafficType(base.Outbound)) + if b != nil { + // interface blocked + return sentinelDubboConsumerFallback(ctx, invoker, invocation, b) + } + ctx = context.WithValue(ctx, InterfaceEntryKey, interfaceEntry) + + methodEntry, b = sentinel.Entry(methodResourceName, sentinel.WithResourceType(base.ResTypeRPC), + sentinel.WithTrafficType(base.Outbound), sentinel.WithArgs(invocation.Arguments()...)) + if b != nil { + // method blocked + return sentinelDubboConsumerFallback(ctx, invoker, invocation, b) + } + ctx = context.WithValue(ctx, MethodEntryKey, methodEntry) + + return invoker.Invoke(ctx, invocation) +} + +func (d *SentinelConsumerFilter) OnResponse(ctx context.Context, result protocol.Result, _ protocol.Invoker, _ protocol.Invocation) protocol.Result { + sentinelExit(ctx, result) + return result +} + +var ( + sentinelDubboConsumerFallback = getDefaultDubboFallback() + sentinelDubboProviderFallback = getDefaultDubboFallback() +) + +type DubboFallback func(context.Context, protocol.Invoker, protocol.Invocation, *base.BlockError) protocol.Result + +func SetDubboConsumerFallback(f DubboFallback) { + sentinelDubboConsumerFallback = f +} +func SetDubboProviderFallback(f DubboFallback) { + sentinelDubboProviderFallback = f +} +func getDefaultDubboFallback() DubboFallback { + return func(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation, blockError *base.BlockError) protocol.Result { + result := &protocol.RPCResult{} + result.SetResult(nil) + result.SetError(blockError) + return result + } +} + +const ( + SentinelProviderFilterName = "sentinel-provider" + SentinelConsumerFilterName = "sentinel-consumer" + + DefaultProviderPrefix = "dubbo:provider:" + DefaultConsumerPrefix = "dubbo:consumer:" + + MethodEntryKey = "$$sentinelMethodEntry" + InterfaceEntryKey = "$$sentinelInterfaceEntry" +) + +func getResourceName(invoker protocol.Invoker, invocation protocol.Invocation, prefix string) (interfaceResourceName, methodResourceName string) { + var sb strings.Builder + + sb.WriteString(prefix) + if getInterfaceGroupAndVersionEnabled() { + interfaceResourceName = getColonSeparatedKey(invoker.GetUrl()) + } else { + interfaceResourceName = invoker.GetUrl().Service() + } + sb.WriteString(interfaceResourceName) + sb.WriteString(":") + sb.WriteString(invocation.MethodName()) + sb.WriteString("(") + isFirst := true + for _, v := range invocation.ParameterTypes() { + if !isFirst { + sb.WriteString(",") + } + sb.WriteString(v.Name()) + isFirst = false + } + sb.WriteString(")") + methodResourceName = sb.String() + return +} + +func getConsumerPrefix() string { + return DefaultConsumerPrefix +} + +func getProviderPrefix() string { + return DefaultProviderPrefix +} + +func getInterfaceGroupAndVersionEnabled() bool { + return true +} + +func getColonSeparatedKey(url common.URL) string { + return fmt.Sprintf("%s:%s:%s", + url.Service(), + url.GetParam(constant.GROUP_KEY, ""), + url.GetParam(constant.VERSION_KEY, "")) +} diff --git a/filter/filter_impl/sentinel_filter_test.go b/filter/filter_impl/sentinel_filter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e8d481b40640bb09e9e60e922d4592603dea8f3a --- /dev/null +++ b/filter/filter_impl/sentinel_filter_test.go @@ -0,0 +1,127 @@ +/* + * 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" + "sync/atomic" + "testing" +) + +import ( + "github.com/alibaba/sentinel-golang/core/flow" + "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" +) + +func TestSentinelFilter_QPS(t *testing.T) { + url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + + "version=1.0.0&group=myGroup&" + + "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) + mockInvoker := protocol.NewBaseInvoker(url) + interfaceResourceName, _ := getResourceName(mockInvoker, + invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})), "prefix_") + mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})) + + _, err = flow.LoadRules([]*flow.Rule{ + { + Resource: interfaceResourceName, + MetricType: flow.QPS, + TokenCalculateStrategy: flow.Direct, + ControlBehavior: flow.Reject, + Count: 100, + RelationStrategy: flow.CurrentResource, + }, + }) + assert.NoError(t, err) + + wg := sync.WaitGroup{} + wg.Add(10) + f := GetSentinelProviderFilter() + pass := int64(0) + block := int64(0) + for i := 0; i < 10; i++ { + go func() { + for j := 0; j < 30; j++ { + result := f.Invoke(context.TODO(), mockInvoker, mockInvocation) + if result.Error() == nil { + atomic.AddInt64(&pass, 1) + } else { + atomic.AddInt64(&block, 1) + } + } + wg.Done() + }() + } + wg.Wait() + assert.True(t, atomic.LoadInt64(&pass) == 100) + assert.True(t, atomic.LoadInt64(&block) == 200) +} + +func TestConsumerFilter_Invoke(t *testing.T) { + f := GetSentinelConsumerFilter() + 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) + mockInvoker := protocol.NewBaseInvoker(url) + mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})) + result := f.Invoke(context.TODO(), mockInvoker, mockInvocation) + assert.NoError(t, result.Error()) +} + +func TestProviderFilter_Invoke(t *testing.T) { + f := GetSentinelProviderFilter() + 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) + mockInvoker := protocol.NewBaseInvoker(url) + mockInvocation := invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})) + result := f.Invoke(context.TODO(), mockInvoker, mockInvocation) + assert.NoError(t, result.Error()) +} + +func TestGetResourceName(t *testing.T) { + url, err := common.NewURL("dubbo://127.0.0.1:20000/UserProvider?anyhost=true&" + + "version=1.0.0&group=myGroup&" + + "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) + mockInvoker := protocol.NewBaseInvoker(url) + interfaceResourceName, methodResourceName := getResourceName(mockInvoker, + invocation.NewRPCInvocation("hello", []interface{}{"OK"}, make(map[string]interface{})), "prefix_") + assert.Equal(t, "com.ikurento.user.UserProvider:myGroup:1.0.0", interfaceResourceName) + assert.Equal(t, "prefix_com.ikurento.user.UserProvider:myGroup:1.0.0:hello()", methodResourceName) +} diff --git a/filter/filter_impl/token_filter.go b/filter/filter_impl/token_filter.go index fe4e38747ed10a40950c87747220339d0d566781..b5e05605c29905d9e66a63129f064c2f844f0e71 100644 --- a/filter/filter_impl/token_filter.go +++ b/filter/filter_impl/token_filter.go @@ -51,7 +51,7 @@ func (tf *TokenFilter) Invoke(ctx context.Context, invoker protocol.Invoker, inv if len(invokerTkn) > 0 { attachs := invocation.Attachments() remoteTkn, exist := attachs[constant.TOKEN_KEY] - if exist && strings.EqualFold(invokerTkn, remoteTkn) { + if exist && remoteTkn != nil && strings.EqualFold(invokerTkn, remoteTkn.(string)) { return invoker.Invoke(ctx, invocation) } return &protocol.RPCResult{Err: perrors.Errorf("Invalid token! Forbid invoke remote service %v method %s ", diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go index c2f69bd03941b1404585dc5842c56eb2bf3c918f..cd1bba3d4a830822c67f1e6157653d5477264c94 100644 --- a/filter/filter_impl/token_filter_test.go +++ b/filter/filter_impl/token_filter_test.go @@ -40,7 +40,7 @@ func TestTokenFilterInvoke(t *testing.T) { url := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) attch[constant.TOKEN_KEY] = "ori_key" result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*url), @@ -54,7 +54,7 @@ func TestTokenFilterInvokeEmptyToken(t *testing.T) { filter := GetTokenFilter() testUrl := common.URL{} - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) attch[constant.TOKEN_KEY] = "ori_key" result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) @@ -67,7 +67,7 @@ func TestTokenFilterInvokeEmptyAttach(t *testing.T) { testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } @@ -78,7 +78,7 @@ func TestTokenFilterInvokeNotEqual(t *testing.T) { testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) attch[constant.TOKEN_KEY] = "err_key" result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) diff --git a/filter/filter_impl/tps/tps_limiter_method_service_test.go b/filter/filter_impl/tps/tps_limiter_method_service_test.go index edae99ec2d3157ad7f0d81c95a2fb181410475fa..61f28e442f4b76d18d7750aa58831c322f939207 100644 --- a/filter/filter_impl/tps/tps_limiter_method_service_test.go +++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go @@ -36,7 +36,7 @@ import ( func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) { methodName := "hello" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -63,7 +63,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) { func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) // ctrl := gomock.NewController(t) // defer ctrl.Finish() @@ -80,7 +80,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) { func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) { methodName := "hello2" methodConfigPrefix := "methods." + methodName + "." - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -113,7 +113,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T) { methodName := "hello3" methodConfigPrefix := "methods." + methodName + "." - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]string, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/filter/filter_impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go index 274e4e6de61b94079e9ad3b2f7a5bcd79a276cc6..da0fc482ce559dad52947786823f604128857c30 100644 --- a/filter/filter_impl/tps_limit_filter_test.go +++ b/filter/filter_impl/tps_limit_filter_test.go @@ -44,7 +44,7 @@ func TestTpsLimitFilterInvokeWithNoTpsLimiter(t *testing.T) { invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TPS_LIMITER_KEY, "")) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) result := tpsFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), @@ -68,7 +68,7 @@ func TestGenericFilterInvokeWithDefaultTpsLimiter(t *testing.T) { invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY)) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) result := tpsFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), @@ -99,7 +99,7 @@ func TestGenericFilterInvokeWithDefaultTpsLimiterNotAllow(t *testing.T) { invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TPS_LIMITER_KEY, constant.DEFAULT_KEY)) - attch := make(map[string]string, 0) + attch := make(map[string]interface{}, 0) result := tpsFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go index 57f4095d49be784d7688d2acf17c1ea0225d0000..e159b7400d46069018a00a849319423285072dc2 100644 --- a/filter/filter_impl/tracing_filter_test.go +++ b/filter/filter_impl/tracing_filter_test.go @@ -42,7 +42,7 @@ func TestTracingFilterInvoke(t *testing.T) { "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) + attach := make(map[string]interface{}, 10) inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) ctx := context.Background() tf := newTracingFilter() diff --git a/go.mod b/go.mod index 3b622d924800a62c1774965ef1d14ce4928796f6..2ac1f85404f54ccd589d4b4ffb59a4c2bf724e0c 100644 --- a/go.mod +++ b/go.mod @@ -1,41 +1,32 @@ module github.com/apache/dubbo-go require ( - github.com/Microsoft/go-winio v0.4.13 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/Workiva/go-datastructures v1.0.50 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 + github.com/alibaba/sentinel-golang v0.6.1 github.com/apache/dubbo-getty v1.3.10 - github.com/apache/dubbo-go-hessian2 v1.6.2 - github.com/coreos/bbolt v1.3.3 // indirect - github.com/coreos/etcd v3.3.13+incompatible - github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect + github.com/apache/dubbo-go-hessian2 v1.7.0 + github.com/coreos/etcd v3.3.25+incompatible github.com/creasty/defaults v1.3.0 - github.com/docker/go-connections v0.4.0 // indirect github.com/dubbogo/go-zookeeper v1.0.1 github.com/dubbogo/gost v1.9.1 github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect github.com/emicklei/go-restful/v3 v3.0.0 github.com/frankban/quicktest v1.4.1 // indirect - github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 // indirect + github.com/fsnotify/fsnotify v1.4.7 github.com/go-co-op/gocron v0.1.1 github.com/go-resty/resty/v2 v2.1.0 - 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/go-cmp v0.3.1 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.9.5 // indirect + github.com/golang/protobuf v1.4.0 github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 github.com/hashicorp/consul v1.8.0 github.com/hashicorp/consul/api v1.5.0 github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a // indirect - github.com/hashicorp/golang-lru v0.5.3 // indirect github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff // indirect github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2 github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 github.com/magiconair/properties v1.8.1 - github.com/mitchellh/hashstructure v1.0.0 // indirect github.com/mitchellh/mapstructure v1.2.3 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd github.com/nacos-group/nacos-sdk-go v1.0.0 @@ -44,14 +35,12 @@ require ( github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.1.0 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b - github.com/shirou/gopsutil v2.19.9+incompatible // indirect github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.5.1 github.com/zouyx/agollo/v3 v3.4.4 - go.etcd.io/bbolt v1.3.4 // indirect go.uber.org/atomic v1.6.0 go.uber.org/zap v1.15.0 - google.golang.org/grpc v1.23.0 + google.golang.org/grpc v1.26.0 gopkg.in/yaml.v2 v2.2.8 k8s.io/api v0.16.9 k8s.io/apimachinery v0.16.9 @@ -59,6 +48,9 @@ require ( k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect ) -go 1.13 +replace ( + github.com/envoyproxy/go-control-plane => github.com/envoyproxy/go-control-plane v0.8.0 + launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a +) -replace launchpad.net/gocheck => github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a +go 1.13 diff --git a/go.sum b/go.sum index 91cdb0da19ca1acd9d35b630a65a9d8596382910..fa3b0d86536767276ebce843a80add00d1929aca 100644 --- a/go.sum +++ b/go.sum @@ -6,25 +6,36 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0 h1:0E3eE8MX426vUOs7aHfI7aN1BrIzzzf4ccKCSfSjGmc= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v32.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v40.3.0+incompatible h1:NthZg3psrLxvQLN6rVm07pZ9mv2wvGNaBNGQ3fnPvLE= github.com/Azure/azure-sdk-for-go v40.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-autorest/autorest v0.1.0/go.mod h1:AKyIcETwSUFxIcs/Wnq/C+kwCtlEYGUVd7FPNb2slmg= +github.com/Azure/go-autorest/autorest v0.5.0/go.mod h1:9HLKlQjVBH6U3oDfsXOeVc56THsLPw1L03yban4xThw= github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= github.com/Azure/go-autorest/autorest v0.9.3/go.mod h1:GsRuLYvwzLjjjRoWEIyMUaYq8GNUx2nRB378IPt/1p0= github.com/Azure/go-autorest/autorest v0.10.0 h1:mvdtztBqcL8se7MdrUweNieTNi4kfNG6GOJuurQJpuY= github.com/Azure/go-autorest/autorest v0.10.0/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= +github.com/Azure/go-autorest/autorest/adal v0.1.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= +github.com/Azure/go-autorest/autorest/adal v0.2.0/go.mod h1:MeS4XhScH55IST095THyTxElntu7WqB7pNbZo8Q5G3E= github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc= github.com/Azure/go-autorest/autorest/adal v0.8.1/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.8.2 h1:O1X4oexUxnZCaEUGsvMnr8ZGj8HI37tNezwY4npRqA0= github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= +github.com/Azure/go-autorest/autorest/azure/auth v0.1.0/go.mod h1:Gf7/i2FUpyb/sGBLIFxTBzrNzBo7aPXXE3ZVeDRwdpM= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk= github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM= +github.com/Azure/go-autorest/autorest/azure/cli v0.1.0/go.mod h1:Dk8CUAt/b/PzkfeRsWzVG9Yj3ps8mS8ECztu43rdU8U= github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U= github.com/Azure/go-autorest/autorest/azure/cli v0.3.1/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= @@ -34,12 +45,15 @@ github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxB github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= github.com/Azure/go-autorest/autorest/mocks v0.3.0 h1:qJumjCaCudz+OcqE9/XtEPfvtOjOmKaui4EOpFI6zZc= github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= +github.com/Azure/go-autorest/autorest/to v0.2.0/go.mod h1:GunWKJp1AEqgMaGLV+iocmRAJWqST1wQYhyyjXJ3SJc= github.com/Azure/go-autorest/autorest/to v0.3.0 h1:zebkZaadz7+wIQYgC7GXaz3Wb28yKYfVkkBKwc38VF8= github.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA= +github.com/Azure/go-autorest/autorest/validation v0.1.0/go.mod h1:Ha3z/SqBeaalWQvokg3NZAlQTalVMtOIAs1aGK7G6u8= github.com/Azure/go-autorest/autorest/validation v0.2.0 h1:15vMO4y76dehZSq7pAaOLQxC6dZYsSrj2GQpflyM/L4= github.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= github.com/Azure/go-autorest/logger v0.1.0 h1:ruG4BSDXONFRrZZJ2GUXDiUyVpayPmb1GnWeHDdaNKY= github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= +github.com/Azure/go-autorest/tracing v0.1.0/go.mod h1:ROEEAFwXycQw7Sn3DXNtEedEvdeRAgDr0izn4z5Ij88= github.com/Azure/go-autorest/tracing v0.5.0 h1:TRn4WjSnkcSy5AEG3pnbtFSwNtwzjr4VYyQflFE619k= github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= @@ -49,31 +63,46 @@ github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3 github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Microsoft/go-winio v0.4.13 h1:Hmi80lzZuI/CaYmlJp/b+FjZdRZhKu9c2mDVqKlLWVs= -github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/hcsshim v0.8.7-0.20191101173118-65519b62243c/go.mod h1:7xhjOwRV2+0HXGmM0jxaEu+ZiXJFoVZOTfL/dmqbrD8= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.0.1/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/Workiva/go-datastructures v1.0.50 h1:slDmfW6KCHcC7U+LP3DDBbm4fqTwZGn1beOFPfGaLvo= github.com/Workiva/go-datastructures v1.0.50/go.mod h1:Z+F2Rca0qCsVYDS8z7bAGm8f3UkzuWYS/oBZz5a7VVA= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af h1:DBNMBMuMiWYu0b+8KMJuWmfCkcxl09JwdlqwDZZ6U14= github.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbdr+Eyxvry3ilGPewKoXw3XGN1k= +github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE= +github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alibaba/sentinel-golang v0.6.1 h1:Pxyw2X7ryklvToF40KG9l4uuO90jRZA2MWb8Z3d1wPo= +github.com/alibaba/sentinel-golang v0.6.1/go.mod h1:5jemKdyCQCKVf+quEia53fo9a17OSe+wnl9HX2NbNpc= +github.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190808125512-07798873deee/go.mod h1:myCDvQSzCW+wB1WAlocEru4wMGJxy+vlxHdhegi1CDQ= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18 h1:zOVTBdCKFd9JbCKz9/nt+FovbjPFmb7mUnp8nH9fQBA= github.com/aliyun/alibaba-cloud-sdk-go v1.61.18/go.mod h1:v8ESoHo4SyHmuB4b1tJqDHxfTGEciD+yhvOU/5s1Rfk= +github.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190307165228-86c17b95fcd5/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= github.com/apache/dubbo-getty v1.3.10 h1:ys5mwjPdxG/KwkPjS6EI0RzQtU6p6FCPoKpaFEzpAL0= github.com/apache/dubbo-getty v1.3.10/go.mod h1:x6rraK01BL5C7jUM2fPl5KMkAxLVIx54ZB8/XEOik9Y= -github.com/apache/dubbo-go-hessian2 v1.6.2 h1:i7F5GjVaUatLQz1x9vUmmSIFj49L8J6rVICdF6xw4qw= -github.com/apache/dubbo-go-hessian2 v1.6.2/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/dubbo-go-hessian2 v1.7.0 h1:u2XxIuepu/zb6JcGZc7EbvKboXdKoJbf7rbmeq6SF1w= +github.com/apache/dubbo-go-hessian2 v1.7.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= 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/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= @@ -84,26 +113,47 @@ github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/aws/aws-sdk-go v1.23.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.25.41 h1:/hj7nZ0586wFqpwjNpzWiUTwtaMgxAZNZKHay80MdXw= github.com/aws/aws-sdk-go v1.25.41/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI= github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bwmarrin/discordgo v0.20.2/go.mod h1:O9S4p+ofTFwB02em7jkpkV8M3R0/PUVOwN61zSZ0r4Q= +github.com/caddyserver/certmagic v0.10.6/go.mod h1:Y8jcUBctgk/IhpAzlHKfimZNyXCkfGgRTC0orl8gROQ= +github.com/cenkalti/backoff/v4 v4.0.0/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= +github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible h1:C29Ae4G5GtYyYMm1aztcyj/J5ckgJm2zwdDajFbx1NY= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3 h1:TJH+oke8D16535+jHExHj4nQvzlZrj7ug5D7I/orNUA= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/cloudflare-go v0.10.2/go.mod h1:qhVI5MKwBGhdNU89ZRz2plgYutcJ5PCekLxXn56w6SY= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= github.com/coredns/coredns v1.1.2 h1:bAFHrSsBeTeRG5W3Nf2su3lUGw7Npw2UKeCJm/3A638= github.com/coredns/coredns v1.1.2/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -112,6 +162,9 @@ github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkE github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.18+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.25+incompatible h1:0GQEw6h3YnuOVdtwygkIfJ+Omx0tZ8/QkVyXI4LkbeY= +github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= @@ -123,7 +176,11 @@ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+ github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpu/goacmedns v0.0.1/go.mod h1:sesf/pNnCYwUevQEQfEwY0Y3DydlQWSGZbaMElOWxok= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creasty/defaults v1.3.0 h1:uG+RAxYbJgOPCOdKEcec9ZJXeva7Y6mj/8egdzwmLtw= github.com/creasty/defaults v1.3.0/go.mod h1:CIEEvs7oIVZm30R8VxtFJs+4k201gReYyuYHJxZc68I= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -140,19 +197,28 @@ github.com/digitalocean/godo v1.10.0 h1:uW1/FcvZE/hoixnJcnlmIUvTVNdZCLjRLzmDtRi1 github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/dnaeon/go-vcr v0.0.0-20180814043457-aafff18a5cc2/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/dnsimple/dnsimple-go v0.30.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v1.4.2-0.20191101170500-ac7306503d23/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dubbogo/go-zookeeper v1.0.1 h1:irLzvOsDOTNsN8Sv9tvYYxVu6DCQfLtziZQtUHmZgz8= github.com/dubbogo/go-zookeeper v1.0.1/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= -github.com/dubbogo/gost v1.9.0 h1:UT+dWwvLyJiDotxJERO75jB3Yxgsdy10KztR5ycxRAk= github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= github.com/dubbogo/gost v1.9.1 h1:0/PPFo13zPbjt4Ia0zYWMFi3C6rAe9X7O1J2Iv+BHNM= github.com/dubbogo/gost v1.9.1/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= +github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/ef-ds/deque v1.0.4-0.20190904040645-54cb57c252a1/go.mod h1:HvODWzv6Y6kBf3Ah2WzN1bHjDUezGLaAhwuWVwfpEJs= github.com/elazarl/go-bindata-assetfs v0.0.0-20160803192304-e1a2a7ec64b0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= @@ -161,33 +227,56 @@ github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1 github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emicklei/go-restful/v3 v3.0.0 h1:Duxxa4x0WIHW3bYEDmoAPNjmy8Rbqn+utcF74dlF/G8= github.com/emicklei/go-restful/v3 v3.0.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.8.0 h1:uE6Fp4fOcAJdc1wTQXLJ+SYistkbG1dNoi6Zs1+Ybvk= github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk= github.com/envoyproxy/protoc-gen-validate v0.0.14 h1:YBW6/cKy9prEGRYLnaGa4IDhzxZhRCtKsax8srGKDnM= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.2.0+incompatible h1:fUDGZCv/7iAN7u0puUVhvKCcsR6vRfwrJatElLBEf0I= github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.0.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/exoscale/egoscale v0.18.1/go.mod h1:Z7OOdzzTOz1Q1PjQXumlz9Wn/CddH0zSYdCF3rnBKXE= github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239 h1:Ghm4eQYC0nEPnSJdVkTrXpu9KtoVCSo1hg7mtI7G9KU= github.com/fastly/go-utils v0.0.0-20180712184237-d95a45783239/go.mod h1:Gdwt2ce0yfBxPvZrHkprdPPTTS3N5rwmLE8T22KBXlw= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/forestgiant/sliceutil v0.0.0-20160425183142-94783f95db6c/go.mod h1:pFdJbAhRf7rh6YYMUdIQGyzne6zYL1tCUW8QV2B3UfY= github.com/frankban/quicktest v1.4.1 h1:Wv2VwvNn73pAdFIVUQRXYDFp31lXKbqblIXo/Q5GPSg= github.com/frankban/quicktest v1.4.1/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsouza/go-dockerclient v1.6.0/go.mod h1:YWwtNPuL4XTX1SKJQk86cWPmmqwx+4np9qfPbb+znGc= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 h1:Mn26/9ZMNWSw9C9ERFA1PUxfmGpolnw2v0bKOREu5ew= github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32/go.mod h1:GIjDIg/heH5DOkXY3YJ/wNhfHsQHoXGjl8G8amsYQ1I= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2 h1:Ujru1hufTHVb++eG6OuNDKMxZnGIvF6o/u8q/8h2+I4= +github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy3PbkQ1AERPfmLMMagS60DKF78eWwLn8= +github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-acme/lego/v3 v3.4.0/go.mod h1:xYbLDuxq3Hy4bMUT1t9JIuz6GWIWb3m5X+TeTHYaT7M= github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-check/check v0.0.0-20140225173054-eb6ee6f84d0a/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= +github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-co-op/gocron v0.1.1 h1:OfDmkqkCguFtFMsm6Eaayci3DADLa8pXvdmOlPU/JcU= github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw= +github.com/go-git/go-git/v5 v5.1.0/go.mod h1:ZKfuPUoY1ZqIG4QG9BDBh3G4gLM5zvPuSJAozQrZuyM= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.44.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= github.com/go-ldap/ldap/v3 v3.1.3/go.mod h1:3rbOH3jRS2u6jg2rJnKAMLE/xQyCKIveG2Sa/Cohzb8= @@ -196,39 +285,61 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-resty/resty/v2 v2.1.0 h1:Z6IefCpUMfnvItVJaJXWv/pMiiD11So35QgwEELsldE= github.com/go-resty/resty/v2 v2.1.0/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.3/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0 h1:kFkMAZBNAn4j7K0GiZr8cRYzejq68VbheufiV3YuyFI= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1 h1:qGJ6qTW+x6xX/my+8YUVl4WNpX9B7+/l2tRsHGZ7f2s= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -238,6 +349,8 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= @@ -250,6 +363,7 @@ github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OI github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2 h1:AtvtonGEH/fZK0XPNNBdB6swgy7Iudfx88wzyIpwqJ8= github.com/google/tcpproxy v0.0.0-20180808230851-dfa16c61dad2/go.mod h1:DavVbd41y+b7ukKDmlnPR4nGYmkWXR6vHUkjQNiHPBs= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= @@ -259,30 +373,43 @@ github.com/googleapis/gnostic v0.2.0 h1:l6N3VoaVzTncYYW+9yOz2LJJammFZGBO13sqgEhp github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/391+sT5o= github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gophercloud/gophercloud v0.3.0 h1:6sjpKIpVwRIIwmcEGp+WwNovNsem+c+2vm6oxshRpL8= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= +github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1QAp/SlnNrZhI= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul v1.8.0 h1:yRKMKZyPLqUxl37t4nFt5OuGmTXoFhTJrakhfnYKCYA= github.com/hashicorp/consul v1.8.0/go.mod h1:Gg9/UgAQ9rdY3CTvzQZ6g2jcIb7NlIfjI+0pvLk5D1A= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/api v1.4.0/go.mod h1:xc8u05kyMa3Wjr9eEAsIAo3dg8+LywT5E/Cl7cNS5nU= github.com/hashicorp/consul/api v1.5.0 h1:Yo2bneoGy68A7aNwmuETFnPhjyBEm7n3vzRacEVMjvI= github.com/hashicorp/consul/api v1.5.0/go.mod h1:LqwrLNW876eYSuUOo4ZLHBcdKc038txr/IMfbLPATa4= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/consul/sdk v0.4.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= github.com/hashicorp/consul/sdk v0.5.0 h1:WC4594Wp/LkEeML/OdQKEC1yqBmEYkRp6i7X5u0zDAs= github.com/hashicorp/consul/sdk v0.5.0/go.mod h1:fY08Y9z5SvJqevyZNy6WWPXiG3KwBPAvlcdx16zZ0fM= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-bexpr v0.1.2 h1:ijMXI4qERbzxbCnkxmfUtwMyjrrk3y+Vt0MxojNCbBs= @@ -309,6 +436,7 @@ github.com/hashicorp/go-memdb v1.0.3/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVz github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= @@ -380,16 +508,21 @@ github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg= +github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= github.com/jackc/pgx v3.3.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk= github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= +github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8 h1:mGIXW/lubQ4B+3bXTLxcTMTjUNDqoF6T/HUW9LbFx9s= github.com/jinzhu/copier v0.0.0-20190625015134-976e0346caa8/go.mod h1:yL958EeXv8Ylng6IfnvG4oflryUi3vgA3xPs9hmII1s= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= @@ -404,13 +537,18 @@ github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/kolo/xmlrpc v0.0.0-20190717152603-07c4ee3fd181/go.mod h1:o03bZfuBwAXHetKXuInt4S7omeXUu62/A845kiycsSQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -421,6 +559,13 @@ github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/labbsr0x/bindman-dns-webhook v1.0.2/go.mod h1:p6b+VCXIR8NYKpDr8/dg1HKfQoRHCdcsROXKvmoehKA= +github.com/labbsr0x/goh v1.0.1/go.mod h1:8K2UhVoaWXcCU7Lxoa2omWnC8gyW8px7/lmO61c027w= +github.com/labstack/echo/v4 v4.1.15/go.mod h1:GWO5IBVzI371K8XJe50CSvHjQCafK6cw8R/moLhEU6o= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570 h1:0iQektZGS248WXmGIYOwRXSQhD4qn3icjMpuxwO7qlo= github.com/lestrrat/go-envload v0.0.0-20180220120943-6ed08b54a570/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f h1:sgUSP4zdTUZYZgAGGtN5Lxk92rK+JUFOwf+FT99EEI4= @@ -428,28 +573,46 @@ github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5jFF4BHGAEDSqwPW1NJS3XshxbRCxtjFAZc= github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/linode/linodego v0.7.1 h1:4WZmMpSA2NRwlPZcc0+4Gyn7rr99Evk9bnr0B3gXRKE= github.com/linode/linodego v0.7.1/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= +github.com/linode/linodego v0.10.0 h1:AMdb82HVgY8o3mjBXJcUv9B+fnJjfDMn2rNRGbX+jvM= +github.com/linode/linodego v0.10.0/go.mod h1:cziNP7pbvE3mXIPneHj0oRY8L1WtGEIKlZ8LANE4eXA= +github.com/liquidweb/liquidweb-go v1.6.0/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVLEIG/i5J9cyixzQ= +github.com/lucas-clemente/quic-go v0.14.1/go.mod h1:Vn3/Fb0/77b02SGhQk36KzOUmXgVpFfizUfW5WMaqyU= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/chacha20 v0.2.0/go.mod h1:HSdjFau7GzYRj+ahFNwsO3ouVJr1HFkWoEwNDb4TMtE= +github.com/marten-seemann/qpack v0.1.0/go.mod h1:LFt1NU/Ptjip0C2CPkhimBz5CGE3WGDAUWqna+CNTrI= +github.com/marten-seemann/qtls v0.4.1/go.mod h1:pxVXcHHw1pNIt8Qo0pwSYQEoZ8yYOOPXTCZLQQunvRc= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/micro/cli/v2 v2.1.2/go.mod h1:EguNh6DAoWKm9nmk+k/Rg0H3lQnDxqzu5x5srOtGtYg= +github.com/micro/go-micro/v2 v2.9.1/go.mod h1:x55ZM3Puy0FyvvkR3e0ha0xsE9DFwfPSUMWAIbFY0SY= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/miekg/dns v1.1.15/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26 h1:gPxPSwALAeHJSjarOs00QjVdV9QoBvc1D2ujQUr5BzU= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.27 h1:aEH/kqUzUxGJ/UHcEKdJY+ugH6WEzsEBBSPa8zuy1aM= +github.com/miekg/dns v1.1.27/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.1.0 h1:tEElEatulEHDeedTxwckzyYMA5c86fbmNIUL1hBIiTg= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= @@ -462,6 +625,7 @@ github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go. github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= @@ -484,27 +648,57 @@ github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lN github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/nacos-group/nacos-sdk-go v1.0.0 h1:CufUF7DZca2ZzIrJtMMCDih1sA58BWCglArLMCZArUc= github.com/nacos-group/nacos-sdk-go v1.0.0/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA= +github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.6/go.mod h1:BL1NOtaBQ5/y97djERRVWNouMW7GT3gxnmbE/eC8u8A= +github.com/nats-io/nats.go v1.9.2/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.4/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s= github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nlopes/slack v0.6.1-0.20191106133607-d06c2a2b3249/go.mod h1:JzQ9m3PMAqcpeCam7UaHSuBuupz7CmpjehYMayT6YOk= +github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= +github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= +github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= +github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20180130162743-b8a9be070da4/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= 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/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/oracle/oci-go-sdk v7.0.0+incompatible/go.mod h1:VQb79nF8Z2cwLkLS35ukwStZIg5F66tcBccjip/j888= +github.com/ovh/go-ovh v0.0.0-20181109152953-ba5adb4cf014/go.mod h1:joRatxRJaZBsY3JAOEMcoOp05CnZzsx4scTxi95DHyQ= +github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c h1:vwpFWvAO8DeIZfFeqASzZfsxuWPno9ncAebBEP0N3uE= github.com/packethost/packngo v0.1.1-0.20180711074735-b9cb5096f54c/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -532,37 +726,47 @@ github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAm github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA= github.com/rboyer/safeio v0.2.1/go.mod h1:Cq/cEPK+YXFn622lsQ0K4KsPZSPtaptHHEldsy7Fmig= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03 h1:Wdi9nwnhFNAlseAOekn6B5G/+GMtks9UKbvRU/CMM/o= github.com/renier/xmlrpc v0.0.0-20170708154548-ce4a1a486c03/go.mod h1:gRAiPF5C5Nd0eyyRdqIu9qTiFSoZzpTq727b5B8fkkU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sacloud/libsacloud v1.26.1/go.mod h1:79ZwATmHLIFZIMd7sxA3LwzVy/B77uj3LDoToVTxDoQ= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b h1:gQZ0qzfKHQIybLANtM3mBXNUtOfsCFXeTsnBqCsx1KM= github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= @@ -570,17 +774,20 @@ github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06q github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/shirou/gopsutil v2.19.9+incompatible h1:IrPVlK4nfwW10DF7pW+7YJKws9NkgNzWozwwWv9FsgY= -github.com/shirou/gopsutil v2.19.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/gopsutil v2.19.12+incompatible h1:WRstheAymn1WOPesh+24+bZKFkqrdCR8JOc77v4xV3Q= +github.com/shirou/gopsutil v2.19.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/skratchdot/open-golang v0.0.0-20160302144031-75fb7ed4208c/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= @@ -620,62 +827,104 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto= github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= +github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible h1:8uRvJleFpqLsO77WaAh2UrasMOzd8MxXrNj20e7El+Q= github.com/tencentcloud/tencentcloud-sdk-go v3.0.83+incompatible/go.mod h1:0PfYow01SHPMhKY31xa+EFz2RStxIqj6JFAJS+IkCi4= 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/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY= 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= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosFVTJwnltaHbSNC3i82I92quFs+OFPRl8kNMVwo= +github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 h1:kF/7m/ZU+0D4Jj5eZ41Zm3IH/J8OElK1Qtd7tVKAwLk= github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3/go.mod h1:QDlpd3qS71vYtakd2hmdpqhJ9nwv6mD6A30bQ1BPBFE= +github.com/transip/gotransip v0.0.0-20190812104329-6d8d9179b66f/go.mod h1:i0f4R4o2HM0m3DZYQWsj6/MEowD57VzoH0v3d7igeFY= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926 h1:G3dpKMzFDjgEh2q1Z7zUUtKa8ViPtH+ocF0bE0g00O8= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/vmware/govmomi v0.18.0 h1:f7QxSmP7meCtoAmiKZogvVbLInT+CZx6Px6K5rYsJZo= github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU= +github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA= +github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xeipuuv/gojsonschema v1.1.0/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/zouyx/agollo/v3 v3.4.4 h1:5G7QNw3fw74Ns8SfnHNhjndV2mlz5Fg8bB7q84ydFYI= github.com/zouyx/agollo/v3 v3.4.4/go.mod h1:ag0XmE1r4iAgPd6PUnU9TJ0DMEjM1VKX1HUNqQJ2ywU= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg= go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= +go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk= go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4= go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.15.0 h1:ZZCA22JRF2gQE5FoNmhmrf7jeJJ2uhqDUNRYKm8dvmM= go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc= +golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190418165655-df01cb2cc480/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo= golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37 h1:cg5LA/zNPRzIXIWSCxQW10Rvpy94aQh3LT/ShoCpkHw= +golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -686,11 +935,17 @@ golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHl golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180611182652-db08ff08e862/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -699,7 +954,9 @@ golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -709,10 +966,16 @@ golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190930134127-c5a3c61f89f3/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2 h1:eDrdRpKgkcCqKZQwyZRyeFZgfqt37SL7Kv3tok06cKE= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= @@ -725,6 +988,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -732,10 +996,12 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -744,12 +1010,16 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190508220229-2d0786266e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -759,6 +1029,10 @@ golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -768,7 +1042,11 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -789,39 +1067,51 @@ golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b h1:zSzQJAznWxAh9fZxiPy2FZo+ZZEYoYFYYDYdOrU7AaM= +golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0 h1:9sdfJOzWlkqPltHAuzT2Cp+yrBeY1KRVYgms8soxMwM= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0 h1:uMf5uLi4eQMRrMKhCplNik4U4H8Z6C1br3zOtAa/aDE= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw= google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1 h1:aQktFqmDE2yjveXJlVIfslDFmFnUXSqG0i6KRcJAeMc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -830,6 +1120,15 @@ google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0 h1:cJv5/xdbk1NnMPR1VP9+HU6gupuG9MLBoH1r6RHZ2MY= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= @@ -837,29 +1136,40 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ns1/ns1-go.v2 v2.0.0-20190730140822-b51389932cbc/go.mod h1:VV+3haRsgDiVLxyifmMBrBIuCWFBPYKbRssXB9z67Hw= +gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc= gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y= gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/telegram-bot-api.v4 v4.6.4/go.mod h1:5DpGO5dbumb40px+dXcwCpcjmeHNYLpk0bp3XRNvWDM= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -881,6 +1191,7 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= k8s.io/utils v0.0.0-20190801114015-581e00157fb1 h1:+ySTxfHnfzZb9ys375PXNlLhkJPLKgHajBU0N62BDvE= k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/integrate_test.sh b/integrate_test.sh index c9c2f23b5b07f0baf96260d8092e7464d4d15659..deccda756a211821978e35b92a1f0865858ff59a 100644 --- a/integrate_test.sh +++ b/integrate_test.sh @@ -63,4 +63,4 @@ docker build . -t ci-consumer --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_ cd ${ROOT_DIR} # run provider # check consumer status -docker run -it --network host ci-consumer +docker run -i --network host ci-consumer diff --git a/metadata/service/exporter/configurable/exporter.go b/metadata/service/exporter/configurable/exporter.go index f8b4b0c0174cb0e5a8753b814f89ed4d332e2fbe..5a930c5e953aead15e4673f1b9537197128a7b35 100644 --- a/metadata/service/exporter/configurable/exporter.go +++ b/metadata/service/exporter/configurable/exporter.go @@ -48,7 +48,6 @@ func NewMetadataServiceExporter(metadataService service.MetadataService) exporte // Export will export the metadataService func (exporter *MetadataServiceExporter) Export() error { if !exporter.IsExported() { - serviceConfig := config.NewServiceConfig(constant.SIMPLE_METADATA_SERVICE_NAME, context.Background()) serviceConfig.Protocol = constant.DEFAULT_PROTOCOL serviceConfig.Protocols = map[string]*config.ProtocolConfig{ diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go index 9fdbd76757815af0aa975ec6e5f1b20fa5f1a83e..b304b9153f92f1a7ab8176236fd9648e09f4366b 100644 --- a/metadata/service/exporter/configurable/exporter_test.go +++ b/metadata/service/exporter/configurable/exporter_test.go @@ -30,15 +30,15 @@ import ( "github.com/apache/dubbo-go/config" _ "github.com/apache/dubbo-go/filter/filter_impl" "github.com/apache/dubbo-go/metadata/service/inmemory" - "github.com/apache/dubbo-go/protocol/dubbo" _ "github.com/apache/dubbo-go/protocol/dubbo" + "github.com/apache/dubbo-go/remoting/getty" ) func TestConfigurableExporter(t *testing.T) { - dubbo.SetServerConfig(dubbo.ServerConfig{ + getty.SetServerConfig(getty.ServerConfig{ SessionNumber: 700, SessionTimeout: "20s", - GettySessionParam: dubbo.GettySessionParam{ + GettySessionParam: getty.GettySessionParam{ CompressEncoding: false, TcpNoDelay: true, TcpKeepAlive: true, diff --git a/metadata/service/inmemory/service_proxy.go b/metadata/service/inmemory/service_proxy.go index 7e01439f042a2046559188ec9df6924da0236cb1..e2b29686f49aeade5c61a46f464e6bf00165e70c 100644 --- a/metadata/service/inmemory/service_proxy.go +++ b/metadata/service/inmemory/service_proxy.go @@ -55,7 +55,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName), invocation.WithArguments([]interface{}{siV.Interface(), gV.Interface(), vV.Interface(), pV.Interface()}), invocation.WithReply(reflect.ValueOf(&[]interface{}{}).Interface()), - invocation.WithAttachments(map[string]string{constant.ASYNC_KEY: "false"}), + invocation.WithAttachments(map[string]interface{}{constant.ASYNC_KEY: "false"}), invocation.WithParameterValues([]reflect.Value{siV, gV, vV, pV})) res := m.invkr.Invoke(context.Background(), inv) diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go index 0cb7d09a2c8e71fb88b54789c8eb3ee2cf967fbf..eaba0e324ff078bdfb2fd4b146ac9ea60d429724 100644 --- a/metrics/prometheus/reporter_test.go +++ b/metrics/prometheus/reporter_test.go @@ -43,7 +43,7 @@ func TestPrometheusReporter_Report(t *testing.T) { "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) + attach := make(map[string]interface{}, 10) inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) assert.False(t, isConsumer(url)) diff --git a/protocol/dubbo/client.go b/protocol/dubbo/client.go deleted file mode 100644 index 530beba3512ec09e353b632b1a4b75d75f8a5ae2..0000000000000000000000000000000000000000 --- a/protocol/dubbo/client.go +++ /dev/null @@ -1,364 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo - -import ( - "math/rand" - "strings" - "sync" - "time" -) - -import ( - "github.com/apache/dubbo-getty" - hessian "github.com/apache/dubbo-go-hessian2" - gxsync "github.com/dubbogo/gost/sync" - perrors "github.com/pkg/errors" - "go.uber.org/atomic" - "gopkg.in/yaml.v2" -) - -import ( - "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/common/logger" - "github.com/apache/dubbo-go/config" -) - -var ( - errInvalidCodecType = perrors.New("illegal CodecType") - errInvalidAddress = perrors.New("remote address invalid or empty") - errSessionNotExist = perrors.New("session not exist") - errClientClosed = perrors.New("client closed") - errClientReadTimeout = perrors.New("client read timeout") - - clientConf *ClientConfig - clientGrpool *gxsync.TaskPool -) - -func init() { - - // load clientconfig from consumer_config - // default use dubbo - consumerConfig := config.GetConsumerConfig() - if consumerConfig.ApplicationConfig == nil { - return - } - protocolConf := config.GetConsumerConfig().ProtocolConf - defaultClientConfig := GetDefaultClientConfig() - if protocolConf == nil { - logger.Info("protocol_conf default use dubbo config") - } else { - dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO] - if dubboConf == nil { - logger.Warnf("dubboConf is nil") - return - } - dubboConfByte, err := yaml.Marshal(dubboConf) - if err != nil { - panic(err) - } - err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig) - if err != nil { - panic(err) - } - } - clientConf = &defaultClientConfig - if err := clientConf.CheckValidity(); err != nil { - logger.Warnf("[CheckValidity] error: %v", err) - return - } - setClientGrpool() - - rand.Seed(time.Now().UnixNano()) -} - -// SetClientConf set dubbo client config. -func SetClientConf(c ClientConfig) { - clientConf = &c - err := clientConf.CheckValidity() - if err != nil { - logger.Warnf("[ClientConfig CheckValidity] error: %v", err) - return - } - setClientGrpool() -} - -// GetClientConf get dubbo client config. -func GetClientConf() ClientConfig { - return *clientConf -} - -func setClientGrpool() { - if clientConf.GrPoolSize > 1 { - clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen), - gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber)) - } -} - -// Options is option for create dubbo client -type Options struct { - // connect timeout - ConnectTimeout time.Duration - // request timeout - RequestTimeout time.Duration -} - -//AsyncCallbackResponse async response for dubbo -type AsyncCallbackResponse struct { - common.CallbackResponse - Opts Options - Cause error - Start time.Time // invoke(call) start time == write start time - ReadStart time.Time // read start time, write duration = ReadStart - Start - Reply interface{} -} - -// Client is dubbo protocol client. -type Client struct { - opts Options - conf ClientConfig - pool *gettyRPCClientPool - sequence atomic.Uint64 - - pendingResponses *sync.Map -} - -// NewClient create a new Client. -func NewClient(opt Options) *Client { - - switch { - case opt.ConnectTimeout == 0: - opt.ConnectTimeout = 3 * time.Second - fallthrough - case opt.RequestTimeout == 0: - opt.RequestTimeout = 3 * time.Second - } - - // make sure that client request sequence is an odd number - initSequence := uint64(rand.Int63n(time.Now().UnixNano())) - if initSequence%2 == 0 { - initSequence++ - } - - c := &Client{ - opts: opt, - pendingResponses: new(sync.Map), - conf: *clientConf, - } - c.sequence.Store(initSequence) - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - return c -} - -// Request is dubbo protocol request. -type Request struct { - addr string - svcUrl common.URL - method string - args interface{} - atta map[string]string -} - -// NewRequest create a new Request. -func NewRequest(addr string, svcUrl common.URL, method string, args interface{}, atta map[string]string) *Request { - return &Request{ - addr: addr, - svcUrl: svcUrl, - method: method, - args: args, - atta: atta, - } -} - -// Response is dubbo protocol response. -type Response struct { - reply interface{} - atta map[string]string -} - -// NewResponse creates a new Response. -func NewResponse(reply interface{}, atta map[string]string) *Response { - return &Response{ - reply: reply, - atta: atta, - } -} - -// CallOneway call by one way -func (c *Client) CallOneway(request *Request) error { - - return perrors.WithStack(c.call(CT_OneWay, request, NewResponse(nil, nil), nil)) -} - -// Call call remoting by two way or one way, if @response.reply is nil, the way of call is one way. -func (c *Client) Call(request *Request, response *Response) error { - ct := CT_TwoWay - if response.reply == nil { - ct = CT_OneWay - } - - return perrors.WithStack(c.call(ct, request, response, nil)) -} - -// AsyncCall call remoting by async with callback. -func (c *Client) AsyncCall(request *Request, callback common.AsyncCallback, response *Response) error { - return perrors.WithStack(c.call(CT_TwoWay, request, response, callback)) -} - -func (c *Client) call(ct CallType, request *Request, response *Response, callback common.AsyncCallback) error { - p := &DubboPackage{} - p.Service.Path = strings.TrimPrefix(request.svcUrl.Path, "/") - p.Service.Interface = request.svcUrl.GetParam(constant.INTERFACE_KEY, "") - p.Service.Version = request.svcUrl.GetParam(constant.VERSION_KEY, "") - p.Service.Group = request.svcUrl.GetParam(constant.GROUP_KEY, "") - p.Service.Method = request.method - c.pool.sslEnabled = request.svcUrl.GetParamBool(constant.SSL_ENABLED_KEY, false) - - 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) - - var rsp *PendingResponse - if ct != CT_OneWay { - p.Header.Type = hessian.PackageRequest_TwoWay - rsp = NewPendingResponse() - rsp.response = response - rsp.callback = callback - } else { - p.Header.Type = hessian.PackageRequest - } - - var ( - err error - session getty.Session - conn *gettyRPCClient - ) - conn, session, err = c.selectSession(request.addr) - if err != nil { - return perrors.WithStack(err) - } - if session == nil { - return errSessionNotExist - } - 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) - } - - if ct == CT_OneWay || callback != nil { - return nil - } - - select { - case <-getty.GetTimeWheel().After(c.opts.RequestTimeout): - c.removePendingResponse(SequenceType(rsp.seq)) - return perrors.WithStack(errClientReadTimeout) - case <-rsp.done: - err = rsp.err - } - - return perrors.WithStack(err) -} - -// Close close the client pool. -func (c *Client) Close() { - if c.pool != nil { - c.pool.close() - } - c.pool = nil -} - -func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { - rpcClient, err := c.pool.getGettyRpcClient(DUBBO, addr) - if err != nil { - return nil, nil, perrors.WithStack(err) - } - return rpcClient, rpcClient.selectSession(), nil -} - -func (c *Client) heartbeat(session getty.Session) error { - return c.transfer(session, nil, NewPendingResponse()) -} - -func (c *Client) transfer(session getty.Session, pkg *DubboPackage, - rsp *PendingResponse) error { - - var ( - sequence uint64 - err error - ) - - sequence = c.sequence.Add(1) - - if pkg == nil { - pkg = &DubboPackage{} - pkg.Body = hessian.NewRequest([]interface{}{}, nil) - pkg.Body = []interface{}{} - pkg.Header.Type = hessian.PackageHeartbeat - pkg.Header.SerialID = byte(S_Dubbo) - } - pkg.Header.ID = int64(sequence) - - // cond1 - if rsp != nil { - rsp.seq = sequence - c.addPendingResponse(rsp) - } - - err = session.WritePkg(pkg, c.opts.RequestTimeout) - if err != nil { - c.removePendingResponse(SequenceType(rsp.seq)) - } else if rsp != nil { // cond2 - // cond2 should not merged with cond1. cause the response package may be returned very - // soon and it will be handled by other goroutine. - rsp.readStart = time.Now() - } - - return perrors.WithStack(err) -} - -func (c *Client) addPendingResponse(pr *PendingResponse) { - c.pendingResponses.Store(SequenceType(pr.seq), pr) -} - -func (c *Client) removePendingResponse(seq SequenceType) *PendingResponse { - if c.pendingResponses == nil { - return nil - } - if presp, ok := c.pendingResponses.Load(seq); ok { - c.pendingResponses.Delete(seq) - return presp.(*PendingResponse) - } - return nil -} diff --git a/protocol/dubbo/client_test.go b/protocol/dubbo/client_test.go deleted file mode 100644 index 8b0ba169b82910652c64011c47568c7a018ae5e0..0000000000000000000000000000000000000000 --- a/protocol/dubbo/client_test.go +++ /dev/null @@ -1,305 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo - -import ( - "bytes" - "context" - "sync" - "testing" - "time" -) - -import ( - hessian "github.com/apache/dubbo-go-hessian2" - perrors "github.com/pkg/errors" - "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" -) - -const ( - mockMethodNameGetUser = "GetUser" - mockMethodNameGetBigPkg = "GetBigPkg" - mockAddress = "127.0.0.1:20000" -) - -func TestClientCallOneway(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 6e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - err := c.CallOneway(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil)) - assert.NoError(t, err) - - // destroy - proto.Destroy() -} - -func TestClientCall(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 10e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - var ( - user *User - err error - ) - - user = &User{} - err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetBigPkg, []interface{}{nil}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.NotEqual(t, "", user.Id) - assert.NotEqual(t, "", user.Name) - - user = &User{} - err = c.Call(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: "username"}, *user) - - user = &User{} - err = c.Call(NewRequest(mockAddress, url, "GetUser0", []interface{}{"1", nil, "username"}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: "username"}, *user) - - err = c.Call(NewRequest(mockAddress, url, "GetUser1", []interface{}{}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - - err = c.Call(NewRequest(mockAddress, url, "GetUser2", []interface{}{}, nil), NewResponse(user, nil)) - assert.EqualError(t, err, "error") - - user2 := []interface{}{} - err = c.Call(NewRequest(mockAddress, url, "GetUser3", []interface{}{}, nil), NewResponse(&user2, nil)) - assert.NoError(t, err) - assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) - - user2 = []interface{}{} - err = c.Call(NewRequest(mockAddress, url, "GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil), NewResponse(&user2, nil)) - assert.NoError(t, err) - assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) - - user3 := map[interface{}]interface{}{} - err = c.Call(NewRequest(mockAddress, url, "GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil), NewResponse(&user3, nil)) - assert.NoError(t, err) - assert.NotNil(t, user3) - assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"]) - - user = &User{} - err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{0}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "", Name: ""}, *user) - - user = &User{} - err = c.Call(NewRequest(mockAddress, url, "GetUser6", []interface{}{1}, nil), NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{Id: "1", Name: ""}, *user) - - // destroy - proto.Destroy() -} - -func TestClientAsyncCall(t *testing.T) { - proto, url := InitTest(t) - - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3e9, - RequestTimeout: 6e9, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) - - user := &User{} - lock := sync.Mutex{} - lock.Lock() - err := c.AsyncCall(NewRequest(mockAddress, url, mockMethodNameGetUser, []interface{}{"1", "username"}, nil), func(response common.CallbackResponse) { - r := response.(AsyncCallbackResponse) - assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) - lock.Unlock() - }, NewResponse(user, nil)) - assert.NoError(t, err) - assert.Equal(t, User{}, *user) - - // destroy - lock.Lock() - proto.Destroy() - lock.Unlock() -} - -func InitTest(t *testing.T) (protocol.Protocol, common.URL) { - - hessian.RegisterPOJO(&User{}) - - methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "dubbo", &UserProvider{}) - assert.NoError(t, err) - assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) - - // config - SetClientConf(ClientConfig{ - ConnectionNum: 2, - HeartbeatPeriod: "5s", - SessionTimeout: "20s", - PoolTTL: 600, - PoolSize: 64, - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "120s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "4s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 10240000000, - SessionName: "client", - }, - }) - assert.NoError(t, clientConf.CheckValidity()) - SetServerConfig(ServerConfig{ - SessionNumber: 700, - SessionTimeout: "20s", - GettySessionParam: GettySessionParam{ - CompressEncoding: false, - TcpNoDelay: true, - TcpKeepAlive: true, - KeepAlivePeriod: "120s", - TcpRBufSize: 262144, - TcpWBufSize: 65536, - PkgWQSize: 512, - TcpReadTimeout: "1s", - TcpWriteTimeout: "5s", - WaitTimeout: "1s", - MaxMsgLen: 10240000000, - SessionName: "server", - }}) - assert.NoError(t, srvConf.CheckValidity()) - - // Export - proto := GetProtocol() - 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{ - BaseInvoker: *protocol.NewBaseInvoker(url), - }) - - time.Sleep(time.Second * 2) - - return proto, url -} - -////////////////////////////////// -// provider -////////////////////////////////// - -type ( - User struct { - Id string `json:"id"` - Name string `json:"name"` - } - - UserProvider struct { - user map[string]User - } -) - -// size:4801228 -func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error { - argBuf := new(bytes.Buffer) - for i := 0; i < 4000; i++ { - argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") - argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") - } - rsp.Id = argBuf.String() - rsp.Name = argBuf.String() - return nil -} - -func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { - rsp.Id = req[0].(string) - rsp.Name = req[1].(string) - return nil -} - -func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { - return User{Id: id, Name: name}, nil -} - -func (u *UserProvider) GetUser1() error { - return nil -} - -func (u *UserProvider) GetUser2() error { - return perrors.New("error") -} - -func (u *UserProvider) GetUser3(rsp *[]interface{}) error { - *rsp = append(*rsp, User{Id: "1", Name: "username"}) - return nil -} - -func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { - - return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil -} - -func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { - return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil -} - -func (u *UserProvider) GetUser6(id int64) (*User, error) { - if id == 0 { - return nil, nil - } - return &User{Id: "1"}, nil -} - -func (u *UserProvider) Reference() string { - return "UserProvider" -} - -func (u User) JavaClassName() string { - return "com.ikurento.user.User" -} diff --git a/protocol/dubbo/codec.go b/protocol/dubbo/codec.go deleted file mode 100644 index 9781c70115e3d8b9e41c3418ae7b859608651ee8..0000000000000000000000000000000000000000 --- a/protocol/dubbo/codec.go +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo - -import ( - "bufio" - "bytes" - "fmt" - "time" -) - -import ( - "github.com/apache/dubbo-go-hessian2" - "github.com/apache/dubbo-go/common" - perrors "github.com/pkg/errors" -) - -//SerialID serial ID -type SerialID byte - -const ( - // S_Dubbo dubbo serial id - S_Dubbo SerialID = 2 -) - -//CallType call type -type CallType int32 - -const ( - // CT_UNKNOWN unknown call type - CT_UNKNOWN CallType = 0 - // CT_OneWay call one way - CT_OneWay CallType = 1 - // CT_TwoWay call in request/response - CT_TwoWay CallType = 2 -) - -//////////////////////////////////////////// -// dubbo package -//////////////////////////////////////////// - -// SequenceType sequence type -type SequenceType int64 - -// nolint -type DubboPackage struct { - Header hessian.DubboHeader - Service hessian.Service - Body interface{} - Err error -} - -// String prints dubbo package detail include header銆乸ath銆乥ody etc. -func (p DubboPackage) String() string { - return fmt.Sprintf("DubboPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) -} - -// Marshal encode hessian package. -func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { - codec := hessian.NewHessianCodec(nil) - - pkg, err := codec.Write(p.Service, p.Header, p.Body) - if err != nil { - return nil, perrors.WithStack(err) - } - - return bytes.NewBuffer(pkg), nil -} - -// Unmarshal decodes hessian package. -func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, opts ...interface{}) error { - // fix issue https://github.com/apache/dubbo-go/issues/380 - bufLen := buf.Len() - if bufLen < hessian.HEADER_LENGTH { - return perrors.WithStack(hessian.ErrHeaderNotEnough) - } - - codec := hessian.NewHessianCodec(bufio.NewReaderSize(buf, bufLen)) - - // read header - err := codec.ReadHeader(&p.Header) - if err != nil { - return perrors.WithStack(err) - } - - if len(opts) != 0 { // for client - client, ok := opts[0].(*Client) - if !ok { - return perrors.Errorf("opts[0] is not of type *Client") - } - - 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} - } - } - - // read body - err = codec.ReadBody(p.Body) - return perrors.WithStack(err) -} - -//////////////////////////////////////////// -// PendingResponse -//////////////////////////////////////////// - -// PendingResponse is a pending response. -type PendingResponse struct { - seq uint64 - err error - start time.Time - readStart time.Time - callback common.AsyncCallback - response *Response - done chan struct{} -} - -// NewPendingResponse create a PendingResponses. -func NewPendingResponse() *PendingResponse { - return &PendingResponse{ - start: time.Now(), - response: &Response{}, - done: make(chan struct{}), - } -} - -// GetCallResponse get AsyncCallbackResponse. -func (r PendingResponse) GetCallResponse() common.CallbackResponse { - return AsyncCallbackResponse{ - Cause: r.err, - Start: r.start, - ReadStart: r.readStart, - Reply: r.response, - } -} diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go new file mode 100644 index 0000000000000000000000000000000000000000..5e859a7fa2254ba0e4806bc60c037c47777bc641 --- /dev/null +++ b/protocol/dubbo/dubbo_codec.go @@ -0,0 +1,290 @@ +/* + * 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 ( + "bytes" + "strconv" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/dubbo/impl" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" +) + +//SerialID serial ID +type SerialID byte + +func init() { + codec := &DubboCodec{} + // this is for registry dubboCodec of dubbo protocol + remoting.RegistryCodec("dubbo", codec) +} + +// DubboCodec. It is implements remoting.Codec +type DubboCodec struct { +} + +// encode request for transport +func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { + if request.Event { + return c.encodeHeartbeartReqeust(request) + } + + invoc, ok := request.Data.(*protocol.Invocation) + if !ok { + err := perrors.Errorf("encode request failed for parameter type :%+v", request) + logger.Errorf(err.Error()) + return nil, err + } + invocation := *invoc + + svc := impl.Service{} + svc.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "") + svc.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "") + svc.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "") + svc.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "") + svc.Method = invocation.MethodName() + timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT))) + if err != nil { + // it will be wrapped in readwrite.Write . + return nil, perrors.WithStack(err) + } + svc.Timeout = time.Duration(timeout) + + header := impl.DubboHeader{} + serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) + if serialization == constant.PROTOBUF_SERIALIZATION { + header.SerialID = constant.S_Proto + } else { + header.SerialID = constant.S_Hessian2 + } + header.ID = request.ID + if request.TwoWay { + header.Type = impl.PackageRequest_TwoWay + } else { + header.Type = impl.PackageRequest + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: svc, + Body: impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, perrors.WithStack(err) + } + + return pkg.Marshal() +} + +// encode heartbeart request +func (c *DubboCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { + header := impl.DubboHeader{ + Type: impl.PackageHeartbeat, + SerialID: constant.S_Hessian2, + ID: request.ID, + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: impl.Service{}, + Body: impl.NewRequestPayload([]interface{}{}, nil), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, err + } + return pkg.Marshal() +} + +// encode response +func (c *DubboCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { + var ptype = impl.PackageResponse + if response.IsHeartbeat() { + ptype = impl.PackageHeartbeat + } + resp := &impl.DubboPackage{ + Header: impl.DubboHeader{ + SerialID: response.SerialID, + Type: ptype, + ID: response.ID, + ResponseStatus: response.Status, + }, + } + if !response.IsHeartbeat() { + resp.Body = &impl.ResponsePayload{ + RspObj: response.Result.(protocol.RPCResult).Rest, + Exception: response.Result.(protocol.RPCResult).Err, + Attachments: response.Result.(protocol.RPCResult).Attrs, + } + } + + codec := impl.NewDubboCodec(nil) + + pkg, err := codec.Encode(*resp) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(pkg), nil +} + +// Decode data, including request and response. +func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { + if c.isRequest(data) { + req, len, err := c.decodeRequest(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err) + } + + resp, len, err := c.decodeResponse(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err) +} + +func (c *DubboCodec) isRequest(data []byte) bool { + if data[2]&byte(0x80) == 0x00 { + return false + } + return true +} + +// decode request +func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) { + var request *remoting.Request = nil + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + pkg.SetBody(make([]interface{}, 7)) + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + //FIXME + return nil, 0, originErr + } + logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) + + return request, 0, perrors.WithStack(err) + } + request = &remoting.Request{ + ID: pkg.Header.ID, + SerialID: pkg.Header.SerialID, + TwoWay: pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00, + Event: pkg.Header.Type&impl.PackageHeartbeat != 0x00, + } + if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 { + // convert params of request + req := pkg.Body.(map[string]interface{}) + + //invocation := request.Data.(*invocation.RPCInvocation) + var methodName string + var args []interface{} + attachments := make(map[string]interface{}) + if req[impl.DubboVersionKey] != nil { + //dubbo version + request.Version = req[impl.DubboVersionKey].(string) + } + //path + attachments[constant.PATH_KEY] = pkg.Service.Path + //version + attachments[constant.VERSION_KEY] = pkg.Service.Version + //method + methodName = pkg.Service.Method + args = req[impl.ArgsKey].([]interface{}) + attachments = req[impl.AttachmentsKey].(map[string]interface{}) + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), + invocation.WithArguments(args), invocation.WithMethodName(methodName)) + request.Data = invoc + + } + return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} + +// decode response +func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + response := &remoting.Response{} + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + // if the data is very big, so the receive need much times. + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, originErr + } + logger.Errorf("pkg.Unmarshal(len(@data):%d) = error:%+v", buf.Len(), err) + + return nil, 0, perrors.WithStack(err) + } + response = &remoting.Response{ + ID: pkg.Header.ID, + //Version: pkg.Header., + SerialID: pkg.Header.SerialID, + Status: pkg.Header.ResponseStatus, + Event: (pkg.Header.Type & impl.PackageHeartbeat) != 0, + } + var error error + if pkg.Header.Type&impl.PackageHeartbeat != 0x00 { + if pkg.Header.Type&impl.PackageResponse != 0x00 { + logger.Debugf("get rpc heartbeat response{header: %#v, body: %#v}", pkg.Header, pkg.Body) + if pkg.Err != nil { + logger.Errorf("rpc heartbeat response{error: %#v}", pkg.Err) + error = pkg.Err + } + } else { + logger.Debugf("get rpc heartbeat request{header: %#v, service: %#v, body: %#v}", pkg.Header, pkg.Service, pkg.Body) + response.Status = hessian.Response_OK + //reply(session, p, hessian.PackageHeartbeat) + } + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error + } + logger.Debugf("get rpc response{header: %#v, body: %#v}", pkg.Header, pkg.Body) + rpcResult := &protocol.RPCResult{} + response.Result = rpcResult + if pkg.Header.Type&impl.PackageRequest == 0x00 { + if pkg.Err != nil { + rpcResult.Err = pkg.Err + } else if pkg.Body.(*impl.ResponsePayload).Exception != nil { + rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception + response.Error = rpcResult.Err + } + rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments + rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj + } + + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index 59202d5f49f30acb9e75c4e2f135601285f08e13..bce33508bec92b706a1722c3bbc0ed0607e66643 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -20,6 +20,7 @@ package dubbo import ( "context" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -34,8 +35,10 @@ 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" invocation_impl "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" ) var ( @@ -46,24 +49,35 @@ var ( ) var ( - attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY} + attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY, + constant.VERSION_KEY} ) -// DubboInvoker is dubbo client invoker. +// DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip. type DubboInvoker struct { protocol.BaseInvoker - client *Client + // the exchange layer, it is focus on network communication. + client *remoting.ExchangeClient quitOnce sync.Once + // timeout for service(interface) level. + timeout time.Duration // Used to record the number of requests. -1 represent this DubboInvoker is destroyed reqNum int64 } -// NewDubboInvoker create dubbo client invoker. -func NewDubboInvoker(url common.URL, client *Client) *DubboInvoker { +// NewDubboInvoker constructor +func NewDubboInvoker(url common.URL, client *remoting.ExchangeClient) *DubboInvoker { + requestTimeout := config.GetConsumerConfig().RequestTimeout + + requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) + if t, err := time.ParseDuration(requestTimeoutStr); err == nil { + requestTimeout = t + } return &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), client: client, reqNum: 0, + timeout: requestTimeout, } } @@ -84,6 +98,8 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati defer atomic.AddInt64(&(di.reqNum), -1) inv := invocation.(*invocation_impl.RPCInvocation) + // init param + inv.SetAttachments(constant.PATH_KEY, di.GetUrl().GetParam(constant.INTERFACE_KEY, "")) for _, k := range attachmentKey { if v := di.GetUrl().GetParam(k, ""); len(v) > 0 { inv.SetAttachments(k, v) @@ -94,35 +110,57 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati di.appendCtx(ctx, inv) url := di.GetUrl() + // default hessian2 serialization, compatible + if url.GetParam(constant.SERIALIZATION_KEY, "") == "" { + url.SetParam(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) + } // async async, err := strconv.ParseBool(inv.AttachmentsByKey(constant.ASYNC_KEY, "false")) if err != nil { logger.Errorf("ParseBool - error: %v", err) async = false } - response := NewResponse(inv.Reply(), nil) + //response := NewResponse(inv.Reply(), nil) + rest := &protocol.RPCResult{} + timeout := di.getTimeout(inv) if async { if callBack, ok := inv.CallBack().(func(response common.CallbackResponse)); ok { - result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) + //result.Err = di.client.AsyncCall(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), callBack, response) + result.Err = di.client.AsyncRequest(&invocation, url, timeout, callBack, rest) } else { - result.Err = di.client.CallOneway(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments())) + result.Err = di.client.Send(&invocation, url, timeout) } } else { if inv.Reply() == nil { result.Err = ErrNoReply } else { - result.Err = di.client.Call(NewRequest(url.Location, url, inv.MethodName(), inv.Arguments(), inv.Attachments()), response) + result.Err = di.client.Request(&invocation, url, timeout, rest) } } if result.Err == nil { result.Rest = inv.Reply() - result.Attrs = response.atta + result.Attrs = rest.Attrs } logger.Debugf("result.Err: %v, result.Rest: %v", result.Err, result.Rest) return &result } +// get timeout including methodConfig +func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) time.Duration { + var timeout = di.GetUrl().GetParam(strings.Join([]string{constant.METHOD_KEYS, invocation.MethodName(), constant.TIMEOUT_KEY}, "."), "") + if len(timeout) != 0 { + if t, err := time.ParseDuration(timeout); err == nil { + // config timeout into attachment + invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(t.Milliseconds()))) + return t + } + } + // set timeout into invocation at method level + invocation.SetAttachments(constant.TIMEOUT_KEY, strconv.Itoa(int(di.timeout.Milliseconds()))) + return di.timeout +} + // Destroy destroy dubbo client invoker. func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { @@ -150,8 +188,7 @@ func (di *DubboInvoker) appendCtx(ctx context.Context, inv *invocation_impl.RPCI // inject opentracing ctx currentSpan := opentracing.SpanFromContext(ctx) if currentSpan != nil { - carrier := opentracing.TextMapCarrier(inv.Attachments()) - err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier) + err := injectTraceCtx(currentSpan, inv) 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 c0640d5558fcb9fb00f02eba0fddc54bb4162592..4d32c29a222e8bb73486d664b2ed2e0038a4b3f5 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -18,6 +18,7 @@ package dubbo import ( + "bytes" "context" "sync" "testing" @@ -25,40 +26,37 @@ import ( ) import ( + hessian "github.com/apache/dubbo-go-hessian2" "github.com/opentracing/opentracing-go" + perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/getty" ) func TestDubboInvokerInvoke(t *testing.T) { proto, url := InitTest(t) - c := &Client{ - pendingResponses: new(sync.Map), - conf: *clientConf, - opts: Options{ - ConnectTimeout: 3 * time.Second, - RequestTimeout: 6 * time.Second, - }, - } - c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) + c := getExchangeClient(url) invoker := NewDubboInvoker(url, c) user := &User{} - inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(mockMethodNameGetUser), invocation.WithArguments([]interface{}{"1", "username"}), - invocation.WithReply(user), invocation.WithAttachments(map[string]string{"test_key": "test_value"})) + inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("GetUser"), invocation.WithArguments([]interface{}{"1", "username"}), + invocation.WithReply(user), invocation.WithAttachments(map[string]interface{}{"test_key": "test_value"})) // Call 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") @@ -69,8 +67,10 @@ func TestDubboInvokerInvoke(t *testing.T) { lock := sync.Mutex{} lock.Lock() inv.SetCallBack(func(response common.CallbackResponse) { - r := response.(AsyncCallbackResponse) - assert.Equal(t, User{Id: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) + r := response.(remoting.AsyncCallbackResponse) + rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult) + assert.Equal(t, User{Id: "1", Name: "username"}, *(rst.Rest.(*User))) + //assert.Equal(t, User{ID: "1", Name: "username"}, *r.Reply.(*Response).reply.(*User)) lock.Unlock() }) res = invoker.Invoke(context.Background(), inv) @@ -92,3 +92,143 @@ func TestDubboInvokerInvoke(t *testing.T) { proto.Destroy() lock.Unlock() } + +func InitTest(t *testing.T) (protocol.Protocol, common.URL) { + + hessian.RegisterPOJO(&User{}) + + methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{}) + assert.NoError(t, err) + assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) + + // config + getty.SetClientConf(getty.ClientConfig{ + ConnectionNum: 2, + HeartbeatPeriod: "5s", + SessionTimeout: "20s", + PoolTTL: 600, + PoolSize: 64, + GettySessionParam: getty.GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "4s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "client", + }, + }) + getty.SetServerConfig(getty.ServerConfig{ + SessionNumber: 700, + SessionTimeout: "20s", + GettySessionParam: getty.GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "server", + }}) + + // Export + proto := GetProtocol() + url, err := common.NewURL("dubbo://127.0.0.1:20702/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{ + BaseInvoker: *protocol.NewBaseInvoker(url), + }) + + time.Sleep(time.Second * 2) + + return proto, url +} + +////////////////////////////////// +// provider +////////////////////////////////// + +type ( + User struct { + Id string `json:"id"` + Name string `json:"name"` + } + + UserProvider struct { + user map[string]User + } +) + +// size:4801228 +func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error { + argBuf := new(bytes.Buffer) + for i := 0; i < 400; i++ { + // use chinese for test + argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") + argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") + } + rsp.Id = argBuf.String() + rsp.Name = argBuf.String() + return nil +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { + rsp.Id = req[0].(string) + rsp.Name = req[1].(string) + return nil +} + +func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { + return User{Id: id, Name: name}, nil +} + +func (u *UserProvider) GetUser1() error { + return nil +} + +func (u *UserProvider) GetUser2() error { + return perrors.New("error") +} + +func (u *UserProvider) GetUser3(rsp *[]interface{}) error { + *rsp = append(*rsp, User{Id: "1", Name: "username"}) + return nil +} + +func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { + + return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +} + +func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { + return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +} + +func (u *UserProvider) GetUser6(id int64) (*User, error) { + if id == 0 { + return nil, nil + } + return &User{Id: "1"}, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 9eeefd079279d82241da8e21df5edfe77b8003e0..8dda52b6b9b79e5d20a730db52f9a376422ccd63 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -18,10 +18,16 @@ package dubbo import ( + "context" + "fmt" "sync" "time" ) +import ( + "github.com/opentracing/opentracing-go" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" @@ -29,14 +35,23 @@ import ( "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" + "github.com/apache/dubbo-go/remoting/getty" ) -// dubbo protocol constant const ( // DUBBO is dubbo protocol name DUBBO = "dubbo" ) +var ( + // Make the connection can be shared. + // It will create one connection for one address (ip+port) + exchangeClientMap = new(sync.Map) + exchangeLock = new(sync.Map) +) + func init() { extension.SetProtocol(DUBBO, GetProtocol) } @@ -45,10 +60,12 @@ var ( dubboProtocol *DubboProtocol ) -// DubboProtocol is a dubbo protocol implement. +// It support dubbo protocol. It implements Protocol interface for dubbo protocol. type DubboProtocol struct { protocol.BaseProtocol - serverMap map[string]*Server + // It is store relationship about serviceKey(group/interface:version) and ExchangeServer + // The ExchangeServer is introduced to replace of Server. Because Server is depend on getty directly. + serverMap map[string]*remoting.ExchangeServer serverLock sync.Mutex } @@ -56,7 +73,7 @@ type DubboProtocol struct { func NewDubboProtocol() *DubboProtocol { return &DubboProtocol{ BaseProtocol: protocol.NewBaseProtocol(), - serverMap: make(map[string]*Server), + serverMap: make(map[string]*remoting.ExchangeServer), } } @@ -67,7 +84,6 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { exporter := NewDubboExporter(serviceKey, invoker, dp.ExporterMap()) dp.SetExporterMap(serviceKey, exporter) logger.Infof("Export service: %s", url.String()) - // start server dp.openServer(url) return exporter @@ -75,18 +91,12 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { // Refer create dubbo service reference. 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 + exchangeClient := getExchangeClient(url) + if exchangeClient == nil { + logger.Warnf("can't dial the server: %+v", url.Location) + return nil } - - invoker := NewDubboInvoker(url, NewClient(Options{ - ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, - RequestTimeout: requestTimeout, - })) + invoker := NewDubboInvoker(url, exchangeClient) dp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) return invoker @@ -116,9 +126,12 @@ func (dp *DubboProtocol) openServer(url common.URL) { dp.serverLock.Lock() _, ok = dp.serverMap[url.Location] if !ok { - srv := NewServer() + handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { + return doHandleRequest(invocation) + } + srv := remoting.NewExchangeServer(url, getty.NewServer(url, handler)) dp.serverMap[url.Location] = srv - srv.Start(url) + srv.Start() } dp.serverLock.Unlock() } @@ -131,3 +144,91 @@ func GetProtocol() protocol.Protocol { } return dubboProtocol } + +func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult { + exporter, _ := dubboProtocol.ExporterMap().Load(rpcInvocation.ServiceKey()) + result := protocol.RPCResult{} + if exporter == nil { + err := fmt.Errorf("don't have this exporter, key: %s", rpcInvocation.ServiceKey()) + logger.Errorf(err.Error()) + result.Err = err + //reply(session, p, hessian.PackageResponse) + return result + } + invoker := exporter.(protocol.Exporter).GetInvoker() + if invoker != nil { + // FIXME + ctx := rebuildCtx(rpcInvocation) + + invokeResult := invoker.Invoke(ctx, rpcInvocation) + if err := invokeResult.Error(); err != nil { + result.Err = invokeResult.Error() + //p.Header.ResponseStatus = hessian.Response_OK + //p.Body = hessian.NewResponse(nil, err, result.Attachments()) + } else { + result.Rest = invokeResult.Result() + //p.Header.ResponseStatus = hessian.Response_OK + //p.Body = hessian.NewResponse(res, nil, result.Attachments()) + } + } else { + result.Err = fmt.Errorf("don't have the invoker, key: %s", rpcInvocation.ServiceKey()) + } + return result +} + +func getExchangeClient(url common.URL) *remoting.ExchangeClient { + clientTmp, ok := exchangeClientMap.Load(url.Location) + if !ok { + var exchangeClientTmp *remoting.ExchangeClient + func() { + // lock for NewExchangeClient and store into map. + _, loaded := exchangeLock.LoadOrStore(url.Location, 0x00) + // unlock + defer exchangeLock.Delete(url.Location) + if loaded { + // retry for 5 times. + for i := 0; i < 5; i++ { + if clientTmp, ok = exchangeClientMap.Load(url.Location); ok { + break + } else { + // if cannot get, sleep a while. + time.Sleep(time.Duration(i*100) * time.Millisecond) + } + } + return + } + // new ExchangeClient + exchangeClientTmp = remoting.NewExchangeClient(url, getty.NewClient(getty.Options{ + ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + RequestTimeout: config.GetConsumerConfig().RequestTimeout, + }), config.GetConsumerConfig().ConnectTimeout, false) + // input store + if exchangeClientTmp != nil { + exchangeClientMap.Store(url.Location, exchangeClientTmp) + } + }() + if exchangeClientTmp != nil { + return exchangeClientTmp + } + } + // cannot dial the server + if clientTmp == nil { + return nil + } + return clientTmp.(*remoting.ExchangeClient) +} + +// 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.WithValue(context.Background(), "attachment", inv.Attachments()) + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(filterContext(inv.Attachments()))) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} diff --git a/protocol/dubbo/dubbo_protocol_test.go b/protocol/dubbo/dubbo_protocol_test.go index 6f3892be67be533dea09dc7bd54de56844dbc79c..9eba90e9da564453649b378f061bc0179ffedc5e 100644 --- a/protocol/dubbo/dubbo_protocol_test.go +++ b/protocol/dubbo/dubbo_protocol_test.go @@ -28,7 +28,9 @@ import ( import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/remoting/getty" ) const ( @@ -39,14 +41,56 @@ const ( "side=provider&timeout=3000×tamp=1556509797245" ) -func TestDubboProtocolExport(t *testing.T) { +func initDubboInvokerTest() { + getty.SetServerConfig(getty.ServerConfig{ + SessionNumber: 700, + SessionTimeout: "20s", + GettySessionParam: getty.GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "server", + }}) + getty.SetClientConf(getty.ClientConfig{ + ConnectionNum: 1, + HeartbeatPeriod: "3s", + SessionTimeout: "20s", + PoolTTL: 600, + PoolSize: 64, + GettySessionParam: getty.GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "4s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "client", + }, + }) +} + +func TestDubboProtocol_Export(t *testing.T) { + initDubboInvokerTest() + srvCfg := getty.GetDefaultServerConfig() + getty.SetServerConfig(srvCfg) // Export proto := GetProtocol() - srvConf = &ServerConfig{} url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) exporter := proto.Export(protocol.NewBaseInvoker(url)) - // make sure url eq := exporter.GetInvoker().GetUrl().URLEqual(url) assert.True(t, eq) @@ -60,10 +104,10 @@ func TestDubboProtocolExport(t *testing.T) { assert.True(t, eq2) // make sure exporterMap after 'Unexport' - _, ok := proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + _, ok := proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.True(t, ok) - exporter.Unexport() - _, ok = proto.(*DubboProtocol).ExporterMap().Load(url.ServiceKey()) + exporter2.Unexport() + _, ok = proto.(*DubboProtocol).ExporterMap().Load(url2.ServiceKey()) assert.False(t, ok) // make sure serverMap after 'Destroy' @@ -74,14 +118,29 @@ func TestDubboProtocolExport(t *testing.T) { assert.False(t, ok) } -func TestDubboProtocolRefer(t *testing.T) { +func TestDubboProtocolReferNoConnect(t *testing.T) { // Refer + initDubboInvokerTest() proto := GetProtocol() url, err := common.NewURL(mockCommonUrl) assert.NoError(t, err) - clientConf = &ClientConfig{} invoker := proto.Refer(url) + assert.Nil(t, invoker) +} + +func TestDubboProtocol_Refer(t *testing.T) { + initDubboInvokerTest() + cliCfg := getty.GetDefaultClientConfig() + getty.SetClientConf(cliCfg) + // Refer + proto := GetProtocol() + url, err := common.NewURL(mockCommonUrl) + proto.Export(&proxy_factory.ProxyInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + }) + assert.NoError(t, err) + invoker := proto.Refer(url) // make sure url eq := invoker.GetUrl().URLEqual(url) assert.True(t, eq) diff --git a/protocol/dubbo/hessian2/const.go b/protocol/dubbo/hessian2/const.go new file mode 100644 index 0000000000000000000000000000000000000000..74a00b601db22397916aab215ccd33bc918d91e7 --- /dev/null +++ b/protocol/dubbo/hessian2/const.go @@ -0,0 +1,243 @@ +/* + * 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. + */ + +// This constants are also defined in dubbo constants. But we will still used these until hessian is stable. + +package hessian2 + +import ( + "reflect" + "regexp" +) + +import ( + perrors "github.com/pkg/errors" +) + +const ( + mask = byte(127) + flag = byte(128) +) + +const ( + // Zero : byte zero + Zero = byte(0x00) +) + +// constansts +const ( + TAG_READ = int32(-1) + ASCII_GAP = 32 + CHUNK_SIZE = 4096 + BC_BINARY = byte('B') // final chunk + BC_BINARY_CHUNK = byte('A') // non-final chunk + + BC_BINARY_DIRECT = byte(0x20) // 1-byte length binary + BINARY_DIRECT_MAX = byte(0x0f) + BC_BINARY_SHORT = byte(0x34) // 2-byte length binary + BINARY_SHORT_MAX = 0x3ff // 0-1023 binary + + BC_DATE = byte(0x4a) // 64-bit millisecond UTC date + BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date + + BC_DOUBLE = byte('D') // IEEE 64-bit double + + BC_DOUBLE_ZERO = byte(0x5b) + BC_DOUBLE_ONE = byte(0x5c) + BC_DOUBLE_BYTE = byte(0x5d) + BC_DOUBLE_SHORT = byte(0x5e) + BC_DOUBLE_MILL = byte(0x5f) + + BC_FALSE = byte('F') // boolean false + + BC_INT = byte('I') // 32-bit int + + INT_DIRECT_MIN = -0x10 + INT_DIRECT_MAX = byte(0x2f) + BC_INT_ZERO = byte(0x90) + + INT_BYTE_MIN = -0x800 + INT_BYTE_MAX = 0x7ff + BC_INT_BYTE_ZERO = byte(0xc8) + + BC_END = byte('Z') + + INT_SHORT_MIN = -0x40000 + INT_SHORT_MAX = 0x3ffff + BC_INT_SHORT_ZERO = byte(0xd4) + + BC_LIST_VARIABLE = byte(0x55) + BC_LIST_FIXED = byte('V') + BC_LIST_VARIABLE_UNTYPED = byte(0x57) + BC_LIST_FIXED_UNTYPED = byte(0x58) + _listFixedTypedLenTagMin = byte(0x70) + _listFixedTypedLenTagMax = byte(0x77) + _listFixedUntypedLenTagMin = byte(0x78) + _listFixedUntypedLenTagMax = byte(0x7f) + + BC_LIST_DIRECT = byte(0x70) + BC_LIST_DIRECT_UNTYPED = byte(0x78) + LIST_DIRECT_MAX = byte(0x7) + + BC_LONG = byte('L') // 64-bit signed integer + LONG_DIRECT_MIN = -0x08 + LONG_DIRECT_MAX = byte(0x0f) + BC_LONG_ZERO = byte(0xe0) + + LONG_BYTE_MIN = -0x800 + LONG_BYTE_MAX = 0x7ff + BC_LONG_BYTE_ZERO = byte(0xf8) + + LONG_SHORT_MIN = -0x40000 + LONG_SHORT_MAX = 0x3ffff + BC_LONG_SHORT_ZERO = byte(0x3c) + + BC_LONG_INT = byte(0x59) + + BC_MAP = byte('M') + BC_MAP_UNTYPED = byte('H') + + BC_NULL = byte('N') // x4e + + BC_OBJECT = byte('O') + BC_OBJECT_DEF = byte('C') + + BC_OBJECT_DIRECT = byte(0x60) + OBJECT_DIRECT_MAX = byte(0x0f) + + BC_REF = byte(0x51) + + BC_STRING = byte('S') // final string + BC_STRING_CHUNK = byte('R') // non-final string + + BC_STRING_DIRECT = byte(0x00) + STRING_DIRECT_MAX = byte(0x1f) + BC_STRING_SHORT = byte(0x30) + STRING_SHORT_MAX = 0x3ff + + BC_TRUE = byte('T') + + P_PACKET_CHUNK = byte(0x4f) + P_PACKET = byte('P') + + P_PACKET_DIRECT = byte(0x80) + PACKET_DIRECT_MAX = byte(0x7f) + + P_PACKET_SHORT = byte(0x70) + PACKET_SHORT_MAX = 0xfff + ARRAY_STRING = "[string" + ARRAY_INT = "[int" + ARRAY_DOUBLE = "[double" + ARRAY_FLOAT = "[float" + ARRAY_BOOL = "[boolean" + ARRAY_LONG = "[long" + + PATH_KEY = "path" + GROUP_KEY = "group" + INTERFACE_KEY = "interface" + VERSION_KEY = "version" + TIMEOUT_KEY = "timeout" + + STRING_NIL = "" + STRING_TRUE = "true" + STRING_FALSE = "false" + STRING_ZERO = "0.0" + STRING_ONE = "1.0" +) + +// DubboResponse related consts +const ( + Response_OK byte = 20 + Response_CLIENT_TIMEOUT byte = 30 + Response_SERVER_TIMEOUT byte = 31 + Response_BAD_REQUEST byte = 40 + Response_BAD_RESPONSE byte = 50 + Response_SERVICE_NOT_FOUND byte = 60 + Response_SERVICE_ERROR byte = 70 + Response_SERVER_ERROR byte = 80 + Response_CLIENT_ERROR byte = 90 + + // According to "java dubbo" There are two cases of response: + // 1. with attachments + // 2. no attachments + RESPONSE_WITH_EXCEPTION int32 = 0 + RESPONSE_VALUE int32 = 1 + RESPONSE_NULL_VALUE int32 = 2 + RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3 + RESPONSE_VALUE_WITH_ATTACHMENTS int32 = 4 + RESPONSE_NULL_VALUE_WITH_ATTACHMENTS int32 = 5 +) + +/** + * the dubbo protocol header length is 16 Bytes. + * the first 2 Bytes is magic code '0xdabb' + * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag + * the next 1 Bytes is response state. + * the next 8 Bytes is package DI. + * the next 4 Bytes is package length. + **/ +const ( + // header length. + HEADER_LENGTH = 16 + + // magic header + MAGIC = uint16(0xdabb) + MAGIC_HIGH = byte(0xda) + MAGIC_LOW = byte(0xbb) + + // message flag. + FLAG_REQUEST = byte(0x80) + FLAG_TWOWAY = byte(0x40) + FLAG_EVENT = byte(0x20) // for heartbeat + SERIAL_MASK = 0x1f + + DUBBO_VERSION = "2.5.4" + DUBBO_VERSION_KEY = "dubbo" + DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2 + LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200 + DEFAULT_LEN = 8388608 // 8 * 1024 * 1024 default body max length +) + +// regular +const ( + JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)" + CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)" + ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))" + DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")" +) + +// Dubbo request response related consts +var ( + DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY} + DubboRequestHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST} + DubboResponseHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK} + DubboRequestHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT} + DubboResponseHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT} +) + +// Error part +var ( + ErrHeaderNotEnough = perrors.New("header buffer too short") + ErrBodyNotEnough = perrors.New("body buffer too short") + ErrJavaException = perrors.New("got java exception") + ErrIllegalPackage = perrors.New("illegal package!") +) + +// nolint +var DescRegex, _ = regexp.Compile(DESC_REGEX) + +var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()) diff --git a/protocol/dubbo/hessian2/hessian_dubbo.go b/protocol/dubbo/hessian2/hessian_dubbo.go new file mode 100644 index 0000000000000000000000000000000000000000..1afa4ec96eccbb8077852dfcc020e0eb05be3257 --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_dubbo.go @@ -0,0 +1,251 @@ +/* + * 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 hessian2 + +import ( + "bufio" + "encoding/binary" + "time" +) + +import ( + "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +// enum part +const ( + PackageError = PackageType(0x01) + PackageRequest = PackageType(0x02) + PackageResponse = PackageType(0x04) + PackageHeartbeat = PackageType(0x08) + PackageRequest_TwoWay = PackageType(0x10) + PackageResponse_Exception = PackageType(0x20) + PackageType_BitSize = 0x2f +) + +// PackageType nolint +type PackageType int + +// DubboHeader dubbo header +type DubboHeader struct { + SerialID byte + Type PackageType + ID int64 + BodyLen int + ResponseStatus byte +} + +// Service defines service instance +type Service struct { + Path string + Interface string + Group string + Version string + Method string + Timeout time.Duration // request timeout +} + +// HessianCodec defines hessian codec +type HessianCodec struct { + pkgType PackageType + reader *bufio.Reader + bodyLen int +} + +// NewHessianCodec generate a new hessian codec instance +func NewHessianCodec(reader *bufio.Reader) *HessianCodec { + return &HessianCodec{ + reader: reader, + } +} + +// NewHessianCodec generate a new hessian codec instance +func NewHessianCodecCustom(pkgType PackageType, reader *bufio.Reader, bodyLen int) *HessianCodec { + return &HessianCodec{ + pkgType: pkgType, + reader: reader, + bodyLen: bodyLen, + } +} + +func (h *HessianCodec) Write(service Service, header DubboHeader, body interface{}) ([]byte, error) { + switch header.Type { + case PackageHeartbeat: + if header.ResponseStatus == Zero { + return packRequest(service, header, body) + } + return packResponse(header, body) + + case PackageRequest, PackageRequest_TwoWay: + return packRequest(service, header, body) + + case PackageResponse: + return packResponse(header, body) + + default: + return nil, perrors.Errorf("Unrecognised message type: %v", header.Type) + } + + // unreachable return nil, nil +} + +// ReadHeader uses hessian codec to read dubbo header +func (h *HessianCodec) ReadHeader(header *DubboHeader) error { + + var err error + + if h.reader.Size() < HEADER_LENGTH { + return ErrHeaderNotEnough + } + buf, err := h.reader.Peek(HEADER_LENGTH) + if err != nil { // this is impossible + return perrors.WithStack(err) + } + _, err = h.reader.Discard(HEADER_LENGTH) + if err != nil { // this is impossible + return perrors.WithStack(err) + } + + //// read header + + if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW { + return ErrIllegalPackage + } + + // Header{serialization id(5 bit), event, two way, req/response} + if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero { + return perrors.Errorf("serialization ID:%v", header.SerialID) + } + + flag := buf[2] & FLAG_EVENT + if flag != Zero { + header.Type |= PackageHeartbeat + } + flag = buf[2] & FLAG_REQUEST + if flag != Zero { + header.Type |= PackageRequest + flag = buf[2] & FLAG_TWOWAY + if flag != Zero { + header.Type |= PackageRequest_TwoWay + } + } else { + header.Type |= PackageResponse + header.ResponseStatus = buf[3] + if header.ResponseStatus != Response_OK { + header.Type |= PackageResponse_Exception + } + } + + // Header{req id} + header.ID = int64(binary.BigEndian.Uint64(buf[4:])) + + // Header{body len} + header.BodyLen = int(binary.BigEndian.Uint32(buf[12:])) + if header.BodyLen < 0 { + return ErrIllegalPackage + } + + h.pkgType = header.Type + h.bodyLen = header.BodyLen + + if h.reader.Buffered() < h.bodyLen { + return ErrBodyNotEnough + } + + return perrors.WithStack(err) + +} + +// ReadBody uses hessian codec to read response body +func (h *HessianCodec) ReadBody(rspObj interface{}) error { + + if h.reader.Buffered() < h.bodyLen { + return ErrBodyNotEnough + } + buf, err := h.reader.Peek(h.bodyLen) + if err != nil { + return perrors.WithStack(err) + } + _, err = h.reader.Discard(h.bodyLen) + if err != nil { // this is impossible + return perrors.WithStack(err) + } + + switch h.pkgType & PackageType_BitSize { + case PackageResponse | PackageHeartbeat | PackageResponse_Exception, PackageResponse | PackageResponse_Exception: + decoder := hessian.NewDecoder(buf[:]) + exception, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + rsp, ok := rspObj.(*DubboResponse) + if !ok { + return perrors.Errorf("java exception:%s", exception.(string)) + } + rsp.Exception = perrors.Errorf("java exception:%s", exception.(string)) + return nil + case PackageRequest | PackageHeartbeat, PackageResponse | PackageHeartbeat: + case PackageRequest: + if rspObj != nil { + if err = unpackRequestBody(hessian.NewDecoder(buf[:]), rspObj); err != nil { + return perrors.WithStack(err) + } + } + case PackageResponse: + if rspObj != nil { + if err = unpackResponseBody(hessian.NewDecoder(buf[:]), rspObj); err != nil { + return perrors.WithStack(err) + } + } + } + + return nil +} + +// ignore body, but only read attachments +func (h *HessianCodec) ReadAttachments() (map[string]interface{}, error) { + if h.reader.Buffered() < h.bodyLen { + return nil, ErrBodyNotEnough + } + buf, err := h.reader.Peek(h.bodyLen) + if err != nil { + return nil, perrors.WithStack(err) + } + _, err = h.reader.Discard(h.bodyLen) + if err != nil { // this is impossible + return nil, perrors.WithStack(err) + } + + switch h.pkgType & PackageType_BitSize { + case PackageRequest: + rspObj := make([]interface{}, 7) + if err = unpackRequestBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil { + return nil, perrors.WithStack(err) + } + return rspObj[6].(map[string]interface{}), nil + case PackageResponse: + rspObj := &DubboResponse{} + if err = unpackResponseBody(hessian.NewDecoderWithSkip(buf[:]), rspObj); err != nil { + return nil, perrors.WithStack(err) + } + return rspObj.Attachments, nil + } + + return nil, nil +} diff --git a/protocol/dubbo/hessian2/hessian_dubbo_test.go b/protocol/dubbo/hessian2/hessian_dubbo_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c3f19f04536484816e4b4f709f534dcbf4adb2b4 --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_dubbo_test.go @@ -0,0 +1,231 @@ +/* + * 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 hessian2 + +import ( + "bufio" + "bytes" + "reflect" + "testing" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/stretchr/testify/assert" +) + +type Case struct { + A string + B int +} + +type CaseA struct { + A string + B int + C Case +} + +type CaseB struct { + A string + B CaseA +} + +func (c *CaseB) JavaClassName() string { + return "com.test.caseb" +} + +func (c CaseA) JavaClassName() string { + return "com.test.casea" +} + +//JavaClassName java fully qualified path +func (c Case) JavaClassName() string { + return "com.test.case" +} + +func doTestHessianEncodeHeader(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) ([]byte, error) { + hessian.RegisterPOJO(&Case{}) + codecW := NewHessianCodec(nil) + resp, err := codecW.Write(Service{ + Path: "test", + Interface: "ITest", + Version: "v1.0", + Method: "test", + Timeout: time.Second * 10, + }, DubboHeader{ + SerialID: 2, + Type: packageType, + ID: 1, + ResponseStatus: responseStatus, + }, body) + assert.Nil(t, err) + return resp, err +} + +func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *DubboResponse, assertFunc func()) { + resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) + + codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) + + h := &DubboHeader{} + err = codecR.ReadHeader(h) + assert.Nil(t, err) + + assert.Equal(t, byte(2), h.SerialID) + assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat)) + assert.Equal(t, int64(1), h.ID) + assert.Equal(t, responseStatus, h.ResponseStatus) + + err = codecR.ReadBody(decodedResponse) + assert.Nil(t, err) + t.Log(decodedResponse) + + if assertFunc != nil { + assertFunc() + return + } + + if h.ResponseStatus != Zero && h.ResponseStatus != Response_OK { + assert.Equal(t, "java exception:"+body.(string), decodedResponse.Exception.Error()) + return + } + + in, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(body)), nil) + out, _ := hessian.EnsureInterface(hessian.UnpackPtrValue(hessian.EnsurePackValue(decodedResponse.RspObj)), nil) + assert.Equal(t, in, out) +} + +func TestResponse(t *testing.T) { + caseObj := Case{A: "a", B: 1} + decodedResponse := &DubboResponse{} + + arr := []*Case{&caseObj} + var arrRes []interface{} + decodedResponse.RspObj = &arrRes + doTestResponse(t, PackageResponse, Response_OK, arr, decodedResponse, func() { + assert.Equal(t, 1, len(arrRes)) + assert.Equal(t, &caseObj, arrRes[0]) + }) + + decodedResponse.RspObj = &Case{} + doTestResponse(t, PackageResponse, Response_OK, &Case{A: "a", B: 1}, decodedResponse, nil) + + s := "ok!!!!!" + strObj := "" + decodedResponse.RspObj = &strObj + doTestResponse(t, PackageResponse, Response_OK, s, decodedResponse, nil) + + var intObj int64 + decodedResponse.RspObj = &intObj + doTestResponse(t, PackageResponse, Response_OK, int64(3), decodedResponse, nil) + + boolObj := false + decodedResponse.RspObj = &boolObj + doTestResponse(t, PackageResponse, Response_OK, true, decodedResponse, nil) + + strObj = "" + decodedResponse.RspObj = &strObj + doTestResponse(t, PackageResponse, hessian.Response_SERVER_ERROR, "error!!!!!", decodedResponse, nil) + + mapObj := map[string][]*Case{"key": {&caseObj}} + mapRes := map[interface{}]interface{}{} + decodedResponse.RspObj = &mapRes + doTestResponse(t, PackageResponse, Response_OK, mapObj, decodedResponse, func() { + c, ok := mapRes["key"] + if !ok { + assert.FailNow(t, "no key in decoded response map") + } + + mapValueArr, ok := c.([]*Case) + if !ok { + assert.FailNow(t, "invalid decoded response map value", "expect []*Case, but get %v", reflect.TypeOf(c)) + } + assert.Equal(t, 1, len(mapValueArr)) + assert.Equal(t, &caseObj, mapValueArr[0]) + }) +} + +func doTestRequest(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) { + resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) + + codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) + + h := &DubboHeader{} + err = codecR.ReadHeader(h) + assert.Nil(t, err) + assert.Equal(t, byte(2), h.SerialID) + assert.Equal(t, packageType, h.Type&(PackageRequest|PackageResponse|PackageHeartbeat)) + assert.Equal(t, int64(1), h.ID) + assert.Equal(t, responseStatus, h.ResponseStatus) + + c := make([]interface{}, 7) + err = codecR.ReadBody(c) + assert.Nil(t, err) + t.Log(c) + assert.True(t, len(body.([]interface{})) == len(c[5].([]interface{}))) +} + +func TestRequest(t *testing.T) { + doTestRequest(t, PackageRequest, Zero, []interface{}{"a"}) + doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3}) + doTestRequest(t, PackageRequest, Zero, []interface{}{"a", true}) + doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true}) + doTestRequest(t, PackageRequest, Zero, []interface{}{3.2, true}) + doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, &Case{A: "a", B: 3}}) + doTestRequest(t, PackageRequest, Zero, []interface{}{"a", 3, true, []*Case{{A: "a", B: 3}}}) + doTestRequest(t, PackageRequest, Zero, []interface{}{map[string][]*Case{"key": {{A: "a", B: 3}}}}) +} + +func TestHessianCodec_ReadAttachments(t *testing.T) { + hessian.RegisterPOJO(&AttachTestObject{}) + body := &DubboResponse{ + RspObj: &CaseB{A: "A", B: CaseA{A: "a", B: 1, C: Case{A: "c", B: 2}}}, + Exception: nil, + Attachments: map[string]interface{}{DUBBO_VERSION_KEY: "2.6.4", "att": AttachTestObject{Id: 23, Name: "haha"}}, + } + resp, err := doTestHessianEncodeHeader(t, PackageResponse, Response_OK, body) + assert.NoError(t, err) + hessian.UnRegisterPOJOs(&CaseB{}, &CaseA{}) + codecR1 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) + codecR2 := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) + h := &DubboHeader{} + assert.NoError(t, codecR1.ReadHeader(h)) + t.Log(h) + assert.NoError(t, codecR2.ReadHeader(h)) + t.Log(h) + + err = codecR1.ReadBody(body) + assert.Equal(t, "can not find go type name com.test.caseb in registry", err.Error()) + attrs, err := codecR2.ReadAttachments() + assert.NoError(t, err) + assert.Equal(t, "2.6.4", attrs[DUBBO_VERSION_KEY]) + assert.Equal(t, AttachTestObject{Id: 23, Name: "haha"}, *(attrs["att"].(*AttachTestObject))) + assert.NotEqual(t, AttachTestObject{Id: 24, Name: "nohaha"}, *(attrs["att"].(*AttachTestObject))) + + t.Log(attrs) +} + +type AttachTestObject struct { + Id int32 + Name string `dubbo:name` +} + +func (AttachTestObject) JavaClassName() string { + return "com.test.Test" +} diff --git a/protocol/dubbo/hessian2/hessian_request.go b/protocol/dubbo/hessian2/hessian_request.go new file mode 100644 index 0000000000000000000000000000000000000000..4ebb4aa1be05d4d1941661fed452dda06cf55fa0 --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_request.go @@ -0,0 +1,350 @@ +/* + * 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 hessian2 + +import ( + "encoding/binary" + "reflect" + "strconv" + "strings" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +///////////////////////////////////////// +// dubbo +///////////////////////////////////////// + +func getArgType(v interface{}) string { + if v == nil { + return "V" + } + + switch v.(type) { + // Serialized tags for base types + case nil: + return "V" + case bool: + return "Z" + case []bool: + return "[Z" + case byte: + return "B" + case []byte: + return "[B" + case int8: + return "B" + case []int8: + return "[B" + case int16: + return "S" + case []int16: + return "[S" + case uint16: // Equivalent to Char of Java + return "C" + case []uint16: + return "[C" + // case rune: + // return "C" + case int: + return "J" + case []int: + return "[J" + case int32: + return "I" + case []int32: + return "[I" + case int64: + return "J" + case []int64: + return "[J" + case time.Time: + return "java.util.Date" + case []time.Time: + return "[Ljava.util.Date" + case float32: + return "F" + case []float32: + return "[F" + case float64: + return "D" + case []float64: + return "[D" + case string: + return "java.lang.String" + case []string: + return "[Ljava.lang.String;" + case []hessian.Object: + return "[Ljava.lang.Object;" + case map[interface{}]interface{}: + // return "java.util.HashMap" + return "java.util.Map" + case hessian.POJOEnum: + return v.(hessian.POJOEnum).JavaClassName() + // Serialized tags for complex types + default: + t := reflect.TypeOf(v) + if reflect.Ptr == t.Kind() { + t = reflect.TypeOf(reflect.ValueOf(v).Elem()) + } + switch t.Kind() { + case reflect.Struct: + return "java.lang.Object" + case reflect.Slice, reflect.Array: + if t.Elem().Kind() == reflect.Struct { + return "[Ljava.lang.Object;" + } + // return "java.util.ArrayList" + return "java.util.List" + case reflect.Map: // Enter here, map may be map[string]int + return "java.util.Map" + default: + return "" + } + } + + // unreachable + // return "java.lang.RuntimeException" +} + +func getArgsTypeList(args []interface{}) (string, error) { + var ( + typ string + types string + ) + + for i := range args { + typ = getArgType(args[i]) + if typ == "" { + return types, perrors.Errorf("cat not get arg %#v type", args[i]) + } + if !strings.Contains(typ, ".") { + types += typ + } else if strings.Index(typ, "[") == 0 { + types += strings.Replace(typ, ".", "/", -1) + } else { + // java.util.List -> Ljava/util/List; + types += "L" + strings.Replace(typ, ".", "/", -1) + ";" + } + } + + return types, nil +} + +type DubboRequest struct { + Params interface{} + Attachments map[string]interface{} +} + +// NewRequest create a new DubboRequest +func NewRequest(params interface{}, atta map[string]interface{}) *DubboRequest { + if atta == nil { + atta = make(map[string]interface{}) + } + return &DubboRequest{ + Params: params, + Attachments: atta, + } +} + +func EnsureRequest(body interface{}) *DubboRequest { + if req, ok := body.(*DubboRequest); ok { + return req + } + return NewRequest(body, nil) +} + +func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, error) { + var ( + err error + types string + byteArray []byte + pkgLen int + ) + + request := EnsureRequest(req) + + args, ok := request.Params.([]interface{}) + if !ok { + return nil, perrors.Errorf("@params is not of type: []interface{}") + } + + hb := header.Type == PackageHeartbeat + + ////////////////////////////////////////// + // byteArray + ////////////////////////////////////////// + // magic + switch header.Type { + case PackageHeartbeat: + byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...) + case PackageRequest_TwoWay: + byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...) + default: + byteArray = append(byteArray, DubboRequestHeaderBytes[:]...) + } + + // serialization id, two way flag, event, request/response flag + // SerialID is id of serialization approach in java dubbo + byteArray[2] |= header.SerialID & SERIAL_MASK + // request id + binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) + + encoder := hessian.NewEncoder() + encoder.Append(byteArray[:HEADER_LENGTH]) + + ////////////////////////////////////////// + // body + ////////////////////////////////////////// + if hb { + encoder.Encode(nil) + goto END + } + + // dubbo version + path + version + method + encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) + encoder.Encode(service.Path) + encoder.Encode(service.Version) + encoder.Encode(service.Method) + + // args = args type list + args value list + if types, err = getArgsTypeList(args); err != nil { + return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args) + } + encoder.Encode(types) + for _, v := range args { + encoder.Encode(v) + } + + request.Attachments[PATH_KEY] = service.Path + request.Attachments[VERSION_KEY] = service.Version + if len(service.Group) > 0 { + request.Attachments[GROUP_KEY] = service.Group + } + if len(service.Interface) > 0 { + request.Attachments[INTERFACE_KEY] = service.Interface + } + if service.Timeout != 0 { + request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond)) + } + + encoder.Encode(request.Attachments) + +END: + byteArray = encoder.Buffer() + pkgLen = len(byteArray) + if pkgLen > int(DEFAULT_LEN) { // 8M + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + } + // byteArray{body length} + binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH)) + return byteArray, nil +} + +// hessian decode request body +func unpackRequestBody(decoder *hessian.Decoder, reqObj interface{}) error { + + if decoder == nil { + return perrors.Errorf("@decoder is nil") + } + + req, ok := reqObj.([]interface{}) + if !ok { + return perrors.Errorf("@reqObj is not of type: []interface{}") + } + if len(req) < 7 { + return perrors.New("length of @reqObj should be 7") + } + + var ( + err error + dubboVersion, target, serviceVersion, method, argsTypes interface{} + args []interface{} + ) + + dubboVersion, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[0] = dubboVersion + + target, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[1] = target + + serviceVersion, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[2] = serviceVersion + + method, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[3] = method + + argsTypes, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[4] = argsTypes + + ats := DescRegex.FindAllString(argsTypes.(string), -1) + var arg interface{} + for i := 0; i < len(ats); i++ { + arg, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + args = append(args, arg) + } + req[5] = args + + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + v[DUBBO_VERSION_KEY] = dubboVersion + req[6] = ToMapStringInterface(v) + return nil + } + + return perrors.Errorf("get wrong attachments: %+v", attachments) +} + +func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} { + dest := make(map[string]interface{}, len(origin)) + for k, v := range origin { + if kv, ok := k.(string); ok { + if v == nil { + dest[kv] = "" + continue + } + dest[kv] = v + } + } + return dest +} diff --git a/protocol/dubbo/hessian2/hessian_request_test.go b/protocol/dubbo/hessian2/hessian_request_test.go new file mode 100644 index 0000000000000000000000000000000000000000..98d5f2399c9fdbf99a7274e0c3e53c2c175f774f --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_request_test.go @@ -0,0 +1,158 @@ +/* + * 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 hessian2 + +import ( + "reflect" + "strconv" + "testing" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/stretchr/testify/assert" +) + +type TestEnumGender hessian.JavaEnum + +const ( + MAN hessian.JavaEnum = iota + WOMAN +) + +var genderName = map[hessian.JavaEnum]string{ + MAN: "MAN", + WOMAN: "WOMAN", +} + +var genderValue = map[string]hessian.JavaEnum{ + "MAN": MAN, + "WOMAN": WOMAN, +} + +func (g TestEnumGender) JavaClassName() string { + return "com.ikurento.test.TestEnumGender" +} + +func (g TestEnumGender) String() string { + s, ok := genderName[hessian.JavaEnum(g)] + if ok { + return s + } + + return strconv.Itoa(int(g)) +} + +func (g TestEnumGender) EnumValue(s string) hessian.JavaEnum { + v, ok := genderValue[s] + if ok { + return v + } + + return hessian.InvalidJavaEnum +} + +func TestPackRequest(t *testing.T) { + bytes, err := packRequest(Service{ + Path: "test", + Interface: "ITest", + Version: "v1.0", + Method: "test", + Timeout: time.Second * 10, + }, DubboHeader{ + SerialID: 0, + Type: PackageRequest, + ID: 123, + }, []interface{}{1, 2}) + + assert.Nil(t, err) + + if bytes != nil { + t.Logf("pack request: %s", string(bytes)) + } +} + +func TestGetArgsTypeList(t *testing.T) { + type Test struct{} + str, err := getArgsTypeList([]interface{}{nil, 1, []int{2}, true, []bool{false}, "a", []string{"b"}, Test{}, &Test{}, []Test{}, map[string]Test{}, TestEnumGender(MAN)}) + assert.NoError(t, err) + assert.Equal(t, "VJ[JZ[ZLjava/lang/String;[Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;Ljava/util/Map;Lcom/ikurento/test/TestEnumGender;", str) +} + +func TestDescRegex(t *testing.T) { + results := DescRegex.FindAllString("Ljava/lang/String;", -1) + assert.Equal(t, 1, len(results)) + assert.Equal(t, "Ljava/lang/String;", results[0]) + + results = DescRegex.FindAllString("Ljava/lang/String;I", -1) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "Ljava/lang/String;", results[0]) + assert.Equal(t, "I", results[1]) + + results = DescRegex.FindAllString("ILjava/lang/String;", -1) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "I", results[0]) + assert.Equal(t, "Ljava/lang/String;", results[1]) + + results = DescRegex.FindAllString("ILjava/lang/String;IZ", -1) + assert.Equal(t, 4, len(results)) + assert.Equal(t, "I", results[0]) + assert.Equal(t, "Ljava/lang/String;", results[1]) + assert.Equal(t, "I", results[2]) + assert.Equal(t, "Z", results[3]) + + results = DescRegex.FindAllString("[Ljava/lang/String;[I", -1) + assert.Equal(t, 2, len(results)) + assert.Equal(t, "[Ljava/lang/String;", results[0]) + assert.Equal(t, "[I", results[1]) +} + +func TestIssue192(t *testing.T) { + type args struct { + origin map[interface{}]interface{} + } + tests := []struct { + name string + args args + want map[string]interface{} + }{ + { + name: "not null", + args: args{ + origin: map[interface{}]interface{}{ + "1": nil, + "2": "3", + "": "", + }, + }, + want: map[string]interface{}{ + "1": "", + "2": "3", + "": "", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ToMapStringInterface(tt.args.origin); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ToMapStringString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/protocol/dubbo/hessian2/hessian_response.go b/protocol/dubbo/hessian2/hessian_response.go new file mode 100644 index 0000000000000000000000000000000000000000..982960ed87e74b325687ac364c97a347efe6c38f --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_response.go @@ -0,0 +1,377 @@ +/* + * 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 hessian2 + +import ( + "encoding/binary" + "math" + "reflect" + "strconv" + "strings" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go-hessian2/java_exception" + perrors "github.com/pkg/errors" +) + +// DubboResponse dubbo response +type DubboResponse struct { + RspObj interface{} + Exception error + Attachments map[string]interface{} +} + +// NewResponse create a new DubboResponse +func NewResponse(rspObj interface{}, exception error, attachments map[string]interface{}) *DubboResponse { + if attachments == nil { + attachments = make(map[string]interface{}, 8) + } + return &DubboResponse{ + RspObj: rspObj, + Exception: exception, + Attachments: attachments, + } +} + +// EnsureResponse check body type, make sure it's a DubboResponse or package it as a DubboResponse +func EnsureResponse(body interface{}) *DubboResponse { + if res, ok := body.(*DubboResponse); ok { + return res + } + if exp, ok := body.(error); ok { + return NewResponse(nil, exp, nil) + } + return NewResponse(body, nil, nil) +} + +// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-remoting/dubbo-remoting-api/src/main/java/org/apache/dubbo/remoting/exchange/codec/ExchangeCodec.java#L256 +// hessian encode response +func packResponse(header DubboHeader, ret interface{}) ([]byte, error) { + var ( + byteArray []byte + ) + + response := EnsureResponse(ret) + + hb := header.Type == PackageHeartbeat + + // magic + if hb { + byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...) + } else { + byteArray = append(byteArray, DubboResponseHeaderBytes[:]...) + } + // set serialID, identify serialization types, eg: fastjson->6, hessian2->2 + byteArray[2] |= header.SerialID & SERIAL_MASK + // response status + if header.ResponseStatus != 0 { + byteArray[3] = header.ResponseStatus + } + + // request id + binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) + + // body + encoder := hessian.NewEncoder() + encoder.Append(byteArray[:HEADER_LENGTH]) + + if header.ResponseStatus == Response_OK { + if hb { + encoder.Encode(nil) + } else { + atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY]) + + var resWithException, resValue, resNullValue int32 + if atta { + resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS + resValue = RESPONSE_VALUE_WITH_ATTACHMENTS + resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS + } else { + resWithException = RESPONSE_WITH_EXCEPTION + resValue = RESPONSE_VALUE + resNullValue = RESPONSE_NULL_VALUE + } + + if response.Exception != nil { // throw error + encoder.Encode(resWithException) + if t, ok := response.Exception.(java_exception.Throwabler); ok { + encoder.Encode(t) + } else { + encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) + } + } else { + if response.RspObj == nil { + encoder.Encode(resNullValue) + } else { + encoder.Encode(resValue) + encoder.Encode(response.RspObj) // result + } + } + + if atta { + encoder.Encode(response.Attachments) // attachments + } + } + } else { + if response.Exception != nil { // throw error + encoder.Encode(response.Exception.Error()) + } else { + encoder.Encode(response.RspObj) + } + } + + byteArray = encoder.Buffer() + byteArray = hessian.EncNull(byteArray) // if not, "java client" will throw exception "unexpected end of file" + pkgLen := len(byteArray) + if pkgLen > int(DEFAULT_LEN) { // 8M + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + } + // byteArray{body length} + binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH)) + return byteArray, nil + +} + +// hessian decode response body +func unpackResponseBody(decoder *hessian.Decoder, resp interface{}) error { + // body + if decoder == nil { + return perrors.Errorf("@decoder is nil") + } + rspType, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + + response := EnsureResponse(resp) + + switch rspType { + case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: + expt, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + atta := ToMapStringInterface(v) + response.Attachments = atta + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + + if e, ok := expt.(error); ok { + response.Exception = e + } else { + response.Exception = perrors.Errorf("got exception: %+v", expt) + } + return nil + + case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS: + rsp, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + response.Attachments = ToMapStringInterface(v) + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + + // If the return value is nil, + // we should consider it normal + if rsp == nil { + return nil + } + + return perrors.WithStack(ReflectResponse(rsp, response.RspObj)) + + case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: + if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + atta := ToMapStringInterface(v) + response.Attachments = atta + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + return nil + } + + return nil +} + +// CopySlice copy from inSlice to outSlice +func CopySlice(inSlice, outSlice reflect.Value) error { + if inSlice.IsNil() { + return perrors.New("@in is nil") + } + if inSlice.Kind() != reflect.Slice { + return perrors.Errorf("@in is not slice, but %v", inSlice.Kind()) + } + + for outSlice.Kind() == reflect.Ptr { + outSlice = outSlice.Elem() + } + + size := inSlice.Len() + outSlice.Set(reflect.MakeSlice(outSlice.Type(), size, size)) + + for i := 0; i < size; i++ { + inSliceValue := inSlice.Index(i) + if !inSliceValue.Type().AssignableTo(outSlice.Index(i).Type()) { + return perrors.Errorf("in element type [%s] can not assign to out element type [%s]", + inSliceValue.Type().String(), outSlice.Type().String()) + } + outSlice.Index(i).Set(inSliceValue) + } + + return nil +} + +// CopyMap copy from in map to out map +func CopyMap(inMapValue, outMapValue reflect.Value) error { + if inMapValue.IsNil() { + return perrors.New("@in is nil") + } + if !inMapValue.CanInterface() { + return perrors.New("@in's Interface can not be used.") + } + if inMapValue.Kind() != reflect.Map { + return perrors.Errorf("@in is not map, but %v", inMapValue.Kind()) + } + + outMapType := hessian.UnpackPtrType(outMapValue.Type()) + hessian.SetValue(outMapValue, reflect.MakeMap(outMapType)) + + outKeyType := outMapType.Key() + + outMapValue = hessian.UnpackPtrValue(outMapValue) + outValueType := outMapValue.Type().Elem() + + for _, inKey := range inMapValue.MapKeys() { + inValue := inMapValue.MapIndex(inKey) + + if !inKey.Type().AssignableTo(outKeyType) { + return perrors.Errorf("in Key:{type:%s, value:%#v} can not assign to out Key:{type:%s} ", + inKey.Type().String(), inKey, outKeyType.String()) + } + if !inValue.Type().AssignableTo(outValueType) { + return perrors.Errorf("in Value:{type:%s, value:%#v} can not assign to out value:{type:%s}", + inValue.Type().String(), inValue, outValueType.String()) + } + outMapValue.SetMapIndex(inKey, inValue) + } + + return nil +} + +// ReflectResponse reflect return value +// TODO response object should not be copied again to another object, it should be the exact type of the object +func ReflectResponse(in interface{}, out interface{}) error { + if in == nil { + return perrors.Errorf("@in is nil") + } + + if out == nil { + return perrors.Errorf("@out is nil") + } + if reflect.TypeOf(out).Kind() != reflect.Ptr { + return perrors.Errorf("@out should be a pointer") + } + + inValue := hessian.EnsurePackValue(in) + outValue := hessian.EnsurePackValue(out) + + outType := outValue.Type().String() + if outType == "interface {}" || outType == "*interface {}" { + hessian.SetValue(outValue, inValue) + return nil + } + + switch inValue.Type().Kind() { + case reflect.Slice, reflect.Array: + return CopySlice(inValue, outValue) + case reflect.Map: + return CopyMap(inValue, outValue) + default: + hessian.SetValue(outValue, inValue) + } + + return nil +} + +var versionInt = make(map[string]int) + +// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96 +// isSupportResponseAttachment is for compatibility among some dubbo version +func isSupportResponseAttachment(ver interface{}) bool { + version, ok := ver.(string) + if !ok || len(version) == 0 { + return false + } + + v, ok := versionInt[version] + if !ok { + v = version2Int(version) + if v == -1 { + return false + } + } + + if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2 + return false + } + return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT +} + +func version2Int(ver interface{}) int { + version, ok := ver.(string) + if !ok || len(version) == 0 { + return 0 + } + var v = 0 + varr := strings.Split(version, ".") + length := len(varr) + for key, value := range varr { + v0, err := strconv.Atoi(value) + if err != nil { + return -1 + } + v += v0 * int(math.Pow10((length-key-1)*2)) + } + if length == 3 { + return v * 100 + } + return v +} diff --git a/protocol/dubbo/hessian2/hessian_response_test.go b/protocol/dubbo/hessian2/hessian_response_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f5c84baa90b3b31b271979cb1107503facac71d9 --- /dev/null +++ b/protocol/dubbo/hessian2/hessian_response_test.go @@ -0,0 +1,225 @@ +/* + * 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 hessian2 + +import ( + "reflect" + "testing" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/stretchr/testify/assert" +) + +func doTestReflectResponse(t *testing.T, in interface{}, out interface{}) { + err := ReflectResponse(in, out) + if err != nil { + t.Error(err) + t.FailNow() + } + + result := hessian.UnpackPtrValue(reflect.ValueOf(out)).Interface() + + equal := reflect.DeepEqual(in, result) + if !equal { + t.Errorf("expect [%v]: %v, but got [%v]: %v", reflect.TypeOf(in), in, reflect.TypeOf(result), result) + } +} + +func TestReflectResponse(t *testing.T) { + var b bool + doTestReflectResponse(t, true, &b) + doTestReflectResponse(t, false, &b) + + var i int + doTestReflectResponse(t, 123, &i) + doTestReflectResponse(t, 234, &i) + + var i16 int16 + doTestReflectResponse(t, int16(456), &i16) + + var i64 int64 + doTestReflectResponse(t, int64(789), &i64) + + var s string + doTestReflectResponse(t, "hello world", &s) + + type rr struct { + Name string + Num int + } + + var r1 rr + doTestReflectResponse(t, rr{"dubbogo", 32}, &r1) + + // ------ map test ------- + m1 := make(map[interface{}]interface{}) + var m1r map[interface{}]interface{} + m1["hello"] = "world" + m1[1] = "go" + m1["dubbo"] = 666 + doTestReflectResponse(t, m1, &m1r) + + m2 := make(map[string]string) + var m2r map[string]string + m2["hello"] = "world" + m2["dubbo"] = "666" + doTestReflectResponse(t, m2, &m2r) + + m3 := make(map[string]rr) + var m3r map[string]rr + m3["dubbo"] = rr{"hello", 123} + m3["go"] = rr{"world", 456} + doTestReflectResponse(t, m3, &m3r) + + // ------ slice test ------- + s1 := []string{"abc", "def", "hello", "world"} + var s1r []string + doTestReflectResponse(t, s1, &s1r) + + s2 := []rr{rr{"dubbo", 666}, rr{"go", 999}} + var s2r []rr + doTestReflectResponse(t, s2, &s2r) + + s3 := []interface{}{rr{"dubbo", 666}, 123, "hello"} + var s3r []interface{} + doTestReflectResponse(t, s3, &s3r) + + // ------ interface test ------- + in1 := []interface{}{rr{"dubbo", 666}, 123, "hello"} + var inr1 *interface{} + doTestReflectResponse(t, in1, reflect.New(reflect.TypeOf(inr1).Elem()).Interface()) + + in2 := make(map[string]rr) + var inr2 map[string]rr + m3["dubbo"] = rr{"hello", 123} + m3["go"] = rr{"world", 456} + doTestReflectResponse(t, in2, &inr2) +} + +// separately test copy normal map to map[interface{}]interface{} +func TestCopyMap(t *testing.T) { + type rr struct { + Name string + Num int + } + + m3 := make(map[string]rr) + var m3r map[interface{}]interface{} + r1 := rr{"hello", 123} + r2 := rr{"world", 456} + m3["dubbo"] = r1 + m3["go"] = r2 + + err := ReflectResponse(m3, &m3r) + if err != nil { + t.Error(err) + t.FailNow() + } + + assert.Equal(t, 2, len(m3r)) + + rr1, ok := m3r["dubbo"] + assert.True(t, ok) + assert.True(t, reflect.DeepEqual(r1, rr1)) + + rr2, ok := m3r["go"] + assert.True(t, ok) + assert.True(t, reflect.DeepEqual(r2, rr2)) +} + +// separately test copy normal slice to []interface{} +func TestCopySlice(t *testing.T) { + type rr struct { + Name string + Num int + } + + r1 := rr{"hello", 123} + r2 := rr{"world", 456} + + s1 := []rr{r1, r2} + var s1r []interface{} + + err := ReflectResponse(s1, &s1r) + if err != nil { + t.Error(err) + t.FailNow() + } + + assert.Equal(t, 2, len(s1r)) + assert.True(t, reflect.DeepEqual(r1, s1r[0])) + assert.True(t, reflect.DeepEqual(r2, s1r[1])) +} + +func TestIsSupportResponseAttachment(t *testing.T) { + is := isSupportResponseAttachment("2.X") + assert.False(t, is) + + is = isSupportResponseAttachment("2.0.10") + assert.False(t, is) + + is = isSupportResponseAttachment("2.5.3") + assert.False(t, is) + + is = isSupportResponseAttachment("2.6.2") + assert.False(t, is) + + is = isSupportResponseAttachment("1.5.5") + assert.False(t, is) + + is = isSupportResponseAttachment("0.0.0") + assert.False(t, is) + + is = isSupportResponseAttachment("2.0.2") + assert.True(t, is) + + is = isSupportResponseAttachment("2.7.2") + assert.True(t, is) +} + +func TestVersion2Int(t *testing.T) { + v := version2Int("2.1.3") + assert.Equal(t, 2010300, v) + + v = version2Int("22.11.33") + assert.Equal(t, 22113300, v) + + v = version2Int("222.111.333") + assert.Equal(t, 223143300, v) + + v = version2Int("220.110.333") + assert.Equal(t, 221133300, v) + + v = version2Int("229.119.333") + assert.Equal(t, 230223300, v) + + v = version2Int("2222.1111.3333") + assert.Equal(t, 2233443300, v) + + v = version2Int("2.11") + assert.Equal(t, 211, v) + + v = version2Int("2.1.3.4") + assert.Equal(t, 2010304, v) + + v = version2Int("2.1.3.4.5") + assert.Equal(t, 201030405, v) + +} diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go new file mode 100644 index 0000000000000000000000000000000000000000..c139f3547b2af5196a38be9bc83f49251bfcf043 --- /dev/null +++ b/protocol/dubbo/impl/codec.go @@ -0,0 +1,291 @@ +/* + * 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 impl + +import ( + "bufio" + "encoding/binary" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/remoting" +) + +type ProtocolCodec struct { + reader *bufio.Reader + pkgType PackageType + bodyLen int + serializer Serializer + headerRead bool +} + +func (c *ProtocolCodec) ReadHeader(header *DubboHeader) error { + var err error + if c.reader.Size() < HEADER_LENGTH { + return hessian.ErrHeaderNotEnough + } + buf, err := c.reader.Peek(HEADER_LENGTH) + if err != nil { // this is impossible + return perrors.WithStack(err) + } + _, err = c.reader.Discard(HEADER_LENGTH) + if err != nil { // this is impossible + return perrors.WithStack(err) + } + + //// read header + if buf[0] != MAGIC_HIGH && buf[1] != MAGIC_LOW { + return hessian.ErrIllegalPackage + } + + // Header{serialization id(5 bit), event, two way, req/response} + if header.SerialID = buf[2] & SERIAL_MASK; header.SerialID == Zero { + return perrors.Errorf("serialization ID:%v", header.SerialID) + } + + flag := buf[2] & FLAG_EVENT + if flag != Zero { + header.Type |= PackageHeartbeat + } + flag = buf[2] & FLAG_REQUEST + if flag != Zero { + header.Type |= PackageRequest + flag = buf[2] & FLAG_TWOWAY + if flag != Zero { + header.Type |= PackageRequest_TwoWay + } + } else { + header.Type |= PackageResponse + header.ResponseStatus = buf[3] + if header.ResponseStatus != Response_OK { + header.Type |= PackageResponse_Exception + } + } + + // Header{req id} + header.ID = int64(binary.BigEndian.Uint64(buf[4:])) + + // Header{body len} + header.BodyLen = int(binary.BigEndian.Uint32(buf[12:])) + if header.BodyLen < 0 { + return hessian.ErrIllegalPackage + } + + c.pkgType = header.Type + c.bodyLen = header.BodyLen + + if c.reader.Buffered() < c.bodyLen { + return hessian.ErrBodyNotEnough + } + c.headerRead = true + return perrors.WithStack(err) +} + +func (c *ProtocolCodec) EncodeHeader(p DubboPackage) []byte { + header := p.Header + bs := make([]byte, 0) + switch header.Type { + case PackageHeartbeat: + if header.ResponseStatus == Zero { + bs = append(bs, hessian.DubboRequestHeartbeatHeader[:]...) + } else { + bs = append(bs, hessian.DubboResponseHeartbeatHeader[:]...) + } + case PackageResponse: + bs = append(bs, hessian.DubboResponseHeaderBytes[:]...) + if header.ResponseStatus != 0 { + bs[3] = header.ResponseStatus + } + case PackageRequest_TwoWay: + bs = append(bs, hessian.DubboRequestHeaderBytesTwoWay[:]...) + } + bs[2] |= header.SerialID & hessian.SERIAL_MASK + binary.BigEndian.PutUint64(bs[4:], uint64(header.ID)) + return bs +} + +func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) { + // header + if c.serializer == nil { + return nil, perrors.New("serializer should not be nil") + } + header := p.Header + switch header.Type { + case PackageHeartbeat: + if header.ResponseStatus == Zero { + return packRequest(p, c.serializer) + } + return packResponse(p, c.serializer) + + case PackageRequest, PackageRequest_TwoWay: + return packRequest(p, c.serializer) + + case PackageResponse: + return packResponse(p, c.serializer) + + default: + return nil, perrors.Errorf("Unrecognised message type: %v", header.Type) + } +} + +func (c *ProtocolCodec) Decode(p *DubboPackage) error { + if !c.headerRead { + if err := c.ReadHeader(&p.Header); err != nil { + return err + } + } + body, err := c.reader.Peek(p.GetBodyLen()) + if err != nil { + return err + } + if p.IsResponseWithException() { + logger.Infof("response with exception: %+v", p.Header) + decoder := hessian.NewDecoder(body) + exception, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + rsp, ok := p.Body.(*ResponsePayload) + if !ok { + return perrors.Errorf("java exception:%s", exception.(string)) + } + rsp.Exception = perrors.Errorf("java exception:%s", exception.(string)) + return nil + } else if p.IsHeartBeat() { + // heartbeat no need to unmarshal contents + return nil + } + if c.serializer == nil { + return perrors.New("Codec serializer is nil") + } + if p.IsResponse() { + p.Body = &ResponsePayload{ + RspObj: remoting.GetPendingResponse(remoting.SequenceType(p.Header.ID)).Reply, + } + } + return c.serializer.Unmarshal(body, p) +} + +func (c *ProtocolCodec) SetSerializer(serializer Serializer) { + c.serializer = serializer +} + +func packRequest(p DubboPackage, serializer Serializer) ([]byte, error) { + var ( + byteArray []byte + pkgLen int + ) + + header := p.Header + + ////////////////////////////////////////// + // byteArray + ////////////////////////////////////////// + // magic + switch header.Type { + case PackageHeartbeat: + byteArray = append(byteArray, DubboRequestHeartbeatHeader[:]...) + case PackageRequest_TwoWay: + byteArray = append(byteArray, DubboRequestHeaderBytesTwoWay[:]...) + default: + byteArray = append(byteArray, DubboRequestHeaderBytes[:]...) + } + + // serialization id, two way flag, event, request/response flag + // SerialID is id of serialization approach in java dubbo + byteArray[2] |= header.SerialID & SERIAL_MASK + // request id + binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) + + ////////////////////////////////////////// + // body + ////////////////////////////////////////// + if p.IsHeartBeat() { + byteArray = append(byteArray, byte('N')) + pkgLen = 1 + } else { + body, err := serializer.Marshal(p) + if err != nil { + return nil, err + } + pkgLen = len(body) + if pkgLen > int(DEFAULT_LEN) { // 8M + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + } + byteArray = append(byteArray, body...) + } + binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen)) + return byteArray, nil +} + +func packResponse(p DubboPackage, serializer Serializer) ([]byte, error) { + var ( + byteArray []byte + ) + header := p.Header + hb := p.IsHeartBeat() + + // magic + if hb { + byteArray = append(byteArray, DubboResponseHeartbeatHeader[:]...) + } else { + byteArray = append(byteArray, DubboResponseHeaderBytes[:]...) + } + // set serialID, identify serialization types, eg: fastjson->6, hessian2->2 + byteArray[2] |= header.SerialID & SERIAL_MASK + // response status + if header.ResponseStatus != 0 { + byteArray[3] = header.ResponseStatus + } + + // request id + binary.BigEndian.PutUint64(byteArray[4:], uint64(header.ID)) + + // body + body, err := serializer.Marshal(p) + if err != nil { + return nil, err + } + + pkgLen := len(body) + if pkgLen > int(DEFAULT_LEN) { // 8M + return nil, perrors.Errorf("Data length %d too large, max payload %d", pkgLen, DEFAULT_LEN) + } + // byteArray{body length} + binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen)) + byteArray = append(byteArray, body...) + return byteArray, nil +} + +func NewDubboCodec(reader *bufio.Reader) *ProtocolCodec { + s, _ := GetSerializerById(constant.S_Hessian2) + return &ProtocolCodec{ + reader: reader, + pkgType: 0, + bodyLen: 0, + headerRead: false, + serializer: s.(Serializer), + } +} diff --git a/protocol/dubbo/codec_test.go b/protocol/dubbo/impl/codec_test.go similarity index 51% rename from protocol/dubbo/codec_test.go rename to protocol/dubbo/impl/codec_test.go index c2ca443637e23101679770e464f49e0cbdeab2a9..1c379282383c90ef27628af19ddaaafb2a4db9a6 100644 --- a/protocol/dubbo/codec_test.go +++ b/protocol/dubbo/impl/codec_test.go @@ -15,42 +15,46 @@ * limitations under the License. */ -package dubbo +package impl import ( - "bytes" "testing" "time" ) import ( - hessian "github.com/apache/dubbo-go-hessian2" - perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) -func TestDubboPackageMarshalAndUnmarshal(t *testing.T) { - pkg := &DubboPackage{} +import ( + "github.com/apache/dubbo-go/common/constant" +) + +func TestDubboPackage_MarshalAndUnmarshal(t *testing.T) { + pkg := NewDubboPackage(nil) pkg.Body = []interface{}{"a"} - pkg.Header.Type = hessian.PackageHeartbeat - pkg.Header.SerialID = byte(S_Dubbo) + pkg.Header.Type = PackageHeartbeat + pkg.Header.SerialID = constant.S_Hessian2 pkg.Header.ID = 10086 + pkg.SetSerializer(HessianSerializer{}) // heartbeat data, err := pkg.Marshal() assert.NoError(t, err) - pkgres := &DubboPackage{} + pkgres := NewDubboPackage(data) + pkgres.SetSerializer(HessianSerializer{}) + pkgres.Body = []interface{}{} - err = pkgres.Unmarshal(data) + err = pkgres.Unmarshal() assert.NoError(t, err) - assert.Equal(t, hessian.PackageHeartbeat|hessian.PackageRequest|hessian.PackageRequest_TwoWay, pkgres.Header.Type) - assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID) + assert.Equal(t, PackageHeartbeat|PackageRequest|PackageRequest_TwoWay, pkgres.Header.Type) + assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID) assert.Equal(t, int64(10086), pkgres.Header.ID) assert.Equal(t, 0, len(pkgres.Body.([]interface{}))) // request - pkg.Header.Type = hessian.PackageRequest + pkg.Header.Type = PackageRequest pkg.Service.Interface = "Service" pkg.Service.Path = "path" pkg.Service.Version = "2.6" @@ -59,25 +63,27 @@ func TestDubboPackageMarshalAndUnmarshal(t *testing.T) { data, err = pkg.Marshal() assert.NoError(t, err) - pkgres = &DubboPackage{} + pkgres = NewDubboPackage(data) + pkgres.SetSerializer(HessianSerializer{}) pkgres.Body = make([]interface{}, 7) - err = pkgres.Unmarshal(data) + err = pkgres.Unmarshal() + reassembleBody := pkgres.GetBody().(map[string]interface{}) assert.NoError(t, err) - assert.Equal(t, hessian.PackageRequest, pkgres.Header.Type) - assert.Equal(t, byte(S_Dubbo), pkgres.Header.SerialID) + assert.Equal(t, PackageRequest, pkgres.Header.Type) + assert.Equal(t, constant.S_Hessian2, pkgres.Header.SerialID) assert.Equal(t, int64(10086), pkgres.Header.ID) - assert.Equal(t, "2.0.2", pkgres.Body.([]interface{})[0]) - assert.Equal(t, "path", pkgres.Body.([]interface{})[1]) - assert.Equal(t, "2.6", pkgres.Body.([]interface{})[2]) - assert.Equal(t, "Method", pkgres.Body.([]interface{})[3]) - assert.Equal(t, "Ljava/lang/String;", pkgres.Body.([]interface{})[4]) - assert.Equal(t, []interface{}{"a"}, pkgres.Body.([]interface{})[5]) - assert.Equal(t, map[string]string{"dubbo": "2.0.2", "interface": "Service", "path": "path", "timeout": "1000", "version": "2.6"}, pkgres.Body.([]interface{})[6]) -} - -func TestIssue380(t *testing.T) { - pkg := &DubboPackage{} - buf := bytes.NewBuffer([]byte("hello")) - err := pkg.Unmarshal(buf) - assert.True(t, perrors.Cause(err) == hessian.ErrHeaderNotEnough) + assert.Equal(t, "2.0.2", reassembleBody["dubboVersion"].(string)) + assert.Equal(t, "path", pkgres.Service.Path) + assert.Equal(t, "2.6", pkgres.Service.Version) + assert.Equal(t, "Method", pkgres.Service.Method) + assert.Equal(t, "Ljava/lang/String;", reassembleBody["argsTypes"].(string)) + assert.Equal(t, []interface{}{"a"}, reassembleBody["args"]) + tmpData := map[string]interface{}{ + "dubbo": "2.0.2", + "interface": "Service", + "path": "path", + "timeout": "1000", + "version": "2.6", + } + assert.Equal(t, tmpData, reassembleBody["attachments"]) } diff --git a/protocol/dubbo/impl/const.go b/protocol/dubbo/impl/const.go new file mode 100644 index 0000000000000000000000000000000000000000..70d8bae6cad63d436f5d9f1ef69c397ee8a052f3 --- /dev/null +++ b/protocol/dubbo/impl/const.go @@ -0,0 +1,252 @@ +/* + * 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 impl + +import ( + "reflect" + "regexp" + + "github.com/pkg/errors" +) + +const ( + DUBBO = "dubbo" +) + +const ( + mask = byte(127) + flag = byte(128) +) + +const ( + // Zero : byte zero + Zero = byte(0x00) +) + +// constansts +const ( + TAG_READ = int32(-1) + ASCII_GAP = 32 + CHUNK_SIZE = 4096 + BC_BINARY = byte('B') // final chunk + BC_BINARY_CHUNK = byte('A') // non-final chunk + + BC_BINARY_DIRECT = byte(0x20) // 1-byte length binary + BINARY_DIRECT_MAX = byte(0x0f) + BC_BINARY_SHORT = byte(0x34) // 2-byte length binary + BINARY_SHORT_MAX = 0x3ff // 0-1023 binary + + BC_DATE = byte(0x4a) // 64-bit millisecond UTC date + BC_DATE_MINUTE = byte(0x4b) // 32-bit minute UTC date + + BC_DOUBLE = byte('D') // IEEE 64-bit double + + BC_DOUBLE_ZERO = byte(0x5b) + BC_DOUBLE_ONE = byte(0x5c) + BC_DOUBLE_BYTE = byte(0x5d) + BC_DOUBLE_SHORT = byte(0x5e) + BC_DOUBLE_MILL = byte(0x5f) + + BC_FALSE = byte('F') // boolean false + + BC_INT = byte('I') // 32-bit int + + INT_DIRECT_MIN = -0x10 + INT_DIRECT_MAX = byte(0x2f) + BC_INT_ZERO = byte(0x90) + + INT_BYTE_MIN = -0x800 + INT_BYTE_MAX = 0x7ff + BC_INT_BYTE_ZERO = byte(0xc8) + + BC_END = byte('Z') + + INT_SHORT_MIN = -0x40000 + INT_SHORT_MAX = 0x3ffff + BC_INT_SHORT_ZERO = byte(0xd4) + + BC_LIST_VARIABLE = byte(0x55) + BC_LIST_FIXED = byte('V') + BC_LIST_VARIABLE_UNTYPED = byte(0x57) + BC_LIST_FIXED_UNTYPED = byte(0x58) + _listFixedTypedLenTagMin = byte(0x70) + _listFixedTypedLenTagMax = byte(0x77) + _listFixedUntypedLenTagMin = byte(0x78) + _listFixedUntypedLenTagMax = byte(0x7f) + + BC_LIST_DIRECT = byte(0x70) + BC_LIST_DIRECT_UNTYPED = byte(0x78) + LIST_DIRECT_MAX = byte(0x7) + + BC_LONG = byte('L') // 64-bit signed integer + LONG_DIRECT_MIN = -0x08 + LONG_DIRECT_MAX = byte(0x0f) + BC_LONG_ZERO = byte(0xe0) + + LONG_BYTE_MIN = -0x800 + LONG_BYTE_MAX = 0x7ff + BC_LONG_BYTE_ZERO = byte(0xf8) + + LONG_SHORT_MIN = -0x40000 + LONG_SHORT_MAX = 0x3ffff + BC_LONG_SHORT_ZERO = byte(0x3c) + + BC_LONG_INT = byte(0x59) + + BC_MAP = byte('M') + BC_MAP_UNTYPED = byte('H') + + BC_NULL = byte('N') // x4e + + BC_OBJECT = byte('O') + BC_OBJECT_DEF = byte('C') + + BC_OBJECT_DIRECT = byte(0x60) + OBJECT_DIRECT_MAX = byte(0x0f) + + BC_REF = byte(0x51) + + BC_STRING = byte('S') // final string + BC_STRING_CHUNK = byte('R') // non-final string + + BC_STRING_DIRECT = byte(0x00) + STRING_DIRECT_MAX = byte(0x1f) + BC_STRING_SHORT = byte(0x30) + STRING_SHORT_MAX = 0x3ff + + BC_TRUE = byte('T') + + P_PACKET_CHUNK = byte(0x4f) + P_PACKET = byte('P') + + P_PACKET_DIRECT = byte(0x80) + PACKET_DIRECT_MAX = byte(0x7f) + + P_PACKET_SHORT = byte(0x70) + PACKET_SHORT_MAX = 0xfff + ARRAY_STRING = "[string" + ARRAY_INT = "[int" + ARRAY_DOUBLE = "[double" + ARRAY_FLOAT = "[float" + ARRAY_BOOL = "[boolean" + ARRAY_LONG = "[long" + + PATH_KEY = "path" + GROUP_KEY = "group" + INTERFACE_KEY = "interface" + VERSION_KEY = "version" + TIMEOUT_KEY = "timeout" + + STRING_NIL = "" + STRING_TRUE = "true" + STRING_FALSE = "false" + STRING_ZERO = "0.0" + STRING_ONE = "1.0" +) + +// ResponsePayload related consts +const ( + Response_OK byte = 20 + Response_CLIENT_TIMEOUT byte = 30 + Response_SERVER_TIMEOUT byte = 31 + Response_BAD_REQUEST byte = 40 + Response_BAD_RESPONSE byte = 50 + Response_SERVICE_NOT_FOUND byte = 60 + Response_SERVICE_ERROR byte = 70 + Response_SERVER_ERROR byte = 80 + Response_CLIENT_ERROR byte = 90 + + // According to "java dubbo" There are two cases of response: + // 1. with attachments + // 2. no attachments + RESPONSE_WITH_EXCEPTION int32 = 0 + RESPONSE_VALUE int32 = 1 + RESPONSE_NULL_VALUE int32 = 2 + RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS int32 = 3 + RESPONSE_VALUE_WITH_ATTACHMENTS int32 = 4 + RESPONSE_NULL_VALUE_WITH_ATTACHMENTS int32 = 5 +) + +/** + * the dubbo protocol header length is 16 Bytes. + * the first 2 Bytes is magic code '0xdabb' + * the next 1 Byte is message flags, in which its 16-20 bit is serial id, 21 for event, 22 for two way, 23 for request/response flag + * the next 1 Bytes is response state. + * the next 8 Bytes is package DI. + * the next 4 Bytes is package length. + **/ +const ( + // header length. + HEADER_LENGTH = 16 + + // magic header + MAGIC = uint16(0xdabb) + MAGIC_HIGH = byte(0xda) + MAGIC_LOW = byte(0xbb) + + // message flag. + FLAG_REQUEST = byte(0x80) + FLAG_TWOWAY = byte(0x40) + FLAG_EVENT = byte(0x20) // for heartbeat + SERIAL_MASK = 0x1f + + DUBBO_VERSION = "2.5.4" + DUBBO_VERSION_KEY = "dubbo" + DEFAULT_DUBBO_PROTOCOL_VERSION = "2.0.2" // Dubbo RPC protocol version, for compatibility, it must not be between 2.0.10 ~ 2.6.2 + LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT = 2000200 + DEFAULT_LEN = 8388608 // 8 * 1024 * 1024 default body max length +) + +// regular +const ( + JAVA_IDENT_REGEX = "(?:[_$a-zA-Z][_$a-zA-Z0-9]*)" + CLASS_DESC = "(?:L" + JAVA_IDENT_REGEX + "(?:\\/" + JAVA_IDENT_REGEX + ")*;)" + ARRAY_DESC = "(?:\\[+(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "))" + DESC_REGEX = "(?:(?:[VZBCDFIJS])|" + CLASS_DESC + "|" + ARRAY_DESC + ")" +) + +// Dubbo request response related consts +var ( + DubboRequestHeaderBytesTwoWay = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY} + DubboRequestHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST} + DubboResponseHeaderBytes = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, Zero, Response_OK} + DubboRequestHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_REQUEST | FLAG_TWOWAY | FLAG_EVENT} + DubboResponseHeartbeatHeader = [HEADER_LENGTH]byte{MAGIC_HIGH, MAGIC_LOW, FLAG_EVENT} +) + +// Error part +var ( + ErrHeaderNotEnough = errors.New("header buffer too short") + ErrBodyNotEnough = errors.New("body buffer too short") + ErrJavaException = errors.New("got java exception") + ErrIllegalPackage = errors.New("illegal package!") +) + +// DescRegex ... +var DescRegex, _ = regexp.Compile(DESC_REGEX) + +var NilValue = reflect.Zero(reflect.TypeOf((*interface{})(nil)).Elem()) + +// Body map keys +var ( + DubboVersionKey = "dubboVersion" + ArgsTypesKey = "argsTypes" + ArgsKey = "args" + ServiceKey = "service" + AttachmentsKey = "attachments" +) diff --git a/protocol/dubbo/impl/hessian.go b/protocol/dubbo/impl/hessian.go new file mode 100644 index 0000000000000000000000000000000000000000..b686d5728d026fdb1e37180d5a9d24d32bf4b7bc --- /dev/null +++ b/protocol/dubbo/impl/hessian.go @@ -0,0 +1,526 @@ +/* + * 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 impl + +import ( + "math" + "reflect" + "strconv" + "strings" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go-hessian2/java_exception" + 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" +) + +type Object interface{} + +type HessianSerializer struct { +} + +func (h HessianSerializer) Marshal(p DubboPackage) ([]byte, error) { + encoder := hessian.NewEncoder() + if p.IsRequest() { + return marshalRequest(encoder, p) + } + return marshalResponse(encoder, p) +} + +func (h HessianSerializer) Unmarshal(input []byte, p *DubboPackage) error { + if p.IsHeartBeat() { + return nil + } + if p.IsRequest() { + return unmarshalRequestBody(input, p) + } + return unmarshalResponseBody(input, p) +} + +func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { + header := p.Header + response := EnsureResponsePayload(p.Body) + if header.ResponseStatus == Response_OK { + if p.IsHeartBeat() { + encoder.Encode(nil) + } else { + var version string + if attachmentVersion, ok := response.Attachments[DUBBO_VERSION_KEY]; ok { + version = attachmentVersion.(string) + } + atta := isSupportResponseAttachment(version) + + var resWithException, resValue, resNullValue int32 + if atta { + resWithException = RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS + resValue = RESPONSE_VALUE_WITH_ATTACHMENTS + resNullValue = RESPONSE_NULL_VALUE_WITH_ATTACHMENTS + } else { + resWithException = RESPONSE_WITH_EXCEPTION + resValue = RESPONSE_VALUE + resNullValue = RESPONSE_NULL_VALUE + } + + if response.Exception != nil { // throw error + encoder.Encode(resWithException) + if t, ok := response.Exception.(java_exception.Throwabler); ok { + encoder.Encode(t) + } else { + encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) + } + } else { + if response.RspObj == nil { + encoder.Encode(resNullValue) + } else { + encoder.Encode(resValue) + encoder.Encode(response.RspObj) // result + } + } + + if atta { + encoder.Encode(response.Attachments) // attachments + } + } + } else { + if response.Exception != nil { // throw error + encoder.Encode(response.Exception.Error()) + } else { + encoder.Encode(response.RspObj) + } + } + bs := encoder.Buffer() + // encNull + bs = append(bs, byte('N')) + return bs, nil +} + +func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { + service := p.Service + request := EnsureRequestPayload(p.Body) + encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) + encoder.Encode(service.Path) + encoder.Encode(service.Version) + encoder.Encode(service.Method) + + args, ok := request.Params.([]interface{}) + + if !ok { + logger.Infof("request args are: %+v", request.Params) + return nil, perrors.Errorf("@params is not of type: []interface{}") + } + types, err := getArgsTypeList(args) + if err != nil { + return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args) + } + encoder.Encode(types) + for _, v := range args { + encoder.Encode(v) + } + + request.Attachments[PATH_KEY] = service.Path + request.Attachments[VERSION_KEY] = service.Version + if len(service.Group) > 0 { + request.Attachments[GROUP_KEY] = service.Group + } + if len(service.Interface) > 0 { + request.Attachments[INTERFACE_KEY] = service.Interface + } + if service.Timeout != 0 { + request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond)) + } + + encoder.Encode(request.Attachments) + return encoder.Buffer(), nil + +} + +var versionInt = make(map[string]int) + +// https://github.com/apache/dubbo/blob/dubbo-2.7.1/dubbo-common/src/main/java/org/apache/dubbo/common/Version.java#L96 +// isSupportResponseAttachment is for compatibility among some dubbo version +func isSupportResponseAttachment(version string) bool { + if version == "" { + return false + } + + v, ok := versionInt[version] + if !ok { + v = version2Int(version) + if v == -1 { + return false + } + } + + if v >= 2001000 && v <= 2060200 { // 2.0.10 ~ 2.6.2 + return false + } + return v >= LOWEST_VERSION_FOR_RESPONSE_ATTACHMENT +} + +func version2Int(version string) int { + var v = 0 + varr := strings.Split(version, ".") + length := len(varr) + for key, value := range varr { + v0, err := strconv.Atoi(value) + if err != nil { + return -1 + } + v += v0 * int(math.Pow10((length-key-1)*2)) + } + if length == 3 { + return v * 100 + } + return v +} + +func unmarshalRequestBody(body []byte, p *DubboPackage) error { + if p.Body == nil { + p.SetBody(make([]interface{}, 7)) + } + decoder := hessian.NewDecoder(body) + var ( + err error + dubboVersion, target, serviceVersion, method, argsTypes interface{} + args []interface{} + ) + req, ok := p.Body.([]interface{}) + if !ok { + return perrors.Errorf("@reqObj is not of type: []interface{}") + } + dubboVersion, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[0] = dubboVersion + + target, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[1] = target + + serviceVersion, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[2] = serviceVersion + + method, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[3] = method + + argsTypes, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + req[4] = argsTypes + + ats := hessian.DescRegex.FindAllString(argsTypes.(string), -1) + var arg interface{} + for i := 0; i < len(ats); i++ { + arg, err = decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + args = append(args, arg) + } + req[5] = args + + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + + if v, ok := attachments.(map[interface{}]interface{}); ok { + v[DUBBO_VERSION_KEY] = dubboVersion + req[6] = ToMapStringInterface(v) + buildServerSidePackageBody(p) + return nil + } + return perrors.Errorf("get wrong attachments: %+v", attachments) +} + +func unmarshalResponseBody(body []byte, p *DubboPackage) error { + decoder := hessian.NewDecoder(body) + rspType, err := decoder.Decode() + if p.Body == nil { + p.SetBody(&ResponsePayload{}) + } + if err != nil { + return perrors.WithStack(err) + } + response := EnsureResponsePayload(p.Body) + + switch rspType { + case RESPONSE_WITH_EXCEPTION, RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS: + expt, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if rspType == RESPONSE_WITH_EXCEPTION_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + atta := ToMapStringInterface(v) + response.Attachments = atta + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + + if e, ok := expt.(error); ok { + response.Exception = e + } else { + response.Exception = perrors.Errorf("got exception: %+v", expt) + } + return nil + + case RESPONSE_VALUE, RESPONSE_VALUE_WITH_ATTACHMENTS: + rsp, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if rspType == RESPONSE_VALUE_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + atta := ToMapStringInterface(v) + response.Attachments = atta + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + + return perrors.WithStack(hessian.ReflectResponse(rsp, response.RspObj)) + + case RESPONSE_NULL_VALUE, RESPONSE_NULL_VALUE_WITH_ATTACHMENTS: + if rspType == RESPONSE_NULL_VALUE_WITH_ATTACHMENTS { + attachments, err := decoder.Decode() + if err != nil { + return perrors.WithStack(err) + } + if v, ok := attachments.(map[interface{}]interface{}); ok { + atta := ToMapStringInterface(v) + response.Attachments = atta + } else { + return perrors.Errorf("get wrong attachments: %+v", attachments) + } + } + return nil + } + return nil +} + +func buildServerSidePackageBody(pkg *DubboPackage) { + req := pkg.GetBody().([]interface{}) // length of body should be 7 + if len(req) > 0 { + var dubboVersion, argsTypes string + var args []interface{} + var attachments map[string]interface{} + svc := Service{} + if req[0] != nil { + dubboVersion = req[0].(string) + } + if req[1] != nil { + svc.Path = req[1].(string) + } + if req[2] != nil { + svc.Version = req[2].(string) + } + if req[3] != nil { + svc.Method = req[3].(string) + } + if req[4] != nil { + argsTypes = req[4].(string) + } + if req[5] != nil { + args = req[5].([]interface{}) + } + if req[6] != nil { + attachments = req[6].(map[string]interface{}) + } + if svc.Path == "" && attachments[constant.PATH_KEY] != nil && len(attachments[constant.PATH_KEY].(string)) > 0 { + svc.Path = attachments[constant.PATH_KEY].(string) + } + if _, ok := attachments[constant.INTERFACE_KEY]; ok { + svc.Interface = attachments[constant.INTERFACE_KEY].(string) + } else { + svc.Interface = svc.Path + } + if _, ok := attachments[constant.GROUP_KEY]; ok { + svc.Group = attachments[constant.GROUP_KEY].(string) + } + pkg.SetService(svc) + pkg.SetBody(map[string]interface{}{ + "dubboVersion": dubboVersion, + "argsTypes": argsTypes, + "args": args, + "service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key + "attachments": attachments, + }) + } +} + +func getArgsTypeList(args []interface{}) (string, error) { + var ( + typ string + types string + ) + + for i := range args { + typ = getArgType(args[i]) + if typ == "" { + return types, perrors.Errorf("cat not get arg %#v type", args[i]) + } + if !strings.Contains(typ, ".") { + types += typ + } else if strings.Index(typ, "[") == 0 { + types += strings.Replace(typ, ".", "/", -1) + } else { + // java.util.List -> Ljava/util/List; + types += "L" + strings.Replace(typ, ".", "/", -1) + ";" + } + } + + return types, nil +} + +func getArgType(v interface{}) string { + if v == nil { + return "V" + } + + switch v.(type) { + // Serialized tags for base types + case nil: + return "V" + case bool: + return "Z" + case []bool: + return "[Z" + case byte: + return "B" + case []byte: + return "[B" + case int8: + return "B" + case []int8: + return "[B" + case int16: + return "S" + case []int16: + return "[S" + case uint16: // Equivalent to Char of Java + return "C" + case []uint16: + return "[C" + // case rune: + // return "C" + case int: + return "J" + case []int: + return "[J" + case int32: + return "I" + case []int32: + return "[I" + case int64: + return "J" + case []int64: + return "[J" + case time.Time: + return "java.util.Date" + case []time.Time: + return "[Ljava.util.Date" + case float32: + return "F" + case []float32: + return "[F" + case float64: + return "D" + case []float64: + return "[D" + case string: + return "java.lang.String" + case []string: + return "[Ljava.lang.String;" + case []Object: + return "[Ljava.lang.Object;" + case map[interface{}]interface{}: + // return "java.util.HashMap" + return "java.util.Map" + case hessian.POJOEnum: + return v.(hessian.POJOEnum).JavaClassName() + // Serialized tags for complex types + default: + t := reflect.TypeOf(v) + if reflect.Ptr == t.Kind() { + t = reflect.TypeOf(reflect.ValueOf(v).Elem()) + } + switch t.Kind() { + case reflect.Struct: + return "java.lang.Object" + case reflect.Slice, reflect.Array: + if t.Elem().Kind() == reflect.Struct { + return "[Ljava.lang.Object;" + } + // return "java.util.ArrayList" + return "java.util.List" + case reflect.Map: // Enter here, map may be map[string]int + return "java.util.Map" + default: + return "" + } + } + + // unreachable + // return "java.lang.RuntimeException" +} + +func ToMapStringInterface(origin map[interface{}]interface{}) map[string]interface{} { + dest := make(map[string]interface{}, len(origin)) + for k, v := range origin { + if kv, ok := k.(string); ok { + if v == nil { + dest[kv] = "" + continue + } + dest[kv] = v + } + } + return dest +} + +func init() { + SetSerializer("hessian2", HessianSerializer{}) +} diff --git a/protocol/dubbo/impl/package.go b/protocol/dubbo/impl/package.go new file mode 100644 index 0000000000000000000000000000000000000000..6f6d2ea9753a513412a4f5099c396dd90cf454ba --- /dev/null +++ b/protocol/dubbo/impl/package.go @@ -0,0 +1,171 @@ +/* + * 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 impl + +import ( + "bufio" + "bytes" + "fmt" + "time" +) + +import ( + "github.com/pkg/errors" +) + +type PackageType int + +// enum part +const ( + PackageError = PackageType(0x01) + PackageRequest = PackageType(0x02) + PackageResponse = PackageType(0x04) + PackageHeartbeat = PackageType(0x08) + PackageRequest_TwoWay = PackageType(0x10) + PackageResponse_Exception = PackageType(0x20) + PackageType_BitSize = 0x2f +) + +type DubboHeader struct { + SerialID byte + Type PackageType + ID int64 + BodyLen int + ResponseStatus byte +} + +// Service defines service instance +type Service struct { + Path string + Interface string + Group string + Version string + Method string + Timeout time.Duration // request timeout +} + +type DubboPackage struct { + Header DubboHeader + Service Service + Body interface{} + Err error + Codec *ProtocolCodec +} + +func (p DubboPackage) String() string { + return fmt.Sprintf("HessianPackage: Header-%v, Path-%v, Body-%v", p.Header, p.Service, p.Body) +} + +func (p *DubboPackage) ReadHeader() error { + return p.Codec.ReadHeader(&p.Header) +} + +func (p *DubboPackage) Marshal() (*bytes.Buffer, error) { + if p.Codec == nil { + return nil, errors.New("Codec is nil") + } + pkg, err := p.Codec.Encode(*p) + if err != nil { + return nil, errors.WithStack(err) + } + return bytes.NewBuffer(pkg), nil +} + +func (p *DubboPackage) Unmarshal() error { + if p.Codec == nil { + return errors.New("Codec is nil") + } + return p.Codec.Decode(p) +} + +func (p DubboPackage) IsHeartBeat() bool { + return p.Header.Type&PackageHeartbeat != 0 +} + +func (p DubboPackage) IsRequest() bool { + return p.Header.Type&(PackageRequest_TwoWay|PackageRequest) != 0 +} + +func (p DubboPackage) IsResponse() bool { + return p.Header.Type == PackageResponse +} + +func (p DubboPackage) IsResponseWithException() bool { + flag := PackageResponse | PackageResponse_Exception + return p.Header.Type&flag == flag +} + +func (p DubboPackage) GetBodyLen() int { + return p.Header.BodyLen +} + +func (p DubboPackage) GetLen() int { + return HEADER_LENGTH + p.Header.BodyLen +} + +func (p DubboPackage) GetBody() interface{} { + return p.Body +} + +func (p *DubboPackage) SetBody(body interface{}) { + p.Body = body +} + +func (p *DubboPackage) SetHeader(header DubboHeader) { + p.Header = header +} + +func (p *DubboPackage) SetService(svc Service) { + p.Service = svc +} + +func (p *DubboPackage) SetID(id int64) { + p.Header.ID = id +} + +func (p DubboPackage) GetHeader() DubboHeader { + return p.Header +} + +func (p DubboPackage) GetService() Service { + return p.Service +} + +func (p *DubboPackage) SetResponseStatus(status byte) { + p.Header.ResponseStatus = status +} + +func (p *DubboPackage) SetSerializer(serializer Serializer) { + p.Codec.SetSerializer(serializer) +} + +func NewDubboPackage(data *bytes.Buffer) *DubboPackage { + var codec *ProtocolCodec + if data == nil { + codec = NewDubboCodec(nil) + } else { + codec = NewDubboCodec(bufio.NewReaderSize(data, len(data.Bytes()))) + } + return &DubboPackage{ + Header: DubboHeader{}, + Service: Service{}, + Body: nil, + Err: nil, + Codec: codec, + } +} diff --git a/protocol/dubbo/impl/request.go b/protocol/dubbo/impl/request.go new file mode 100644 index 0000000000000000000000000000000000000000..ef520083e6457f0ceaf3e80778f79d3e0ce686ab --- /dev/null +++ b/protocol/dubbo/impl/request.go @@ -0,0 +1,40 @@ +/* + * 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 impl + +type RequestPayload struct { + Params interface{} + Attachments map[string]interface{} +} + +func NewRequestPayload(args interface{}, atta map[string]interface{}) *RequestPayload { + if atta == nil { + atta = make(map[string]interface{}) + } + return &RequestPayload{ + Params: args, + Attachments: atta, + } +} + +func EnsureRequestPayload(body interface{}) *RequestPayload { + if req, ok := body.(*RequestPayload); ok { + return req + } + return NewRequestPayload(body, nil) +} diff --git a/protocol/dubbo/impl/response.go b/protocol/dubbo/impl/response.go new file mode 100644 index 0000000000000000000000000000000000000000..9fde1eb249c40e546bdabc54d15e4a3b6d1ea399 --- /dev/null +++ b/protocol/dubbo/impl/response.go @@ -0,0 +1,46 @@ +/* + * 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 impl + +type ResponsePayload struct { + RspObj interface{} + Exception error + Attachments map[string]interface{} +} + +// NewResponse create a new ResponsePayload +func NewResponsePayload(rspObj interface{}, exception error, attachments map[string]interface{}) *ResponsePayload { + if attachments == nil { + attachments = make(map[string]interface{}) + } + return &ResponsePayload{ + RspObj: rspObj, + Exception: exception, + Attachments: attachments, + } +} + +func EnsureResponsePayload(body interface{}) *ResponsePayload { + if res, ok := body.(*ResponsePayload); ok { + return res + } + if exp, ok := body.(error); ok { + return NewResponsePayload(nil, exp, nil) + } + return NewResponsePayload(body, nil, nil) +} diff --git a/protocol/dubbo/impl/serialization.go b/protocol/dubbo/impl/serialization.go new file mode 100644 index 0000000000000000000000000000000000000000..7ce76a87c17376cfb17983b6b6b3bdaf2771c22e --- /dev/null +++ b/protocol/dubbo/impl/serialization.go @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package impl + +import ( + "fmt" +) + +import ( + "github.com/apache/dubbo-go/common/constant" +) + +var ( + serializers = make(map[string]interface{}) + nameMaps = make(map[byte]string) +) + +func init() { + nameMaps = map[byte]string{ + constant.S_Hessian2: constant.HESSIAN2_SERIALIZATION, + constant.S_Proto: constant.PROTOBUF_SERIALIZATION, + } +} + +func SetSerializer(name string, serializer interface{}) { + serializers[name] = serializer +} + +func GetSerializerById(id byte) (interface{}, error) { + name, ok := nameMaps[id] + if !ok { + panic(fmt.Sprintf("serialId %d not found", id)) + } + serializer, ok := serializers[name] + if !ok { + panic(fmt.Sprintf("serialization %s not found", name)) + } + return serializer, nil +} diff --git a/protocol/dubbo/impl/serialize.go b/protocol/dubbo/impl/serialize.go new file mode 100644 index 0000000000000000000000000000000000000000..1f913f7caae2a2c758764a92e4e63a0d9555ff1a --- /dev/null +++ b/protocol/dubbo/impl/serialize.go @@ -0,0 +1,40 @@ +/* + * 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 impl + +import ( + "github.com/apache/dubbo-go/common/constant" +) + +type Serializer interface { + Marshal(p DubboPackage) ([]byte, error) + Unmarshal([]byte, *DubboPackage) error +} + +func LoadSerializer(p *DubboPackage) error { + // NOTE: default serialID is S_Hessian + serialID := p.Header.SerialID + if serialID == 0 { + serialID = constant.S_Hessian2 + } + serializer, err := GetSerializerById(serialID) + if err != nil { + panic(err) + } + p.SetSerializer(serializer.(Serializer)) + return nil +} diff --git a/protocol/dubbo/listener.go b/protocol/dubbo/listener.go deleted file mode 100644 index a17b282fdf3a47cc739793eb93079f758d8de205..0000000000000000000000000000000000000000 --- a/protocol/dubbo/listener.go +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo - -import ( - "context" - "fmt" - "net/url" - "sync" - "sync/atomic" - "time" -) - -import ( - "github.com/apache/dubbo-getty" - "github.com/apache/dubbo-go-hessian2" - "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" - "github.com/apache/dubbo-go/protocol" - "github.com/apache/dubbo-go/protocol/invocation" -) - -// todo: writePkg_Timeout will entry *.yml -const ( - writePkg_Timeout = 5 * time.Second -) - -var ( - errTooManySessions = perrors.New("too many sessions") -) - -type rpcSession struct { - session getty.Session - reqNum int32 -} - -// AddReqNum adds total request number safely -func (s *rpcSession) AddReqNum(num int32) { - atomic.AddInt32(&s.reqNum, num) -} - -// GetReqNum gets total request number safely -func (s *rpcSession) GetReqNum() int32 { - return atomic.LoadInt32(&s.reqNum) -} - -// ////////////////////////////////////////// -// RpcClientHandler -// ////////////////////////////////////////// - -// RpcClientHandler is handler of RPC Client -type RpcClientHandler struct { - conn *gettyRPCClient -} - -// NewRpcClientHandler creates RpcClientHandler with @gettyRPCClient -func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { - return &RpcClientHandler{conn: client} -} - -// OnOpen notified when RPC client session opened -func (h *RpcClientHandler) OnOpen(session getty.Session) error { - h.conn.addSession(session) - return nil -} - -// OnError notified when RPC client session got any error -func (h *RpcClientHandler) OnError(session getty.Session, err error) { - logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) - h.conn.removeSession(session) -} - -// OnOpen notified when RPC client session closed -func (h *RpcClientHandler) OnClose(session getty.Session) { - logger.Infof("session{%s} is closing......", session.Stat()) - h.conn.removeSession(session) -} - -// OnMessage notified when RPC client session got any message in connection -func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { - p, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal package") - return - } - - if p.Header.Type&hessian.PackageHeartbeat != 0x00 { - 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) - } - return - } - logger.Debugf("get rpc response{header: %#v, body: %#v}", p.Header, p.Body) - - h.conn.updateSession(session) - - pendingResponse := h.conn.pool.rpcClient.removePendingResponse(SequenceType(p.Header.ID)) - if pendingResponse == nil { - logger.Errorf("failed to get pending response context for response package %s", *p) - return - } - - if p.Err != nil { - pendingResponse.err = p.Err - } - - pendingResponse.response.atta = p.Body.(*Response).atta - - if pendingResponse.callback == nil { - pendingResponse.done <- struct{}{} - } else { - pendingResponse.callback(pendingResponse.GetCallResponse()) - } -} - -// OnCron notified when RPC client session got any message in cron job -func (h *RpcClientHandler) OnCron(session getty.Session) { - clientRpcSession, err := h.conn.getClientRpcSession(session) - if err != nil { - logger.Errorf("client.getClientSession(session{%s}) = error{%v}", - session.Stat(), perrors.WithStack(err)) - return - } - if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { - logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(session.GetActive()).String(), clientRpcSession.reqNum) - h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) - return - } - - h.conn.pool.rpcClient.heartbeat(session) -} - -// ////////////////////////////////////////// -// RpcServerHandler -// ////////////////////////////////////////// - -// RpcServerHandler is handler of RPC Server -type RpcServerHandler struct { - maxSessionNum int - sessionTimeout time.Duration - sessionMap map[getty.Session]*rpcSession - rwlock sync.RWMutex -} - -// NewRpcServerHandler creates RpcServerHandler with @maxSessionNum and @sessionTimeout -func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration) *RpcServerHandler { - return &RpcServerHandler{ - maxSessionNum: maxSessionNum, - sessionTimeout: sessionTimeout, - sessionMap: make(map[getty.Session]*rpcSession), - } -} - -// OnOpen notified when RPC server session opened -func (h *RpcServerHandler) OnOpen(session getty.Session) error { - var err error - h.rwlock.RLock() - if h.maxSessionNum <= len(h.sessionMap) { - err = errTooManySessions - } - h.rwlock.RUnlock() - if err != nil { - return perrors.WithStack(err) - } - - logger.Infof("got session:%s", session.Stat()) - h.rwlock.Lock() - h.sessionMap[session] = &rpcSession{session: session} - h.rwlock.Unlock() - return nil -} - -// OnError notified when RPC server session got any error -func (h *RpcServerHandler) OnError(session getty.Session, err error) { - logger.Warnf("session{%s} got error{%v}, will be closed.", session.Stat(), err) - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() -} - -// OnOpen notified when RPC server session closed -func (h *RpcServerHandler) OnClose(session getty.Session) { - logger.Infof("session{%s} is closing......", session.Stat()) - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() -} - -// OnMessage notified when RPC server session got any message in connection -func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { - h.rwlock.Lock() - if _, ok := h.sessionMap[session]; ok { - h.sessionMap[session].reqNum++ - } - h.rwlock.Unlock() - - p, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal package{%#v}", pkg) - return - } - p.Header.ResponseStatus = hessian.Response_OK - - // 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) - reply(session, p, hessian.PackageHeartbeat) - return - } - - twoway := true - // not twoway - if p.Header.Type&hessian.PackageRequest_TwoWay == 0x00 { - twoway = false - } - - defer func() { - if e := recover(); e != nil { - p.Header.ResponseStatus = hessian.Response_SERVER_ERROR - if err, ok := e.(error); ok { - logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) - p.Body = perrors.WithStack(err) - } else if err, ok := e.(string); ok { - logger.Errorf("OnMessage panic: %+v", perrors.New(err)) - p.Body = perrors.New(err) - } else { - logger.Errorf("OnMessage panic: %+v, this is impossible.", e) - p.Body = e - } - - if !twoway { - return - } - reply(session, p, hessian.PackageResponse) - } - - }() - - u := common.NewURLWithOptions(common.WithPath(p.Service.Path), common.WithParams(url.Values{}), - common.WithParamsValue(constant.GROUP_KEY, p.Service.Group), - common.WithParamsValue(constant.INTERFACE_KEY, p.Service.Interface), - common.WithParamsValue(constant.VERSION_KEY, p.Service.Version)) - exporter, _ := dubboProtocol.ExporterMap().Load(u.ServiceKey()) - if exporter == nil { - err := fmt.Errorf("don't have this exporter, key: %s", u.ServiceKey()) - logger.Errorf(err.Error()) - p.Header.ResponseStatus = hessian.Response_OK - p.Body = err - reply(session, p, hessian.PackageResponse) - return - } - invoker := exporter.(protocol.Exporter).GetInvoker() - if invoker != nil { - attachments := p.Body.(map[string]interface{})["attachments"].(map[string]string) - attachments[constant.LOCAL_ADDR] = session.LocalAddr() - attachments[constant.REMOTE_ADDR] = session.RemoteAddr() - - args := p.Body.(map[string]interface{})["args"].([]interface{}) - inv := invocation.NewRPCInvocation(p.Service.Method, args, attachments) - - 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()) - } else { - res := result.Result() - p.Header.ResponseStatus = hessian.Response_OK - p.Body = hessian.NewResponse(res, nil, result.Attachments()) - } - } - - if !twoway { - return - } - reply(session, p, hessian.PackageResponse) -} - -// OnCron notified when RPC server session got any message in cron job -func (h *RpcServerHandler) OnCron(session getty.Session) { - var ( - flag bool - active time.Time - ) - - h.rwlock.RLock() - if _, ok := h.sessionMap[session]; ok { - active = session.GetActive() - if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { - flag = true - logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) - } - } - h.rwlock.RUnlock() - - if flag { - h.rwlock.Lock() - delete(h.sessionMap, session) - h.rwlock.Unlock() - session.Close() - } -} - -// 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, - Type: tp, - ID: req.Header.ID, - ResponseStatus: req.Header.ResponseStatus, - }, - } - - if req.Header.Type&hessian.PackageRequest != 0x00 { - resp.Body = req.Body - } else { - resp.Body = nil - } - - if err := session.WritePkg(resp, writePkg_Timeout); err != nil { - logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), req.Header) - } -} diff --git a/protocol/dubbo/opentracing.go b/protocol/dubbo/opentracing.go new file mode 100644 index 0000000000000000000000000000000000000000..2dcd6a4d0d9f491ba6d51ea7a3ba96812a6f9e08 --- /dev/null +++ b/protocol/dubbo/opentracing.go @@ -0,0 +1,60 @@ +/* + * 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 ( + "github.com/opentracing/opentracing-go" +) +import ( + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" +) + +func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error { + // inject opentracing ctx + traceAttachments := filterContext(inv.Attachments()) + carrier := opentracing.TextMapCarrier(traceAttachments) + err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier) + if err == nil { + fillTraceAttachments(inv.Attachments(), traceAttachments) + } + return err +} + +func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) { + traceAttachments := filterContext(inv.Attachments()) + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(traceAttachments)) + return spanCtx, err +} + +func filterContext(attachments map[string]interface{}) map[string]string { + var traceAttchment = make(map[string]string) + for k, v := range attachments { + if r, ok := v.(string); ok { + traceAttchment[k] = r + } + } + return traceAttchment +} + +func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) { + for k, v := range traceAttachment { + attachments[k] = v + } +} diff --git a/protocol/dubbo/readwriter.go b/protocol/dubbo/readwriter.go deleted file mode 100644 index adc6311b0ee6b050a5da5ebb1b95472fe0602503..0000000000000000000000000000000000000000 --- a/protocol/dubbo/readwriter.go +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package dubbo - -import ( - "bytes" - "reflect" -) - -import ( - "github.com/apache/dubbo-getty" - "github.com/apache/dubbo-go-hessian2" - 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" -) - -//////////////////////////////////////////// -// RpcClientPackageHandler -//////////////////////////////////////////// - -// RpcClientPackageHandler handle package for client in getty. -type RpcClientPackageHandler struct { - client *Client -} - -// NewRpcClientPackageHandler create a RpcClientPackageHandler. -func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { - return &RpcClientPackageHandler{client: client} -} - -// Read decode @data to DubboPackage. -func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - pkg := &DubboPackage{} - - buf := bytes.NewBuffer(data) - err := pkg.Unmarshal(buf, p.client) - if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return nil, 0, nil - } - - logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - - return nil, 0, perrors.WithStack(err) - } - - 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 -} - -// Write encode @pkg. -func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { - req, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal pkg:%+v\n", pkg) - return nil, perrors.New("invalid rpc request") - } - - buf, err := req.Marshal() - if err != nil { - logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - - return buf.Bytes(), nil -} - -//////////////////////////////////////////// -// RpcServerPackageHandler -//////////////////////////////////////////// - -var ( - rpcServerPkgHandler = &RpcServerPackageHandler{} -) - -// RpcServerPackageHandler handle package for server in getty. -type RpcServerPackageHandler struct{} - -// Read decode @data to DubboPackage. -func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { - pkg := &DubboPackage{ - Body: make([]interface{}, 7), - } - - buf := bytes.NewBuffer(data) - err := pkg.Unmarshal(buf) - if err != nil { - originErr := perrors.Cause(err) - if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { - return nil, 0, nil - } - - logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) - - return nil, 0, perrors.WithStack(err) - } - - if pkg.Header.Type&hessian.PackageHeartbeat == 0x00 { - // convert params of request - req := pkg.Body.([]interface{}) // length of body should be 7 - if len(req) > 0 { - var dubboVersion, argsTypes string - var args []interface{} - var attachments map[string]string - if req[0] != nil { - dubboVersion = req[0].(string) - } - if req[1] != nil { - pkg.Service.Path = req[1].(string) - } - if req[2] != nil { - pkg.Service.Version = req[2].(string) - } - if req[3] != nil { - pkg.Service.Method = req[3].(string) - } - if req[4] != nil { - argsTypes = req[4].(string) - } - if req[5] != nil { - args = req[5].([]interface{}) - } - if req[6] != nil { - attachments = req[6].(map[string]string) - } - if pkg.Service.Path == "" && len(attachments[constant.PATH_KEY]) > 0 { - pkg.Service.Path = attachments[constant.PATH_KEY] - } - if _, ok := attachments[constant.INTERFACE_KEY]; ok { - pkg.Service.Interface = attachments[constant.INTERFACE_KEY] - } else { - pkg.Service.Interface = pkg.Service.Path - } - if len(attachments[constant.GROUP_KEY]) > 0 { - pkg.Service.Group = attachments[constant.GROUP_KEY] - } - pkg.Body = map[string]interface{}{ - "dubboVersion": dubboVersion, - "argsTypes": argsTypes, - "args": args, - "service": common.ServiceMap.GetService(DUBBO, pkg.Service.Path), // path as a key - "attachments": attachments, - } - } - } - - return pkg, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil -} - -// Write encode @pkg. -func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { - res, ok := pkg.(*DubboPackage) - if !ok { - logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) - return nil, perrors.New("invalid rpc response") - } - - buf, err := res.Marshal() - if err != nil { - logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) - return nil, perrors.WithStack(err) - } - - return buf.Bytes(), nil -} diff --git a/protocol/invocation.go b/protocol/invocation.go index 296ec0540c1eed69c30e1b1477be038a4a9cc00e..452f619e2dd9a5835141d91d7adfed37bb9f6859 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -34,15 +34,16 @@ type Invocation interface { // Reply gets response of request Reply() interface{} // Attachments gets all attachments - Attachments() map[string]string - // AttachmentsByKey gets attachment by key , if nil then return default value + Attachments() map[string]interface{} + // AttachmentsByKey gets attachment by key , if nil then return default value. 锛圛t will be deprecated in the future锛� AttachmentsByKey(string, string) string + Attachment(string) interface{} // Attributes refers to dubbo 2.7.6. It is different from attachment. It is used in internal process. Attributes() map[string]interface{} // AttributeByKey gets attribute by key , if nil then return default value AttributeByKey(string, interface{}) interface{} // SetAttachments sets attribute by @key and @value. - SetAttachments(key string, value string) + SetAttachments(key string, value interface{}) // Invoker gets the invoker in current context. Invoker() Invoker } diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index c72e105d3594195457a7d7abcccc8badb85c678b..4e806324bf7b236d80a932a92898ba117fb1638d 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -23,6 +23,8 @@ import ( ) import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" ) @@ -39,7 +41,7 @@ type RPCInvocation struct { arguments []interface{} reply interface{} callBack interface{} - attachments map[string]string + attachments map[string]interface{} // Refer to dubbo 2.7.6. It is different from attachment. It is used in internal process. attributes map[string]interface{} invoker protocol.Invoker @@ -47,7 +49,7 @@ type RPCInvocation struct { } // NewRPCInvocation creates a RPC invocation. -func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]string) *RPCInvocation { +func NewRPCInvocation(methodName string, arguments []interface{}, attachments map[string]interface{}) *RPCInvocation { return &RPCInvocation{ methodName: methodName, arguments: arguments, @@ -99,7 +101,7 @@ func (r *RPCInvocation) SetReply(reply interface{}) { } // Attachments gets all attachments of RPC. -func (r *RPCInvocation) Attachments() map[string]string { +func (r *RPCInvocation) Attachments() map[string]interface{} { return r.attachments } @@ -112,11 +114,25 @@ func (r *RPCInvocation) AttachmentsByKey(key string, defaultValue string) string } value, ok := r.attachments[key] if ok { - return value + return value.(string) } return defaultValue } +// Attachment returns the corresponding value from dubbo's attachment with the given key. +func (r *RPCInvocation) Attachment(key string) interface{} { + r.lock.RLock() + defer r.lock.RUnlock() + if r.attachments == nil { + return nil + } + value, ok := r.attachments[key] + if ok { + return value + } + return nil +} + // Attributes gets all attributes of RPC. func (r *RPCInvocation) Attributes() map[string]interface{} { return r.attributes @@ -134,11 +150,11 @@ func (r *RPCInvocation) AttributeByKey(key string, defaultValue interface{}) int } // SetAttachments sets attribute by @key and @value. -func (r *RPCInvocation) SetAttachments(key string, value string) { +func (r *RPCInvocation) SetAttachments(key string, value interface{}) { r.lock.Lock() defer r.lock.Unlock() if r.attachments == nil { - r.attachments = make(map[string]string) + r.attachments = make(map[string]interface{}) } r.attachments[key] = value } @@ -172,6 +188,11 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { r.callBack = c } +func (r *RPCInvocation) ServiceKey() string { + return common.ServiceKey(r.AttachmentsByKey(constant.INTERFACE_KEY, ""), + r.AttachmentsByKey(constant.GROUP_KEY, ""), r.AttachmentsByKey(constant.VERSION_KEY, "")) +} + // ///////////////////////// // option // ///////////////////////// @@ -221,7 +242,7 @@ func WithCallBack(callBack interface{}) option { } // WithAttachments creates option with @attachments. -func WithAttachments(attachments map[string]string) option { +func WithAttachments(attachments map[string]interface{}) option { return func(invo *RPCInvocation) { invo.attachments = attachments } diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index 576591940dd3021e7bbd9d2eda0ac5498391a1f7..4a9645e828a3b092c938c62091fb400b05605b67 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -75,7 +75,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser ctx := context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser", }) @@ -89,7 +89,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser0 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser0", }) @@ -102,7 +102,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser1 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser1", }) @@ -114,7 +114,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser2 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser2", }) @@ -126,7 +126,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser3 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser3", }) @@ -138,7 +138,7 @@ func TestHTTPClientCall(t *testing.T) { // call GetUser4 ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser4", }) @@ -149,7 +149,7 @@ func TestHTTPClientCall(t *testing.T) { assert.Equal(t, &User{Id: "", Name: ""}, reply) ctx = context.WithValue(context.Background(), constant.DUBBOGO_CTX_KEY, map[string]string{ - "X-Proxy-Id": "dubbogo", + "X-Proxy-ID": "dubbogo", "X-Services": url.Path, "X-Method": "GetUser4", }) diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index 90a6bf5ef7aa017488f723804b22cc613850bcf2..1778d99d4078058dc43a630b89b9d256350da0f3 100644 --- a/protocol/jsonrpc/jsonrpc_protocol.go +++ b/protocol/jsonrpc/jsonrpc_protocol.go @@ -59,7 +59,7 @@ func NewJsonrpcProtocol() *JsonrpcProtocol { } } -// Export JSON RPC service for remote invocation +// Export JSON RPC service for remote invocation func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := strings.TrimPrefix(url.Path, "/") diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go index aa458a1614df29997b05ac4462200f9e9ffffc25..9755a481fdba325887a1d16731cbbcb8e3943d8a 100644 --- a/protocol/jsonrpc/server.go +++ b/protocol/jsonrpc/server.go @@ -127,10 +127,10 @@ func (s *Server) handlePkg(conn net.Conn) { } reqBody, err := ioutil.ReadAll(r.Body) + r.Body.Close() if err != nil { return } - r.Body.Close() reqHeader := make(map[string]string) for k := range r.Header { @@ -263,8 +263,7 @@ func (s *Server) Stop() { }) } -func serveRequest(ctx context.Context, - header map[string]string, body []byte, conn net.Conn) error { +func serveRequest(ctx context.Context, header map[string]string, body []byte, conn net.Conn) error { sendErrorResp := func(header map[string]string, body []byte) error { rsp := &http.Response{ Header: make(http.Header), @@ -324,13 +323,12 @@ func serveRequest(ctx context.Context, if err == io.EOF || err == io.ErrUnexpectedEOF { return perrors.WithStack(err) } - return perrors.New("server cannot decode request: " + err.Error()) } + path := header["Path"] methodName := codec.req.Method if len(path) == 0 || len(methodName) == 0 { - codec.ReadBody(nil) return perrors.New("service/method request ill-formed: " + path + "/" + methodName) } @@ -345,7 +343,7 @@ func serveRequest(ctx context.Context, exporter, _ := jsonrpcProtocol.ExporterMap().Load(path) invoker := exporter.(*JsonrpcExporter).GetInvoker() if invoker != nil { - result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]string{ + result := invoker.Invoke(ctx, invocation.NewRPCInvocation(methodName, args, map[string]interface{}{ constant.PATH_KEY: path, constant.VERSION_KEY: codec.req.Version})) if err := result.Error(); err != nil { diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go index 87f90d3b7c73328a30e883da8251ac8364a63b2d..4b2702b99f9793d4e567de65bd9555fb20381b1d 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper.go @@ -68,21 +68,19 @@ func (pfw *ProtocolFilterWrapper) Destroy() { } func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker { - filtName := invoker.GetUrl().GetParam(key, "") - if filtName == "" { + filterName := invoker.GetUrl().GetParam(key, "") + if filterName == "" { return invoker } - filtNames := strings.Split(filtName, ",") - next := invoker + filterNames := strings.Split(filterName, ",") // The order of filters is from left to right, so loading from right to left - - for i := len(filtNames) - 1; i >= 0; i-- { - flt := extension.GetFilter(filtNames[i]) + next := invoker + for i := len(filterNames) - 1; i >= 0; i-- { + flt := extension.GetFilter(filterNames[i]) fi := &FilterInvoker{next: next, invoker: invoker, filter: flt} next = fi } - return next } diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go index e39558caeae9811817cc26a1717c1b8e3729234c..359506a842437ba15c121c29b50478ae775a8ed7 100644 --- a/protocol/rest/rest_exporter.go +++ b/protocol/rest/rest_exporter.go @@ -49,5 +49,4 @@ func (re *RestExporter) Unexport() { if err != nil { logger.Errorf("[RestExporter.Unexport] error: %v", err) } - return } diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go index fbd6fb7ad9dd81f043e4d45ee94a54e12ef89cdd..d9542bb8760cd2ddb7b839ebc6761dab75ae9c30 100644 --- a/protocol/rest/server/rest_server.go +++ b/protocol/rest/server/rest_server.go @@ -111,7 +111,7 @@ func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethod logger.Errorf("[Go Restful] WriteErrorString error:%v", err) } } - result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]string))) + result := invoker.Invoke(context.Background(), invocation.NewRPCInvocation(methodConfig.MethodName, args, make(map[string]interface{}))) if result.Error() != nil { err = resp.WriteError(http.StatusInternalServerError, result.Error()) if err != nil { diff --git a/protocol/result.go b/protocol/result.go index 2a33be612fd1f319c8c46cbd480865d5564b189d..a36b16d1cc56557c2976df5550f5d9c01b88619b 100644 --- a/protocol/result.go +++ b/protocol/result.go @@ -28,13 +28,14 @@ type Result interface { // Result gets invoker result. Result() interface{} // SetAttachments replaces the existing attachments with the specified param. - SetAttachments(map[string]string) + SetAttachments(map[string]interface{}) // Attachments gets all attachments - Attachments() map[string]string + Attachments() map[string]interface{} + // AddAttachment adds the specified map to existing attachments in this instance. - AddAttachment(string, string) + AddAttachment(string, interface{}) // Attachment gets attachment by key with default value. - Attachment(string, string) string + Attachment(string, interface{}) interface{} } ///////////////////////////// @@ -43,7 +44,7 @@ type Result interface { // RPCResult is default RPC result. type RPCResult struct { - Attrs map[string]string + Attrs map[string]interface{} Err error Rest interface{} } @@ -69,22 +70,22 @@ func (r *RPCResult) Result() interface{} { } // SetAttachments replaces the existing attachments with the specified param. -func (r *RPCResult) SetAttachments(attr map[string]string) { +func (r *RPCResult) SetAttachments(attr map[string]interface{}) { r.Attrs = attr } // Attachments gets all attachments -func (r *RPCResult) Attachments() map[string]string { +func (r *RPCResult) Attachments() map[string]interface{} { return r.Attrs } // AddAttachment adds the specified map to existing attachments in this instance. -func (r *RPCResult) AddAttachment(key, value string) { +func (r *RPCResult) AddAttachment(key string, value interface{}) { r.Attrs[key] = value } // Attachment gets attachment by key with default value. -func (r *RPCResult) Attachment(key, defaultValue string) string { +func (r *RPCResult) Attachment(key string, defaultValue interface{}) interface{} { v, ok := r.Attrs[key] if !ok { v = defaultValue diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go index 60becfb34135470b0e69972c25a743f44efe19d5..978534ea8b98ddd76f68619155ff90fe5e031ac1 100644 --- a/protocol/rpc_status.go +++ b/protocol/rpc_status.go @@ -98,7 +98,10 @@ func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 { // GetURLStatus get URL RPC status. func GetURLStatus(url common.URL) *RPCStatus { - rpcStatus, _ := serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{}) + rpcStatus, found := serviceStatistic.Load(url.Key()) + if !found { + rpcStatus, _ = serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{}) + } return rpcStatus.(*RPCStatus) } @@ -107,15 +110,13 @@ func GetMethodStatus(url common.URL, methodName string) *RPCStatus { identifier := url.Key() methodMap, found := methodStatistics.Load(identifier) if !found { - methodMap = &sync.Map{} - methodStatistics.Store(identifier, methodMap) + methodMap, _ = methodStatistics.LoadOrStore(identifier, &sync.Map{}) } methodActive := methodMap.(*sync.Map) rpcStatus, found := methodActive.Load(methodName) if !found { - rpcStatus = &RPCStatus{} - methodActive.Store(methodName, rpcStatus) + rpcStatus, _ = methodActive.LoadOrStore(methodName, &RPCStatus{}) } status := rpcStatus.(*RPCStatus) diff --git a/registry/consul/service_discovery.go b/registry/consul/service_discovery.go new file mode 100644 index 0000000000000000000000000000000000000000..d8ab93f31ee6fdcf79aa869a35548e8192841fe4 --- /dev/null +++ b/registry/consul/service_discovery.go @@ -0,0 +1,498 @@ +/* + * 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 consul + +import ( + "encoding/base64" + "fmt" + "strconv" + "sync" + "time" +) + +import ( + "github.com/dubbogo/gost/container/set" + "github.com/dubbogo/gost/page" + consul "github.com/hashicorp/consul/api" + "github.com/hashicorp/consul/api/watch" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/registry" +) + +const ( + enable = "enable" + watch_type = "type" + watch_type_service = "service" + watch_service = "service" + watch_passingonly = "passingonly" + watch_passingonly_true = true +) + +var ( + errConsulClientClosed = perrors.New("consul client is closed") +) + +// init will put the service discovery into extension +func init() { + extension.SetServiceDiscovery(constant.CONSUL_KEY, newConsulServiceDiscovery) +} + +// consulServiceDiscovery is the implementation of service discovery based on consul. +type consulServiceDiscovery struct { + // descriptor is a short string about the basic information of this instance + descriptor string + clientLock sync.RWMutex + // Consul client. + consulClient *consul.Client + checkPassInterval int64 + tag string + address string + deregisterCriticalServiceAfter string + ttl sync.Map + *consul.Config +} + +// newConsulServiceDiscovery will create new service discovery instance +// use double-check pattern to reduce race condition +func newConsulServiceDiscovery(name string) (registry.ServiceDiscovery, error) { + sdc, ok := config.GetBaseConfig().GetServiceDiscoveries(name) + if !ok || len(sdc.RemoteRef) == 0 { + return nil, perrors.New("could not init the instance because the config is invalid") + } + + remoteConfig, ok := config.GetBaseConfig().GetRemoteConfig(sdc.RemoteRef) + if !ok { + return nil, perrors.New("could not find the remote config for name: " + sdc.RemoteRef) + } + + descriptor := fmt.Sprintf("consul-service-discovery[%s]", remoteConfig.Address) + + config := &consul.Config{Address: remoteConfig.Address, Token: remoteConfig.Params[constant.ACL_TOKEN]} + client, err := consul.NewClient(config) + if err != nil { + return nil, perrors.WithMessage(err, "create consul client failed.") + } + + return &consulServiceDiscovery{ + address: remoteConfig.Address, + descriptor: descriptor, + checkPassInterval: getCheckPassInterval(remoteConfig.Params), + Config: config, + tag: remoteConfig.Params[constant.QUERY_TAG], + consulClient: client, + deregisterCriticalServiceAfter: getDeregisterAfter(remoteConfig.Params), + clientLock: sync.RWMutex{}, + }, nil +} + +func (csd *consulServiceDiscovery) String() string { + return csd.descriptor +} + +// nolint +func (csd *consulServiceDiscovery) getConsulClient() *consul.Client { + csd.clientLock.RLock() + defer csd.clientLock.RUnlock() + return csd.consulClient +} + +// nolint +func (csd *consulServiceDiscovery) setConsulClient(consulClient *consul.Client) { + csd.clientLock.Lock() + defer csd.clientLock.Unlock() + csd.consulClient = consulClient +} + +func (csd *consulServiceDiscovery) Destroy() error { + csd.setConsulClient(nil) + csd.ttl.Range(func(key, t interface{}) bool { + close(t.(chan struct{})) + csd.ttl.Delete(key) + return true + }) + return nil +} + +func (csd *consulServiceDiscovery) Register(instance registry.ServiceInstance) error { + var ( + err error + consulClient *consul.Client + ) + ins, _ := csd.buildRegisterInstance(instance) + if consulClient = csd.getConsulClient(); consulClient == nil { + return errConsulClientClosed + } + err = consulClient.Agent().ServiceRegister(ins) + if err != nil { + logger.Errorf("consul register the instance %s fail:%v", instance.GetServiceName(), err) + return perrors.WithMessage(err, "consul could not register the instance. "+instance.GetServiceName()) + } + + return csd.registerTtl(instance) +} + +func (csd *consulServiceDiscovery) registerTtl(instance registry.ServiceInstance) error { + var ( + err error + consulClient *consul.Client + ) + + checkID := buildID(instance) + + stopChan := make(chan struct{}) + csd.ttl.LoadOrStore(buildID(instance), stopChan) + + period := time.Duration(csd.checkPassInterval/8) * time.Millisecond + timer := time.NewTicker(period) + go func() { + defer timer.Stop() + for { + select { + case <-timer.C: + if consulClient = csd.getConsulClient(); consulClient == nil { + logger.Debugf("consul client is closed!") + return + } + err = consulClient.Agent().PassTTL(fmt.Sprintf("service:%s", checkID), "") + if err != nil { + logger.Warnf("pass ttl heartbeat fail:%v", err) + break + } + logger.Debugf("passed ttl heartbeat for %s", checkID) + break + case <-stopChan: + logger.Info("ttl %s for service %s is stopped", checkID, instance.GetServiceName()) + return + } + } + }() + return nil +} + +func (csd *consulServiceDiscovery) Update(instance registry.ServiceInstance) error { + var ( + err error + consulClient *consul.Client + ) + ins, _ := csd.buildRegisterInstance(instance) + consulClient = csd.getConsulClient() + if consulClient == nil { + return errConsulClientClosed + } + err = consulClient.Agent().ServiceDeregister(buildID(instance)) + if err != nil { + logger.Warnf("unregister instance %s fail:%v", instance.GetServiceName(), err) + } + return consulClient.Agent().ServiceRegister(ins) +} + +func (csd *consulServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + var ( + err error + consulClient *consul.Client + ) + if consulClient = csd.getConsulClient(); consulClient == nil { + return errConsulClientClosed + } + err = consulClient.Agent().ServiceDeregister(buildID(instance)) + if err != nil { + logger.Errorf("unregister service instance %s,error: %v", instance.GetId(), err) + return err + } + stopChanel, ok := csd.ttl.Load(buildID(instance)) + if !ok { + logger.Warnf("ttl for service instance %s didn't exist", instance.GetId()) + return nil + } + close(stopChanel.(chan struct{})) + csd.ttl.Delete(buildID(instance)) + return nil +} + +func (csd *consulServiceDiscovery) GetDefaultPageSize() int { + return registry.DefaultPageSize +} + +func (csd *consulServiceDiscovery) GetServices() *gxset.HashSet { + var ( + err error + consulClient *consul.Client + services map[string][]string + ) + var res = gxset.NewSet() + if consulClient = csd.getConsulClient(); consulClient == nil { + logger.Warnf("consul client is closed!") + return res + } + services, _, err = consulClient.Catalog().Services(nil) + if err != nil { + logger.Errorf("get services,error: %v", err) + return res + } + + for service, _ := range services { + res.Add(service) + } + return res + +} + +// encodeConsulMetadata because consul validate key strictly. +func encodeConsulMetadata(metadata map[string]string) map[string]string { + consulMetadata := make(map[string]string, len(metadata)) + encoder := base64.RawStdEncoding + for k, v := range metadata { + consulMetadata[encoder.EncodeToString([]byte(k))] = v + } + return consulMetadata +} + +// nolint +func decodeConsulMetadata(metadata map[string]string) map[string]string { + dubboMetadata := make(map[string]string, len(metadata)) + encoder := base64.RawStdEncoding + for k, v := range metadata { + kBytes, err := encoder.DecodeString(k) + if err != nil { + logger.Warnf("can not decoded consul metadata key %s", k) + continue + } + dubboMetadata[string(kBytes)] = v + } + return dubboMetadata +} + +func (csd *consulServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + var ( + err error + consulClient *consul.Client + instances []*consul.ServiceEntry + ) + if consulClient = csd.getConsulClient(); consulClient == nil { + logger.Warn("consul client is closed!") + return nil + } + instances, _, err = consulClient.Health().Service(serviceName, csd.tag, true, &consul.QueryOptions{ + WaitTime: time.Duration(csd.checkPassInterval), + }) + + if err != nil { + logger.Errorf("get instances for service %s,error: %v", serviceName, err) + return nil + } + + res := make([]registry.ServiceInstance, 0, len(instances)) + for _, ins := range instances { + metadata := ins.Service.Meta + + // enable status + enableStr := metadata[enable] + delete(metadata, enable) + enable, _ := strconv.ParseBool(enableStr) + metadata = decodeConsulMetadata(metadata) + + // health status + status := ins.Checks.AggregatedStatus() + healthy := false + if status == consul.HealthPassing { + healthy = true + } + res = append(res, ®istry.DefaultServiceInstance{ + Id: ins.Service.ID, + ServiceName: ins.Service.Service, + Host: ins.Service.Address, + Port: ins.Service.Port, + Enable: enable, + Healthy: healthy, + Metadata: metadata, + }) + } + + return res +} + +func (csd *consulServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + all := csd.GetInstances(serviceName) + res := make([]interface{}, 0, pageSize) + for i := offset; i < len(all) && i < offset+pageSize; i++ { + res = append(res, all[i]) + } + return gxpage.New(offset, pageSize, res, len(all)) +} + +func (csd *consulServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { + all := csd.GetInstances(serviceName) + res := make([]interface{}, 0, pageSize) + // could not use res = all[a:b] here because the res should be []interface{}, not []ServiceInstance + var ( + i = offset + count = 0 + ) + for i < len(all) && count < pageSize { + ins := all[i] + if ins.IsHealthy() == healthy { + res = append(res, all[i]) + count++ + } + i++ + } + return gxpage.New(offset, pageSize, res, len(all)) +} + +func (csd *consulServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { + res := make(map[string]gxpage.Pager, len(serviceNames)) + for _, name := range serviceNames { + res[name] = csd.GetInstancesByPage(name, offset, requestedSize) + } + return res +} + +func (csd *consulServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + + params := make(map[string]interface{}, 8) + params[watch_type] = watch_type_service + params[watch_service] = listener.ServiceName + params[watch_passingonly] = watch_passingonly_true + plan, err := watch.Parse(params) + if err != nil { + logger.Errorf("add listener for service %s,error:%v", listener.ServiceName, err) + return err + } + + plan.Handler = func(idx uint64, raw interface{}) { + services, ok := raw.([]*consul.ServiceEntry) + if !ok { + err = perrors.New("handler get non ServiceEntry type parameter") + return + } + instances := make([]registry.ServiceInstance, 0, len(services)) + for _, ins := range services { + metadata := ins.Service.Meta + + // enable status + enableStr := metadata[enable] + delete(metadata, enable) + enable, _ := strconv.ParseBool(enableStr) + + // health status + status := ins.Checks.AggregatedStatus() + healthy := false + if status == consul.HealthPassing { + healthy = true + } + instances = append(instances, ®istry.DefaultServiceInstance{ + Id: ins.Service.ID, + ServiceName: ins.Service.Service, + Host: ins.Service.Address, + Port: ins.Service.Port, + Enable: enable, + Healthy: healthy, + Metadata: metadata, + }) + } + e := csd.DispatchEventForInstances(listener.ServiceName, instances) + if e != nil { + logger.Errorf("Dispatching event got exception, service name: %s, err: %v", listener.ServiceName, err) + } + } + go func() { + err = plan.RunWithConfig(csd.Config.Address, csd.Config) + if err != nil { + logger.Error("consul plan run failure!error:%v", err) + } + }() + return nil +} + +func (csd *consulServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + return csd.DispatchEventForInstances(serviceName, csd.GetInstances(serviceName)) +} + +func (csd *consulServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { + return csd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances)) +} + +func (csd *consulServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + extension.GetGlobalDispatcher().Dispatch(event) + return nil +} + +func (csd *consulServiceDiscovery) buildRegisterInstance(instance registry.ServiceInstance) (*consul.AgentServiceRegistration, error) { + metadata := instance.GetMetadata() + metadata = encodeConsulMetadata(metadata) + metadata[enable] = strconv.FormatBool(instance.IsEnable()) + // check + check := csd.buildCheck(instance) + + return &consul.AgentServiceRegistration{ + ID: buildID(instance), + Name: instance.GetServiceName(), + Port: instance.GetPort(), + Address: instance.GetHost(), + Meta: metadata, + Check: &check, + }, nil +} + +func (csd *consulServiceDiscovery) buildCheck(instance registry.ServiceInstance) consul.AgentServiceCheck { + + deregister, ok := instance.GetMetadata()[constant.DEREGISTER_AFTER] + if !ok || len(deregister) == 0 { + deregister = constant.DEFAULT_DEREGISTER_TIME + } + return consul.AgentServiceCheck{ + TTL: strconv.FormatInt(csd.checkPassInterval/1000, 10) + "s", + DeregisterCriticalServiceAfter: csd.deregisterCriticalServiceAfter, + } +} + +// nolint +func getCheckPassInterval(params map[string]string) int64 { + checkPassIntervalStr, ok := params[constant.CHECK_PASS_INTERVAL] + if !ok { + return constant.DEFAULT_CHECK_PASS_INTERVAL + } + checkPassInterval, err := strconv.ParseInt(checkPassIntervalStr, 10, 64) + if err != nil { + logger.Warnf("consul service discovery remote config error:%s", checkPassIntervalStr) + return constant.DEFAULT_CHECK_PASS_INTERVAL + } + return checkPassInterval +} + +// nolint +func getDeregisterAfter(metadata map[string]string) string { + deregister, ok := metadata[constant.DEREGISTER_AFTER] + if !ok || len(deregister) == 0 { + deregister = constant.DEFAULT_DEREGISTER_TIME + } + return deregister +} + +// nolint +func buildID(instance registry.ServiceInstance) string { + id := fmt.Sprintf("id:%s,serviceName:%s,host:%s,port:%d", instance.GetId(), instance.GetServiceName(), instance.GetHost(), instance.GetPort()) + return id +} diff --git a/registry/consul/service_discovery_test.go b/registry/consul/service_discovery_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ed7220f2de3140d4e97ea48acac1f53d29953208 --- /dev/null +++ b/registry/consul/service_discovery_test.go @@ -0,0 +1,231 @@ +/* + * 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 consul + +import ( + "fmt" + "math/rand" + "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/common/extension" + "github.com/apache/dubbo-go/common/observer" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/registry" + "github.com/apache/dubbo-go/remoting/consul" +) + +var ( + testName = "test" + consulCheckPassInterval = 17000 + consulDeregisterCriticalServiceAfter = "20s" + consulWatchTimeout = 60000 + registryURL = common.URL{} +) + +func TestConsulServiceDiscovery_newConsulServiceDiscovery(t *testing.T) { + name := "consul1" + _, err := newConsulServiceDiscovery(name) + assert.NotNil(t, err) + + sdc := &config.ServiceDiscoveryConfig{ + Protocol: "consul", + RemoteRef: "mock", + } + + config.GetBaseConfig().ServiceDiscoveries[name] = sdc + + _, err = newConsulServiceDiscovery(name) + assert.NotNil(t, err) + + config.GetBaseConfig().Remotes["mock"] = &config.RemoteConfig{ + Address: "localhost:8081", + } + + res, err := newConsulServiceDiscovery(name) + assert.Nil(t, err) + assert.NotNil(t, res) +} + +func TestConsulServiceDiscovery_Destroy(t *testing.T) { + prepareData() + serviceDiscovery, err := extension.GetServiceDiscovery(constant.CONSUL_KEY, testName) + prepareService() + assert.Nil(t, err) + assert.NotNil(t, serviceDiscovery) + err = serviceDiscovery.Destroy() + assert.Nil(t, err) + assert.Nil(t, serviceDiscovery.(*consulServiceDiscovery).consulClient) +} + +func TestConsulServiceDiscovery_CRUD(t *testing.T) { + // start consul agent + consulAgent := consul.NewConsulAgent(t, registryPort) + defer consulAgent.Shutdown() + + prepareData() + var eventDispatcher = MockEventDispatcher{Notify: make(chan struct{}, 1)} + extension.SetEventDispatcher("mock", func() observer.EventDispatcher { + return &eventDispatcher + }) + + extension.SetAndInitGlobalDispatcher("mock") + rand.Seed(time.Now().Unix()) + + instance, _ := prepareService() + + // clean data + serviceDiscovery, err := extension.GetServiceDiscovery(constant.CONSUL_KEY, testName) + assert.Nil(t, err) + + err = serviceDiscovery.Unregister(instance) + assert.Nil(t, err) + + err = serviceDiscovery.Register(instance) + assert.Nil(t, err) + + //sometimes nacos may be failed to push update of instance, + //so it need 10s to pull, we sleep 10 second to make sure instance has been update + time.Sleep(3 * time.Second) + page := serviceDiscovery.GetHealthyInstancesByPage(instance.GetServiceName(), 0, 10, true) + assert.NotNil(t, page) + assert.Equal(t, 0, page.GetOffset()) + assert.Equal(t, 10, page.GetPageSize()) + assert.Equal(t, 1, page.GetDataSize()) + + instanceResult := page.GetData()[0].(*registry.DefaultServiceInstance) + assert.NotNil(t, instanceResult) + assert.Equal(t, buildID(instance), instanceResult.GetId()) + assert.Equal(t, instance.GetHost(), instanceResult.GetHost()) + assert.Equal(t, instance.GetPort(), instanceResult.GetPort()) + assert.Equal(t, instance.GetServiceName(), instanceResult.GetServiceName()) + metadata := instanceResult.GetMetadata() + assert.Equal(t, 0, len(metadata)) + + instance.GetMetadata()["aaa"] = "bbb" + err = serviceDiscovery.Update(instance) + assert.Nil(t, err) + + time.Sleep(3 * time.Second) + pageMap := serviceDiscovery.GetRequestInstances([]string{instance.GetServiceName()}, 0, 1) + assert.Equal(t, 1, len(pageMap)) + + page = pageMap[instance.GetServiceName()] + assert.NotNil(t, page) + assert.Equal(t, 1, len(page.GetData())) + + instanceResult = page.GetData()[0].(*registry.DefaultServiceInstance) + v, _ := instanceResult.Metadata["aaa"] + assert.Equal(t, "bbb", v) + + // test dispatcher event + //err = serviceDiscovery.DispatchEventByServiceName(instanceResult.GetServiceName()) + //assert.Nil(t, err) + + // test AddListener + err = serviceDiscovery.AddListener(®istry.ServiceInstancesChangedListener{ServiceName: instance.GetServiceName()}) + assert.Nil(t, err) + err = serviceDiscovery.Unregister(instance) + assert.Nil(t, err) + timer := time.NewTimer(time.Second * 10) + select { + case <-eventDispatcher.Notify: + assert.NotNil(t, eventDispatcher.Event) + break + case <-timer.C: + assert.Fail(t, "") + break + } +} + +func prepareData() { + config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{ + Protocol: "consul", + RemoteRef: testName, + } + + config.GetBaseConfig().Remotes[testName] = &config.RemoteConfig{ + Address: fmt.Sprintf("%s:%d", registryHost, registryPort), + } +} + +func prepareService() (registry.ServiceInstance, common.URL) { + id := "id" + + registryUrl, _ := common.NewURL(protocol + "://" + providerHost + ":" + strconv.Itoa(providerPort) + "/" + service + "?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&consul-check-pass-interval=" + strconv.Itoa(consulCheckPassInterval) + "&consul-deregister-critical-service-after=" + consulDeregisterCriticalServiceAfter + "&" + + "consul-watch-timeout=" + strconv.Itoa(consulWatchTimeout)) + + return ®istry.DefaultServiceInstance{ + Id: id, + ServiceName: service, + Host: registryHost, + Port: registryPort, + Enable: true, + Healthy: true, + Metadata: nil, + }, registryUrl +} + +type MockEventDispatcher struct { + Notify chan struct{} + Event observer.Event +} + +// AddEventListener do nothing +func (m *MockEventDispatcher) AddEventListener(listener observer.EventListener) { +} + +// AddEventListeners do nothing +func (m *MockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) { +} + +// RemoveEventListener do nothing +func (m *MockEventDispatcher) RemoveEventListener(listener observer.EventListener) { +} + +// RemoveEventListeners do nothing +func (m *MockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) { +} + +// GetAllEventListeners return empty list +func (m *MockEventDispatcher) GetAllEventListeners() []observer.EventListener { + return make([]observer.EventListener, 0) +} + +// RemoveAllEventListeners do nothing +func (m *MockEventDispatcher) RemoveAllEventListeners() { +} + +// Dispatch do nothing +func (m *MockEventDispatcher) Dispatch(event observer.Event) { + m.Event = event + m.Notify <- struct{}{} +} diff --git a/registry/directory/directory.go b/registry/directory/directory.go index 2fbf9410f76c473362964c9ef148e3c581d3d045..8871a2a26e406145679e2911256ffb633fbb7f8c 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -18,6 +18,7 @@ package directory import ( + "fmt" "sync" ) @@ -89,69 +90,120 @@ func (dir *RegistryDirectory) subscribe(url *common.URL) { } // Notify monitor changes from registry,and update the cacheServices -func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) { - go dir.update(event) +func (dir *RegistryDirectory) Notify(events ...*registry.ServiceEvent) { + go dir.refreshInvokers(events...) } -// update the cacheServices and subscribe service from registry -func (dir *RegistryDirectory) update(res *registry.ServiceEvent) { - if res == nil { - return +// refreshInvokers refreshes service's events. It supports two modes: incremental mode and batch mode. If a single +// service event is passed in, then it is incremental mode, and if an array of service events are passed in, it is +// batch mode, in this mode, we assume the registry center have the complete list of the service events, therefore +// in this case, we can safely assume any cached invoker not in the incoming list can be removed. It is necessary +// since in batch mode, the register center handles the different type of events by itself, then notify the directory +// a batch of 'Update' events, instead of omit the different type of event one by one. +func (dir *RegistryDirectory) refreshInvokers(events ...*registry.ServiceEvent) { + var oldInvokers []protocol.Invoker + + // in batch mode, it is safe to remove since we have the complete list of events. + if len(events) > 1 { + dir.cacheInvokersMap.Range(func(k, v interface{}) bool { + if !dir.eventMatched(k.(string), events) { + if invoker := dir.uncacheInvokerWithKey(k.(string)); invoker != nil { + oldInvokers = append(oldInvokers, invoker) + } + } + return true + }) } - logger.Debugf("registry update, result{%s}", res) - logger.Debugf("update service name: %s!", res.Service) - dir.refreshInvokers(res) -} - -func (dir *RegistryDirectory) refreshInvokers(res *registry.ServiceEvent) { - var ( - url *common.URL - oldInvoker protocol.Invoker = nil - ) - // judge is override or others - if res != nil { - url = &res.Service - // 1.for override url in 2.6.x - if url.Protocol == constant.OVERRIDE_PROTOCOL || - url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY { - dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(url)) - url = nil - } else if url.Protocol == constant.ROUTER_PROTOCOL || // 2.for router - url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY { - url = nil + for _, event := range events { + logger.Debugf("registry update, result{%s}", event) + if oldInvoker, _ := dir.cacheInvokerByEvent(event); oldInvoker != nil { + oldInvokers = append(oldInvokers, oldInvoker) } - switch res.Action { - case remoting.EventTypeAdd, remoting.EventTypeUpdate: - logger.Infof("selector add service url{%s}", res.Service) + } - var urls []*common.URL - for _, v := range config.GetRouterURLSet().Values() { - urls = append(urls, v.(*common.URL)) - } + if len(events) > 0 { + dir.setNewInvokers() + } - if len(urls) > 0 { - dir.SetRouters(urls) - } - oldInvoker = dir.cacheInvoker(url) - case remoting.EventTypeDel: - oldInvoker = dir.uncacheInvoker(url) - logger.Infof("selector delete service url{%s}", res.Service) - default: - return + // After dir.cacheInvokers is updated,destroy the oldInvoker + // Ensure that no request will enter the oldInvoker + for _, invoker := range oldInvokers { + invoker.Destroy() + } +} + +// eventMatched checks if a cached invoker appears in the incoming invoker list, if no, then it is safe to remove. +func (dir *RegistryDirectory) eventMatched(key string, events []*registry.ServiceEvent) bool { + for _, event := range events { + if dir.invokerCacheKey(&event.Service) == key { + return true } } + return false +} +// invokerCacheKey generates the key in the cache for a given URL. +func (dir *RegistryDirectory) invokerCacheKey(url *common.URL) string { + referenceUrl := dir.GetDirectoryUrl().SubURL + newUrl := common.MergeUrl(url, referenceUrl) + return newUrl.Key() +} + +// setNewInvokers groups the invokers from the cache first, then set the result to both directory and router chain. +func (dir *RegistryDirectory) setNewInvokers() { newInvokers := dir.toGroupInvokers() dir.listenerLock.Lock() + defer dir.listenerLock.Unlock() dir.cacheInvokers = newInvokers - dir.listenerLock.Unlock() - // After dir.cacheInvokers is updated,destroy the oldInvoker - // Ensure that no request will enter the oldInvoker - if oldInvoker != nil { - oldInvoker.Destroy() + dir.RouterChain().SetInvokers(newInvokers) +} + +// cacheInvokerByEvent caches invokers from the service event +func (dir *RegistryDirectory) cacheInvokerByEvent(event *registry.ServiceEvent) (protocol.Invoker, error) { + // judge is override or others + if event != nil { + u := dir.convertUrl(event) + switch event.Action { + case remoting.EventTypeAdd, remoting.EventTypeUpdate: + logger.Infof("selector add service url{%s}", event.Service) + // FIXME: routers are built in every address notification? + dir.configRouters() + return dir.cacheInvoker(u), nil + case remoting.EventTypeDel: + logger.Infof("selector delete service url{%s}", event.Service) + return dir.uncacheInvoker(u), nil + default: + return nil, fmt.Errorf("illegal event type: %v", event.Action) + } + } + return nil, nil +} + +// configRouters configures dynamic routers into the router chain, but, the current impl is incorrect, see FIXME above. +func (dir *RegistryDirectory) configRouters() { + var urls []*common.URL + for _, v := range config.GetRouterURLSet().Values() { + urls = append(urls, v.(*common.URL)) } + if len(urls) > 0 { + dir.SetRouters(urls) + } +} + +// convertUrl processes override:// and router:// +func (dir *RegistryDirectory) convertUrl(res *registry.ServiceEvent) *common.URL { + ret := &res.Service + if ret.Protocol == constant.OVERRIDE_PROTOCOL || // 1.for override url in 2.6.x + ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.CONFIGURATORS_CATEGORY { + dir.configurators = append(dir.configurators, extension.GetDefaultConfigurator(ret)) + ret = nil + } else if ret.Protocol == constant.ROUTER_PROTOCOL || // 2.for router + ret.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY) == constant.ROUTER_CATEGORY { + ret = nil + } + return ret } func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { @@ -197,11 +249,15 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { return groupInvokersList } -// uncacheInvoker will return abandoned Invoker,if no Invoker to be abandoned,return nil +// uncacheInvoker will return abandoned Invoker, if no Invoker to be abandoned, return nil func (dir *RegistryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker { - logger.Debugf("service will be deleted in cache invokers: invokers key is %s!", url.Key()) - if cacheInvoker, ok := dir.cacheInvokersMap.Load(url.Key()); ok { - dir.cacheInvokersMap.Delete(url.Key()) + return dir.uncacheInvokerWithKey(url.Key()) +} + +func (dir *RegistryDirectory) uncacheInvokerWithKey(key string) protocol.Invoker { + logger.Debugf("service will be deleted in cache invokers: invokers key is %s!", key) + if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); ok { + dir.cacheInvokersMap.Delete(key) return cacheInvoker.(protocol.Invoker) } return nil @@ -232,6 +288,12 @@ func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker { dir.cacheInvokersMap.Store(newUrl.Key(), newInvoker) } } else { + // if cached invoker has the same URL with the new URL, then no need to re-refer, and no need to destroy + // the old invoker. + if common.IsEquals(*newUrl, cacheInvoker.(protocol.Invoker).GetUrl()) { + return nil + } + logger.Debugf("service will be updated in cache invokers: new invoker url is %s, old invoker url is %s", newUrl, cacheInvoker.(protocol.Invoker).GetUrl()) newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(*newUrl) if newInvoker != nil { @@ -312,7 +374,8 @@ func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL) // Process handle events and update Invokers func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) - l.directory.refreshInvokers(nil) + // FIXME: this doesn't trigger dir.overrideUrl() + l.directory.refreshInvokers() } type consumerConfigurationListener struct { @@ -338,5 +401,6 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti // Process handles events from Configuration Center and update Invokers func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) - l.directory.refreshInvokers(nil) + // FIXME: this doesn't trigger dir.overrideUrl() + l.directory.refreshInvokers() } diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index cc4ea257ccabb5591689ec961ae279e12f24dd29..1cf06d17dcd4c92afc5221dab404e9d180c03e6c 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -63,7 +63,6 @@ func (suite *RegistryTestSuite) SetupSuite() { } suite.etcd = e - return } // stop etcd server diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go index 2fec8eaad25e716fc5ed5ee33775d8898cb212e2..9cbc4945605c2ac42242d7c0727e8cd487703c9c 100644 --- a/registry/etcdv3/registry.go +++ b/registry/etcdv3/registry.go @@ -112,8 +112,9 @@ func (r *etcdV3Registry) InitListeners() { } // DoRegister actually do the register job in the registry center of etcd +// for lease func (r *etcdV3Registry) DoRegister(root string, node string) error { - return r.client.Create(path.Join(root, node), "") + return r.client.RegisterTemp(path.Join(root, node), "") } // nolint diff --git a/registry/event/service_revision_customizer.go b/registry/event/service_revision_customizer.go index fd21e8f4c7a71cedfe1de7e9c836e7cee278182e..4793e91948fe4c30fffbfd21f0dcc3efe57c5095 100644 --- a/registry/event/service_revision_customizer.go +++ b/registry/event/service_revision_customizer.go @@ -126,7 +126,7 @@ func resolveRevision(urls []interface{}) string { // append url params if we need it } - sort.Sort(sort.StringSlice(candidates)) + sort.Strings(candidates) // it's nearly impossible to be overflow res := uint64(0) diff --git a/registry/file/listener.go b/registry/file/listener.go new file mode 100644 index 0000000000000000000000000000000000000000..3fe7400226067f1232ed7993b1fe1b5575b870df --- /dev/null +++ b/registry/file/listener.go @@ -0,0 +1,29 @@ +/* + * 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 file + +import "github.com/apache/dubbo-go/config_center" + +// RegistryConfigurationListener represent the processor of flie watcher +type RegistryConfigurationListener struct { +} + +// Process submit the ConfigChangeEvent to the event chan to notify all observer +func (l *RegistryConfigurationListener) Process(configType *config_center.ConfigChangeEvent) { + +} diff --git a/registry/file/service_discovery.go b/registry/file/service_discovery.go new file mode 100644 index 0000000000000000000000000000000000000000..59c5cf9a89140be537e4f08816e8a080a7f6a6fc --- /dev/null +++ b/registry/file/service_discovery.go @@ -0,0 +1,285 @@ +/* + * 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 file + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path" + "strconv" +) + +import ( + gxset "github.com/dubbogo/gost/container/set" + gxpage "github.com/dubbogo/gost/page" + 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/config" + "github.com/apache/dubbo-go/config_center" + "github.com/apache/dubbo-go/config_center/file" + "github.com/apache/dubbo-go/registry" +) + +// init will put the service discovery into extension +func init() { + extension.SetServiceDiscovery(constant.FILE_KEY, newFileSystemServiceDiscovery) +} + +// fileServiceDiscovery is the implementation of service discovery based on file. +type fileSystemServiceDiscovery struct { + dynamicConfiguration file.FileSystemDynamicConfiguration + rootPath string + fileMap map[string]string +} + +func newFileSystemServiceDiscovery(name string) (registry.ServiceDiscovery, error) { + sdc, ok := config.GetBaseConfig().GetServiceDiscoveries(name) + if !ok || sdc.Protocol != constant.FILE_KEY { + return nil, perrors.New("could not init the instance because the config is invalid") + } + + rp, err := file.Home() + if err != nil { + return nil, perrors.WithStack(err) + } + + fdcf := extension.GetConfigCenterFactory(constant.FILE_KEY) + p := path.Join(rp, ".dubbo", constant.REGISTRY_KEY) + url, _ := common.NewURL("") + url.AddParamAvoidNil(file.CONFIG_CENTER_DIR_PARAM_NAME, p) + c, err := fdcf.GetDynamicConfiguration(&url) + if err != nil { + return nil, perrors.WithStack(err) + } + + sd := &fileSystemServiceDiscovery{ + dynamicConfiguration: *c.(*file.FileSystemDynamicConfiguration), + rootPath: p, + fileMap: make(map[string]string), + } + + extension.AddCustomShutdownCallback(func() { + sd.Destroy() + }) + + for _, v := range sd.GetServices().Values() { + for _, i := range sd.GetInstances(v.(string)) { + // like java do nothing + l := &RegistryConfigurationListener{} + sd.dynamicConfiguration.AddListener(getServiceInstanceId(i), l, config_center.WithGroup(getServiceName(i))) + } + } + + return sd, nil +} + +// nolint +func (fssd *fileSystemServiceDiscovery) String() string { + return fmt.Sprintf("file-system-service-discovery") +} + +// Destroy will destroy the service discovery. +// If the discovery cannot be destroy, it will return an error. +func (fssd *fileSystemServiceDiscovery) Destroy() error { + fssd.dynamicConfiguration.Close() + + for _, f := range fssd.fileMap { + fssd.releaseAndRemoveRegistrationFiles(f) + } + + return nil +} + +// nolint +func (fssd *fileSystemServiceDiscovery) releaseAndRemoveRegistrationFiles(file string) { + os.RemoveAll(file) +} + +// ----------------- registration ---------------- + +// Register will register an instance of ServiceInstance to registry +func (fssd *fileSystemServiceDiscovery) Register(instance registry.ServiceInstance) error { + id := getServiceInstanceId(instance) + sn := getServiceName(instance) + + c, err := toJsonString(instance) + if err != nil { + return perrors.WithStack(err) + } + + err = fssd.dynamicConfiguration.PublishConfig(id, sn, c) + if err != nil { + return perrors.WithStack(err) + } + + fssd.fileMap[id] = fssd.dynamicConfiguration.GetPath(id, sn) + + return nil +} + +// nolint +func getServiceInstanceId(si registry.ServiceInstance) string { + if si.GetId() == "" { + return si.GetHost() + "." + strconv.Itoa(si.GetPort()) + } + + return si.GetId() +} + +// nolint +func getServiceName(si registry.ServiceInstance) string { + return si.GetServiceName() +} + +// toJsonString to json string +func toJsonString(si registry.ServiceInstance) (string, error) { + bytes, err := json.Marshal(si) + if err != nil { + return "", perrors.WithStack(err) + } + + return string(bytes), nil +} + +// Update will update the data of the instance in registry +func (fssd *fileSystemServiceDiscovery) Update(instance registry.ServiceInstance) error { + return fssd.Register(instance) +} + +// Unregister will unregister this instance from registry +func (fssd *fileSystemServiceDiscovery) Unregister(instance registry.ServiceInstance) error { + id := getServiceInstanceId(instance) + sn := getServiceName(instance) + + err := fssd.dynamicConfiguration.RemoveConfig(id, sn) + if err != nil { + return perrors.WithStack(err) + } + + delete(fssd.fileMap, instance.GetId()) + return nil +} + +// ----------------- discovery ------------------- +// GetDefaultPageSize will return the default page size +func (fssd *fileSystemServiceDiscovery) GetDefaultPageSize() int { + return 100 +} + +// GetServices will return the all service names. +func (fssd *fileSystemServiceDiscovery) GetServices() *gxset.HashSet { + r := gxset.NewSet() + // dynamicConfiguration root path is the actual root path + fileInfo, _ := ioutil.ReadDir(fssd.dynamicConfiguration.RootPath()) + + for _, file := range fileInfo { + if file.IsDir() { + r.Add(file.Name()) + } + } + + return r +} + +// GetInstances will return all service instances with serviceName +func (fssd *fileSystemServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { + set, err := fssd.dynamicConfiguration.GetConfigKeysByGroup(serviceName) + if err != nil { + logger.Errorf("[FileServiceDiscovery] Could not query the instances for service{%s}, error = err{%v} ", + serviceName, err) + return make([]registry.ServiceInstance, 0, 0) + } + + res := make([]registry.ServiceInstance, 0, set.Size()) + for _, v := range set.Values() { + id := v.(string) + p, err := fssd.dynamicConfiguration.GetProperties(id, config_center.WithGroup(serviceName)) + if err != nil { + logger.Errorf("[FileServiceDiscovery] Could not get the properties for id{%s}, service{%s}, "+ + "error = err{%v} ", + id, serviceName, err) + return make([]registry.ServiceInstance, 0, 0) + } + + dsi := ®istry.DefaultServiceInstance{} + err = json.Unmarshal([]byte(p), dsi) + if err != nil { + logger.Errorf("[FileServiceDiscovery] Could not unmarshal the properties for id{%s}, service{%s}, "+ + "error = err{%v} ", + id, serviceName, err) + return make([]registry.ServiceInstance, 0, 0) + } + + res = append(res, dsi) + } + + return res +} + +// GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName +// the page will start at offset +func (fssd *fileSystemServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { + return nil +} + +// GetHealthyInstancesByPage will return a page containing instances of ServiceInstance. +// The param healthy indices that the instance should be healthy or not. +// The page will start at offset +func (fssd *fileSystemServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, + healthy bool) gxpage.Pager { + return nil +} + +// Batch get all instances by the specified service names +func (fssd *fileSystemServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, + requestedSize int) map[string]gxpage.Pager { + return nil +} + +// ----------------- event ---------------------- +// AddListener adds a new ServiceInstancesChangedListener +// client +func (fssd *fileSystemServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { + //fssd.dynamicConfiguration.AddListener(listener.ServiceName) + return nil +} + +// DispatchEventByServiceName dispatches the ServiceInstancesChangedEvent to service instance whose name is serviceName +func (fssd *fileSystemServiceDiscovery) DispatchEventByServiceName(serviceName string) error { + return fssd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, fssd.GetInstances(serviceName))) +} + +// DispatchEventForInstances dispatches the ServiceInstancesChangedEvent to target instances +func (fssd *fileSystemServiceDiscovery) DispatchEventForInstances(serviceName string, + instances []registry.ServiceInstance) error { + return fssd.DispatchEvent(registry.NewServiceInstancesChangedEvent(serviceName, instances)) +} + +// DispatchEvent dispatches the event +func (fssd *fileSystemServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { + extension.GetGlobalDispatcher().Dispatch(event) + return nil +} diff --git a/registry/file/service_discovery_test.go b/registry/file/service_discovery_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0bffcae31d039a49d8cf696a6de2f6858c42ada2 --- /dev/null +++ b/registry/file/service_discovery_test.go @@ -0,0 +1,89 @@ +/* + * 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 file + +import ( + "math/rand" + "strconv" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/registry" +) + +var ( + testName = "test" +) + +func TestNewFileSystemServiceDiscoveryAndDestroy(t *testing.T) { + prepareData() + serviceDiscovery, err := newFileSystemServiceDiscovery(testName) + assert.NoError(t, err) + assert.NotNil(t, serviceDiscovery) + defer serviceDiscovery.Destroy() +} + +func TestCURDFileSystemServiceDiscovery(t *testing.T) { + prepareData() + serviceDiscovery, err := extension.GetServiceDiscovery(constant.FILE_KEY, testName) + assert.NoError(t, err) + md := make(map[string]string) + + rand.Seed(time.Now().Unix()) + serviceName := "service-name" + strconv.Itoa(rand.Intn(10000)) + md["t1"] = "test1" + r1 := ®istry.DefaultServiceInstance{ + Id: "123456789", + ServiceName: serviceName, + Host: "127.0.0.1", + Port: 2233, + Enable: true, + Healthy: true, + Metadata: md, + } + err = serviceDiscovery.Register(r1) + assert.NoError(t, err) + + instances := serviceDiscovery.GetInstances(r1.ServiceName) + assert.Equal(t, 1, len(instances)) + assert.Equal(t, r1.Id, instances[0].GetId()) + assert.Equal(t, r1.ServiceName, instances[0].GetServiceName()) + assert.Equal(t, r1.Port, instances[0].GetPort()) + + err = serviceDiscovery.Unregister(r1) + assert.NoError(t, err) + + err = serviceDiscovery.Register(r1) + + defer serviceDiscovery.Destroy() +} + +func prepareData() { + config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{ + Protocol: "file", + } +} diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index c0608ad7989e69c104f07aa95e9a77fb9ac212fa..69a31ef2f2897013cf67ed6ae320c7550c5fe912 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -22,6 +22,7 @@ import ( "strings" "sync" ) + import ( gxset "github.com/dubbogo/gost/container/set" ) @@ -31,7 +32,6 @@ 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/common/proxy/proxy_factory" "github.com/apache/dubbo-go/config" "github.com/apache/dubbo-go/config_center" _ "github.com/apache/dubbo-go/config_center/configurator" @@ -54,9 +54,10 @@ var ( type registryProtocol struct { invokers []protocol.Invoker - // Registry Map<RegistryAddress, Registry> + // Registry Map<RegistryAddress, Registry> registries *sync.Map - // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer exposed. + // To solve the problem of RMI repeated exposure port conflicts, + // the services that have been exposed are no longer exposed. // providerurl <--> exporter bounds *sync.Map overrideListeners *sync.Map @@ -100,10 +101,9 @@ func getUrlToRegistry(providerUrl *common.URL, registryUrl *common.URL) *common. // filterHideKey filter the parameters that do not need to be output in url(Starting with .) func filterHideKey(url *common.URL) *common.URL { - // be careful params maps in url is map type removeSet := gxset.NewSet() - for k, _ := range url.GetParams() { + for k := range url.GetParams() { if strings.HasPrefix(k, ".") { removeSet.Add(k) } @@ -139,7 +139,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { } var reg registry.Registry - if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { reg = getRegistry(®istryUrl) proto.registries.Store(registryUrl.Key(), reg) @@ -150,7 +149,7 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { // new registry directory for store service url from registry directory, err := extension.GetDefaultRegistryDirectory(®istryUrl, reg) if err != nil { - logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!", + logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!", serviceUrl.String(), err.Error()) return nil } @@ -163,7 +162,6 @@ func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { // new cluster invoker cluster := extension.GetCluster(serviceUrl.GetParam(constant.CLUSTER_KEY, constant.DEFAULT_CLUSTER)) - invoker := cluster.Join(directory) proto.invokers = append(proto.invokers, invoker) return invoker @@ -204,7 +202,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte } key := getCacheKey(providerUrl) - logger.Infof("The cached exporter keys is %v !", key) + logger.Infof("The cached exporter keys is %v!", key) cachedExporter, loaded := proto.bounds.Load(key) if loaded { logger.Infof("The exporter has been cached, and will return cached exporter!") @@ -217,7 +215,6 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte go reg.Subscribe(overriderUrl, overrideSubscribeListener) return cachedExporter.(protocol.Exporter) - } func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common.URL) { @@ -229,7 +226,6 @@ func (proto *registryProtocol) reExport(invoker protocol.Invoker, newUrl *common proto.bounds.Delete(key) proto.Export(wrappedNewInvoker) // TODO: unregister & unsubscribe - } } @@ -245,7 +241,12 @@ func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Inv } // Notify will be triggered when a service change notification is received. -func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) { +func (nl *overrideSubscribeListener) Notify(events ...*registry.ServiceEvent) { + if len(events) == 0 { + return + } + + event := events[0] if isMatched(&(event.Service), nl.url) && event.Action == remoting.EventTypeAdd { nl.configurator = extension.GetDefaultConfigurator(&(event.Service)) nl.doOverrideIfNecessary() @@ -366,7 +367,7 @@ func (proto *registryProtocol) Destroy() { func getRegistryUrl(invoker protocol.Invoker) *common.URL { // here add * for return a new url url := invoker.GetUrl() - // if the protocol == registry ,set protocol the registry value in url.params + // if the protocol == registry, set protocol the registry value in url.params if url.Protocol == constant.REGISTRY_PROTOCOL { protocol := url.GetParam(constant.REGISTRY_KEY, "") url.Protocol = protocol @@ -406,8 +407,6 @@ func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoke // Invoke remote service base on URL of wrappedInvoker func (ivk *wrappedInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { - // get right url - ivk.invoker.(*proxy_factory.ProxyInvoker).BaseInvoker = *protocol.NewBaseInvoker(ivk.GetUrl()) return ivk.invoker.Invoke(ctx, invocation) } diff --git a/registry/registry.go b/registry/registry.go index 5e77eab186680671f27b44bbe2e6a6b964a28721..2225d2c1fc6d465bfbf27cd9622d7296e4596d8f 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -30,38 +30,49 @@ import ( // Registry Extension - Registry type Registry interface { common.Node - //used for service provider calling , register services to registry - //And it is also used for service consumer calling , register services cared about ,for dubbo's admin monitoring. + + // Register is used for service provider calling, register services + // to registry. And it is also used for service consumer calling, register + // services cared about, for dubbo's admin monitoring. Register(url common.URL) error // UnRegister is required to support the contract: - // 1. If it is the persistent stored data of dynamic=false, the registration data can not be found, then the IllegalStateException is thrown, otherwise it is ignored. + // 1. If it is the persistent stored data of dynamic=false, the + // registration data can not be found, then the IllegalStateException + // is thrown, otherwise it is ignored. // 2. Unregister according to the full url match. - // url Registration information , is not allowed to be empty, e.g: dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin + // url Registration information, is not allowed to be empty, e.g: + // dubbo://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin UnRegister(url common.URL) error - //When creating new registry extension,pls select one of the following modes. - //Will remove in dubbogo version v1.1.0 - //mode1 : return Listener with Next function which can return subscribe service event from registry - //Deprecated! - //subscribe(event.URL) (Listener, error) - - //Will replace mode1 in dubbogo version v1.1.0 - //mode2 : callback mode, subscribe with notify(notify listener). + // Subscribe is required to support the contract: + // When creating new registry extension, pls select one of the + // following modes. + // Will remove in dubbogo version v1.1.0 + // mode1: return Listener with Next function which can return + // subscribe service event from registry + // Deprecated! + // subscribe(event.URL) (Listener, error) + // Will replace mode1 in dubbogo version v1.1.0 + // mode2: callback mode, subscribe with notify(notify listener). Subscribe(*common.URL, NotifyListener) error // UnSubscribe is required to support the contract: // 1. If don't subscribe, ignore it directly. // 2. Unsubscribe by full URL match. - // url Subscription condition, not allowed to be empty, e.g. consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin + // url Subscription condition, not allowed to be empty, e.g. + // consumer://10.20.153.10/org.apache.dubbo.foo.BarService?version=1.0.0&application=kylin // listener A listener of the change event, not allowed to be empty UnSubscribe(*common.URL, NotifyListener) error } // nolint type NotifyListener interface { - // Notify supports notifications on the service interface and the dimension of the data type. - Notify(*ServiceEvent) + // Notify supports notifications on the service interface and the dimension of the data type. When a list of + // events are passed in, it's considered as a complete list, on the other side, if one single event is + // passed in, then it's a incremental event. Pls. note when a list (instead of single event) comes, + // the impl of NotifyListener may abandon the accumulated result from previous notifications. + Notify(...*ServiceEvent) } // Listener Deprecated! diff --git a/remoting/codec.go b/remoting/codec.go new file mode 100644 index 0000000000000000000000000000000000000000..607d1643cc1967e93bf5288d8d4c0788c73a735e --- /dev/null +++ b/remoting/codec.go @@ -0,0 +1,45 @@ +/* + * 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 remoting + +import ( + "bytes" +) + +// codec for exchangeClient +type Codec interface { + EncodeRequest(request *Request) (*bytes.Buffer, error) + EncodeResponse(response *Response) (*bytes.Buffer, error) + Decode(data []byte) (DecodeResult, int, error) +} + +type DecodeResult struct { + IsRequest bool + Result interface{} +} + +var ( + codec = make(map[string]Codec, 2) +) + +func RegistryCodec(protocol string, codecTmp Codec) { + codec[protocol] = codecTmp +} + +func GetCodec(protocol string) Codec { + return codec[protocol] +} diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go index 4e7436e445f652d8fe1db2991c87303653beb9ec..ebd454242d49ee82c81fe1a1fae1a19980c238a4 100644 --- a/remoting/etcdv3/client.go +++ b/remoting/etcdv3/client.go @@ -408,7 +408,8 @@ func (c *Client) keepAliveKV(k string, v string) error { return ErrNilETCDV3Client } - lease, err := c.rawClient.Grant(c.ctx, int64(time.Second.Seconds())) + // make lease time longer, since 1 second is too short + lease, err := c.rawClient.Grant(c.ctx, int64(30*time.Second.Seconds())) if err != nil { return perrors.WithMessage(err, "grant lease") } diff --git a/remoting/exchange.go b/remoting/exchange.go new file mode 100644 index 0000000000000000000000000000000000000000..848d9cbbcc23b0f565c45b646cf443be3f811efc --- /dev/null +++ b/remoting/exchange.go @@ -0,0 +1,144 @@ +/* + * 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 remoting + +import ( + "time" +) + +import ( + "go.uber.org/atomic" +) + +import ( + "github.com/apache/dubbo-go/common" +) + +var ( + // generate request ID for global use + sequence atomic.Int64 +) + +func init() { + // init request ID + sequence.Store(0) +} + +func SequenceId() int64 { + // increse 2 for every request as the same before. + // We expect that the request from client to server, the requestId is even; but from server to client, the requestId is odd. + return sequence.Add(2) +} + +// this is request for transport layer +type Request struct { + ID int64 + // protocol version + Version string + // serial ID (ignore) + SerialID byte + // Data + Data interface{} + TwoWay bool + Event bool +} + +// NewRequest aims to create Request. +// The ID is auto increase. +func NewRequest(version string) *Request { + return &Request{ + ID: SequenceId(), + Version: version, + } +} + +// this is response for transport layer +type Response struct { + ID int64 + Version string + SerialID byte + Status uint8 + Event bool + Error error + Result interface{} +} + +// NewResponse create to a new Response. +func NewResponse(id int64, version string) *Response { + return &Response{ + ID: id, + Version: version, + } +} + +// the response is heartbeat +func (response *Response) IsHeartbeat() bool { + return response.Event && response.Result == nil +} + +type Options struct { + // connect timeout + ConnectTimeout time.Duration +} + +//AsyncCallbackResponse async response for dubbo +type AsyncCallbackResponse struct { + common.CallbackResponse + Opts Options + Cause error + Start time.Time // invoke(call) start time == write start time + ReadStart time.Time // read start time, write duration = ReadStart - Start + Reply interface{} +} + +// the client sends requst to server, there is one pendingResponse at client side to wait the response from server +type PendingResponse struct { + seq int64 + Err error + start time.Time + ReadStart time.Time + Callback common.AsyncCallback + response *Response + Reply interface{} + Done chan struct{} +} + +// NewPendingResponse aims to create PendingResponse. +// Id is always from ID of Request +func NewPendingResponse(id int64) *PendingResponse { + return &PendingResponse{ + seq: id, + start: time.Now(), + response: &Response{}, + Done: make(chan struct{}), + } +} + +func (r *PendingResponse) SetResponse(response *Response) { + r.response = response +} + +// GetCallResponse is used for callback of async. +// It is will return AsyncCallbackResponse. +func (r PendingResponse) GetCallResponse() common.CallbackResponse { + return AsyncCallbackResponse{ + Cause: r.Err, + Start: r.start, + ReadStart: r.ReadStart, + Reply: r.response, + } +} diff --git a/remoting/exchange_client.go b/remoting/exchange_client.go new file mode 100644 index 0000000000000000000000000000000000000000..efcfca55868e7042b685adb5fb2452dd2d3a65c0 --- /dev/null +++ b/remoting/exchange_client.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. + */ +package remoting + +import ( + "errors" + "sync" + "time" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" +) + +var ( + // store requestID and response + pendingResponses = new(sync.Map) +) + +type SequenceType int64 + +// It is interface of client for network communication. +// If you use getty as network communication, you should define GettyClient that implements this interface. +type Client interface { + SetExchangeClient(client *ExchangeClient) + // responseHandler is used to deal with msg + SetResponseHandler(responseHandler ResponseHandler) + // connect url + Connect(url common.URL) error + // close + Close() + // send request to server. + Request(request *Request, timeout time.Duration, response *PendingResponse) error +} + +// This is abstraction level. it is like facade. +type ExchangeClient struct { + // connect server timeout + ConnectTimeout time.Duration + // to dial server address. The format: ip:port + address string + // the client that will deal with the transport. It is interface, and it will use gettyClient by default. + client Client + // the tag for init. + init bool +} + +// handle the message from server +type ResponseHandler interface { + Handler(response *Response) +} + +// create ExchangeClient +func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient { + exchangeClient := &ExchangeClient{ + ConnectTimeout: connectTimeout, + address: url.Location, + client: client, + } + client.SetExchangeClient(exchangeClient) + if !lazyInit { + if err := exchangeClient.doInit(url); err != nil { + return nil + } + } + + client.SetResponseHandler(exchangeClient) + return exchangeClient +} + +func (cl *ExchangeClient) doInit(url common.URL) error { + if cl.init { + return nil + } + if cl.client.Connect(url) != nil { + //retry for a while + time.Sleep(100 * time.Millisecond) + if cl.client.Connect(url) != nil { + logger.Errorf("Failed to connect server %+v " + url.Location) + return errors.New("Failed to connect server " + url.Location) + } + } + //FIXME atomic operation + cl.init = true + return nil +} + +// two way request +func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, + result *protocol.RPCResult) error { + if er := client.doInit(url); er != nil { + return er + } + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = true + + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") + rsp.Reply = (*invocation).Reply() + AddPendingResponse(rsp) + + err := client.client.Request(request, timeout, rsp) + // request error + if err != nil { + result.Err = err + return err + } + if resultTmp, ok := rsp.response.Result.(*protocol.RPCResult); ok { + result.Rest = resultTmp.Rest + result.Attrs = resultTmp.Attrs + result.Err = resultTmp.Err + } + return nil +} + +// async two way request +func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, + callback common.AsyncCallback, result *protocol.RPCResult) error { + if er := client.doInit(url); er != nil { + return er + } + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = true + + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") + rsp.Callback = callback + rsp.Reply = (*invocation).Reply() + AddPendingResponse(rsp) + + err := client.client.Request(request, timeout, rsp) + if err != nil { + result.Err = err + return err + } + result.Rest = rsp.response + return nil +} + +// oneway request +func (client *ExchangeClient) Send(invocation *protocol.Invocation, url common.URL, timeout time.Duration) error { + if er := client.doInit(url); er != nil { + return er + } + request := NewRequest("2.0.2") + request.Data = invocation + request.Event = false + request.TwoWay = false + + rsp := NewPendingResponse(request.ID) + rsp.response = NewResponse(request.ID, "2.0.2") + + err := client.client.Request(request, timeout, rsp) + if err != nil { + return err + } + return nil +} + +// close client +func (client *ExchangeClient) Close() { + client.client.Close() +} + +// handle the response from server +func (client *ExchangeClient) Handler(response *Response) { + + pendingResponse := removePendingResponse(SequenceType(response.ID)) + if pendingResponse == nil { + logger.Errorf("failed to get pending response context for response package %s", *response) + return + } + + pendingResponse.response = response + + if pendingResponse.Callback == nil { + pendingResponse.Err = pendingResponse.response.Error + pendingResponse.Done <- struct{}{} + } else { + pendingResponse.Callback(pendingResponse.GetCallResponse()) + } +} + +// store response into map +func AddPendingResponse(pr *PendingResponse) { + pendingResponses.Store(SequenceType(pr.seq), pr) +} + +// get and remove response +func removePendingResponse(seq SequenceType) *PendingResponse { + if pendingResponses == nil { + return nil + } + if presp, ok := pendingResponses.Load(seq); ok { + pendingResponses.Delete(seq) + return presp.(*PendingResponse) + } + return nil +} + +// get response +func GetPendingResponse(seq SequenceType) *PendingResponse { + if presp, ok := pendingResponses.Load(seq); ok { + return presp.(*PendingResponse) + } + return nil +} diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go new file mode 100644 index 0000000000000000000000000000000000000000..a31e994d7411df2da94c210a1b3d4b24aae9fcba --- /dev/null +++ b/remoting/exchange_server.go @@ -0,0 +1,55 @@ +/* + * 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 remoting + +import ( + "github.com/apache/dubbo-go/common" +) + +// It is interface of server for network communication. +// If you use getty as network communication, you should define GettyServer that implements this interface. +type Server interface { + //invoke once for connection + Start() + //it is for destroy + Stop() +} + +// This is abstraction level. it is like facade. +type ExchangeServer struct { + Server Server + Url common.URL +} + +// Create ExchangeServer +func NewExchangeServer(url common.URL, server Server) *ExchangeServer { + exchangServer := &ExchangeServer{ + Server: server, + Url: url, + } + return exchangServer +} + +// start server +func (server *ExchangeServer) Start() { + server.Server.Start() +} + +// stop server +func (server *ExchangeServer) Stop() { + server.Server.Stop() +} diff --git a/protocol/dubbo/config.go b/remoting/getty/config.go similarity index 96% rename from protocol/dubbo/config.go rename to remoting/getty/config.go index b47ec1cc3422dcbcac921f08888c7a777e72e246..dcf59d0821b90c052f736d1976400a1efe7d3445 100644 --- a/protocol/dubbo/config.go +++ b/remoting/getty/config.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package dubbo +package getty import ( "time" @@ -30,7 +30,7 @@ import ( ) type ( - // GettySessionParam is session configuration for getty. + // GettySessionParam is session configuration for getty GettySessionParam struct { CompressEncoding bool `default:"false" yaml:"compress_encoding" json:"compress_encoding,omitempty"` TcpNoDelay bool `default:"true" yaml:"tcp_no_delay" json:"tcp_no_delay,omitempty"` @@ -52,6 +52,8 @@ type ( // ServerConfig holds supported types by the multiconfig package ServerConfig struct { + SSLEnabled bool + // session SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` sessionTimeout time.Duration @@ -95,7 +97,7 @@ type ( } ) -// GetDefaultClientConfig gets client default configuration. +// GetDefaultClientConfig gets client default configuration func GetDefaultClientConfig() ClientConfig { return ClientConfig{ ReconnectInterval: 0, @@ -123,7 +125,7 @@ func GetDefaultClientConfig() ClientConfig { }} } -// GetDefaultServerConfig gets server default configuration. +// GetDefaultServerConfig gets server default configuration func GetDefaultServerConfig() ServerConfig { return ServerConfig{ SessionTimeout: "180s", @@ -148,7 +150,7 @@ func GetDefaultServerConfig() ServerConfig { } } -// CheckValidity confirm getty sessian params. +// CheckValidity confirm getty sessian params func (c *GettySessionParam) CheckValidity() error { var err error @@ -193,7 +195,7 @@ func (c *ClientConfig) CheckValidity() error { return perrors.WithStack(c.GettySessionParam.CheckValidity()) } -// CheckValidity confirm server params. +// CheckValidity confirm server params func (c *ServerConfig) CheckValidity() error { var err error diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b91fc9f4ccf69299870f9daf3707521d913cd4c0 --- /dev/null +++ b/remoting/getty/dubbo_codec_for_test.go @@ -0,0 +1,276 @@ +/* + * 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 getty + +// copy from dubbo/dubbo_codec.go . +// it is used to unit test. +import ( + "bytes" + "strconv" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/dubbo/impl" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" +) + +func init() { + codec := &DubboTestCodec{} + remoting.RegistryCodec("dubbo", codec) +} + +type DubboTestCodec struct { +} + +// encode request for transport +func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, error) { + if request.Event { + return c.encodeHeartbeartReqeust(request) + } + + invoc, ok := request.Data.(*invocation.RPCInvocation) + if !ok { + return nil, perrors.Errorf("encode request failed for parameter type :%+v", request) + } + invocation := *invoc + + svc := impl.Service{} + svc.Path = invocation.AttachmentsByKey(constant.PATH_KEY, "") + svc.Interface = invocation.AttachmentsByKey(constant.INTERFACE_KEY, "") + svc.Version = invocation.AttachmentsByKey(constant.VERSION_KEY, "") + svc.Group = invocation.AttachmentsByKey(constant.GROUP_KEY, "") + svc.Method = invocation.MethodName() + timeout, err := strconv.Atoi(invocation.AttachmentsByKey(constant.TIMEOUT_KEY, strconv.Itoa(constant.DEFAULT_REMOTING_TIMEOUT))) + if err != nil { + // it will be wrapped in readwrite.Write . + return nil, perrors.WithStack(err) + } + svc.Timeout = time.Duration(timeout) + + header := impl.DubboHeader{} + serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) + if serialization == constant.PROTOBUF_SERIALIZATION { + header.SerialID = constant.S_Proto + } else { + header.SerialID = constant.S_Hessian2 + } + header.ID = request.ID + if request.TwoWay { + header.Type = impl.PackageRequest_TwoWay + } else { + header.Type = impl.PackageRequest + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: svc, + Body: impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, perrors.WithStack(err) + } + + return pkg.Marshal() +} + +// encode heartbeart request +func (c *DubboTestCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { + header := impl.DubboHeader{ + Type: impl.PackageHeartbeat, + SerialID: constant.S_Hessian2, + ID: request.ID, + } + + pkg := &impl.DubboPackage{ + Header: header, + Service: impl.Service{}, + Body: impl.NewRequestPayload([]interface{}{}, nil), + Err: nil, + Codec: impl.NewDubboCodec(nil), + } + + if err := impl.LoadSerializer(pkg); err != nil { + return nil, err + } + return pkg.Marshal() +} + +// encode response +func (c *DubboTestCodec) EncodeResponse(response *remoting.Response) (*bytes.Buffer, error) { + var ptype = impl.PackageResponse + if response.IsHeartbeat() { + ptype = impl.PackageHeartbeat + } + resp := &impl.DubboPackage{ + Header: impl.DubboHeader{ + SerialID: response.SerialID, + Type: ptype, + ID: response.ID, + ResponseStatus: response.Status, + }, + } + if !response.IsHeartbeat() { + resp.Body = &impl.ResponsePayload{ + RspObj: response.Result.(protocol.RPCResult).Rest, + Exception: response.Result.(protocol.RPCResult).Err, + Attachments: response.Result.(protocol.RPCResult).Attrs, + } + } + + codec := impl.NewDubboCodec(nil) + + pkg, err := codec.Encode(*resp) + if err != nil { + return nil, perrors.WithStack(err) + } + + return bytes.NewBuffer(pkg), nil +} + +// Decode data, including request and response. +func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { + if c.isRequest(data) { + req, len, err := c.decodeRequest(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: true, Result: req}, len, perrors.WithStack(err) + } else { + resp, len, err := c.decodeResponse(data) + if err != nil { + return remoting.DecodeResult{}, len, perrors.WithStack(err) + } + return remoting.DecodeResult{IsRequest: false, Result: resp}, len, perrors.WithStack(err) + } +} + +func (c *DubboTestCodec) isRequest(data []byte) bool { + if data[2]&byte(0x80) == 0x00 { + return false + } + return true +} + +// decode request +func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, error) { + var request *remoting.Request = nil + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + pkg.SetBody(make([]interface{}, 7)) + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + //FIXME + return nil, 0, originErr + } + return request, 0, perrors.WithStack(err) + } + request = &remoting.Request{ + ID: pkg.Header.ID, + SerialID: pkg.Header.SerialID, + TwoWay: pkg.Header.Type&impl.PackageRequest_TwoWay != 0x00, + Event: pkg.Header.Type&impl.PackageHeartbeat != 0x00, + } + if (pkg.Header.Type & impl.PackageHeartbeat) == 0x00 { + // convert params of request + req := pkg.Body.(map[string]interface{}) + + //invocation := request.Data.(*invocation.RPCInvocation) + var methodName string + var args []interface{} + attachments := make(map[string]interface{}) + if req[impl.DubboVersionKey] != nil { + //dubbo version + request.Version = req[impl.DubboVersionKey].(string) + } + //path + attachments[constant.PATH_KEY] = pkg.Service.Path + //version + attachments[constant.VERSION_KEY] = pkg.Service.Version + //method + methodName = pkg.Service.Method + args = req[impl.ArgsKey].([]interface{}) + attachments = req[impl.AttachmentsKey].(map[string]interface{}) + invoc := invocation.NewRPCInvocationWithOptions(invocation.WithAttachments(attachments), + invocation.WithArguments(args), invocation.WithMethodName(methodName)) + request.Data = invoc + + } + return request, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} + +// decode response +func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, error) { + buf := bytes.NewBuffer(data) + pkg := impl.NewDubboPackage(buf) + response := &remoting.Response{} + err := pkg.Unmarshal() + if err != nil { + originErr := perrors.Cause(err) + // if the data is very big, so the receive need much times. + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, originErr + } + return nil, 0, perrors.WithStack(err) + } + response = &remoting.Response{ + ID: pkg.Header.ID, + //Version: pkg.Header., + SerialID: pkg.Header.SerialID, + Status: pkg.Header.ResponseStatus, + Event: (pkg.Header.Type & impl.PackageHeartbeat) != 0, + } + var error error + if pkg.Header.Type&impl.PackageHeartbeat != 0x00 { + if pkg.Header.Type&impl.PackageResponse != 0x00 { + if pkg.Err != nil { + error = pkg.Err + } + } else { + response.Status = hessian.Response_OK + //reply(session, p, hessian.PackageHeartbeat) + } + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, error + } + rpcResult := &protocol.RPCResult{} + response.Result = rpcResult + if pkg.Header.Type&impl.PackageRequest == 0x00 { + if pkg.Err != nil { + rpcResult.Err = pkg.Err + } else if pkg.Body.(*impl.ResponsePayload).Exception != nil { + rpcResult.Err = pkg.Body.(*impl.ResponsePayload).Exception + response.Error = rpcResult.Err + } + rpcResult.Attrs = pkg.Body.(*impl.ResponsePayload).Attachments + rpcResult.Rest = pkg.Body.(*impl.ResponsePayload).RspObj + } + + return response, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go new file mode 100644 index 0000000000000000000000000000000000000000..6af3971f5c1f9ff5b0fafbc00ae2ba3f44eb34b5 --- /dev/null +++ b/remoting/getty/getty_client.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. + */ + +package getty + +import ( + "math/rand" + "time" +) + +import ( + "github.com/apache/dubbo-getty" + gxsync "github.com/dubbogo/gost/sync" + perrors "github.com/pkg/errors" + "gopkg.in/yaml.v2" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/remoting" +) + +var ( + errInvalidCodecType = perrors.New("illegal CodecType") + errInvalidAddress = perrors.New("remote address invalid or empty") + errSessionNotExist = perrors.New("session not exist") + errClientClosed = perrors.New("client closed") + errClientReadTimeout = perrors.New("client read timeout") + + clientConf *ClientConfig + clientGrpool *gxsync.TaskPool +) + +// it is init client for single protocol. +func initClient(protocol string) { + if protocol == "" { + return + } + + // load clientconfig from consumer_config + // default use dubbo + consumerConfig := config.GetConsumerConfig() + if consumerConfig.ApplicationConfig == nil { + return + } + protocolConf := config.GetConsumerConfig().ProtocolConf + defaultClientConfig := GetDefaultClientConfig() + if protocolConf == nil { + logger.Info("protocol_conf default use dubbo config") + } else { + dubboConf := protocolConf.(map[interface{}]interface{})[protocol] + if dubboConf == nil { + logger.Warnf("dubboConf is nil") + return + } + dubboConfByte, err := yaml.Marshal(dubboConf) + if err != nil { + panic(err) + } + err = yaml.Unmarshal(dubboConfByte, &defaultClientConfig) + if err != nil { + panic(err) + } + } + clientConf = &defaultClientConfig + if err := clientConf.CheckValidity(); err != nil { + logger.Warnf("[CheckValidity] error: %v", err) + return + } + setClientGrpool() + + rand.Seed(time.Now().UnixNano()) +} + +// Config ClientConf +func SetClientConf(c ClientConfig) { + clientConf = &c + err := clientConf.CheckValidity() + if err != nil { + logger.Warnf("[ClientConfig CheckValidity] error: %v", err) + return + } + setClientGrpool() +} + +func setClientGrpool() { + if clientConf.GrPoolSize > 1 { + clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen), + gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber)) + } +} + +// Options : param config +type Options struct { + // connect timeout + // remove request timeout, it will be calulate for every request + ConnectTimeout time.Duration + // request timeout + RequestTimeout time.Duration +} + +// Client : some configuration for network communication. +type Client struct { + addr string + opts Options + conf ClientConfig + pool *gettyRPCClientPool + codec remoting.Codec + responseHandler remoting.ResponseHandler + ExchangeClient *remoting.ExchangeClient +} + +// create client +func NewClient(opt Options) *Client { + switch { + case opt.ConnectTimeout == 0: + opt.ConnectTimeout = 3 * time.Second + fallthrough + case opt.RequestTimeout == 0: + opt.RequestTimeout = 3 * time.Second + } + + c := &Client{ + opts: opt, + } + return c +} + +func (c *Client) SetExchangeClient(client *remoting.ExchangeClient) { + c.ExchangeClient = client +} +func (c *Client) SetResponseHandler(responseHandler remoting.ResponseHandler) { + c.responseHandler = responseHandler +} + +// init client and try to connection. +func (c *Client) Connect(url common.URL) error { + initClient(url.Protocol) + c.conf = *clientConf + // new client + c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) + c.pool.sslEnabled = url.GetParamBool(constant.SSL_ENABLED_KEY, false) + + // codec + c.codec = remoting.GetCodec(url.Protocol) + c.addr = url.Location + _, _, err := c.selectSession(c.addr) + if err != nil { + logger.Errorf("try to connect server %v failed for : %v", url.Location, err) + } + return err +} + +// close network connection +func (c *Client) Close() { + if c.pool != nil { + c.pool.close() + } + c.pool = nil +} + +// send request +func (c *Client) Request(request *remoting.Request, timeout time.Duration, response *remoting.PendingResponse) error { + _, session, err := c.selectSession(c.addr) + if err != nil { + return perrors.WithStack(err) + } + if session == nil { + return errSessionNotExist + } + + if err = c.transfer(session, request, timeout); err != nil { + return perrors.WithStack(err) + } + + if !request.TwoWay || response.Callback != nil { + return nil + } + + select { + case <-getty.GetTimeWheel().After(timeout): + return perrors.WithStack(errClientReadTimeout) + case <-response.Done: + err = response.Err + } + + return perrors.WithStack(err) +} + +func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { + rpcClient, err := c.pool.getGettyRpcClient(addr) + if err != nil { + return nil, nil, perrors.WithStack(err) + } + return rpcClient, rpcClient.selectSession(), nil +} + +func (c *Client) heartbeat(session getty.Session) error { + req := remoting.NewRequest("2.0.2") + req.TwoWay = true + req.Event = true + resp := remoting.NewPendingResponse(req.ID) + remoting.AddPendingResponse(resp) + return c.transfer(session, req, 3*time.Second) +} + +func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) error { + err := session.WritePkg(request, timeout) + return perrors.WithStack(err) +} diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go new file mode 100644 index 0000000000000000000000000000000000000000..41ca3108a8a8d578c6aaaf374dc9f5fa6300a8b0 --- /dev/null +++ b/remoting/getty/getty_client_test.go @@ -0,0 +1,492 @@ +/* + * 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 getty + +import ( + "bytes" + "context" + "reflect" + "sync" + "testing" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + . "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" +) + +func TestRunSuite(t *testing.T) { + svr, url := InitTest(t) + client := getClient(url) + testRequestOneWay(t, svr, url, client) + testClient_Call(t, svr, url, client) + testClient_AsyncCall(t, svr, url, client) + svr.Stop() +} + +func testRequestOneWay(t *testing.T, svr *Server, url common.URL, client *Client) { + + request := remoting.NewRequest("2.0.2") + up := &UserProvider{} + invocation := createInvocation("GetUser", nil, nil, []interface{}{[]interface{}{"1", "username"}, up}, + []reflect.Value{reflect.ValueOf([]interface{}{"1", "username"}), reflect.ValueOf(up)}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = false + //user := &User{} + err := client.Request(request, 3*time.Second, nil) + assert.NoError(t, err) +} + +func createInvocation(methodName string, callback interface{}, reply interface{}, arguments []interface{}, + parameterValues []reflect.Value) *invocation.RPCInvocation { + return invocation.NewRPCInvocationWithOptions(invocation.WithMethodName(methodName), + invocation.WithArguments(arguments), invocation.WithReply(reply), + invocation.WithCallBack(callback), invocation.WithParameterValues(parameterValues)) +} + +func setAttachment(invocation *invocation.RPCInvocation, attachments map[string]string) { + for key, value := range attachments { + invocation.SetAttachments(key, value) + } +} + +func getClient(url common.URL) *Client { + client := NewClient(Options{ + ConnectTimeout: config.GetConsumerConfig().ConnectTimeout, + }) + + exchangeClient := remoting.NewExchangeClient(url, client, 5*time.Second, false) + client.SetExchangeClient(exchangeClient) + client.Connect(url) + client.SetResponseHandler(exchangeClient) + return client +} + +func testClient_Call(t *testing.T, svr *Server, url common.URL, c *Client) { + c.pool = newGettyRPCClientConnPool(c, clientConf.PoolSize, time.Duration(int(time.Second)*clientConf.PoolTTL)) + + testGetBigPkg(t, c) + testGetUser(t, c) + testGetUser0(t, c) + testGetUser1(t, c) + testGetUser2(t, c) + testGetUser3(t, c) + testGetUser4(t, c) + testGetUser5(t, c) + testGetUser6(t, c) + testGetUser61(t, c) +} + +func testGetBigPkg(t *testing.T, c *Client) { + user := &User{} + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetBigPkg", nil, nil, []interface{}{[]interface{}{nil}, user}, + []reflect.Value{reflect.ValueOf([]interface{}{nil}), reflect.ValueOf(user)}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + pendingResponse.Reply = user + remoting.AddPendingResponse(pendingResponse) + err := c.Request(request, 8*time.Second, pendingResponse) + assert.NoError(t, err) + assert.NotEqual(t, "", user.Id) + assert.NotEqual(t, "", user.Name) +} + +func testGetUser(t *testing.T, c *Client) { + user := &User{} + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser", nil, nil, []interface{}{"1", "username"}, + []reflect.Value{reflect.ValueOf("1"), reflect.ValueOf("username")}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + pendingResponse.Reply = user + remoting.AddPendingResponse(pendingResponse) + err := c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.Equal(t, User{Id: "1", Name: "username"}, *user) +} + +func testGetUser0(t *testing.T, c *Client) { + var ( + user *User + err error + ) + user = &User{} + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser0", nil, nil, []interface{}{"1", nil, "username"}, + []reflect.Value{reflect.ValueOf("1"), reflect.ValueOf(nil), reflect.ValueOf("username")}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + rsp := remoting.NewPendingResponse(request.ID) + rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2")) + remoting.AddPendingResponse(rsp) + rsp.Reply = user + err = c.Request(request, 3*time.Second, rsp) + assert.NoError(t, err) + assert.Equal(t, User{Id: "1", Name: "username"}, *user) +} + +func testGetUser1(t *testing.T, c *Client) { + var ( + err error + ) + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser1", nil, nil, []interface{}{}, + []reflect.Value{}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + user := &User{} + pendingResponse.Reply = user + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) +} + +func testGetUser2(t *testing.T, c *Client) { + var ( + err error + ) + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser2", nil, nil, []interface{}{}, + []reflect.Value{}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.EqualError(t, err, "error") +} + +func testGetUser3(t *testing.T, c *Client) { + var ( + err error + ) + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser3", nil, nil, []interface{}{}, + []reflect.Value{}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + user2 := []interface{}{} + pendingResponse.Reply = &user2 + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) +} + +func testGetUser4(t *testing.T, c *Client) { + var ( + err error + ) + request := remoting.NewRequest("2.0.2") + invocation := invocation.NewRPCInvocation("GetUser4", []interface{}{[]interface{}{"1", "username"}}, nil) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + user2 := []interface{}{} + pendingResponse.Reply = &user2 + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.Equal(t, &User{Id: "1", Name: "username"}, user2[0]) +} + +func testGetUser5(t *testing.T, c *Client) { + var ( + err error + ) + request := remoting.NewRequest("2.0.2") + invocation := invocation.NewRPCInvocation("GetUser5", []interface{}{map[interface{}]interface{}{"id": "1", "name": "username"}}, nil) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + user3 := map[interface{}]interface{}{} + pendingResponse.Reply = &user3 + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.NotNil(t, user3) + assert.Equal(t, &User{Id: "1", Name: "username"}, user3["key"]) +} + +func testGetUser6(t *testing.T, c *Client) { + var ( + user *User + err error + ) + user = &User{} + request := remoting.NewRequest("2.0.2") + invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{0}, nil) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + pendingResponse.Reply = user + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.Equal(t, User{Id: "", Name: ""}, *user) +} + +func testGetUser61(t *testing.T, c *Client) { + var ( + user *User + err error + ) + user = &User{} + request := remoting.NewRequest("2.0.2") + invocation := invocation.NewRPCInvocation("GetUser6", []interface{}{1}, nil) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + pendingResponse := remoting.NewPendingResponse(request.ID) + pendingResponse.Reply = user + remoting.AddPendingResponse(pendingResponse) + err = c.Request(request, 3*time.Second, pendingResponse) + assert.NoError(t, err) + assert.Equal(t, User{Id: "1", Name: ""}, *user) +} + +func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Client) { + user := &User{} + lock := sync.Mutex{} + request := remoting.NewRequest("2.0.2") + invocation := createInvocation("GetUser0", nil, nil, []interface{}{"4", nil, "username"}, + []reflect.Value{reflect.ValueOf("4"), reflect.ValueOf(nil), reflect.ValueOf("username")}) + attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + setAttachment(invocation, attachment) + request.Data = invocation + request.Event = false + request.TwoWay = true + rsp := remoting.NewPendingResponse(request.ID) + rsp.SetResponse(remoting.NewResponse(request.ID, "2.0.2")) + remoting.AddPendingResponse(rsp) + rsp.Reply = user + rsp.Callback = func(response common.CallbackResponse) { + r := response.(remoting.AsyncCallbackResponse) + rst := *r.Reply.(*remoting.Response).Result.(*protocol.RPCResult) + assert.Equal(t, User{Id: "4", Name: "username"}, *(rst.Rest.(*User))) + lock.Unlock() + } + lock.Lock() + err := client.Request(request, 3*time.Second, rsp) + assert.NoError(t, err) + assert.Equal(t, User{}, *user) + time.Sleep(1 * time.Second) +} + +func InitTest(t *testing.T) (*Server, common.URL) { + + hessian.RegisterPOJO(&User{}) + remoting.RegistryCodec("dubbo", &DubboTestCodec{}) + + methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{}) + assert.NoError(t, err) + assert.Equal(t, "GetBigPkg,GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4,GetUser5,GetUser6", methods) + + // config + SetClientConf(ClientConfig{ + ConnectionNum: 2, + HeartbeatPeriod: "5s", + SessionTimeout: "20s", + PoolTTL: 600, + PoolSize: 64, + GettySessionParam: GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "4s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "client", + }, + }) + assert.NoError(t, clientConf.CheckValidity()) + SetServerConfig(ServerConfig{ + SessionNumber: 700, + SessionTimeout: "20s", + GettySessionParam: GettySessionParam{ + CompressEncoding: false, + TcpNoDelay: true, + TcpKeepAlive: true, + KeepAlivePeriod: "120s", + TcpRBufSize: 262144, + TcpWBufSize: 65536, + PkgWQSize: 512, + TcpReadTimeout: "1s", + TcpWriteTimeout: "5s", + WaitTimeout: "1s", + MaxMsgLen: 10240000000, + SessionName: "server", + }}) + assert.NoError(t, srvConf.CheckValidity()) + + url, err := common.NewURL("dubbo://127.0.0.1:20060/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=127.0.0.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") + // init server + userProvider := &UserProvider{} + common.ServiceMap.Register("", url.Protocol, userProvider) + invoker := &proxy_factory.ProxyInvoker{ + BaseInvoker: *protocol.NewBaseInvoker(url), + } + handler := func(invocation *invocation.RPCInvocation) protocol.RPCResult { + //result := protocol.RPCResult{} + r := invoker.Invoke(context.Background(), invocation) + result := protocol.RPCResult{ + Err: r.Error(), + Rest: r.Result(), + Attrs: r.Attachments(), + } + return result + } + server := NewServer(url, handler) + server.Start() + + time.Sleep(time.Second * 2) + + return server, url +} + +////////////////////////////////// +// provider +////////////////////////////////// + +type ( + User struct { + Id string `json:"id"` + Name string `json:"name"` + } + + UserProvider struct { + user map[string]User + } +) + +// size:4801228 +func (u *UserProvider) GetBigPkg(ctx context.Context, req []interface{}, rsp *User) error { + argBuf := new(bytes.Buffer) + for i := 0; i < 400; i++ { + argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") + argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") + } + rsp.Id = argBuf.String() + rsp.Name = argBuf.String() + return nil +} + +func (u *UserProvider) GetUser(ctx context.Context, req []interface{}, rsp *User) error { + rsp.Id = req[0].(string) + rsp.Name = req[1].(string) + return nil +} + +func (u *UserProvider) GetUser0(id string, k *User, name string) (User, error) { + return User{Id: id, Name: name}, nil +} + +func (u *UserProvider) GetUser1() error { + return nil +} + +func (u *UserProvider) GetUser2() error { + return perrors.New("error") +} + +func (u *UserProvider) GetUser3(rsp *[]interface{}) error { + *rsp = append(*rsp, User{Id: "1", Name: "username"}) + return nil +} + +func (u *UserProvider) GetUser4(ctx context.Context, req []interface{}) ([]interface{}, error) { + + return []interface{}{User{Id: req[0].([]interface{})[0].(string), Name: req[0].([]interface{})[1].(string)}}, nil +} + +func (u *UserProvider) GetUser5(ctx context.Context, req []interface{}) (map[interface{}]interface{}, error) { + return map[interface{}]interface{}{"key": User{Id: req[0].(map[interface{}]interface{})["id"].(string), Name: req[0].(map[interface{}]interface{})["name"].(string)}}, nil +} + +func (u *UserProvider) GetUser6(id int64) (*User, error) { + if id == 0 { + return nil, nil + } + return &User{Id: "1"}, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +func (u User) JavaClassName() string { + return "com.ikurento.user.User" +} diff --git a/protocol/dubbo/server.go b/remoting/getty/getty_server.go similarity index 62% rename from protocol/dubbo/server.go rename to remoting/getty/getty_server.go index 4ad4796c5409e39984af3ff336c2556507d15d93..7c8fa29a622b4b746801d1d2ce878f8b353c1a76 100644 --- a/protocol/dubbo/server.go +++ b/remoting/getty/getty_server.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package dubbo +package getty import ( "crypto/tls" @@ -25,7 +25,8 @@ import ( import ( "github.com/apache/dubbo-getty" - "github.com/dubbogo/gost/sync" + gxsync "github.com/dubbogo/gost/sync" + perrors "github.com/pkg/errors" "gopkg.in/yaml.v2" ) @@ -34,6 +35,9 @@ import ( "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" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" ) var ( @@ -41,8 +45,7 @@ var ( srvGrpool *gxsync.TaskPool ) -func init() { - +func initServer(protocol string) { // load clientconfig from provider_config // default use dubbo providerConfig := config.GetProviderConfig() @@ -54,7 +57,7 @@ func init() { if protocolConf == nil { logger.Info("protocol_conf default use dubbo config") } else { - dubboConf := protocolConf.(map[interface{}]interface{})[DUBBO] + dubboConf := protocolConf.(map[interface{}]interface{})[protocol] if dubboConf == nil { logger.Warnf("dubboConf is nil") return @@ -73,7 +76,7 @@ func init() { if err := srvConf.CheckValidity(); err != nil { panic(err) } - setServerGrpool() + SetServerGrpool() } // SetServerConfig set dubbo server config. @@ -84,36 +87,50 @@ func SetServerConfig(s ServerConfig) { logger.Warnf("[ServerConfig CheckValidity] error: %v", err) return } - setServerGrpool() + SetServerGrpool() } -// GetServerConfig get dubbo server config. +// GetServerConfig get getty server config. func GetServerConfig() ServerConfig { return *srvConf } -func setServerGrpool() { +// SetServerGrpool set getty server GrPool +func SetServerGrpool() { if srvConf.GrPoolSize > 1 { - srvGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), - gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber)) + srvGrpool = gxsync.NewTaskPool( + gxsync.WithTaskPoolTaskPoolSize(srvConf.GrPoolSize), + gxsync.WithTaskPoolTaskQueueLength(srvConf.QueueLen), + gxsync.WithTaskPoolTaskQueueNumber(srvConf.QueueNumber), + ) } } -// Server is dubbo protocol server. +// Server define getty server type Server struct { - conf ServerConfig - tcpServer getty.Server - rpcHandler *RpcServerHandler + conf ServerConfig + addr string + codec remoting.Codec + tcpServer getty.Server + rpcHandler *RpcServerHandler + requestHandler func(*invocation.RPCInvocation) protocol.RPCResult } -// NewServer create a new Server. -func NewServer() *Server { +// NewServer create a new Server +func NewServer(url common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { + //init + initServer(url.Protocol) + + srvConf.SSLEnabled = url.GetParamBool(constant.SSL_ENABLED_KEY, false) s := &Server{ - conf: *srvConf, + conf: *srvConf, + addr: url.Location, + codec: remoting.GetCodec(url.Protocol), + requestHandler: handlers, } - s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout) + s.rpcHandler = NewRpcServerHandler(s.conf.SessionNumber, s.conf.sessionTimeout, s) return s } @@ -122,6 +139,7 @@ func (s *Server) newSession(session getty.Session) error { var ( ok bool tcpConn *net.TCPConn + err error ) conf := s.conf @@ -131,7 +149,7 @@ func (s *Server) newSession(session getty.Session) error { if _, ok = session.Conn().(*tls.Conn); ok { session.SetName(conf.GettySessionParam.SessionName) session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) - session.SetPkgHandler(rpcServerPkgHandler) + session.SetPkgHandler(NewRpcServerPackageHandler(s)) session.SetEventListener(s.rpcHandler) session.SetWQLen(conf.GettySessionParam.PkgWQSize) session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout) @@ -146,57 +164,69 @@ func (s *Server) newSession(session getty.Session) error { panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) } - tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay) - tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive) - if conf.GettySessionParam.TcpKeepAlive { - tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) + if _, ok = session.Conn().(*tls.Conn); !ok { + if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { + return perrors.New(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection", session.Stat(), session.Conn())) + } + + if err = tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay); err != nil { + return err + } + if err = tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive); err != nil { + return err + } + if conf.GettySessionParam.TcpKeepAlive { + if err = tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod); err != nil { + return err + } + } + if err = tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize); err != nil { + return err + } + if err = tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize); err != nil { + return err + } } - tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) - tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) session.SetName(conf.GettySessionParam.SessionName) session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) - session.SetPkgHandler(rpcServerPkgHandler) + session.SetPkgHandler(NewRpcServerPackageHandler(s)) session.SetEventListener(s.rpcHandler) session.SetWQLen(conf.GettySessionParam.PkgWQSize) session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout) session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout) session.SetCronPeriod((int)(conf.sessionTimeout.Nanoseconds() / 1e6)) session.SetWaitTime(conf.GettySessionParam.waitTimeout) - logger.Debugf("app accepts new session:%s\n", session.Stat()) - + logger.Debugf("server accepts new session: %s", session.Stat()) session.SetTaskPool(srvGrpool) - return nil } -// Start start dubbo server. -func (s *Server) Start(url common.URL) { +// Start dubbo server. +func (s *Server) Start() { var ( addr string tcpServer getty.Server ) - addr = url.Location - if url.GetParamBool(constant.SSL_ENABLED_KEY, false) { + addr = s.addr + if s.conf.SSLEnabled { tcpServer = getty.NewTCPServer( getty.WithLocalAddress(addr), - getty.WithServerSslEnabled(url.GetParamBool(constant.SSL_ENABLED_KEY, false)), + getty.WithServerSslEnabled(s.conf.SSLEnabled), getty.WithServerTlsConfigBuilder(config.GetServerTlsConfigBuilder()), ) - } else { tcpServer = getty.NewTCPServer( getty.WithLocalAddress(addr), ) } tcpServer.RunEventLoop(s.newSession) - logger.Debugf("s bind addr{%s} ok!", addr) + logger.Debugf("s bind addr{%s} ok!", s.addr) s.tcpServer = tcpServer - } -// Stop stop dubbo server. +// Stop dubbo server func (s *Server) Stop() { s.tcpServer.Close() } diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go new file mode 100644 index 0000000000000000000000000000000000000000..196aa20a087e7562f88865d6bb40e6d8feac1502 --- /dev/null +++ b/remoting/getty/listener.go @@ -0,0 +1,319 @@ +/* + * 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 getty + +import ( + "fmt" + "sync" + "sync/atomic" + "time" +) + +import ( + "github.com/apache/dubbo-getty" + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/remoting" +) + +// todo: WritePkg_Timeout will entry *.yml +const ( + // WritePkg_Timeout the timeout of write pkg + WritePkg_Timeout = 5 * time.Second +) + +var ( + errTooManySessions = perrors.New("too many sessions") +) + +type rpcSession struct { + session getty.Session + reqNum int32 +} + +func (s *rpcSession) AddReqNum(num int32) { + atomic.AddInt32(&s.reqNum, num) +} + +func (s *rpcSession) GetReqNum() int32 { + return atomic.LoadInt32(&s.reqNum) +} + +// ////////////////////////////////////////// +// RpcClientHandler +// ////////////////////////////////////////// + +// nolint +type RpcClientHandler struct { + conn *gettyRPCClient +} + +// nolint +func NewRpcClientHandler(client *gettyRPCClient) *RpcClientHandler { + return &RpcClientHandler{conn: client} +} + +// OnOpen call the getty client session opened, add the session to getty client session list +func (h *RpcClientHandler) OnOpen(session getty.Session) error { + h.conn.addSession(session) + return nil +} + +// OnError the getty client session has errored, so remove the session from the getty client session list +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 close the session, remove it from the getty session list +func (h *RpcClientHandler) OnClose(session getty.Session) { + logger.Infof("session{%s} is closing......", session.Stat()) + h.conn.removeSession(session) +} + +// OnMessage get response from getty server, and update the session to the getty client session list +func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { + result, ok := pkg.(remoting.DecodeResult) + if !ok { + logger.Errorf("illegal package") + return + } + // get heartbeart request from server + if result.IsRequest { + req := result.Result.(*remoting.Request) + if req.Event { + logger.Debugf("get rpc heartbeat request{%#v}", req) + resp := remoting.NewResponse(req.ID, req.Version) + resp.Status = hessian.Response_OK + resp.Event = req.Event + resp.SerialID = req.SerialID + resp.Version = "2.0.2" + reply(session, resp, hessian.PackageHeartbeat) + return + } + logger.Errorf("illegal request but not heartbeart. {%#v}", req) + return + } + + p := result.Result.(*remoting.Response) + // get heartbeart + if p.Event { + logger.Debugf("get rpc heartbeat response{%#v}", p) + if p.Error != nil { + logger.Errorf("rpc heartbeat response{error: %#v}", p.Error) + } + h.conn.pool.rpcClient.responseHandler.Handler(p) + return + } + if result.IsRequest { + logger.Errorf("illegal package for it is response type. {%#v}", pkg) + return + } + + logger.Debugf("get rpc response{%#v}", p) + + h.conn.updateSession(session) + + h.conn.pool.rpcClient.responseHandler.Handler(p) +} + +// OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session +func (h *RpcClientHandler) OnCron(session getty.Session) { + rpcSession, err := h.conn.getClientRpcSession(session) + if err != nil { + logger.Errorf("client.getClientSession(session{%s}) = error{%v}", + session.Stat(), perrors.WithStack(err)) + return + } + if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { + logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", + session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) + h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) + return + } + + h.conn.pool.rpcClient.heartbeat(session) +} + +// ////////////////////////////////////////// +// RpcServerHandler +// ////////////////////////////////////////// + +// nolint +type RpcServerHandler struct { + maxSessionNum int + sessionTimeout time.Duration + sessionMap map[getty.Session]*rpcSession + rwlock sync.RWMutex + server *Server +} + +// nolint +func NewRpcServerHandler(maxSessionNum int, sessionTimeout time.Duration, serverP *Server) *RpcServerHandler { + return &RpcServerHandler{ + maxSessionNum: maxSessionNum, + sessionTimeout: sessionTimeout, + sessionMap: make(map[getty.Session]*rpcSession), + server: serverP, + } +} + +// OnOpen call server session opened, add the session to getty server session list. also onOpen +// will check the max getty server session number +func (h *RpcServerHandler) OnOpen(session getty.Session) error { + var err error + h.rwlock.RLock() + if h.maxSessionNum <= len(h.sessionMap) { + err = errTooManySessions + } + h.rwlock.RUnlock() + if err != nil { + return perrors.WithStack(err) + } + + logger.Infof("got session:%s", session.Stat()) + h.rwlock.Lock() + h.sessionMap[session] = &rpcSession{session: session} + h.rwlock.Unlock() + return nil +} + +// OnError the getty server session has errored, so remove the session from the getty server session list +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() + delete(h.sessionMap, session) + h.rwlock.Unlock() +} + +// OnClose close the session, remove it from the getty server list +func (h *RpcServerHandler) OnClose(session getty.Session) { + logger.Infof("session{%s} is closing......", session.Stat()) + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() +} + +// OnMessage get request from getty client, update the session reqNum and reply response to client +func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { + h.rwlock.Lock() + if _, ok := h.sessionMap[session]; ok { + h.sessionMap[session].reqNum++ + } + h.rwlock.Unlock() + + decodeResult, ok := pkg.(remoting.DecodeResult) + if !ok { + logger.Errorf("illegal package{%#v}", pkg) + return + } + if !decodeResult.IsRequest { + logger.Errorf("illegal package for it is response type. {%#v}", pkg) + return + } + req := decodeResult.Result.(*remoting.Request) + + resp := remoting.NewResponse(req.ID, req.Version) + resp.Status = hessian.Response_OK + resp.Event = req.Event + resp.SerialID = req.SerialID + resp.Version = "2.0.2" + + // heartbeat + if req.Event { + logger.Debugf("get rpc heartbeat request{%#v}", resp) + reply(session, resp, hessian.PackageHeartbeat) + return + } + + defer func() { + if e := recover(); e != nil { + resp.Status = hessian.Response_SERVER_ERROR + if err, ok := e.(error); ok { + logger.Errorf("OnMessage panic: %+v", perrors.WithStack(err)) + resp.Error = perrors.WithStack(err) + } else if err, ok := e.(string); ok { + logger.Errorf("OnMessage panic: %+v", perrors.New(err)) + resp.Error = perrors.New(err) + } else { + logger.Errorf("OnMessage panic: %+v, this is impossible.", e) + resp.Error = fmt.Errorf("OnMessage panic unknow exception. %+v", e) + } + + if !req.TwoWay { + return + } + reply(session, resp, hessian.PackageResponse) + } + + }() + + invoc, ok := req.Data.(*invocation.RPCInvocation) + if !ok { + panic("create invocation occur some exception for the type is not suitable one.") + return + } + attachments := invoc.Attachments() + attachments[constant.LOCAL_ADDR] = session.LocalAddr() + attachments[constant.REMOTE_ADDR] = session.RemoteAddr() + + result := h.server.requestHandler(invoc) + if !req.TwoWay { + return + } + resp.Result = result + reply(session, resp, hessian.PackageResponse) +} + +// OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session +func (h *RpcServerHandler) OnCron(session getty.Session) { + var ( + flag bool + active time.Time + ) + + h.rwlock.RLock() + if _, ok := h.sessionMap[session]; ok { + active = session.GetActive() + if h.sessionTimeout.Nanoseconds() < time.Since(active).Nanoseconds() { + flag = true + logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", + session.Stat(), time.Since(active).String(), h.sessionMap[session].reqNum) + } + } + h.rwlock.RUnlock() + + if flag { + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() + session.Close() + } +} + +func reply(session getty.Session, resp *remoting.Response, tp hessian.PackageType) { + if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { + logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp) + } +} diff --git a/protocol/dubbo/listener_test.go b/remoting/getty/listener_test.go similarity index 69% rename from protocol/dubbo/listener_test.go rename to remoting/getty/listener_test.go index 5f809814607558650e09934019db96dbb2ceeeae..7e7ac5fed440a02188057d520a944b48c8bf7b64 100644 --- a/protocol/dubbo/listener_test.go +++ b/remoting/getty/listener_test.go @@ -15,27 +15,28 @@ * limitations under the License. */ -package dubbo +package getty import ( + "context" "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" + "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go/mocktracer" ) // test rebuild the ctx func TestRebuildCtx(t *testing.T) { opentracing.SetGlobalTracer(mocktracer.New()) - attach := make(map[string]string, 10) + attach := make(map[string]interface{}, 10) attach[constant.VERSION_KEY] = "1.0" attach[constant.GROUP_KEY] = "MyGroup" inv := invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) @@ -47,8 +48,9 @@ func TestRebuildCtx(t *testing.T) { span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") - opentracing.GlobalTracer().Inject(span.Context(), opentracing.TextMap, - opentracing.TextMapCarrier(inv.Attachments())) + err := injectTraceCtx(span, inv) + assert.NoError(t, err) + // rebuild the context success inv = invocation.NewRPCInvocation("MethodName", []interface{}{"OK", "Hello"}, attach) ctx = rebuildCtx(inv) @@ -56,3 +58,18 @@ func TestRebuildCtx(t *testing.T) { assert.NotNil(t, ctx) assert.NotNil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) } + +// 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.WithValue(context.Background(), "attachment", inv.Attachments()) + + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(filterContext(inv.Attachments()))) + if err == nil { + ctx = context.WithValue(ctx, constant.TRACING_REMOTE_SPAN_CTX, spanCtx) + } + return ctx +} diff --git a/remoting/getty/opentracing.go b/remoting/getty/opentracing.go new file mode 100644 index 0000000000000000000000000000000000000000..7db733cbe919f2bef46cfc477bda836dc2da0d45 --- /dev/null +++ b/remoting/getty/opentracing.go @@ -0,0 +1,60 @@ +/* + * 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 getty + +import ( + "github.com/opentracing/opentracing-go" +) +import ( + invocation_impl "github.com/apache/dubbo-go/protocol/invocation" +) + +func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvocation) error { + // inject opentracing ctx + traceAttachments := filterContext(inv.Attachments()) + carrier := opentracing.TextMapCarrier(traceAttachments) + err := opentracing.GlobalTracer().Inject(currentSpan.Context(), opentracing.TextMap, carrier) + if err == nil { + fillTraceAttachments(inv.Attachments(), traceAttachments) + } + return err +} + +func extractTraceCtx(inv *invocation_impl.RPCInvocation) (opentracing.SpanContext, error) { + traceAttachments := filterContext(inv.Attachments()) + // actually, if user do not use any opentracing framework, the err will not be nil. + spanCtx, err := opentracing.GlobalTracer().Extract(opentracing.TextMap, + opentracing.TextMapCarrier(traceAttachments)) + return spanCtx, err +} + +func filterContext(attachments map[string]interface{}) map[string]string { + var traceAttchment = make(map[string]string) + for k, v := range attachments { + if r, ok := v.(string); ok { + traceAttchment[k] = r + } + } + return traceAttchment +} + +func fillTraceAttachments(attachments map[string]interface{}, traceAttachment map[string]string) { + for k, v := range traceAttachment { + attachments[k] = v + } +} diff --git a/protocol/dubbo/pool.go b/remoting/getty/pool.go similarity index 91% rename from protocol/dubbo/pool.go rename to remoting/getty/pool.go index 6a7d211b496d3f6905c8a84e2f1bd648718ebb92..464cff956e0b4667944942be1c4721a05845bd33 100644 --- a/protocol/dubbo/pool.go +++ b/remoting/getty/pool.go @@ -15,7 +15,7 @@ * limitations under the License. */ -package dubbo +package getty import ( "crypto/tls" @@ -38,10 +38,10 @@ import ( ) type gettyRPCClient struct { - once sync.Once - protocol string - addr string - active int64 // zero, not create or be destroyed + once sync.Once + //protocol string + addr string + active int64 // zero, not create or be destroyed pool *gettyRPCClientPool @@ -54,7 +54,7 @@ var ( errClientPoolClosed = perrors.New("client pool closed") ) -func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*gettyRPCClient, error) { +func newGettyRPCClientConn(pool *gettyRPCClientPool, addr string) (*gettyRPCClient, error) { var ( gettyClient getty.Client sslEnabled bool @@ -76,7 +76,6 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, protocol, addr string) (*ge ) } c := &gettyRPCClient{ - protocol: protocol, addr: addr, pool: pool, gettyClient: gettyClient, @@ -117,7 +116,6 @@ func (c *gettyRPCClient) newSession(session getty.Session) error { conf ClientConfig sslEnabled bool ) - conf = c.pool.rpcClient.conf sslEnabled = c.pool.sslEnabled if conf.GettySessionParam.CompressEncoding { @@ -255,25 +253,25 @@ func (c *gettyRPCClient) updateSession(session getty.Session) { func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { var ( - err error - rpcClientSession rpcSession + err error + rpcSession rpcSession ) c.lock.RLock() defer c.lock.RUnlock() if c.sessions == nil { - return rpcClientSession, errClientClosed + return rpcSession, errClientClosed } err = errSessionNotExist for _, s := range c.sessions { if s.session == session { - rpcClientSession = *s + rpcSession = *s err = nil break } } - return rpcClientSession, perrors.WithStack(err) + return rpcSession, perrors.WithStack(err) } func (c *gettyRPCClient) isAvailable() bool { @@ -338,7 +336,8 @@ func newGettyRPCClientConnPool(rpcClient *Client, size int, ttl time.Duration) * rpcClient: rpcClient, size: size, ttl: int64(ttl.Seconds()), - conns: make([]*gettyRPCClient, 0, 16), + // init capacity : 2 + conns: make([]*gettyRPCClient, 0, 2), } } @@ -352,11 +351,15 @@ func (p *gettyRPCClientPool) close() { } } -func (p *gettyRPCClientPool) getGettyRpcClient(protocol, addr string) (*gettyRPCClient, error) { +func (p *gettyRPCClientPool) getGettyRpcClient(addr string) (*gettyRPCClient, error) { conn, err := p.get() if err == nil && conn == nil { // create new conn - conn, err = newGettyRPCClientConn(p, protocol, addr) + rpcClientConn, err := newGettyRPCClientConn(p, addr) + if err == nil { + p.put(rpcClientConn) + } + return rpcClientConn, perrors.WithStack(err) } return conn, perrors.WithStack(err) } @@ -369,10 +372,15 @@ func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { if p.conns == nil { return nil, errClientPoolClosed } - - for len(p.conns) > 0 { - conn := p.conns[len(p.conns)-1] - p.conns = p.conns[:len(p.conns)-1] + for num := len(p.conns); num > 0; { + var conn *gettyRPCClient + if num != 1 { + conn = p.conns[rand.Int31n(int32(num))] + } else { + conn = p.conns[0] + } + // This will recreate gettyRpcClient for remove last position + //p.conns = p.conns[:len(p.conns)-1] if d := now - conn.getActive(); d > p.ttl { p.remove(conn) @@ -389,21 +397,17 @@ func (p *gettyRPCClientPool) put(conn *gettyRPCClient) { if conn == nil || conn.getActive() == 0 { return } - p.Lock() defer p.Unlock() - if p.conns == nil { 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) diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go new file mode 100644 index 0000000000000000000000000000000000000000..66c33a6db63f714e686382e199e19efd693e1dda --- /dev/null +++ b/remoting/getty/readwriter.go @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package getty + +import ( + "reflect" +) + +import ( + "github.com/apache/dubbo-getty" + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/remoting" +) + +//////////////////////////////////////////// +// RpcClientPackageHandler +//////////////////////////////////////////// + +// RpcClientPackageHandler Read data from server and Write data to server +type RpcClientPackageHandler struct { + client *Client +} + +// NewRpcClientPackageHandler create a RpcClientPackageHandler +func NewRpcClientPackageHandler(client *Client) *RpcClientPackageHandler { + return &RpcClientPackageHandler{client: client} +} + +// Read data from server. if the package size from server is larger than 4096 byte, server will read 4096 byte +// and send to client each time. the Read can assemble it. +func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { + resp, length, err := (p.client.codec).Decode(data) + //err := pkg.Unmarshal(buf, p.client) + if err != nil { + err = perrors.Cause(err) + if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { + return nil, 0, nil + } + + logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) + + return nil, length, err + } + + return resp, length, nil +} + +// Write send the data to server +func (p *RpcClientPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { + req, ok := pkg.(*remoting.Request) + if !ok { + logger.Errorf("illegal pkg:%+v\n", pkg) + return nil, perrors.New("invalid rpc request") + } + + buf, err := (p.client.codec).EncodeRequest(req) + if err != nil { + logger.Warnf("binary.Write(req{%#v}) = err{%#v}", req, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + + return buf.Bytes(), nil +} + +//////////////////////////////////////////// +// RpcServerPackageHandler +//////////////////////////////////////////// + +//var ( +// rpcServerPkgHandler = &RpcServerPackageHandler{} +//) + +// RpcServerPackageHandler Read data from client and Write data to client +type RpcServerPackageHandler struct { + server *Server +} + +func NewRpcServerPackageHandler(server *Server) *RpcServerPackageHandler { + return &RpcServerPackageHandler{server: server} +} + +// Read data from client. if the package size from client is larger than 4096 byte, client will read 4096 byte +// and send to client each time. the Read can assemble it. +func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface{}, int, error) { + req, length, err := (p.server.codec).Decode(data) + //resp,len, err := (*p.).DecodeResponse(buf) + if err != nil { + if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { + return nil, 0, nil + } + + logger.Errorf("pkg.Unmarshal(ss:%+v, len(@data):%d) = error:%+v", ss, len(data), err) + + return nil, 0, err + } + + return req, length, err +} + +// Write send the data to client +func (p *RpcServerPackageHandler) Write(ss getty.Session, pkg interface{}) ([]byte, error) { + res, ok := pkg.(*remoting.Response) + if !ok { + logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) + return nil, perrors.New("invalid rpc response") + } + + buf, err := (p.server.codec).EncodeResponse(res) + if err != nil { + logger.Warnf("binary.Write(res{%#v}) = err{%#v}", res, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + + return buf.Bytes(), nil +} diff --git a/remoting/zookeeper/curator_discovery/service_discovery.go b/remoting/zookeeper/curator_discovery/service_discovery.go index 9566b5494389325520b4eb6a8eb170e0b305bb47..acd43c0b92bd6220efc6527efc1748ed3021f7ac 100644 --- a/remoting/zookeeper/curator_discovery/service_discovery.go +++ b/remoting/zookeeper/curator_discovery/service_discovery.go @@ -154,7 +154,6 @@ func (sd *ServiceDiscovery) updateInternalService(name, id string) { return } entry.instance = instance - return } // UnregisterService un-register service in zookeeper and delete service in cache diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index 486a67e20a0736be20813226b622f1bfa5bd87c0..b6c6d78106a5a97ec94e621d2e664185a8216656 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -322,7 +322,7 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen for { select { case <-ticker.C: - l.handleZkNodeEvent(zkEvent.Path, children, listener) + l.handleZkNodeEvent(zkPath, children, listener) case zkEvent = <-childEventCh: logger.Warnf("get a zookeeper zkEvent{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, StateToString(zkEvent.State), zkEvent.Err) diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod index 4708eb1f0f48c10acc254880ecb6dad3a03529f2..162f32ba9b7a217e7ae8bcfea5aa5b91d76b383c 100644 --- a/test/integrate/dubbo/go-client/go.mod +++ b/test/integrate/dubbo/go-client/go.mod @@ -1,3 +1,5 @@ module github.com/apache/dubbo-go/test/integrate/dubbo/go-client +require github.com/apache/dubbo-go-hessian2 v1.7.0 + go 1.13 diff --git a/test/integrate/dubbo/go-client/go.sum b/test/integrate/dubbo/go-client/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..fc378395782f0f7e8032cdaed2ec4c4b0266c149 --- /dev/null +++ b/test/integrate/dubbo/go-client/go.sum @@ -0,0 +1,11 @@ +github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/dubbo-go-hessian2 v1.7.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod index 9e1162327de374fb131c2a0b89d1be3baa578a1b..f9d950e0d1b9b7e56922120772c98699f7b3acc9 100644 --- a/test/integrate/dubbo/go-server/go.mod +++ b/test/integrate/dubbo/go-server/go.mod @@ -1,3 +1,5 @@ module github.com/apache/dubbo-go/test/integrate/dubbo/go-server +require github.com/apache/dubbo-go-hessian2 v1.7.0 + go 1.13 diff --git a/test/integrate/dubbo/go-server/go.sum b/test/integrate/dubbo/go-server/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..fc378395782f0f7e8032cdaed2ec4c4b0266c149 --- /dev/null +++ b/test/integrate/dubbo/go-server/go.sum @@ -0,0 +1,11 @@ +github.com/apache/dubbo-go-hessian2 v1.6.0-rc1.0.20200906044240-6c1fb5c3bd44/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/dubbo-go-hessian2 v1.7.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dubbogo/gost v1.9.0/go.mod h1:pPTjVyoJan3aPxBPNUX0ADkXjPibLo+/Ib0/fADXSG8= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=