diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 3eb1ec055f18f29a7886e01c24e10c97f88fb1e8..0c5d3e0a7c7fc333c3fd7b012f04f4b40f244e33 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,4 +1,5 @@ -<!-- Thanks for sending a pull request! +<!-- Thanks for sending a pull request! +Read https://github.com/apache/dubbo-go/blob/master/contributing.md before commit pull request. --> **What this PR does**: diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000000000000000000000000000000000..6ce02861026b4ee7e13f199c29ca74a9f78ffd32 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "gomod" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" + target-branch: "develop" + + - package-ecosystem: "github-actions" + # Workflow files stored in the + # default location of `.github/workflows` + directory: "/" + schedule: + interval: "weekly" + target-branch: "develop" \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000000000000000000000000000000000000..f2e185cc1eb21e0ac3458f06ef21c407c8e0d06f --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,54 @@ +name: "CodeQL" + +on: + push: + branches: [master, ] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 4 * * 5' + +jobs: + analyse: + name: Analyse + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # 鈩癸笍 Command-line programs to run using the OS shell. + # 馃摎 https://git.io/JvXDl + + # 鉁忥笍 If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 9ac051a9de252de42f19ae977a2f3da8548520d2..865b60eed973173ec787ee94e31233081af50c57 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -19,49 +19,63 @@ jobs: os: - ubuntu-latest + env: + DING_TOKEN: ${{ secrets.DING_TOKEN }} + DING_SIGN: ${{ secrets.DING_SIGN }} + 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) \ No newline at end of file + - 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.1.4 + 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: gofmt + run: | + go fmt ./... && git status && [[ -z `git status -s` ]] + # diff -u <(echo -n) <(gofmt -d -s .) + + - name: Install go ci lint + run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.27.0 + + - name: Run Linter + run: golangci-lint run --timeout=10m -v + + - name: Verify + run: | + make verify + + - name: Integrate Test + run: | + chmod +x integrate_test.sh && ./integrate_test.sh ${{github.event.pull_request.head.repo.full_name}} ${{github.event.pull_request.head.sha}} + + - name: Post Coverage + run: bash <(curl -s https://codecov.io/bash) + + - name: Hello world + run: echo Hello world ${{ secrets.DING_TOKEN }} ${{ secrets.DING_SIGN }} + diff --git a/.gitignore b/.gitignore index 8158b497e3620a15fc5a52e91020e1fcebd5be41..898962e244d0ba1d030837f76fed47576f38ab5e 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ config_center/apollo/mockDubbog.properties.json # vim stuff *~ .*.sw? +/license-header-checker-linux/ +/license-header-checker-linux.zip diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000000000000000000000000000000000000..6f214f445ac5544b26d4fa704b7ea4524535f8bd --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,63 @@ +linters-settings: + govet: + check-shadowing: true + golint: + min-confidence: 0 + gocyclo: + min-complexity: 10 + maligned: + suggest-new: true + dupl: + threshold: 100 + goconst: + min-len: 2 + min-occurrences: 2 + depguard: + list-type: blacklist + packages: + # logging is allowed only by logutils.Log, logrus + # is allowed to use only in logutils package + - github.com/sirupsen/logrus + misspell: + locale: US + lll: + line-length: 140 + goimports: + local-prefixes: github.com/golangci/golangci-lint + gocritic: + enabled-tags: + - performance + - style + - experimental + disabled-checks: + - wrapperFunc + +linters: + disable-all: true + enable: + - govet + - staticcheck + - ineffassign + - misspell + +run: + skip-dirs: + - test/testdata_etc + - pkg/golinters/goanalysis/(checker|passes) + +issues: + exclude-rules: + - text: "weak cryptographic primitive" + linters: + - gosec + - linters: + - staticcheck + text: "SA1019:" + +# golangci.com configuration +# https://github.com/golangci/golangci/wiki/Configuration +service: + golangci-lint-version: 1.15.x # use the fixed version to not introduce new linters unexpectedly + prepare: + - echo "here I can run custom commands, but no preparation needed for this repo" + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 566c88ece05bd80175eea2d1de8fd061a279e273..0000000000000000000000000000000000000000 --- a/.travis.yml +++ /dev/null @@ -1,35 +0,0 @@ -dist: trusty -sudo: required - -# define the dependence env -language: go -os: - - linux -go: - - "1.13" -services: - - docker -env: - - GO111MODULE=on -install: true - -# define ci-stage -script: - # license-check - - echo 'start license check' - - go fmt ./... && [[ -z `git status -s` ]] - - sh before_validate_license.sh - - chmod u+x /tmp/tools/license/license-header-checker - - /tmp/tools/license/license-header-checker -v -a -r -i vendor /tmp/tools/license/license.txt . go && [[ -z `git status -s` ]] - # unit-test - - echo 'start unit-test' - - chmod u+x before_ut.sh && ./before_ut.sh - - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic - # integrate-test - - chmod +x integrate_test.sh && ./integrate_test.sh - -after_success: - - bash <(curl -s https://codecov.io/bash) - -notifications: - webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f diff --git a/CHANGE.md b/CHANGE.md index a2849f40648b89d2c7fb9d7f180bfedb729e71d4..28ffeb812c67105e66447ad1f22c8673f8c5aff4 100644 --- a/CHANGE.md +++ b/CHANGE.md @@ -1,5 +1,122 @@ # Release Notes --- +## 1.5.6 + +### New Features +- [Add dubbo-go-cli telnet tool](https://github.com/apache/dubbo-go/pull/818) +- [Add Prox ImplementFunc to allow override impl](https://github.com/apache/dubbo-go/pull/1019) +- [Add read configuration path from the command line when start](https://github.com/apache/dubbo-go/pull/1039) + +### Enhancement +- [introduce ConfigPostProcessor extension](https://github.com/apache/dubbo-go/pull/943) +- [Impl extension of two urls comparison](https://github.com/apache/dubbo-go/pull/854) +- [using event-driven to let router send signal to notify channel](https://github.com/apache/dubbo-go/pull/976) +- [lint codes](https://github.com/apache/dubbo-go/pull/941) + +### Bugfixes +- [Fix: generic struct2MapAll key of map keep type](https://github.com/apache/dubbo-go/pull/928) +- [Fix: when events empty, delete all the invokers](https://github.com/apache/dubbo-go/pull/758) +- [Fix: file service discovery run in windows](https://github.com/apache/dubbo-go/pull/932) +- [Fix: make metadata report work without serviceDiscovery](https://github.com/apache/dubbo-go/pull/948) +- [Fix: consumer invoker cache set nil after the ZK connection is lost](https://github.com/apache/dubbo-go/pull/985) +- [Fix: integration test in Github action](https://github.com/apache/dubbo-go/pull/1012) +- [Fix: etcd exit panic](https://github.com/apache/dubbo-go/pull/1013) +- [Fix: when connect to provider fail, will occur panic](https://github.com/apache/dubbo-go/pull/1021) +- [Fix: support getty send Length, when the data transfer failed](https://github.com/apache/dubbo-go/pull/1028) + +Milestone: [https://github.com/apache/dubbo-go/milestone/7](https://github.com/apache/dubbo-go/milestone/7?closed=1) + +## 1.5.5 + +### New Features +- [Add Address notification batch mode](https://github.com/apache/dubbo-go/pull/741) +- [Add dubbo-gen stream support](https://github.com/apache/dubbo-go/pull/794) +- [Add Change verify to Makefile](https://github.com/apache/dubbo-go/pull/831) +- [Add more automatic components](https://github.com/apache/dubbo-go/pull/832) +- [Add grpc max message size config](https://github.com/apache/dubbo-go/pull/824) + +### Enhancement +- [when it need local ip, it will get it every time. We can get local ip once, and reused it](https://github.com/apache/dubbo-go/pull/807) +- [enhance client's connectivity](https://github.com/apache/dubbo-go/pull/800) +- [Imp: get local ip once and reused it](https://github.com/apache/dubbo-go/pull/808) +- [Remove unmeaning logic](https://github.com/apache/dubbo-go/pull/855) + +### Bugfixes +- [Fix: nacos registry can not get namespaceId](https://github.com/apache/dubbo-go/pull/778) [@peaman](https://github.com/peaman) +- [Fix: url encode](https://github.com/apache/dubbo-go/pull/802) +- [Fix: try to fix too many files open error](https://github.com/apache/dubbo-go/pull/797) +- [Fix: refact heartbeat](https://github.com/apache/dubbo-go/pull/889) +- [Fix: router_config add &url to url](https://github.com/apache/dubbo-go/pull/910) +- [Fix: Router chain can not build immediately when started](https://github.com/apache/dubbo-go/pull/927) +- [Fix: client block until timeout when provider return with PackageResponse_Exception](https://github.com/apache/dubbo-go/pull/926) +- [Fix: URL.String() data race panic](https://github.com/apache/dubbo-go/pull/944) +- [Fix: generic "encode hessian.Object"](https://github.com/apache/dubbo-go/pull/945) + +### Dependencies +- [Bump github.com/mitchellh/mapstructure from 1.2.3 to 1.3.3](https://github.com/apache/dubbo-go/pull/838) +- [Bump github.com/go-resty/resty/v2 from 2.1.0 to 2.3.0](https://github.com/apache/dubbo-go/pull/837) +- [Bump github.com/opentracing/opentracing-go from 1.1.0 to 1.2.0](https://github.com/apache/dubbo-go/pull/836) +- [Bump github.com/creasty/defaults from 1.3.0 to 1.5.1](https://github.com/apache/dubbo-go/pull/835) +- [Bump github.com/dubbogo/gost from 1.9.1 to 1.9.2](https://github.com/apache/dubbo-go/pull/834) +- [Bump github.com/zouyx/agollo/v3 from 3.4.4 to 3.4.5](https://github.com/apache/dubbo-go/pull/845) +- [Bump github.com/golang/mock from 1.3.1 to 1.4.4](https://github.com/apache/dubbo-go/pull/844) +- [Bump github.com/nacos-group/nacos-sdk-go from 1.0.0 to 1.0.1](https://github.com/apache/dubbo-go/pull/843) +- [Bump github.com/magiconair/properties from 1.8.1 to 1.8.4](https://github.com/apache/dubbo-go/pull/861) +- [Bump github.com/prometheus/client_golang from 1.1.0 to 1.8.0 ](https://github.com/apache/dubbo-go/pull/860) +- [Bump go.uber.org/atomic from 1.6.0 to 1.7.0](https://github.com/apache/dubbo-go/pull/859) +- [](https://github.com/apache/dubbo-go/pull/843) + +Milestone: [https://github.com/apache/dubbo-go/milestone/5](https://github.com/apache/dubbo-go/milestone/5?closed=1) + +## 1.4.5 + +### Bugfixes +- [Fix too many files open error](https://github.com/apache/dubbo-go/pull/828) [@wenxuwan](https://github.com/wenxuwan) Milestone: [https://github.com/apache/dubbo-go/milestone/6](https://github.com/apache/dubbo-go/milestone/6?closed=1) + +## 1.5.4 + +### Bugfixes +- [Fix etcd cluster reconnect](https://github.com/apache/dubbo-go/pull/828) +- [Fix zookeeper deadlock problem](https://github.com/apache/dubbo-go/pull/826) +- [Fix generic struct2MapAll](https://github.com/apache/dubbo-go/pull/822) +- [Fix Consumer panic when restart provider](https://github.com/apache/dubbo-go/pull/803) +- [Fix etcd can not registry](https://github.com/apache/dubbo-go/pull/819) [@lin-jianjun](https://github.com/lin-jianjun) +- [Fix cannot call go provider service when used by java dubbo 2.7.7 version](https://github.com/apache/dubbo-go/pull/815) [@jack15083](https://github.com/jack15083) +- [Fix go client quit abnormally when it connects java server](https://github.com/apache/dubbo-go/pull/820) [@wenxuwan](https://github.com/wenxuwan) +- [Fix sentinel windows issue](https://github.com/apache/dubbo-go/pull/821) [@louyuting](https://github.com/louyuting) +- [Fix metadata default port](https://github.com/apache/dubbo-go/pull/821) [@sanxun0325](https://github.com/sanxun0325) +- [Fix consul can not destory](https://github.com/apache/dubbo-go/pull/788) [@LaurenceLiZhixin](https://github.com/LaurenceLiZhixin) + +Milestone: [https://github.com/apache/dubbo-go/milestone/6](https://github.com/apache/dubbo-go/milestone/6?closed=1) + +## 1.5.3 + +### 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.4 @@ -167,7 +284,7 @@ Project: [https://github.com/apache/dubbo-go/projects/8](https://github.com/apac - [Add grpc protocol](https://github.com/apache/dubbo-go/pull/311) ### Enhancement - + - [The SIGSYS and SIGSTOP are not supported in windows platform](https://github.com/apache/dubbo-go/pull/262) - [Error should be returned when `NewURL` failed](https://github.com/apache/dubbo-go/pull/266) - [Split config center GetConfig method](https://github.com/apache/dubbo-go/pull/267) @@ -177,7 +294,7 @@ Project: [https://github.com/apache/dubbo-go/projects/8](https://github.com/apac - [Change zk version and add base_registry](https://github.com/apache/dubbo-go/pull/355) ### Bugfixes - + - [Fix negative wait group count](https://github.com/apache/dubbo-go/pull/253) - [After disconnection with ZK registry, cosumer can't listen to provider changes](https://github.com/apache/dubbo-go/pull/258) - [The generic filter and default reference filters lack ','](https://github.com/apache/dubbo-go/pull/260) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..2f6c9bd4e21a56fa4a6060db8795ba6e716160bb --- /dev/null +++ b/Makefile @@ -0,0 +1,76 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +VERSION ?= latest + +GO = go +GO_PATH = $(shell $(GO) env GOPATH) +GO_OS = $(shell $(GO) env GOOS) +ifeq ($(GO_OS), darwin) + GO_OS = mac +endif +GO_BUILD = $(GO) build +GO_GET = $(GO) get +GO_TEST = $(GO) test +GO_BUILD_FLAGS = -v +GO_BUILD_LDFLAGS = -X main.version=$(VERSION) + +GO_LICENSE_CHECKER_DIR = license-header-checker-$(GO_OS) +GO_LICENSE_CHECKER = $(GO_PATH)/bin/license-header-checker +LICENSE_DIR = /tmp/tools/license + +ARCH = amd64 +# for add zookeeper fatjar +ZK_TEST_LIST=config_center/zookeeper registry/zookeeper cluster/router/chain cluster/router/condition cluster/router/tag metadata/report/zookeeper +ZK_JAR_NAME=zookeeper-3.4.9-fatjar.jar +ZK_FATJAR_BASE=/zookeeper-4unittest/contrib/fatjar +ZK_JAR_PATH=remoting/zookeeper$(ZK_FATJAR_BASE) +ZK_JAR=$(ZK_JAR_PATH)/$(ZK_JAR_NAME) + +SHELL = /bin/bash + +prepareLic: + $(GO_LICENSE_CHECKER) -version || (wget https://github.com/lsm-dev/license-header-checker/releases/download/v1.2.0/$(GO_LICENSE_CHECKER_DIR).zip -O $(GO_LICENSE_CHECKER_DIR).zip && unzip -o $(GO_LICENSE_CHECKER_DIR).zip && mkdir -p $(GO_PATH)/bin/ && cp $(GO_LICENSE_CHECKER_DIR)/64bit/license-header-checker $(GO_PATH)/bin/) + ls /tmp/tools/license/license.txt || wget -P $(LICENSE_DIR) https://github.com/dubbogo/resources/raw/master/tools/license/license.txt + +prepareZk: + ls $(ZK_JAR) || (mkdir -p $(ZK_JAR_PATH)&& wget -P $(ZK_JAR_PATH) https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/${ZK_JAR_NAME}) + @for i in $(ZK_TEST_LIST); do \ + mkdir -p $$i$(ZK_FATJAR_BASE);\ + cp ${ZK_JAR} $$i$(ZK_FATJAR_BASE);\ + done + +prepare: prepareZk prepareLic + +.PHONE: test +test: clean prepareZk + $(GO_TEST) ./... -coverprofile=coverage.txt -covermode=atomic + +deps: prepare + $(GO_GET) -v -t -d ./... + +.PHONY: license +license: clean prepareLic + $(GO_LICENSE_CHECKER) -v -a -r -i vendor $(LICENSE_DIR)/license.txt . go && [[ -z `git status -s` ]] + +.PHONY: verify +verify: clean license test + +.PHONY: clean +clean: prepare + rm -rf coverage.txt + rm -rf license-header-checker* diff --git a/NOTICE b/NOTICE index 1120c200c997fe6befbe3f78d95e9bdb8a05a487..e0f4af68f6ce22fb817ba1173663395ee8f2bfd9 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache Dubbo-go -Copyright 2018-2020 The Apache Software Foundation +Copyright 2018-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/README.md b/README.md index 1eab98bb72b5d0b2340f4bdebd2f1171b4ada594..906c08fc05d4398bcee8edfc1e4afa67cc3f01e4 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,14 @@ # Apache Dubbo-go [涓枃](./README_CN.md) # -[](https://travis-ci.org/apache/dubbo-go) +[](https://travis-ci.org/apache/dubbo-go) [](https://codecov.io/gh/apache/dubbo-go) [](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc) [](https://goreportcard.com/report/github.com/apache/dubbo-go)  --- -Apache Dubbo Go Implementation. + +Apache Dubbo Go Implementation to bridge the gap between java and go. ## License @@ -16,23 +17,17 @@ Apache License, Version 2.0 ## Release note ## +[v1.5.5 - Jan 5, 2021](https://github.com/apache/dubbo-go/releases/tag/v1.5.5) + +[v1.4.5 - Nov 18, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.5) + [v1.5.4 - Nov 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.4) [v1.5.3 - Sep 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.3) -[v1.5.2 - discard]() - [v1.5.1 - Aug 23, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.1) -[v1.5.0 - Jul 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0) - -[v1.4.4 - Aug 11, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.4) - -[v1.4.3 - Jul 29, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.3) - -[v1.4.2 - Jun 7, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.2) - -[v1.4.1 - Apr 20, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.1) +[v1.5.0 - July 24, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.5.0) [v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0) @@ -50,7 +45,7 @@ Both extension module and layered project architecture is according to Apache Du  -If you wanna know more about dubbo-go, please visit this reference [Project Architecture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design) +If you want to know more about dubbo-go, please visit this reference [Project Architecture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design) ## Feature list ## @@ -74,10 +69,11 @@ Finished List: * Jsonrpc2.0 * [gRPC](https://github.com/apache/dubbo-go/pull/311) * [RESTful](https://github.com/apache/dubbo-go/pull/352) - + - Router * [Condition router](https://github.com/apache/dubbo-go/pull/294) * [Health check router](https://github.com/apache/dubbo-go/pull/389) + * [Dynamic_tag_router](https://github.com/apache/dubbo-go/pull/703) - Registry * ZooKeeper @@ -119,7 +115,7 @@ Finished List: - Invoke * [generic invoke](https://github.com/apache/dubbo-go/pull/122) - + - Monitor * Opentracing API * [Prometheus](https://github.com/apache/dubbo-go/pull/342) @@ -139,14 +135,10 @@ Finished List: * [Nacos](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/nacos/service_discovery.go) * [Zookeeper](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/zookeeper/service_discovery.go) * [Etcd](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/etcdv3/service_discovery.go) - -- Others: - * start check - * connecting certain provider - * multi-protocols - * multi-registries - * multi-versions - * service group + * [File](https://github.com/apache/dubbo-go/pull/732) + +- Tool + * [Dubbo-go-cli](https://github.com/apache/dubbo-go/pull/818) You can know more about dubbo-go by its [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap). @@ -154,32 +146,30 @@ You can know more about dubbo-go by its [roadmap](https://github.com/apache/dubb ## Document -https://dubbogo.github.io/dubbo-go-website (**Improving**) +[dubbo-doc](http://dubbo.apache.org/zh/blog/) or [dubbo-go-doc-list](http://alexstocks.github.io/html/dubbogo.html) ## Quick Start -[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) shows how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) carefully to learn how to dispose the configuration and compile the program. +[dubbo-go-samples](https://github.com/apache/dubbo-go-samples) gives many examples to tell u how to use dubbo-go. Please read the [dubbo-samples/golang/README.md](https://github.com/apache/dubbo-go-samples/blob/master/README.md) carefully to learn how to dispose the configuration and compile the program. ## Running unit tests -### Prepare +### Run -Mac/Linux ```bash -sh ./before_ut.sh +make verify ``` -Windows +### Verify license + ```bash -before_ut.bat +make license ``` -### Run -```bash -go test ./... +### Run unit test -# coverage -go test ./... -coverprofile=coverage.txt -covermode=atomic +```bash +make test ``` ## Build @@ -190,6 +180,30 @@ Please move to [dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) If you are willing to do some code contributions and document contributions to [Apache/dubbo-go](https://github.com/apache/dubbo-go), please visit [contribution intro](https://github.com/apache/dubbo-go/blob/master/contributing.md). +## Community + +If u want to communicate with our community, pls scan the following dubbobo Ding-Ding QR code or search our commnity DingDing group code 31363295. + +<div> +<table> + <tbody> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://alexstocks.github.io/html/dubbogo.html" target="_blank"> + <img width="80px" height="85px" src="./doc/pic/misc/dubbogo-dingding.png"> + </a> + </td> + </tr> + <tr></tr> + </tbody> +</table> +</div> + +If u want to visit the wechat group, pls add my wechat AlexanderStocks. + +We welcome the friends who can give us constructing suggestions instead of known-nothing. + ## Benchmark Benchmark project [dubbo-go-benchmark](https://github.com/dubbogo/dubbo-go-benchmark). @@ -198,7 +212,7 @@ About dubbo-go benchmarking report, please refer to [dubbo benchmarking report]( ## [User List](https://github.com/apache/dubbo-go/issues/2) -If you are using [apache/dubbo-go](github.com/apache/dubbo-go) and think that it helps you or want do some contributions to it, please add your company to to [the user list](https://github.com/apache/dubbo-go/issues/2) to let us know your needs. +If you are using [apache/dubbo-go](https://github.com/apache/dubbo-go) and think that it helps you or want do some contributions to it, please add your company to to [the user list](https://github.com/apache/dubbo-go/issues/2) to let us know your needs. <div> @@ -226,6 +240,52 @@ If you are using [apache/dubbo-go](github.com/apache/dubbo-go) and think that it <img width="222px" src="https://raw.githubusercontent.com/mosn/community/master/icons/png/mosn-labeled-horizontal.png"> </a> </td> + <td align="center" valign="middle"> + <a href="" target="_blank"> + <img width="222px" src="https://festatic.estudy.cn/assets/xhx-web/layout/logo.png"> + </a> + </td> + </tr> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://www.j.cn" target="_blank"> + <img width="222px" src="http://image.guang.j.cn/bbs/imgs/home/pc/icon_8500.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.genshuixue.com/" target="_blank"> + <img width="222px" src="https://i.gsxcdn.com/0cms/d/file/content/2020/02/5e572137d7d94.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="http://www.51h5.com" target="_blank"> + <img width="222px" src="https://fs-ews.51h5.com/common/hw_220_black.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.zto.com" target="_blank"> + <img width="222px" src="https://fscdn.zto.com/fs8/M02/B2/E4/wKhBD1-8o52Ae3GnAAASU3r62ME040.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.icsoc.net/" target="_blank"> + <img width="222px" src="https://oss.icsoc.net/icsoc-ekt-test-files/icsoc.png"> + </a> + </td> + </tr> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://www.mgtv.com" target="_blank"> + <img width="222px" src="https://ugc.hitv.com/platform_oss/F6077F1AA82542CDBDD88FD518E6E727.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="http://www.dmall.com" target="_blank"> + <img width="222px" src="https://mosn.io/images/community/duodian.png"> + </a> + </td> </tr> <tr></tr> </tbody> diff --git a/README_CN.md b/README_CN.md index 88e34e66f1530bd04fbdc11ef77e9632a539d420..060b5ded69c0d291e28d6e40841331cba3bcc89c 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,13 +1,14 @@ # Apache Dubbo-go [English](./README.md) # -[](https://travis-ci.org/apache/dubbo-go) +[](https://travis-ci.org/apache/dubbo-go) [](https://codecov.io/gh/apache/dubbo-go) [](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc) [](https://goreportcard.com/report/github.com/apache/dubbo-go)  --- -Apache Dubbo Go 璇█瀹炵幇 + +Apache Dubbo Go 璇█瀹炵幇锛屾灦璧穓ava鍜実o涔嬮棿鐨勬ˉ姊侊紝涓� gRPC/Spring Cloud 鐢熸€佷簰鑱斾簰閫氾紝甯﹂Java鐢熸€佷韩鍙椾簯鍘熺敓鏃朵唬鐨勬妧鏈孩鍒┿€� ## 璇佷功 ## @@ -15,24 +16,18 @@ Apache License, Version 2.0 ## 鍙戝竷鏃ュ織 ## +[v1.5.5 - 2021骞�1鏈�5鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.5) + +[v1.4.5 - 2020骞�11鏈�18鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.5) + [v1.5.4 - 2020骞�11鏈�1鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.4) [v1.5.3 - 2020骞�9鏈�23鏃(https://github.com/apache/dubbo-go/releases/tag/v1.5.3) -[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.4 - 2020骞�8鏈�11鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.4) - -[v1.4.3 - 2020骞�7鏈�29鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.3) - -[v1.4.2 - 2020骞�6鏈�7鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.2) - -[v1.4.1 - 2020骞�4鏈�20鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.1) - [v1.4.0 - 2020骞�3鏈�17鏃(https://github.com/apache/dubbo-go/releases/tag/v1.4.0) [v1.3.0 - 2020骞�3鏈�1鏃(https://github.com/apache/dubbo-go/releases/tag/v1.3.0) @@ -73,11 +68,12 @@ Apache License, Version 2.0 * Jsonrpc2.0 * [gRPC](https://github.com/apache/dubbo-go/pull/311) * [RESTful](https://github.com/apache/dubbo-go/pull/352) - + - 璺敱鍣� * [Condition router](https://github.com/apache/dubbo-go/pull/294) * [Health check router](https://github.com/apache/dubbo-go/pull/389) - + * [Dynamic_tag_router](https://github.com/apache/dubbo-go/pull/703) + - 娉ㄥ唽涓績 * ZooKeeper * [etcd v3](https://github.com/apache/dubbo-go/pull/148) @@ -111,13 +107,14 @@ Apache License, Version 2.0 * [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214) * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237) * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246) + * [GenericServiceFilter](https://github.com/apache/dubbo-go/pull/291) * [Auth/Sign](https://github.com/apache/dubbo-go/pull/323) * [Metrics filter](https://github.com/apache/dubbo-go/pull/342) * [Tracing filter](https://github.com/apache/dubbo-go/pull/335) - 璋冪敤 * [娉涘寲璋冪敤](https://github.com/apache/dubbo-go/pull/122) - + - 鐩戞帶 * Opentracing API * [Prometheus](https://github.com/apache/dubbo-go/pull/342) @@ -137,14 +134,10 @@ Apache License, Version 2.0 * [Nacos](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/nacos/service_discovery.go) * [Zookeeper](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/zookeeper/service_discovery.go) * [Etcd](https://github.com/apache/dubbo-go/blob/9a5990d9a9c3d5e6633c0d7d926c156416bcb931/registry/etcdv3/service_discovery.go) - -- 鍏朵粬鍔熻兘鏀寔: - * 鍚姩鏃舵鏌� - * 鏈嶅姟鐩磋繛 - * 澶氭湇鍔″崗璁� - * 澶氭敞鍐屼腑蹇� - * 澶氭湇鍔$増鏈� - * 鏈嶅姟鍒嗙粍 + * [File](https://github.com/apache/dubbo-go/pull/732) + +- 宸ュ叿绠� + * [Dubbo-go-cli](https://github.com/apache/dubbo-go/pull/818) 浣犲彲浠ラ€氳繃璁块棶 [roadmap](https://github.com/apache/dubbo-go/wiki/Roadmap) 鐭ラ亾鏇村鍏充簬 dubbo-go 鐨勪俊鎭€� @@ -152,42 +145,79 @@ Apache License, Version 2.0 ## 鏂囨。 -https://dubbogo.github.io/dubbo-go-website (**瀹屽杽涓�**) +璇疯闂� [dubbo瀹樻柟鏂囨。涓叧浜� dubbo-go 鐨勭郴鍒楁枃妗(http://dubbo.apache.org/zh/blog/) 鎴� [杩囧線dubbo-go鏂囨。鍒楄〃](http://alexstocks.github.io/html/dubbogo.html)銆� ## 蹇€熷紑濮� ## -[dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbo-samples/golang/README.md](https://github.com/dubbogo/dubbo-samples/blob/master/golang/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€� +[dubbo-samples/golang](https://github.com/apache/dubbo-go-samples)杩欎釜椤圭洰鐨勪簨渚嬪睍绀轰簡濡備綍浣跨敤 dubbo-go 銆傝浠旂粏闃呰 [dubbo-samples/golang/README.md](https://github.com/apache/dubbo-go-samples/blob/master/README.md) 瀛︿範濡備綍澶勭悊閰嶇疆骞剁紪璇戠▼搴忋€� ## 杩愯鍗曟祴 -### 鍑嗗 +### 鎵ц鍏ㄩ儴鏍¢獙 -Mac/Linux ```bash -sh ./before_ut.sh +make verify ``` -Windows +### 鏍¢獙璁稿彲璇� + ```bash -before_ut.bat +make license ``` -### 鎵ц -```bash -go test ./... +### 鎵ц鍗曞厓娴嬭瘯 -# coverage -go test ./... -coverprofile=coverage.txt -covermode=atomic +```bash +make test ``` ## 缂栬瘧 -璇风Щ姝� [dubbo-samples/golang](https://github.com/dubbogo/dubbo-samples) +璇风Щ姝� [dubbo-go-samples](https://github.com/apache/dubbo-go-samples) ## 濡備綍璐$尞 濡傛灉鎮ㄦ効鎰忕粰 [Apache/dubbo-go](https://github.com/apache/dubbo-go) 璐$尞浠g爜鎴栬€呮枃妗o紝鎴戜滑閮界儹鐑堟杩庛€傚叿浣撹鍙傝€� [contribution intro](https://github.com/apache/dubbo-go/blob/master/contributing.md)銆� +## 绀惧尯 + +濡傛灉鎯宠闂畼鏂归拤閽夌兢锛岃鍦ㄩ拤閽変腑鎼滅储绀惧尯缇ゅ彿 31363295 鎴栬€� 鎵弿濡備笅[浜岀淮鐮乚(https://mmbiz.qpic.cn/mmbiz_jpg/yvBJb5IiafvnHVBdtia30dxA2hKotr9DEckWsZ7aOJcDWDaSVMGwLmYv8GRgIQtqb4C2svicp8nVkMmGy7yKC5tyA/640?wx_fmt=jpeg&tp=webp&wxfrom=5&wx_lazy=1&wx_co=1)銆� + +<div> +<table> + <tbody> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://alexstocks.github.io/html/dubbogo.html" target="_blank"> + <img width="80px" height="85px" src="./doc/pic/misc/dubbogo-dingding.png"> + </a> + </td> + </tr> + <tr></tr> + </tbody> +</table> +</div> + +dubbogo 绀惧尯宸茬粡寮€閫氬井淇″叕浼楀彿 "dubbogo澶у尯"锛屽彲鍦ㄥ井淇℃悳绱� "dubbogo澶у尯" 鎴栬€呮壂鎻忓涓嬩簩缁寸爜鍏虫敞锛屽彲閫氳繃鍏紬鍙风淇$暀瑷€鍔犲叆 dubbogo 寰俊绀惧尯銆� + +<div> +<table> + <tbody> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <img width="80px" height="115px" src="./doc/pic/misc/dubbogo-wechat.png"> + </a> + </td> + </tr> + <tr></tr> + </tbody> +</table> +</div> + +浣滀负涓€涓淮鎶ゅ凡缁忓府鍔╂瀯寤轰簡缁忓彈澶氬澶у瀷寰湇鍔$郴缁熺殑绀惧尯锛屾垜浠冻浠ヤ负鐜版湁鐨勬垚缁╂劅鍒拌嚜璞€傜ぞ鍖烘杩庤兘鎻愬嚭寤鸿鎬ф剰瑙佽€咃紝鍙煡绱㈠彇鑰呭拰鍠峰瓙璇风粫琛屻€� + ## 鎬ц兘娴嬭瘯 ## 鎬ц兘娴嬭瘯椤圭洰鏄� [dubbo-go-benchmark](https://github.com/dubbogo/dubbo-go-benchmark)銆� @@ -196,7 +226,7 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic ## [User List](https://github.com/apache/dubbo-go/issues/2) -鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呮兂瀵瑰叾鍋氭敼杩涳紝璇锋坊鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撱€� +鑻ヤ綘姝e湪浣跨敤 [apache/dubbo-go](https://github.com/apache/dubbo-go) 涓旇涓哄叾鏈夌敤鎴栬€呮兂瀵瑰叾鍋氭敼杩涳紝璇锋坊鍒楄吹鍙镐俊鎭簬 [鐢ㄦ埛鍒楄〃](https://github.com/apache/dubbo-go/issues/2)锛屼互渚挎垜浠煡鏅撱€� <div> <table> @@ -223,6 +253,52 @@ go test ./... -coverprofile=coverage.txt -covermode=atomic <img width="222px" src="https://raw.githubusercontent.com/mosn/community/master/icons/png/mosn-labeled-horizontal.png"> </a> </td> + <td align="center" valign="middle"> + <a href="" target="_blank"> + <img width="222px" src="https://festatic.estudy.cn/assets/xhx-web/layout/logo.png"> + </a> + </td> + </tr> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://www.j.cn" target="_blank"> + <img width="222px" src="http://image.guang.j.cn/bbs/imgs/home/pc/icon_8500.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.genshuixue.com/" target="_blank"> + <img width="222px" src="https://i.gsxcdn.com/0cms/d/file/content/2020/02/5e572137d7d94.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="http://www.51h5.com" target="_blank"> + <img width="222px" src="https://fs-ews.51h5.com/common/hw_220_black.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.zto.com" target="_blank"> + <img width="222px" src="https://fscdn.zto.com/fs8/M02/B2/E4/wKhBD1-8o52Ae3GnAAASU3r62ME040.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="https://www.icsoc.net/" target="_blank"> + <img width="222px" src="https://oss.icsoc.net/icsoc-ekt-test-files/icsoc.png"> + </a> + </td> + </tr> + <tr></tr> + <tr> + <td align="center" valign="middle"> + <a href="http://www.mgtv.com" target="_blank"> + <img width="222px" src="https://ugc.hitv.com/platform_oss/F6077F1AA82542CDBDD88FD518E6E727.png"> + </a> + </td> + <td align="center" valign="middle"> + <a href="http://www.dmall.com" target="_blank"> + <img width="222px" src="https://mosn.io/images/community/duodian.png"> + </a> + </td> </tr> <tr></tr> </tbody> diff --git a/before_ut.bat b/before_ut.bat deleted file mode 100644 index 7f5cf50e900955f784552221569d9caf414274d4..0000000000000000000000000000000000000000 --- a/before_ut.bat +++ /dev/null @@ -1,43 +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. - -set zkJarName=zookeeper-3.4.9-fatjar.jar -set remoteJarUrl="https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/%zkJarName%" -set zkJarPath=remoting/zookeeper/zookeeper-4unittest/contrib/fatjar -set zkJar=%zkJarPath%/%zkJarName% - -if not exist "%zkJar%" ( - md "%zkJarPath%" - curl -L %remoteJarUrl% -o %zkJar% -) - -md config_center\zookeeper\zookeeper-4unittest\contrib\fatjar -xcopy /f "%zkJar%" "config_center/zookeeper/zookeeper-4unittest/contrib/fatjar/" - -md registry\zookeeper\zookeeper-4unittest\contrib\fatjar -xcopy /f "%zkJar%" "registry/zookeeper/zookeeper-4unittest/contrib/fatjar/" - -md cluster\router\chain\zookeeper-4unittest\contrib\fatjar -xcopy /f "%zkJar%" "cluster/router/chain/zookeeper-4unittest/contrib/fatjar/" - -md cluster\router\condition\zookeeper-4unittest\contrib\fatjar -xcopy /f "%zkJar%" "cluster/router/condition/zookeeper-4unittest/contrib/fatjar/" - -mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar - -md metadata\report\zookeeper\zookeeper-4unittest\contrib\fatjar -xcopy /f "%zkJar%" "metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar/" \ No newline at end of file diff --git a/before_ut.sh b/before_ut.sh deleted file mode 100755 index b55e424ef72b33181b2ea40fdb37ac319110aec0..0000000000000000000000000000000000000000 --- a/before_ut.sh +++ /dev/null @@ -1,43 +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. - -zkJarName="zookeeper-3.4.9-fatjar.jar" -remoteJarUrl="https://github.com/dubbogo/resources/raw/master/zookeeper-4unitest/contrib/fatjar/${zkJarName}" -zkJarPath="remoting/zookeeper/zookeeper-4unittest/contrib/fatjar" -zkJar="${zkJarPath}/${zkJarName}" - -if [ ! -f "${zkJar}" ]; then - mkdir -p ${zkJarPath} - wget -P "${zkJarPath}" ${remoteJarUrl} -fi - -mkdir -p config_center/zookeeper/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} config_center/zookeeper/zookeeper-4unittest/contrib/fatjar - -mkdir -p registry/zookeeper/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} registry/zookeeper/zookeeper-4unittest/contrib/fatjar - -mkdir -p cluster/router/chain/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} cluster/router/chain/zookeeper-4unittest/contrib/fatjar - -mkdir -p cluster/router/condition/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} cluster/router/condition/zookeeper-4unittest/contrib/fatjar - -mkdir -p cluster/router/tag/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} cluster/router/tag/zookeeper-4unittest/contrib/fatjar - -mkdir -p metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar -cp ${zkJar} metadata/report/zookeeper/zookeeper-4unittest/contrib/fatjar \ No newline at end of file diff --git a/before_validate_license.sh b/before_validate_license.sh deleted file mode 100644 index 8fa6e381c7a4cd44835d107ba9213f685f899a10..0000000000000000000000000000000000000000 --- a/before_validate_license.sh +++ /dev/null @@ -1,26 +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. - -remoteLicenseCheckerPath="https://github.com/dubbogo/resources/raw/master/tools/license" -remoteLicenseCheckerName="license-header-checker" -remoteLicenseCheckerURL="${remoteLicenseCheckerPath}/${remoteLicenseCheckerName}" -remoteLicenseName="license.txt" -remoteLicenseURL="${remoteLicenseCheckerPath}/${remoteLicenseName}" - -licensePath="/tmp/tools/license" -mkdir -p ${licensePath} -wget -P "${licensePath}" ${remoteLicenseCheckerURL} -wget -P "${licensePath}" ${remoteLicenseURL} diff --git a/cluster/cluster_impl/available_cluster.go b/cluster/cluster_impl/available_cluster.go index ebd5767e4c320f10c8911cf9ac3f2c81deaafb0e..1f41890f5824ff0e2ca542b923b7cf85a6e3a2a1 100644 --- a/cluster/cluster_impl/available_cluster.go +++ b/cluster/cluster_impl/available_cluster.go @@ -39,6 +39,6 @@ func NewAvailableCluster() cluster.Cluster { } // Join returns a baseClusterInvoker instance -func (cluser *availableCluster) Join(directory cluster.Directory) protocol.Invoker { +func (cluster *availableCluster) Join(directory cluster.Directory) protocol.Invoker { return NewAvailableClusterInvoker(directory) } diff --git a/cluster/cluster_impl/base_cluster_invoker.go b/cluster/cluster_impl/base_cluster_invoker.go index ced5b15cb9a1c2292ca866f6f7478ce2b23a30b9..0d39bff13ee3a917268dd2ee82e8f6aff7ccecbc 100644 --- a/cluster/cluster_impl/base_cluster_invoker.go +++ b/cluster/cluster_impl/base_cluster_invoker.go @@ -22,7 +22,6 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "go.uber.org/atomic" ) @@ -32,6 +31,7 @@ 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/protocol" ) @@ -51,7 +51,7 @@ func newBaseClusterInvoker(directory cluster.Directory) baseClusterInvoker { } } -func (invoker *baseClusterInvoker) GetUrl() common.URL { +func (invoker *baseClusterInvoker) GetUrl() *common.URL { return invoker.directory.GetUrl() } @@ -72,7 +72,7 @@ func (invoker *baseClusterInvoker) IsAvailable() bool { //check invokers availables func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, invocation protocol.Invocation) error { if len(invokers) == 0 { - ip, _ := gxnet.GetLocalIP() + ip := common.GetLocalIp() return perrors.Errorf("Failed to invoke the method %v. No provider available for the service %v from "+ "registry %v on the consumer %v using the dubbo version %v .Please check if the providers have been started and registered.", invocation.MethodName(), invoker.directory.GetUrl().SubURL.Key(), invoker.directory.GetUrl().String(), ip, constant.Version) @@ -84,7 +84,7 @@ func (invoker *baseClusterInvoker) checkInvokers(invokers []protocol.Invoker, in //check cluster invoker is destroyed or not func (invoker *baseClusterInvoker) checkWhetherDestroyed() error { if invoker.destroyed.Load() { - ip, _ := gxnet.GetLocalIP() + ip := common.GetLocalIp() return perrors.Errorf("Rpc cluster invoker for %v on consumer %v use dubbo version %v is now destroyed! can not invoke any more. ", invoker.directory.GetUrl().Service(), ip, constant.Version) } @@ -120,35 +120,50 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p } func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { + if len(invokers) == 0 { + return nil + } + go protocol.TryRefreshBlackList() if len(invokers) == 1 { - return invokers[0] + if invokers[0].IsAvailable() { + return invokers[0] + } + protocol.SetInvokerUnhealthyStatus(invokers[0]) + logger.Errorf("the invokers of %s is nil. ", invokers[0].GetUrl().ServiceKey()) + return nil } selectedInvoker := lb.Select(invokers, invocation) - //judge to if the selectedInvoker is invoked - + //judge if the selected Invoker is invoked and available if (!selectedInvoker.IsAvailable() && invoker.availablecheck) || isInvoked(selectedInvoker, invoked) { + protocol.SetInvokerUnhealthyStatus(selectedInvoker) + otherInvokers := getOtherInvokers(invokers, selectedInvoker) // do reselect - var reslectInvokers []protocol.Invoker - - for _, invoker := range invokers { - if !invoker.IsAvailable() { + for i := 0; i < 3; i++ { + if len(otherInvokers) == 0 { + // no other ivk to reselect, return to fallback + break + } + reselectedInvoker := lb.Select(otherInvokers, invocation) + if isInvoked(reselectedInvoker, invoked) { + otherInvokers = getOtherInvokers(otherInvokers, reselectedInvoker) continue } - - if !isInvoked(invoker, invoked) { - reslectInvokers = append(reslectInvokers, invoker) + if !reselectedInvoker.IsAvailable() { + logger.Infof("the invoker of %s is not available, maybe some network error happened or the server is shutdown.", + invoker.GetUrl().Ip) + protocol.SetInvokerUnhealthyStatus(reselectedInvoker) + otherInvokers = getOtherInvokers(otherInvokers, reselectedInvoker) + continue } + return reselectedInvoker } - - if len(reslectInvokers) > 0 { - selectedInvoker = lb.Select(reslectInvokers, invocation) - } else { - return nil - } + } else { + return selectedInvoker } - return selectedInvoker + logger.Errorf("all %d invokers is unavailable for %s.", len(invokers), selectedInvoker.GetUrl().String()) + return nil } func (invoker *baseClusterInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { @@ -187,3 +202,13 @@ func getLoadBalance(invoker protocol.Invoker, invocation protocol.Invocation) cl } return extension.GetLoadbalance(lb) } + +func getOtherInvokers(invokers []protocol.Invoker, invoker protocol.Invoker) []protocol.Invoker { + otherInvokers := make([]protocol.Invoker, 0) + for _, i := range invokers { + if i != invoker { + otherInvokers = append(otherInvokers, i) + } + } + return otherInvokers +} diff --git a/cluster/cluster_impl/failback_cluster_invoker.go b/cluster/cluster_impl/failback_cluster_invoker.go index 62f48045ec6edbc19d6603509fa1ae8c2d4ce9ee..5e0d133a670160df4acf321ea05356cb2a45a8b5 100644 --- a/cluster/cluster_impl/failback_cluster_invoker.go +++ b/cluster/cluster_impl/failback_cluster_invoker.go @@ -77,8 +77,7 @@ func (invoker *failbackClusterInvoker) tryTimerTaskProc(ctx context.Context, ret invoked = append(invoked, retryTask.lastInvoker) retryInvoker := invoker.doSelect(retryTask.loadbalance, retryTask.invocation, retryTask.invokers, invoked) - var result protocol.Result - result = retryInvoker.Invoke(ctx, retryTask.invocation) + result := retryInvoker.Invoke(ctx, retryTask.invocation) if result.Error() != nil { retryTask.lastInvoker = retryInvoker invoker.checkRetry(retryTask, result.Error()) @@ -121,8 +120,11 @@ func (invoker *failbackClusterInvoker) checkRetry(retryTask *retryTimerTask, err if retryTask.retries > invoker.maxRetries { logger.Errorf("Failed retry times exceed threshold (%v), We have to abandon, invocation-> %v.\n", retryTask.retries, retryTask.invocation) - } else { - invoker.taskList.Put(retryTask) + return + } + + if err := invoker.taskList.Put(retryTask); err != nil { + logger.Errorf("invoker.taskList.Put(retryTask:%#v) = error:%v", retryTask, err) } } diff --git a/cluster/cluster_impl/failback_cluster_test.go b/cluster/cluster_impl/failback_cluster_test.go index 0edb81d4285fa68ceefd96100b541ba334f95bda..d36e16e2693f0926f8c8e39b6cd328b688ffa4b2 100644 --- a/cluster/cluster_impl/failback_cluster_test.go +++ b/cluster/cluster_impl/failback_cluster_test.go @@ -72,6 +72,8 @@ func TestFailbackSuceess(t *testing.T) { invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() + invoker.EXPECT().IsAvailable().Return(true) + mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) @@ -88,6 +90,7 @@ func TestFailbackRetryOneSuccess(t *testing.T) { clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() + invoker.EXPECT().IsAvailable().Return(true) // failed at first mockFailedResult := &protocol.RPCResult{Err: perrors.New("error")} @@ -98,6 +101,7 @@ func TestFailbackRetryOneSuccess(t *testing.T) { wg.Add(1) now := time.Now() mockSuccResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} + invoker.EXPECT().IsAvailable().Return(true) invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result { delta := time.Since(now).Nanoseconds() / int64(time.Second) assert.True(t, delta >= 5) @@ -131,6 +135,7 @@ func TestFailbackRetryFailed(t *testing.T) { clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() mockFailedResult := &protocol.RPCResult{Err: perrors.New("error")} invoker.EXPECT().Invoke(gomock.Any()).Return(mockFailedResult) @@ -177,6 +182,7 @@ func TestFailbackRetryFailed10Times(t *testing.T) { clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker) clusterInvoker.maxRetries = 10 + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() // 10 task should failed firstly. @@ -220,6 +226,7 @@ func TestFailbackOutOfLimit(t *testing.T) { clusterInvoker.failbackTasks = 1 invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() mockFailedResult := &protocol.RPCResult{Err: perrors.New("error")} invoker.EXPECT().Invoke(gomock.Any()).Return(mockFailedResult).Times(11) diff --git a/cluster/cluster_impl/failfast_cluster_test.go b/cluster/cluster_impl/failfast_cluster_test.go index 77e8e9c5da73bfc8bcf08dbd90351bfd23d7e651..9ac06b8d4a7bec39b08a308cc52fe41e9524b1c0 100644 --- a/cluster/cluster_impl/failfast_cluster_test.go +++ b/cluster/cluster_impl/failfast_cluster_test.go @@ -53,6 +53,7 @@ func registerFailfast(invoker *mock.MockInvoker) protocol.Invoker { invokers := []protocol.Invoker{} invokers = append(invokers, invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failfastUrl) staticDir := directory.NewStaticDirectory(invokers) @@ -67,6 +68,7 @@ func TestFailfastInvokeSuccess(t *testing.T) { invoker := mock.NewMockInvoker(ctrl) clusterInvoker := registerFailfast(invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes() mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} @@ -87,6 +89,7 @@ func TestFailfastInvokeFail(t *testing.T) { invoker := mock.NewMockInvoker(ctrl) clusterInvoker := registerFailfast(invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failfastUrl).AnyTimes() mockResult := &protocol.RPCResult{Err: perrors.New("error")} diff --git a/cluster/cluster_impl/failover_cluster_invoker.go b/cluster/cluster_impl/failover_cluster_invoker.go index 4260a9324dd360ac24f80e425d8542a423c8815e..ca490e7f8e2ff44303abe7d695313153b7466a00 100644 --- a/cluster/cluster_impl/failover_cluster_invoker.go +++ b/cluster/cluster_impl/failover_cluster_invoker.go @@ -24,12 +24,12 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) import ( "github.com/apache/dubbo-go/cluster" + "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" @@ -51,6 +51,7 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr result protocol.Result invoked []protocol.Invoker providers []string + ivk protocol.Invoker ) invokers := invoker.directory.List(invocation) @@ -75,7 +76,7 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr return &protocol.RPCResult{Err: err} } } - ivk := invoker.doSelect(loadBalance, invocation, invokers, invoked) + ivk = invoker.doSelect(loadBalance, invocation, invokers, invoked) if ivk == nil { continue } @@ -88,10 +89,17 @@ func (invoker *failoverClusterInvoker) Invoke(ctx context.Context, invocation pr } return result } - - ip, _ := gxnet.GetLocalIP() + ip := common.GetLocalIp() invokerSvc := invoker.GetUrl().Service() invokerUrl := invoker.directory.GetUrl() + if ivk == nil { + logger.Errorf("Failed to invoke the method %s of the service %s .No provider is available.", methodName, invokerSvc) + return &protocol.RPCResult{ + Err: perrors.Errorf("Failed to invoke the method %s of the service %s .No provider is available because can't connect server.", + methodName, invokerSvc), + } + } + return &protocol.RPCResult{ Err: perrors.Wrap(result.Error(), fmt.Sprintf("Failed to invoke the method %v in the service %v. "+ "Tried %v times of the providers %v (%v/%v)from the registry %v on the consumer %v using the dubbo version %v. "+ diff --git a/cluster/cluster_impl/failover_cluster_test.go b/cluster/cluster_impl/failover_cluster_test.go index d3ac2c8a5ffd7fce647649c53fe343ba93999636..3ea6232d4747f722c5a933cc056ff06304913965 100644 --- a/cluster/cluster_impl/failover_cluster_test.go +++ b/cluster/cluster_impl/failover_cluster_test.go @@ -45,7 +45,7 @@ import ( // nolint type MockInvoker struct { - url common.URL + url *common.URL available bool destroyed bool @@ -53,7 +53,7 @@ type MockInvoker struct { } // nolint -func NewMockInvoker(url common.URL, successCount int) *MockInvoker { +func NewMockInvoker(url *common.URL, successCount int) *MockInvoker { return &MockInvoker{ url: url, available: true, @@ -63,7 +63,7 @@ func NewMockInvoker(url common.URL, successCount int) *MockInvoker { } // nolint -func (bi *MockInvoker) GetUrl() common.URL { +func (bi *MockInvoker) GetUrl() *common.URL { return bi.url } diff --git a/cluster/cluster_impl/failsafe_cluster_test.go b/cluster/cluster_impl/failsafe_cluster_test.go index d9a716e1ae65a84b605b4b7af1872b3a85dc9369..5e208bdddee335cc5dff6beed5ab34243831dbb5 100644 --- a/cluster/cluster_impl/failsafe_cluster_test.go +++ b/cluster/cluster_impl/failsafe_cluster_test.go @@ -52,6 +52,7 @@ func registerFailsafe(invoker *mock.MockInvoker) protocol.Invoker { invokers := []protocol.Invoker{} invokers = append(invokers, invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl) @@ -67,6 +68,8 @@ func TestFailSafeInvokeSuccess(t *testing.T) { invoker := mock.NewMockInvoker(ctrl) clusterInvoker := registerFailsafe(invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() + invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} @@ -85,6 +88,7 @@ func TestFailSafeInvokeFail(t *testing.T) { invoker := mock.NewMockInvoker(ctrl) clusterInvoker := registerFailsafe(invoker) + invoker.EXPECT().IsAvailable().Return(true).AnyTimes() invoker.EXPECT().GetUrl().Return(failsafeUrl).AnyTimes() diff --git a/cluster/cluster_impl/forking_cluster_invoker.go b/cluster/cluster_impl/forking_cluster_invoker.go index 168444881653ca38ef61a9bc8e50f2d4bc3e624c..3ffda580ce9847bb60a0a837c5fac6b420450141 100644 --- a/cluster/cluster_impl/forking_cluster_invoker.go +++ b/cluster/cluster_impl/forking_cluster_invoker.go @@ -56,7 +56,7 @@ func (invoker *forkingClusterInvoker) Invoke(ctx context.Context, invocation pro } var selected []protocol.Invoker - forks := int(invoker.GetUrl().GetParamInt(constant.FORKS_KEY, constant.DEFAULT_FORKS)) + forks := invoker.GetUrl().GetParamByIntValue(constant.FORKS_KEY, constant.DEFAULT_FORKS) timeouts := invoker.GetUrl().GetParamInt(constant.TIMEOUT_KEY, constant.DEFAULT_TIMEOUT) if forks < 0 || forks > len(invokers) { selected = invokers diff --git a/cluster/cluster_impl/zone_aware_cluster_invoker.go b/cluster/cluster_impl/zone_aware_cluster_invoker.go index 0f52b0442c235d570e4cc9d8491aa80a9df5842d..050f831f06c65c9d3e7cbb5123123281ec55df89 100644 --- a/cluster/cluster_impl/zone_aware_cluster_invoker.go +++ b/cluster/cluster_impl/zone_aware_cluster_invoker.go @@ -43,7 +43,7 @@ func newZoneAwareClusterInvoker(directory cluster.Directory) protocol.Invoker { invoke := &zoneAwareClusterInvoker{ baseClusterInvoker: newBaseClusterInvoker(directory), } - // add self to interceptor + // add local to interceptor invoke.interceptor = invoke return invoke } diff --git a/cluster/directory/base_directory.go b/cluster/directory/base_directory.go index 20db1f2b7de71c843caf7c7abda39e40c68e4ecd..309cd4429c7256eccab556369db9b0dd264e7480 100644 --- a/cluster/directory/base_directory.go +++ b/cluster/directory/base_directory.go @@ -65,8 +65,8 @@ func (dir *BaseDirectory) SetRouterChain(routerChain router.Chain) { } // GetUrl Get URL -func (dir *BaseDirectory) GetUrl() common.URL { - return *dir.url +func (dir *BaseDirectory) GetUrl() *common.URL { + return dir.url } // GetDirectoryUrl Get URL instance @@ -82,6 +82,8 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { routers := make([]router.PriorityRouter, 0, len(urls)) + rc := dir.routerChain + for _, url := range urls { routerKey := url.GetParam(constant.ROUTER_KEY, "") @@ -94,7 +96,7 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { } } factory := extension.GetRouterFactory(url.Protocol) - r, err := factory.NewPriorityRouter(url) + r, err := factory.NewPriorityRouter(url, rc.GetNotifyChan()) if err != nil { logger.Errorf("Create router fail. router key: %s, url:%s, error: %+v", routerKey, url.Service(), err) return @@ -104,10 +106,8 @@ func (dir *BaseDirectory) SetRouters(urls []*common.URL) { logger.Infof("Init file condition router success, size: %v", len(routers)) dir.mutex.Lock() - rc := dir.routerChain - dir.mutex.Unlock() - rc.AddRouters(routers) + dir.mutex.Unlock() } func (dir *BaseDirectory) isProperRouter(url *common.URL) bool { diff --git a/cluster/directory/base_directory_test.go b/cluster/directory/base_directory_test.go index a2b62dfa008e6cd17b1200d93cd235da17d03905..443f07de2cc7d420fa8a7caa22709980f522b47b 100644 --- a/cluster/directory/base_directory_test.go +++ b/cluster/directory/base_directory_test.go @@ -24,11 +24,11 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" "github.com/stretchr/testify/assert" ) import ( + "github.com/apache/dubbo-go/cluster/router/chain" _ "github.com/apache/dubbo-go/cluster/router/condition" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" @@ -41,21 +41,20 @@ var ( ) func TestNewBaseDirectory(t *testing.T) { - directory := NewBaseDirectory(&url) - assert.NotNil(t, directory) - assert.Equal(t, url, directory.GetUrl()) - assert.Equal(t, &url, directory.GetDirectoryUrl()) + dir := NewBaseDirectory(url) + assert.Equal(t, url, dir.GetUrl()) + assert.Equal(t, url, dir.GetDirectoryUrl()) } func TestBuildRouterChain(t *testing.T) { regURL := url regURL.AddParam(constant.INTERFACE_KEY, "mock-app") - directory := NewBaseDirectory(®URL) - - assert.NotNil(t, directory) - - localIP, _ := gxnet.GetLocalIP() + directory := NewBaseDirectory(regURL) + var err error + directory.routerChain, err = chain.NewRouterChain(regURL) + assert.Nil(t, err) + localIP := common.GetLocalIp() rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) routeURL := getRouteURL(rule, anyURL) routeURL.AddParam(constant.INTERFACE_KEY, "mock-app") @@ -67,19 +66,19 @@ func TestBuildRouterChain(t *testing.T) { assert.NotNil(t, chain) } -func getRouteURL(rule string, u common.URL) *common.URL { +func getRouteURL(rule string, u *common.URL) *common.URL { ru := u ru.AddParam("rule", rule) ru.AddParam("force", "true") ru.AddParam(constant.ROUTER_KEY, "router") - return &ru + return ru } func TestIsProperRouter(t *testing.T) { regURL := url regURL.AddParam(constant.APPLICATION_KEY, "mock-app") - d := NewBaseDirectory(®URL) - localIP, _ := gxnet.GetLocalIP() + d := NewBaseDirectory(regURL) + localIP := common.GetLocalIp() rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) routeURL := getRouteURL(rule, anyURL) routeURL.AddParam(constant.APPLICATION_KEY, "mock-app") @@ -88,7 +87,7 @@ func TestIsProperRouter(t *testing.T) { regURL.AddParam(constant.APPLICATION_KEY, "") regURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService") - d = NewBaseDirectory(®URL) + d = NewBaseDirectory(regURL) routeURL = getRouteURL(rule, anyURL) routeURL.AddParam(constant.INTERFACE_KEY, "com.foo.BarService") rst = d.isProperRouter(routeURL) @@ -96,14 +95,14 @@ func TestIsProperRouter(t *testing.T) { regURL.AddParam(constant.APPLICATION_KEY, "") regURL.AddParam(constant.INTERFACE_KEY, "") - d = NewBaseDirectory(®URL) + d = NewBaseDirectory(regURL) routeURL = getRouteURL(rule, anyURL) rst = d.isProperRouter(routeURL) assert.True(t, rst) regURL.SetParam(constant.APPLICATION_KEY, "") regURL.SetParam(constant.INTERFACE_KEY, "") - d = NewBaseDirectory(®URL) + d = NewBaseDirectory(regURL) routeURL = getRouteURL(rule, anyURL) routeURL.AddParam(constant.APPLICATION_KEY, "mock-service") rst = d.isProperRouter(routeURL) @@ -111,7 +110,7 @@ func TestIsProperRouter(t *testing.T) { regURL.SetParam(constant.APPLICATION_KEY, "") regURL.SetParam(constant.INTERFACE_KEY, "") - d = NewBaseDirectory(®URL) + d = NewBaseDirectory(regURL) routeURL = getRouteURL(rule, anyURL) routeURL.AddParam(constant.INTERFACE_KEY, "mock-service") rst = d.isProperRouter(routeURL) diff --git a/cluster/directory/static_directory.go b/cluster/directory/static_directory.go index 87f51356495dbd0a956c42bf4f34022b4d21ad4d..d9695d46b3d25dd89a80e10c2396e650492840ad 100644 --- a/cluster/directory/static_directory.go +++ b/cluster/directory/static_directory.go @@ -34,15 +34,18 @@ type staticDirectory struct { // NewStaticDirectory Create a new staticDirectory with invokers func NewStaticDirectory(invokers []protocol.Invoker) *staticDirectory { - var url common.URL + var url *common.URL if len(invokers) > 0 { url = invokers[0].GetUrl() } - return &staticDirectory{ - BaseDirectory: NewBaseDirectory(&url), + dir := &staticDirectory{ + BaseDirectory: NewBaseDirectory(url), invokers: invokers, } + + dir.routerChain.SetInvokers(invokers) + return dir } //for-loop invokers ,if all invokers is available ,then it means directory is available @@ -69,7 +72,7 @@ func (dir *staticDirectory) List(invocation protocol.Invocation) []protocol.Invo return invokers } dirUrl := dir.GetUrl() - return routerChain.Route(invokers, &dirUrl, invocation) + return routerChain.Route(dirUrl, invocation) } // Destroy Destroy @@ -88,10 +91,11 @@ func (dir *staticDirectory) BuildRouterChain(invokers []protocol.Invoker) error return perrors.Errorf("invokers == null") } url := invokers[0].GetUrl() - routerChain, e := chain.NewRouterChain(&url) + routerChain, e := chain.NewRouterChain(url) if e != nil { return e } + routerChain.SetInvokers(dir.invokers) dir.SetRouterChain(routerChain) return nil } diff --git a/cluster/loadbalance/consistent_hash.go b/cluster/loadbalance/consistent_hash.go index 27ce2369f9de6dfe76bd35581ea26f0e0c24e480..3d036b4f3c5c50874e09efb8fd1ef34e969585d8 100644 --- a/cluster/loadbalance/consistent_hash.go +++ b/cluster/loadbalance/consistent_hash.go @@ -105,7 +105,7 @@ func newConsistentHashSelector(invokers []protocol.Invoker, methodName string, selector.virtualInvokers = make(map[uint32]protocol.Invoker) selector.hashCode = hashCode url := invokers[0].GetUrl() - selector.replicaNum = int(url.GetMethodParamInt(methodName, HashNodes, 160)) + selector.replicaNum = url.GetMethodParamIntValue(methodName, HashNodes, 160) indices := re.Split(url.GetMethodParam(methodName, HashArguments, "0"), -1) for _, index := range indices { i, err := strconv.Atoi(index) diff --git a/cluster/loadbalance/consistent_hash_test.go b/cluster/loadbalance/consistent_hash_test.go index 9f22d39dc46243dddda89151e07dbea39ab933fb..0fbb74059cf0af6cf7e75aab646d7c5c146bcc28 100644 --- a/cluster/loadbalance/consistent_hash_test.go +++ b/cluster/loadbalance/consistent_hash_test.go @@ -84,9 +84,9 @@ func TestConsistentHashLoadBalanceSuite(t *testing.T) { type consistentHashLoadBalanceSuite struct { suite.Suite - url1 common.URL - url2 common.URL - url3 common.URL + url1 *common.URL + url2 *common.URL + url3 *common.URL invokers []protocol.Invoker invoker1 protocol.Invoker invoker2 protocol.Invoker diff --git a/cluster/loadbalance/random_test.go b/cluster/loadbalance/random_test.go index b94d7da43d5bd42e6798fca750c8616830a8df8f..c24fdf0556b1323ccfc08a2f9f384c60e2fa2f5e 100644 --- a/cluster/loadbalance/random_test.go +++ b/cluster/loadbalance/random_test.go @@ -83,6 +83,7 @@ func TestRandomlbSelectWeight(t *testing.T) { } selectedInvoker = append(selectedInvoker, s) } + assert.Equal(t, 10000, len(selectedInvoker)) assert.Condition(t, func() bool { // really is 0.9999999999999 @@ -114,6 +115,8 @@ func TestRandomlbSelectWarmup(t *testing.T) { } selectedInvoker = append(selectedInvoker, s) } + assert.Equal(t, 10000, len(selectedInvoker)) + assert.Condition(t, func() bool { return selected/10000 < 0.1 }) diff --git a/cluster/router/.gitkeep b/cluster/router/.gitkeep deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/cluster/router/chan.go b/cluster/router/chain.go similarity index 80% rename from cluster/router/chan.go rename to cluster/router/chain.go index e3e84b81f3f812fcb0bcfbcddc200b4c54e31213..cb33cf927fb982ec8e62133d0c68f2dce5e142f3 100644 --- a/cluster/router/chan.go +++ b/cluster/router/chain.go @@ -18,15 +18,17 @@ package router import ( + "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/protocol" ) // Chain type Chain interface { - router + Route(*common.URL, protocol.Invocation) []protocol.Invoker + // Refresh invokers + SetInvokers([]protocol.Invoker) // 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) + // GetNotifyChan get notify channel of this chain + GetNotifyChan() chan struct{} } diff --git a/cluster/router/chain/chain.go b/cluster/router/chain/chain.go index 8746c1daf7f878a066ea005f910520e07c28318c..13cb1ff3528414a3c164c6b634cfd92a3cd17664 100644 --- a/cluster/router/chain/chain.go +++ b/cluster/router/chain/chain.go @@ -18,23 +18,29 @@ package chain import ( - "math" "sort" "sync" + "time" ) import ( perrors "github.com/pkg/errors" + "go.uber.org/atomic" ) import ( "github.com/apache/dubbo-go/cluster/router" "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/protocol" ) +const ( + timeInterval = 5 * time.Second +) + // RouterChain Router chain type RouterChain struct { // Full list of addresses from registry, classified by method name. @@ -47,31 +53,43 @@ type RouterChain struct { mutex sync.RWMutex - url common.URL + url *common.URL + + // The times of address notification since last update for address cache + count int64 + // The timestamp of last update for address cache + last time.Time + // Channel for notify to update the address cache + notify chan struct{} + // Address cache + cache atomic.Value +} + +func (c *RouterChain) GetNotifyChan() chan struct{} { + return c.notify } // Route Loop routers in RouterChain and call Route method to determine the target invokers list. -func (c *RouterChain) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - finalInvokers := invokers - l := len(c.routers) - rs := make([]router.PriorityRouter, l, int(math.Ceil(float64(l)*1.2))) - c.mutex.RLock() - copy(rs, c.routers) - c.mutex.RUnlock() +func (c *RouterChain) Route(url *common.URL, invocation protocol.Invocation) []protocol.Invoker { + cache := c.loadCache() + if cache == nil { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.invokers + } - for _, r := range rs { - finalInvokers = r.Route(finalInvokers, url, invocation) + bitmap := cache.bitmap + for _, r := range c.copyRouters() { + bitmap = r.Route(bitmap, cache, url, invocation) } - return finalInvokers -} -// SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change. -func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) { - for _, r := range c.routers { - if notifyRouter, ok := r.(router.NotifyRouter); ok { - notifyRouter.Notify(invokers) - } + indexes := bitmap.ToArray() + finalInvokers := make([]protocol.Invoker, len(indexes)) + for i, index := range indexes { + finalInvokers[i] = cache.invokers[index] } + + return finalInvokers } // AddRouters Add routers to router chain @@ -86,10 +104,119 @@ func (c *RouterChain) AddRouters(routers []router.PriorityRouter) { c.mutex.Lock() defer c.mutex.Unlock() c.routers = newRouters + go func() { + c.notify <- struct{}{} + }() +} + +// SetInvokers receives updated invokers from registry center. If the times of notification exceeds countThreshold and +// time interval exceeds timeThreshold since last cache update, then notify to update the cache. +func (c *RouterChain) SetInvokers(invokers []protocol.Invoker) { + c.mutex.Lock() + c.invokers = invokers + c.mutex.Unlock() + + go func() { + c.notify <- struct{}{} + }() +} + +// loop listens on events to update the address cache when it receives notification +// from address update, +func (c *RouterChain) loop() { + ticker := time.NewTicker(timeInterval) + for { + select { + case <-ticker.C: + if protocol.GetAndRefreshState() { + c.buildCache() + } + case <-c.notify: + c.buildCache() + } + } +} + +// copyRouters make a snapshot copy from RouterChain's router list. +func (c *RouterChain) copyRouters() []router.PriorityRouter { + c.mutex.RLock() + defer c.mutex.RUnlock() + ret := make([]router.PriorityRouter, 0, len(c.routers)) + ret = append(ret, c.routers...) + return ret +} + +// copyInvokers copies a snapshot of the received invokers. +func (c *RouterChain) copyInvokers() []protocol.Invoker { + c.mutex.RLock() + defer c.mutex.RUnlock() + if c.invokers == nil || len(c.invokers) == 0 { + return nil + } + ret := make([]protocol.Invoker, 0, len(c.invokers)) + ret = append(ret, c.invokers...) + return ret +} + +// loadCache loads cache from sync.Value to guarantee the visibility +func (c *RouterChain) loadCache() *InvokerCache { + v := c.cache.Load() + if v == nil { + return nil + } + + return v.(*InvokerCache) +} + +// copyInvokerIfNecessary compares chain's invokers copy and cache's invokers copy, to avoid copy as much as possible +func (c *RouterChain) copyInvokerIfNecessary(cache *InvokerCache) []protocol.Invoker { + var invokers []protocol.Invoker + if cache != nil { + invokers = cache.invokers + } + + c.mutex.RLock() + defer c.mutex.RUnlock() + if isInvokersChanged(invokers, c.invokers) { + invokers = c.copyInvokers() + } + return invokers +} + +// buildCache builds address cache with the new invokers for all poolable routers. +func (c *RouterChain) buildCache() { + origin := c.loadCache() + invokers := c.copyInvokerIfNecessary(origin) + if len(invokers) == 0 { + return + } + + var ( + mutex sync.Mutex + wg sync.WaitGroup + ) + + cache := BuildCache(invokers) + for _, r := range c.copyRouters() { + if p, ok := r.(router.Poolable); ok { + wg.Add(1) + go func(p router.Poolable) { + defer wg.Done() + pool, info := poolRouter(p, origin, invokers) + mutex.Lock() + defer mutex.Unlock() + cache.pools[p.Name()] = pool + cache.metadatas[p.Name()] = info + }(p) + } + } + wg.Wait() + + c.cache.Store(cache) } // URL Return URL in RouterChain -func (c *RouterChain) URL() common.URL { +func (c *RouterChain) URL() *common.URL { return c.url } @@ -100,9 +227,15 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { if len(routerFactories) == 0 { return nil, perrors.Errorf("No routerFactory exits , create one please") } + + chain := &RouterChain{ + last: time.Now(), + notify: make(chan struct{}), + } + routers := make([]router.PriorityRouter, 0, len(routerFactories)) for key, routerFactory := range routerFactories { - r, err := routerFactory().NewPriorityRouter(url) + r, err := routerFactory().NewPriorityRouter(url, chain.notify) if r == nil || err != nil { logger.Errorf("router chain build router fail! routerFactories key:%s error:%s", key, err.Error()) continue @@ -115,17 +248,65 @@ func NewRouterChain(url *common.URL) (*RouterChain, error) { sortRouter(newRouters) - chain := &RouterChain{ - builtinRouters: routers, - routers: newRouters, - } + routerNeedsUpdateInit := atomic.Bool{} + routerNeedsUpdateInit.Store(false) + chain.routers = newRouters + chain.builtinRouters = routers if url != nil { - chain.url = *url + chain.url = url } + go chain.loop() return chain, nil } +// poolRouter calls poolable router's Pool() to create new address pool and address metadata if necessary. +// If the corresponding cache entry exists, and the poolable router answers no need to re-pool (possibly because its +// rule doesn't change), and the address list doesn't change, then the existing data will be re-used. +func poolRouter(p router.Poolable, origin *InvokerCache, invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + name := p.Name() + if isCacheMiss(origin, name) || p.ShouldPool() || &(origin.invokers) != &invokers { + logger.Debugf("build address cache for router %q", name) + return p.Pool(invokers) + } + + logger.Debugf("reuse existing address cache for router %q", name) + return origin.pools[name], origin.metadatas[name] +} + +// isCacheMiss checks if the corresponding cache entry for a poolable router has already existed. +// False returns when the cache is nil, or cache's pool is nil, or cache's invokers snapshot is nil, or the entry +// doesn't exist. +func isCacheMiss(cache *InvokerCache, key string) bool { + if cache == nil || cache.pools == nil || cache.invokers == nil || cache.pools[key] == nil { + return true + } + return false +} + +// isInvokersChanged compares new invokers on the right changes, compared with the old invokers on the left. +func isInvokersChanged(left []protocol.Invoker, right []protocol.Invoker) bool { + if len(right) != len(left) { + return true + } + + for _, r := range right { + found := false + rurl := r.GetUrl() + for _, l := range left { + lurl := l.GetUrl() + if common.GetCompareURLEqualFunc()(lurl, rurl, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY) { + found = true + break + } + } + if !found { + return true + } + } + return false +} + // sortRouter Sort router instance by priority with stable algorithm func sortRouter(routers []router.PriorityRouter) { sort.Stable(byPriority(routers)) diff --git a/cluster/router/chain/chain_test.go b/cluster/router/chain/chain_test.go index dec03894ebc73e315c2bb161911bdc67235e1ebb..d38b3be99487c8301d099044361da2fb5af93076 100644 --- a/cluster/router/chain/chain_test.go +++ b/cluster/router/chain/chain_test.go @@ -25,6 +25,8 @@ import ( ) import ( + zk "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) @@ -38,7 +40,6 @@ import ( _ "github.com/apache/dubbo-go/config_center/zookeeper" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" - "github.com/apache/dubbo-go/remoting/zookeeper" ) const ( @@ -53,19 +54,21 @@ const ( consumerFormat = "consumer://%s/com.foo.BarService" dubboForamt = "dubbo://%s:%d/com.foo.BarService" anyUrlFormat = "condition://%s/com.foo.BarService" - zk = "zookeeper" + zkName = "zookeeper" applicationKey = "test-condition" applicationField = "application" forceField = "force" forceValue = "true" ) +var zkCluster *zk.TestCluster + func TestNewRouterChain(t *testing.T) { - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) + zkCluster = ts err = z.Create(path) assert.NoError(t, err) - testyml := `scope: application key: mock-app enabled: true @@ -77,11 +80,13 @@ conditions: _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + z.Delete(path) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(zkName).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) @@ -113,7 +118,7 @@ func TestNewRouterChainURLNil(t *testing.T) { } func TestRouterChainAddRouters(t *testing.T) { - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + _, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, gxzookeeper.WithTestCluster(zkCluster)) assert.NoError(t, err) err = z.Create(path) assert.NoError(t, err) @@ -129,11 +134,14 @@ conditions: _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + z.Delete(path) + z.Close() + }() - zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) + zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, zkCluster.Servers[0].Port)) + configuration, err := extension.GetConfigCenterFactory(zkName).GetDynamicConfiguration(zkUrl) + assert.NoError(t, err) config.GetEnvInstance().SetDynamicConfiguration(configuration) chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) @@ -143,7 +151,12 @@ conditions: url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) factory := extension.GetRouterFactory(url.Protocol) - r, err := factory.NewPriorityRouter(url) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + r, err := factory.NewPriorityRouter(url, notify) assert.Nil(t, err) assert.NotNil(t, r) @@ -154,12 +167,11 @@ conditions: } func TestRouterChainRoute(t *testing.T) { - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) - defer ts.Stop() - defer z.Close() - + ts, _, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, gxzookeeper.WithTestCluster(zkCluster)) + assert.Nil(t, err) zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(zkName).GetDynamicConfiguration(zkUrl) + assert.NoError(t, err) config.GetEnvInstance().SetDynamicConfiguration(configuration) chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) @@ -169,21 +181,21 @@ func TestRouterChainRoute(t *testing.T) { url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) - invokers := []protocol.Invoker{} + var invokers []protocol.Invoker dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) - chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(targetURL, inv) assert.Equal(t, 1, len(finalInvokers)) } func TestRouterChainRouteAppRouter(t *testing.T) { - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, gxzookeeper.WithTestCluster(zkCluster)) assert.NoError(t, err) err = z.Create(path) assert.NoError(t, err) @@ -199,36 +211,46 @@ conditions: _, err = z.Conn.Set(path, []byte(testyml), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + z.Delete(path) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(zkName).GetDynamicConfiguration(zkUrl) + assert.NoError(t, err) config.GetEnvInstance().SetDynamicConfiguration(configuration) chain, err := NewRouterChain(getConditionRouteUrl(applicationKey)) assert.Nil(t, err) assert.Equal(t, 2, len(chain.routers)) - invokers := []protocol.Invoker{} + var invokers []protocol.Invoker dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) + chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(targetURL, inv) assert.Equal(t, 0, len(finalInvokers)) } func TestRouterChainRouteNoRoute(t *testing.T) { - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) - defer ts.Stop() - defer z.Close() + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, gxzookeeper.WithTestCluster(zkCluster)) + assert.Nil(t, err) + defer func() { + _ = ts.Stop() + assert.NoError(t, err) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, localIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(zk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(zkName).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) + assert.Nil(t, err) chain, err := NewRouterChain(getConditionNoRouteUrl(applicationKey)) assert.Nil(t, err) @@ -236,13 +258,16 @@ func TestRouterChainRouteNoRoute(t *testing.T) { url := getConditionRouteUrl(applicationKey) assert.NotNil(t, url) - invokers := []protocol.Invoker{} + + var invokers []protocol.Invoker dubboURL, _ := common.NewURL(fmt.Sprintf(dubboForamt, test1234IP, port20000)) invokers = append(invokers, protocol.NewBaseInvoker(dubboURL)) + chain.SetInvokers(invokers) + chain.buildCache() targetURL, _ := common.NewURL(fmt.Sprintf(consumerFormat, test1111IP)) inv := &invocation.RPCInvocation{} - finalInvokers := chain.Route(invokers, &targetURL, inv) + finalInvokers := chain.Route(targetURL, inv) assert.Equal(t, 0, len(finalInvokers)) } @@ -253,7 +278,7 @@ func getConditionNoRouteUrl(applicationKey string) *common.URL { url.AddParam(forceField, forceValue) rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host != 1.2.3.4")) url.AddParam(constant.RULE_KEY, rule) - return &url + return url } func getConditionRouteUrl(applicationKey string) *common.URL { @@ -262,12 +287,12 @@ func getConditionRouteUrl(applicationKey string) *common.URL { url.AddParam(forceField, forceValue) rule := base64.URLEncoding.EncodeToString([]byte("host = 1.1.1.1 => host = 1.2.3.4")) url.AddParam(constant.RULE_KEY, rule) - return &url + return url } func getRouteUrl(applicationKey string) *common.URL { url, _ := common.NewURL(fmt.Sprintf(anyUrlFormat, test0000IP)) url.AddParam(applicationField, applicationKey) url.AddParam(forceField, forceValue) - return &url + return url } diff --git a/cluster/router/chain/invoker_cache.go b/cluster/router/chain/invoker_cache.go new file mode 100644 index 0000000000000000000000000000000000000000..43cdfa50670f5f09666dbea10a2837d58cafd1b0 --- /dev/null +++ b/cluster/router/chain/invoker_cache.go @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package chain + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" + "github.com/apache/dubbo-go/protocol" +) + +// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received +// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if +// the router implements Poolable. +type InvokerCache struct { + // The snapshot of invokers + invokers []protocol.Invoker + + // The bitmap representation for invokers snapshot + bitmap *roaring.Bitmap + + // Address pool from routers which implement Poolable + pools map[string]router.AddrPool + + // Address metadata from routers which implement Poolable + metadatas map[string]router.AddrMetadata +} + +// BuildCache builds address cache from the given invokers. +func BuildCache(invokers []protocol.Invoker) *InvokerCache { + return &InvokerCache{ + invokers: invokers, + bitmap: utils.ToBitmap(invokers), + pools: make(map[string]router.AddrPool, 8), + metadatas: make(map[string]router.AddrMetadata, 8), + } +} + +// GetInvokers get invokers snapshot. +func (c *InvokerCache) GetInvokers() []protocol.Invoker { + return c.invokers +} + +// FindAddrPool finds address pool for a poolable router. +func (c *InvokerCache) FindAddrPool(p router.Poolable) router.AddrPool { + return c.pools[p.Name()] +} + +// FindAddrMeta finds address metadata for a poolable router. +func (c *InvokerCache) FindAddrMeta(p router.Poolable) router.AddrMetadata { + return c.metadatas[p.Name()] +} + +// SetAddrPool sets address pool for a poolable router, for unit test only +func (c *InvokerCache) SetAddrPool(name string, pool router.AddrPool) { + c.pools[name] = pool +} + +// SetAddrMeta sets address metadata for a poolable router, for unit test only +func (c *InvokerCache) SetAddrMeta(name string, meta router.AddrMetadata) { + c.metadatas[name] = meta +} diff --git a/cluster/router/condition/app_router.go b/cluster/router/condition/app_router.go index 056e32851c11696c80d18a2a55b109fcdae06627..64b3914b87003419c04755b584b06f66d7f05435 100644 --- a/cluster/router/condition/app_router.go +++ b/cluster/router/condition/app_router.go @@ -34,14 +34,15 @@ const ( // AppRouter For listen application router with config center type AppRouter struct { listenableRouter + notify interface{} } // NewAppRouter Init AppRouter by url -func NewAppRouter(url *common.URL) (*AppRouter, error) { +func NewAppRouter(url *common.URL, notify chan struct{}) (*AppRouter, error) { if url == nil { return nil, perrors.Errorf("No route URL for create app router!") } - appRouter, err := newListenableRouter(url, url.GetParam(constant.APPLICATION_KEY, "")) + appRouter, err := newListenableRouter(url, url.GetParam(constant.APPLICATION_KEY, ""), notify) if err != nil { return nil, err } diff --git a/cluster/router/condition/app_router_test.go b/cluster/router/condition/app_router_test.go index cce96b12c95a691e828d91ba3d0629ddb6421954..86fdedea1f4caf40034dd7c879a168d97c4aa648 100644 --- a/cluster/router/condition/app_router_test.go +++ b/cluster/router/condition/app_router_test.go @@ -24,6 +24,7 @@ import ( ) import ( + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) @@ -35,7 +36,6 @@ import ( "github.com/apache/dubbo-go/config_center" _ "github.com/apache/dubbo-go/config_center/zookeeper" "github.com/apache/dubbo-go/remoting" - "github.com/apache/dubbo-go/remoting/zookeeper" ) const ( @@ -60,25 +60,32 @@ runtime: false conditions: - => host != 172.22.3.91 ` - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) err = z.Create(routerPath) assert.NoError(t, err) _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + assert.NoError(t, err) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) appRouteURL := getAppRouteURL(routerKey) - appRouter, err := NewAppRouter(appRouteURL) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + appRouter, err := NewAppRouter(appRouteURL, notify) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -106,27 +113,34 @@ force: true runtime: false conditions: - => host != 172.22.3.91 - - host = 192.168.199.208 => host = 192.168.199.208 + - host = 192.168.199.208 => host = 192.168.199.208 ` - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) err = z.Create(routerPath) assert.NoError(t, err) _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + assert.NoError(t, err) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) appRouteURL := getAppRouteURL(routerKey) - appRouter, err := NewAppRouter(appRouteURL) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + appRouter, err := NewAppRouter(appRouteURL, notify) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -147,25 +161,33 @@ runtime: false conditions: - => host != 172.22.3.91 ` - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) err = z.Create(routerPath) assert.NoError(t, err) _, err = z.Conn.Set(routerPath, []byte(testYML), 0) assert.NoError(t, err) - defer ts.Stop() - defer z.Close() + defer func() { + err = ts.Stop() + assert.NoError(t, err) + z.Close() + }() zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, ts.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) assert.Nil(t, err) assert.NotNil(t, configuration) appRouteURL := getAppRouteURL(routerKey) - appRouter, err := NewAppRouter(appRouteURL) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + appRouter, err := NewAppRouter(appRouteURL, notify) assert.Nil(t, err) assert.NotNil(t, appRouter) @@ -194,5 +216,5 @@ func getAppRouteURL(applicationKey string) *common.URL { url, _ := common.NewURL(fmt.Sprintf(conditionFormat, constant.ANYHOST_VALUE)) url.AddParam("application", applicationKey) url.AddParam("force", "true") - return &url + return url } diff --git a/cluster/router/condition/factory.go b/cluster/router/condition/factory.go index f8d3e130102d4311f8b1ddb1055aece8a0633296..2c1f03516a88be2846a74029a990c67554201f05 100644 --- a/cluster/router/condition/factory.go +++ b/cluster/router/condition/factory.go @@ -37,8 +37,8 @@ func newConditionRouterFactory() router.PriorityRouterFactory { } // NewPriorityRouter creates ConditionRouterFactory by URL -func (c *ConditionRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { - return NewConditionRouter(url) +func (c *ConditionRouterFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewConditionRouter(url, notify) } // NewRouter Create FileRouterFactory by Content @@ -54,6 +54,6 @@ func newAppRouterFactory() router.PriorityRouterFactory { } // NewPriorityRouter creates AppRouterFactory by URL -func (c *AppRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { - return NewAppRouter(url) +func (c *AppRouterFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewAppRouter(url, notify) } diff --git a/cluster/router/condition/factory_test.go b/cluster/router/condition/factory_test.go index 0f61b39fc71af3aaeffc731974a0fa997503693e..e08016d13ebba72f0a36c944082de27d6c95afdb 100644 --- a/cluster/router/condition/factory_test.go +++ b/cluster/router/condition/factory_test.go @@ -26,12 +26,14 @@ import ( ) import ( - "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/logger" @@ -50,13 +52,13 @@ const ( ) type MockInvoker struct { - url common.URL + url *common.URL available bool destroyed bool successCount int } -func NewMockInvoker(url common.URL, successCount int) *MockInvoker { +func NewMockInvoker(url *common.URL, successCount int) *MockInvoker { return &MockInvoker{ url: url, available: true, @@ -65,7 +67,7 @@ func NewMockInvoker(url common.URL, successCount int) *MockInvoker { } } -func (bi *MockInvoker) GetUrl() common.URL { +func (bi *MockInvoker) GetUrl() *common.URL { return bi.url } @@ -73,20 +75,20 @@ func getRouteUrl(rule string) *common.URL { url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) url.AddParam("force", "true") - return &url + return url } func getRouteUrlWithForce(rule, force string) *common.URL { url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) url.AddParam("force", force) - return &url + return url } func getRouteUrlWithNoForce(rule string) *common.URL { url, _ := common.NewURL(fmt.Sprintf(factoryUrlFormat, constant.ANYHOST_VALUE)) url.AddParam("rule", rule) - return &url + return url } func (bi *MockInvoker) IsAvailable() bool { @@ -130,38 +132,48 @@ func (bi *MockInvoker) Destroy() { func TestRoute_matchWhen(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("=> host = 1.2.3.4")) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) cUrl, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, factory1111Ip)) - matchWhen := router.(*ConditionRouter).MatchWhen(&cUrl, inv) + matchWhen := router.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen) rule1 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1)) - matchWhen1 := router1.(*ConditionRouter).MatchWhen(&cUrl, inv) + router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1), notify) + matchWhen1 := router1.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen1) rule2 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4")) - router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) - matchWhen2 := router2.(*ConditionRouter).MatchWhen(&cUrl, inv) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2), notify) + matchWhen2 := router2.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, false, matchWhen2) rule3 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.4 & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) - matchWhen3 := router3.(*ConditionRouter).MatchWhen(&cUrl, inv) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3), notify) + matchWhen3 := router3.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen3) rule4 := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4)) - matchWhen4 := router4.(*ConditionRouter).MatchWhen(&cUrl, inv) + router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4), notify) + matchWhen4 := router4.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen4) rule5 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.1 => host = 1.2.3.4")) - router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5)) - matchWhen5 := router5.(*ConditionRouter).MatchWhen(&cUrl, inv) + router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5), notify) + matchWhen5 := router5.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, false, matchWhen5) rule6 := base64.URLEncoding.EncodeToString([]byte("host = 2.2.2.2,1.1.1.*,3.3.3.3 & host != 1.1.1.2 => host = 1.2.3.4")) - router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6)) - matchWhen6 := router6.(*ConditionRouter).MatchWhen(&cUrl, inv) + router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6), notify) + matchWhen6 := router6.(*ConditionRouter).MatchWhen(cUrl, inv) assert.Equal(t, true, matchWhen6) } func TestRoute_matchFilter(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() t.Logf("The local ip is %s", localIP) url1, _ := common.NewURL("dubbo://10.20.3.3:20880/com.foo.BarService?default.serialization=fastjson") url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -173,77 +185,97 @@ func TestRoute_matchFilter(t *testing.T) { rule4 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = 10.20.3.2,10.20.3.3,10.20.3.4")) rule5 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host != 10.20.3.3")) rule6 := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " serialization = fastjson")) - router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1)) - router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) - router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) - router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4)) - router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5)) - router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6)) + router1, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule1), notify) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2), notify) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3), notify) + router4, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule4), notify) + router5, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule5), notify) + router6, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule6), notify) cUrl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - fileredInvokers1 := router1.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers2 := router2.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers3 := router3.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers4 := router4.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers5 := router5.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - fileredInvokers6 := router6.Route(invokers, &cUrl, &invocation.RPCInvocation{}) - assert.Equal(t, 1, len(fileredInvokers1)) - assert.Equal(t, 0, len(fileredInvokers2)) - assert.Equal(t, 0, len(fileredInvokers3)) - assert.Equal(t, 1, len(fileredInvokers4)) - assert.Equal(t, 2, len(fileredInvokers5)) - assert.Equal(t, 1, len(fileredInvokers6)) + ret1 := router1.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + ret2 := router2.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + ret3 := router3.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + ret4 := router4.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + ret5 := router5.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + ret6 := router6.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), cUrl, &invocation.RPCInvocation{}) + assert.Equal(t, 1, len(ret1.ToArray())) + assert.Equal(t, 0, len(ret2.ToArray())) + assert.Equal(t, 0, len(ret3.ToArray())) + assert.Equal(t, 1, len(ret4.ToArray())) + assert.Equal(t, 2, len(ret5.ToArray())) + assert.Equal(t, 1, len(ret6.ToArray())) } func TestRoute_methodRoute(t *testing.T) { + notify := make(chan struct{}) + go func() { + for range notify { + } + }() inv := invocation.NewRPCInvocationWithOptions(invocation.WithMethodName("getFoo"), invocation.WithParameterTypes([]reflect.Type{}), invocation.WithArguments([]interface{}{})) rule := base64.URLEncoding.EncodeToString([]byte("host !=4.4.4.* & host = 2.2.2.2,1.1.1.1,3.3.3.3 => host = 1.2.3.4")) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) url, _ := common.NewURL("consumer://1.1.1.1/com.foo.BarService?methods=setFoo,getFoo,findFoo") - matchWhen := router.(*ConditionRouter).MatchWhen(&url, inv) + matchWhen := r.(*ConditionRouter).MatchWhen(url, inv) assert.Equal(t, true, matchWhen) url1, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) - matchWhen = router.(*ConditionRouter).MatchWhen(&url1, inv) + matchWhen = r.(*ConditionRouter).MatchWhen(url1, inv) assert.Equal(t, true, matchWhen) url2, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) rule2 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host!=1.1.1.1 => host = 1.2.3.4")) - router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2)) - matchWhen = router2.(*ConditionRouter).MatchWhen(&url2, inv) + router2, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule2), notify) + matchWhen = router2.(*ConditionRouter).MatchWhen(url2, inv) assert.Equal(t, false, matchWhen) url3, _ := common.NewURL(fmt.Sprintf(factoryConsumerMethodFormat, factory1111Ip)) rule3 := base64.URLEncoding.EncodeToString([]byte("methods=getFoo & host=1.1.1.1 => host = 1.2.3.4")) - router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3)) - matchWhen = router3.(*ConditionRouter).MatchWhen(&url3, inv) + router3, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule3), notify) + matchWhen = router3.(*ConditionRouter).MatchWhen(url3, inv) assert.Equal(t, true, matchWhen) } func TestRoute_ReturnFalse(t *testing.T) { + notify := make(chan struct{}) + go func() { + for range notify { + } + }() url, _ := common.NewURL("") - localIP, _ := gxnet.GetLocalIP() + localIP := common.GetLocalIp() invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => false")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 0, len(ret.ToArray())) } func TestRoute_ReturnEmpty(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url, _ := common.NewURL("") invokers := []protocol.Invoker{NewMockInvoker(url, 1), NewMockInvoker(url, 2), NewMockInvoker(url, 3)} inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => ")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 0, len(ret.ToArray())) } func TestRoute_ReturnAll(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() urlString := "dubbo://" + localIP + "/com.foo.BarService" dubboURL, _ := common.NewURL(urlString) mockInvoker1 := NewMockInvoker(dubboURL, 1) @@ -253,16 +285,21 @@ func TestRoute_ReturnAll(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, invokers, fileredInvokers) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, len(invokers), len(ret.ToArray())) } func TestRoute_HostFilter(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() invoker1 := NewMockInvoker(url1, 1) invoker2 := NewMockInvoker(url2, 2) invoker3 := NewMockInvoker(url3, 3) @@ -270,15 +307,20 @@ func TestRoute_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_Empty_HostFilter(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -289,15 +331,20 @@ func TestRoute_Empty_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(" => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_False_HostFilter(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -308,15 +355,20 @@ func TestRoute_False_HostFilter(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("true => " + " host = " + localIP)) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_Placeholder(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -327,15 +379,20 @@ func TestRoute_Placeholder(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte("host = " + localIP + " => " + " host = $host")) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 2, len(fileredInvokers)) - assert.Equal(t, invoker2, fileredInvokers[0]) - assert.Equal(t, invoker3, fileredInvokers[1]) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrl(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 2, len(ret.ToArray())) + assert.Equal(t, invoker2, invokers[ret.ToArray()[0]]) + assert.Equal(t, invoker3, invokers[ret.ToArray()[1]]) } func TestRoute_NoForce(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -346,13 +403,18 @@ func TestRoute_NoForce(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule)) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, invokers, fileredInvokers) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithNoForce(rule), notify) + ret := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, len(invokers), len(ret.ToArray())) } func TestRoute_Force(t *testing.T) { - localIP, _ := gxnet.GetLocalIP() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + localIP := common.GetLocalIp() url1, _ := common.NewURL(factory333URL) url2, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) url3, _ := common.NewURL(fmt.Sprintf(factoryDubboFormat, localIP)) @@ -363,9 +425,9 @@ func TestRoute_Force(t *testing.T) { inv := &invocation.RPCInvocation{} rule := base64.URLEncoding.EncodeToString([]byte(fmt.Sprintf(factoryHostIp1234Format, localIP))) curl, _ := common.NewURL(fmt.Sprintf(factoryConsumerFormat, localIP)) - router, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true")) - fileredInvokers := router.(*ConditionRouter).Route(invokers, &curl, inv) - assert.Equal(t, 0, len(fileredInvokers)) + r, _ := newConditionRouterFactory().NewPriorityRouter(getRouteUrlWithForce(rule, "true"), notify) + fileredInvokers := r.Route(utils.ToBitmap(invokers), setUpAddrCache(invokers), curl, inv) + assert.Equal(t, 0, len(fileredInvokers.ToArray())) } func TestNewConditionRouterFactory(t *testing.T) { @@ -377,3 +439,7 @@ func TestNewAppRouterFactory(t *testing.T) { factory := newAppRouterFactory() assert.NotNil(t, factory) } + +func setUpAddrCache(addrs []protocol.Invoker) router.Cache { + return chain.BuildCache(addrs) +} diff --git a/cluster/router/condition/file.go b/cluster/router/condition/file.go index 996db7443faff88d3baa1a2363f98fa62623d121..a97d9cb83b9c176a353a3851c5590df91eb22730 100644 --- a/cluster/router/condition/file.go +++ b/cluster/router/condition/file.go @@ -39,7 +39,7 @@ import ( type FileConditionRouter struct { listenableRouter parseOnce sync.Once - url common.URL + url *common.URL } // NewFileConditionRouter Create file condition router instance with content ( from config file) @@ -60,11 +60,11 @@ func NewFileConditionRouter(content []byte) (*FileConditionRouter, error) { } // URL Return URL in file condition router n -func (f *FileConditionRouter) URL() common.URL { +func (f *FileConditionRouter) URL() *common.URL { f.parseOnce.Do(func() { routerRule := f.routerRule rule := parseCondition(routerRule.Conditions) - f.url = *common.NewURLWithOptions( + f.url = common.NewURLWithOptions( common.WithProtocol(constant.CONDITION_ROUTE_PROTOCOL), common.WithIp(constant.ANYHOST_VALUE), common.WithParams(url.Values{}), diff --git a/cluster/router/condition/file_test.go b/cluster/router/condition/file_test.go index bd19a0d18c6692af181ffef77c5cd3f9fc16d67d..9035e9559223e30cc2a56873b5aa0e4432c0a358 100644 --- a/cluster/router/condition/file_test.go +++ b/cluster/router/condition/file_test.go @@ -89,33 +89,41 @@ func TestParseServiceRouterKey(t *testing.T) { assert.Equal(t, "mock-group", grp) assert.Equal(t, "mock-service", srv) assert.Equal(t, "1.0.0", ver) + assert.Nil(t, err) testString = "mock-group/mock-service" grp, srv, ver, err = parseServiceRouterKey(testString) assert.Equal(t, "mock-group", grp) assert.Equal(t, "mock-service", srv) assert.Equal(t, "", ver) + assert.Nil(t, err) testString = "mock-service:1.0.0" grp, srv, ver, err = parseServiceRouterKey(testString) assert.Equal(t, "", grp) assert.Equal(t, "mock-service", srv) assert.Equal(t, "1.0.0", ver) + assert.Nil(t, err) testString = "mock-service" grp, srv, ver, err = parseServiceRouterKey(testString) assert.Equal(t, "", grp) assert.Equal(t, "mock-service", srv) assert.Equal(t, "", ver) + assert.NoError(t, err) testString = "/mock-service:" grp, srv, ver, err = parseServiceRouterKey(testString) assert.Equal(t, "", grp) assert.Equal(t, "mock-service", srv) assert.Equal(t, "", ver) + assert.NoError(t, err) testString = "grp:mock-service:123" grp, srv, ver, err = parseServiceRouterKey(testString) + assert.Equal(t, "", grp) + assert.Equal(t, "", srv) + assert.Equal(t, "", ver) assert.Error(t, err) testString = "" @@ -123,4 +131,5 @@ func TestParseServiceRouterKey(t *testing.T) { assert.Equal(t, "", grp) assert.Equal(t, "", srv) assert.Equal(t, "", ver) + assert.NoError(t, err) } diff --git a/cluster/router/condition/listenable_router.go b/cluster/router/condition/listenable_router.go index 7f4f14a8e47173253e2e5b7f4eed5db2bed64958..2e55b2075d28af7ef3bb00faec5b6cbae1a78840 100644 --- a/cluster/router/condition/listenable_router.go +++ b/cluster/router/condition/listenable_router.go @@ -22,10 +22,12 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/constant" @@ -47,6 +49,7 @@ type listenableRouter struct { url *common.URL force bool priority int64 + notify chan struct{} } // RouterRule Get RouterRule instance from listenableRouter @@ -54,7 +57,7 @@ func (l *listenableRouter) RouterRule() *RouterRule { return l.routerRule } -func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) { +func newListenableRouter(url *common.URL, ruleKey string, notify chan struct{}) (*AppRouter, error) { if ruleKey == "" { return nil, perrors.Errorf("NewListenableRouter ruleKey is nil, can't create Listenable router") } @@ -62,16 +65,17 @@ func newListenableRouter(url *common.URL, ruleKey string) (*AppRouter, error) { l.url = url l.priority = listenableRouterDefaultPriority + l.notify = notify routerKey := ruleKey + constant.ConditionRouterRuleSuffix - //add listener + // add listener dynamicConfiguration := config.GetEnvInstance().GetDynamicConfiguration() if dynamicConfiguration == nil { return nil, perrors.Errorf("Get dynamicConfiguration fail, dynamicConfiguration is nil, init config center plugin please") } dynamicConfiguration.AddListener(routerKey, l) - //get rule + // get rule rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP)) if len(rule) == 0 || err != nil { return nil, perrors.Errorf("Get rule fail, config rule{%s}, error{%v}", rule, err) @@ -108,6 +112,9 @@ func (l *listenableRouter) Process(event *config_center.ConfigChangeEvent) { return } l.generateConditions(routerRule) + go func() { + l.notify <- struct{}{} + }() } func (l *listenableRouter) generateConditions(rule *RouterRule) { @@ -129,13 +136,13 @@ func (l *listenableRouter) generateConditions(rule *RouterRule) { } // Route Determine the target invokers list. -func (l *listenableRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if len(invokers) == 0 || len(l.conditionRouters) == 0 { +func (l *listenableRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if invokers.IsEmpty() || len(l.conditionRouters) == 0 { return invokers } - //We will check enabled status inside each router. + // We will check enabled status inside each router. for _, r := range l.conditionRouters { - invokers = r.Route(invokers, url, invocation) + invokers = r.Route(invokers, cache, url, invocation) } return invokers } @@ -146,6 +153,6 @@ func (l *listenableRouter) Priority() int64 { } // URL Return URL in listenable router -func (l *listenableRouter) URL() common.URL { - return *l.url +func (l *listenableRouter) URL() *common.URL { + return l.url } diff --git a/cluster/router/condition/router.go b/cluster/router/condition/router.go index 751b5a7111655577566c561614d39093485130cd..d543ca3f94644370179c18a9c3a3f9a00268461e 100644 --- a/cluster/router/condition/router.go +++ b/cluster/router/condition/router.go @@ -23,20 +23,22 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" + "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "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/dubbogo/gost/container/set" - "github.com/dubbogo/gost/net" ) const ( - //pattern route pattern regex + // pattern route pattern regex pattern = `([&!=,]*)\\s*([^&!=,\\s]+)` ) @@ -44,6 +46,13 @@ var ( routerPatternReg = regexp.MustCompile(`([&!=,]*)\s*([^&!=,\s]+)`) ) +var ( + emptyMatchPair = MatchPair{ + Matches: gxset.NewSet(), + Mismatches: gxset.NewSet(), + } +) + // ConditionRouter Condition router struct type ConditionRouter struct { Pattern string @@ -53,6 +62,7 @@ type ConditionRouter struct { enabled bool WhenCondition map[string]MatchPair ThenCondition map[string]MatchPair + notify chan struct{} } // NewConditionRouterWithRule Init condition router by raw rule @@ -102,7 +112,7 @@ func NewConditionRouterWithRule(rule string) (*ConditionRouter, error) { } // NewConditionRouter Init condition router by URL -func NewConditionRouter(url *common.URL) (*ConditionRouter, error) { +func NewConditionRouter(url *common.URL, notify chan struct{}) (*ConditionRouter, error) { if url == nil { return nil, perrors.Errorf("Illegal route URL!") } @@ -126,6 +136,7 @@ func NewConditionRouter(url *common.URL) (*ConditionRouter, error) { router.priority = url.GetParamInt(constant.RouterPriority, defaultPriority) router.Force = url.GetParamBool(constant.RouterForce, false) router.enabled = url.GetParamBool(constant.RouterEnabled, true) + router.notify = notify return router, nil } @@ -136,8 +147,8 @@ func (c *ConditionRouter) Priority() int64 { } // URL Return URL in condition router -func (c *ConditionRouter) URL() common.URL { - return *c.url +func (c *ConditionRouter) URL() *common.URL { + return c.url } // Enabled Return is condition router is enabled @@ -148,36 +159,44 @@ func (c *ConditionRouter) Enabled() bool { } // Route Determine the target invokers list. -func (c *ConditionRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { +func (c *ConditionRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { if !c.Enabled() { return invokers } - if len(invokers) == 0 { + + if invokers.IsEmpty() { return invokers } + isMatchWhen := c.MatchWhen(url, invocation) if !isMatchWhen { return invokers } - var result []protocol.Invoker + if len(c.ThenCondition) == 0 { - return result + return utils.EmptyAddr } - for _, invoker := range invokers { + + result := roaring.NewBitmap() + for iter := invokers.Iterator(); iter.HasNext(); { + index := iter.Next() + invoker := cache.GetInvokers()[index] invokerUrl := invoker.GetUrl() - isMatchThen := c.MatchThen(&invokerUrl, url) + isMatchThen := c.MatchThen(invokerUrl, url) if isMatchThen { - result = append(result, invoker) + result.Add(index) } } - if len(result) > 0 { + + if !result.IsEmpty() { return result } else if c.Force { rule, _ := url.GetParamAndDecoded(constant.RULE_KEY) - localIP, _ := gxnet.GetLocalIP() + localIP := common.GetLocalIp() logger.Warnf("The route result is empty and force execute. consumer: %s, service: %s, router: %s", localIP, url.Service(), rule) return result } + return invokers } @@ -211,14 +230,14 @@ func parseRule(rule string) (map[string]MatchPair, error) { condition[content] = pair } case "=": - if &pair == nil { + if pair == emptyMatchPair { var startIndex = getStartIndex(rule) return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex) } values = pair.Matches values.Add(content) case "!=": - if &pair == nil { + if pair == emptyMatchPair { var startIndex = getStartIndex(rule) return nil, perrors.Errorf("Illegal route rule \"%s\", The error char '%s' at index %d before \"%d\".", rule, separator, startIndex, startIndex) } diff --git a/cluster/router/condition/router_rule_test.go b/cluster/router/condition/router_rule_test.go index 369b14f08a6a650b36fe63a2d890c04fe7b892a5..192e5282f6f50c31b1106a5fc547a1616405a22a 100644 --- a/cluster/router/condition/router_rule_test.go +++ b/cluster/router/condition/router_rule_test.go @@ -80,5 +80,5 @@ conditions: func TestIsMatchGlobPattern(t *testing.T) { url, _ := common.NewURL("dubbo://localhost:8080/Foo?key=v*e") - assert.Equal(t, true, isMatchGlobalPattern("$key", "value", &url)) + assert.Equal(t, true, isMatchGlobalPattern("$key", "value", url)) } diff --git a/cluster/router/condition/router_test.go b/cluster/router/condition/router_test.go index c27a1d9552a331d7e67af0eb3d444c480758891e..2895703dbc4672a300468f351d667198f52b5bf7 100644 --- a/cluster/router/condition/router_test.go +++ b/cluster/router/condition/router_test.go @@ -58,8 +58,13 @@ func TestParseRule(t *testing.T) { } func TestNewConditionRouter(t *testing.T) { + notify := make(chan struct{}) + go func() { + for range notify { + } + }() url, _ := common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) - router, err := NewConditionRouter(&url) + router, err := NewConditionRouter(url, notify) assert.Nil(t, err) assert.Equal(t, true, router.Enabled()) assert.Equal(t, true, router.Force) @@ -69,20 +74,22 @@ func TestNewConditionRouter(t *testing.T) { assert.EqualValues(t, router.WhenCondition, whenRule) assert.EqualValues(t, router.ThenCondition, thenRule) - router, err = NewConditionRouter(nil) + router, err = NewConditionRouter(nil, notify) + assert.Nil(t, router) assert.Error(t, err) url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&priority=1&router=condition&rule=YSAmT4gYiAmIGQ%3D`) - router, err = NewConditionRouter(&url) + router, err = NewConditionRouter(url, notify) + assert.Nil(t, router) assert.Error(t, err) url, _ = common.NewURL(`condition://0.0.0.0:?application=mock-app&category=routers&force=true&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) - router, err = NewConditionRouter(&url) + router, err = NewConditionRouter(url, notify) assert.Nil(t, err) assert.Equal(t, int64(150), router.Priority()) url, _ = common.NewURL(`condition://0.0.0.0:?category=routers&force=true&interface=mock-service&router=condition&rule=YSAmIGMgPT4gYiAmIGQ%3D`) - router, err = NewConditionRouter(&url) + router, err = NewConditionRouter(url, notify) assert.Nil(t, err) assert.Equal(t, int64(140), router.Priority()) } diff --git a/cluster/router/conn_checker.go b/cluster/router/conn_checker.go new file mode 100644 index 0000000000000000000000000000000000000000..fda0ef868b32dfcd82980394cec0bce99615ecbb --- /dev/null +++ b/cluster/router/conn_checker.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 router + +import ( + "github.com/apache/dubbo-go/protocol" +) + +// ConnChecker is used to determine whether the invoker is healthy or not +type ConnChecker interface { + // IsConnHealthy evaluates the healthy state on the given Invoker + IsConnHealthy(invoker protocol.Invoker) bool +} diff --git a/cluster/router/conncheck/conn_check_route.go b/cluster/router/conncheck/conn_check_route.go new file mode 100644 index 0000000000000000000000000000000000000000..97f049d85bb0369982e0a84f27a4c6b3b8925a55 --- /dev/null +++ b/cluster/router/conncheck/conn_check_route.go @@ -0,0 +1,105 @@ +/* + * 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 conncheck + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" + "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/protocol" +) + +const ( + connHealthy = "conn-healthy" + name = "conn-check-router" +) + +// ConnCheckRouter provides a health-first routing mechanism through ConnChecker +type ConnCheckRouter struct { + url *common.URL + checker router.ConnChecker + notify chan struct{} +} + +// NewConnCheckRouter construct an NewConnCheckRouter via url +func NewConnCheckRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + r := &ConnCheckRouter{ + url: url, + notify: notify, + } + checkerName := url.GetParam(constant.HEALTH_CHECKER, constant.DEFAULT_HEALTH_CHECKER) + r.checker = extension.GetConnChecker(checkerName, url) + return r, nil +} + +// Route gets a list of healthy invoker +func (r *ConnCheckRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + addrPool := cache.FindAddrPool(r) + // Add healthy invoker to the list + healthyInvokers := utils.JoinIfNotEqual(addrPool[connHealthy], invokers) + // If all invokers are considered unhealthy, downgrade to all invoker + if healthyInvokers.IsEmpty() { + logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey()) + return invokers + } + return healthyInvokers +} + +// Pool separates healthy invokers from others. +func (r *ConnCheckRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + rb := make(router.AddrPool, 8) + rb[connHealthy] = roaring.NewBitmap() + for i, invoker := range invokers { + if r.checker.IsConnHealthy(invoker) { + rb[connHealthy].Add(uint32(i)) + } + } + return rb, nil +} + +// ShouldPool will always return true to make sure healthy check constantly. +func (r *ConnCheckRouter) ShouldPool() bool { + return true +} + +// Name get name of ConnCheckerRouter +func (r *ConnCheckRouter) Name() string { + return name +} + +// Priority get Router priority level +func (r *ConnCheckRouter) Priority() int64 { + return 0 +} + +// URL Return URL in router +func (r *ConnCheckRouter) URL() *common.URL { + return r.url +} + +// ConnChecker returns the HealthChecker bound to this HealthCheckRouter +func (r *ConnCheckRouter) ConnChecker() router.ConnChecker { + return r.checker +} diff --git a/cluster/router/conncheck/conn_check_route_test.go b/cluster/router/conncheck/conn_check_route_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fec733167f38c2c642e6a35795fcc57289d242ca --- /dev/null +++ b/cluster/router/conncheck/conn_check_route_test.go @@ -0,0 +1,111 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package conncheck + +import ( + "fmt" + "testing" +) + +import ( + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" + "github.com/apache/dubbo-go/protocol/mock" +) + +const ( + connCheckRoute1010IP = "192.168.10.10" + connCheckRoute1011IP = "192.168.10.11" + connCheckRoute1012IP = "192.168.10.12" + connCheckRouteMethodNameTest = "test" + connCheck1001URL = "dubbo://192.168.10.1/com.ikurento.user.UserProvider" + connCheckRouteUrlFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider" +) + +func TestConnCheckRouterRoute(t *testing.T) { + defer protocol.CleanAllStatus() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + consumerURL, _ := common.NewURL(connCheck1001URL) + url1, _ := common.NewURL(fmt.Sprintf(connCheckRouteUrlFormat, connCheckRoute1010IP)) + url2, _ := common.NewURL(fmt.Sprintf(connCheckRouteUrlFormat, connCheckRoute1011IP)) + url3, _ := common.NewURL(fmt.Sprintf(connCheckRouteUrlFormat, connCheckRoute1012IP)) + hcr, _ := NewConnCheckRouter(consumerURL, notify) + + var invokers []protocol.Invoker + invoker1 := NewMockInvoker(url1) + invoker2 := NewMockInvoker(url2) + invoker3 := NewMockInvoker(url3) + protocol.SetInvokerUnhealthyStatus(invoker1) + protocol.SetInvokerUnhealthyStatus(invoker2) + + invokers = append(invokers, invoker1, invoker2, invoker3) + inv := invocation.NewRPCInvocation(connCheckRouteMethodNameTest, nil, nil) + res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*ConnCheckRouter), invokers), consumerURL, inv) + + // now invoker3 is healthy + assert.True(t, len(res.ToArray()) == 1) + + // check blacklist remove + protocol.RemoveInvokerUnhealthyStatus(invoker1) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*ConnCheckRouter), invokers), consumerURL, inv) + // now invoker3 invoker1 is healthy + assert.True(t, len(res.ToArray()) == 2) + +} + +func TestRecovery(t *testing.T) { + // check recover + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + invoker1 := mock.NewMockInvoker(ctrl) + invoker2 := mock.NewMockInvoker(ctrl) + + invoker1.EXPECT().GetUrl().Return(&common.URL{Path: "path1"}).AnyTimes() + invoker2.EXPECT().GetUrl().Return(&common.URL{Path: "path2"}).AnyTimes() + invoker1.EXPECT().IsAvailable().Return(true).AnyTimes() + invoker2.EXPECT().IsAvailable().Return(true).AnyTimes() + + protocol.SetInvokerUnhealthyStatus(invoker1) + protocol.SetInvokerUnhealthyStatus(invoker2) + assert.Equal(t, len(protocol.GetBlackListInvokers(16)), 2) + protocol.TryRefreshBlackList() + assert.Equal(t, len(protocol.GetBlackListInvokers(16)), 0) +} + +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrMeta(r.Name(), info) + cache.SetAddrPool(r.Name(), pool) + return cache +} diff --git a/cluster/router/conncheck/conn_health_check.go b/cluster/router/conncheck/conn_health_check.go new file mode 100644 index 0000000000000000000000000000000000000000..9f05b0695f2b7a569ab669baac7540ca7c896861 --- /dev/null +++ b/cluster/router/conncheck/conn_health_check.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 conncheck + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/protocol" +) + +func init() { + extension.SetConnChecker(constant.DEFAULT_CONN_CHECKER, NewDefaultConnChecker) +} + +// DefaultConnChecker is the default implementation of ConnChecker, which determines the health status of invoker conn +type DefaultConnChecker struct { +} + +// IsConnHealthy evaluates the healthy state on the given Invoker based on the number of successive bad request +// and the current active request +func (c *DefaultConnChecker) IsConnHealthy(invoker protocol.Invoker) bool { + return protocol.GetInvokerHealthyStatus(invoker) +} + +// NewDefaultConnChecker constructs a new DefaultConnChecker based on the url +func NewDefaultConnChecker(url *common.URL) router.ConnChecker { + return &DefaultConnChecker{} +} diff --git a/cluster/router/conncheck/conn_health_check_test.go b/cluster/router/conncheck/conn_health_check_test.go new file mode 100644 index 0000000000000000000000000000000000000000..44d148e1b8c494cc48a04ba56f2175df8ceec19e --- /dev/null +++ b/cluster/router/conncheck/conn_health_check_test.go @@ -0,0 +1,52 @@ +/* + * 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 conncheck + +import ( + "fmt" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +const ( + connCheckDubbo1010IP = "192.168.10.10" + connCheckDubboUrlFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider" +) + +func TestDefaultConnCheckerIsHealthy(t *testing.T) { + defer protocol.CleanAllStatus() + url, _ := common.NewURL(fmt.Sprintf(connCheckDubboUrlFormat, connCheckDubbo1010IP)) + cc := NewDefaultConnChecker(url).(*DefaultConnChecker) + invoker := NewMockInvoker(url) + healthy := cc.IsConnHealthy(invoker) + assert.True(t, healthy) + + invoker = NewMockInvoker(url) + cc = NewDefaultConnChecker(url).(*DefaultConnChecker) + // add to black list + protocol.SetInvokerUnhealthyStatus(invoker) + assert.False(t, cc.IsConnHealthy(invoker)) +} diff --git a/cluster/router/conncheck/factory.go b/cluster/router/conncheck/factory.go new file mode 100644 index 0000000000000000000000000000000000000000..a7b19aaea668b31006f8caa384067a3051ddb5ce --- /dev/null +++ b/cluster/router/conncheck/factory.go @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package conncheck + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" +) + +func init() { + extension.SetRouterFactory(constant.ConnCheckRouterName, newConnCheckRouteFactory) +} + +// ConnCheckRouteFactory is the factory to create conn check router, it aims at filter ip with unhealthy status +// the unhealthy status is storied in protocol/rpc_status.go with sync.Map +type ConnCheckRouteFactory struct { +} + +// newConnCheckRouteFactory construct a new ConnCheckRouteFactory +func newConnCheckRouteFactory() router.PriorityRouterFactory { + return &ConnCheckRouteFactory{} +} + +// NewPriorityRouter construct a new NewConnCheckRouter via url +func (f *ConnCheckRouteFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewConnCheckRouter(url, notify) +} diff --git a/cluster/router/conncheck/factory_test.go b/cluster/router/conncheck/factory_test.go new file mode 100644 index 0000000000000000000000000000000000000000..02f8fb472ed808b7db4d7903f94ac96064aedbb8 --- /dev/null +++ b/cluster/router/conncheck/factory_test.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package conncheck + +import ( + "context" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +// nolint +type MockInvoker struct { + url *common.URL +} + +// nolint +func NewMockInvoker(url *common.URL) *MockInvoker { + return &MockInvoker{ + url: url, + } +} + +// nolint +func (bi *MockInvoker) GetUrl() *common.URL { + return bi.url +} + +// nolint +func (bi *MockInvoker) IsAvailable() bool { + return true +} + +// nolint +func (bi *MockInvoker) IsDestroyed() bool { + return true +} + +// nolint +func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result { + return nil +} + +// nolint +func (bi *MockInvoker) Destroy() { +} + +// nolint +func TestHealthCheckRouteFactory(t *testing.T) { + factory := newConnCheckRouteFactory() + assert.NotNil(t, factory) +} diff --git a/cluster/router/healthcheck/default_health_check.go b/cluster/router/healthcheck/default_health_check.go index c522bdf0f98244f5f8bdfba6085cb45dd89c1004..eb15e6f6422d1bdc0ae4d0f4a76db50161db1870 100644 --- a/cluster/router/healthcheck/default_health_check.go +++ b/cluster/router/healthcheck/default_health_check.go @@ -110,8 +110,8 @@ func (c *DefaultHealthChecker) GetOutStandingRequestCountLimit() int32 { // NewDefaultHealthChecker constructs a new DefaultHealthChecker based on the url func NewDefaultHealthChecker(url *common.URL) router.HealthChecker { return &DefaultHealthChecker{ - outStandingRequestConutLimit: int32(url.GetParamInt(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, math.MaxInt32)), - requestSuccessiveFailureThreshold: int32(url.GetParamInt(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF)), - circuitTrippedTimeoutFactor: int32(url.GetParamInt(constant.CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY, constant.DEFAULT_CIRCUIT_TRIPPED_TIMEOUT_FACTOR)), + outStandingRequestConutLimit: url.GetParamInt32(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, math.MaxInt32), + requestSuccessiveFailureThreshold: url.GetParamInt32(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF), + circuitTrippedTimeoutFactor: url.GetParamInt32(constant.CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY, constant.DEFAULT_CIRCUIT_TRIPPED_TIMEOUT_FACTOR), } } diff --git a/cluster/router/healthcheck/default_health_check_test.go b/cluster/router/healthcheck/default_health_check_test.go index 5d35ae8e486e3f7b29b2a68a3864ef806a1053c7..c32a607889fb096fee07e1c391f5f3461aacfbc5 100644 --- a/cluster/router/healthcheck/default_health_check_test.go +++ b/cluster/router/healthcheck/default_health_check_test.go @@ -43,7 +43,7 @@ const ( func TestDefaultHealthCheckerIsHealthy(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - hc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + hc := NewDefaultHealthChecker(url).(*DefaultHealthChecker) invoker := NewMockInvoker(url) healthy := hc.IsHealthy(invoker) assert.True(t, healthy) @@ -54,8 +54,9 @@ func TestDefaultHealthCheckerIsHealthy(t *testing.T) { for i := 0; i < 11; i++ { request(url, healthCheckMethodTest, 0, true, false) } - hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + hc = NewDefaultHealthChecker(url).(*DefaultHealthChecker) healthy = hc.IsHealthy(invoker) + assert.False(t, false, healthy) // the outgoing request is more than OUTSTANDING_REQUEST_COUNT_LIMIT, go to unhealthy assert.False(t, hc.IsHealthy(invoker)) @@ -65,20 +66,22 @@ func TestDefaultHealthCheckerIsHealthy(t *testing.T) { } url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10") url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000") - hc = NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + hc = NewDefaultHealthChecker(url).(*DefaultHealthChecker) healthy = hc.IsHealthy(invoker) + assert.False(t, false, healthy) assert.False(t, hc.IsHealthy(invoker)) // reset successive failed count and go to healthy request(url, healthCheckMethodTest, 0, false, true) healthy = hc.IsHealthy(invoker) + assert.False(t, false, healthy) assert.True(t, hc.IsHealthy(invoker)) } func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker) // Increase the number of failed requests for i := 0; i < 100; i++ { request(url, healthCheckMethodTest, 1, false, false) @@ -88,7 +91,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) { // Adjust the threshold size to 1000 url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "1000") - sleepWindowTime = NewDefaultHealthChecker(&url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url)) + sleepWindowTime = NewDefaultHealthChecker(url).(*DefaultHealthChecker).getCircuitBreakerSleepWindowTime(protocol.GetURLStatus(url)) assert.True(t, sleepWindowTime == 0) url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP)) @@ -107,7 +110,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerSleepWindowTime(t *testing.T) { func TestDefaultHealthCheckerGetCircuitBreakerTimeout(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker) timeout := defaultHc.getCircuitBreakerTimeout(protocol.GetURLStatus(url)) assert.True(t, timeout == 0) url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1011IP)) @@ -126,7 +129,7 @@ func TestDefaultHealthCheckerGetCircuitBreakerTimeout(t *testing.T) { func TestDefaultHealthCheckerIsCircuitBreakerTripped(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker) status := protocol.GetURLStatus(url) tripped := defaultHc.isCircuitBreakerTripped(status) assert.False(t, tripped) @@ -142,7 +145,7 @@ func TestDefaultHealthCheckerIsCircuitBreakerTripped(t *testing.T) { func TestNewDefaultHealthChecker(t *testing.T) { defer protocol.CleanAllStatus() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - defaultHc := NewDefaultHealthChecker(&url).(*DefaultHealthChecker) + defaultHc := NewDefaultHealthChecker(url).(*DefaultHealthChecker) assert.NotNil(t, defaultHc) assert.Equal(t, defaultHc.outStandingRequestConutLimit, int32(math.MaxInt32)) assert.Equal(t, defaultHc.requestSuccessiveFailureThreshold, int32(constant.DEFAULT_SUCCESSIVE_FAILED_REQUEST_MAX_DIFF)) @@ -150,13 +153,13 @@ func TestNewDefaultHealthChecker(t *testing.T) { url1, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) url1.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "10") url1.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10") - nondefaultHc := NewDefaultHealthChecker(&url1).(*DefaultHealthChecker) + nondefaultHc := NewDefaultHealthChecker(url1).(*DefaultHealthChecker) assert.NotNil(t, nondefaultHc) assert.Equal(t, nondefaultHc.outStandingRequestConutLimit, int32(10)) assert.Equal(t, nondefaultHc.requestSuccessiveFailureThreshold, int32(10)) } -func request(url common.URL, method string, elapsed int64, active, succeeded bool) { +func request(url *common.URL, method string, elapsed int64, active, succeeded bool) { protocol.BeginCount(url, method) if !active { protocol.EndCount(url, method, elapsed, succeeded) diff --git a/cluster/router/healthcheck/factory.go b/cluster/router/healthcheck/factory.go index 40c9dd7ab9e431a16833507ee4093ff7fbff8c95..a9054c7714417426bcf916dddfe61099df69e26e 100644 --- a/cluster/router/healthcheck/factory.go +++ b/cluster/router/healthcheck/factory.go @@ -38,6 +38,6 @@ func newHealthCheckRouteFactory() router.PriorityRouterFactory { } // NewPriorityRouter construct a new NewHealthCheckRouter via url -func (f *HealthCheckRouteFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { - return NewHealthCheckRouter(url) +func (f *HealthCheckRouteFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewHealthCheckRouter(url, notify) } diff --git a/cluster/router/healthcheck/factory_test.go b/cluster/router/healthcheck/factory_test.go index e80fd4c4d38dbb1c0f2b14ba1e22971249bc54b6..1e736832cecf07d523960fce8eebcc9248cee0a9 100644 --- a/cluster/router/healthcheck/factory_test.go +++ b/cluster/router/healthcheck/factory_test.go @@ -33,18 +33,18 @@ import ( // nolint type MockInvoker struct { - url common.URL + url *common.URL } // nolint -func NewMockInvoker(url common.URL) *MockInvoker { +func NewMockInvoker(url *common.URL) *MockInvoker { return &MockInvoker{ url: url, } } // nolint -func (bi *MockInvoker) GetUrl() common.URL { +func (bi *MockInvoker) GetUrl() *common.URL { return bi.url } diff --git a/cluster/router/healthcheck/health_check_route.go b/cluster/router/healthcheck/health_check_route.go index ee42e47e3b26c9a1976b4599d3464d752b615e0a..bd9543e1b27586408293bb9dcbed35a44036d3c7 100644 --- a/cluster/router/healthcheck/health_check_route.go +++ b/cluster/router/healthcheck/health_check_route.go @@ -17,8 +17,13 @@ package healthcheck +import ( + "github.com/RoaringBitmap/roaring" +) + import ( "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -27,7 +32,8 @@ import ( ) const ( - HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled" + healthy = "healthy" + name = "health-check-router" ) // HealthCheckRouter provides a health-first routing mechanism through HealthChecker @@ -35,13 +41,15 @@ type HealthCheckRouter struct { url *common.URL enabled bool checker router.HealthChecker + notify chan struct{} } // NewHealthCheckRouter construct an HealthCheckRouter via url -func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) { +func NewHealthCheckRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { r := &HealthCheckRouter{ url: url, - enabled: url.GetParamBool(HEALTH_ROUTE_ENABLED_KEY, false), + enabled: url.GetParamBool(constant.HEALTH_ROUTE_ENABLED_KEY, false), + notify: notify, } if r.enabled { checkerName := url.GetParam(constant.HEALTH_CHECKER, constant.DEFAULT_HEALTH_CHECKER) @@ -51,33 +59,55 @@ func NewHealthCheckRouter(url *common.URL) (router.PriorityRouter, error) { } // Route gets a list of healthy invoker -func (r *HealthCheckRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { +func (r *HealthCheckRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { if !r.enabled { return invokers } - healthyInvokers := make([]protocol.Invoker, 0, len(invokers)) + + addrPool := cache.FindAddrPool(r) // Add healthy invoker to the list - for _, invoker := range invokers { - if r.checker.IsHealthy(invoker) { - healthyInvokers = append(healthyInvokers, invoker) - } - } - // If all Invoke are considered unhealthy, downgrade to all inovker - if len(healthyInvokers) == 0 { + healthyInvokers := utils.JoinIfNotEqual(addrPool[healthy], invokers) + // If all invokers are considered unhealthy, downgrade to all invoker + if healthyInvokers.IsEmpty() { logger.Warnf(" Now all invokers are unhealthy, so downgraded to all! Service: [%s]", url.ServiceKey()) return invokers } return healthyInvokers } +// Pool separates healthy invokers from others. +func (r *HealthCheckRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + if !r.enabled { + return nil, nil + } + + rb := make(router.AddrPool, 8) + rb[healthy] = roaring.NewBitmap() + for i, invoker := range invokers { + if r.checker.IsHealthy(invoker) { + rb[healthy].Add(uint32(i)) + } + } + return rb, nil +} + +// ShouldPool will always return true to make sure healthy check constantly. +func (r *HealthCheckRouter) ShouldPool() bool { + return r.enabled +} + +func (r *HealthCheckRouter) Name() string { + return name +} + // Priority func (r *HealthCheckRouter) Priority() int64 { return 0 } // URL Return URL in router -func (r *HealthCheckRouter) URL() common.URL { - return *r.url +func (r *HealthCheckRouter) URL() *common.URL { + return r.url } // HealthyChecker returns the HealthChecker bound to this HealthCheckRouter diff --git a/cluster/router/healthcheck/health_check_route_test.go b/cluster/router/healthcheck/health_check_route_test.go index d5862fb884114bac0ea2ec9ee8926baac57d5ba6..f499c85c093add1cc247fdb9fac02e391ef22bfc 100644 --- a/cluster/router/healthcheck/health_check_route_test.go +++ b/cluster/router/healthcheck/health_check_route_test.go @@ -29,6 +29,9 @@ import ( ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" @@ -46,12 +49,17 @@ const ( func TestHealthCheckRouterRoute(t *testing.T) { defer protocol.CleanAllStatus() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() consumerURL, _ := common.NewURL(healthCheck1001URL) - consumerURL.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true") + consumerURL.SetParam(constant.HEALTH_ROUTE_ENABLED_KEY, "true") url1, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1010IP)) url2, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1011IP)) url3, _ := common.NewURL(fmt.Sprintf(healthCheckRouteUrlFormat, healthCheckRoute1012IP)) - hcr, _ := NewHealthCheckRouter(&consumerURL) + hcr, _ := NewHealthCheckRouter(consumerURL, notify) var invokers []protocol.Invoker invoker1 := NewMockInvoker(url1) @@ -59,25 +67,25 @@ func TestHealthCheckRouterRoute(t *testing.T) { invoker3 := NewMockInvoker(url3) invokers = append(invokers, invoker1, invoker2, invoker3) inv := invocation.NewRPCInvocation(healthCheckRouteMethodNameTest, nil, nil) - res := hcr.Route(invokers, &consumerURL, inv) + res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) // now all invokers are healthy - assert.True(t, len(res) == len(invokers)) + assert.True(t, len(res.ToArray()) == len(invokers)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) // invokers1 is unhealthy now - assert.True(t, len(res) == 2 && !contains(res, invoker1)) + assert.True(t, len(res.ToArray()) == 2 && !res.Contains(0)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) request(url2, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) // only invokers3 is healthy now - assert.True(t, len(res) == 1 && !contains(res, invoker1) && !contains(res, invoker2)) + assert.True(t, len(res.ToArray()) == 1 && !res.Contains(0) && !res.Contains(1)) for i := 0; i < 10; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) @@ -85,46 +93,42 @@ func TestHealthCheckRouterRoute(t *testing.T) { request(url3, healthCheckRouteMethodNameTest, 0, false, false) } - res = hcr.Route(invokers, &consumerURL, inv) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) // now all invokers are unhealthy, so downgraded to all - assert.True(t, len(res) == 3) + assert.True(t, len(res.ToArray()) == 3) // reset the invoker1 successive failed count, so invoker1 go to healthy request(url1, healthCheckRouteMethodNameTest, 0, false, true) - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, contains(res, invoker1)) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) + assert.True(t, res.Contains(0)) for i := 0; i < 6; i++ { request(url1, healthCheckRouteMethodNameTest, 0, false, false) } // now all invokers are unhealthy, so downgraded to all again - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, len(res) == 3) + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) + assert.True(t, len(res.ToArray()) == 3) time.Sleep(time.Second * 2) // invoker1 go to healthy again after 2s - res = hcr.Route(invokers, &consumerURL, inv) - assert.True(t, contains(res, invoker1)) - -} + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*HealthCheckRouter), invokers), consumerURL, inv) + assert.True(t, res.Contains(0)) -func contains(invokers []protocol.Invoker, invoker protocol.Invoker) bool { - for _, e := range invokers { - if e == invoker { - return true - } - } - return false } func TestNewHealthCheckRouter(t *testing.T) { defer protocol.CleanAllStatus() + notify := make(chan struct{}) + go func() { + for range notify { + } + }() url, _ := common.NewURL(fmt.Sprintf(healthCheckDubboUrlFormat, healthCheckDubbo1010IP)) - hcr, _ := NewHealthCheckRouter(&url) + hcr, _ := NewHealthCheckRouter(url, notify) h := hcr.(*HealthCheckRouter) assert.Nil(t, h.checker) - url.SetParam(HEALTH_ROUTE_ENABLED_KEY, "true") - hcr, _ = NewHealthCheckRouter(&url) + url.SetParam(constant.HEALTH_ROUTE_ENABLED_KEY, "true") + hcr, _ = NewHealthCheckRouter(url, notify) h = hcr.(*HealthCheckRouter) assert.NotNil(t, h.checker) @@ -136,10 +140,18 @@ func TestNewHealthCheckRouter(t *testing.T) { url.SetParam(constant.CIRCUIT_TRIPPED_TIMEOUT_FACTOR_KEY, "500") url.SetParam(constant.SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY, "10") url.SetParam(constant.OUTSTANDING_REQUEST_COUNT_LIMIT_KEY, "1000") - hcr, _ = NewHealthCheckRouter(&url) + hcr, _ = NewHealthCheckRouter(url, notify) h = hcr.(*HealthCheckRouter) dhc = h.checker.(*DefaultHealthChecker) assert.Equal(t, dhc.outStandingRequestConutLimit, int32(1000)) assert.Equal(t, dhc.requestSuccessiveFailureThreshold, int32(10)) assert.Equal(t, dhc.circuitTrippedTimeoutFactor, int32(500)) } + +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrMeta(r.Name(), info) + cache.SetAddrPool(r.Name(), pool) + return cache +} diff --git a/cluster/router/local/factory.go b/cluster/router/local/factory.go new file mode 100644 index 0000000000000000000000000000000000000000..42e03e25abdfa4c04851bddd7f57434d4c03a112 --- /dev/null +++ b/cluster/router/local/factory.go @@ -0,0 +1,43 @@ +/* + * 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 local + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/extension" +) + +func init() { + extension.SetRouterFactory(constant.LocalPriorityRouterName, newLocalPriorityRouteFactory) +} + +// LocalPriorityRouteFactory +type LocalPriorityRouteFactory struct { +} + +// newLocalPriorityRouteFactory construct a new LocalDiscRouteFactory +func newLocalPriorityRouteFactory() router.PriorityRouterFactory { + return &LocalPriorityRouteFactory{} +} + +// NewPriorityRouter construct a new NewLocalDiscRouter via url +func (f *LocalPriorityRouteFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewLocalPriorityRouter(url) +} diff --git a/cluster/router/local/factory_test.go b/cluster/router/local/factory_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a5a683b4bcb90d77085ee3b9c127590299725e9f --- /dev/null +++ b/cluster/router/local/factory_test.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package local + +import ( + "context" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" +) + +// nolint +type MockInvoker struct { + url *common.URL +} + +// nolint +func NewMockInvoker(url *common.URL) *MockInvoker { + return &MockInvoker{ + url: url, + } +} + +// nolint +func (bi *MockInvoker) GetUrl() *common.URL { + return bi.url +} + +// nolint +func (bi *MockInvoker) IsAvailable() bool { + return true +} + +// nolint +func (bi *MockInvoker) IsDestroyed() bool { + return true +} + +// nolint +func (bi *MockInvoker) Invoke(_ context.Context, _ protocol.Invocation) protocol.Result { + return nil +} + +// nolint +func (bi *MockInvoker) Destroy() { +} + +// nolint +func TestLocalDiscRouteFactory(t *testing.T) { + factory := newLocalPriorityRouteFactory() + assert.NotNil(t, factory) +} diff --git a/cluster/router/local/self_priority_route.go b/cluster/router/local/self_priority_route.go new file mode 100644 index 0000000000000000000000000000000000000000..87eaaf7837d546672b41bc9389503e9964344839 --- /dev/null +++ b/cluster/router/local/self_priority_route.go @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package local + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" + "github.com/apache/dubbo-go/protocol" +) + +const ( + localPriority = "local-priority" + name = "local-priority-router" +) + +// LocalPriorityRouter provides a ip-same-first routing logic +// if there is not provider with same ip as consumer, it would not filter any invoker +// if exists same ip invoker, it would retains this invoker only +type LocalPriorityRouter struct { + url *common.URL + localIP string +} + +// NewLocalPriorityRouter construct an LocalPriorityRouter via url +func NewLocalPriorityRouter(url *common.URL) (router.PriorityRouter, error) { + r := &LocalPriorityRouter{ + url: url, + localIP: url.Ip, + } + return r, nil +} + +// Route gets a list of match-logic invoker +func (r *LocalPriorityRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + addrPool := cache.FindAddrPool(r) + // Add localPriority invoker to the list + selectedInvokers := utils.JoinIfNotEqual(addrPool[localPriority], invokers) + // If all invokers are considered not match, downgrade to all invoker + if selectedInvokers.IsEmpty() { + logger.Warnf(" Now all invokers are not match, so downgraded to all! Service: [%s]", url.ServiceKey()) + return invokers + } + return selectedInvokers +} + +// Pool separates same ip invoker from others. +func (r *LocalPriorityRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + rb := make(router.AddrPool, 8) + rb[localPriority] = roaring.NewBitmap() + localIpFound := false + for i, invoker := range invokers { + if invoker.GetUrl().Ip == r.localIP { + rb[localPriority].Add(uint32(i)) + localIpFound = true + } + } + if localIpFound { + // found local desc + logger.Debug("found local ip ") + return rb, nil + } + for i, _ := range invokers { + rb[localPriority].Add(uint32(i)) + } + return rb, nil +} + +// ShouldPool will always return true to make sure local call logic constantly. +func (r *LocalPriorityRouter) ShouldPool() bool { + return true +} + +func (r *LocalPriorityRouter) Name() string { + return name +} + +// Priority +func (r *LocalPriorityRouter) Priority() int64 { + return 0 +} + +// URL Return URL in router +func (r *LocalPriorityRouter) URL() *common.URL { + return r.url +} diff --git a/cluster/router/local/self_priority_route_test.go b/cluster/router/local/self_priority_route_test.go new file mode 100644 index 0000000000000000000000000000000000000000..88535d3dd2b438cdcc9c4bfa1789a40256813a80 --- /dev/null +++ b/cluster/router/local/self_priority_route_test.go @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package local + +import ( + "fmt" + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/protocol" + "github.com/apache/dubbo-go/protocol/invocation" +) + +const ( + localDiscRoute1010IP = "192.168.10.10" + localDiscRoute1011IP = "192.168.10.11" + localDiscRoute1012IP = "192.168.10.12" + localDiscRouteMethodNameTest = "test" + localDiscRouteUrlFormat = "dubbo://%s:20000/com.ikurento.user.UserProvider" +) + +func TestLocalDiscRouterRoute(t *testing.T) { + defer protocol.CleanAllStatus() + consumerURL, _ := common.NewURL(fmt.Sprintf(localDiscRouteUrlFormat, localDiscRoute1010IP)) + url1, _ := common.NewURL(fmt.Sprintf(localDiscRouteUrlFormat, localDiscRoute1010IP)) + url2, _ := common.NewURL(fmt.Sprintf(localDiscRouteUrlFormat, localDiscRoute1011IP)) + url3, _ := common.NewURL(fmt.Sprintf(localDiscRouteUrlFormat, localDiscRoute1012IP)) + hcr, _ := NewLocalPriorityRouter(consumerURL) + + var invokers []protocol.Invoker + invoker1 := NewMockInvoker(url1) + invoker2 := NewMockInvoker(url2) + invoker3 := NewMockInvoker(url3) + invokers = append(invokers, invoker1, invoker2, invoker3) + inv := invocation.NewRPCInvocation(localDiscRouteMethodNameTest, nil, nil) + res := hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*LocalPriorityRouter), invokers), consumerURL, inv) + // now only same ip invoker is selected + assert.True(t, len(res.ToArray()) == 1) + + // now all invoker with ip that not match client are selected + invokers = invokers[1:] + res = hcr.Route(utils.ToBitmap(invokers), setUpAddrCache(hcr.(*LocalPriorityRouter), invokers), consumerURL, inv) + assert.True(t, len(res.ToArray()) == 2) +} + +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrMeta(r.Name(), info) + cache.SetAddrPool(r.Name(), pool) + return cache +} diff --git a/cluster/router/router.go b/cluster/router/router.go index 66603c1d4d0efedad3489712ecea91b43254fffd..1d71554f6b547bce86a2f960ceebfcf6c2a1daa2 100644 --- a/cluster/router/router.go +++ b/cluster/router/router.go @@ -17,6 +17,10 @@ package router +import ( + "github.com/RoaringBitmap/roaring" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/protocol" @@ -26,7 +30,7 @@ import ( // PriorityRouterFactory creates creates priority router with url type PriorityRouterFactory interface { // NewPriorityRouter creates router instance with URL - NewPriorityRouter(*common.URL) (PriorityRouter, error) + NewPriorityRouter(*common.URL, chan struct{}) (PriorityRouter, error) } // FilePriorityRouterFactory creates priority router with parse config file @@ -38,9 +42,10 @@ type FilePriorityRouterFactory interface { // Router type router interface { // Route Determine the target invokers list. - Route([]protocol.Invoker, *common.URL, protocol.Invocation) []protocol.Invoker + Route(*roaring.Bitmap, Cache, *common.URL, protocol.Invocation) *roaring.Bitmap + // URL Return URL in router - URL() common.URL + URL() *common.URL } // Router @@ -51,10 +56,38 @@ type PriorityRouter interface { Priority() int64 } -// NotifyRouter notify router use the invoker list. Invoker list may change from time to time. This method gives the router a -// chance to prepare before {@link Router#route(List, URL, Invocation)} gets called. -type NotifyRouter interface { - PriorityRouter - // Notify notify whenever addresses in registry change - Notify([]protocol.Invoker) +// Poolable caches address pool and address metadata for a router instance which will be used later in Router's Route. +type Poolable interface { + // Pool created address pool and address metadata from the invokers. + Pool([]protocol.Invoker) (AddrPool, AddrMetadata) + + // ShouldPool returns if it should pool. One typical scenario is a router rule changes, in this case, a pooling + // is necessary, even if the addresses not changed at all. + ShouldPool() bool + + // Name return the Poolable's name. + Name() string +} + +// AddrPool is an address pool, backed by a snapshot of address list, divided into categories. +type AddrPool map[string]*roaring.Bitmap + +// AddrMetadta is address metadata, collected from a snapshot of address list by a router, if it implements Poolable. +type AddrMetadata interface { + // Source indicates where the metadata comes from. + Source() string +} + +// Cache caches all addresses relevant info for a snapshot of received invokers. It keeps a snapshot of the received +// address list, and also keeps address pools and address metadata from routers based on the same address snapshot, if +// the router implements Poolable. +type Cache interface { + // GetInvokers returns the snapshot of received invokers. + GetInvokers() []protocol.Invoker + + // FindAddrPool returns address pool associated with the given Poolable instance. + FindAddrPool(Poolable) AddrPool + + // FindAddrMeta returns address metadata associated with the given Poolable instance. + FindAddrMeta(Poolable) AddrMetadata } diff --git a/cluster/router/tag/factory.go b/cluster/router/tag/factory.go index a5d989cd31453f6d02eee9c5902dc3666defe4fe..fd2c15cddfe521a66f5ad4c5072fa0fb325a3201 100644 --- a/cluster/router/tag/factory.go +++ b/cluster/router/tag/factory.go @@ -37,8 +37,8 @@ func NewTagRouterFactory() router.PriorityRouterFactory { // NewPriorityRouter create a tagRouter by tagRouterFactory with a url // The url contains router configuration information -func (c *tagRouterFactory) NewPriorityRouter(url *common.URL) (router.PriorityRouter, error) { - return NewTagRouter(url) +func (c *tagRouterFactory) NewPriorityRouter(url *common.URL, notify chan struct{}) (router.PriorityRouter, error) { + return NewTagRouter(url, notify) } // NewFileRouter create a tagRouter by profile content diff --git a/cluster/router/tag/factory_test.go b/cluster/router/tag/factory_test.go index ee195820c123e1fc67a2c27cd12aaa544650b615..2a8eac20bace9047563568d2bda2e4efef09c392 100644 --- a/cluster/router/tag/factory_test.go +++ b/cluster/router/tag/factory_test.go @@ -39,7 +39,12 @@ func TestTagRouterFactoryNewRouter(t *testing.T) { u1, err := common.NewURL(fmt.Sprintf(factoryFormat, factoryLocalIP)) assert.Nil(t, err) factory := NewTagRouterFactory() - tagRouter, e := factory.NewPriorityRouter(&u1) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + tagRouter, e := factory.NewPriorityRouter(u1, notify) assert.Nil(t, e) assert.NotNil(t, tagRouter) } diff --git a/cluster/router/tag/file.go b/cluster/router/tag/file.go index 433abcb72eb6e201e64790af932e847d8faba8af..81c25109dd85f2e358db57633967a851618bf924 100644 --- a/cluster/router/tag/file.go +++ b/cluster/router/tag/file.go @@ -24,10 +24,12 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" @@ -39,10 +41,11 @@ type FileTagRouter struct { router *tagRouter routerRule *RouterRule url *common.URL - force bool + //force bool } // NewFileTagRouter Create file tag router instance with content (from config file) +// todo fix this router, now it is useless, tag router is nil func NewFileTagRouter(content []byte) (*FileTagRouter, error) { fileRouter := &FileTagRouter{} rule, err := getRule(string(content)) @@ -50,13 +53,13 @@ func NewFileTagRouter(content []byte) (*FileTagRouter, error) { return nil, perrors.Errorf("yaml.Unmarshal() failed , error:%v", perrors.WithStack(err)) } fileRouter.routerRule = rule - url := fileRouter.URL() - fileRouter.router, err = NewTagRouter(&url) + notify := make(chan struct{}) + fileRouter.router, err = NewTagRouter(fileRouter.URL(), notify) return fileRouter, err } // URL Return URL in file tag router n -func (f *FileTagRouter) URL() common.URL { +func (f *FileTagRouter) URL() *common.URL { f.parseOnce.Do(func() { routerRule := f.routerRule f.url = common.NewURLWithOptions( @@ -66,7 +69,7 @@ func (f *FileTagRouter) URL() common.URL { common.WithParamsValue(constant.RouterPriority, strconv.Itoa(routerRule.Priority)), common.WithParamsValue(constant.ROUTER_KEY, constant.TAG_ROUTE_PROTOCOL)) }) - return *f.url + return f.url } // Priority Return Priority in listenable router @@ -74,9 +77,10 @@ func (f *FileTagRouter) Priority() int64 { return f.router.priority } -func (f *FileTagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if len(invokers) == 0 { +func (f *FileTagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if invokers.IsEmpty() { return invokers } - return f.Route(invokers, url, invocation) + // FIXME: I believe this is incorrect. + return f.Route(invokers, cache, url, invocation) } diff --git a/cluster/router/tag/router_rule.go b/cluster/router/tag/router_rule.go index c0a2d763ae7396ae41694f558f443d46084c3357..512d8f10795b04a18947396be8ae7fa6c4f49d4a 100644 --- a/cluster/router/tag/router_rule.go +++ b/cluster/router/tag/router_rule.go @@ -84,9 +84,9 @@ func (t *RouterRule) getTagNames() []string { return result } -func (t *RouterRule) hasTag(tag string) bool { - return len(t.TagNameToAddresses[tag]) > 0 -} +//func (t *RouterRule) hasTag(tag string) bool { +// return len(t.TagNameToAddresses[tag]) > 0 +//} func (t *RouterRule) getAddressToTagNames() map[string][]string { return t.AddressToTagNames @@ -96,10 +96,10 @@ func (t *RouterRule) getTagNameToAddresses() map[string][]string { return t.TagNameToAddresses } -func (t *RouterRule) getTags() []Tag { - return t.Tags -} +//func (t *RouterRule) getTags() []Tag { +// return t.Tags +//} -func (t *RouterRule) setTags(tags []Tag) { - t.Tags = tags -} +//func (t *RouterRule) setTags(tags []Tag) { +// t.Tags = tags +//} diff --git a/cluster/router/tag/tag_router.go b/cluster/router/tag/tag_router.go index fafed68c76414ed24b40f8d691858da8d3622e44..3d0393a487deb3eb592da3edb9f37becc394d7df 100644 --- a/cluster/router/tag/tag_router.go +++ b/cluster/router/tag/tag_router.go @@ -18,17 +18,20 @@ package tag import ( - "net" "strconv" + "strings" + "sync" ) import ( + "github.com/RoaringBitmap/roaring" gxnet "github.com/dubbogo/gost/net" - "github.com/jinzhu/copier" perrors "github.com/pkg/errors" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" "github.com/apache/dubbo-go/common/constant" @@ -38,6 +41,32 @@ import ( "github.com/apache/dubbo-go/remoting" ) +const ( + name = "tag-router" + staticPrefix = "static-" + dynamicPrefix = "dynamic-" +) + +// addrMetadata keeps snapshot data for app name, and some infos extracted from dynamic tag rule in order to make +// Route() method lock-free. +type addrMetadata struct { + // application name + application string + // is rule a runtime rule + //ruleRuntime bool + // is rule a force rule + ruleForce bool + // is rule a valid rule + ruleValid bool + // is rule an enabled rule + ruleEnabled bool +} + +// Source indicates where the metadata comes from. +func (m *addrMetadata) Source() string { + return name +} + // tagRouter defines url, enable and the priority type tagRouter struct { url *common.URL @@ -45,10 +74,13 @@ type tagRouter struct { enabled bool priority int64 application string + ruleChanged bool + mutex sync.RWMutex + notify chan struct{} } // NewTagRouter returns a tagRouter instance if url is not nil -func NewTagRouter(url *common.URL) (*tagRouter, error) { +func NewTagRouter(url *common.URL, notify chan struct{}) (*tagRouter, error) { if url == nil { return nil, perrors.Errorf("Illegal route URL!") } @@ -56,6 +88,7 @@ func NewTagRouter(url *common.URL) (*tagRouter, error) { url: url, enabled: url.GetParamBool(constant.RouterEnabled, true), priority: url.GetParamInt(constant.RouterPriority, 0), + notify: notify, }, nil } @@ -65,62 +98,79 @@ func (c *tagRouter) isEnabled() bool { } // Route gets a list of invoker -func (c *tagRouter) Route(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - var ( - result []protocol.Invoker - addresses []string - tag string - ) - - if !c.isEnabled() || len(invokers) == 0 { +func (c *tagRouter) Route(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + if !c.isEnabled() || invokers.IsEmpty() { return invokers } - // Use static tags if dynamic tags are not set or invalid - if c.tagRouterRule == nil || !c.tagRouterRule.Valid || !c.tagRouterRule.Enabled { - return filterUsingStaticTag(invokers, url, invocation) + if shouldUseDynamicTag(cache.FindAddrMeta(c)) { + return c.routeWithDynamicTag(invokers, cache, url, invocation) } + return c.routeWithStaticTag(invokers, cache, url, invocation) +} - // since the rule can be changed by config center, we should copy one to use. - tagRouterRuleCopy := new(RouterRule) - _ = copier.Copy(tagRouterRuleCopy, c.tagRouterRule) - 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, "") - } +// routeWithStaticTag routes with static tag rule +func (c *tagRouter) routeWithStaticTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + tag := findTag(invocation, url) + if tag == "" { + return invokers } - // if we are requesting for a Provider with a specific tag - if len(tag) > 0 { - return filterInvokersWithTag(invokers, url, invocation, *tagRouterRuleCopy, tag) + ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag) + if ret.IsEmpty() && !isForceUseTag(url, invocation) { + return invokers } - // return all addresses in dynamic tag group. - addresses = tagRouterRuleCopy.getAddresses() - if len(addresses) > 0 { - filterAddressNotMatches := func(invoker protocol.Invoker) bool { - url := invoker.GetUrl() - return len(addresses) == 0 || !checkAddressMatch(addresses, url.Ip, url.Port) - } - result = filterInvoker(invokers, filterAddressNotMatches) - // 1. all addresses are in dynamic tag group, return empty list. - if len(result) == 0 { - return result + return ret +} + +// routeWithDynamicTag routes with dynamic tag rule +func (c *tagRouter) routeWithDynamicTag(invokers *roaring.Bitmap, cache router.Cache, url *common.URL, invocation protocol.Invocation) *roaring.Bitmap { + tag := findTag(invocation, url) + if tag == "" { + return c.filterNotInDynamicTag(invokers, cache) + } + + ret, ok := c.filterWithTag(invokers, cache, dynamicPrefix+tag) + if ok && (!ret.IsEmpty() || isTagRuleForce(cache.FindAddrMeta(c))) { + return ret + } + + // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by + // dynamic tag group but force=false. check static tag + if ret.IsEmpty() { + ret, _ = c.filterWithTag(invokers, cache, staticPrefix+tag) + // If there's no tagged providers that can match the current tagged request. force.tag is set by default + // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. + if !ret.IsEmpty() || isForceUseTag(url, invocation) { + return ret } + return c.filterNotInDynamicTag(invokers, cache) } - // 2. if there are some addresses that are not in any dynamic tag group, continue to filter using the - // static tag group. - filter := func(invoker protocol.Invoker) bool { - localTag := invoker.GetUrl().GetParam(constant.Tagkey, "") - return len(localTag) == 0 || !(tagRouterRuleCopy.hasTag(localTag)) + return ret +} + +// filterWithTag filters incoming invokers with the given tag +func (c *tagRouter) filterWithTag(invokers *roaring.Bitmap, cache router.Cache, tag string) (*roaring.Bitmap, bool) { + if target, ok := cache.FindAddrPool(c)[tag]; ok { + return utils.JoinIfNotEqual(target, invokers), true } - return filterInvoker(result, filter) + return utils.EmptyAddr, false } +// filterNotInDynamicTag filters incoming invokers not applied to dynamic tag rule +func (c *tagRouter) filterNotInDynamicTag(invokers *roaring.Bitmap, cache router.Cache) *roaring.Bitmap { + // FAILOVER: return all Providers without any tags. + invokers = invokers.Clone() + for k, v := range cache.FindAddrPool(c) { + if strings.HasPrefix(k, dynamicPrefix) { + invokers.AndNot(v) + } + } + return invokers +} + +// Process parses dynamic tag rule func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) { logger.Infof("Notification of tag rule, change type is:[%s] , raw rule is:[%v]", event.ConfigType, event.Value) if remoting.EventTypeDel == event.ConfigType { @@ -138,16 +188,53 @@ func (c *tagRouter) Process(event *config_center.ConfigChangeEvent) { logger.Errorf("Parse dynamic tag router rule fail,error:[%s] ", err) return } + + c.mutex.Lock() + defer c.mutex.Unlock() c.tagRouterRule = routerRule - return + c.ruleChanged = true + c.notify <- struct{}{} } -func (c *tagRouter) Notify(invokers []protocol.Invoker) { +// URL gets the url of tagRouter +func (c *tagRouter) URL() *common.URL { + return c.url +} + +// Priority gets the priority of tagRouter +func (c *tagRouter) Priority() int64 { + return c.priority +} + +// Pool divided invokers into different address pool by tag. +func (c *tagRouter) Pool(invokers []protocol.Invoker) (router.AddrPool, router.AddrMetadata) { + c.fetchRuleIfNecessary(invokers) + + rb := make(router.AddrPool, 8) + poolWithStaticTag(invokers, rb) + + c.mutex.Lock() + defer c.mutex.Unlock() + poolWithDynamicTag(invokers, c.tagRouterRule, rb) + c.ruleChanged = false + // create metadata in order to avoid lock in route() + meta := addrMetadata{application: c.application} + if c.tagRouterRule != nil { + meta.ruleForce = c.tagRouterRule.Force + meta.ruleEnabled = c.tagRouterRule.Enabled + meta.ruleValid = c.tagRouterRule.Valid + } + + return rb, &meta +} + +// fetchRuleIfNecessary fetches, parses rule and register listener for the further change +func (c *tagRouter) fetchRuleIfNecessary(invokers []protocol.Invoker) { if len(invokers) == 0 { return } - invoker := invokers[0] - url := invoker.GetUrl() + + url := invokers[0].GetUrl() providerApplication := url.GetParam(constant.RemoteApplicationKey, "") if len(providerApplication) == 0 { logger.Error("TagRouter must getConfig from or subscribe to a specific application, but the application " + @@ -162,11 +249,15 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) { if providerApplication != c.application { dynamicConfiguration.RemoveListener(c.application+constant.TagRouterRuleSuffix, c) + } else { + // if app name from URL is as same as the current app name, then it is safe to jump out + return } + c.application = providerApplication routerKey := providerApplication + constant.TagRouterRuleSuffix dynamicConfiguration.AddListener(routerKey, c) - //get rule + // get rule rule, err := dynamicConfiguration.GetRule(routerKey, config_center.WithGroup(config_center.DEFAULT_GROUP)) if len(rule) == 0 || err != nil { logger.Errorf("Get rule fail, config rule{%s}, error{%v}", rule, err) @@ -180,71 +271,52 @@ func (c *tagRouter) Notify(invokers []protocol.Invoker) { } } -// URL gets the url of tagRouter -func (c *tagRouter) URL() common.URL { - return *c.url +// ShouldPool returns false, to make sure address cache for tag router happens once and only once. +func (c *tagRouter) ShouldPool() bool { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.ruleChanged } -// Priority gets the priority of tagRouter -func (c *tagRouter) Priority() int64 { - return c.priority +// Name returns pool's name +func (c *tagRouter) Name() string { + return name } -// filterUsingStaticTag gets a list of invoker using static tag -func filterUsingStaticTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation) []protocol.Invoker { - if tag, ok := invocation.Attachments()[constant.Tagkey]; ok { - result := make([]protocol.Invoker, 0, 8) - for _, v := range invokers { - if v.GetUrl().GetParam(constant.Tagkey, "") == tag { - result = append(result, v) - } - } - if len(result) == 0 && !isForceUseTag(url, invocation) { - return invokers - } - return result +// poolWithDynamicTag pools addresses with the tags defined in dynamic tag rule, all keys have prefix "dynamic-" +func poolWithDynamicTag(invokers []protocol.Invoker, rule *RouterRule, pool router.AddrPool) { + if rule == nil { + return } - return invokers -} -// filterInvokersWithTag gets a list of invoker using dynamic route with tag -func filterInvokersWithTag(invokers []protocol.Invoker, url *common.URL, invocation protocol.Invocation, tagRouterRule RouterRule, tag string) []protocol.Invoker { - var ( - result []protocol.Invoker - addresses []string - ) - addresses, _ = tagRouterRule.getTagNameToAddresses()[tag] - // filter by dynamic tag group first - if len(addresses) > 0 { - filterAddressMatches := func(invoker protocol.Invoker) bool { - url := invoker.GetUrl() - return len(addresses) > 0 && checkAddressMatch(addresses, url.Ip, url.Port) - } - result = filterInvoker(invokers, filterAddressMatches) - if len(result) > 0 || tagRouterRule.Force { - return result - } - } - // dynamic tag group doesn't have any item about the requested app OR it's null after filtered by - // dynamic tag group but force=false. check static tag - filter := func(invoker protocol.Invoker) bool { - return invoker.GetUrl().GetParam(constant.Tagkey, "") == tag - } - result = filterInvoker(invokers, filter) - // If there's no tagged providers that can match the current tagged request. force.tag is set by default - // to false, which means it will invoke any providers without a tag unless it's explicitly disallowed. - if len(result) > 0 || isForceUseTag(url, invocation) { - return result + tagNameToAddresses := rule.getTagNameToAddresses() + for tag, addrs := range tagNameToAddresses { + pool[dynamicPrefix+tag] = addrsToBitmap(addrs, invokers) } - // FAILOVER: return all Providers without any tags. - filterAddressNotMatches := func(invoker protocol.Invoker) bool { +} + +// poolWithStaticTag pools addresses with tags found from incoming URLs, all keys have prefix "static-" +func poolWithStaticTag(invokers []protocol.Invoker, pool router.AddrPool) { + for i, invoker := range invokers { url := invoker.GetUrl() - return len(addresses) == 0 || !checkAddressMatch(tagRouterRule.getAddresses(), url.Ip, url.Port) - } - filterTagIsEmpty := func(invoker protocol.Invoker) bool { - return invoker.GetUrl().GetParam(constant.Tagkey, "") == "" + tag := url.GetParam(constant.Tagkey, "") + if len(tag) > 0 { + if _, ok := pool[staticPrefix+tag]; !ok { + pool[staticPrefix+tag] = roaring.NewBitmap() + } + pool[staticPrefix+tag].AddInt(i) + } } - return filterInvoker(invokers, filterAddressNotMatches, filterTagIsEmpty) +} + +// shouldUseDynamicTag uses the snapshot data from the parsed rule to decide if dynamic tag rule should be used or not +func shouldUseDynamicTag(meta router.AddrMetadata) bool { + return meta.(*addrMetadata).ruleValid && meta.(*addrMetadata).ruleEnabled +} + +// isTagRuleForce uses the snapshot data from the parsed rule to decide if dynamic tag rule is forced or not +func isTagRuleForce(meta router.AddrMetadata) bool { + return meta.(*addrMetadata).ruleForce } // isForceUseTag returns whether force use tag @@ -255,31 +327,46 @@ func isForceUseTag(url *common.URL, invocation protocol.Invocation) bool { return false } -type filter func(protocol.Invoker) bool +// addrsToBitmap finds indexes for the given IP addresses in the target URL list, if any '0.0.0.0' IP address is met, +// then returns back all indexes of the URLs list. +func addrsToBitmap(addrs []string, invokers []protocol.Invoker) *roaring.Bitmap { + ret := roaring.NewBitmap() + for _, addr := range addrs { + if isAnyHost(addr) { + ret.AddRange(0, uint64(len(invokers))) + return ret + } -func filterInvoker(invokers []protocol.Invoker, filters ...filter) []protocol.Invoker { - var res []protocol.Invoker -OUTER: - for _, invoker := range invokers { - for _, filter := range filters { - if !filter(invoker) { - continue OUTER - } + index := findIndexWithIp(addr, invokers) + if index != -1 { + ret.AddInt(index) } - res = append(res, invoker) } - return res + return ret } -func checkAddressMatch(addresses []string, host, port string) bool { - addr := net.JoinHostPort(constant.ANYHOST_VALUE, port) - for _, address := range addresses { - if gxnet.MatchIP(address, host, port) { - return true - } - if address == addr { - return true +// findIndexWithIp finds index for one particular IP +func findIndexWithIp(addr string, invokers []protocol.Invoker) int { + for i, invoker := range invokers { + if gxnet.MatchIP(addr, invoker.GetUrl().Ip, invoker.GetUrl().Port) { + return i } } - return false + return -1 +} + +// isAnyHost checks if an IP is '0.0.0.0' +func isAnyHost(addr string) bool { + return strings.HasPrefix(addr, constant.ANYHOST_VALUE) +} + +// findTag finds tag, first from invocation's attachment, then from URL +func findTag(invocation protocol.Invocation, consumerUrl *common.URL) string { + tag, ok := invocation.Attachments()[constant.Tagkey] + if !ok { + return consumerUrl.GetParam(constant.Tagkey, "") + } else if v, t := tag.(string); t { + return v + } + return "" } diff --git a/cluster/router/tag/tag_router_test.go b/cluster/router/tag/tag_router_test.go index 6b9a5e08f133403565d8b2dd38e03a789c5e95e7..3b53d473faf4e8eddefec8b6cf7be897175ffae7 100644 --- a/cluster/router/tag/tag_router_test.go +++ b/cluster/router/tag/tag_router_test.go @@ -25,22 +25,25 @@ import ( ) import ( + "github.com/RoaringBitmap/roaring" "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/cluster/router/chain" + "github.com/apache/dubbo-go/cluster/router/utils" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" - "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/config_center" _ "github.com/apache/dubbo-go/config_center/zookeeper" "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/zookeeper" ) const ( @@ -70,19 +73,18 @@ const ( ) var ( - zkFormat = "zookeeper://%s:%d" - conditionFormat = "condition://%s/com.foo.BarService" + zkFormat = "zookeeper://%s:%d" ) // MockInvoker is only mock the Invoker to support test tagRouter type MockInvoker struct { - url common.URL + url *common.URL available bool destroyed bool successCount int } -func NewMockInvoker(url common.URL) *MockInvoker { +func NewMockInvoker(url *common.URL) *MockInvoker { return &MockInvoker{ url: url, available: true, @@ -91,7 +93,7 @@ func NewMockInvoker(url common.URL) *MockInvoker { } } -func (bi *MockInvoker) GetUrl() common.URL { +func (bi *MockInvoker) GetUrl() *common.URL { return bi.url } @@ -117,17 +119,27 @@ func (bi *MockInvoker) Destroy() { func TestTagRouterPriority(t *testing.T) { u1, err := common.NewURL(tagRouterTestUserConsumerTag) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() assert.Nil(t, err) - tagRouter, e := NewTagRouter(&u1) + tagRouter, e := NewTagRouter(u1, notify) assert.Nil(t, e) p := tagRouter.Priority() assert.Equal(t, int64(0), p) } func TestTagRouterRouteForce(t *testing.T) { + notify := make(chan struct{}) + go func() { + for range notify { + } + }() u1, e1 := common.NewURL(tagRouterTestUserConsumerTag) assert.Nil(t, e1) - tagRouter, e := NewTagRouter(&u1) + tagRouter, e := NewTagRouter(u1, notify) assert.Nil(t, e) u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) @@ -143,23 +155,28 @@ func TestTagRouterRouteForce(t *testing.T) { invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) - invRst1 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 1, len(invRst1)) - assert.Equal(t, tagRouterTestHangZhou, invRst1[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) + invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 1, len(invRst1.ToArray())) + assert.Equal(t, tagRouterTestHangZhou, invokers[invRst1.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, "")) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) - invRst2 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 0, len(invRst2)) + invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 0, len(invRst2.ToArray())) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) - invRst3 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 3, len(invRst3)) + invRst3 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 3, len(invRst3.ToArray())) } func TestTagRouterRouteNoForce(t *testing.T) { u1, e1 := common.NewURL(tagRouterTestUserConsumer) assert.Nil(t, e1) - tagRouter, e := NewTagRouter(&u1) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + tagRouter, e := NewTagRouter(u1, notify) assert.Nil(t, e) u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) @@ -175,20 +192,37 @@ func TestTagRouterRouteNoForce(t *testing.T) { invokers = append(invokers, inv2, inv3, inv4) inv := &invocation.RPCInvocation{} inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestHangZhou) - invRst := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 1, len(invRst)) - assert.Equal(t, tagRouterTestHangZhou, invRst[0].GetUrl().GetParam(tagRouterTestDubboTag, "")) + invRst := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 1, len(invRst.ToArray())) + assert.Equal(t, tagRouterTestHangZhou, invokers[invRst.ToArray()[0]].GetUrl().GetParam(tagRouterTestDubboTag, "")) inv.SetAttachments(tagRouterTestDubboTag, tagRouterTestGuangZhou) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestTrue) - invRst1 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 0, len(invRst1)) + invRst1 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 0, len(invRst1.ToArray())) inv.SetAttachments(tagRouterTestDubboForceTag, tagRouterTestFalse) - invRst2 := tagRouter.Route(invokers, &u1, inv) - assert.Equal(t, 3, len(invRst2)) + invRst2 := tagRouter.Route(utils.ToBitmap(invokers), setUpAddrCache(tagRouter, invokers), u1, inv) + assert.Equal(t, 3, len(invRst2.ToArray())) +} + +func setUpAddrCache(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + cache := chain.BuildCache(addrs) + cache.SetAddrPool(r.Name(), pool) + cache.SetAddrMeta(r.Name(), info) + return cache } -func TestFilterInvoker(t *testing.T) { +func setUpAddrCacheWithRuleDisabled(r router.Poolable, addrs []protocol.Invoker) router.Cache { + pool, info := r.Pool(addrs) + info.(*addrMetadata).ruleEnabled = false + cache := chain.BuildCache(addrs) + cache.SetAddrPool(r.Name(), pool) + cache.SetAddrMeta(r.Name(), info) + return cache +} + +func TestRouteBeijingInvoker(t *testing.T) { u2, e2 := common.NewURL(tagRouterTestHangZhouUrl) u3, e3 := common.NewURL(tagRouterTestShangHaiUrl) u4, e4 := common.NewURL(tagRouterTestBeijingUrl) @@ -203,31 +237,30 @@ func TestFilterInvoker(t *testing.T) { inv5 := NewMockInvoker(u5) var invokers []protocol.Invoker invokers = append(invokers, inv2, inv3, inv4, inv5) - filterTag := func(invoker protocol.Invoker) bool { - if invoker.GetUrl().GetParam(constant.Tagkey, "") == "beijing" { - return true - } - return false - } - res := filterInvoker(invokers, filterTag) - assert.Equal(t, []protocol.Invoker{inv4, inv5}, res) - flag := true - filterEnabled := func(invoker protocol.Invoker) bool { - if invoker.GetUrl().GetParamBool(constant.RouterEnabled, false) == flag { - return true + + url, _ := common.NewURL(tagRouterTestBeijingUrl) + notify := make(chan struct{}) + go func() { + for range notify { } - return false - } - res2 := filterInvoker(invokers, filterTag, filterEnabled) - assert.Equal(t, []protocol.Invoker{inv4}, res2) + }() + tagRouter, _ := NewTagRouter(url, notify) + + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(tagRouter, invokers) + inv := &invocation.RPCInvocation{} + res := tagRouter.Route(rb, cache, url, inv) + // inv4 and inv5 + assert.Equal(t, []uint32{2, 3}, res.ToArray()) } type DynamicTagRouter struct { suite.Suite - rule *RouterRule + //rule *RouterRule route *tagRouter - zkClient *zookeeper.ZookeeperClient + zkClient *gxzookeeper.ZookeeperClient testCluster *zk.TestCluster invokers []protocol.Invoker url *common.URL @@ -266,7 +299,7 @@ tags: - name: tag3 addresses: ["127.0.0.1:20003", "127.0.0.1:20004"] ` - ts, z, _, err := zookeeper.NewMockZookeeperClient("test", 15*time.Second) + ts, z, _, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) suite.NoError(err) err = z.Create(routerPath) suite.NoError(err) @@ -278,7 +311,7 @@ tags: suite.NoError(err) zkUrl, _ := common.NewURL(fmt.Sprintf(zkFormat, routerLocalIP, suite.testCluster.Servers[0].Port)) - configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(&zkUrl) + configuration, err := extension.GetConfigCenterFactory(routerZk).GetDynamicConfiguration(zkUrl) config.GetEnvInstance().SetDynamicConfiguration(configuration) suite.Nil(err) @@ -287,64 +320,80 @@ tags: url, e1 := common.NewURL(tagRouterTestUserConsumerTag) suite.Nil(e1) - tagRouter, err := NewTagRouter(&url) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + tagRouter, err := NewTagRouter(url, notify) suite.Nil(err) suite.NotNil(tagRouter) suite.route = tagRouter - suite.url = &url + suite.url = url } func (suite *DynamicTagRouter) TearDownTest() { suite.zkClient.Close() - suite.testCluster.Stop() } func (suite *DynamicTagRouter) TestDynamicTagRouterSetByIPv4() { invokers := suite.invokers - suite.route.Notify(invokers) + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(suite.route, invokers) suite.NotNil(suite.route.tagRouterRule) consumer := &invocation.RPCInvocation{} consumer.SetAttachments(tagRouterTestDubboTag, "tag1") - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[0]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(0)) consumer.SetAttachments(tagRouterTestDubboTag, "tag3") - targetInvokers = suite.route.Route(invokers, suite.url, consumer) - suite.Equal(2, len(targetInvokers)) - suite.Equal(targetInvokers, []protocol.Invoker{suite.invokers[2], suite.invokers[3]}) + targetInvokers = suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(2), targetInvokers.GetCardinality()) + suite.True(targetInvokers.Contains(2) && targetInvokers.Contains(3)) } func (suite *DynamicTagRouter) TestDynamicTagRouterStaticTag() { invokers := suite.invokers + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCacheWithRuleDisabled(suite.route, invokers) consumer := &invocation.RPCInvocation{} consumer.SetAttachments(tagRouterTestDubboTag, "tag4") - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[3]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(3)) } // Teas no tag and return a address are not in dynamic tag group func (suite *DynamicTagRouter) TestDynamicTagRouterByNoTagAndAddressMatch() { invokers := suite.invokers - suite.route.Notify(invokers) + rb := roaring.NewBitmap() + rb.AddRange(0, uint64(len(invokers))) + cache := setUpAddrCache(suite.route, invokers) suite.NotNil(suite.route.tagRouterRule) consumer := &invocation.RPCInvocation{} - targetInvokers := suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[4]) + targetInvokers := suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(4)) // test if there are some addresses that are not in any dynamic tag group, continue to filter using the static tag group. consumer.SetAttachments(tagRouterTestDubboTag, "tag5") - targetInvokers = suite.route.Route(invokers, suite.url, consumer) - suite.Equal(1, len(targetInvokers)) - suite.Equal(targetInvokers[0], suite.invokers[4]) + targetInvokers = suite.route.Route(rb, cache, suite.url, consumer) + suite.Equal(uint64(1), targetInvokers.GetCardinality()) + suite.Equal(targetInvokers.ToArray()[0], uint32(4)) } func TestProcess(t *testing.T) { u1, err := common.NewURL(tagRouterTestUserConsumerTag) assert.Nil(t, err) - tagRouter, e := NewTagRouter(&u1) + notify := make(chan struct{}) + go func() { + for range notify { + } + }() + tagRouter, e := NewTagRouter(u1, notify) assert.Nil(t, e) assert.NotNil(t, tagRouter) @@ -362,12 +411,17 @@ tags: - name: hangzhou addresses: [192.168.1.3, 192.168.1.4] ` + go func() { + for range notify { + } + }() tagRouter.Process(&config_center.ConfigChangeEvent{Value: testYML, ConfigType: remoting.EventTypeAdd}) assert.NotNil(t, tagRouter.tagRouterRule) assert.Equal(t, []string{"beijing", "hangzhou"}, tagRouter.tagRouterRule.getTagNames()) assert.Equal(t, []string{"192.168.1.1", "192.168.1.2", "192.168.1.3", "192.168.1.4"}, tagRouter.tagRouterRule.getAddresses()) assert.Equal(t, []string{"192.168.1.3", "192.168.1.4"}, tagRouter.tagRouterRule.getTagNameToAddresses()["hangzhou"]) assert.Equal(t, []string{"beijing"}, tagRouter.tagRouterRule.getAddressToTagNames()["192.168.1.1"]) + tagRouter.Process(&config_center.ConfigChangeEvent{ConfigType: remoting.EventTypeDel}) assert.Nil(t, tagRouter.tagRouterRule) } diff --git a/cluster/router/utils/bitmap_util.go b/cluster/router/utils/bitmap_util.go new file mode 100644 index 0000000000000000000000000000000000000000..8b4ee5538f339276fb4c41ceefe4df3575723f15 --- /dev/null +++ b/cluster/router/utils/bitmap_util.go @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package utils + +import ( + "github.com/RoaringBitmap/roaring" +) + +import ( + "github.com/apache/dubbo-go/protocol" +) + +var EmptyAddr = roaring.NewBitmap() + +func JoinIfNotEqual(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap { + if !left.Equals(right) { + left = left.Clone() + left.And(right) + } + return left +} + +func FallbackIfJoinToEmpty(left *roaring.Bitmap, right *roaring.Bitmap) *roaring.Bitmap { + ret := JoinIfNotEqual(left, right) + if ret == nil || ret.IsEmpty() { + return right + } + return ret +} + +func ToBitmap(invokers []protocol.Invoker) *roaring.Bitmap { + bitmap := roaring.NewBitmap() + bitmap.AddRange(0, uint64(len(invokers))) + return bitmap +} diff --git a/common/config/environment.go b/common/config/environment.go index 44cdd1fca18bfed306b135fe38ef536779e148aa..23baa702c7711965d4b9e258c0e71a568b7888a2 100644 --- a/common/config/environment.go +++ b/common/config/environment.go @@ -34,8 +34,8 @@ import ( // We just have config center configuration which can override configuration in consumer.yaml & provider.yaml. // But for add these features in future ,I finish the environment struct following Environment class in java. type Environment struct { - configCenterFirst bool - externalConfigs sync.Map + configCenterFirst bool + //externalConfigs sync.Map externalConfigMap sync.Map appExternalConfigMap sync.Map dynamicConfiguration config_center.DynamicConfiguration @@ -72,6 +72,12 @@ func (env *Environment) UpdateExternalConfigMap(externalMap map[string]string) { for k, v := range externalMap { env.externalConfigMap.Store(k, v) } + env.externalConfigMap.Range(func(key, value interface{}) bool { + if _, ok := externalMap[key.(string)]; !ok { + env.externalConfigMap.Delete(key) + } + return true + }) } // UpdateAppExternalConfigMap updates env appExternalConfigMap field @@ -79,15 +85,21 @@ func (env *Environment) UpdateAppExternalConfigMap(externalMap map[string]string for k, v := range externalMap { env.appExternalConfigMap.Store(k, v) } + env.appExternalConfigMap.Range(func(key, value interface{}) bool { + if _, ok := externalMap[key.(string)]; !ok { + env.appExternalConfigMap.Delete(key) + } + return true + }) } // Configuration puts externalConfigMap and appExternalConfigMap into list // List represents a doubly linked list. func (env *Environment) Configuration() *list.List { cfgList := list.New() - // The sequence would be: SystemConfiguration -> ExternalConfiguration -> AppExternalConfiguration -> AbstractConfig -> PropertiesConfiguration - cfgList.PushFront(newInmemoryConfiguration(&(env.externalConfigMap))) - cfgList.PushFront(newInmemoryConfiguration(&(env.appExternalConfigMap))) + // The sequence would be: SystemConfiguration -> AppExternalConfiguration -> ExternalConfiguration -> AbstractConfig -> PropertiesConfiguration + cfgList.PushBack(newInmemoryConfiguration(&(env.appExternalConfigMap))) + cfgList.PushBack(newInmemoryConfiguration(&(env.externalConfigMap))) return cfgList } diff --git a/common/config/environment_test.go b/common/config/environment_test.go index 183071b0ef0d10e9ce010965668b46d30b008a15..ed3ca5506ecb349a2b5af385f58b81c81a368e9a 100644 --- a/common/config/environment_test.go +++ b/common/config/environment_test.go @@ -34,6 +34,29 @@ func TestEnvironmentUpdateExternalConfigMap(t *testing.T) { v, ok := GetEnvInstance().externalConfigMap.Load("1") assert.True(t, ok) assert.Equal(t, "2", v) + + GetEnvInstance().UpdateExternalConfigMap(map[string]string{"a": "b"}) + v, ok = GetEnvInstance().externalConfigMap.Load("a") + assert.True(t, ok) + assert.Equal(t, "b", v) + v, ok = GetEnvInstance().externalConfigMap.Load("1") + assert.False(t, ok) + assert.Equal(t, nil, v) +} + +func TestEnvironmentUpdateAppExternalConfigMap(t *testing.T) { + GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{"1": "2"}) + v, ok := GetEnvInstance().appExternalConfigMap.Load("1") + assert.True(t, ok) + assert.Equal(t, "2", v) + + GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{"a": "b"}) + v, ok = GetEnvInstance().appExternalConfigMap.Load("a") + assert.True(t, ok) + assert.Equal(t, "b", v) + v, ok = GetEnvInstance().appExternalConfigMap.Load("1") + assert.False(t, ok) + assert.Equal(t, nil, v) } func TestEnvironmentConfigurationAndGetProperty(t *testing.T) { diff --git a/common/constant/default.go b/common/constant/default.go index 4165942a615e220f6384a898b07c04bafd39c3b0..bbe022cb2efebf7dc716f324253743a5a5a0aff5 100644 --- a/common/constant/default.go +++ b/common/constant/default.go @@ -81,7 +81,7 @@ const ( const ( SIMPLE_METADATA_SERVICE_NAME = "MetadataService" - DEFAULT_REVIESION = "N/A" + DEFAULT_REVISION = "N/A" ) const ( diff --git a/common/constant/key.go b/common/constant/key.go index 943338f8e6f13512d96cfed4cbc4f275d6aab2cb..4b867d8d0c3fb21cb51b92771437f3bb69902dd2 100644 --- a/common/constant/key.go +++ b/common/constant/key.go @@ -17,6 +17,8 @@ package constant +type DubboCtxKey string + const ( ASYNC_KEY = "async" // it's value should be "true" or "false" of string type ) @@ -25,6 +27,7 @@ const ( GROUP_KEY = "group" VERSION_KEY = "version" INTERFACE_KEY = "interface" + MESSAGE_SIZE_KEY = "message_size" PATH_KEY = "path" SERVICE_KEY = "service" METHODS_KEY = "methods" @@ -46,8 +49,8 @@ const ( PORT_KEY = "port" PROTOCOL_KEY = "protocol" PATH_SEPARATOR = "/" - DUBBO_KEY = "dubbo" - SSL_ENABLED_KEY = "ssl-enabled" + //DUBBO_KEY = "dubbo" + SSL_ENABLED_KEY = "ssl-enabled" ) const ( @@ -88,10 +91,11 @@ const ( RETRY_PERIOD_KEY = "retry.period" RETRY_TIMES_KEY = "retry.times" CYCLE_REPORT_KEY = "cycle.report" + DEFAULT_BLACK_LIST_RECOVER_BLOCK = 16 ) const ( - DUBBOGO_CTX_KEY = "dubbogo-ctx" + DUBBOGO_CTX_KEY = DubboCtxKey("dubbogo-ctx") ) const ( @@ -126,6 +130,7 @@ const ( TAG_ROUTE_PROTOCOL = "tag" PROVIDERS_CATEGORY = "providers" ROUTER_KEY = "router" + EXPORT_KEY = "export" ) const ( @@ -169,6 +174,7 @@ const ( NACOS_NAMESPACE_ID = "namespaceId" NACOS_PASSWORD = "password" NACOS_USERNAME = "username" + NACOS_NOT_LOAD_LOCAL_CACHE = "nacos.not.load.cache" ) const ( @@ -196,7 +202,7 @@ const ( ) const ( - TRACING_REMOTE_SPAN_CTX = "tracing.remote.span.ctx" + TRACING_REMOTE_SPAN_CTX = DubboCtxKey("tracing.remote.span.ctx") ) // Use for router module @@ -209,6 +215,10 @@ const ( ListenableRouterName = "listenable" // HealthCheckRouterName Specify the name of HealthCheckRouter HealthCheckRouterName = "health_check" + // LocalPriorityRouterName Specify the name of LocalPriorityRouter + LocalPriorityRouterName = "local_priority" + // ConnCheckRouterName Specify the name of ConnCheckRouter + ConnCheckRouterName = "conn_check" // TagRouterName Specify the name of TagRouter TagRouterName = "tag" // TagRouterRuleSuffix Specify tag router suffix @@ -234,9 +244,10 @@ const ( // ForceUseTag is the tag in attachment ForceUseTag = "dubbo.force.tag" Tagkey = "dubbo.tag" - - // Attachment key in context in invoker - AttachmentKey = "attachment" + // HEALTH_ROUTE_ENABLED_KEY defines if use health router + HEALTH_ROUTE_ENABLED_KEY = "health.route.enabled" + // AttachmentKey in context in invoker + AttachmentKey = DubboCtxKey("attachment") ) const ( @@ -263,7 +274,7 @@ const ( // signature format SIGNATURE_STRING_FORMAT = "%s#%s#%s#%s" // key whether enable signature - PARAMTER_SIGNATURE_ENABLE_KEY = "param.sign" + PARAMETER_SIGNATURE_ENABLE_KEY = "param.sign" // consumer CONSUMER = "consumer" // key of access key id @@ -291,7 +302,9 @@ const ( HEALTH_CHECKER = "health.checker" // The name of the default implementation of HealthChecker DEFAULT_HEALTH_CHECKER = "default" - // The key of oustanding-request-limit + // The name of the default implementation of C + DEFAULT_CONN_CHECKER = "default" + // The key of outstanding-request-limit\ OUTSTANDING_REQUEST_COUNT_LIMIT_KEY = "outstanding.request.limit" // The key of successive-failed-request's threshold SUCCESSIVE_FAILED_REQUEST_THRESHOLD_KEY = "successive.failed.threshold" diff --git a/common/constant/version.go b/common/constant/version.go index 730224376054a36b0c7cfeda7d5ea5e7ce058618..0403e3aff7d77ed614c727addf9e9a162e60b69c 100644 --- a/common/constant/version.go +++ b/common/constant/version.go @@ -19,9 +19,9 @@ package constant const ( // Version apache/dubbo-go version - Version = "1.3.0" + Version = "1.5.5" // Name module name Name = "dubbogo" // Date release date - DATE = "2020/01/12" + DATE = "2021/01/05" ) diff --git a/common/extension/config_post_processor.go b/common/extension/config_post_processor.go new file mode 100644 index 0000000000000000000000000000000000000000..db126b744d54562a7f45b59aec26ef6e950a23a8 --- /dev/null +++ b/common/extension/config_post_processor.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 extension + +import ( + "github.com/apache/dubbo-go/config/interfaces" +) + +var ( + processors = make(map[string]interfaces.ConfigPostProcessor) +) + +// SetConfigPostProcessor registers a ConfigPostProcessor with the given name. +func SetConfigPostProcessor(name string, processor interfaces.ConfigPostProcessor) { + processors[name] = processor +} + +// GetConfigPostProcessor finds a ConfigPostProcessor by name. +func GetConfigPostProcessor(name string) interfaces.ConfigPostProcessor { + return processors[name] +} + +// GetConfigPostProcessors returns all registered instances of ConfigPostProcessor. +func GetConfigPostProcessors() []interfaces.ConfigPostProcessor { + ret := make([]interfaces.ConfigPostProcessor, 0, len(processors)) + for _, v := range processors { + ret = append(ret, v) + } + return ret +} diff --git a/common/extension/conn_checker.go b/common/extension/conn_checker.go new file mode 100644 index 0000000000000000000000000000000000000000..fbd9e34b2366f62a355e574c373dad4a290fe814 --- /dev/null +++ b/common/extension/conn_checker.go @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package extension + +import ( + "github.com/apache/dubbo-go/cluster/router" + "github.com/apache/dubbo-go/common" +) + +var ( + connCheckers = make(map[string]func(url *common.URL) router.ConnChecker) +) + +// SetHealthChecker sets the HealthChecker with @name +func SetConnChecker(name string, fcn func(_ *common.URL) router.ConnChecker) { + connCheckers[name] = fcn +} + +// GetHealthChecker gets the HealthChecker with @name +func GetConnChecker(name string, url *common.URL) router.ConnChecker { + f, ok := connCheckers[name] + if !ok || f == nil { + panic("connCheckers for " + name + " is not existing, make sure you have import the package.") + } + return connCheckers[name](url) +} diff --git a/common/extension/metadata_service.go b/common/extension/metadata_service.go index e35677d148eee121c3a6c018a128b5d372c6f2c7..08ddbc333e85fbfc328bec15aa76d2a588b11afb 100644 --- a/common/extension/metadata_service.go +++ b/common/extension/metadata_service.go @@ -26,12 +26,15 @@ import ( ) import ( + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/metadata/service" ) var ( // there will be two types: local or remote metadataServiceInsMap = make(map[string]func() (service.MetadataService, error), 2) + // remoteMetadataService + remoteMetadataService service.MetadataService ) // SetMetadataService will store the msType => creator pair @@ -48,3 +51,17 @@ func GetMetadataService(msType string) (service.MetadataService, error) { "local - github.com/apache/dubbo-go/metadata/service/inmemory, \n"+ "remote - github.com/apache/dubbo-go/metadata/service/remote", msType)) } + +// GetRemoteMetadataService will get a RemoteMetadataService instance +func GetRemoteMetadataService() (service.MetadataService, error) { + if remoteMetadataService != nil { + return remoteMetadataService, nil + } + if creator, ok := metadataServiceInsMap["remote"]; ok { + var err error + remoteMetadataService, err = creator() + return remoteMetadataService, err + } + logger.Warn("could not find the metadata service creator for metadataType: remote") + return nil, perrors.New(fmt.Sprintf("could not find the metadata service creator for metadataType: remote")) +} diff --git a/common/host_util.go b/common/host_util.go new file mode 100644 index 0000000000000000000000000000000000000000..9861c92bee15624dd79e02a6e3aba6b443dbd7da --- /dev/null +++ b/common/host_util.go @@ -0,0 +1,30 @@ +/* + * 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 common + +import gxnet "github.com/dubbogo/gost/net" + +var localIp string + +func GetLocalIp() string { + if len(localIp) != 0 { + return localIp + } + localIp, _ = gxnet.GetLocalIP() + return localIp +} diff --git a/common/host_util_test.go b/common/host_util_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ad96dde4c77667a940c27455262a6aeca921a73d --- /dev/null +++ b/common/host_util_test.go @@ -0,0 +1,30 @@ +/* + * 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 common + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestGetLocalIp(t *testing.T) { + assert.NotNil(t, GetLocalIp()) +} diff --git a/common/logger/logger.go b/common/logger/logger.go index 63eda231ddd174468602577d8b042bc0664700d0..8519543c6a143c08d34d11dc8513a7294e48773f 100644 --- a/common/logger/logger.go +++ b/common/logger/logger.go @@ -18,6 +18,7 @@ package logger import ( + "flag" "io/ioutil" "log" "os" @@ -64,8 +65,14 @@ func init() { if logger != nil { return } - logConfFile := os.Getenv(constant.APP_LOG_CONF_FILE) - err := InitLog(logConfFile) + + fs := flag.NewFlagSet("log", flag.ContinueOnError) + logConfFile := fs.String("logConf", os.Getenv(constant.APP_LOG_CONF_FILE), "default log config path") + fs.Parse(os.Args[1:]) + for len(fs.Args()) != 0 { + fs.Parse(fs.Args()[1:]) + } + err := InitLog(*logConfFile) if err != nil { log.Printf("[InitLog] warn: %v", err) } @@ -163,6 +170,7 @@ type OpsLogger interface { // SetLoggerLevel use for set logger level func (dl *DubboLogger) SetLoggerLevel(level string) { l := new(zapcore.Level) - l.Set(level) - dl.dynamicLevel.SetLevel(*l) + if err := l.Set(level); err == nil { + dl.dynamicLevel.SetLevel(*l) + } } diff --git a/common/node.go b/common/node.go index 4febd78536126c67bdc65fc09d4be47fb869ef5e..b9c1f39bf960b466276edf1e46bb7c1ecddd6f19 100644 --- a/common/node.go +++ b/common/node.go @@ -19,7 +19,7 @@ package common // Node use for process dubbo node type Node interface { - GetUrl() URL + GetUrl() *URL IsAvailable() bool Destroy() } diff --git a/common/proxy/proxy.go b/common/proxy/proxy.go index d51ce1cc1bf40a8ad25804c797eeed3b88e7d132..fd3481021c99d64843a20e8e41f7938b37efbbba 100644 --- a/common/proxy/proxy.go +++ b/common/proxy/proxy.go @@ -40,23 +40,47 @@ import ( type Proxy struct { rpc common.RPCService invoke protocol.Invoker - callBack interface{} + callback interface{} attachments map[string]string - - once sync.Once + implement ImplementFunc + once sync.Once } +type ( + // ProxyOption a function to init Proxy with options + ProxyOption func(p *Proxy) + // ImplementFunc function for proxy impl of RPCService functions + ImplementFunc func(p *Proxy, v common.RPCService) +) + var ( typError = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem()).Type() ) // NewProxy create service proxy. -func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[string]string) *Proxy { - return &Proxy{ +func NewProxy(invoke protocol.Invoker, callback interface{}, attachments map[string]string) *Proxy { + return NewProxyWithOptions(invoke, callback, attachments, + WithProxyImplementFunc(DefaultProxyImplementFunc)) +} + +// NewProxyWithOptions create service proxy with options. +func NewProxyWithOptions(invoke protocol.Invoker, callback interface{}, attachments map[string]string, opts ...ProxyOption) *Proxy { + p := &Proxy{ invoke: invoke, - callBack: callBack, + callback: callback, attachments: attachments, } + for _, opt := range opts { + opt(p) + } + return p +} + +// WithProxyImplementFunc an option function to setup proxy.ImplementFunc +func WithProxyImplementFunc(f ImplementFunc) ProxyOption { + return func(p *Proxy) { + p.implement = f + } } // Implement @@ -66,7 +90,29 @@ func NewProxy(invoke protocol.Invoker, callBack interface{}, attachments map[str // Yyy func(ctx context.Context, args []interface{}, rsp *Zzz) error // } func (p *Proxy) Implement(v common.RPCService) { + p.once.Do(func() { + p.implement(p, v) + p.rpc = v + }) +} + +// Get gets rpc service instance. +func (p *Proxy) Get() common.RPCService { + return p.rpc +} +// GetCallback gets callback. +func (p *Proxy) GetCallback() interface{} { + return p.callback +} + +// GetInvoker gets Invoker. +func (p *Proxy) GetInvoker() protocol.Invoker { + return p.invoke +} + +// DefaultProxyImplementFunc the default function for proxy impl +func DefaultProxyImplementFunc(p *Proxy, v common.RPCService) { // check parameters, incoming interface must be a elem's pointer. valueOf := reflect.ValueOf(v) logger.Debugf("[Implement] reflect.TypeOf: %s", valueOf.String()) @@ -139,13 +185,13 @@ func (p *Proxy) Implement(v common.RPCService) { inv = invocation_impl.NewRPCInvocationWithOptions(invocation_impl.WithMethodName(methodName), invocation_impl.WithArguments(inIArr), invocation_impl.WithReply(reply.Interface()), - invocation_impl.WithCallBack(p.callBack), invocation_impl.WithParameterValues(inVArr)) + invocation_impl.WithCallBack(p.callback), invocation_impl.WithParameterValues(inVArr)) for k, value := range p.attachments { inv.SetAttachments(k, value) } - // add user setAttachment. It is compatibility with previous versions. + // 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 { @@ -159,15 +205,11 @@ func (p *Proxy) Implement(v common.RPCService) { } result := p.invoke.Invoke(invCtx, inv) - if len(result.Attachments()) > 0 { - invCtx = context.WithValue(invCtx, constant.AttachmentKey, result.Attachments()) - } - err = result.Error() if err != nil { // the cause reason err = perrors.Cause(err) - // if some error happened, it should be log some info in the seperate file. + // if some error happened, it should be log some info in the separate file. if throwabler, ok := err.(java_exception.Throwabler); ok { logger.Warnf("invoke service throw exception: %v , stackTraceElements: %v", err.Error(), throwabler.GetStackTrace()) } else { @@ -220,18 +262,4 @@ func (p *Proxy) Implement(v common.RPCService) { } } - p.once.Do(func() { - p.rpc = v - }) - -} - -// Get gets rpc service instance. -func (p *Proxy) Get() common.RPCService { - return p.rpc -} - -// GetCallback gets callback. -func (p *Proxy) GetCallback() interface{} { - return p.callBack } diff --git a/common/proxy/proxy_factory.go b/common/proxy/proxy_factory.go index 117428cb253e1ad4a4ceee59aa620d7097b41a75..2e66c346b2c8ab6af0b1485f0ea867eb1af0bfc5 100644 --- a/common/proxy/proxy_factory.go +++ b/common/proxy/proxy_factory.go @@ -26,7 +26,7 @@ import ( type ProxyFactory interface { GetProxy(invoker protocol.Invoker, url *common.URL) *Proxy GetAsyncProxy(invoker protocol.Invoker, callBack interface{}, url *common.URL) *Proxy - GetInvoker(url common.URL) protocol.Invoker + GetInvoker(url *common.URL) protocol.Invoker } // Option will define a function of handling ProxyFactory diff --git a/common/proxy/proxy_factory/default.go b/common/proxy/proxy_factory/default.go index 752f3ea38193ca46a5c9dbcb8ec4b05811d19159..ff3d7955a027411b0697797d32a44023f8f70caf 100644 --- a/common/proxy/proxy_factory/default.go +++ b/common/proxy/proxy_factory/default.go @@ -72,7 +72,7 @@ func (factory *DefaultProxyFactory) GetAsyncProxy(invoker protocol.Invoker, call } // GetInvoker gets a invoker -func (factory *DefaultProxyFactory) GetInvoker(url common.URL) protocol.Invoker { +func (factory *DefaultProxyFactory) GetInvoker(url *common.URL) protocol.Invoker { return &ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), } @@ -88,9 +88,8 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati result := &protocol.RPCResult{} result.SetAttachments(invocation.Attachments()) - url := pi.GetUrl() //get providerUrl. The origin url may be is registry URL. - url = *getProviderURL(&url) + url := getProviderURL(pi.GetUrl()) methodName := invocation.MethodName() proto := url.Protocol @@ -98,7 +97,7 @@ func (pi *ProxyInvoker) Invoke(ctx context.Context, invocation protocol.Invocati args := invocation.Arguments() // get service - svc := common.ServiceMap.GetService(proto, path) + svc := common.ServiceMap.GetServiceByServiceKey(proto, url.ServiceKey()) if svc == nil { logger.Errorf("cannot find service [%s] in %s", path, proto) result.SetError(perrors.Errorf("cannot find service [%s] in %s", path, proto)) diff --git a/common/proxy/proxy_factory/default_test.go b/common/proxy/proxy_factory/default_test.go index 99d5c020f4bfd0254ddddc65e78bcae2e252b6be..4002ab95849b79aef26c8d94822e731cd6f229e3 100644 --- a/common/proxy/proxy_factory/default_test.go +++ b/common/proxy/proxy_factory/default_test.go @@ -34,7 +34,7 @@ import ( func TestGetProxy(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() - proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(*url), url) + proxy := proxyFactory.GetProxy(protocol.NewBaseInvoker(url), url) assert.NotNil(t, proxy) } @@ -49,13 +49,13 @@ func TestGetAsyncProxy(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() async := &TestAsync{} - proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(*url), async.CallBack, url) + proxy := proxyFactory.GetAsyncProxy(protocol.NewBaseInvoker(url), async.CallBack, url) assert.NotNil(t, proxy) } func TestGetInvoker(t *testing.T) { proxyFactory := NewDefaultProxyFactory() url := common.NewURLWithOptions() - invoker := proxyFactory.GetInvoker(*url) + invoker := proxyFactory.GetInvoker(url) assert.True(t, invoker.IsAvailable()) } diff --git a/common/proxy/proxy_test.go b/common/proxy/proxy_test.go index c6066157fd8d2147fd3a319c8d48fdd910752711..c335bf67834074607619edc734f0e0b3c2d72124 100644 --- a/common/proxy/proxy_test.go +++ b/common/proxy/proxy_test.go @@ -19,12 +19,12 @@ package proxy import ( "context" + "fmt" "reflect" "testing" ) import ( - "github.com/apache/dubbo-go/protocol/invocation" perrors "github.com/pkg/errors" "github.com/stretchr/testify/assert" ) @@ -34,6 +34,7 @@ import ( "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/dubbo/hessian2" + "github.com/apache/dubbo-go/protocol/invocation" ) type TestService struct { @@ -58,7 +59,7 @@ func (s *TestServiceInt) Reference() string { func TestProxyImplement(t *testing.T) { - invoker := protocol.NewBaseInvoker(common.URL{}) + invoker := protocol.NewBaseInvoker(&common.URL{}) p := NewProxy(invoker, nil, map[string]string{constant.ASYNC_KEY: "false"}) s := &TestService{} p.Implement(s) @@ -126,15 +127,15 @@ func TestProxyImplement(t *testing.T) { func TestProxyImplementForContext(t *testing.T) { invoker := &TestProxyInvoker{ - BaseInvoker: *protocol.NewBaseInvoker(common.URL{}), + 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) + attachments1 := make(map[string]interface{}, 4) + attachments1["k1"] = "v1" + attachments1["k2"] = "v2" + context := context.WithValue(context.Background(), constant.AttachmentKey, attachments1) r, err := p.Get().(*TestService).MethodSix(context, "xxx") v1 := r.(map[string]interface{}) assert.NoError(t, err) @@ -145,11 +146,14 @@ type TestProxyInvoker struct { protocol.BaseInvoker } -func (bi *TestProxyInvoker) Invoke(context context.Context, inv protocol.Invocation) protocol.Result { +func (bi *TestProxyInvoker) Invoke(_ context.Context, inv protocol.Invocation) protocol.Result { rpcInv := inv.(*invocation.RPCInvocation) mapV := inv.Attachments() mapV["TestProxyInvoker"] = "TestProxyInvokerValue" - hessian2.ReflectResponse(mapV, rpcInv.Reply()) + if err := hessian2.ReflectResponse(mapV, rpcInv.Reply()); err != nil { + fmt.Printf("hessian2.ReflectResponse(mapV:%v) = error:%v", mapV, err) + } + return &protocol.RPCResult{ Rest: inv.Arguments(), } diff --git a/common/rpc_service.go b/common/rpc_service.go index 5ed4df6d717db31021d8585a88d5576f59756f8a..30d72c1420f254182f5d2f16c6fc9444881f0b71 100644 --- a/common/rpc_service.go +++ b/common/rpc_service.go @@ -106,10 +106,10 @@ func (m *MethodType) ReplyType() reflect.Type { return m.replyType } -// SuiteContext tranfers @ctx to reflect.Value type or get it from @m.ctxType. +// SuiteContext transfers @ctx to reflect.Value type or get it from @m.ctxType. func (m *MethodType) SuiteContext(ctx context.Context) reflect.Value { - if contextv := reflect.ValueOf(ctx); contextv.IsValid() { - return contextv + if ctxV := reflect.ValueOf(ctx); ctxV.IsValid() { + return ctxV } return reflect.Zero(m.ctxType) } @@ -156,12 +156,18 @@ type serviceMap struct { interfaceMap map[string][]*Service // interface -> service } -// GetService gets a service defination by protocol and name -func (sm *serviceMap) GetService(protocol, name string) *Service { +// GetService gets a service definition by protocol and name +func (sm *serviceMap) GetService(protocol, interfaceName, group, version string) *Service { + serviceKey := ServiceKey(interfaceName, group, version) + return sm.GetServiceByServiceKey(protocol, serviceKey) +} + +// GetService gets a service definition by protocol and service key +func (sm *serviceMap) GetServiceByServiceKey(protocol, serviceKey string) *Service { sm.mutex.RLock() defer sm.mutex.RUnlock() if s, ok := sm.serviceMap[protocol]; ok { - if srv, ok := s[name]; ok { + if srv, ok := s[serviceKey]; ok { return srv } return nil @@ -180,7 +186,7 @@ func (sm *serviceMap) GetInterface(interfaceName string) []*Service { } // Register registers a service by @interfaceName and @protocol -func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) (string, error) { +func (sm *serviceMap) Register(interfaceName, protocol, group, version string, rcvr RPCService) (string, error) { if sm.serviceMap[protocol] == nil { sm.serviceMap[protocol] = make(map[string]*Service) } @@ -203,8 +209,8 @@ func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) return "", perrors.New(s) } - sname = rcvr.Reference() - if server := sm.GetService(protocol, sname); server != nil { + sname = ServiceKey(interfaceName, group, version) + if server := sm.GetService(protocol, interfaceName, group, version); server != nil { return "", perrors.New("service already defined: " + sname) } s.name = sname @@ -228,9 +234,9 @@ func (sm *serviceMap) Register(interfaceName, protocol string, rcvr RPCService) } // UnRegister cancels a service by @interfaceName, @protocol and @serviceId -func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) error { - if protocol == "" || serviceId == "" { - return perrors.New("protocol or serviceName is nil") +func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceKey string) error { + if protocol == "" || serviceKey == "" { + return perrors.New("protocol or serviceKey is nil") } var ( @@ -248,9 +254,9 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro if !ok { return perrors.New("no services for " + protocol) } - s, ok := svcs[serviceId] + s, ok := svcs[serviceKey] if !ok { - return perrors.New("no service for " + serviceId) + return perrors.New("no service for " + serviceKey) } svrs, ok = sm.interfaceMap[interfaceName] if !ok { @@ -276,7 +282,7 @@ func (sm *serviceMap) UnRegister(interfaceName, protocol, serviceId string) erro sm.interfaceMap[interfaceName] = append(sm.interfaceMap[interfaceName], svrs[i]) } } - delete(svcs, serviceId) + delete(svcs, serviceKey) if len(sm.serviceMap[protocol]) == 0 { delete(sm.serviceMap, protocol) } diff --git a/common/rpc_service_test.go b/common/rpc_service_test.go index 7c4eb421d7ed0e5e0306eb1a1a2ff5ac31c29b6d..e8bd3933da89b0a6c607f776eae1bb8603f8f467 100644 --- a/common/rpc_service_test.go +++ b/common/rpc_service_test.go @@ -27,6 +27,10 @@ import ( "github.com/stretchr/testify/assert" ) +import ( + "github.com/apache/dubbo-go/common/constant" +) + const ( referenceTestPath = "com.test.Path" referenceTestPathDistinct = "com.test.Path1" @@ -85,23 +89,23 @@ func TestServiceMapRegister(t *testing.T) { // lowercase s0 := &testService{} // methods, err := ServiceMap.Register("testporotocol", s0) - _, err := ServiceMap.Register(testInterfaceName, "testporotocol", s0) + _, err := ServiceMap.Register(testInterfaceName, "testporotocol", "", "v0", s0) assert.EqualError(t, err, "type testService is not exported") // succ s := &TestService{} - methods, err := ServiceMap.Register(testInterfaceName, "testporotocol", s) + methods, err := ServiceMap.Register(testInterfaceName, "testporotocol", "", "v1", s) assert.NoError(t, err) assert.Equal(t, "MethodOne,MethodThree,methodTwo", methods) // repeat - _, err = ServiceMap.Register(testInterfaceName, "testporotocol", s) - assert.EqualError(t, err, "service already defined: com.test.Path") + _, err = ServiceMap.Register(testInterfaceName, "testporotocol", "", "v1", s) + assert.EqualError(t, err, "service already defined: testService:v1") // no method s1 := &TestService1{} - _, err = ServiceMap.Register(testInterfaceName, "testporotocol", s1) - assert.EqualError(t, err, "type com.test.Path1 has no exported methods of suitable type") + _, err = ServiceMap.Register(testInterfaceName, "testporotocol", "", "v2", s1) + assert.EqualError(t, err, "type testService:v2 has no exported methods of suitable type") ServiceMap = &serviceMap{ serviceMap: make(map[string]map[string]*Service), @@ -111,35 +115,34 @@ func TestServiceMapRegister(t *testing.T) { func TestServiceMapUnRegister(t *testing.T) { s := &TestService{} - _, err := ServiceMap.Register("TestService", testProtocol, s) + _, err := ServiceMap.Register("TestService", testProtocol, "", "v1", s) assert.NoError(t, err) - assert.NotNil(t, ServiceMap.GetService(testProtocol, referenceTestPath)) + assert.NotNil(t, ServiceMap.GetService(testProtocol, "TestService", "", "v1")) assert.Equal(t, 1, len(ServiceMap.GetInterface("TestService"))) - err = ServiceMap.UnRegister("", "", referenceTestPath) - assert.EqualError(t, err, "protocol or serviceName is nil") + err = ServiceMap.UnRegister("", "", ServiceKey("TestService", "", "v1")) + assert.EqualError(t, err, "protocol or serviceKey is nil") - err = ServiceMap.UnRegister("", "protocol", referenceTestPath) + err = ServiceMap.UnRegister("", "protocol", ServiceKey("TestService", "", "v1")) assert.EqualError(t, err, "no services for protocol") - err = ServiceMap.UnRegister("", testProtocol, referenceTestPathDistinct) - assert.EqualError(t, err, "no service for com.test.Path1") + err = ServiceMap.UnRegister("", testProtocol, ServiceKey("TestService", "", "v0")) + assert.EqualError(t, err, "no service for TestService:v0") - // succ - err = ServiceMap.UnRegister("TestService", testProtocol, referenceTestPath) + // success + err = ServiceMap.UnRegister("TestService", testProtocol, ServiceKey("TestService", "", "v1")) assert.NoError(t, err) } func TestMethodTypeSuiteContext(t *testing.T) { mt := &MethodType{ctxType: reflect.TypeOf(context.TODO())} - ctx := context.WithValue(context.Background(), "key", "value") + ctx := context.Background() + key := constant.DubboCtxKey("key") + ctx = context.WithValue(ctx, key, "value") assert.Equal(t, reflect.ValueOf(ctx), mt.SuiteContext(ctx)) - - assert.Equal(t, reflect.Zero(mt.ctxType), mt.SuiteContext(nil)) } func TestSuiteMethod(t *testing.T) { - s := &TestService{} method, ok := reflect.TypeOf(s).MethodByName("MethodOne") assert.True(t, ok) diff --git a/common/url.go b/common/url.go index 5a3e57406bf221a341d9a3ea120943144a35aca0..80b85fcf42a924f4707c9ab31e2ec263c671e55a 100644 --- a/common/url.go +++ b/common/url.go @@ -26,9 +26,11 @@ import ( "net/url" "strconv" "strings" + "sync" ) import ( + cm "github.com/Workiva/go-datastructures/common" gxset "github.com/dubbogo/gost/container/set" "github.com/jinzhu/copier" perrors "github.com/pkg/errors" @@ -62,8 +64,14 @@ var ( DubboNodes = [...]string{"consumers", "configurators", "routers", "providers"} // DubboRole Dubbo service role DubboRole = [...]string{"consumer", "", "routers", "provider"} + // CompareURLEqualFunc compare two url is equal + compareURLEqualFunc CompareURLEqualFunc ) +func init() { + compareURLEqualFunc = defaultCompareURLEqual +} + // nolint type RoleType int @@ -77,22 +85,39 @@ func (t RoleType) Role() string { } type baseUrl struct { - Protocol string - Location string // ip+port - Ip string - Port string - params url.Values + Protocol string + Location string // ip+port + Ip string + Port string + PrimitiveURL string } -// URL is not thread-safe. +// noCopy may be embedded into structs which must not be copied +// after the first use. +// +// See https://golang.org/issues/8005#issuecomment-190753527 +// for details. +type noCopy struct{} + +// Lock is a no-op used by -copylocks checker from `go vet`. +func (*noCopy) Lock() {} +func (*noCopy) Unlock() {} + +// URL thread-safe. but this url should not be copied. // we fail to define this struct to be immutable object. // but, those method which will update the URL, including SetParam, SetParams // are only allowed to be invoked in creating URL instance // Please keep in mind that this struct is immutable after it has been created and initialized. type URL struct { + noCopy noCopy + baseUrl - Path string // like /com.ikurento.dubbo.UserProvider3 + //url.Values is not safe map, add to avoid concurrent map read and map write error + paramsLock sync.RWMutex + params url.Values + + Path string // like /com.ikurento.dubbo.UserProvider Username string Password string Methods []string @@ -102,80 +127,80 @@ type URL struct { // Option accepts url // Option will define a function of handling URL -type option func(*URL) +type Option func(*URL) // WithUsername sets username for url -func WithUsername(username string) option { +func WithUsername(username string) Option { return func(url *URL) { url.Username = username } } // WithPassword sets password for url -func WithPassword(pwd string) option { +func WithPassword(pwd string) Option { return func(url *URL) { url.Password = pwd } } // WithMethods sets methods for url -func WithMethods(methods []string) option { +func WithMethods(methods []string) Option { return func(url *URL) { url.Methods = methods } } // WithParams sets params for url -func WithParams(params url.Values) option { +func WithParams(params url.Values) Option { return func(url *URL) { url.params = params } } // WithParamsValue sets params field for url -func WithParamsValue(key, val string) option { +func WithParamsValue(key, val string) Option { return func(url *URL) { url.SetParam(key, val) } } // WithProtocol sets protocol for url -func WithProtocol(proto string) option { +func WithProtocol(proto string) Option { return func(url *URL) { url.Protocol = proto } } // WithIp sets ip for url -func WithIp(ip string) option { +func WithIp(ip string) Option { return func(url *URL) { url.Ip = ip } } // WithPort sets port for url -func WithPort(port string) option { +func WithPort(port string) Option { return func(url *URL) { url.Port = port } } // WithPath sets path for url -func WithPath(path string) option { +func WithPath(path string) Option { return func(url *URL) { url.Path = "/" + strings.TrimPrefix(path, "/") } } // WithLocation sets location for url -func WithLocation(location string) option { +func WithLocation(location string) Option { return func(url *URL) { url.Location = location } } // WithToken sets token for url -func WithToken(token string) option { +func WithToken(token string) Option { return func(url *URL) { if len(token) > 0 { value := token @@ -193,33 +218,26 @@ func WithToken(token string) option { } // NewURLWithOptions will create a new url with options -func NewURLWithOptions(opts ...option) *URL { - url := &URL{} +func NewURLWithOptions(opts ...Option) *URL { + newURL := &URL{} for _, opt := range opts { - opt(url) + opt(newURL) } - url.Location = url.Ip + ":" + url.Port - return url + newURL.Location = newURL.Ip + ":" + newURL.Port + return newURL } // NewURL will create a new url // the urlString should not be empty -func NewURL(urlString string, opts ...option) (URL, error) { - var ( - err error - rawUrlString string - serviceUrl *url.URL - s = URL{baseUrl: baseUrl{}} - ) - - // new a null instance +func NewURL(urlString string, opts ...Option) (*URL, error) { + s := URL{baseUrl: baseUrl{}} if urlString == "" { - return s, nil + return &s, nil } - rawUrlString, err = url.QueryUnescape(urlString) + rawUrlString, err := url.QueryUnescape(urlString) if err != nil { - return s, perrors.Errorf("url.QueryUnescape(%s), error{%v}", urlString, err) + return &s, perrors.Errorf("url.QueryUnescape(%s), error{%v}", urlString, err) } // rawUrlString = "//" + rawUrlString @@ -230,14 +248,15 @@ func NewURL(urlString string, opts ...option) (URL, error) { } rawUrlString = t.Protocol + "://" + rawUrlString } - serviceUrl, err = url.Parse(rawUrlString) - if err != nil { - return s, perrors.Errorf("url.Parse(url string{%s}), error{%v}", rawUrlString, err) + + serviceUrl, urlParseErr := url.Parse(rawUrlString) + if urlParseErr != nil { + return &s, perrors.Errorf("url.Parse(url string{%s}), error{%v}", rawUrlString, err) } s.params, err = url.ParseQuery(serviceUrl.RawQuery) if err != nil { - return s, perrors.Errorf("url.ParseQuery(raw url string{%s}), error{%v}", serviceUrl.RawQuery, err) + return &s, perrors.Errorf("url.ParseQuery(raw url string{%s}), error{%v}", serviceUrl.RawQuery, err) } s.PrimitiveURL = urlString @@ -249,25 +268,29 @@ func NewURL(urlString string, opts ...option) (URL, error) { if strings.Contains(s.Location, ":") { s.Ip, s.Port, err = net.SplitHostPort(s.Location) if err != nil { - return s, perrors.Errorf("net.SplitHostPort(url.Host{%s}), error{%v}", s.Location, err) + return &s, perrors.Errorf("net.SplitHostPort(url.Host{%s}), error{%v}", s.Location, err) } } for _, opt := range opts { opt(&s) } - return s, nil + return &s, nil } // URLEqual judge @url and @c is equal or not. -func (c URL) URLEqual(url URL) bool { - c.Ip = "" - c.Port = "" - url.Ip = "" - url.Port = "" - cGroup := c.GetParam(constant.GROUP_KEY, "") - urlGroup := url.GetParam(constant.GROUP_KEY, "") - cKey := c.Key() - urlKey := url.Key() +func (c *URL) URLEqual(url *URL) bool { + tmpC := c.Clone() + tmpC.Ip = "" + tmpC.Port = "" + + tmpUrl := url.Clone() + tmpUrl.Ip = "" + tmpUrl.Port = "" + + cGroup := tmpC.GetParam(constant.GROUP_KEY, "") + urlGroup := tmpUrl.GetParam(constant.GROUP_KEY, "") + cKey := tmpC.Key() + urlKey := tmpUrl.Key() if cGroup == constant.ANY_VALUE { cKey = strings.Replace(cKey, "group=*", "group="+urlGroup, 1) @@ -281,12 +304,12 @@ func (c URL) URLEqual(url URL) bool { } // 2. if url contains enabled key, should be true, or * - if url.GetParam(constant.ENABLED_KEY, "true") != "true" && url.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE { + if tmpUrl.GetParam(constant.ENABLED_KEY, "true") != "true" && tmpUrl.GetParam(constant.ENABLED_KEY, "") != constant.ANY_VALUE { return false } // TODO :may need add interface key any value condition - return isMatchCategory(url.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), c.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) + return isMatchCategory(tmpUrl.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY), tmpC.GetParam(constant.CATEGORY_KEY, constant.DEFAULT_CATEGORY)) } func isMatchCategory(category1 string, category2 string) bool { @@ -301,31 +324,28 @@ func isMatchCategory(category1 string, category2 string) bool { } } -func (c URL) String() string { +func (c *URL) String() string { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() var buf strings.Builder if len(c.Username) == 0 && len(c.Password) == 0 { - buf.WriteString(fmt.Sprintf( - "%s://%s:%s%s?", - c.Protocol, c.Ip, c.Port, c.Path)) + buf.WriteString(fmt.Sprintf("%s://%s:%s%s?", c.Protocol, c.Ip, c.Port, c.Path)) } else { - buf.WriteString(fmt.Sprintf( - "%s://%s:%s@%s:%s%s?", - c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)) + buf.WriteString(fmt.Sprintf("%s://%s:%s@%s:%s%s?", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Path)) } buf.WriteString(c.params.Encode()) return buf.String() } // Key gets key -func (c URL) Key() string { - buildString := fmt.Sprintf( - "%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s", +func (c *URL) Key() string { + buildString := fmt.Sprintf("%s://%s:%s@%s:%s/?interface=%s&group=%s&version=%s", c.Protocol, c.Username, c.Password, c.Ip, c.Port, c.Service(), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, "")) return buildString } // ServiceKey gets a unique key of a service. -func (c URL) ServiceKey() string { +func (c *URL) ServiceKey() string { return ServiceKey(c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")), c.GetParam(constant.GROUP_KEY, ""), c.GetParam(constant.VERSION_KEY, "")) } @@ -379,7 +399,7 @@ func (c *URL) EncodedServiceKey() string { } // Service gets service -func (c URL) Service() string { +func (c *URL) Service() string { service := c.GetParam(constant.INTERFACE_KEY, strings.TrimPrefix(c.Path, "/")) if service != "" { return service @@ -393,34 +413,57 @@ func (c URL) Service() string { } // AddParam will add the key-value pair -// Not thread-safe -// think twice before using it. func (c *URL) AddParam(key string, value string) { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() + if c.params == nil { + c.params = url.Values{} + } 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) { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() 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 // usually it should only be invoked when you want to initialized an url func (c *URL) SetParam(key string, value string) { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() + if c.params == nil { + c.params = url.Values{} + } c.params.Set(key, value) } +// DelParam will delete the given key from the url +func (c *URL) DelParam(key string) { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() + if c.params != nil { + c.params.Del(key) + } +} + +// ReplaceParams will replace the URL.params +// usually it should only be invoked when you want to modify an url, such as MergeURL +func (c *URL) ReplaceParams(param url.Values) { + c.paramsLock.Lock() + defer c.paramsLock.Unlock() + c.params = param +} + // RangeParams will iterate the params -// it's not thread-safe func (c *URL) RangeParams(f func(key, value string) bool) { + c.paramsLock.RLock() + defer c.paramsLock.RUnlock() for k, v := range c.params { if !f(k, v[0]) { break @@ -429,28 +472,35 @@ func (c *URL) RangeParams(f func(key, value string) bool) { } // GetParam gets value by key -func (c URL) GetParam(s string, d string) string { - r := c.params.Get(s) +func (c *URL) GetParam(s string, d string) string { + c.paramsLock.RLock() + defer c.paramsLock.RUnlock() + + var r string + if len(c.params) > 0 { + r = c.params.Get(s) + } if len(r) == 0 { r = d } + return r } // GetParams gets values -func (c URL) GetParams() url.Values { +func (c *URL) GetParams() url.Values { return c.params } // GetParamAndDecoded gets values and decode -func (c URL) GetParamAndDecoded(key string) (string, error) { +func (c *URL) GetParamAndDecoded(key string) (string, error) { ruleDec, err := base64.URLEncoding.DecodeString(c.GetParam(key, "")) value := string(ruleDec) return value, err } // GetRawParam gets raw param -func (c URL) GetRawParam(key string) string { +func (c *URL) GetRawParam(key string) string { switch key { case PROTOCOL: return c.Protocol @@ -470,7 +520,7 @@ func (c URL) GetRawParam(key string) string { } // GetParamBool judge whether @key exists or not -func (c URL) GetParamBool(key string, d bool) bool { +func (c *URL) GetParamBool(key string, d bool) bool { r, err := strconv.ParseBool(c.GetParam(key, "")) if err != nil { return d @@ -478,26 +528,53 @@ func (c URL) GetParamBool(key string, d bool) bool { return r } -// GetParamInt gets int value by @key -func (c URL) GetParamInt(key string, d int64) int64 { - r, err := strconv.Atoi(c.GetParam(key, "")) - if r == 0 || err != nil { +// GetParamInt gets int64 value by @key +func (c *URL) GetParamInt(key string, d int64) int64 { + r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 64) + if err != nil { + return d + } + return r +} + +// GetParamInt32 gets int32 value by @key +func (c *URL) GetParamInt32(key string, d int32) int32 { + r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 32) + if err != nil { + return d + } + return int32(r) +} + +// GetParamByIntValue gets int value by @key +func (c *URL) GetParamByIntValue(key string, d int) int { + r, err := strconv.ParseInt(c.GetParam(key, ""), 10, 0) + if err != nil { return d } - return int64(r) + return int(r) } // GetMethodParamInt gets int method param -func (c URL) GetMethodParamInt(method string, key string, d int64) int64 { - r, err := strconv.Atoi(c.GetParam("methods."+method+"."+key, "")) - if r == 0 || err != nil { +func (c *URL) GetMethodParamInt(method string, key string, d int64) int64 { + r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 64) + if err != nil { + return d + } + return r +} + +// GetMethodParamIntValue gets int method param +func (c *URL) GetMethodParamIntValue(method string, key string, d int) int { + r, err := strconv.ParseInt(c.GetParam("methods."+method+"."+key, ""), 10, 0) + if err != nil { return d } - return int64(r) + return int(r) } // GetMethodParamInt64 gets int64 method param -func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { +func (c *URL) GetMethodParamInt64(method string, key string, d int64) int64 { r := c.GetMethodParamInt(method, key, math.MinInt64) if r == math.MinInt64 { return c.GetParamInt(key, d) @@ -506,7 +583,7 @@ func (c URL) GetMethodParamInt64(method string, key string, d int64) int64 { } // GetMethodParam gets method param -func (c URL) GetMethodParam(method string, key string, d string) string { +func (c *URL) GetMethodParam(method string, key string, d string) string { r := c.GetParam("methods."+method+"."+key, "") if r == "" { r = d @@ -515,7 +592,7 @@ func (c URL) GetMethodParam(method string, key string, d string) string { } // GetMethodParamBool judge whether @method param exists or not -func (c URL) GetMethodParamBool(method string, key string, d bool) bool { +func (c *URL) GetMethodParamBool(method string, key string, d bool) bool { r := c.GetParamBool("methods."+method+"."+key, d) return r } @@ -531,7 +608,7 @@ func (c *URL) SetParams(m url.Values) { } // ToMap transfer URL to Map -func (c URL) ToMap() map[string]string { +func (c *URL) ToMap() map[string]string { paramsMap := make(map[string]string) c.RangeParams(func(key, value string) bool { @@ -582,22 +659,26 @@ func (c URL) ToMap() map[string]string { // You should notice that the value of b1 is v2, not v4. // due to URL is not thread-safe, so this method is not thread-safe func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { + // After Clone, it is a new url that there is no thread safe issue. mergedUrl := serviceUrl.Clone() - + params := mergedUrl.GetParams() // iterator the referenceUrl if serviceUrl not have the key ,merge in - referenceUrl.RangeParams(func(key, value string) bool { + // referenceUrl usually will not changed. so change RangeParams to GetParams to avoid the string value copy. + for key, value := range referenceUrl.GetParams() { if v := mergedUrl.GetParam(key, ""); len(v) == 0 { - mergedUrl.SetParam(key, value) + if len(value) > 0 { + params[key] = value + } } - return true - }) + } + // loadBalance,cluster,retries strategy config - methodConfigMergeFcn := mergeNormalParam(mergedUrl, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY}) + methodConfigMergeFcn := mergeNormalParam(params, referenceUrl, []string{constant.LOADBALANCE_KEY, constant.CLUSTER_KEY, constant.RETRIES_KEY, constant.TIMEOUT_KEY}) // remote timestamp if v := serviceUrl.GetParam(constant.TIMESTAMP_KEY, ""); len(v) > 0 { - mergedUrl.SetParam(constant.REMOTE_TIMESTAMP_KEY, v) - mergedUrl.SetParam(constant.TIMESTAMP_KEY, referenceUrl.GetParam(constant.TIMESTAMP_KEY, "")) + params[constant.REMOTE_TIMESTAMP_KEY] = []string{v} + params[constant.TIMESTAMP_KEY] = []string{referenceUrl.GetParam(constant.TIMESTAMP_KEY, "")} } // finally execute methodConfigMergeFcn @@ -606,33 +687,54 @@ func MergeUrl(serviceUrl *URL, referenceUrl *URL) *URL { fcn("methods." + method) } } - + // In this way, we will raise some performance. + mergedUrl.ReplaceParams(params) return mergedUrl } // Clone will copy the url func (c *URL) Clone() *URL { - newUrl := &URL{} - copier.Copy(newUrl, c) - newUrl.params = url.Values{} + newURL := &URL{} + if err := copier.Copy(newURL, c); err != nil { + // this is impossible + return newURL + } + newURL.params = url.Values{} c.RangeParams(func(key, value string) bool { - newUrl.SetParam(key, value) + newURL.SetParam(key, value) return true }) - return newUrl + + return newURL } func (c *URL) CloneExceptParams(excludeParams *gxset.HashSet) *URL { - newUrl := &URL{} - copier.Copy(newUrl, c) - newUrl.params = url.Values{} + newURL := &URL{} + if err := copier.Copy(newURL, c); err != nil { + // this is impossible + return newURL + } + newURL.params = url.Values{} c.RangeParams(func(key, value string) bool { if !excludeParams.Contains(key) { - newUrl.SetParam(key, value) + newURL.SetParam(key, value) } return true }) - return newUrl + return newURL +} + +func (c *URL) Compare(comp cm.Comparator) int { + a := c.String() + b := comp.(*URL).String() + switch { + case a > b: + return 1 + case a < b: + return -1 + default: + return 0 + } } // Copy url based on the reserved parameter's keys. @@ -658,7 +760,10 @@ 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 { +func IsEquals(left *URL, right *URL, excludes ...string) bool { + if (left == nil && right != nil) || (right == nil && left != nil) { + return false + } if left.Ip != right.Ip || left.Port != right.Port { return false } @@ -685,15 +790,15 @@ func IsEquals(left URL, right URL, excludes ...string) bool { return true } -func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []func(method string) { +func mergeNormalParam(params url.Values, referenceUrl *URL, paramKeys []string) []func(method string) { methodConfigMergeFcn := make([]func(method string), 0, len(paramKeys)) for _, paramKey := range paramKeys { if v := referenceUrl.GetParam(paramKey, ""); len(v) > 0 { - mergedUrl.SetParam(paramKey, v) + params[paramKey] = []string{v} } methodConfigMergeFcn = append(methodConfigMergeFcn, func(method string) { if v := referenceUrl.GetParam(method+"."+paramKey, ""); len(v) > 0 { - mergedUrl.SetParam(method+"."+paramKey, v) + params[method+"."+paramKey] = []string{v} } }) } @@ -702,7 +807,7 @@ func mergeNormalParam(mergedUrl *URL, referenceUrl *URL, paramKeys []string) []f // URLSlice will be used to sort URL instance // Instances will be order by URL.String() -type URLSlice []URL +type URLSlice []*URL // nolint func (s URLSlice) Len() int { @@ -718,3 +823,17 @@ func (s URLSlice) Less(i, j int) bool { func (s URLSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } + +type CompareURLEqualFunc func(l *URL, r *URL, excludeParam ...string) bool + +func defaultCompareURLEqual(l *URL, r *URL, excludeParam ...string) bool { + return IsEquals(l, r, excludeParam...) +} + +func SetCompareURLEqualFunc(f CompareURLEqualFunc) { + compareURLEqualFunc = f +} + +func GetCompareURLEqualFunc() CompareURLEqualFunc { + return compareURLEqualFunc +} diff --git a/common/url_test.go b/common/url_test.go index 6845190a7362571ebbd4738bd146c94f6d644253..c645f1a046777ddf298b54ae5cb19124dd7808c1 100644 --- a/common/url_test.go +++ b/common/url_test.go @@ -68,12 +68,17 @@ func TestURL(t *testing.T) { "side=provider&timeout=3000×tamp=1556509797245") assert.NoError(t, err) + urlInst := URL{} + urlInst.noCopy.Lock() + urlInst.SetParam("hello", "world") + urlInst.noCopy.Unlock() + assert.Equal(t, "/com.ikurento.user.UserProvider", u.Path) assert.Equal(t, "127.0.0.1:20000", u.Location) assert.Equal(t, "dubbo", u.Protocol) assert.Equal(t, loopbackAddress, u.Ip) assert.Equal(t, "20000", u.Port) - assert.Equal(t, URL{}.Methods, u.Methods) + assert.Equal(t, urlInst.Methods, u.Methods) assert.Equal(t, "", u.Username) assert.Equal(t, "", u.Password) assert.Equal(t, "anyhost=true&application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-"+ @@ -156,7 +161,10 @@ func TestURLEqual(t *testing.T) { func TestURLGetParam(t *testing.T) { params := url.Values{} params.Set("key", "value") - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v := u.GetParam("key", "default") assert.Equal(t, "value", v) @@ -167,10 +175,28 @@ func TestURLGetParam(t *testing.T) { func TestURLGetParamInt(t *testing.T) { params := url.Values{} - params.Set("key", "3") - u := URL{baseUrl: baseUrl{params: params}} + params.Set("key", "value") + + u := URL{} + u.SetParams(params) + v := u.GetParamInt("key", 1) - assert.Equal(t, int64(3), v) + assert.Equal(t, int64(1), v) + + u = URL{} + v = u.GetParamInt("key", 1) + assert.Equal(t, int64(1), v) +} + +func TestURLGetParamIntValue(t *testing.T) { + params := url.Values{} + params.Set("key", "0") + + u := URL{} + u.SetParams(params) + + v := u.GetParamInt("key", 1) + assert.Equal(t, int64(0), v) u = URL{} v = u.GetParamInt("key", 1) @@ -180,7 +206,10 @@ func TestURLGetParamInt(t *testing.T) { func TestURLGetParamBool(t *testing.T) { params := url.Values{} params.Set("force", "true") - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v := u.GetParamBool("force", false) assert.Equal(t, true, v) @@ -193,7 +222,10 @@ func TestURLGetParamAndDecoded(t *testing.T) { rule := "host = 2.2.2.2,1.1.1.1,3.3.3.3 & host !=1.1.1.1 => host = 1.2.3.4" params := url.Values{} params.Set("rule", base64.URLEncoding.EncodeToString([]byte(rule))) - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v, _ := u.GetParamAndDecoded("rule") assert.Equal(t, rule, v) } @@ -230,7 +262,10 @@ func TestURLToMap(t *testing.T) { func TestURLGetMethodParamInt(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.timeout", "3") - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v := u.GetMethodParamInt("GetValue", "timeout", 1) assert.Equal(t, int64(3), v) @@ -242,7 +277,10 @@ func TestURLGetMethodParamInt(t *testing.T) { func TestURLGetMethodParam(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.timeout", "3s") - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v := u.GetMethodParam("GetValue", "timeout", "1s") assert.Equal(t, "3s", v) @@ -254,7 +292,10 @@ func TestURLGetMethodParam(t *testing.T) { func TestURLGetMethodParamBool(t *testing.T) { params := url.Values{} params.Set("methods.GetValue.async", "true") - u := URL{baseUrl: baseUrl{params: params}} + + u := URL{} + u.SetParams(params) + v := u.GetMethodParamBool("GetValue", "async", false) assert.Equal(t, true, v) @@ -277,7 +318,7 @@ func TestMergeUrl(t *testing.T) { referenceUrl, _ := NewURL("mock1://127.0.0.1:1111", WithParams(referenceUrlParams), WithMethods([]string{"testMethod"})) serviceUrl, _ := NewURL("mock2://127.0.0.1:20000", WithParams(serviceUrlParams)) - mergedUrl := MergeUrl(&serviceUrl, &referenceUrl) + mergedUrl := MergeUrl(serviceUrl, referenceUrl) assert.Equal(t, "random", mergedUrl.GetParam(constant.CLUSTER_KEY, "")) assert.Equal(t, "1", mergedUrl.GetParam("test2", "")) assert.Equal(t, "1", mergedUrl.GetParam("test3", "")) @@ -295,6 +336,16 @@ func TestURLSetParams(t *testing.T) { assert.Equal(t, "2.6.0", u1.GetParam("version", "")) } +func TestURLReplaceParams(t *testing.T) { + u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") + assert.NoError(t, err) + params := url.Values{} + params.Set("key", "3") + u1.ReplaceParams(params) + assert.Equal(t, "3", u1.GetParam("key", "")) + assert.Equal(t, "", u1.GetParam("version", "")) +} + func TestClone(t *testing.T) { u1, err := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=&version=2.6.0&configVersion=1.0") assert.NoError(t, err) @@ -317,5 +368,56 @@ func TestColonSeparatedKey(t *testing.T) { assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+":version1:group1") u1.SetParam(constant.VERSION_KEY, "") assert.Equal(t, u1.ColonSeparatedKey(), u1.GetParam(constant.INTERFACE_KEY, "")+"::group1") +} + +func TestCompareURLEqualFunc(t *testing.T) { + // test Default + url1, _ := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245") + url2, _ := NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=155650979798") + assert.False(t, GetCompareURLEqualFunc()(url1, url2)) + assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY)) + + // test custom + url1, _ = NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245") + url2, _ = NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=155650979798") + assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY)) + SetCompareURLEqualFunc(CustomCompareURLEqual) + assert.False(t, GetCompareURLEqualFunc()(url1, url2)) + assert.False(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY)) + + url1, _ = NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000") + url2, _ = NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000") + assert.True(t, GetCompareURLEqualFunc()(url1, url2)) + assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY)) + SetCompareURLEqualFunc(CustomCompareURLEqual) + assert.True(t, GetCompareURLEqualFunc()(url1, url2)) + assert.True(t, GetCompareURLEqualFunc()(url1, url2, constant.TIMESTAMP_KEY, constant.REMOTE_TIMESTAMP_KEY)) +} +func CustomCompareURLEqual(l *URL, r *URL, execludeParam ...string) bool { + return l.PrimitiveURL == r.PrimitiveURL } diff --git a/common/yaml/yaml_test.go b/common/yaml/yaml_test.go index 5a271a25823f576cb9cdb10c657520bbf5666017..19f4fe148aace5baf4790f329aa228506c8b5f70 100644 --- a/common/yaml/yaml_test.go +++ b/common/yaml/yaml_test.go @@ -66,5 +66,5 @@ type Config struct { } type ChildConfig struct { - StrTest string `default:"strTest" default:"default" yaml:"strTest" json:"strTest,omitempty"` + StrTest string `default:"default" yaml:"strTest" json:"strTest,omitempty"` } diff --git a/config/application_config.go b/config/application_config.go index ef99664fa298c28365ed7acc54d0c18a88c9b5c2..6fe6c5d049f8ce95a56fcfd62eb1cffc03bf3c43 100644 --- a/config/application_config.go +++ b/config/application_config.go @@ -48,8 +48,5 @@ func (c *ApplicationConfig) UnmarshalYAML(unmarshal func(interface{}) error) err return err } type plain ApplicationConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } diff --git a/config/base_config.go b/config/base_config.go index 336bb03c7b61ad8aad8465bb3c7754abeb9e9f5a..df1686ae854692454538f0395af34f68e4532e57 100644 --- a/config/base_config.go +++ b/config/base_config.go @@ -33,10 +33,6 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -type multiConfiger interface { - Prefix() string -} - // BaseConfig is the common configuration for provider and consumer type BaseConfig struct { ConfigCenterConfig *ConfigCenterConfig `yaml:"config_center" json:"config_center,omitempty"` @@ -49,11 +45,14 @@ type BaseConfig struct { // application config ApplicationConfig *ApplicationConfig `yaml:"application" json:"application,omitempty" property:"application"` - prefix string + //prefix string fatherConfig interface{} EventDispatcherType string `default:"direct" yaml:"event_dispatcher_type" json:"event_dispatcher_type,omitempty"` MetricConfig *MetricConfig `yaml:"metrics" json:"metrics,omitempty"` fileStream *bytes.Buffer + + // cache file used to store the current used configurations. + CacheFile string `yaml:"cache_file" json:"cache_file,omitempty" property:"cache_file"` } // nolint @@ -244,7 +243,7 @@ func setFieldValue(val reflect.Value, id reflect.Value, config *config.InmemoryC func (c *BaseConfig) fresh() { configList := config.GetEnvInstance().Configuration() - for element := configList.Front(); element != nil; element = element.Next() { + for element := configList.Back(); element != nil; element = element.Prev() { cfg := element.Value.(*config.InmemoryConfiguration) c.freshInternalConfig(cfg) } diff --git a/config/base_config_test.go b/config/base_config_test.go index 849a9c4586c0c8cd2d74e3dd1011aaab466f0e93..d3f50c8e774a2246a0ac07acf486e9102902dc14 100644 --- a/config/base_config_test.go +++ b/config/base_config_test.go @@ -17,7 +17,6 @@ package config import ( - "fmt" "reflect" "testing" ) @@ -108,10 +107,12 @@ var baseMockRef = map[string]*ReferenceConfig{ func TestRefresh(t *testing.T) { c := &BaseConfig{} + c.fileStream = nil mockMap := getMockMap() mockMap["dubbo.shutdown.timeout"] = "12s" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + config.GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{}) father := &ConsumerConfig{ Check: &[]bool{true}[0], @@ -144,9 +145,10 @@ func TestAppExternalRefresh(t *testing.T) { mockMap := getMockMap() mockMap["dubbo.reference.com.MockService.retries"] = "5" - config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) - mockMap["dubbo.consumer.check"] = "true" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + mockMap["dubbo.consumer.check"] = "true" + config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) + father := &ConsumerConfig{ Check: &[]bool{true}[0], BaseConfig: BaseConfig{ @@ -172,9 +174,9 @@ func TestAppExternalWithoutIDRefresh(t *testing.T) { delete(mockMap, "dubbo.reference.com.MockService.MockService.retries") mockMap["dubbo.reference.com.MockService.retries"] = "10" - config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) - mockMap["dubbo.consumer.check"] = "true" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + mockMap["dubbo.consumer.check"] = "true" + config.GetEnvInstance().UpdateAppExternalConfigMap(mockMap) father := &ConsumerConfig{ Check: &[]bool{true}[0], BaseConfig: BaseConfig{ @@ -204,6 +206,7 @@ func TestRefreshSingleRegistry(t *testing.T) { mockMap["dubbo.application.name"] = "dubbo" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + config.GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{}) father := &ConsumerConfig{ Check: &[]bool{true}[0], @@ -235,6 +238,7 @@ func TestRefreshProvider(t *testing.T) { mockMap["dubbo.protocols.jsonrpc1.port"] = "20001" config.GetEnvInstance().UpdateExternalConfigMap(mockMap) + config.GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{}) father := &ProviderConfig{ BaseConfig: BaseConfig{ @@ -285,8 +289,7 @@ func TestInitializeStruct(t *testing.T) { tp := reflect.TypeOf(ConsumerConfig{}) v := reflect.New(tp) initializeStruct(tp, v.Elem()) - fmt.Println(reflect.ValueOf(testConsumerConfig).Elem().Type().String()) - fmt.Println(v.Elem().Type().String()) + t.Logf("testConsumerConfig type:%s", reflect.ValueOf(testConsumerConfig).Elem().Type().String()) reflect.ValueOf(testConsumerConfig).Elem().Set(v.Elem()) assert.Condition(t, func() (success bool) { diff --git a/config/config_api.go b/config/config_api.go new file mode 100644 index 0000000000000000000000000000000000000000..eac5a79425e349d4d3f39debbd997ebb36bde3d5 --- /dev/null +++ b/config/config_api.go @@ -0,0 +1,556 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package config + +import ( + "context" + "time" +) + +//////////////////////////////////// default registry config +const ( + // defaultZKAddr is the default registry address of zookeeper + defaultZKAddr = "127.0.0.1:2181" + + // defaultConsulAddr is the default registry address of consul + defaultConsulAddr = "127.0.0.1:8500" + + // defaultNacosAddr is the default registry address of nacos + defaultNacosAddr = "127.0.0.1:8848" + + // defaultRegistryTimeout is the default registry timeout + defaultRegistryTimeout = "3s" +) + +// NewDefaultRegistryConfig New default registry config +// the input @protocol can only be: +// "zookeeper" with default addr "127.0.0.1:2181" +// "consul" with default addr "127.0.0.1:8500" +// "nacos" with default addr "127.0.0.1:8848" +func NewDefaultRegistryConfig(protocol string) *RegistryConfig { + switch protocol { + case "zookeeper": + return &RegistryConfig{ + Protocol: protocol, + Address: defaultZKAddr, + TimeoutStr: defaultRegistryTimeout, + } + case "consul": + return &RegistryConfig{ + Protocol: protocol, + Address: defaultConsulAddr, + TimeoutStr: defaultRegistryTimeout, + } + case "nacos": + return &RegistryConfig{ + Protocol: protocol, + Address: defaultNacosAddr, + TimeoutStr: defaultRegistryTimeout, + } + default: + return &RegistryConfig{ + Protocol: protocol, + } + } +} + +///////////////////////////////////// registry config api +type RegistryConfigOpt func(config *RegistryConfig) *RegistryConfig + +// NewRegistryConfig creates New RegistryConfig with @opts +func NewRegistryConfig(opts ...RegistryConfigOpt) *RegistryConfig { + newRegistryConfig := NewDefaultRegistryConfig("") + for _, v := range opts { + newRegistryConfig = v(newRegistryConfig) + } + return newRegistryConfig +} + +// WithRegistryProtocol returns RegistryConfigOpt with given @regProtocol name +func WithRegistryProtocol(regProtocol string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Protocol = regProtocol + return config + } +} + +// WithRegistryAddress returns RegistryConfigOpt with given @addr registry address +func WithRegistryAddress(addr string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Address = addr + return config + } +} + +// WithRegistryTimeOut returns RegistryConfigOpt with given @timeout registry config +func WithRegistryTimeOut(timeout string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.TimeoutStr = timeout + return config + } +} + +// WithRegistryGroup returns RegistryConfigOpt with given @group registry group +func WithRegistryGroup(group string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Group = group + return config + } +} + +// WithRegistryTTL returns RegistryConfigOpt with given @ttl registry ttl +func WithRegistryTTL(ttl string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.TTL = ttl + return config + } +} + +// WithRegistryUserName returns RegistryConfigOpt with given @userName registry userName +func WithRegistryUserName(userName string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Username = userName + return config + } +} + +// WithRegistryPassword returns RegistryConfigOpt with given @psw registry password +func WithRegistryPassword(psw string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Password = psw + return config + } +} + +// WithRegistrySimplified returns RegistryConfigOpt with given @simplified registry simplified flag +func WithRegistrySimplified(simplified bool) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Simplified = simplified + return config + } +} + +// WithRegistryPreferred returns RegistryConfig with given @preferred registry preferred flag +func WithRegistryPreferred(preferred bool) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Preferred = preferred + return config + } +} + +// WithRegistryWeight returns RegistryConfigOpt with given @weight registry weight flag +func WithRegistryWeight(weight int64) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Weight = weight + return config + } +} + +// WithRegistryParams returns RegistryConfigOpt with given registry @params +func WithRegistryParams(params map[string]string) RegistryConfigOpt { + return func(config *RegistryConfig) *RegistryConfig { + config.Params = params + return config + } +} + +///////////////////////////////////// consumer config api +// ConsumerConfigOpt is the options to init ConsumerConfig +type ConsumerConfigOpt func(config *ConsumerConfig) *ConsumerConfig + +// NewDefaultConsumerConfig returns default ConsumerConfig +// with connection timeout = 3s, request timeout = 3s +func NewDefaultConsumerConfig() *ConsumerConfig { + check := true + newConsumerConfig := &ConsumerConfig{ + BaseConfig: BaseConfig{}, + Registries: make(map[string]*RegistryConfig, 8), + References: make(map[string]*ReferenceConfig, 8), + ConnectTimeout: 3 * time.Second, + RequestTimeout: 3 * time.Second, + Check: &check, + } + return newConsumerConfig +} + +// NewConsumerConfig returns ConsumerConfig with @opts +func NewConsumerConfig(opts ...ConsumerConfigOpt) *ConsumerConfig { + newConfig := NewDefaultConsumerConfig() + for _, v := range opts { + v(newConfig) + } + return newConfig +} + +// WithConsumerAppConfig returns ConsumerConfigOpt with given @appConfig +func WithConsumerAppConfig(appConfig *ApplicationConfig) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.ApplicationConfig = appConfig + return config + } +} + +// WithConsumerRegistryConfig returns ConsumerConfigOpt with given @registryKey and @regConfig +func WithConsumerRegistryConfig(registryKey string, regConfig *RegistryConfig) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.Registries[registryKey] = regConfig + return config + } +} + +// WithConsumerReferenceConfig returns ConsumerConfigOpt with +func WithConsumerReferenceConfig(referenceKey string, refConfig *ReferenceConfig) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.References[referenceKey] = refConfig + return config + } +} + +// WithConsumerConnTimeout returns ConsumerConfigOpt with given consumer conn @timeout +func WithConsumerConnTimeout(timeout time.Duration) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.ConnectTimeout = timeout + return config + } +} + +// WithConsumerRequestTimeout returns ConsumerConfigOpt with given consumer request @timeout +func WithConsumerRequestTimeout(timeout time.Duration) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.RequestTimeout = timeout + return config + } +} + +// WithConsumerConfigCenterConfig returns ConsumerConfigOpt with given @configCenterConfig +func WithConsumerConfigCenterConfig(configCenterConfig *ConfigCenterConfig) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + config.ConfigCenterConfig = configCenterConfig + return config + } +} + +// WithConsumerConfigCheck returns ConsumerConfigOpt with given @check flag +func WithConsumerConfigCheck(check bool) ConsumerConfigOpt { + return func(config *ConsumerConfig) *ConsumerConfig { + *config.Check = check + return config + } +} + +//////////////////////////////////// reference config api +// ReferenceConfigOpt is consumer's reference config +type ReferenceConfigOpt func(config *ReferenceConfig) *ReferenceConfig + +// NewDefaultReferenceConfig returns empty ReferenceConfig +func NewDefaultReferenceConfig() *ReferenceConfig { + newReferenceConfig := NewReferenceConfig("", context.Background()) + newReferenceConfig.Methods = make([]*MethodConfig, 0, 8) + newReferenceConfig.Params = make(map[string]string, 8) + return newReferenceConfig +} + +// NewReferenceConfigByAPI returns ReferenceConfig with given @opts +func NewReferenceConfigByAPI(opts ...ReferenceConfigOpt) *ReferenceConfig { + newreferenceConfig := NewDefaultReferenceConfig() + for _, v := range opts { + v(newreferenceConfig) + } + return newreferenceConfig +} + +// WithReferenceRegistry returns ReferenceConfigOpt with given registryKey: @registry +func WithReferenceRegistry(registry string) ReferenceConfigOpt { + return func(config *ReferenceConfig) *ReferenceConfig { + config.Registry = registry + return config + } +} + +// WithReferenceProtocol returns ReferenceConfigOpt with given protocolKey: @protocol +func WithReferenceProtocol(protocol string) ReferenceConfigOpt { + return func(config *ReferenceConfig) *ReferenceConfig { + config.Protocol = protocol + return config + } +} + +// WithReferenceInterface returns ReferenceConfigOpt with given @interfaceName +func WithReferenceInterface(interfaceName string) ReferenceConfigOpt { + return func(config *ReferenceConfig) *ReferenceConfig { + config.InterfaceName = interfaceName + return config + } +} + +// WithReferenceCluster returns ReferenceConfigOpt with given cluster name: @cluster +func WithReferenceCluster(cluster string) ReferenceConfigOpt { + return func(config *ReferenceConfig) *ReferenceConfig { + config.Cluster = cluster + return config + } +} + +// WithReferenceMethod returns ReferenceConfigOpt with given @method, @retries, and load balance: @lb +func WithReferenceMethod(methodName, retries, lb string) ReferenceConfigOpt { + return func(config *ReferenceConfig) *ReferenceConfig { + config.Methods = append(config.Methods, &MethodConfig{ + Name: methodName, + Retries: retries, + LoadBalance: lb, + }) + return config + } +} + +///////////////////////////////////// provider config api +// ProviderConfigOpt is the +type ProviderConfigOpt func(config *ProviderConfig) *ProviderConfig + +// NewDefaultProviderConfig returns ProviderConfig with default ApplicationConfig +func NewDefaultProviderConfig() *ProviderConfig { + newConsumerConfig := &ProviderConfig{ + BaseConfig: BaseConfig{ + ApplicationConfig: &ApplicationConfig{ + Name: "dubbo", + Module: "module", + Organization: "dubbo_org", + Owner: "dubbo", + }, + }, + Services: make(map[string]*ServiceConfig), + Registries: make(map[string]*RegistryConfig, 8), + Protocols: make(map[string]*ProtocolConfig, 8), + } + return newConsumerConfig +} + +// NewProviderConfig returns ProviderConfig with given @opts +func NewProviderConfig(opts ...ProviderConfigOpt) *ProviderConfig { + newConfig := NewDefaultProviderConfig() + for _, v := range opts { + v(newConfig) + } + return newConfig +} + +// WithPrividerRegistryConfig returns ProviderConfigOpt with given registry config: @regConfig +func WithPrividerRegistryConfig(regConfig *RegistryConfig) ProviderConfigOpt { + return func(config *ProviderConfig) *ProviderConfig { + config.Registries[regConfig.Protocol] = regConfig + return config + } +} + +// WithProviderAppConfig returns ProviderConfigOpt with given @appConfig +func WithProviderAppConfig(appConfig *ApplicationConfig) ProviderConfigOpt { + return func(config *ProviderConfig) *ProviderConfig { + config.ApplicationConfig = appConfig + return config + } +} + +// WithProviderServices returns ProviderConfig with given serviceNameKey @serviceName and @serviceConfig +func WithProviderServices(serviceName string, serviceConfig *ServiceConfig) ProviderConfigOpt { + return func(config *ProviderConfig) *ProviderConfig { + config.Services[serviceName] = serviceConfig + return config + } +} + +// WithProviderProtocol returns ProviderConfigOpt with given @protocolKey, protocolName @protocol and @port +func WithProviderProtocol(protocolKey, protocol, port string) ProviderConfigOpt { + return func(config *ProviderConfig) *ProviderConfig { + config.Protocols[protocolKey] = &ProtocolConfig{ + Name: protocol, + Port: port, + } + return config + } +} + +// WithProviderRegistry returns ProviderConfigOpt with given @registryKey and registry @registryConfig +func WithProviderRegistry(registryKey string, registryConfig *RegistryConfig) ProviderConfigOpt { + return func(config *ProviderConfig) *ProviderConfig { + config.Registries[registryKey] = registryConfig + return config + } +} + +/////////////////////////////////////// service config api +// ServiceConfigOpt is the option to init ServiceConfig +type ServiceConfigOpt func(config *ServiceConfig) *ServiceConfig + +// NewDefaultServiceConfig returns default ServiceConfig +func NewDefaultServiceConfig() *ServiceConfig { + newServiceConfig := NewServiceConfig("", context.Background()) + newServiceConfig.Params = make(map[string]string) + newServiceConfig.Methods = make([]*MethodConfig, 0, 8) + return newServiceConfig +} + +// NewServiceConfigByAPI is named as api, because there is NewServiceConfig func already declared +// NewServiceConfigByAPI returns ServiceConfig with given @opts +func NewServiceConfigByAPI(opts ...ServiceConfigOpt) *ServiceConfig { + defaultServiceConfig := NewDefaultServiceConfig() + for _, v := range opts { + v(defaultServiceConfig) + } + return defaultServiceConfig +} + +// WithServiceRegistry returns ServiceConfigOpt with given registryKey @registry +func WithServiceRegistry(registry string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Registry = registry + return config + } +} + +// WithServiceProtocol returns ServiceConfigOpt with given protocolKey @protocol +func WithServiceProtocol(protocol string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Protocol = protocol + return config + } +} + +// WithServiceInterface returns ServiceConfigOpt with given @interfaceName +func WithServiceInterface(interfaceName string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.InterfaceName = interfaceName + return config + } +} + +// WithServiceLoadBalance returns ServiceConfigOpt with given load balance @lb +func WithServiceLoadBalance(lb string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Loadbalance = lb + return config + } +} + +// WithServiceWarmUpTime returns ServiceConfigOpt with given @warmUp time +func WithServiceWarmUpTime(warmUp string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Warmup = warmUp + return config + } +} + +// WithServiceCluster returns ServiceConfigOpt with given cluster name @cluster +func WithServiceCluster(cluster string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Cluster = cluster + return config + } +} + +// WithServiceMethod returns ServiceConfigOpt with given @name, @retries and load balance @lb +func WithServiceMethod(name, retries, lb string) ServiceConfigOpt { + return func(config *ServiceConfig) *ServiceConfig { + config.Methods = append(config.Methods, &MethodConfig{ + Name: name, + Retries: retries, + LoadBalance: lb, + }) + return config + } +} + +///////////////////////////////////////// Application config api +// ApplicationConfigOpt is option to init ApplicationConfig +type ApplicationConfigOpt func(config *ApplicationConfig) *ApplicationConfig + +// NewDefaultApplicationConfig returns ApplicationConfig with default +// name: dubbo.io +// module: sample +// organization: dubbo.io +// owner: dubbogo +// version: 0.0.1 +// environment dev +func NewDefaultApplicationConfig() *ApplicationConfig { + newAppConfig := &ApplicationConfig{ + Name: "dubbo.io", + Module: "sample", + Organization: "dubbo.io", + Owner: "dubbogo", + Version: "0.0.1", + Environment: "dev", + } + return newAppConfig +} + +// NewApplicationConfig is named as api, because there is NewServiceConfig func already declared +// NewApplicationConfig returns ApplicationConfig wigh default application config +func NewApplicationConfig(opts ...ApplicationConfigOpt) *ApplicationConfig { + defaultServiceConfig := NewDefaultApplicationConfig() + for _, v := range opts { + v(defaultServiceConfig) + } + return defaultServiceConfig +} + +// WithAppName returns ApplicationConfigOpt with given @name +func WithAppName(name string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Name = name + return config + } +} + +// WithAppModule returns ApplicationConfigOpt with given @module +func WithAppModule(module string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Module = module + return config + } +} + +// WithAppOrganization returns ApplicationConfigOpt wight given organization @org +func WithAppOrganization(org string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Organization = org + return config + } +} + +// WithAppOwner returns ApplicationConfigOpt with given @owner +func WithAppOwner(owner string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Owner = owner + return config + } +} + +// WithAppVersion returns ApplicationConfigOpt with given version @version +func WithAppVersion(version string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Version = version + return config + } +} + +// WithAppEnvironment returns ApplicationConfigOpt wigh given environment @env +func WithAppEnvironment(env string) ApplicationConfigOpt { + return func(config *ApplicationConfig) *ApplicationConfig { + config.Environment = env + return config + } +} diff --git a/config/config_api_test.go b/config/config_api_test.go new file mode 100644 index 0000000000000000000000000000000000000000..fd8d5613f49745aebf141ff28a03f3bdb72419bb --- /dev/null +++ b/config/config_api_test.go @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package config + +import ( + "strconv" + "testing" + "time" +) + +import ( + "github.com/stretchr/testify/assert" +) + +func TestNewDefaultServiceConfig(t *testing.T) { + serviceConfig := NewServiceConfigByAPI( + WithServiceCluster("test-cluster"), + WithServiceInterface("test-interface"), + WithServiceLoadBalance("test-loadbalance"), + WithServiceMethod("test-method1", "test-retries1", "test-lb1"), + WithServiceMethod("test-method2", "test-retries2", "test-lb2"), + WithServiceMethod("test-method3", "test-retries3", "test-lb3"), + WithServiceProtocol("test-protocol"), + WithServiceRegistry("test-registry"), + WithServiceWarmUpTime("test-warmup"), + ) + assert.Equal(t, serviceConfig.Cluster, "test-cluster") + assert.Equal(t, serviceConfig.InterfaceName, "test-interface") + assert.Equal(t, serviceConfig.Loadbalance, "test-loadbalance") + for i, v := range serviceConfig.Methods { + backFix := strconv.Itoa(i + 1) + assert.Equal(t, v.Name, "test-method"+backFix) + assert.Equal(t, v.Retries, "test-retries"+backFix) + assert.Equal(t, v.LoadBalance, "test-lb"+backFix) + } + assert.Equal(t, serviceConfig.Protocol, "test-protocol") + assert.Equal(t, serviceConfig.Registry, "test-registry") + assert.Equal(t, serviceConfig.Warmup, "test-warmup") +} + +func TestNewReferenceConfigByAPI(t *testing.T) { + refConfig := NewReferenceConfigByAPI( + WithReferenceCluster("test-cluster"), + WithReferenceInterface("test-interface"), + WithReferenceMethod("test-method1", "test-retries1", "test-lb1"), + WithReferenceMethod("test-method2", "test-retries2", "test-lb2"), + WithReferenceMethod("test-method3", "test-retries3", "test-lb3"), + WithReferenceProtocol("test-protocol"), + WithReferenceRegistry("test-registry"), + ) + assert.Equal(t, refConfig.Cluster, "test-cluster") + assert.Equal(t, refConfig.InterfaceName, "test-interface") + for i, v := range refConfig.Methods { + backFix := strconv.Itoa(i + 1) + assert.Equal(t, v.Name, "test-method"+backFix) + assert.Equal(t, v.Retries, "test-retries"+backFix) + assert.Equal(t, v.LoadBalance, "test-lb"+backFix) + } + assert.Equal(t, refConfig.Protocol, "test-protocol") + assert.Equal(t, refConfig.Registry, "test-registry") +} + +func TestNewRegistryConfig(t *testing.T) { + regConfig := NewRegistryConfig( + WithRegistryTimeOut("test-timeout"), + WithRegistryProtocol("test-protocol"), + WithRegistryGroup("test-group"), + WithRegistryAddress("test-address"), + WithRegistrySimplified(true), + WithRegistryUserName("test-username"), + WithRegistryPassword("test-password"), + ) + assert.Equal(t, regConfig.TimeoutStr, "test-timeout") + assert.Equal(t, regConfig.Protocol, "test-protocol") + assert.Equal(t, regConfig.Group, "test-group") + assert.Equal(t, regConfig.Address, "test-address") + assert.Equal(t, regConfig.Simplified, true) + assert.Equal(t, regConfig.Username, "test-username") + assert.Equal(t, regConfig.Password, "test-password") +} + +func TestNewConsumerConfig(t *testing.T) { + referConfig := NewReferenceConfigByAPI( + WithReferenceCluster("test-cluster"), + WithReferenceInterface("test-interface"), + WithReferenceMethod("test-method1", "test-retries1", "test-lb1"), + WithReferenceMethod("test-method2", "test-retries2", "test-lb2"), + WithReferenceMethod("test-method3", "test-retries3", "test-lb3"), + WithReferenceProtocol("test-protocol"), + WithReferenceRegistry("test-registry"), + ) + defaultZKRegistry := NewDefaultRegistryConfig("zookeeper") + assert.Equal(t, defaultZKRegistry.Address, defaultZKAddr) + assert.Equal(t, defaultZKRegistry.Protocol, "zookeeper") + assert.Equal(t, defaultZKRegistry.TimeoutStr, defaultRegistryTimeout) + + testConsumerConfig := NewConsumerConfig( + WithConsumerConfigCheck(true), + WithConsumerConnTimeout(time.Minute), + WithConsumerRequestTimeout(time.Hour), + WithConsumerReferenceConfig("UserProvider", referConfig), + WithConsumerRegistryConfig("demoZK", defaultZKRegistry), + ) + + assert.Equal(t, *testConsumerConfig.Check, true) + assert.Equal(t, testConsumerConfig.ConnectTimeout, time.Minute) + assert.Equal(t, testConsumerConfig.RequestTimeout, time.Hour) + assert.Equal(t, testConsumerConfig.Registries["demoZK"], defaultZKRegistry) + assert.Equal(t, testConsumerConfig.References["UserProvider"], referConfig) +} + +func TestNewProviderConfig(t *testing.T) { + serviceConfig := NewServiceConfigByAPI( + WithServiceCluster("test-cluster"), + WithServiceInterface("test-interface"), + WithServiceLoadBalance("test-loadbalance"), + WithServiceMethod("test-method1", "test-retries1", "test-lb1"), + WithServiceMethod("test-method2", "test-retries2", "test-lb2"), + WithServiceMethod("test-method3", "test-retries3", "test-lb3"), + WithServiceProtocol("test-protocol"), + WithServiceRegistry("test-registry"), + WithServiceWarmUpTime("test-warmup"), + ) + + defaultConsulRegistry := NewDefaultRegistryConfig("consul") + assert.Equal(t, defaultConsulRegistry.Address, defaultConsulAddr) + assert.Equal(t, defaultConsulRegistry.Protocol, "consul") + assert.Equal(t, defaultConsulRegistry.TimeoutStr, defaultRegistryTimeout) + + defaultNacosRegistry := NewDefaultRegistryConfig("nacos") + assert.Equal(t, defaultNacosRegistry.Address, defaultNacosAddr) + assert.Equal(t, defaultNacosRegistry.Protocol, "nacos") + assert.Equal(t, defaultNacosRegistry.TimeoutStr, defaultRegistryTimeout) + + testProviderConfig := NewProviderConfig( + WithProviderServices("UserProvider", serviceConfig), + WithProviderProtocol("dubbo", "dubbo", "20000"), + WithProviderRegistry("demoConsul", defaultConsulRegistry), + WithProviderRegistry("demoNacos", defaultNacosRegistry), + ) + + assert.NotNil(t, testProviderConfig.Services) + for k, v := range testProviderConfig.Services { + assert.Equal(t, k, "UserProvider") + assert.Equal(t, v, serviceConfig) + } + assert.NotNil(t, testProviderConfig.Registries) + i := 0 + for k, v := range testProviderConfig.Registries { + if i == 0 { + assert.Equal(t, k, "demoConsul") + assert.Equal(t, v, defaultConsulRegistry) + i++ + } else { + assert.Equal(t, k, "demoNacos") + assert.Equal(t, v, defaultNacosRegistry) + } + } + + assert.NotNil(t, testProviderConfig.Protocols) + assert.Equal(t, testProviderConfig.Protocols["dubbo"].Name, "dubbo") + assert.Equal(t, testProviderConfig.Protocols["dubbo"].Port, "20000") +} diff --git a/config/config_center_config.go b/config/config_center_config.go index 0fc4007940d9b1ac2456c9b2d379493bb5d8edb0..2489709af1462da43e6989f656c89e0392593d3d 100644 --- a/config/config_center_config.go +++ b/config/config_center_config.go @@ -18,14 +18,13 @@ package config import ( - "context" "net/url" "reflect" - "time" ) import ( "github.com/creasty/defaults" + perrors "github.com/pkg/errors" ) import ( @@ -35,7 +34,6 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/config_center" - perrors "github.com/pkg/errors" ) // ConfigCenterConfig is configuration for config center @@ -46,7 +44,7 @@ import ( // // ConfigCenter has currently supported Zookeeper, Nacos, Etcd, Consul, Apollo type ConfigCenterConfig struct { - context context.Context + //context context.Context Protocol string `required:"true" yaml:"protocol" json:"protocol,omitempty"` Address string `yaml:"address" json:"address,omitempty"` Cluster string `yaml:"cluster" json:"cluster,omitempty"` @@ -60,7 +58,7 @@ type ConfigCenterConfig struct { AppId string `default:"dubbo" yaml:"app_id" json:"app_id,omitempty"` TimeoutStr string `yaml:"timeout" json:"timeout,omitempty"` RemoteRef string `required:"false" yaml:"remote_ref" json:"remote_ref,omitempty"` - timeout time.Duration + //timeout time.Duration } // UnmarshalYAML unmarshals the ConfigCenterConfig by @unmarshal function @@ -69,10 +67,7 @@ func (c *ConfigCenterConfig) UnmarshalYAML(unmarshal func(interface{}) error) er return err } type plain ConfigCenterConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } // GetUrlMap gets url map from ConfigCenterConfig @@ -91,7 +86,7 @@ type configCenter struct { // toURL will compatible with baseConfig.ConfigCenterConfig.Address and baseConfig.ConfigCenterConfig.RemoteRef before 1.6.0 // After 1.6.0 will not compatible, only baseConfig.ConfigCenterConfig.RemoteRef -func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) { +func (b *configCenter) toURL(baseConfig BaseConfig) (*common.URL, error) { if len(baseConfig.ConfigCenterConfig.Address) > 0 { return common.NewURL(baseConfig.ConfigCenterConfig.Address, common.WithProtocol(baseConfig.ConfigCenterConfig.Protocol), common.WithParams(baseConfig.ConfigCenterConfig.GetUrlMap())) @@ -101,7 +96,7 @@ func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) { rc, ok := baseConfig.GetRemoteConfig(remoteRef) if !ok { - return common.URL{}, perrors.New("Could not find out the remote ref config, name: " + remoteRef) + return nil, perrors.New("Could not find out the remote ref config, name: " + remoteRef) } newURL, err := rc.toURL() @@ -114,11 +109,11 @@ func (b *configCenter) toURL(baseConfig BaseConfig) (common.URL, error) { // startConfigCenter will start the config center. // it will prepare the environment func (b *configCenter) startConfigCenter(baseConfig BaseConfig) error { - url, err := b.toURL(baseConfig) + newUrl, err := b.toURL(baseConfig) if err != nil { return err } - if err = b.prepareEnvironment(baseConfig, &url); err != nil { + if err = b.prepareEnvironment(baseConfig, newUrl); err != nil { return perrors.WithMessagef(err, "start config center error!") } // c.fresh() @@ -167,11 +162,11 @@ func (b *configCenter) prepareEnvironment(baseConfig BaseConfig, configCenterUrl // appGroup config file if len(appContent) != 0 { - appMapConent, err := dynamicConfig.Parser().Parse(appContent) + appMapContent, err := dynamicConfig.Parser().Parse(appContent) if err != nil { return perrors.WithStack(err) } - config.GetEnvInstance().UpdateAppExternalConfigMap(appMapConent) + config.GetEnvInstance().UpdateAppExternalConfigMap(appMapContent) } return nil diff --git a/config/config_loader.go b/config/config_loader.go index c66e526921e7e5ab017105e2f4ea2baa62563205..1fd27d5bcdbf2375300cd744e5fcac6e73bda47e 100644 --- a/config/config_loader.go +++ b/config/config_loader.go @@ -18,7 +18,9 @@ package config import ( + "flag" "fmt" + "io/ioutil" "log" "os" "reflect" @@ -28,7 +30,6 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) @@ -38,6 +39,7 @@ import ( "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" _ "github.com/apache/dubbo-go/common/observer/dispatcher" + "github.com/apache/dubbo-go/common/yaml" "github.com/apache/dubbo-go/registry" ) @@ -64,9 +66,14 @@ func init() { confProFile string ) - confConFile = os.Getenv(constant.CONF_CONSUMER_FILE_PATH) - confProFile = os.Getenv(constant.CONF_PROVIDER_FILE_PATH) - confRouterFile = os.Getenv(constant.CONF_ROUTER_FILE_PATH) + fs := flag.NewFlagSet("config", flag.ContinueOnError) + fs.StringVar(&confConFile, "conConf", os.Getenv(constant.CONF_CONSUMER_FILE_PATH), "default client config path") + fs.StringVar(&confProFile, "proConf", os.Getenv(constant.CONF_PROVIDER_FILE_PATH), "default server config path") + fs.StringVar(&confRouterFile, "rouConf", os.Getenv(constant.CONF_ROUTER_FILE_PATH), "default router config path") + fs.Parse(os.Args[1:]) + for len(fs.Args()) != 0 { + fs.Parse(fs.Args()[1:]) + } if errCon := ConsumerInit(confConFile); errCon != nil { log.Printf("[consumerInit] %#v", errCon) @@ -139,6 +146,17 @@ func loadConsumerConfig() { ref.Implement(rpcService) } + // Write current configuration to cache file. + if consumerConfig.CacheFile != "" { + if data, err := yaml.MarshalYML(consumerConfig); err != nil { + logger.Errorf("Marshal consumer config err: %s", err.Error()) + } else { + if err := ioutil.WriteFile(consumerConfig.CacheFile, data, 0666); err != nil { + logger.Errorf("Write consumer config cache file err: %s", err.Error()) + } + } + } + // wait for invoker is available, if wait over default 3s, then panic var count int for { @@ -195,6 +213,17 @@ func loadProviderConfig() { } checkRegistries(providerConfig.Registries, providerConfig.Registry) + // Write the current configuration to cache file. + if providerConfig.CacheFile != "" { + if data, err := yaml.MarshalYML(providerConfig); err != nil { + logger.Errorf("Marshal provider config err: %s", err.Error()) + } else { + if err := ioutil.WriteFile(providerConfig.CacheFile, data, 0666); err != nil { + logger.Errorf("Write provider config cache file err: %s", err.Error()) + } + } + } + for key, svs := range providerConfig.Services { rpcService := GetProviderService(key) if rpcService == nil { @@ -217,7 +246,7 @@ func registerServiceInstance() { if url == nil { return } - instance, err := createInstance(*url) + instance, err := createInstance(url) if err != nil { panic(err) } @@ -241,7 +270,7 @@ func registerServiceInstance() { } // nolint -func createInstance(url common.URL) (registry.ServiceInstance, error) { +func createInstance(url *common.URL) (registry.ServiceInstance, error) { appConfig := GetApplicationConfig() port, err := strconv.ParseInt(url.Port, 10, 32) if err != nil { @@ -250,10 +279,7 @@ func createInstance(url common.URL) (registry.ServiceInstance, error) { host := url.Ip if len(host) == 0 { - host, err = gxnet.GetLocalIP() - if err != nil { - return nil, perrors.WithMessage(err, "could not get the local Ip") - } + host = common.GetLocalIp() } // usually we will add more metadata @@ -273,7 +299,7 @@ func createInstance(url common.URL) (registry.ServiceInstance, error) { // selectMetadataServiceExportedURL get already be exported url func selectMetadataServiceExportedURL() *common.URL { - var selectedUrl common.URL + var selectedUrl *common.URL metaDataService, err := extension.GetMetadataService(GetApplicationConfig().MetadataType) if err != nil { logger.Warn(err) @@ -298,7 +324,7 @@ func selectMetadataServiceExportedURL() *common.URL { break } } - return &selectedUrl + return selectedUrl } func initRouter() { @@ -410,9 +436,9 @@ func GetBaseConfig() *BaseConfig { baseConfig = &BaseConfig{ MetricConfig: &MetricConfig{}, ConfigCenterConfig: &ConfigCenterConfig{}, - Remotes: make(map[string]*RemoteConfig, 0), + Remotes: make(map[string]*RemoteConfig), ApplicationConfig: &ApplicationConfig{}, - ServiceDiscoveries: make(map[string]*ServiceDiscoveryConfig, 0), + ServiceDiscoveries: make(map[string]*ServiceDiscoveryConfig), } } } diff --git a/config/config_loader_test.go b/config/config_loader_test.go index 461e607c1e0ad2e9471770224c8eb6d5f3ee96f6..5cda3b2a99fb1f8219fba762c097d8369ba07fdc 100644 --- a/config/config_loader_test.go +++ b/config/config_loader_test.go @@ -25,10 +25,9 @@ import ( ) import ( - cm "github.com/Workiva/go-datastructures/common" "github.com/Workiva/go-datastructures/slice/skip" gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" "github.com/stretchr/testify/assert" "go.uber.org/atomic" ) @@ -105,7 +104,8 @@ func TestLoad(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - err := common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") + err := common.ServiceMap.UnRegister("com.MockService", "mock", + common.ServiceKey("com.MockService", "huadong_idc", "1.0.0")) assert.Nil(t, err) consumerConfig = nil providerConfig = nil @@ -144,7 +144,7 @@ func TestLoadWithSingleReg(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") + common.ServiceMap.UnRegister("com.MockService", "mock", common.ServiceKey("com.MockService", "huadong_idc", "1.0.0")) consumerConfig = nil providerConfig = nil } @@ -183,7 +183,10 @@ func TestWithNoRegLoad(t *testing.T) { conServices = map[string]common.RPCService{} proServices = map[string]common.RPCService{} - common.ServiceMap.UnRegister("com.MockService", "mock", "MockService") + err := common.ServiceMap.UnRegister("com.MockService", "mock", + common.ServiceKey("com.MockService", "huadong_idc", "1.0.0")) + assert.Nil(t, err) + common.ServiceMap.UnRegister("com.MockService", "mock", common.ServiceKey("com.MockService", "huadong_idc", "1.0.0")) consumerConfig = nil providerConfig = nil } @@ -204,10 +207,12 @@ func TestConfigLoaderWithConfigCenter(t *testing.T) { assert.Equal(t, ProviderConfig{}, GetProviderConfig()) err = ConsumerInit(conPath) - configCenterRefreshConsumer() + assert.NoError(t, err) + err = configCenterRefreshConsumer() assert.NoError(t, err) err = ProviderInit(proPath) - configCenterRefreshProvider() + assert.NoError(t, err) + err = configCenterRefreshProvider() assert.NoError(t, err) assert.NotNil(t, consumerConfig) @@ -257,13 +262,15 @@ func TestConfigLoaderWithConfigCenterSingleRegistry(t *testing.T) { assert.Equal(t, ProviderConfig{}, GetProviderConfig()) err = ConsumerInit(conPath) + assert.NoError(t, err) checkApplicationName(consumerConfig.ApplicationConfig) - configCenterRefreshConsumer() + err = configCenterRefreshConsumer() checkRegistries(consumerConfig.Registries, consumerConfig.Registry) assert.NoError(t, err) err = ProviderInit(proPath) + assert.NoError(t, err) checkApplicationName(providerConfig.ApplicationConfig) - configCenterRefreshProvider() + err = configCenterRefreshProvider() checkRegistries(providerConfig.Registries, providerConfig.Registry) assert.NoError(t, err) @@ -353,27 +360,27 @@ func (m *mockMetadataService) ServiceName() (string, error) { panic("implement me") } -func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { - return m.addURL(m.exportedServiceURLs, &url), nil +func (m *mockMetadataService) ExportURL(url *common.URL) (bool, error) { + return m.addURL(m.exportedServiceURLs, url), nil } -func (m *mockMetadataService) UnexportURL(url common.URL) error { +func (m *mockMetadataService) UnexportURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { +func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) { panic("implement me") } -func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { +func (m *mockMetadataService) UnsubscribeURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { +func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error { return nil } -func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { +func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) { return ConvertURLArrToIntfArr(m.getAllService(m.exportedServiceURLs)), nil } @@ -381,19 +388,19 @@ func (m *mockMetadataService) MethodMapper() map[string]string { panic("implement me") } -func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { +func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) { panic("implement me") } -func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { +func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) { panic("implement me") } -func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) { panic("implement me") } -func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { +func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) { panic("implement me") } @@ -409,7 +416,7 @@ func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) boo logger.Debug(url.ServiceKey()) if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded { mts.lock.RLock() - wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + wantedUrl := urlSet.(*skip.SkipList).Get(url) if len(wantedUrl) > 0 && wantedUrl[0] != nil { mts.lock.RUnlock() return false @@ -418,23 +425,23 @@ func (mts *mockMetadataService) addURL(targetMap *sync.Map, url *common.URL) boo } mts.lock.Lock() // double chk - wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + wantedUrl := urlSet.(*skip.SkipList).Get(url) if len(wantedUrl) > 0 && wantedUrl[0] != nil { mts.lock.Unlock() return false } - urlSet.(*skip.SkipList).Insert(Comparator(*url)) + urlSet.(*skip.SkipList).Insert(url) mts.lock.Unlock() return true } -func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL { +func (m *mockMetadataService) getAllService(services *sync.Map) []*common.URL { // using skip list to dedup and sorting - res := make([]common.URL, 0) + var res []*common.URL services.Range(func(key, value interface{}) bool { urls := value.(*skip.SkipList) for i := uint64(0); i < urls.Len(); i++ { - url := common.URL(urls.ByPosition(i).(Comparator)) + url := urls.ByPosition(i).(*common.URL) if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME { res = append(res, url) } @@ -445,26 +452,10 @@ func (m *mockMetadataService) getAllService(services *sync.Map) []common.URL { return res } -type Comparator common.URL - -// Compare is defined as Comparator for skip list to compare the URL -func (c Comparator) Compare(comp cm.Comparator) int { - a := common.URL(c).String() - b := common.URL(comp.(Comparator)).String() - switch { - case a > b: - return 1 - case a < b: - return -1 - default: - return 0 - } -} - type mockServiceDiscoveryRegistry struct { } -func (mr *mockServiceDiscoveryRegistry) GetUrl() common.URL { +func (mr *mockServiceDiscoveryRegistry) GetUrl() *common.URL { panic("implement me") } @@ -476,11 +467,11 @@ func (mr *mockServiceDiscoveryRegistry) Destroy() { panic("implement me") } -func (mr *mockServiceDiscoveryRegistry) Register(url common.URL) error { +func (mr *mockServiceDiscoveryRegistry) Register(*common.URL) error { panic("implement me") } -func (mr *mockServiceDiscoveryRegistry) UnRegister(url common.URL) error { +func (mr *mockServiceDiscoveryRegistry) UnRegister(*common.URL) error { panic("implement me") } @@ -507,15 +498,15 @@ func (m *mockServiceDiscovery) Destroy() error { panic("implement me") } -func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Register(registry.ServiceInstance) error { return nil } -func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Update(registry.ServiceInstance) error { panic("implement me") } -func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Unregister(registry.ServiceInstance) error { panic("implement me") } @@ -527,39 +518,39 @@ func (m *mockServiceDiscovery) GetServices() *gxset.HashSet { panic("implement me") } -func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { +func (m *mockServiceDiscovery) GetInstances(string) []registry.ServiceInstance { panic("implement me") } -func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { +func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int) gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { +func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int, bool) gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { +func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int) map[string]gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { +func (m *mockServiceDiscovery) AddListener(*registry.ServiceInstancesChangedListener) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error { +func (m *mockServiceDiscovery) DispatchEventByServiceName(string) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { +func (m *mockServiceDiscovery) DispatchEventForInstances(string, []registry.ServiceInstance) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { +func (m *mockServiceDiscovery) DispatchEvent(*registry.ServiceInstancesChangedEvent) error { panic("implement me") } -func ConvertURLArrToIntfArr(urls []common.URL) []interface{} { +func ConvertURLArrToIntfArr(urls []*common.URL) []interface{} { if len(urls) == 0 { return []interface{}{} } diff --git a/config/consumer_config.go b/config/consumer_config.go index c8083603e12570a4492dd63a749adb4aa89663c8..ca88fe3d846d97822f16fa86aed15c0849f279e4 100644 --- a/config/consumer_config.go +++ b/config/consumer_config.go @@ -43,9 +43,9 @@ const ( // ConsumerConfig is Consumer default configuration type ConsumerConfig struct { - BaseConfig `yaml:",inline"` - configCenter - Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` + BaseConfig `yaml:",inline"` + configCenter `yaml:"-"` + Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` // client Connect_Timeout string `default:"100ms" yaml:"connect_timeout" json:"connect_timeout,omitempty" property:"connect_timeout"` ConnectTimeout time.Duration @@ -71,10 +71,7 @@ func (c *ConsumerConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } type plain ConsumerConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } // nolint diff --git a/config/graceful_shutdown.go b/config/graceful_shutdown.go index aa102f35e9048dbc6fbcb10db19cb802b2f3147b..89ac2e38fceceb2471372c3e2a859ee771bd36a6 100644 --- a/config/graceful_shutdown.go +++ b/config/graceful_shutdown.go @@ -51,6 +51,7 @@ import ( * The signals are different on different platforms. * We define them by using 'package build' feature https://golang.org/pkg/go/build/ */ +const defaultShutDownTime = time.Second * 60 // nolint func GracefulShutdownInit() { @@ -64,20 +65,17 @@ func GracefulShutdownInit() { case sig := <-signals: logger.Infof("get signal %s, application will shutdown.", sig) // gracefulShutdownOnce.Do(func() { + time.AfterFunc(totalTimeout(), func() { + logger.Warn("Shutdown gracefully timeout, application will shutdown immediately. ") + os.Exit(0) + }) BeforeShutdown() - // those signals' original behavior is exit with dump ths stack, so we try to keep the behavior for _, dumpSignal := range DumpHeapShutdownSignals { if sig == dumpSignal { debug.WriteHeapDump(os.Stdout.Fd()) } } - - time.AfterFunc(totalTimeout(), func() { - logger.Warn("Shutdown gracefully timeout, application will shutdown immediately. ") - os.Exit(0) - }) - os.Exit(0) } }() @@ -196,7 +194,7 @@ func waitingProcessedTimeout(shutdownConfig *ShutdownConfig) { } func totalTimeout() time.Duration { - var providerShutdown time.Duration + var providerShutdown = defaultShutDownTime if providerConfig != nil && providerConfig.ShutdownConfig != nil { providerShutdown = providerConfig.ShutdownConfig.GetTimeout() } diff --git a/config/graceful_shutdown_config_test.go b/config/graceful_shutdown_config_test.go index 80eb5317386f4e9d966f7a9f07635a810727d77c..43d2fc3c1bfdabf618ca07c696a0bc1d1018aa42 100644 --- a/config/graceful_shutdown_config_test.go +++ b/config/graceful_shutdown_config_test.go @@ -32,8 +32,8 @@ func TestShutdownConfigGetTimeout(t *testing.T) { assert.False(t, config.RequestsFinished) config = ShutdownConfig{ - Timeout: "12x", - StepTimeout: "34a", + Timeout: "60s", + StepTimeout: "10s", } assert.Equal(t, 60*time.Second, config.GetTimeout()) diff --git a/config/instance/metadata_report.go b/config/instance/metadata_report.go index 8e833dd70bcc0db8e65cd8703f2bc1859432a887..68a197d3ddb4abfd8ab924749857a7f94a43e1d4 100644 --- a/config/instance/metadata_report.go +++ b/config/instance/metadata_report.go @@ -29,7 +29,7 @@ import ( var ( instance report.MetadataReport - reportUrl common.URL + reportUrl *common.URL once sync.Once ) @@ -41,18 +41,18 @@ func GetMetadataReportInstance(selectiveUrl ...*common.URL) report.MetadataRepor if len(selectiveUrl) > 0 { url = selectiveUrl[0] instance = extension.GetMetadataReportFactory(url.Protocol).CreateMetadataReport(url) - reportUrl = *url + reportUrl = url } }) return instance } // GetMetadataReportUrl will return the report instance url -func GetMetadataReportUrl() common.URL { +func GetMetadataReportUrl() *common.URL { return reportUrl } // SetMetadataReportUrl will only can be used by unit test to mock url -func SetMetadataReportUrl(url common.URL) { +func SetMetadataReportUrl(url *common.URL) { reportUrl = url } diff --git a/config/instance/metadata_report_test.go b/config/instance/metadata_report_test.go index d489af048055b362f2fa68a8963dd2cdf9632945..110903a41f577ef78b4d7abbcef17864fd6e281c 100644 --- a/config/instance/metadata_report_test.go +++ b/config/instance/metadata_report_test.go @@ -38,7 +38,7 @@ func TestGetMetadataReportInstance(t *testing.T) { return &mockMetadataReportFactory{} }) u, _ := common.NewURL("mock://127.0.0.1") - rpt := GetMetadataReportInstance(&u) + rpt := GetMetadataReportInstance(u) assert.NotNil(t, rpt) } @@ -60,7 +60,7 @@ func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier panic("implement me") } -func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error { +func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error { panic("implement me") } diff --git a/config/interfaces/config_post_processor.go b/config/interfaces/config_post_processor.go new file mode 100644 index 0000000000000000000000000000000000000000..53dd71780ff0807065e8c4b0e00ab4fe8b6a4823 --- /dev/null +++ b/config/interfaces/config_post_processor.go @@ -0,0 +1,32 @@ +/* + * 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 interfaces + +import ( + "github.com/apache/dubbo-go/common" +) + +// ConfigPostProcessor is an extension to give users a chance to customize configs against ReferenceConfig and +// ServiceConfig during deployment time. +type ConfigPostProcessor interface { + // PostProcessReferenceConfig customizes ReferenceConfig's params. + PostProcessReferenceConfig(*common.URL) + + // PostProcessServiceConfig customizes ServiceConfig's params. + PostProcessServiceConfig(*common.URL) +} diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go index 6d319e5ecb8007e06dcf790fff145bfab754df3d..24eba5ec8dc483966b8e63271b40cde9cbf54e3e 100644 --- a/config/metadata_report_config.go +++ b/config/metadata_report_config.go @@ -84,7 +84,7 @@ func (c *MetadataReportConfig) ToUrl() (*common.URL, error) { return nil, perrors.New("Invalid MetadataReportConfig.") } res.SetParam("metadata", res.Protocol) - return &res, nil + return res, nil } func (c *MetadataReportConfig) IsValid() bool { @@ -101,8 +101,8 @@ func startMetadataReport(metadataType string, metadataReportConfig *MetadataRepo return perrors.New("MetadataConfig remote ref can not be empty.") } - if url, err := metadataReportConfig.ToUrl(); err == nil { - instance.GetMetadataReportInstance(url) + if tmpUrl, err := metadataReportConfig.ToUrl(); err == nil { + instance.GetMetadataReportInstance(tmpUrl) } else { return perrors.Wrap(err, "Start MetadataReport failed.") } diff --git a/config/method_config.go b/config/method_config.go index b64306fd6aa865d219506ea2722067619b00fea7..db52940c3db6b4b81b0467945b538c9b540cd46a 100644 --- a/config/method_config.go +++ b/config/method_config.go @@ -57,8 +57,5 @@ func (c *MethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { return err } type plain MethodConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } diff --git a/config/provider_config.go b/config/provider_config.go index fcb429b6400936f72c09035968b4d4c72387246c..4113aab7ef17a19cfd2cac782a0d48e43d36e641 100644 --- a/config/provider_config.go +++ b/config/provider_config.go @@ -37,8 +37,8 @@ import ( // ProviderConfig is the default configuration of service provider type ProviderConfig struct { - BaseConfig `yaml:",inline"` - configCenter + BaseConfig `yaml:",inline"` + configCenter `yaml:"-"` Filter string `yaml:"filter" json:"filter,omitempty" property:"filter"` ProxyFactory string `yaml:"proxy_factory" default:"default" json:"proxy_factory,omitempty" property:"proxy_factory"` Services map[string]*ServiceConfig `yaml:"services" json:"services,omitempty" property:"services"` @@ -58,10 +58,7 @@ func (c *ProviderConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } type plain ProviderConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } // nolint diff --git a/config/reference_config.go b/config/reference_config.go index cd10f89eb7773e16ed953623c6fb38dcb98b01b4..f71c7ec1925209c138ad1ccf5668f3ef18d35e13 100644 --- a/config/reference_config.go +++ b/config/reference_config.go @@ -86,17 +86,13 @@ func (c *ReferenceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error } *c = ReferenceConfig(raw) - if err := defaults.Set(c); err != nil { - return err - } - - return nil + return defaults.Set(c) } // Refer ... func (c *ReferenceConfig) Refer(_ interface{}) { cfgURL := common.NewURLWithOptions( - common.WithPath(c.id), + common.WithPath(c.InterfaceName), common.WithProtocol(c.Protocol), common.WithParams(c.getUrlMap()), common.WithParamsValue(constant.BEAN_NAME_KEY, c.id), @@ -104,6 +100,7 @@ func (c *ReferenceConfig) Refer(_ interface{}) { if c.ForceTag { cfgURL.AddParam(constant.ForceUseTag, "true") } + c.postProcessConfig(cfgURL) if c.Url != "" { // 1. user specified URL, could be peer-to-peer address, or register center's address. urlStrings := gxstrings.RegSplit(c.Url, "\\s*[;]+\\s*") @@ -114,13 +111,13 @@ func (c *ReferenceConfig) Refer(_ interface{}) { } if serviceUrl.Protocol == constant.REGISTRY_PROTOCOL { serviceUrl.SubURL = cfgURL - c.urls = append(c.urls, &serviceUrl) + c.urls = append(c.urls, serviceUrl) } else { if serviceUrl.Path == "" { - serviceUrl.Path = "/" + c.id + serviceUrl.Path = "/" + c.InterfaceName } // merge url need to do - newUrl := common.MergeUrl(&serviceUrl, cfgURL) + newUrl := common.MergeUrl(serviceUrl, cfgURL) c.urls = append(c.urls, newUrl) } } @@ -135,12 +132,12 @@ func (c *ReferenceConfig) Refer(_ interface{}) { } if len(c.urls) == 1 { - c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(*c.urls[0]) + c.invoker = extension.GetProtocol(c.urls[0].Protocol).Refer(c.urls[0]) } else { invokers := make([]protocol.Invoker, 0, len(c.urls)) var regUrl *common.URL for _, u := range c.urls { - invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(*u)) + invokers = append(invokers, extension.GetProtocol(u.Protocol).Refer(u)) if u.Protocol == constant.REGISTRY_PROTOCOL { regUrl = u } @@ -168,7 +165,8 @@ func (c *ReferenceConfig) Refer(_ interface{}) { // FailoverClusterInvoker(RegistryDirectory, routing happens here) -> Invoker c.invoker = cluster.Join(directory.NewStaticDirectory(invokers)) } - + // publish consumer metadata + publishConsumerDefinition(cfgURL) // create proxy if c.Async { callback := GetCallback(c.id) @@ -189,6 +187,11 @@ func (c *ReferenceConfig) GetRPCService() common.RPCService { return c.pxy.Get() } +// GetProxy gets proxy +func (c *ReferenceConfig) GetProxy() *proxy.Proxy { + return c.pxy +} + func (c *ReferenceConfig) getUrlMap() url.Values { urlMap := url.Values{} //first set user params @@ -252,3 +255,21 @@ func (c *ReferenceConfig) GenericLoad(id string) { c.Refer(genericService) c.Implement(genericService) } + +// GetInvoker get invoker from ReferenceConfig +func (c *ReferenceConfig) GetInvoker() protocol.Invoker { + return c.invoker +} + +func publishConsumerDefinition(url *common.URL) { + if remoteMetadataService, err := extension.GetRemoteMetadataService(); err == nil && remoteMetadataService != nil { + remoteMetadataService.PublishServiceDefinition(url) + } +} + +// postProcessConfig asks registered ConfigPostProcessor to post-process the current ReferenceConfig. +func (c *ReferenceConfig) postProcessConfig(url *common.URL) { + for _, p := range extension.GetConfigPostProcessors() { + p.PostProcessReferenceConfig(url) + } +} diff --git a/config/reference_config_test.go b/config/reference_config_test.go index a4345ad13d4944b8fae0930930584fccbd0fb33c..0207e1ff24af022690e3773573ddd61ebad88e15 100644 --- a/config/reference_config_test.go +++ b/config/reference_config_test.go @@ -334,7 +334,7 @@ func newRegistryProtocol() protocol.Protocol { type mockRegistryProtocol struct{} -func (*mockRegistryProtocol) Refer(url common.URL) protocol.Invoker { +func (*mockRegistryProtocol) Refer(url *common.URL) protocol.Invoker { return protocol.NewBaseInvoker(url) } @@ -345,7 +345,7 @@ func (*mockRegistryProtocol) Export(invoker protocol.Invoker) protocol.Exporter if err != nil { panic(err) } - ok, err := metaDataService.ExportURL(*invoker.GetUrl().SubURL.Clone()) + ok, err := metaDataService.ExportURL(invoker.GetUrl().SubURL.Clone()) if err != nil { panic(err) } @@ -367,7 +367,7 @@ func getRegistryUrl(invoker protocol.Invoker) *common.URL { protocol := url.GetParam(constant.REGISTRY_KEY, "") url.Protocol = protocol } - return &url + return url } func (p *mockRegistryProtocol) GetRegistries() []registry.Registry { diff --git a/config/registry_config.go b/config/registry_config.go index 89566c428ed14f460c0f214358c9fa05d529ddb6..ed81a07c637e5de2972bbbd21dab8847c3d42f78 100644 --- a/config/registry_config.go +++ b/config/registry_config.go @@ -53,7 +53,7 @@ type RegistryConfig struct { //ZoneForce bool `yaml:"zoneForce" json:"zoneForce,omitempty" property:"zoneForce"` // Affects traffic distribution among registries, // useful when subscribe to multiple registries Take effect only when no preferred registry is specified. - Weight int64 `yaml:"weight" json:"params,omitempty" property:"weight"` + Weight int64 `yaml:"weight" json:"weight,omitempty" property:"weight"` Params map[string]string `yaml:"params" json:"params,omitempty" property:"params"` } @@ -63,10 +63,7 @@ func (c *RegistryConfig) UnmarshalYAML(unmarshal func(interface{}) error) error return err } type plain RegistryConfig - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return nil + return unmarshal((*plain)(c)) } // nolint @@ -114,7 +111,7 @@ func loadRegistries(targetRegistries string, registries map[string]*RegistryConf logger.Errorf("The registry id: %s url is invalid, error: %#v", k, err) panic(err) } else { - urls = append(urls, &url) + urls = append(urls, url) } } } diff --git a/config/registry_config_test.go b/config/registry_config_test.go index 6e5dedc34ff5489fc190841bce73cd015eb78132..8bd2de1735496ab9e785759609d8bc3459fc1505 100644 --- a/config/registry_config_test.go +++ b/config/registry_config_test.go @@ -17,7 +17,6 @@ package config import ( - "fmt" "testing" ) @@ -43,7 +42,7 @@ func TestLoadRegistries(t *testing.T) { }, } urls := loadRegistries(target, regs, common.CONSUMER) - fmt.Println(urls[0]) + t.Logf("loadRegistries() = urls:%v", urls) assert.Equal(t, "127.0.0.2:2181,128.0.0.1:2181", urls[0].Location) } @@ -61,6 +60,6 @@ func TestLoadRegistries1(t *testing.T) { }, } urls := loadRegistries(target, regs, common.CONSUMER) - fmt.Println(urls[0]) + t.Logf("loadRegistries() = urls:%v", urls) assert.Equal(t, "127.0.0.2:2181", urls[0].Location) } diff --git a/config/remote_config.go b/config/remote_config.go index 0f0c3e5cb7991e19ea0ad722fc4d40da01c1fad7..61d4dce0cdc104d6f9710d1a1f3caa540edec076 100644 --- a/config/remote_config.go +++ b/config/remote_config.go @@ -63,9 +63,9 @@ func (rc *RemoteConfig) GetParam(key string, def string) string { return param } -func (rc *RemoteConfig) toURL() (common.URL, error) { +func (rc *RemoteConfig) toURL() (*common.URL, error) { if len(rc.Protocol) == 0 { - return common.URL{}, perrors.Errorf("Must provide protocol in RemoteConfig.") + return nil, perrors.Errorf("Must provide protocol in RemoteConfig.") } return common.NewURL(rc.Address, common.WithUsername(rc.Username), diff --git a/config/router_config.go b/config/router_config.go index ed42577ed3cce2e5a1ab0da290f0d5450553d8fb..ea19b46800d747de763bbb1ee679f8398e440f3d 100644 --- a/config/router_config.go +++ b/config/router_config.go @@ -66,7 +66,7 @@ func initRouterConfig(content []byte, factories map[string]router.FilePriorityRo r, e := factory.NewFileRouter(content) if e == nil { url := r.URL() - routerURLSet.Add(&url) + routerURLSet.Add(url) return nil } logger.Warnf("router config type %s create fail {%v}\n", k, e) diff --git a/config/service_config.go b/config/service_config.go index 48632a1b1e295eb5ec3027bd495ef8d19e978ec4..e8523bdea7aa014836580b7ce33fe215c145289d 100644 --- a/config/service_config.go +++ b/config/service_config.go @@ -73,10 +73,12 @@ type ServiceConfig struct { Auth string `yaml:"auth" json:"auth,omitempty" property:"auth"` ParamSign string `yaml:"param.sign" json:"param.sign,omitempty" property:"param.sign"` Tag string `yaml:"tag" json:"tag,omitempty" property:"tag"` + GrpcMaxMessageSize int `default:"4" yaml:"max_message_size" json:"max_message_size,omitempty"` Protocols map[string]*ProtocolConfig unexported *atomic.Bool exported *atomic.Bool + export bool // a flag to control whether the current service should export or not rpcService common.RPCService cacheMutex sync.Mutex cacheProtocol protocol.Protocol @@ -101,6 +103,7 @@ func (c *ServiceConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { } c.exported = atomic.NewBool(false) c.unexported = atomic.NewBool(false) + c.export = true return nil } @@ -111,6 +114,7 @@ func NewServiceConfig(id string, context context.Context) *ServiceConfig { id: id, unexported: atomic.NewBool(false), exported: atomic.NewBool(false), + export: true, } } @@ -170,9 +174,10 @@ func (c *ServiceConfig) Export() error { proxyFactory := extension.GetProxyFactory(providerConfig.ProxyFactory) for _, proto := range protocolConfigs { // registry the service reflect - methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.rpcService) + methods, err := common.ServiceMap.Register(c.InterfaceName, proto.Name, c.Group, c.Version, 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 } @@ -183,7 +188,7 @@ func (c *ServiceConfig) Export() error { nextPort = nextPort.Next() } ivkURL := common.NewURLWithOptions( - common.WithPath(c.id), + common.WithPath(c.InterfaceName), common.WithProtocol(proto.Name), common.WithIp(proto.Ip), common.WithPort(port), @@ -197,6 +202,13 @@ func (c *ServiceConfig) Export() error { ivkURL.AddParam(constant.Tagkey, c.Tag) } + // post process the URL to be exported + c.postProcessConfig(ivkURL) + // config post processor may set "export" to false + if !ivkURL.GetParamBool(constant.EXPORT_KEY, true) { + return nil + } + if len(regUrls) > 0 { c.cacheMutex.Lock() if c.cacheProtocol == nil { @@ -207,7 +219,7 @@ func (c *ServiceConfig) Export() error { for _, regUrl := range regUrls { regUrl.SubURL = ivkURL - invoker := proxyFactory.GetInvoker(*regUrl) + invoker := proxyFactory.GetInvoker(regUrl) exporter := c.cacheProtocol.Export(invoker) if exporter == nil { return perrors.New(fmt.Sprintf("Registry protocol new exporter error, registry is {%v}, url is {%v}", regUrl, ivkURL)) @@ -215,13 +227,14 @@ func (c *ServiceConfig) Export() error { c.exporters = append(c.exporters, exporter) } } else { - invoker := proxyFactory.GetInvoker(*ivkURL) + invoker := proxyFactory.GetInvoker(ivkURL) exporter := extension.GetProtocol(protocolwrapper.FILTER).Export(invoker) if exporter == nil { return perrors.New(fmt.Sprintf("Filter protocol without registry new exporter error, url is {%v}", ivkURL)) } c.exporters = append(c.exporters, exporter) } + publishServiceDefinition(ivkURL) } c.exported.Store(true) return nil @@ -271,6 +284,7 @@ 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()) + urlMap.Set(constant.MESSAGE_SIZE_KEY, strconv.Itoa(c.GrpcMaxMessageSize)) // todo: move urlMap.Set(constant.SERIALIZATION_KEY, c.Serialization) // application info @@ -300,7 +314,10 @@ func (c *ServiceConfig) getUrlMap() url.Values { // auth filter urlMap.Set(constant.SERVICE_AUTH_KEY, c.Auth) - urlMap.Set(constant.PARAMTER_SIGNATURE_ENABLE_KEY, c.ParamSign) + urlMap.Set(constant.PARAMETER_SIGNATURE_ENABLE_KEY, c.ParamSign) + + // whether to export or not + urlMap.Set(constant.EXPORT_KEY, strconv.FormatBool(c.export)) for _, v := range c.Methods { prefix := "methods." + v.Name + "." @@ -324,10 +341,22 @@ func (c *ServiceConfig) GetExportedUrls() []*common.URL { if c.exported.Load() { var urls []*common.URL for _, exporter := range c.exporters { - url := exporter.GetInvoker().GetUrl() - urls = append(urls, &url) + urls = append(urls, exporter.GetInvoker().GetUrl()) } return urls } return nil } + +func publishServiceDefinition(url *common.URL) { + if remoteMetadataService, err := extension.GetRemoteMetadataService(); err == nil && remoteMetadataService != nil { + remoteMetadataService.PublishServiceDefinition(url) + } +} + +// postProcessConfig asks registered ConfigPostProcessor to post-process the current ServiceConfig. +func (c *ServiceConfig) postProcessConfig(url *common.URL) { + for _, p := range extension.GetConfigPostProcessors() { + p.PostProcessServiceConfig(url) + } +} diff --git a/config/service_config_test.go b/config/service_config_test.go index 4d4122ee7057043af47aa0400ca8c5b5e9a20cd0..aea0bde7a25d14bb720af0b83f06f44bdd6806c9 100644 --- a/config/service_config_test.go +++ b/config/service_config_test.go @@ -18,11 +18,11 @@ package config import ( + "github.com/apache/dubbo-go/common" "testing" ) import ( - gxnet "github.com/dubbogo/gost/net" "github.com/stretchr/testify/assert" "go.uber.org/atomic" ) @@ -176,7 +176,8 @@ func TestExport(t *testing.T) { service := providerConfig.Services[i] service.Implement(&MockService{}) service.Protocols = providerConfig.Protocols - service.Export() + err := service.Export() + assert.Nil(t, err) } providerConfig = nil } @@ -184,7 +185,7 @@ func TestExport(t *testing.T) { func TestGetRandomPort(t *testing.T) { protocolConfigs := make([]*ProtocolConfig, 0, 3) - ip, err := gxnet.GetLocalIP() + ip := common.GetLocalIp() protocolConfigs = append(protocolConfigs, &ProtocolConfig{ Ip: ip, }) @@ -194,7 +195,7 @@ func TestGetRandomPort(t *testing.T) { protocolConfigs = append(protocolConfigs, &ProtocolConfig{ Ip: ip, }) - assert.NoError(t, err) + //assert.NoError(t, err) ports := getRandomPort(protocolConfigs) assert.Equal(t, ports.Len(), len(protocolConfigs)) diff --git a/config/testdata/router_config.yml b/config/testdata/router_config.yml index 1845650d93c152585c4d4caf60ab7aa3d5b04887..eec4c8624f26c4007b1b8d7b7ed46ee0bf354745 100644 --- a/config/testdata/router_config.yml +++ b/config/testdata/router_config.yml @@ -1,5 +1,5 @@ # dubbo router yaml configure file -routerRules: +routerRules: - scope: application key: mock-app priority: 1 diff --git a/config/testdata/router_config_error.yml b/config/testdata/router_config_error.yml index 74e89cc52ec772a52c3c424a94276075fb099f8c..1a1fda696663f5bee0e7bd5af1752558033ba333 100644 --- a/config/testdata/router_config_error.yml +++ b/config/testdata/router_config_error.yml @@ -1,5 +1,5 @@ # dubbo router yaml configure file -routerRules: +routerRules: - priority: 1 force: true noConditions : diff --git a/config_center/apollo/impl.go b/config_center/apollo/impl.go index ac5328c27a95425333276be58e6dd614e23bb5ac..5b74f5e15013c07de000cc18cab98238b96ac060 100644 --- a/config_center/apollo/impl.go +++ b/config_center/apollo/impl.go @@ -109,11 +109,11 @@ func getNamespaceName(namespace string, configFileFormat agolloConstant.ConfigFi } func (c *apolloConfiguration) GetInternalProperty(key string, opts ...cc.Option) (string, error) { - config := agollo.GetConfig(c.appConf.NamespaceName) - if config == nil { + newConfig := agollo.GetConfig(c.appConf.NamespaceName) + if newConfig == nil { return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } - return config.GetStringValue(key, ""), nil + return newConfig.GetStringValue(key, ""), nil } func (c *apolloConfiguration) GetRule(key string, opts ...cc.Option) (string, error) { @@ -135,20 +135,19 @@ func (c *apolloConfiguration) GetProperties(key string, opts ...cc.Option) (stri * when group is not null, we are getting startup configs(config file) from Config Center, for example: * key=dubbo.propertie */ - config := agollo.GetConfig(key) - if config == nil { + tmpConfig := agollo.GetConfig(key) + if tmpConfig == nil { return "", perrors.New(fmt.Sprintf("nothing in namespace:%s ", key)) } - return config.GetContent(), nil + return tmpConfig.GetContent(), nil } func (c *apolloConfiguration) getAddressWithProtocolPrefix(url *common.URL) string { address := url.Location converted := address if len(address) != 0 { - reg := regexp.MustCompile("\\s+") - address = reg.ReplaceAllString(address, "") - parts := strings.Split(address, ",") + addr := regexp.MustCompile(`\s+`).ReplaceAllString(address, "") + parts := strings.Split(addr, ",") addrs := make([]string, 0) for _, part := range parts { addr := part diff --git a/config_center/apollo/impl_test.go b/config_center/apollo/impl_test.go index 50c4e689de4542e1c595d0778e2dce69b1e7dda9..3b2cb168402534c1a8071e644827a0f948282f75 100644 --- a/config_center/apollo/impl_test.go +++ b/config_center/apollo/impl_test.go @@ -126,24 +126,24 @@ func initApollo() *httptest.Server { } func configResponse(rw http.ResponseWriter, _ *http.Request) { - result := fmt.Sprintf(mockConfigRes) + result := mockConfigRes fmt.Fprintf(rw, "%s", result) } func notifyResponse(rw http.ResponseWriter, req *http.Request) { - result := fmt.Sprintf(mockNotifyRes) + result := mockNotifyRes fmt.Fprintf(rw, "%s", result) } func serviceConfigResponse(rw http.ResponseWriter, _ *http.Request) { - result := fmt.Sprintf(mockServiceConfigRes) + result := mockServiceConfigRes fmt.Fprintf(rw, "%s", result) } // run mock config server func runMockConfigServer(handlerMap map[string]func(http.ResponseWriter, *http.Request), notifyHandler func(http.ResponseWriter, *http.Request)) *httptest.Server { - uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 0) + uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request)) for namespace, handler := range handlerMap { uri := fmt.Sprintf("/configs/%s/%s/%s", mockAppId, mockCluster, namespace) uriHandlerMap[uri] = handler @@ -197,7 +197,7 @@ func initMockApollo(t *testing.T) *apolloConfiguration { apolloUrl := strings.ReplaceAll(apollo.URL, "http", "apollo") url, err := common.NewURL(apolloUrl, common.WithParams(c.ConfigCenterConfig.GetUrlMap())) assert.NoError(t, err) - configuration, err := newApolloConfiguration(&url) + configuration, err := newApolloConfiguration(url) assert.NoError(t, err) return configuration } @@ -227,7 +227,7 @@ func TestListener(t *testing.T) { apollo.listeners.Range(func(_, value interface{}) bool { apolloListener := value.(*apolloListener) for e := range apolloListener.listeners { - fmt.Println(e) + t.Logf("listener:%v", e) listenerCount++ } return true diff --git a/config_center/apollo/listener.go b/config_center/apollo/listener.go index ace5ed026875d2478e5d81b1974a6c60f856378c..44d325582f3b04871aaadc5b39cbc17ab7a0eeea 100644 --- a/config_center/apollo/listener.go +++ b/config_center/apollo/listener.go @@ -36,7 +36,7 @@ type apolloListener struct { // nolint func newApolloListener() *apolloListener { return &apolloListener{ - listeners: make(map[config_center.ConfigurationListener]struct{}, 0), + listeners: make(map[config_center.ConfigurationListener]struct{}), } } diff --git a/config_center/configurator/override.go b/config_center/configurator/override.go index ec4e606e0dff300729e2a2bc56f510db94ea9f26..7f5abb6175056b44a5d9c6a9fa527ad6e9ac6791 100644 --- a/config_center/configurator/override.go +++ b/config_center/configurator/override.go @@ -23,7 +23,6 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" - gxnet "github.com/dubbogo/gost/net" ) import ( @@ -61,7 +60,7 @@ func (c *overrideConfigurator) Configure(url *common.URL) { currentSide := url.GetParam(constant.SIDE_KEY, "") configuratorSide := c.configuratorUrl.GetParam(constant.SIDE_KEY, "") if currentSide == configuratorSide && common.DubboRole[common.CONSUMER] == currentSide && c.configuratorUrl.Port == "0" { - localIP, _ := gxnet.GetLocalIP() + localIP := common.GetLocalIp() c.configureIfMatch(localIP, url) } else if currentSide == configuratorSide && common.DubboRole[common.PROVIDER] == currentSide && c.configuratorUrl.Port == url.Port { c.configureIfMatch(url.Ip, url) @@ -127,7 +126,7 @@ func (c *overrideConfigurator) configureDeprecated(url *common.URL) { // 1.If it is a consumer ip address, the intention is to control a specific consumer instance, it must takes effect at the consumer side, any provider received this override url should ignore; // 2.If the ip is 0.0.0.0, this override url can be used on consumer, and also can be used on provider if url.GetParam(constant.SIDE_KEY, "") == common.DubboRole[common.CONSUMER] { - localIP, _ := gxnet.GetLocalIP() + localIP := common.GetLocalIp() c.configureIfMatch(localIP, url) } else { c.configureIfMatch(constant.ANYHOST_VALUE, url) diff --git a/config_center/configurator/override_test.go b/config_center/configurator/override_test.go index 8eccb5091272b033cf31b612dfb19bce6514ccce..4d2552d1845e57ada38b635bf3cb047891e73abf 100644 --- a/config_center/configurator/override_test.go +++ b/config_center/configurator/override_test.go @@ -37,51 +37,51 @@ const ( failover = "failover" ) -func TestConfigureVerison2p6(t *testing.T) { +func TestConfigureVersion2p6(t *testing.T) { url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) - configurator := extension.GetConfigurator(defaults, &url) + configurator := extension.GetConfigurator(defaults, url) assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) - configurator.Configure(&providerUrl) + configurator.Configure(providerUrl) assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func TestConfigureVerisonOverrideAddr(t *testing.T) { +func TestConfigureVersionOverrideAddr(t *testing.T) { url, err := common.NewURL("override://0.0.0.0:0/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&providerAddresses=127.0.0.2:20001|127.0.0.3:20001") assert.NoError(t, err) - configurator := extension.GetConfigurator(defaults, &url) + configurator := extension.GetConfigurator(defaults, url) assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) - configurator.Configure(&providerUrl) + configurator.Configure(providerUrl) assert.Equal(t, failover, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func TestConfigureVerison2p6WithIp(t *testing.T) { +func TestConfigureVersion2p6WithIp(t *testing.T) { url, err := common.NewURL("override://127.0.0.1:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService") assert.NoError(t, err) - configurator := extension.GetConfigurator(defaults, &url) + configurator := extension.GetConfigurator(defaults, url) assert.Equal(t, override, configurator.GetUrl().Protocol) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) - configurator.Configure(&providerUrl) + configurator.Configure(providerUrl) assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } -func TestConfigureVerison2p7(t *testing.T) { +func TestConfigureVersion2p7(t *testing.T) { url, err := common.NewURL("jsonrpc://0.0.0.0:20001/com.xxx.mock.userProvider?group=1&version=1&cluster=failfast&application=BDTService&configVersion=1.0&side=provider") assert.NoError(t, err) - configurator := extension.GetConfigurator(defaults, &url) + configurator := extension.GetConfigurator(defaults, url) providerUrl, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&app.version=0.0.1&application=BDTService&category=providers&cluster=failover&dubbo=dubbo-provider-golang-2.6.0&environment=dev&group=&interface=com.ikurento.user.UserProvider&ip=10.32.20.124&loadbalance=random&methods.GetUser.loadbalance=random&methods.GetUser.retries=1&methods.GetUser.weight=0&module=dubbogo+user-info+server&name=BDTService&organization=ikurento.com&owner=ZX&pid=64225&retries=0&service.filter=echo&side=provider×tamp=1562076628&version=&warmup=100") assert.NoError(t, err) - configurator.Configure(&providerUrl) + configurator.Configure(providerUrl) assert.Equal(t, failfast, providerUrl.GetParam(constant.CLUSTER_KEY, "")) } diff --git a/config_center/dynamic_configuration.go b/config_center/dynamic_configuration.go index cbf8e8cf031ed20d6e69c5a3d7556f22ad4dc223..69f84213e7ecd226331821a07fc9bcfc48cd735c 100644 --- a/config_center/dynamic_configuration.go +++ b/config_center/dynamic_configuration.go @@ -89,6 +89,6 @@ func WithTimeout(time time.Duration) Option { } // GetRuleKey The format is '{interfaceName}:[version]:[group]' -func GetRuleKey(url common.URL) string { +func GetRuleKey(url *common.URL) string { return url.ColonSeparatedKey() } diff --git a/config_center/file/impl.go b/config_center/file/impl.go index 9d8254026bf3740631798bb0906436029be15abd..6489a073deff643ad7eecc7f3c26ef0b8899ac53 100644 --- a/config_center/file/impl.go +++ b/config_center/file/impl.go @@ -24,7 +24,6 @@ import ( "os" "os/exec" "os/user" - "path" "path/filepath" "runtime" "strings" @@ -41,11 +40,19 @@ import ( "github.com/apache/dubbo-go/config_center/parser" ) +var ( + osType = runtime.GOOS +) + +const ( + windowsOS = "windows" +) + 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" + ParamNamePrefix = "dubbo.config-center." + ConfigCenterDirParamName = ParamNamePrefix + "dir" + ConfigCenterEncodingParamName = ParamNamePrefix + "encoding" + defaultConfigCenterEncoding = "UTF-8" ) // FileSystemDynamicConfiguration @@ -59,24 +66,14 @@ type FileSystemDynamicConfiguration struct { } func newFileSystemDynamicConfiguration(url *common.URL) (*FileSystemDynamicConfiguration, error) { - encode := url.GetParam(CONFIG_CENTER_ENCODING_PARAM_NAME, DEFAULT_CONFIG_CENTER_ENCODING) + encode := url.GetParam(ConfigCenterEncodingParamName, defaultConfigCenterEncoding) - root := url.GetParam(CONFIG_CENTER_DIR_PARAM_NAME, "") + root := url.GetParam(ConfigCenterDirParamName, "") 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) - } + root, err := mkdirIfNecessary(root) + if err != nil { + return nil, err } c = &FileSystemDynamicConfiguration{ @@ -113,9 +110,8 @@ func (fsdc *FileSystemDynamicConfiguration) AddListener(key string, listener con opt(tmpOpts) } - path := fsdc.GetPath(key, tmpOpts.Group) - - fsdc.cacheListener.AddListener(path, listener) + tmpPath := fsdc.GetPath(key, tmpOpts.Group) + fsdc.cacheListener.AddListener(tmpPath, listener) } // RemoveListener Remove listener @@ -126,9 +122,8 @@ func (fsdc *FileSystemDynamicConfiguration) RemoveListener(key string, listener opt(tmpOpts) } - path := fsdc.GetPath(key, tmpOpts.Group) - - fsdc.cacheListener.RemoveListener(path, listener) + tmpPath := fsdc.GetPath(key, tmpOpts.Group) + fsdc.cacheListener.RemoveListener(tmpPath, listener) } // GetProperties get properties file @@ -138,12 +133,11 @@ func (fsdc *FileSystemDynamicConfiguration) GetProperties(key string, opts ...co opt(tmpOpts) } - path := fsdc.GetPath(key, tmpOpts.Group) - file, err := ioutil.ReadFile(path) + tmpPath := fsdc.GetPath(key, tmpOpts.Group) + file, err := ioutil.ReadFile(tmpPath) if err != nil { return "", perrors.WithStack(err) } - return string(file), nil } @@ -160,16 +154,16 @@ func (fsdc *FileSystemDynamicConfiguration) GetInternalProperty(key string, 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) + tmpPath := fsdc.GetPath(key, group) + return fsdc.write2File(tmpPath, value) } // GetConfigKeysByGroup will return all keys with the group func (fsdc *FileSystemDynamicConfiguration) GetConfigKeysByGroup(group string) (*gxset.HashSet, error) { - path := fsdc.GetPath("", group) + tmpPath := fsdc.GetPath("", group) r := gxset.NewSet() - fileInfo, _ := ioutil.ReadDir(path) + fileInfo, _ := ioutil.ReadDir(tmpPath) for _, file := range fileInfo { // list file @@ -183,10 +177,10 @@ func (fsdc *FileSystemDynamicConfiguration) GetConfigKeysByGroup(group string) ( return r, nil } -// RemoveConfig will remove the config whit hte (key, group) +// RemoveConfig will remove tconfig_center/nacos/impl_testhe config whit hte (key, group) func (fsdc *FileSystemDynamicConfiguration) RemoveConfig(key string, group string) error { - path := fsdc.GetPath(key, group) - _, err := fsdc.deleteDelay(path) + tmpPath := fsdc.GetPath(key, group) + _, err := fsdc.deleteDelay(tmpPath) return err } @@ -198,18 +192,18 @@ func (fsdc *FileSystemDynamicConfiguration) Close() error { // GetPath get path func (fsdc *FileSystemDynamicConfiguration) GetPath(key string, group string) string { if len(key) == 0 { - return path.Join(fsdc.rootPath, group) + return filepath.Join(fsdc.rootPath, group) } if len(group) == 0 { group = config_center.DEFAULT_GROUP } - return path.Join(fsdc.rootPath, group, key) + return filepath.Join(fsdc.rootPath, group, adapterKey(key)) } func (fsdc *FileSystemDynamicConfiguration) deleteDelay(path string) (bool, error) { - if path == "" { + if len(path) == 0 { return false, nil } @@ -229,9 +223,7 @@ func (fsdc *FileSystemDynamicConfiguration) write2File(fp string, value string) } func forceMkdirParent(fp string) error { - pd := getParentDirectory(fp) - - return createDir(pd) + return createDir(getParentDirectory(fp)) } func createDir(path string) error { @@ -253,6 +245,7 @@ func substr(s string, pos, length int) string { if l > len(runes) { l = len(runes) } + return string(runes[pos:l]) } @@ -261,13 +254,13 @@ func substr(s string, pos, length int) string { // 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() + currentUser, err := user.Current() if nil == err { - return user.HomeDir, nil + return currentUser.HomeDir, nil } // cross compile support - if "windows" == runtime.GOOS { + if windowsOS == osType { return homeWindows() } @@ -290,7 +283,7 @@ func homeUnix() (string, error) { } result := strings.TrimSpace(stdout.String()) - if result == "" { + if len(result) == 0 { return "", errors.New("blank output when reading home directory") } @@ -299,14 +292,68 @@ func homeUnix() (string, error) { func homeWindows() (string, error) { drive := os.Getenv("HOMEDRIVE") - path := os.Getenv("HOMEPATH") - home := drive + path - if drive == "" || path == "" { + homePath := os.Getenv("HOMEPATH") + home := drive + homePath + if len(drive) == 0 || len(homePath) == 0 { home = os.Getenv("USERPROFILE") } - if home == "" { + if len(home) == 0 { return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank") } return home, nil } + +func mkdirIfNecessary(urlRoot string) (string, error) { + if !legalPath(urlRoot) { + // not exist, use default, mac is: /XXX/xx/.dubbo/config-center + rp, err := Home() + if err != nil { + return "", perrors.WithStack(err) + } + + urlRoot = adapterUrl(rp) + } + + if _, err := os.Stat(urlRoot); err != nil { + // it must be dir, if not exist, will create + if err = createDir(urlRoot); err != nil { + return "", perrors.WithStack(err) + } + } + + return urlRoot, nil +} + +func legalPath(path string) bool { + if len(path) == 0 { + return false + } + if _, err := os.Stat(path); err != nil { + return false + } + + return true +} + +func adapterUrl(rp string) string { + if osType == windowsOS { + return filepath.Join(rp, "_dubbo", "config-center") + } + + return filepath.Join(rp, ".dubbo", "config-center") +} + +// used for GetPath. param key default is instance's id. +// e.g: (ip:port) 127.0.0.1:20081, in windows env, will change to 127_0_0_1_20081 +func adapterKey(key string) string { + if len(key) == 0 { + return "" + } + + if osType == windowsOS { + return strings.ReplaceAll(strings.ReplaceAll(key, ".", "_"), ":", "_") + } + + return key +} diff --git a/config_center/file/impl_test.go b/config_center/file/impl_test.go index 58892953d556512a88689baa5110995091d75f7b..556b6a3d7aa168a83b0861936ad1dc54f827eb18 100644 --- a/config_center/file/impl_test.go +++ b/config_center/file/impl_test.go @@ -42,7 +42,7 @@ 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) + dc, err := extension.GetConfigCenterFactory("file").GetDynamicConfiguration(regurl) assert.NoError(t, err) return dc.(*FileSystemDynamicConfiguration), err @@ -63,6 +63,7 @@ func TestPublishAndGetConfig(t *testing.T) { func TestAddListener(t *testing.T) { file, err := initFileData(t) + assert.Nil(t, err) group := "dubbogo" value := "Test Value" err = file.PublishConfig(key, group, value) @@ -81,6 +82,7 @@ func TestAddListener(t *testing.T) { func TestRemoveListener(t *testing.T) { file, err := initFileData(t) + assert.NoError(t, err) group := "dubbogo" value := "Test Value" err = file.PublishConfig(key, group, value) @@ -106,9 +108,11 @@ func TestRemoveListener(t *testing.T) { func TestGetConfigKeysByGroup(t *testing.T) { file, err := initFileData(t) + assert.Nil(t, err) group := "dubbogo" value := "Test Value" err = file.PublishConfig(key, group, value) + assert.NoError(t, err) gs, err := file.GetConfigKeysByGroup(group) assert.NoError(t, err) assert.Equal(t, 1, gs.Size()) diff --git a/config_center/nacos/client.go b/config_center/nacos/client.go index acfe2609ceb0fc62650b7b494b25084ea4df6946..1e96b36a4f19b8548d2ffc9818251ebfa96f365b 100644 --- a/config_center/nacos/client.go +++ b/config_center/nacos/client.go @@ -18,7 +18,6 @@ package nacos import ( - "path/filepath" "strconv" "strings" "sync" @@ -38,9 +37,6 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -// Nacos Log dir, it can be override when creating client by config_center.log_dir -var logDir = filepath.Join("logs", "nacos", "log") - // NacosClient Nacos client type NacosClient struct { name string @@ -69,7 +65,7 @@ type option func(*options) type options struct { nacosName string - client *NacosClient + //client *NacosClient } // WithNacosName Set nacos name @@ -122,7 +118,7 @@ func ValidateNacosClient(container nacosClientFacade, opts ...option) error { return perrors.WithMessagef(nil, "newNacosClient(address:%+v)", url.PrimitiveURL) } -func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url common.URL) (*NacosClient, error) { +func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url *common.URL) (*NacosClient, error) { var ( err error n *NacosClient @@ -149,8 +145,8 @@ func newNacosClient(name string, nacosAddrs []string, timeout time.Duration, url return n, nil } -func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url common.URL) (config_client.IConfigClient, error) { - svrConfList := []nacosconst.ServerConfig{} +func initNacosConfigClient(nacosAddrs []string, timeout time.Duration, url *common.URL) (config_client.IConfigClient, error) { + var svrConfList []nacosconst.ServerConfig for _, nacosAddr := range nacosAddrs { split := strings.Split(nacosAddr, ":") port, err := strconv.ParseUint(split[1], 10, 64) diff --git a/config_center/nacos/client_test.go b/config_center/nacos/client_test.go index 01319f362b956a0a15be8d5d4dde2a8b2be57c89..2ce3e375557c69721611799d414b3db31d141fca 100644 --- a/config_center/nacos/client_test.go +++ b/config_center/nacos/client_test.go @@ -36,7 +36,7 @@ func TestNewNacosClient(t *testing.T) { nacosURL := strings.ReplaceAll(server.URL, "http", "registry") registryUrl, _ := common.NewURL(nacosURL) c := &nacosDynamicConfiguration{ - url: ®istryUrl, + url: registryUrl, done: make(chan struct{}), } err := ValidateNacosClient(c, WithNacosName(nacosClientName)) @@ -59,7 +59,7 @@ func TestSetNacosClient(t *testing.T) { nacosURL := "registry://" + server.Listener.Addr().String() registryUrl, _ := common.NewURL(nacosURL) c := &nacosDynamicConfiguration{ - url: ®istryUrl, + url: registryUrl, done: make(chan struct{}), } var client *NacosClient @@ -93,7 +93,7 @@ func TestNewNacosClient_connectError(t *testing.T) { registryUrl, err := common.NewURL(nacosURL) assert.NoError(t, err) c := &nacosDynamicConfiguration{ - url: ®istryUrl, + url: registryUrl, done: make(chan struct{}), } err = ValidateNacosClient(c, WithNacosName(nacosClientName)) diff --git a/config_center/nacos/impl.go b/config_center/nacos/impl.go index be94b9a2e356b797b580ef4861895259ca7a4315..7c67930026369e7e2b6c58fc57d05d3a9edb50cc 100644 --- a/config_center/nacos/impl.go +++ b/config_center/nacos/impl.go @@ -186,8 +186,8 @@ func (n *nacosDynamicConfiguration) GetDone() chan struct{} { } // GetUrl Get Url -func (n *nacosDynamicConfiguration) GetUrl() common.URL { - return *n.url +func (n *nacosDynamicConfiguration) GetUrl() *common.URL { + return n.url } // Destroy Destroy configuration instance diff --git a/config_center/nacos/impl_test.go b/config_center/nacos/impl_test.go index 88d200edc927c62967975dff28e19c03743125f0..b7bd94bf96bfdafed1591e58f338fa23a0b96a6f 100644 --- a/config_center/nacos/impl_test.go +++ b/config_center/nacos/impl_test.go @@ -40,7 +40,7 @@ import ( // run mock config server func runMockConfigServer(configHandler func(http.ResponseWriter, *http.Request), configListenHandler func(http.ResponseWriter, *http.Request)) *httptest.Server { - uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request), 0) + uriHandlerMap := make(map[string]func(http.ResponseWriter, *http.Request)) uriHandlerMap["/nacos/v1/cs/configs"] = configHandler uriHandlerMap["/nacos/v1/cs/configs/listener"] = configListenHandler @@ -73,7 +73,7 @@ func initNacosData(t *testing.T) (*nacosDynamicConfiguration, error) { nacosURL := strings.ReplaceAll(server.URL, "http", "registry") regurl, _ := common.NewURL(nacosURL) factory := &nacosDynamicConfigurationFactory{} - nacosConfiguration, err := factory.GetDynamicConfiguration(®url) + nacosConfiguration, err := factory.GetDynamicConfiguration(regurl) assert.NoError(t, err) nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{}) @@ -85,6 +85,8 @@ func TestGetConfig(t *testing.T) { nacos, err := initNacosData(t) assert.NoError(t, err) configs, err := nacos.GetProperties("dubbo.properties", config_center.WithGroup("dubbo")) + assert.Empty(t, configs) + assert.NoError(t, err) _, err = nacos.Parser().Parse(configs) assert.NoError(t, err) } @@ -100,12 +102,13 @@ func TestNacosDynamicConfiguration_GetConfigKeysByGroup(t *testing.T) { } ` ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write([]byte(data)) + _, err := w.Write([]byte(data)) + assert.Nil(t, err) })) nacosURL := strings.ReplaceAll(ts.URL, "http", "registry") regurl, _ := common.NewURL(nacosURL) - nacosConfiguration, err := newNacosDynamicConfiguration(®url) + nacosConfiguration, err := newNacosDynamicConfiguration(regurl) assert.NoError(t, err) nacosConfiguration.SetParser(&parser.DefaultConfigurationParser{}) diff --git a/config_center/nacos/listener.go b/config_center/nacos/listener.go index 57df0363a1d7712e1e02a66595030c207b19c170..b0607e29b4d4d3d5caf9ee7072d9c2e44088738a 100644 --- a/config_center/nacos/listener.go +++ b/config_center/nacos/listener.go @@ -38,7 +38,6 @@ func callback(listener config_center.ConfigurationListener, _, _, dataId, data s func (n *nacosDynamicConfiguration) addListener(key string, listener config_center.ConfigurationListener) { _, loaded := n.keyListeners.Load(key) if !loaded { - _, cancel := context.WithCancel(context.Background()) err := (*n.client.Client()).ListenConfig(vo.ConfigParam{ DataId: key, Group: "dubbo", @@ -50,13 +49,15 @@ func (n *nacosDynamicConfiguration) addListener(key string, listener config_cent logger.Errorf("nacos : listen config fail, error:%v ", err) return } + _, cancel := context.WithCancel(context.Background()) newListener := make(map[config_center.ConfigurationListener]context.CancelFunc) newListener[listener] = cancel n.keyListeners.Store(key, newListener) - } else { - // TODO check goroutine alive, but this version of go_nacos_sdk is not support. - logger.Infof("profile:%s. this profile is already listening", key) + return } + + // TODO check goroutine alive, but this version of go_nacos_sdk is not support. + logger.Infof("profile:%s. this profile is already listening", key) } func (n *nacosDynamicConfiguration) removeListener(key string, listener config_center.ConfigurationListener) { diff --git a/config_center/parser/configuration_parser.go b/config_center/parser/configuration_parser.go index f794221f9cf3c689d52b32a57ab51f7908f17297..b104d3ddb6b2e10842b82a0498e039cb7e17ab12 100644 --- a/config_center/parser/configuration_parser.go +++ b/config_center/parser/configuration_parser.go @@ -144,14 +144,14 @@ func serviceItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.UR if err != nil { return nil, perrors.WithStack(err) } - urls = append(urls, &url) + urls = append(urls, url) } } else { url, err := common.NewURL(urlStr) if err != nil { return nil, perrors.WithStack(err) } - urls = append(urls, &url) + urls = append(urls, url) } } return urls, nil @@ -192,7 +192,7 @@ func appItemToUrls(item ConfigItem, config ConfiguratorConfig) ([]*common.URL, e if err != nil { return nil, perrors.WithStack(err) } - urls = append(urls, &url) + urls = append(urls, url) } } return urls, nil diff --git a/config_center/zookeeper/impl.go b/config_center/zookeeper/impl.go index 485abcb5f0403050f0ed4797d8cce408a23eed62..e24e63fd03d509cfcee42ab3b9f10ee4e4a31906 100644 --- a/config_center/zookeeper/impl.go +++ b/config_center/zookeeper/impl.go @@ -24,6 +24,7 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" perrors "github.com/pkg/errors" ) @@ -50,9 +51,9 @@ type zookeeperDynamicConfiguration struct { wg sync.WaitGroup cltLock sync.Mutex done chan struct{} - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient - listenerLock sync.Mutex + //listenerLock sync.Mutex listener *zookeeper.ZkEventListener cacheListener *CacheListener parser parser.ConfigurationParser @@ -63,7 +64,7 @@ func newZookeeperDynamicConfiguration(url *common.URL) (*zookeeperDynamicConfigu url: url, rootPath: "/" + url.GetParam(constant.CONFIG_NAMESPACE_KEY, config_center.DEFAULT_GROUP) + "/config", } - err := zookeeper.ValidateZookeeperClient(c, zookeeper.WithZkName(ZkClient)) + err := zookeeper.ValidateZookeeperClient(c, ZkClient) if err != nil { logger.Errorf("zookeeper client start error ,error message is %v", err) return nil, err @@ -163,11 +164,11 @@ func (c *zookeeperDynamicConfiguration) SetParser(p parser.ConfigurationParser) c.parser = p } -func (c *zookeeperDynamicConfiguration) ZkClient() *zookeeper.ZookeeperClient { +func (c *zookeeperDynamicConfiguration) ZkClient() *gxzookeeper.ZookeeperClient { return c.client } -func (c *zookeeperDynamicConfiguration) SetZkClient(client *zookeeper.ZookeeperClient) { +func (c *zookeeperDynamicConfiguration) SetZkClient(client *gxzookeeper.ZookeeperClient) { c.client = client } @@ -183,8 +184,8 @@ func (c *zookeeperDynamicConfiguration) Done() chan struct{} { return c.done } -func (c *zookeeperDynamicConfiguration) GetUrl() common.URL { - return *c.url +func (c *zookeeperDynamicConfiguration) GetUrl() *common.URL { + return c.url } func (c *zookeeperDynamicConfiguration) Destroy() { @@ -206,10 +207,9 @@ func (c *zookeeperDynamicConfiguration) IsAvailable() bool { } func (c *zookeeperDynamicConfiguration) closeConfigs() { + logger.Infof("begin to close provider zk client") c.cltLock.Lock() defer c.cltLock.Unlock() - logger.Infof("begin to close provider zk client") - // Close the old client first to close the tmp node c.client.Close() c.client = nil } diff --git a/config_center/zookeeper/impl_test.go b/config_center/zookeeper/impl_test.go index ecc3527c486fdd2aa2bc6f4d2f2adab1147e495a..7f17153e24ff2c4fa89be9b4dfb5d619a8a83db9 100644 --- a/config_center/zookeeper/impl_test.go +++ b/config_center/zookeeper/impl_test.go @@ -50,7 +50,7 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC assert.NoError(t, err) regurl.AddParam(constant.REGISTRY_TIMEOUT_KEY, "15s") zkFactory := &zookeeperDynamicConfigurationFactory{} - reg, err := zkFactory.GetDynamicConfiguration(®url) + reg, err := zkFactory.GetDynamicConfiguration(regurl) zreg, ok := reg.(*zookeeperDynamicConfiguration) assert.True(t, ok) assert.NoError(t, err) @@ -81,10 +81,10 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` if group != "" { - err = zreg.client.Create(path.Join(zreg.rootPath, "dubbo", dubboPropertyFileName)) + err = zreg.client.Create(path.Join(zreg.rootPath, group, dubboPropertyFileName)) assert.NoError(t, err) - _, err = zreg.client.Conn.Set(path.Join(zreg.rootPath, "dubbo", dubboPropertyFileName), []byte(data), 0) + _, err = zreg.client.Conn.Set(path.Join(zreg.rootPath, group, dubboPropertyFileName), []byte(data), 0) assert.NoError(t, err) } else { err = zreg.client.Create(path.Join(zreg.rootPath, dubboPropertyFileName)) @@ -99,7 +99,11 @@ func initZkData(group string, t *testing.T) (*zk.TestCluster, *zookeeperDynamicC func TestGetConfig(t *testing.T) { ts, reg := initZkData("dubbo", t) - defer ts.Stop() + defer func() { + reg.client.Close() + err := ts.Stop() + assert.NoError(t, err) + }() configs, err := reg.GetProperties(dubboPropertyFileName, config_center.WithGroup("dubbo")) assert.NoError(t, err) m, err := reg.Parser().Parse(configs) @@ -107,17 +111,25 @@ func TestGetConfig(t *testing.T) { assert.Equal(t, "5s", m["dubbo.consumer.request_timeout"]) configs, err = reg.GetProperties(dubboPropertyFileName) assert.Error(t, err) + assert.Equal(t, "", configs) configs, err = reg.GetInternalProperty(dubboPropertyFileName) assert.Error(t, err) + assert.Equal(t, "", configs) configs, err = reg.GetRule(dubboPropertyFileName) assert.Error(t, err) + assert.Equal(t, "", configs) } func TestAddListener(t *testing.T) { ts, reg := initZkData("", t) - defer ts.Stop() + defer func() { + reg.client.Close() + err := ts.Stop() + assert.NoError(t, err) + }() listener := &mockDataListener{} reg.AddListener(dubboPropertyFileName, listener) + listener.wg.Add(1) data := ` dubbo.consumer.request_timeout=3s @@ -148,7 +160,11 @@ func TestAddListener(t *testing.T) { func TestRemoveListener(t *testing.T) { ts, reg := initZkData("", t) - defer ts.Stop() + defer func() { + reg.client.Close() + err := ts.Stop() + assert.NoError(t, err) + }() listener := &mockDataListener{} reg.AddListener(dubboPropertyFileName, listener) listener.wg.Add(1) @@ -185,16 +201,20 @@ func TestZookeeperDynamicConfigurationPublishConfig(t *testing.T) { value := "Test Data" customGroup := "Custom Group" key := "myKey" - ts, zk := initZkData(config_center.DEFAULT_GROUP, t) - defer ts.Stop() - err := zk.PublishConfig(key, customGroup, value) + ts, reg := initZkData(config_center.DEFAULT_GROUP, t) + defer func() { + reg.client.Close() + err := ts.Stop() + assert.NoError(t, err) + }() + err := reg.PublishConfig(key, customGroup, value) assert.Nil(t, err) - result, err := zk.GetInternalProperty("myKey", config_center.WithGroup(customGroup)) + result, err := reg.GetInternalProperty("myKey", config_center.WithGroup(customGroup)) assert.Nil(t, err) assert.Equal(t, value, result) var keys *gxset.HashSet - keys, err = zk.GetConfigKeysByGroup(customGroup) + keys, err = reg.GetConfigKeysByGroup(customGroup) assert.Nil(t, err) assert.Equal(t, 1, keys.Size()) assert.True(t, keys.Contains(key)) diff --git a/config_center/zookeeper/listener.go b/config_center/zookeeper/listener.go index bc6eb6d6eec4413515228a712cd11810e11f08cc..244451a2d0af2b2e743f1d0889cb926df89b3c3b 100644 --- a/config_center/zookeeper/listener.go +++ b/config_center/zookeeper/listener.go @@ -23,6 +23,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/remoting" ) @@ -77,5 +78,12 @@ func (l *CacheListener) DataChange(event remoting.Event) bool { } func (l *CacheListener) pathToKey(path string) string { - return strings.Replace(strings.Replace(path, l.rootPath+"/", "", -1), "/", ".", -1) + key := strings.Replace(strings.Replace(path, l.rootPath+"/", "", -1), "/", ".", -1) + if strings.HasSuffix(key, constant.CONFIGURATORS_SUFFIX) || + strings.HasSuffix(key, constant.TagRouterRuleSuffix) || + strings.HasSuffix(key, constant.ConditionRouterRuleSuffix) { + //governance config, so we remove the "dubbo." prefix + return key[strings.Index(key, ".")+1:] + } + return key } diff --git a/config_center/zookeeper/listener_test.go b/config_center/zookeeper/listener_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a8c07549381ed3be0bac5a65fbc1036078e7cee7 --- /dev/null +++ b/config_center/zookeeper/listener_test.go @@ -0,0 +1,101 @@ +/* + * 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 zookeeper + +import ( + "path" + "strconv" + "testing" +) + +import ( + "github.com/dubbogo/go-zookeeper/zk" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/config_center/parser" +) + +func initZkDynamicConfiguration(t *testing.T) (*zk.TestCluster, *zookeeperDynamicConfiguration) { + ts, err := zk.StartTestCluster(1, nil, nil) + assert.NoError(t, err) + assert.NotNil(t, ts.Servers[0]) + urlString := "registry://127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port) + regurl, err := common.NewURL(urlString) + assert.NoError(t, err) + regurl.AddParam(constant.REGISTRY_TIMEOUT_KEY, "15s") + zkFactory := &zookeeperDynamicConfigurationFactory{} + reg, err := zkFactory.GetDynamicConfiguration(regurl) + zreg, ok := reg.(*zookeeperDynamicConfiguration) + assert.True(t, ok) + assert.NoError(t, err) + assert.True(t, zreg.IsAvailable()) + assert.Equal(t, zreg.GetUrl(), regurl) + assert.True(t, zreg.RestartCallBack()) + zreg.SetParser(&parser.DefaultConfigurationParser{}) + + data := ` + dubbo.application.name=dubbogo +` + err = zreg.client.Create(path.Join(zreg.rootPath, dubboPropertyFileName)) + assert.NoError(t, err) + _, err = zreg.client.Conn.Set(path.Join(zreg.rootPath, dubboPropertyFileName), []byte(data), 0) + assert.NoError(t, err) + + return ts, zreg +} + +func TestZookeeperDynamicConfigurationPathToKey(t *testing.T) { + ts, reg := initZkDynamicConfiguration(t) + defer func() { + err := ts.Stop() + assert.NoError(t, err) + }() + listener := &mockDataListener{} + key := path.Join("dubbogoDemo" + constant.CONFIGURATORS_SUFFIX) + reg.AddListener(key, listener) + listener.wg.Add(1) + + data := ` +scope: application +key: dubbogoDemo +enabled: true +configs: + - addresses: [0.0.0.0:20880] + side: provider + parameters: + weight: 60 + - addresses: [0.0.0.0:20881] + side: provider + parameters: + weight: 40 +` + zkPath := path.Join(reg.rootPath, "dubbo", key) + exists, _, err := reg.client.Conn.Exists(zkPath) + assert.NoError(t, err) + if !exists { + err = reg.client.Create(zkPath) + assert.NoError(t, err) + } + _, err = reg.client.SetContent(zkPath, []byte(data), 0) + assert.NoError(t, err) + listener.wg.Wait() + assert.Equal(t, key, listener.event) +} diff --git a/contributing.md b/contributing.md index 51301511e01c12e388edf574e5c8660ad4a542a1..0da128190dba2a4feb0f0ef49065942f7dbd312d 100644 --- a/contributing.md +++ b/contributing.md @@ -40,7 +40,7 @@ The title format of the pull request `MUST` follow the following rules: >- 1 there should be comment for every export func/var. >- 2 the comment should begin with function name/var name. -### 3.4 import +### 3.4 import We dubbogo import blocks should be splited into 3 blocks. @@ -53,12 +53,12 @@ import ( // 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/apache/apache-release-procedure-20200306.md b/doc/apache/apache-release-procedure-20200306.md index 3f677ff56b8a97f881ae3d1b9bf842754ddb05ab..a36e2b05b0dca6632c1932bbab7e97e4089e452a 100644 --- a/doc/apache/apache-release-procedure-20200306.md +++ b/doc/apache/apache-release-procedure-20200306.md @@ -21,7 +21,7 @@ Apache寮€婧愯蒋浠舵槸鏈夌ぞ鍖洪┍鍔ㄧ殑锛屼负浜嗘彁楂樺彂甯冭蒋浠惰川閲忚€屾寚 ## 1. 鍙戠増鍑嗗 -鍙戠増鏂囦欢闇€瑕佺鍚嶏紝闇€瑕佸畨瑁卲gp宸ュ叿. +鍙戠増鏂囦欢闇€瑕佺鍚嶏紝闇€瑕佸畨瑁卲gp宸ュ叿. ```bash $ brew install gpg @@ -167,7 +167,7 @@ $ svn commit --username wongoo -m "Release dubbo-go-hessian2 v1.4.0-rc1" 鍙戜换浣曢偖浠堕兘鏄湁涓€瀹氭牸寮忕殑锛屼綘鍔犲叆绀惧尯閭欢鍒楄〃鍚庯紝灏变細鏀跺埌寰堝杩欐牱鐨勯偖浠讹紝澶氱湅鐪嬪氨鐭ラ亾浜嗭紝鍏蜂綋閭欢鑼冩湰鍙傝€冩枃绔犲悗闈㈢殑閭欢鑼冩湰銆� 鍙戝畬銆怴OTE銆戦偖浠讹紝绉佷笅娌熼€氱兢閲岄潰璇峰ぇ浣琍MC鎶曠エ銆� -PMC鎶曠エ浼氬浣犱笂浼犳墦鍖呮枃浠惰繘琛岀浉鍏虫鏌�, +PMC鎶曠エ浼氬浣犱笂浼犳墦鍖呮枃浠惰繘琛岀浉鍏虫鏌�, 璇︾粏鍙互浜嗚В瀛靛寲涓殑椤圭洰鍙戝竷瀹屾暣鐨勬鏌ラ」鍙傝€冿細 https://cwiki.apache.org/confluence/display/INCUBATOR2/IncubatorReleaseChecklist 鏀跺埌3涓猙inding閭欢涓旇秴杩�72灏忔椂鍚庯紝灏卞彲浠ュ彂 鎶曠エ缁撴灉 [RESULT] [VOTE] 閭欢浜嗐€� @@ -185,7 +185,7 @@ PMC鎶曠エ浼氬浣犱笂浼犳墦鍖呮枃浠惰繘琛岀浉鍏虫鏌�, ## 5. 鍙戝竷鐗堟湰 -褰撴寮忓彂甯冩姇绁ㄦ垚鍔熷悗锛屽厛鍙慬Result]閭欢锛岀劧鍚庡氨鍑嗗 release package銆� +褰撴寮忓彂甯冩姇绁ㄦ垚鍔熷悗锛屽厛鍙慬Result]閭欢锛岀劧鍚庡氨鍑嗗 release package銆� 灏嗕箣鍓嶅湪dev涓嬪彂甯冪殑瀵瑰簲rc鏂囦欢澶逛笅鐨勬簮鐮佸寘銆佺鍚嶆枃浠跺拰hash鏂囦欢鎷疯礉鍒板彟涓€涓洰褰� v1.4.0锛� 娉ㄦ剰鏂囦欢鍚嶅瓧涓笉瑕乺cxx (鍙互rename锛屼絾涓嶈閲嶆柊璁$畻绛惧悕锛宧ash鍙互閲嶆柊璁$畻锛岀粨鏋滀笉浼氬彉)銆� @@ -253,7 +253,7 @@ Hello Dubbo/Dubbogo Community, [ ] +1 approve [ ] +0 no opinion [ ] -1 disapprove with the reason - + Thanks, The Apache Dubbo-go Team ``` @@ -284,7 +284,7 @@ between text And link. I suggest add one to make it looks better. <--- 涓€浜� I checked the following items: [v] Are release files in correct location? <-- 鍙戝竷鏂囦欢鐩綍鏄惁姝g‘ -[v] Do release files have the word incubating in their name? +[v] Do release files have the word incubating in their name? [v] Are the digital signature and hashes correct? <-- 绛惧悕銆乭ash鏄惁姝g‘ [v] Do LICENSE and NOTICE files exists? [v] Is the LICENSE and NOTICE text correct? <-- 鍗忚鏂囨湰鏄惁姝g‘ diff --git a/doc/pic/misc/dubbogo-dingding.png b/doc/pic/misc/dubbogo-dingding.png new file mode 100644 index 0000000000000000000000000000000000000000..dc27d4ecce94a095e2eb70f97446c76c5b8537da Binary files /dev/null and b/doc/pic/misc/dubbogo-dingding.png differ diff --git a/doc/pic/misc/dubbogo-wechat.png b/doc/pic/misc/dubbogo-wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..dd34357199271dbb2bd977547d5bf1b90b3f6c70 Binary files /dev/null and b/doc/pic/misc/dubbogo-wechat.png differ diff --git a/filter/access_key.go b/filter/access_key.go index 4801d64fe46461424c5dac5aef2eebc719ee19c4..f38764bcea9fd1fc2dd5b0e7028aaf21a2c92c92 100644 --- a/filter/access_key.go +++ b/filter/access_key.go @@ -26,7 +26,7 @@ import ( type AccessKeyPair struct { AccessKey string `yaml:"accessKey" json:"accessKey,omitempty" property:"accessKey"` SecretKey string `yaml:"secretKey" json:"secretKey,omitempty" property:"secretKey"` - ConsumerSide string `yaml:"consumerSide" json:"ConsumerSide,consumerSide" property:"consumerSide"` + ConsumerSide string `yaml:"consumerSide" json:"consumerSide,omitempty" property:"consumerSide"` ProviderSide string `yaml:"providerSide" json:"providerSide,omitempty" property:"providerSide"` Creator string `yaml:"creator" json:"creator,omitempty" property:"creator"` Options string `yaml:"options" json:"options,omitempty" property:"options"` diff --git a/filter/filter.go b/filter/filter.go index 804bf3b9df8040c6377648f150c25d421b29b93d..f17c1a231e53e9a3df48072c851cbb26b42a04b4 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -20,6 +20,7 @@ package filter import ( "context" ) + import ( "github.com/apache/dubbo-go/protocol" ) diff --git a/filter/filter_impl/access_log_filter.go b/filter/filter_impl/access_log_filter.go index 6eaf9cb00bafe8fb0d4b9f8cda50e6bc7115461b..167b5edd804fd5b4b319e86d13815d1b4f698e1c 100644 --- a/filter/filter_impl/access_log_filter.go +++ b/filter/filter_impl/access_log_filter.go @@ -105,8 +105,12 @@ 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() - if v, ok := attachments[constant.INTERFACE_KEY]; ok && v != nil { - dataMap[constant.INTERFACE_KEY] = v.(string) + itf := attachments[constant.INTERFACE_KEY] + if itf == nil || len(itf.(string)) == 0 { + itf = attachments[constant.PATH_KEY] + } + if itf != nil { + dataMap[constant.INTERFACE_KEY] = itf.(string) } if v, ok := attachments[constant.METHOD_KEY]; ok && v != nil { dataMap[constant.METHOD_KEY] = v.(string) diff --git a/filter/filter_impl/access_log_filter_test.go b/filter/filter_impl/access_log_filter_test.go index a3a6151aa1b6a933c57543248f3703125fa356d9..9c4b947db4adbe0a9490f4b13964632028469d18 100644 --- a/filter/filter_impl/access_log_filter_test.go +++ b/filter/filter_impl/access_log_filter_test.go @@ -77,6 +77,6 @@ func TestAccessLogFilterInvokeDefaultConfig(t *testing.T) { func TestAccessLogFilterOnResponse(t *testing.T) { result := &protocol.RPCResult{} accessLogFilter := GetAccessLogFilter() - response := accessLogFilter.OnResponse(nil, result, nil, nil) + response := accessLogFilter.OnResponse(context.TODO(), result, nil, nil) assert.Equal(t, result, response) } diff --git a/filter/filter_impl/active_filter_test.go b/filter/filter_impl/active_filter_test.go index 9837a49c72e28c7a7209f8af6059bdc30c222cc2..2397503dc8f46381a131bed6f924d465a71a9193 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]interface{}, 0)) + invoc := invocation.NewRPCInvocation("test", []interface{}{"OK"}, make(map[string]interface{})) url, _ := common.NewURL("dubbo://192.168.10.10:20000/com.ikurento.user.UserProvider") filter := ActiveFilter{} ctrl := gomock.NewController(t) @@ -65,7 +65,7 @@ func TestActiveFilterOnResponse(t *testing.T) { result := &protocol.RPCResult{ Err: errors.New("test"), } - filter.OnResponse(nil, result, invoker, invoc) + filter.OnResponse(context.TODO(), result, invoker, invoc) methodStatus := protocol.GetMethodStatus(url, "test") urlStatus := protocol.GetURLStatus(url) diff --git a/filter/filter_impl/auth/consumer_sign.go b/filter/filter_impl/auth/consumer_sign.go index 945cf3e6e7e728042b5422174162dd5aded50361..823db82bfcb0bb165ef36bb9d141650df1e01432 100644 --- a/filter/filter_impl/auth/consumer_sign.go +++ b/filter/filter_impl/auth/consumer_sign.go @@ -42,8 +42,8 @@ func (csf *ConsumerSignFilter) Invoke(ctx context.Context, invoker protocol.Invo logger.Infof("invoking ConsumerSign filter.") url := invoker.GetUrl() - err := doAuthWork(&url, func(authenticator filter.Authenticator) error { - return authenticator.Sign(invocation, &url) + err := doAuthWork(url, func(authenticator filter.Authenticator) error { + return authenticator.Sign(invocation, url) }) if err != nil { panic(fmt.Sprintf("Sign for invocation %s # %s failed", url.ServiceKey(), invocation.MethodName())) diff --git a/filter/filter_impl/auth/default_authenticator.go b/filter/filter_impl/auth/default_authenticator.go index 5b86fc148e42b6364bcb0752c31bfbbc3cfa2b9d..7c7131c9cf1a4abba7b2ec1b4282b49b15f3c8b9 100644 --- a/filter/filter_impl/auth/default_authenticator.go +++ b/filter/filter_impl/auth/default_authenticator.go @@ -69,7 +69,7 @@ func getSignature(url *common.URL, invocation protocol.Invocation, secrectKey st requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, url.ColonSeparatedKey(), invocation.MethodName(), secrectKey, currentTime) var signature string - if parameterEncrypt := url.GetParamBool(constant.PARAMTER_SIGNATURE_ENABLE_KEY, false); parameterEncrypt { + if parameterEncrypt := url.GetParamBool(constant.PARAMETER_SIGNATURE_ENABLE_KEY, false); parameterEncrypt { var err error if signature, err = SignWithParams(invocation.Arguments(), requestString, secrectKey); err != nil { // TODO diff --git a/filter/filter_impl/auth/default_authenticator_test.go b/filter/filter_impl/auth/default_authenticator_test.go index 8b0fb6b0e3de04ccb2c30d2c7e22c32af5733742..d915b6a16ee5ccd15a390059d74e108bdf4c2bce 100644 --- a/filter/filter_impl/auth/default_authenticator_test.go +++ b/filter/filter_impl/auth/default_authenticator_test.go @@ -39,7 +39,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { secret := "dubbo-sk" access := "dubbo-ak" testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") - testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "true") + testurl.SetParam(constant.PARAMETER_SIGNATURE_ENABLE_KEY, "true") testurl.SetParam(constant.ACCESS_KEY_ID_KEY, access) testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, secret) parmas := []interface{}{"OK", struct { @@ -48,7 +48,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { }{"YUYU", 1}} inv := invocation.NewRPCInvocation("test", parmas, nil) requestTime := strconv.Itoa(int(time.Now().Unix() * 1000)) - signature, _ := getSignature(&testurl, inv, secret, requestTime) + signature, _ := getSignature(testurl, inv, secret, requestTime) var authenticator = &DefaultAuthenticator{} @@ -58,7 +58,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { constant.REQUEST_TIMESTAMP_KEY: requestTime, constant.AK_KEY: access, }) - err := authenticator.Authenticate(invcation, &testurl) + err := authenticator.Authenticate(invcation, testurl) assert.Nil(t, err) // modify the params invcation = invocation.NewRPCInvocation("test", parmas[:1], map[string]interface{}{ @@ -67,7 +67,7 @@ func TestDefaultAuthenticator_Authenticate(t *testing.T) { constant.REQUEST_TIMESTAMP_KEY: requestTime, constant.AK_KEY: access, }) - err = authenticator.Authenticate(invcation, &testurl) + err = authenticator.Authenticate(invcation, testurl) assert.NotNil(t, err) } @@ -77,9 +77,9 @@ func TestDefaultAuthenticator_Sign(t *testing.T) { testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?application=test&interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") testurl.SetParam(constant.ACCESS_KEY_ID_KEY, "akey") testurl.SetParam(constant.SECRET_ACCESS_KEY_KEY, "skey") - testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false") + testurl.SetParam(constant.PARAMETER_SIGNATURE_ENABLE_KEY, "false") inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil) - _ = authenticator.Sign(inv, &testurl) + _ = authenticator.Sign(inv, testurl) assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_SIGNATURE_KEY, ""), "") assert.NotEqual(t, inv.AttachmentsByKey(constant.CONSUMER, ""), "") assert.NotEqual(t, inv.AttachmentsByKey(constant.REQUEST_TIMESTAMP_KEY, ""), "") @@ -113,18 +113,19 @@ func Test_getAccessKeyPairFailed(t *testing.T) { common.WithParamsValue(constant.SECRET_ACCESS_KEY_KEY, "skey"), common.WithParamsValue(constant.ACCESS_KEY_ID_KEY, "akey"), common.WithParamsValue(constant.ACCESS_KEY_STORAGE_KEY, "dubbo")) _, e = getAccessKeyPair(invcation, testurl) + assert.NoError(t, e) } 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") + testurl.SetParam(constant.PARAMETER_SIGNATURE_ENABLE_KEY, "true") inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{ "": "", }) secret := "dubbo" current := strconv.Itoa(int(time.Now().Unix() * 1000)) - signature, _ := getSignature(&testurl, inv, secret, current) + signature, _ := getSignature(testurl, inv, secret, current) requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, testurl.ColonSeparatedKey(), inv.MethodName(), secret, current) s, _ := SignWithParams(inv.Arguments(), requestString, secret) @@ -134,11 +135,11 @@ func Test_getSignatureWithinParams(t *testing.T) { func Test_getSignature(t *testing.T) { testurl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?interface=com.ikurento.user.UserProvider&group=gg&version=2.6.0") - testurl.SetParam(constant.PARAMTER_SIGNATURE_ENABLE_KEY, "false") + testurl.SetParam(constant.PARAMETER_SIGNATURE_ENABLE_KEY, "false") inv := invocation.NewRPCInvocation("test", []interface{}{"OK"}, nil) secret := "dubbo" current := strconv.Itoa(int(time.Now().Unix() * 1000)) - signature, _ := getSignature(&testurl, inv, secret, current) + signature, _ := getSignature(testurl, inv, secret, current) requestString := fmt.Sprintf(constant.SIGNATURE_STRING_FORMAT, testurl.ColonSeparatedKey(), inv.MethodName(), secret, current) s := Sign(requestString, secret) diff --git a/filter/filter_impl/auth/provider_auth.go b/filter/filter_impl/auth/provider_auth.go index d5f5db300d4e7c94978d5d52e32f741f7d27bb48..774fdb2fb8f69bc33fcbee36faae28f8f6e6d0cc 100644 --- a/filter/filter_impl/auth/provider_auth.go +++ b/filter/filter_impl/auth/provider_auth.go @@ -42,8 +42,8 @@ func (paf *ProviderAuthFilter) Invoke(ctx context.Context, invoker protocol.Invo logger.Infof("invoking providerAuth filter.") url := invoker.GetUrl() - err := doAuthWork(&url, func(authenticator filter.Authenticator) error { - return authenticator.Authenticate(invocation, &url) + err := doAuthWork(url, func(authenticator filter.Authenticator) error { + return authenticator.Authenticate(invocation, url) }) if err != nil { logger.Infof("auth the request: %v occur exception, cause: %s", invocation, err.Error()) diff --git a/filter/filter_impl/auth/provider_auth_test.go b/filter/filter_impl/auth/provider_auth_test.go index f6ebfcd7abb83a55b4a09c1ec2d6c71b88bf7727..dc130b59851ddb02572103b2e29361f9fc8f1c59 100644 --- a/filter/filter_impl/auth/provider_auth_test.go +++ b/filter/filter_impl/auth/provider_auth_test.go @@ -52,7 +52,7 @@ func TestProviderAuthFilter_Invoke(t *testing.T) { } inv := invocation.NewRPCInvocation("test", parmas, nil) requestTime := strconv.Itoa(int(time.Now().Unix() * 1000)) - signature, _ := getSignature(&url, inv, secret, requestTime) + signature, _ := getSignature(url, inv, secret, requestTime) inv = invocation.NewRPCInvocation("test", []interface{}{"OK"}, map[string]interface{}{ constant.REQUEST_SIGNATURE_KEY: signature, diff --git a/filter/filter_impl/auth/sign_util.go b/filter/filter_impl/auth/sign_util.go index 45170bb8117284275a87a3a57d14ce68d6cc4e9c..4c12c762ed6715af551f6e37c251cf095bb43dc4 100644 --- a/filter/filter_impl/auth/sign_util.go +++ b/filter/filter_impl/auth/sign_util.go @@ -26,6 +26,10 @@ import ( "strings" ) +import ( + "github.com/apache/dubbo-go/common/logger" +) + // Sign gets a signature string with given bytes func Sign(metadata, key string) string { return doSign([]byte(metadata), key) @@ -33,7 +37,7 @@ func Sign(metadata, key string) string { // SignWithParams returns a signature with giving params and metadata. func SignWithParams(params []interface{}, metadata, key string) (string, error) { - if params == nil || len(params) == 0 { + if len(params) == 0 { return Sign(metadata, key), nil } @@ -56,7 +60,9 @@ func toBytes(data []interface{}) ([]byte, error) { func doSign(bytes []byte, key string) string { mac := hmac.New(sha256.New, []byte(key)) - mac.Write(bytes) + if _, err := mac.Write(bytes); err != nil { + logger.Error(err) + } signature := mac.Sum(nil) return base64.URLEncoding.EncodeToString(signature) } diff --git a/filter/filter_impl/echo_filter_test.go b/filter/filter_impl/echo_filter_test.go index b821a1a30cb8af7e957fac45beecd46067627f91..f2ec81a3e63a81c66ef08d482f111e8bae5774b8 100644 --- a/filter/filter_impl/echo_filter_test.go +++ b/filter/filter_impl/echo_filter_test.go @@ -34,10 +34,10 @@ import ( func TestEchoFilterInvoke(t *testing.T) { filter := GetFilter() - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(&common.URL{}), invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, nil)) assert.Equal(t, "OK", result.Result()) - result = filter.Invoke(context.Background(), protocol.NewBaseInvoker(common.URL{}), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)) + result = filter.Invoke(context.Background(), protocol.NewBaseInvoker(&common.URL{}), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, nil)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } diff --git a/filter/filter_impl/execute_limit_filter.go b/filter/filter_impl/execute_limit_filter.go index 5fc309cfb49fbeef5e933a84c1b80654087da701..35611611c8a716c3799d85e4c31fd3440b3611ef 100644 --- a/filter/filter_impl/execute_limit_filter.go +++ b/filter/filter_impl/execute_limit_filter.go @@ -84,7 +84,7 @@ func (ef *ExecuteLimitFilter) Invoke(ctx context.Context, invoker protocol.Invok methodConfigPrefix := "methods." + invocation.MethodName() + "." ivkURL := invoker.GetUrl() limitTarget := ivkURL.ServiceKey() - limitRateConfig := constant.DEFAULT_EXECUTE_LIMIT + var limitRateConfig string methodLevelConfig := ivkURL.GetParam(methodConfigPrefix+constant.EXECUTE_LIMIT_KEY, "") if len(methodLevelConfig) > 0 { diff --git a/filter/filter_impl/execute_limit_filter_test.go b/filter/filter_impl/execute_limit_filter_test.go index 953f5e1cc8f6ff3299dcac21c5cd2a41de08cdc1..682f8fa8b475348624a33d44d9a08190fe270fa8 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]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), @@ -44,14 +44,14 @@ func TestExecuteLimitFilterInvokeIgnored(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), @@ -61,14 +61,14 @@ func TestExecuteLimitFilterInvokeConfigureError(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } func TestExecuteLimitFilterInvoke(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), @@ -78,7 +78,7 @@ func TestExecuteLimitFilterInvoke(t *testing.T) { limitFilter := GetExecuteLimitFilter() - result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) + result := limitFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) } diff --git a/filter/filter_impl/generic_filter.go b/filter/filter_impl/generic_filter.go index 36b4b13186361d85beea0625eeda1ad58c3f2dca..cf307d0ad865da704ae048f21606704fd4204cb3 100644 --- a/filter/filter_impl/generic_filter.go +++ b/filter/filter_impl/generic_filter.go @@ -122,15 +122,15 @@ func struct2MapAll(obj interface{}) interface{} { } return newTemps } else if t.Kind() == reflect.Map { - var newTempMap = make(map[string]interface{}, v.Len()) + var newTempMap = make(map[interface{}]interface{}, v.Len()) iter := v.MapRange() for iter.Next() { - mapK := iter.Key().String() if !iter.Value().CanInterface() { continue } + key := iter.Key() mapV := iter.Value().Interface() - newTempMap[mapK] = struct2MapAll(mapV) + newTempMap[convertMapKey(key)] = struct2MapAll(mapV) } return newTempMap } else { @@ -138,6 +138,19 @@ func struct2MapAll(obj interface{}) interface{} { } } +func convertMapKey(key reflect.Value) interface{} { + switch key.Kind() { + case reflect.Bool, reflect.Int, reflect.Int8, + reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, + reflect.Uint32, reflect.Uint64, reflect.Float32, + reflect.Float64, reflect.String: + return key.Interface() + default: + return key.String() + } +} + func setInMap(m map[string]interface{}, structField reflect.StructField, value interface{}) (result map[string]interface{}) { result = m if tagName := structField.Tag.Get("m"); tagName == "" { diff --git a/filter/filter_impl/generic_filter_test.go b/filter/filter_impl/generic_filter_test.go index 40cf743106821f12425d1abe6dbc82bad0559917..4203dd6dd816c9a4cfe3c2142551dad7baa7dbf2 100644 --- a/filter/filter_impl/generic_filter_test.go +++ b/filter/filter_impl/generic_filter_test.go @@ -98,15 +98,17 @@ func TestStruct2MapAllSlice(t *testing.T) { func TestStruct2MapAllMap(t *testing.T) { var testData struct { - AaAa string - Baba map[string]interface{} - CaCa map[string]string - DdDd map[string]interface{} + AaAa string + Baba map[string]interface{} + CaCa map[string]string + DdDd map[string]interface{} + IntMap map[int]interface{} } testData.AaAa = "aaaa" testData.Baba = make(map[string]interface{}) testData.CaCa = make(map[string]string) testData.DdDd = nil + testData.IntMap = make(map[int]interface{}) testData.Baba["kk"] = 1 var structData struct { @@ -117,14 +119,19 @@ func TestStruct2MapAllMap(t *testing.T) { testData.Baba["nil"] = nil testData.CaCa["k1"] = "v1" testData.CaCa["kv2"] = "v2" + testData.IntMap[1] = 1 m := struct2MapAll(testData) assert.Equal(t, reflect.Map, reflect.TypeOf(m).Kind()) - assert.Equal(t, reflect.String, reflect.TypeOf(m.(map[string]interface{})["aaAa"]).Kind()) - assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["baba"]).Kind()) - assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["baba"].(map[string]interface{})["struct"]).Kind()) - assert.Equal(t, "str", m.(map[string]interface{})["baba"].(map[string]interface{})["struct"].(map[string]interface{})["str"]) - assert.Equal(t, nil, m.(map[string]interface{})["baba"].(map[string]interface{})["nil"]) - assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["caCa"]).Kind()) - assert.Equal(t, reflect.Map, reflect.TypeOf(m.(map[string]interface{})["ddDd"]).Kind()) + mappedStruct := m.(map[string]interface{}) + assert.Equal(t, reflect.String, reflect.TypeOf(mappedStruct["aaAa"]).Kind()) + assert.Equal(t, reflect.Map, reflect.TypeOf(mappedStruct["baba"]).Kind()) + assert.Equal(t, reflect.Map, reflect.TypeOf(mappedStruct["baba"].(map[interface{}]interface{})["struct"]).Kind()) + assert.Equal(t, "str", mappedStruct["baba"].(map[interface{}]interface{})["struct"].(map[string]interface{})["str"]) + assert.Equal(t, nil, mappedStruct["baba"].(map[interface{}]interface{})["nil"]) + assert.Equal(t, reflect.Map, reflect.TypeOf(mappedStruct["caCa"]).Kind()) + assert.Equal(t, reflect.Map, reflect.TypeOf(mappedStruct["ddDd"]).Kind()) + intMap := mappedStruct["intMap"] + assert.Equal(t, reflect.Map, reflect.TypeOf(intMap).Kind()) + assert.Equal(t, 1, intMap.(map[interface{}]interface{})[1]) } diff --git a/filter/filter_impl/generic_service_filter.go b/filter/filter_impl/generic_service_filter.go index 3711e68cce67dacfec074a5e44c080a629bce305..89b009b8d136d34ca0fd14548b3ef5536c7234d8 100644 --- a/filter/filter_impl/generic_service_filter.go +++ b/filter/filter_impl/generic_service_filter.go @@ -20,7 +20,6 @@ package filter_impl import ( "context" "reflect" - "strings" ) import ( @@ -75,7 +74,7 @@ func (ef *GenericServiceFilter) Invoke(ctx context.Context, invoker protocol.Inv url := invoker.GetUrl() methodName = invocation.Arguments()[0].(string) // get service - svc := common.ServiceMap.GetService(url.Protocol, strings.TrimPrefix(url.Path, "/")) + svc := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey()) // get method method := svc.Method()[methodName] if method == nil { diff --git a/filter/filter_impl/generic_service_filter_test.go b/filter/filter_impl/generic_service_filter_test.go index 67819717cf07383373f905ed9420a42cc0bfddb9..c755a2d28080e8c6a56e742181b944d299e493ed 100644 --- a/filter/filter_impl/generic_service_filter_test.go +++ b/filter/filter_impl/generic_service_filter_test.go @@ -96,7 +96,7 @@ func TestGenericServiceFilterInvoke(t *testing.T) { hessian.Object("222")}, } s := &TestService{} - _, _ = common.ServiceMap.Register("TestService", "testprotocol", s) + _, _ = common.ServiceMap.Register("com.test.Path", "testprotocol", "", "", s) rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) filter := GetGenericServiceFilter() url, _ := common.NewURL("testprotocol://127.0.0.1:20000/com.test.Path") @@ -125,7 +125,7 @@ func TestGenericServiceFilterResponseTestStruct(t *testing.T) { filter := GetGenericServiceFilter() methodName := "$invoke" rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) - r := filter.OnResponse(nil, result, nil, rpcInvocation) + r := filter.OnResponse(context.TODO(), result, nil, rpcInvocation) assert.NotNil(t, r.Result()) assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.Map) } @@ -143,7 +143,7 @@ func TestGenericServiceFilterResponseString(t *testing.T) { filter := GetGenericServiceFilter() methodName := "$invoke" rpcInvocation := invocation.NewRPCInvocation(methodName, aurguments, nil) - r := filter.OnResponse(nil, result, nil, rpcInvocation) + r := filter.OnResponse(context.TODO(), result, nil, rpcInvocation) assert.NotNil(t, r.Result()) assert.Equal(t, reflect.ValueOf(r.Result()).Kind(), reflect.String) } diff --git a/filter/filter_impl/graceful_shutdown_filter_test.go b/filter/filter_impl/graceful_shutdown_filter_test.go index 220ef6f20857aa06bcc45d3ac0a9bd52b0d65af2..b16956e01c85b52ec0009f96998a72e2a4314911 100644 --- a/filter/filter_impl/graceful_shutdown_filter_test.go +++ b/filter/filter_impl/graceful_shutdown_filter_test.go @@ -39,10 +39,8 @@ import ( ) func TestGenericFilterInvoke(t *testing.T) { - invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]interface{}, 0)) - - invokeUrl := common.NewURLWithOptions( - common.WithParams(url.Values{})) + invoc := invocation.NewRPCInvocation("GetUser", []interface{}{"OK"}, make(map[string]interface{})) + invokeUrl := common.NewURLWithOptions(common.WithParams(url.Values{})) shutdownFilter := extension.GetFilter(constant.PROVIDER_SHUTDOWN_FILTER).(*gracefulShutdownFilter) @@ -54,7 +52,7 @@ func TestGenericFilterInvoke(t *testing.T) { assert.Equal(t, extension.GetRejectedExecutionHandler(constant.DEFAULT_KEY), shutdownFilter.getRejectHandler()) - result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(*invokeUrl), invoc) + result := shutdownFilter.Invoke(context.Background(), protocol.NewBaseInvoker(invokeUrl), invoc) assert.NotNil(t, result) assert.Nil(t, result.Error()) @@ -65,7 +63,8 @@ func TestGenericFilterInvoke(t *testing.T) { shutdownFilter.shutdownConfig = providerConfig.ShutdownConfig assert.True(t, shutdownFilter.rejectNewRequest()) - result = shutdownFilter.OnResponse(nil, nil, protocol.NewBaseInvoker(*invokeUrl), invoc) + result = shutdownFilter.OnResponse(context.Background(), nil, protocol.NewBaseInvoker(invokeUrl), invoc) + assert.Nil(t, result) rejectHandler := &common2.OnlyLogRejectedExecutionHandler{} extension.SetRejectedExecutionHandler("mock", func() filter.RejectedExecutionHandler { diff --git a/filter/filter_impl/hystrix_filter_test.go b/filter/filter_impl/hystrix_filter_test.go index eebbae55565aa7072846d368403605002df0c61b..4973ce7f70353a70befec74f1bab9333b6b58fd1 100644 --- a/filter/filter_impl/hystrix_filter_test.go +++ b/filter/filter_impl/hystrix_filter_test.go @@ -18,6 +18,7 @@ package filter_impl import ( "context" + "fmt" "regexp" "testing" ) @@ -29,6 +30,8 @@ import ( ) import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol/invocation" ) @@ -147,7 +150,11 @@ func (iv *testMockFailInvoker) Invoke(_ context.Context, _ protocol.Invocation) func TestHystrixFilterInvokeSuccess(t *testing.T) { hf := &HystrixFilter{} - result := hf.Invoke(context.Background(), &testMockSuccessInvoker{}, &invocation.RPCInvocation{}) + testUrl, err := common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) + assert.NoError(t, err) + testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)} + result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{}) assert.NotNil(t, result) assert.NoError(t, result.Error()) assert.NotNil(t, result.Result()) @@ -155,7 +162,11 @@ func TestHystrixFilterInvokeSuccess(t *testing.T) { func TestHystrixFilterInvokeFail(t *testing.T) { hf := &HystrixFilter{} - result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) + testUrl, err := common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) + assert.NoError(t, err) + testInvoker := testMockFailInvoker{*protocol.NewBaseInvoker(testUrl)} + result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{}) assert.NotNil(t, result) assert.Error(t, result.Error()) } @@ -167,7 +178,11 @@ func TestHystricFilterInvokeCircuitBreak(t *testing.T) { resChan := make(chan protocol.Result, 50) for i := 0; i < 50; i++ { go func() { - result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) + testUrl, err := common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) + assert.NoError(t, err) + testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)} + result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{}) resChan <- result }() } @@ -192,7 +207,11 @@ func TestHystricFilterInvokeCircuitBreakOmitException(t *testing.T) { resChan := make(chan protocol.Result, 50) for i := 0; i < 50; i++ { go func() { - result := hf.Invoke(context.Background(), &testMockFailInvoker{}, &invocation.RPCInvocation{}) + testUrl, err := common.NewURL( + fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT)) + assert.NoError(t, err) + testInvoker := testMockSuccessInvoker{*protocol.NewBaseInvoker(testUrl)} + result := hf.Invoke(context.Background(), &testInvoker, &invocation.RPCInvocation{}) resChan <- result }() } diff --git a/filter/filter_impl/seata_filter.go b/filter/filter_impl/seata_filter.go index 7722d2954f905ece4a1b48628c31c06debf45614..b7b7b0e94b4633dda10278e9674566a82701b7f6 100644 --- a/filter/filter_impl/seata_filter.go +++ b/filter/filter_impl/seata_filter.go @@ -23,6 +23,7 @@ import ( ) import ( + "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/filter" @@ -30,12 +31,12 @@ import ( ) const ( - SEATA = "seata" - SEATA_XID = "SEATA_XID" + SEATA = constant.DubboCtxKey("seata") + SEATA_XID = constant.DubboCtxKey("SEATA_XID") ) func init() { - extension.SetFilter(SEATA, getSeataFilter) + extension.SetFilter(string(SEATA), getSeataFilter) } // SeataFilter when use seata-golang, use this filter to transfer xid @@ -45,7 +46,7 @@ type SeataFilter struct{} // Invoke Get Xid by attachment key `SEATA_XID` func (sf *SeataFilter) Invoke(ctx context.Context, invoker protocol.Invoker, invocation protocol.Invocation) protocol.Result { logger.Infof("invoking seata filter.") - xid := invocation.AttachmentsByKey(SEATA_XID, "") + xid := invocation.AttachmentsByKey(string(SEATA_XID), "") if strings.TrimSpace(xid) != "" { logger.Debugf("Method: %v,Xid: %v", invocation.MethodName(), xid) return invoker.Invoke(context.WithValue(ctx, SEATA_XID, xid), invocation) diff --git a/filter/filter_impl/seata_filter_test.go b/filter/filter_impl/seata_filter_test.go index 45817e95cbd2eaa7365adc8a299523af8310f797..1705eba787f5569064841d6a713ed2f2e428314c 100644 --- a/filter/filter_impl/seata_filter_test.go +++ b/filter/filter_impl/seata_filter_test.go @@ -50,7 +50,7 @@ func TestSeataFilter_Invoke(t *testing.T) { filter := getSeataFilter() result := filter.Invoke(context.Background(), &testMockSeataInvoker{}, invocation.NewRPCInvocation("$echo", []interface{}{"OK"}, map[string]interface{}{ - SEATA_XID: "10.30.21.227:8091:2000047792", + string(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 index 86d6460acd840fb85b821f50c81f944da4c1b926..1de27adbac1808dbbbf8017f0119077752191d6f 100644 --- a/filter/filter_impl/sentinel_filter.go +++ b/filter/filter_impl/sentinel_filter.go @@ -38,13 +38,15 @@ import ( "github.com/apache/dubbo-go/protocol" ) +// Integrate Sentinel Go MUST HAVE: +// 1. Must initialize Sentinel Go run environment, +// refer to https://github.com/alibaba/sentinel-golang/blob/master/api/init.go +// 2. Register rules for resources user want to guard + 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") } @@ -54,20 +56,36 @@ type DubboLoggerWrapper struct { logger.Logger } -func (d DubboLoggerWrapper) Fatal(v ...interface{}) { - d.Logger.Error(v...) +func (d DubboLoggerWrapper) Debug(msg string, keysAndValues ...interface{}) { + d.Logger.Debug(logging.AssembleMsg(logging.GlobalCallerDepth, "DEBUG", msg, nil, keysAndValues)) +} + +func (d DubboLoggerWrapper) DebugEnabled() bool { + return true +} + +func (d DubboLoggerWrapper) Info(msg string, keysAndValues ...interface{}) { + d.Logger.Info(logging.AssembleMsg(logging.GlobalCallerDepth, "INFO", msg, nil, keysAndValues)) } -func (d DubboLoggerWrapper) Fatalf(format string, v ...interface{}) { - d.Logger.Errorf(format, v...) +func (d DubboLoggerWrapper) InfoEnabled() bool { + return true +} + +func (d DubboLoggerWrapper) Warn(msg string, keysAndValues ...interface{}) { + d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "WARN", msg, nil, keysAndValues)) } -func (d DubboLoggerWrapper) Panic(v ...interface{}) { - d.Logger.Error(v...) +func (d DubboLoggerWrapper) WarnEnabled() bool { + return true } -func (d DubboLoggerWrapper) Panicf(format string, v ...interface{}) { - d.Logger.Errorf(format, v...) +func (d DubboLoggerWrapper) Error(err error, msg string, keysAndValues ...interface{}) { + d.Logger.Warn(logging.AssembleMsg(logging.GlobalCallerDepth, "ERROR", msg, err, keysAndValues)) +} + +func (d DubboLoggerWrapper) ErrorEnabled() bool { + return true } func GetSentinelConsumerFilter() filter.Filter { @@ -187,8 +205,8 @@ const ( DefaultProviderPrefix = "dubbo:provider:" DefaultConsumerPrefix = "dubbo:consumer:" - MethodEntryKey = "$$sentinelMethodEntry" - InterfaceEntryKey = "$$sentinelInterfaceEntry" + MethodEntryKey = constant.DubboCtxKey("$$sentinelMethodEntry") + InterfaceEntryKey = constant.DubboCtxKey("$$sentinelInterfaceEntry") ) func getResourceName(invoker protocol.Invoker, invocation protocol.Invocation, prefix string) (interfaceResourceName, methodResourceName string) { @@ -229,7 +247,7 @@ func getInterfaceGroupAndVersionEnabled() bool { return true } -func getColonSeparatedKey(url common.URL) string { +func getColonSeparatedKey(url *common.URL) string { return fmt.Sprintf("%s:%s:%s", url.Service(), url.GetParam(constant.GROUP_KEY, ""), diff --git a/filter/filter_impl/sentinel_filter_test.go b/filter/filter_impl/sentinel_filter_test.go index e8d481b40640bb09e9e60e922d4592603dea8f3a..c6b6d4280f08099ce3eef8bf1e5a3c37e9e67c59 100644 --- a/filter/filter_impl/sentinel_filter_test.go +++ b/filter/filter_impl/sentinel_filter_test.go @@ -50,11 +50,11 @@ func TestSentinelFilter_QPS(t *testing.T) { _, err = flow.LoadRules([]*flow.Rule{ { - Resource: interfaceResourceName, - MetricType: flow.QPS, + Resource: interfaceResourceName, + //MetricType: flow.QPS, TokenCalculateStrategy: flow.Direct, ControlBehavior: flow.Reject, - Count: 100, + Threshold: 100, RelationStrategy: flow.CurrentResource, }, }) diff --git a/filter/filter_impl/token_filter_test.go b/filter/filter_impl/token_filter_test.go index cd1bba3d4a830822c67f1e6157653d5477264c94..024ae2ae3142b1e193d6a24001f51f00badf537a 100644 --- a/filter/filter_impl/token_filter_test.go +++ b/filter/filter_impl/token_filter_test.go @@ -40,10 +40,10 @@ func TestTokenFilterInvoke(t *testing.T) { url := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]interface{}, 0) + attch := make(map[string]interface{}) attch[constant.TOKEN_KEY] = "ori_key" result := filter.Invoke(context.Background(), - protocol.NewBaseInvoker(*url), + protocol.NewBaseInvoker(url), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) @@ -54,9 +54,9 @@ func TestTokenFilterInvokeEmptyToken(t *testing.T) { filter := GetTokenFilter() testUrl := common.URL{} - attch := make(map[string]interface{}, 0) + attch := make(map[string]interface{}) attch[constant.TOKEN_KEY] = "ori_key" - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(&testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } @@ -67,8 +67,8 @@ func TestTokenFilterInvokeEmptyAttach(t *testing.T) { testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]interface{}, 0) - result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + attch := make(map[string]interface{}) + result := filter.Invoke(context.Background(), protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } @@ -78,9 +78,9 @@ func TestTokenFilterInvokeNotEqual(t *testing.T) { testUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TOKEN_KEY, "ori_key")) - attch := make(map[string]interface{}, 0) + attch := make(map[string]interface{}) attch[constant.TOKEN_KEY] = "err_key" result := filter.Invoke(context.Background(), - protocol.NewBaseInvoker(*testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + protocol.NewBaseInvoker(testUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.NotNil(t, result.Error()) } diff --git a/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go index cbbba19fff65be222cb895dcbe9b2e4d02082985..bc78f6471bda2c87d9e82eccdf73700547454340 100644 --- a/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go +++ b/filter/filter_impl/tps/tps_limit_sliding_window_strategy.go @@ -54,7 +54,7 @@ type SlidingWindowTpsLimitStrategyImpl struct { queue *list.List } -// IsAllowable determins whether the number of requests within the time window overs the threshold +// IsAllowable determines whether the number of requests within the time window overs the threshold // It is thread-safe. func (impl *SlidingWindowTpsLimitStrategyImpl) IsAllowable() bool { impl.mutex.Lock() diff --git a/filter/filter_impl/tps/tps_limiter_method_service.go b/filter/filter_impl/tps/tps_limiter_method_service.go index 5761579a38a22500d54193a9564170cc0215cf0f..f0c276438beaa161a6f6b13bc95c1fd7f357e8b6 100644 --- a/filter/filter_impl/tps/tps_limiter_method_service.go +++ b/filter/filter_impl/tps/tps_limiter_method_service.go @@ -120,7 +120,7 @@ type MethodServiceTpsLimiterImpl struct { // The key point is how to keep thread-safe // This implementation use concurrent map + loadOrStore to make implementation thread-safe // You can image that even multiple threads create limiter, but only one could store the limiter into tpsState -func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocation protocol.Invocation) bool { +func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url *common.URL, invocation protocol.Invocation) bool { methodConfigPrefix := "methods." + invocation.MethodName() + "." @@ -176,7 +176,7 @@ func (limiter MethodServiceTpsLimiterImpl) IsAllowable(url common.URL, invocatio // If we can convert the methodLevelConfig to int64, return; // Or, we will try to look up server-level configuration and then convert it to int64 func getLimitConfig(methodLevelConfig string, - url common.URL, + url *common.URL, invocation protocol.Invocation, configKey string, defaultVal string) int64 { 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 61f28e442f4b76d18d7750aa58831c322f939207..5baa70a5bc4eebe162243a485521543802a51f2c 100644 --- a/filter/filter_impl/tps/tps_limiter_method_service_test.go +++ b/filter/filter_impl/tps/tps_limiter_method_service_test.go @@ -22,7 +22,6 @@ import ( "testing" ) import ( - "github.com/apache/dubbo-go/filter" "github.com/golang/mock/gomock" "github.com/stretchr/testify/assert" ) @@ -31,12 +30,13 @@ import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" + "github.com/apache/dubbo-go/filter" "github.com/apache/dubbo-go/protocol/invocation" ) func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) { methodName := "hello" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -57,13 +57,13 @@ func TestMethodServiceTpsLimiterImplIsAllowableOnlyServiceLevel(t *testing.T) { }) limiter := GetMethodServiceTpsLimiter() - result := limiter.IsAllowable(*invokeUrl, invoc) + result := limiter.IsAllowable(invokeUrl, invoc) assert.True(t, result) } func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) { methodName := "hello1" - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) // ctrl := gomock.NewController(t) // defer ctrl.Finish() @@ -73,14 +73,14 @@ func TestMethodServiceTpsLimiterImplIsAllowableNoConfig(t *testing.T) { common.WithParamsValue(constant.TPS_LIMIT_RATE_KEY, "")) limiter := GetMethodServiceTpsLimiter() - result := limiter.IsAllowable(*invokeUrl, invoc) + result := limiter.IsAllowable(invokeUrl, invoc) assert.True(t, result) } func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) { methodName := "hello2" methodConfigPrefix := "methods." + methodName + "." - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -106,14 +106,14 @@ func TestMethodServiceTpsLimiterImplIsAllowableMethodLevelOverride(t *testing.T) }) limiter := GetMethodServiceTpsLimiter() - result := limiter.IsAllowable(*invokeUrl, invoc) + result := limiter.IsAllowable(invokeUrl, invoc) assert.True(t, result) } func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T) { methodName := "hello3" methodConfigPrefix := "methods." + methodName + "." - invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{}, 0)) + invoc := invocation.NewRPCInvocation(methodName, []interface{}{"OK"}, make(map[string]interface{})) ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -136,7 +136,7 @@ func TestMethodServiceTpsLimiterImplIsAllowableBothMethodAndService(t *testing.T }) limiter := GetMethodServiceTpsLimiter() - result := limiter.IsAllowable(*invokeUrl, invoc) + result := limiter.IsAllowable(invokeUrl, invoc) assert.True(t, result) } diff --git a/filter/filter_impl/tps/tps_limiter_mock.go b/filter/filter_impl/tps/tps_limiter_mock.go index b49084f28e7d4b62d64762170b6dfbbec4a4de96..34c279032713595066638095b5f4d2f6fa2c4aa4 100644 --- a/filter/filter_impl/tps/tps_limiter_mock.go +++ b/filter/filter_impl/tps/tps_limiter_mock.go @@ -58,7 +58,7 @@ func (m *MockTpsLimiter) EXPECT() *MockTpsLimiterMockRecorder { } // IsAllowable mocks base method -func (m *MockTpsLimiter) IsAllowable(arg0 common.URL, arg1 protocol.Invocation) bool { +func (m *MockTpsLimiter) IsAllowable(arg0 *common.URL, arg1 protocol.Invocation) bool { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "IsAllowable", arg0, arg1) ret0, _ := ret[0].(bool) diff --git a/filter/filter_impl/tps_limit_filter_test.go b/filter/filter_impl/tps_limit_filter_test.go index da0fc482ce559dad52947786823f604128857c30..55a3a55fab28efc7c27d9f5ccc4a3ec454bb6d15 100644 --- a/filter/filter_impl/tps_limit_filter_test.go +++ b/filter/filter_impl/tps_limit_filter_test.go @@ -44,10 +44,10 @@ func TestTpsLimitFilterInvokeWithNoTpsLimiter(t *testing.T) { invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.TPS_LIMITER_KEY, "")) - attch := make(map[string]interface{}, 0) + attch := make(map[string]interface{}) result := tpsFilter.Invoke(context.Background(), - protocol.NewBaseInvoker(*invokeUrl), + protocol.NewBaseInvoker(invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) @@ -68,10 +68,10 @@ 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]interface{}, 0) + attch := make(map[string]interface{}) result := tpsFilter.Invoke(context.Background(), - protocol.NewBaseInvoker(*invokeUrl), + protocol.NewBaseInvoker(invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) @@ -99,10 +99,12 @@ 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]interface{}, 0) + attch := make(map[string]interface{}) result := tpsFilter.Invoke(context.Background(), - protocol.NewBaseInvoker(*invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) + protocol.NewBaseInvoker( + + invokeUrl), invocation.NewRPCInvocation("MethodName", []interface{}{"OK"}, attch)) assert.Nil(t, result.Error()) assert.Nil(t, result.Result()) } diff --git a/filter/filter_impl/tracing_filter.go b/filter/filter_impl/tracing_filter.go index b8058aa601af98b5416da882321546675459c413..dcdbe5b30e3c288462e9078df234243435ca86c6 100644 --- a/filter/filter_impl/tracing_filter.go +++ b/filter/filter_impl/tracing_filter.go @@ -83,7 +83,7 @@ func (tf *tracingFilter) Invoke(ctx context.Context, invoker protocol.Invoker, i }() result := invoker.Invoke(spanCtx, invocation) - span.SetTag(successKey, result.Error() != nil) + span.SetTag(successKey, result.Error() == nil) if result.Error() != nil { span.LogFields(log.String(errorKey, result.Error().Error())) } diff --git a/filter/filter_impl/tracing_filter_test.go b/filter/filter_impl/tracing_filter_test.go index e159b7400d46069018a00a849319423285072dc2..bf96a7d064b3919825d26c505b637f5ea539d9d6 100644 --- a/filter/filter_impl/tracing_filter_test.go +++ b/filter/filter_impl/tracing_filter_test.go @@ -57,6 +57,6 @@ func TestTracingFilterInvoke(t *testing.T) { tf.Invoke(ctx, invoker, inv) // has remote ctx - ctx = context.WithValue(context.Background(), constant.TRACING_REMOTE_SPAN_CTX, span.Context()) + ctx = context.WithValue(context.Background(), constant.DubboCtxKey(constant.TRACING_REMOTE_SPAN_CTX), span.Context()) tf.Invoke(ctx, invoker, inv) } diff --git a/filter/handler/rejected_execution_handler_mock.go b/filter/handler/rejected_execution_handler_mock.go index bff54769cb007ed2fce5a7623c96edc370e63903..5f2f458f83d0ca2cdff68eec4d0f3d22cf4e3277 100644 --- a/filter/handler/rejected_execution_handler_mock.go +++ b/filter/handler/rejected_execution_handler_mock.go @@ -58,7 +58,7 @@ func (m *MockRejectedExecutionHandler) EXPECT() *MockRejectedExecutionHandlerMoc } // RejectedExecution mocks base method -func (m *MockRejectedExecutionHandler) RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result { +func (m *MockRejectedExecutionHandler) RejectedExecution(url *common.URL, invocation protocol.Invocation) protocol.Result { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "RejectedExecution", url, invocation) ret0, _ := ret[0].(protocol.Result) diff --git a/filter/handler/rejected_execution_handler_only_log.go b/filter/handler/rejected_execution_handler_only_log.go index 52ac1765f78172c0062de8884198e759b8d494ca..5242b5b49e8a12322821481d237d84710c15eafd 100644 --- a/filter/handler/rejected_execution_handler_only_log.go +++ b/filter/handler/rejected_execution_handler_only_log.go @@ -63,7 +63,7 @@ type OnlyLogRejectedExecutionHandler struct { } // RejectedExecution will do nothing, it only log the invocation. -func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url common.URL, +func (handler *OnlyLogRejectedExecutionHandler) RejectedExecution(url *common.URL, _ protocol.Invocation) protocol.Result { logger.Errorf("The invocation was rejected. url: %s", url.String()) diff --git a/filter/handler/rejected_execution_handler_only_log_test.go b/filter/handler/rejected_execution_handler_only_log_test.go index 409f09f61bd958992749231fca045b54601fc627..7aa4aff936d6da1e46ed5a24609a0346e9d2edb4 100644 --- a/filter/handler/rejected_execution_handler_only_log_test.go +++ b/filter/handler/rejected_execution_handler_only_log_test.go @@ -31,5 +31,5 @@ func TestOnlyLogRejectedExecutionHandler_RejectedExecution(t *testing.T) { invokeUrl := common.NewURLWithOptions( common.WithParams(url.Values{}), common.WithParamsValue(constant.INTERFACE_KEY, "methodName")) - handler.RejectedExecution(*invokeUrl, nil) + handler.RejectedExecution(invokeUrl, nil) } diff --git a/filter/rejected_execution_handler.go b/filter/rejected_execution_handler.go index 3d1e1c1e641a836411ce0f71f97acf5d5a55f6d1..73ac3d3bbf37cbce5822e1955fb49f84bdf6d678 100644 --- a/filter/rejected_execution_handler.go +++ b/filter/rejected_execution_handler.go @@ -33,5 +33,5 @@ import ( type RejectedExecutionHandler interface { // RejectedExecution will be called if the invocation was rejected by some component. - RejectedExecution(url common.URL, invocation protocol.Invocation) protocol.Result + RejectedExecution(url *common.URL, invocation protocol.Invocation) protocol.Result } diff --git a/filter/tps_limiter.go b/filter/tps_limiter.go index 8385d7b5d84a420b54df6bf51e32a35d17e1b249..6f2466c0a40620b9f7dee358db681c4032c773fc 100644 --- a/filter/tps_limiter.go +++ b/filter/tps_limiter.go @@ -35,5 +35,5 @@ import ( */ type TpsLimiter interface { // IsAllowable will check whether this invocation should be enabled for further process - IsAllowable(common.URL, protocol.Invocation) bool + IsAllowable(*common.URL, protocol.Invocation) bool } diff --git a/go.mod b/go.mod index 317431f49b4ccd68cf4c7044a33de01860d7ffeb..4f79f3d6b8ca37ee5b8efd51827ef1ea78674707 100644 --- a/go.mod +++ b/go.mod @@ -1,56 +1,54 @@ module github.com/apache/dubbo-go +go 1.15 + require ( - github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/Workiva/go-datastructures v1.0.50 + github.com/RoaringBitmap/roaring v0.5.5 + github.com/Workiva/go-datastructures v1.0.52 github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 - github.com/alibaba/sentinel-golang v0.6.2 - github.com/apache/dubbo-getty v1.3.10 - github.com/apache/dubbo-go-hessian2 v1.7.0 + github.com/alibaba/sentinel-golang v1.0.2 + github.com/apache/dubbo-getty v1.4.3 + github.com/apache/dubbo-go-hessian2 v1.9.1 github.com/coreos/etcd v3.3.25+incompatible - github.com/creasty/defaults v1.3.0 - github.com/dubbogo/go-zookeeper v1.0.2 - 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/fsnotify/fsnotify v1.4.7 + github.com/creasty/defaults v1.5.1 + github.com/dubbogo/go-zookeeper v1.0.3 + github.com/dubbogo/gost v1.11.2 + github.com/emicklei/go-restful/v3 v3.4.0 + github.com/fsnotify/fsnotify v1.4.9 github.com/go-co-op/gocron v0.1.1 - github.com/go-resty/resty/v2 v2.1.0 - github.com/golang/mock v1.3.1 - github.com/golang/protobuf v1.4.0 + github.com/go-resty/resty/v2 v2.3.0 + github.com/golang/mock v1.4.4 + github.com/golang/protobuf v1.4.3 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/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/mapstructure v1.2.3 + github.com/magiconair/properties v1.8.4 + github.com/mitchellh/mapstructure v1.4.1 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd - github.com/nacos-group/nacos-sdk-go v1.0.0 - github.com/opentracing/opentracing-go v1.1.0 - github.com/pierrec/lz4 v2.2.6+incompatible // indirect + github.com/nacos-group/nacos-sdk-go v1.0.6 + github.com/opentracing/opentracing-go v1.2.0 github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.1.0 + github.com/prometheus/client_golang v1.9.0 github.com/satori/go.uuid v1.2.1-0.20181028125025-b2ce2384e17b - github.com/stretchr/objx v0.2.0 // indirect - github.com/stretchr/testify v1.5.1 - github.com/zouyx/agollo/v3 v3.4.4 - go.uber.org/atomic v1.6.0 - go.uber.org/zap v1.15.0 - google.golang.org/grpc v1.26.0 - gopkg.in/yaml.v2 v2.2.8 + github.com/stretchr/testify v1.7.0 + github.com/zouyx/agollo/v3 v3.4.5 + go.uber.org/atomic v1.7.0 + go.uber.org/zap v1.16.0 + golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect + google.golang.org/grpc v1.33.1 + google.golang.org/grpc/examples v0.0.0-20210301210255-fc8f38cccf75 // indirect + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.16.9 k8s.io/apimachinery v0.16.9 k8s.io/client-go v0.16.9 - k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a // indirect ) replace ( + github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4 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 + github.com/shirou/gopsutil => github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880 + go.etcd.io/bbolt v1.3.4 => github.com/coreos/bbolt v1.3.3 + google.golang.org/grpc => google.golang.org/grpc v1.26.0 ) - -go 1.13 diff --git a/go.sum b/go.sum index 20d53452be3f03c411d54219999d69807462b015..b533e4a8b47a881c5a0f4042a0f49a65b331d443 100644 --- a/go.sum +++ b/go.sum @@ -62,11 +62,14 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 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/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/Microsoft/go-winio v0.4.3 h1:M3NHMuPgMSUPdE5epwNUHlRPSVzHs8HpRTrVXhR0myo= github.com/Microsoft/go-winio v0.4.3/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= 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 h1:iLrQrdwjDd52kHDA5op2UBJFjmOb9g+7scBan4RN8F0= 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= @@ -74,14 +77,16 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE 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/RoaringBitmap/roaring v0.5.5 h1:naNqvO1mNnghk2UvcsqnzHDBn9DRbCIRy94GmDTRVTQ= +github.com/RoaringBitmap/roaring v0.5.5/go.mod h1:puNo5VdzwbaIQxSiDIwfXl4Hnc+fbovcX4IW/dSTtUk= 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/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI= +github.com/Workiva/go-datastructures v1.0.52/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= @@ -90,19 +95,27 @@ github.com/akamai/AkamaiOPEN-edgegrid-golang v0.9.0/go.mod h1:zpDJeKyp9ScW4NNrbd 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/template v0.0.0-20190718012654-fb15b899a751/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.2 h1:1OjjpljJbNKWp9p5RJKxOqS1gHGZPUWPlCcokv5xYJs= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alibaba/sentinel-golang v0.6.2/go.mod h1:5jemKdyCQCKVf+quEia53fo9a17OSe+wnl9HX2NbNpc= +github.com/alibaba/sentinel-golang v1.0.2 h1:Acopq74hOtZN4MV1v811MQ6QcqPFLDSczTrRXv9zpIg= +github.com/alibaba/sentinel-golang v1.0.2/go.mod h1:QsB99f/z35D2AiMrAWwgWE85kDTkBUIkcmPrRt+61NI= 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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/dubbo-getty v1.3.10/go.mod h1:x6rraK01BL5C7jUM2fPl5KMkAxLVIx54ZB8/XEOik9Y= -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/dubbo-getty v1.4.3 h1:PCKpryDasKOxwT5MBC6MIMO+0NLOaHF6Xco9YXQw7HI= +github.com/apache/dubbo-getty v1.4.3/go.mod h1:ansXgKxxyhCOiQL29nO5ce1MDcEKmCyZuNR9oMs3hek= +github.com/apache/dubbo-go v1.5.5/go.mod h1:KAty5L/vcHjh3qcTfC4R5Nfd0tkk7MLJnux/IQKfQLg= +github.com/apache/dubbo-go-hessian2 v1.9.1 h1:ceSsU/9z/gv3hzUpl8GceEhQvF3i0BionfdHUGMmjHU= +github.com/apache/dubbo-go-hessian2 v1.9.1/go.mod h1:xQUjE7F8PX49nm80kChFvepA/AvqAZ0oh/UaB6+6pBE= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.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= @@ -114,9 +127,13 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj 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/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 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/aws/aws-sdk-go v1.27.0 h1:0xphMHGMLBrPMfxR2AmVjZKcMEESEgWF8Kru94BNByk= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= 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= @@ -134,17 +151,26 @@ github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1 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/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= 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 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 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/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= 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/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= 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= @@ -156,11 +182,9 @@ github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDG 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= github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY= github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 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= @@ -171,18 +195,23 @@ github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 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.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 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/creasty/defaults v1.5.1 h1:j8WexcS3d/t4ZmllX4GEkl4wIB/trOr035ajcLHCISM= +github.com/creasty/defaults v1.5.1/go.mod h1:FPZ+Y0WNrbqOVw+c6av63eyHUAl6pMHZwqLPvXUZGfY= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -203,6 +232,7 @@ github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyG 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 h1:3lOnM9cSzgGwx8VfK/NGOW5fLQ0GjIlCkaktF+n1M6o= 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= @@ -210,27 +240,35 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/dubbogo/go-zookeeper v1.0.2 h1:xmEnPL8SlCe3/+J5ZR9e8qE35LmFVYe8VVpDakjNM4A= github.com/dubbogo/go-zookeeper v1.0.2/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= +github.com/dubbogo/go-zookeeper v1.0.3 h1:UkuY+rBsxdT7Bs63QAzp9z7XqQ53W1j8E5rwl83me8g= +github.com/dubbogo/go-zookeeper v1.0.3/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= 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/dubbogo/gost v1.10.1/go.mod h1:+mQGS51XQEUWZP2JeGZTxJwipjRKtJO7Tr+FOg+72rI= +github.com/dubbogo/gost v1.11.0 h1:9KtyWQz1gMlAfwzen5iyhMdoe08SPBBUVhco4rdgJ9I= +github.com/dubbogo/gost v1.11.0/go.mod h1:w8Yw29eDWtRVo3tx9nPpHkNZnOi4SRx1fZf7eVlAAU4= +github.com/dubbogo/gost v1.11.2 h1:NanyHmvzE1HrgI2T9H/jE/N1wkxFEj+IbM1A4RT9H7Q= +github.com/dubbogo/gost v1.11.2/go.mod h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7FzHsMc= +github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= 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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= 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 h1:ZoRgc53qJCfSLimXqJDrmBhnt5GChDsExMCK7t48o0Y= 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= github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633 h1:H2pdYOb3KQ1/YsqVWoWNLQO+fusocsw354rqGTZtAgw= 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/emicklei/go-restful/v3 v3.4.0 h1:IIDhql3oyWZj1ay2xBZGb4sTOWMad0HVW8rwhVxN/Yk= +github.com/emicklei/go-restful/v3 v3.4.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= @@ -246,15 +284,16 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL 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/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= 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/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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= @@ -264,7 +303,6 @@ github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31 h1:gclg6gY70GLy 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= @@ -276,14 +314,17 @@ github.com/go-git/go-git-fixtures/v4 v4.0.1/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1 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-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/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-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= 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= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 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= @@ -294,8 +335,9 @@ github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dp 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-resty/resty/v2 v2.3.0 h1:JOOeAvjSlapTT92p8xiS19Zxev1neGikoHsXJeOq8So= +github.com/go-resty/resty/v2 v2.3.0/go.mod h1:UpN9CgLZNsv4e9XG50UU8xdI0F43UQ4HmxLBDwaroHU= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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= @@ -313,32 +355,39 @@ github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a 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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 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.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 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/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 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= @@ -347,10 +396,11 @@ github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-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-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= +github.com/google/go-cmp v0.5.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= @@ -363,8 +413,12 @@ 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.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= @@ -375,12 +429,14 @@ github.com/gophercloud/gophercloud v0.1.0 h1:P/nh25+rzXouhytV2pUHBb65fnds26Ghl8/ 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/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw= +github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/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 v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= 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= @@ -388,24 +444,31 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U 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.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg= +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/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 h1:FlFbCRLd5Jr4iYXZufAvgWN6Ao0JrI5chLINnUXDDr0= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= 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-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= 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.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= 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.3.0/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= @@ -441,10 +504,12 @@ github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHh github.com/hashicorp/go-multierror v1.1.0 h1:B9UzwGQJehnUY1yNrnwREHc3fGbC2xefo8g4TbElacI= github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-raftchunking v0.6.1 h1:moEnaG3gcwsWNyIBJoD5PCByE+Ewkqxh6N05CT+MbwA= github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a h1:FmnBDwGwlTgugDGbVxwV8UavqSMACbGrUpfc98yFLR4= github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod h1:xbXnmKqX9/+RhPkJ4zrEx4738HacP72aaUPlT2RZ4sU= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.5.4 h1:1BZvpawXoJCWX6pNtow9+rpEj+3itIlutiqnntI6jOE= github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.2 h1:bHM2aVXwBtBJWxHtkSrWuI4umABCUczs52eiUS9nSiw= github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= @@ -467,6 +532,7 @@ github.com/hashicorp/go-version v1.2.0 h1:3vNe/fWF5CBgRIguda1meWhsZHy3m8gCJ5wx+d github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -494,6 +560,7 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0= github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/vault/api v1.0.4 h1:j08Or/wryXT4AcHj1oCbMd7IijXcKzYUGw59LGu9onU= github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff h1:cl94LQIrs/mNbh3ny1R8lM1gtYcUBa7HnGtOCi35SlQ= github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff/go.mod h1:Uf8LaHyrYsgVgHzO2tMZKhqRGlL3UJ6XaSwW2EA1Iqo= @@ -508,6 +575,7 @@ 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/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= 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= @@ -515,6 +583,7 @@ github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJ 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/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= 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= @@ -529,29 +598,40 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5i github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f h1:ENpDacvnr8faw5ugQmEF1QYk+f/Y9lXFvuYmRxykago= github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.7 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.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68= +github.com/json-iterator/go v1.1.10/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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88 h1:uC1QfSlInpQF+M0ao65imhwqKnz3Q2z/d8PWZRMQvDM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v3.0.1+incompatible h1:3tqvf7QgUnZ5tXO6pNAZlrvHgl6DvifjDrd9g2S9Z40= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= 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/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 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= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= @@ -574,15 +654,19 @@ github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042 h1:Bvq8AziQ5j 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/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= 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/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= 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/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY= +github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= 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= @@ -592,7 +676,10 @@ github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc 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-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw= +github.com/mattn/go-colorable v0.1.7/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.4/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= @@ -628,14 +715,17 @@ github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp 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 h1:hOY53G+kBFhbYFpRVxHl5eS7laP6B1+Cq+Z9Dry1iMU= github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y= github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU= github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.0.0 h1:ATSdz4NWrmWPOF1CeCBU4sMCno2hgqdbSrRPFWQSVZI= github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= @@ -649,15 +739,24 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN 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/mschoch/smat v0.0.0-20160514031455-90eadee771ae h1:VeRdUYdCw49yizlSbMEn2SZ+gT+3IUKx8BqxyQdz+BY= +github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= 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/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/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/nacos-group/nacos-sdk-go v1.0.1/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA= +github.com/nacos-group/nacos-sdk-go v1.0.6 h1:0OqjS37qIKKKZKRQSJ5pNFGRrfzP7+gD4L6dvOkPFZw= +github.com/nacos-group/nacos-sdk-go v1.0.6/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.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= github.com/nats-io/nats-server/v2 v2.1.6/go.mod h1:BL1NOtaBQ5/y97djERRVWNouMW7GT3gxnmbE/eC8u8A= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= github.com/nats-io/nats.go v1.9.2/go.mod h1:AjGArbfyR50+afOUotNX2Xs5SYHf+CoOa5HH1eEl2HE= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= 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= @@ -671,8 +770,10 @@ github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/Hzq 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/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 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-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= 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= @@ -686,6 +787,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa 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/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= 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= @@ -693,22 +795,36 @@ github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5X 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-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= 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/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/philhofer/fwd v1.0.0 h1:UbZqGr5Y38ApvM/V/jEljVxwocdweyH+vmYvRPBnbqQ= +github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pierrec/lz4 v2.2.6+incompatible h1:6aCX4/YZ9v8q69hTyiR7dNLnTA3fgtKHVVW5BCd5Znw= github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -716,6 +832,7 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -729,28 +846,42 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4 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_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.8.0 h1:zvJNkoCFAnYFNC24FV8nW4JdRJ3GIFcLbg65lL/JDcw= +github.com/prometheus/client_golang v1.8.0/go.mod h1:O9VU6huf47PktckDQfMTX0Y8tY0/7TSWwj+ITvv0TnM= +github.com/prometheus/client_golang v1.9.0 h1:Rrch9mh17XcxvEu9D9DEpb4isxjGBtcevQjKvxPRQIU= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= 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/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/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/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.14.0 h1:RHRyE8UocrbjU+6UvRzwi6HjiDfxrrBU91TtbKzkGp4= +github.com/prometheus/common v0.14.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/common v0.15.0 h1:4fgOnadei3EZvgRwxJ7RMpG1k1pOZth5Pc13tyspaKM= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= 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/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 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= @@ -758,6 +889,7 @@ github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqn 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/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= 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= @@ -767,6 +899,7 @@ github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFo 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/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= 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= @@ -775,9 +908,8 @@ github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6 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 h1:1Ge4j/3uB2rxzPWD3TC+daeCw+w91z8UCUL/7WH5gn8= github.com/shirou/gopsutil v0.0.0-20181107111621-48177ef5f880/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= @@ -785,8 +917,9 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV 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/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 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= @@ -797,6 +930,7 @@ github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d h1:bVQRCxQv github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= @@ -804,18 +938,24 @@ github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= +github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -823,8 +963,10 @@ github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRci github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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= @@ -837,10 +979,14 @@ github.com/tent/http-link-go v0.0.0-20130702225549-ac974c61c2f9/go.mod h1:RHkNRt 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/tinylib/msgp v1.1.0 h1:9fQd+ICuRIu/ue4vxJZu6/LzxN0HwMds2nq/0cFvxHU= +github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 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/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/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= @@ -852,6 +998,7 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM 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.20.0/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= @@ -859,6 +1006,17 @@ github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU 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/wenxuwan/gost v1.9.4-0.20210302062512-1556cdf1d101/go.mod h1:I1V26ofEK1+4l48D2vDkuTxW2jInvt83MqKQ3P+Sl5Y= +github.com/wenxuwan/gost v1.9.4-0.20210302063022-6b9b17ad3c8a h1:+9K9TcsUxQzTktj5I66oNkq/VbwRBeKbOgPGmmofG8o= +github.com/wenxuwan/gost v1.9.4-0.20210302063022-6b9b17ad3c8a/go.mod h1:I1V26ofEK1+4l48D2vDkuTxW2jInvt83MqKQ3P+Sl5Y= +github.com/wenxuwan/gost v1.9.4-0.20210302073456-846ce528eb63 h1:uWjgNklk3bmU9uQQIcYxIevVlRlDhIfWOS2djKtUV0A= +github.com/wenxuwan/gost v1.9.4-0.20210302073456-846ce528eb63/go.mod h1:I1V26ofEK1+4l48D2vDkuTxW2jInvt83MqKQ3P+Sl5Y= +github.com/wenxuwan/gost v1.9.4-0.20210302082346-4f0dc3bef61f h1:V5XL115SW539g25+QeASGE7vbBg4BJOf9hxkZSZhOIY= +github.com/wenxuwan/gost v1.9.4-0.20210302082346-4f0dc3bef61f/go.mod h1:I1V26ofEK1+4l48D2vDkuTxW2jInvt83MqKQ3P+Sl5Y= +github.com/wenxuwan/gost v1.9.4-0.20210303060806-fde1b7159726 h1:RJ+oVEiY3g6LobTCfOxV6hDofuXAJjQvJXhjIykENOQ= +github.com/wenxuwan/gost v1.9.4-0.20210303060806-fde1b7159726/go.mod h1:3QQEj50QOhkWTERT785YZ5ZxIRGNdR11FCLP7FzHsMc= +github.com/willf/bitset v1.1.10 h1:NotGKqX0KwQ72NUzqrjZq5ipPNDQex9lo3WpaS8L2sc= +github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= 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= @@ -868,21 +1026,27 @@ github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/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= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/zouyx/agollo/v3 v3.4.5 h1:7YCxzY9ZYaH9TuVUBvmI6Tk0mwMggikah+cfbYogcHQ= +github.com/zouyx/agollo/v3 v3.4.5/go.mod h1:LJr3kDmm23QSW+F1Ol4TMHDa7HvJvscMdVxJ2IpUTVc= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/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.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= 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.opencensus.io v0.22.2 h1:75k/FF0Q2YM8QYo07VPddOLBslDt1MZOdEslOHvmzAs= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 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/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 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= @@ -892,8 +1056,9 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEa 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= +go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM= +go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= 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= @@ -912,19 +1077,20 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 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/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= 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= @@ -933,17 +1099,19 @@ golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTk golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 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.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.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= @@ -965,28 +1133,38 @@ golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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-20190813141303-74dc4d7220e7/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-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 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= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -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/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/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= @@ -1018,33 +1196,49 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w 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-20190826190057-c7b8b68b1456/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-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88= +golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo= +golang.org/x/sys v0.0.0-20201223074533-0d417f636930/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= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/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= @@ -1068,18 +1262,25 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn 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/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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200426102838-f3a5411a4c3b/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200928182047-19e03678916f h1:VwGa2Wf+rHGIxvsssCkUNIyFv8jQY0VCBCNWtikoWq0= +golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/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/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= @@ -1090,6 +1291,7 @@ google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsb 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.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1108,27 +1310,48 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 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/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884 h1:fiNLklpBwWK1mth30Hlwk+fcdBmIALlgF5iy77O37Ig= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98 h1:LCO0fg4kb6WwkXQXRQQgUYsFeFb5taTX5WAx5O/Vt28= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 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.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1 h1:DGeFlSan2f+WEtCERJ4J9GJWk15TxUi8QGagfI87Xyc= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0 h1:o1bcQ6imQMIOpdrO3SWf2z5RV72WbDwdXuK0MDlc8As= +google.golang.org/grpc/examples v0.0.0-20210301210255-fc8f38cccf75 h1:IFbcGka/Xo8VIhRtCz9UBaDblKK7/iwLiXwm9D62Jzk= +google.golang.org/grpc/examples v0.0.0-20210301210255-fc8f38cccf75/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= 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= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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= @@ -1138,9 +1361,11 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR 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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= 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/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= 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= @@ -1165,9 +1390,15 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN 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.3/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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= @@ -1188,13 +1419,18 @@ k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUc k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= +k8s.io/kube-openapi v0.0.0-20190816220812-743ec37842bf h1:EYm5AW/UUDbnmnI+gK0TJDVK9qPLhM+sRHYanNKw0EQ= 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= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/integrate_test.sh b/integrate_test.sh index deccda756a211821978e35b92a1f0865858ff59a..e1bbc8c11b0195f7cc4f08946d57464c88e74f19 100644 --- a/integrate_test.sh +++ b/integrate_test.sh @@ -26,12 +26,12 @@ ROOT_DIR=$(pwd) echo "integrate-test root work-space -> ${ROOT_DIR}" # show all travis-env -echo "travis current commit id -> ${TRAVIS_COMMIT}" -echo "travis pull request -> ${TRAVIS_PULL_REQUEST}" -echo "travis pull request branch -> ${TRAVIS_PULL_REQUEST_BRANCH}" -echo "travis pull request slug -> ${TRAVIS_PULL_REQUEST_SLUG}" -echo "travis pull request sha -> ${TRAVIS_PULL_REQUEST_SHA}" -echo "travis pull request repo slug -> ${TRAVIS_REPO_SLUG}" +echo "travis current commit id -> $2" +echo "travis pull request branch -> ${GITHUB_REF}" +echo "travis pull request slug -> ${GITHUB_REPOSITORY}" +echo "travis pull request repo slug -> ${GITHUB_REPOSITORY}" +echo "travis pull request actor -> ${GITHUB_ACTOR}" +echo "travis pull request repo param -> $1" # #start etcd registry insecure listen in [:]:2379 @@ -53,13 +53,13 @@ echo "zookeeper listen in [:]2181" # build go-server image cd ./test/integrate/dubbo/go-server -docker build . -t ci-provider --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +docker build . -t ci-provider --build-arg PR_ORIGIN_REPO=$1 --build-arg PR_ORIGIN_COMMITID=$2 cd ${ROOT_DIR} docker run -d --network host ci-provider # build go-client image cd ./test/integrate/dubbo/go-client -docker build . -t ci-consumer --build-arg PR_ORIGIN_REPO=${TRAVIS_PULL_REQUEST_SLUG} --build-arg PR_ORIGIN_COMMITID=${TRAVIS_PULL_REQUEST_SHA} +docker build . -t ci-consumer --build-arg PR_ORIGIN_REPO=$1 --build-arg PR_ORIGIN_COMMITID=$2 cd ${ROOT_DIR} # run provider # check consumer status diff --git a/metadata/definition/definition.go b/metadata/definition/definition.go index dbbc0c8f1685edf1a26ab1fe4ad091c501e76f5f..a0323137b70ff6ea34249b10e82d4f5cd84c9cb0 100644 --- a/metadata/definition/definition.go +++ b/metadata/definition/definition.go @@ -93,7 +93,7 @@ type TypeDefinition struct { } // BuildServiceDefinition can build service definition which will be used to describe a service -func BuildServiceDefinition(service common.Service, url common.URL) *ServiceDefinition { +func BuildServiceDefinition(service common.Service, url *common.URL) *ServiceDefinition { sd := &ServiceDefinition{} sd.CanonicalName = url.Service() diff --git a/metadata/definition/definition_test.go b/metadata/definition/definition_test.go index 958f9324d0f9f4a5027792bd8e54b238a5f56feb..389159239b4f4d7156983c83096ead151561ad28 100644 --- a/metadata/definition/definition_test.go +++ b/metadata/definition/definition_test.go @@ -28,7 +28,6 @@ import ( import ( "github.com/apache/dubbo-go/common" - "github.com/apache/dubbo-go/common/constant" ) func TestBuildServiceDefinition(t *testing.T) { @@ -44,9 +43,9 @@ func TestBuildServiceDefinition(t *testing.T) { "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", protocol, serviceName, group, version, beanName)) assert.NoError(t, err) - _, err = common.ServiceMap.Register(serviceName, protocol, &UserProvider{}) + _, err = common.ServiceMap.Register(serviceName, protocol, group, version, &UserProvider{}) assert.NoError(t, err) - service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service())) + service := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey()) sd := BuildServiceDefinition(*service, url) assert.Equal(t, "{canonicalName:com.ikurento.user.UserProvider, codeSource:, methods:[{name:GetUser,parameterTypes:[{type:slice}],returnType:ptr,params:[] }], types:[]}", sd.String()) } diff --git a/metadata/identifier/service_metadata_identifier.go b/metadata/identifier/service_metadata_identifier.go index b9e65967e0f707a6efcc9f8ded2ce5dec4f058b8..3035cf3d720bd453baecb147c605f1bfc928d825 100644 --- a/metadata/identifier/service_metadata_identifier.go +++ b/metadata/identifier/service_metadata_identifier.go @@ -32,7 +32,7 @@ type ServiceMetadataIdentifier struct { // NewServiceMetadataIdentifier create instance. // The ServiceInterface is the @url.Service() // other parameters are read from @url -func NewServiceMetadataIdentifier(url common.URL) *ServiceMetadataIdentifier { +func NewServiceMetadataIdentifier(url *common.URL) *ServiceMetadataIdentifier { return &ServiceMetadataIdentifier{ BaseMetadataIdentifier: BaseMetadataIdentifier{ ServiceInterface: url.Service(), diff --git a/metadata/mapping/dynamic/service_name_mapping_test.go b/metadata/mapping/dynamic/service_name_mapping_test.go index 2896b0fd4aa4fb6bada132c276c70a1653e59f99..af21704b69c674a2cd1b8963bb252ac73c0abf1e 100644 --- a/metadata/mapping/dynamic/service_name_mapping_test.go +++ b/metadata/mapping/dynamic/service_name_mapping_test.go @@ -39,6 +39,7 @@ func TestDynamicConfigurationServiceNameMapping(t *testing.T) { dc, err := (&config_center.MockDynamicConfigurationFactory{ Content: appName, }).GetDynamicConfiguration(nil) + assert.NoError(t, err) config.GetApplicationConfig().Name = appName mapping := &DynamicConfigurationServiceNameMapping{dc: dc} diff --git a/metadata/report/consul/report.go b/metadata/report/consul/report.go index eb2bdc25ecec596a8f89abb80856b8d6e7be70a4..e211f7fde4d462bd20603af54444036681a46f4d 100644 --- a/metadata/report/consul/report.go +++ b/metadata/report/consul/report.go @@ -61,7 +61,7 @@ func (m *consulMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier } // SaveServiceMetadata saves the metadata. -func (m *consulMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (m *consulMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error { kv := &consul.KVPair{Key: metadataIdentifier.GetIdentifierKey(), Value: []byte(url.String())} _, err := m.client.KV().Put(kv, nil) return err diff --git a/metadata/report/consul/report_test.go b/metadata/report/consul/report_test.go index e07a7429f12bac0cfa4b2701d3e87ef25bcb077a..51e93179a21851f4a81039180ecdff7519ae36fe 100644 --- a/metadata/report/consul/report_test.go +++ b/metadata/report/consul/report_test.go @@ -100,7 +100,7 @@ func (suite *consulMetadataReportTestSuite) testStoreConsumerMetadata() { assert.NoError(suite.t, err) } -func (suite *consulMetadataReportTestSuite) testSaveServiceMetadata(url common.URL) { +func (suite *consulMetadataReportTestSuite) testSaveServiceMetadata(url *common.URL) { serviceMi := newServiceMetadataIdentifier("provider") err := suite.m.SaveServiceMetadata(serviceMi, url) assert.NoError(suite.t, err) @@ -119,7 +119,7 @@ func (suite *consulMetadataReportTestSuite) testGetExportedURLs() { assert.NoError(suite.t, err) } -func (suite *consulMetadataReportTestSuite) testSaveSubscribedData(url common.URL) { +func (suite *consulMetadataReportTestSuite) testSaveSubscribedData(url *common.URL) { subscribeMi := newSubscribeMetadataIdentifier("provider") urls := []string{url.String()} bytes, _ := json.Marshal(urls) @@ -143,7 +143,9 @@ func (suite *consulMetadataReportTestSuite) testGetServiceDefinition() { func test1(t *testing.T) { consulAgent := consul.NewConsulAgent(t, 8500) - defer consulAgent.Shutdown() + defer func() { + _ = consulAgent.Shutdown() + }() url := newProviderRegistryUrl("localhost", 8500) mf := extension.GetMetadataReportFactory("consul") @@ -152,10 +154,10 @@ func test1(t *testing.T) { suite := newConsulMetadataReportTestSuite(t, m) suite.testStoreProviderMetadata() suite.testStoreConsumerMetadata() - suite.testSaveServiceMetadata(*url) + suite.testSaveServiceMetadata(url) suite.testGetExportedURLs() suite.testRemoveServiceMetadata() - suite.testSaveSubscribedData(*url) + suite.testSaveSubscribedData(url) suite.testGetSubscribedURLs() suite.testGetServiceDefinition() } diff --git a/metadata/report/delegate/delegate_report.go b/metadata/report/delegate/delegate_report.go index cdd29ab2e483647ed90f37032e10d174f7583e39..56a22de8f8e56c040260edf86859a4499f1b2f39 100644 --- a/metadata/report/delegate/delegate_report.go +++ b/metadata/report/delegate/delegate_report.go @@ -94,7 +94,7 @@ func (mrr *metadataReportRetry) startRetryTask() { // MetadataReport is a absolute delegate for MetadataReport type MetadataReport struct { - reportUrl common.URL + reportUrl *common.URL syncReport bool metadataReportRetry *metadataReportRetry @@ -109,6 +109,10 @@ type MetadataReport struct { // NewMetadataReport will create a MetadataReport with initiation func NewMetadataReport() (*MetadataReport, error) { url := instance.GetMetadataReportUrl() + if url == nil { + logger.Warn("the metadataReport URL is not configured, you should configure it.") + return nil, perrors.New("the metadataReport URL is not configured, you should configure it.") + } bmr := &MetadataReport{ reportUrl: url, syncReport: url.GetParamBool(constant.SYNC_REPORT_KEY, false), @@ -215,12 +219,16 @@ func (mr *MetadataReport) StoreConsumerMetadata(identifier *identifier.MetadataI } // SaveServiceMetadata will delegate to call remote metadata's sdk to save service metadata -func (mr *MetadataReport) SaveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (mr *MetadataReport) SaveServiceMetadata(identifier *identifier.ServiceMetadataIdentifier, url *common.URL) error { report := instance.GetMetadataReportInstance() if mr.syncReport { return report.SaveServiceMetadata(identifier, url) } - go report.SaveServiceMetadata(identifier, url) + go func() { + if err := report.SaveServiceMetadata(identifier, url); err != nil { + logger.Warnf("report.SaveServiceMetadata(identifier:%v, url:%v) = error:%v", identifier, url, err) + } + }() return nil } @@ -230,7 +238,11 @@ func (mr *MetadataReport) RemoveServiceMetadata(identifier *identifier.ServiceMe if mr.syncReport { return report.RemoveServiceMetadata(identifier) } - go report.RemoveServiceMetadata(identifier) + go func() { + if err := report.RemoveServiceMetadata(identifier); err != nil { + logger.Warnf("report.RemoveServiceMetadata(identifier:%v) = error:%v", identifier, err) + } + }() return nil } @@ -241,7 +253,7 @@ func (mr *MetadataReport) GetExportedURLs(identifier *identifier.ServiceMetadata } // SaveSubscribedData will delegate to call remote metadata's sdk to save subscribed data -func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMetadataIdentifier, urls []common.URL) error { +func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMetadataIdentifier, urls []*common.URL) error { urlStrList := make([]string, 0, len(urls)) for _, url := range urls { urlStrList = append(urlStrList, url.String()) @@ -255,18 +267,23 @@ func (mr *MetadataReport) SaveSubscribedData(identifier *identifier.SubscriberMe if mr.syncReport { return report.SaveSubscribedData(identifier, string(bytes)) } - go report.SaveSubscribedData(identifier, string(bytes)) + go func() { + if err := report.SaveSubscribedData(identifier, string(bytes)); err != nil { + logger.Warnf("report.SaveSubscribedData(identifier:%v, string(bytes):%v) = error: %v", + identifier, string(bytes), err) + } + }() return nil } // GetSubscribedURLs will delegate to call remote metadata's sdk to get subscribed urls -func (MetadataReport) GetSubscribedURLs(identifier *identifier.SubscriberMetadataIdentifier) ([]string, error) { +func (mr *MetadataReport) GetSubscribedURLs(identifier *identifier.SubscriberMetadataIdentifier) ([]string, error) { report := instance.GetMetadataReportInstance() return report.GetSubscribedURLs(identifier) } // GetServiceDefinition will delegate to call remote metadata's sdk to get service definitions -func (MetadataReport) GetServiceDefinition(identifier *identifier.MetadataIdentifier) (string, error) { +func (mr *MetadataReport) GetServiceDefinition(identifier *identifier.MetadataIdentifier) (string, error) { report := instance.GetMetadataReportInstance() return report.GetServiceDefinition(identifier) } diff --git a/metadata/report/delegate/delegate_report_test.go b/metadata/report/delegate/delegate_report_test.go index 3dfca577ba06598b90c553048777951c8823b256..f60acf6e117c866cba716c538806f278d77363f0 100644 --- a/metadata/report/delegate/delegate_report_test.go +++ b/metadata/report/delegate/delegate_report_test.go @@ -46,13 +46,10 @@ func TestMetadataReport_MetadataReportRetry(t *testing.T) { }) assert.NoError(t, err) retry.startRetryTask() - itsTime := time.After(2500 * time.Millisecond) - select { - case <-itsTime: - retry.scheduler.Clear() - assert.Equal(t, counter.Load(), int64(3)) - logger.Info("over") - } + <-time.After(2500 * time.Millisecond) + retry.scheduler.Clear() + assert.Equal(t, counter.Load(), int64(3)) + logger.Info("over") } func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) { @@ -64,13 +61,10 @@ func TestMetadataReport_MetadataReportRetryWithLimit(t *testing.T) { }) assert.NoError(t, err) retry.startRetryTask() - itsTime := time.After(2500 * time.Millisecond) - select { - case <-itsTime: - retry.scheduler.Clear() - assert.Equal(t, counter.Load(), int64(2)) - logger.Info("over") - } + <-time.After(2500 * time.Millisecond) + retry.scheduler.Clear() + assert.Equal(t, counter.Load(), int64(2)) + logger.Info("over") } func mockNewMetadataReport(t *testing.T) *MetadataReport { @@ -116,8 +110,8 @@ func getMockDefinition(id *identifier.MetadataIdentifier, t *testing.T) *definit "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", protocol, id.ServiceInterface, id.Group, id.Version, beanName)) assert.NoError(t, err) - _, err = common.ServiceMap.Register(id.ServiceInterface, protocol, &definition.UserProvider{}) + _, err = common.ServiceMap.Register(id.ServiceInterface, protocol, id.Group, id.Version, &definition.UserProvider{}) assert.NoError(t, err) - service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service())) + service := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey()) return definition.BuildServiceDefinition(*service, url) } diff --git a/metadata/report/etcd/report.go b/metadata/report/etcd/report.go index 097835c986962850d137255ec807b417de1484ad..1939b911822b6b5db3d41f999d4dcb4ca3fae1f2 100644 --- a/metadata/report/etcd/report.go +++ b/metadata/report/etcd/report.go @@ -63,7 +63,7 @@ func (e *etcdMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier *i // SaveServiceMetadata will store the metadata // metadata including the basic info of the server, service info, and other user custom info -func (e *etcdMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (e *etcdMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error { key := e.getNodeKey(metadataIdentifier) return e.client.Create(key, url.String()) } diff --git a/metadata/report/etcd/report_test.go b/metadata/report/etcd/report_test.go index 5dd8780b48a741a8eb8735b059bc3617a100f63d..59d0975ca675ef9550d3b2669a24ebd70ce94ec0 100644 --- a/metadata/report/etcd/report_test.go +++ b/metadata/report/etcd/report_test.go @@ -60,7 +60,7 @@ func TestEtcdMetadataReportFactory_CreateMetadataReport(t *testing.T) { t.Fatal(err) } metadataReportFactory := &etcdMetadataReportFactory{} - metadataReport := metadataReportFactory.CreateMetadataReport(&url) + metadataReport := metadataReportFactory.CreateMetadataReport(url) assert.NotNil(t, metadataReport) e.Close() } @@ -72,7 +72,7 @@ func TestEtcdMetadataReport_CRUD(t *testing.T) { t.Fatal(err) } metadataReportFactory := &etcdMetadataReportFactory{} - metadataReport := metadataReportFactory.CreateMetadataReport(&url) + metadataReport := metadataReportFactory.CreateMetadataReport(url) assert.NotNil(t, metadataReport) err = metadataReport.StoreConsumerMetadata(newMetadataIdentifier("consumer"), "consumer metadata") @@ -82,8 +82,9 @@ func TestEtcdMetadataReport_CRUD(t *testing.T) { assert.Nil(t, err) serviceMi := newServiceMetadataIdentifier() - serviceUrl, _ := common.NewURL("registry://localhost:8848", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - metadataReport.SaveServiceMetadata(serviceMi, serviceUrl) + serviceUrl, err := common.NewURL("registry://localhost:8848", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + assert.Nil(t, err) + err = metadataReport.SaveServiceMetadata(serviceMi, serviceUrl) assert.Nil(t, err) subMi := newSubscribeMetadataIdentifier() diff --git a/metadata/report/nacos/report.go b/metadata/report/nacos/report.go index d69913bd8fbb04da2d50770c1196917cb1efdaa5..42e9859c9fae70cc110dc45fca43dd95a5627d6e 100644 --- a/metadata/report/nacos/report.go +++ b/metadata/report/nacos/report.go @@ -69,7 +69,7 @@ func (n *nacosMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifier * } // SaveServiceMetadata saves the metadata. -func (n *nacosMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (n *nacosMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error { return n.storeMetadata(vo.ConfigParam{ DataId: metadataIdentifier.GetIdentifierKey(), Group: metadataIdentifier.Group, diff --git a/metadata/report/nacos/report_test.go b/metadata/report/nacos/report_test.go index be01eb22f7e95966c3bf816fdf648629b64380a3..1b7075ec78e4ac496983564b684d8776e1476b63 100644 --- a/metadata/report/nacos/report_test.go +++ b/metadata/report/nacos/report_test.go @@ -19,8 +19,10 @@ package nacos import ( "encoding/json" + "net/http" "strconv" "testing" + "time" ) import ( @@ -36,6 +38,9 @@ import ( ) func TestNacosMetadataReport_CRUD(t *testing.T) { + if !checkNacosServerAlive() { + return + } rpt := newTestReport() assert.NotNil(t, rpt) @@ -111,6 +116,14 @@ func TestNacosMetadataReportFactory_CreateMetadataReport(t *testing.T) { func newTestReport() report.MetadataReport { regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) - res := extension.GetMetadataReportFactory("nacos").CreateMetadataReport(®url) + res := extension.GetMetadataReportFactory("nacos").CreateMetadataReport(regurl) return res } + +func checkNacosServerAlive() bool { + c := http.Client{Timeout: time.Second} + if _, err := c.Get("http://console.nacos.io/nacos/"); err != nil { + return false + } + return true +} diff --git a/metadata/report/report.go b/metadata/report/report.go index 62a9055e843297bd0d69ad94cb09ece64efda85f..dcb414209c1e6188dc195e9609f60124adaf2173 100644 --- a/metadata/report/report.go +++ b/metadata/report/report.go @@ -38,7 +38,7 @@ type MetadataReport interface { // SaveServiceMetadata saves the metadata. // Metadata includes the basic info of the server, // service info, and other user custom info. - SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error + SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error // RemoveServiceMetadata removes the metadata. RemoveServiceMetadata(*identifier.ServiceMetadataIdentifier) error diff --git a/metadata/report/zookeeper/report.go b/metadata/report/zookeeper/report.go index 8f46bb023054ec27ca0dbff2c249dc0135af07cd..472f50a3ebdce1945bac401676816e6f334ced86 100644 --- a/metadata/report/zookeeper/report.go +++ b/metadata/report/zookeeper/report.go @@ -22,6 +22,10 @@ import ( "time" ) +import ( + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" @@ -29,7 +33,6 @@ import ( "github.com/apache/dubbo-go/metadata/identifier" "github.com/apache/dubbo-go/metadata/report" "github.com/apache/dubbo-go/metadata/report/factory" - "github.com/apache/dubbo-go/remoting/zookeeper" ) var ( @@ -46,7 +49,7 @@ func init() { // zookeeperMetadataReport is the implementation of // MetadataReport based on zookeeper. type zookeeperMetadataReport struct { - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient rootDir string } @@ -63,7 +66,7 @@ func (m *zookeeperMetadataReport) StoreConsumerMetadata(consumerMetadataIdentifi } // SaveServiceMetadata saves the metadata. -func (m *zookeeperMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (m *zookeeperMetadataReport) SaveServiceMetadata(metadataIdentifier *identifier.ServiceMetadataIdentifier, url *common.URL) error { k := m.rootDir + metadataIdentifier.GetFilePathKey() return m.client.CreateWithValue(k, []byte(url.String())) } @@ -112,10 +115,11 @@ type zookeeperMetadataReportFactory struct { // nolint func (mf *zookeeperMetadataReportFactory) CreateMetadataReport(url *common.URL) report.MetadataReport { - client, err := zookeeper.NewZookeeperClient( + client, err := gxzookeeper.NewZookeeperClient( "zookeeperMetadataReport", strings.Split(url.Location, ","), - 15*time.Second, + false, + gxzookeeper.WithZkTimeOut(15*time.Second), ) if err != nil { panic(err) diff --git a/metadata/report/zookeeper/report_test.go b/metadata/report/zookeeper/report_test.go index a1e46e2e8d019c0415699ee409833b392a85b504..3f927b1af7ae30ac3dc4af3743e38dba00bb0ae9 100644 --- a/metadata/report/zookeeper/report_test.go +++ b/metadata/report/zookeeper/report_test.go @@ -100,7 +100,7 @@ func (suite *zookeeperMetadataReportTestSuite) testStoreConsumerMetadata() { assert.NoError(suite.t, err) } -func (suite *zookeeperMetadataReportTestSuite) testSaveServiceMetadata(url common.URL) { +func (suite *zookeeperMetadataReportTestSuite) testSaveServiceMetadata(url *common.URL) { serviceMi := newServiceMetadataIdentifier("provider") err := suite.m.SaveServiceMetadata(serviceMi, url) assert.NoError(suite.t, err) @@ -119,7 +119,7 @@ func (suite *zookeeperMetadataReportTestSuite) testGetExportedURLs() { assert.NoError(suite.t, err) } -func (suite *zookeeperMetadataReportTestSuite) testSaveSubscribedData(url common.URL) { +func (suite *zookeeperMetadataReportTestSuite) testSaveSubscribedData(url *common.URL) { subscribeMi := newSubscribeMetadataIdentifier("provider") urls := []string{url.String()} bytes, _ := json.Marshal(urls) @@ -144,7 +144,10 @@ func (suite *zookeeperMetadataReportTestSuite) testGetServiceDefinition() { func test1(t *testing.T) { testCluster, err := zk.StartTestCluster(1, nil, nil) assert.NoError(t, err) - defer testCluster.Stop() + defer func() { + err := testCluster.Stop() + assert.Nil(t, err) + }() url := newProviderRegistryUrl("127.0.0.1", testCluster.Servers[0].Port) mf := extension.GetMetadataReportFactory("zookeeper") @@ -153,10 +156,10 @@ func test1(t *testing.T) { suite := newZookeeperMetadataReportTestSuite(t, m) suite.testStoreProviderMetadata() suite.testStoreConsumerMetadata() - suite.testSaveServiceMetadata(*url) + suite.testSaveServiceMetadata(url) suite.testGetExportedURLs() suite.testRemoveServiceMetadata() - suite.testSaveSubscribedData(*url) + suite.testSaveSubscribedData(url) suite.testGetSubscribedURLs() suite.testGetServiceDefinition() } diff --git a/metadata/service/exporter/configurable/exporter_test.go b/metadata/service/exporter/configurable/exporter_test.go index ceda2550e2e60ed2e587dabe92cca20831e708a6..7c2baa2728b4c4888d9dbb117816648d72874c0b 100644 --- a/metadata/service/exporter/configurable/exporter_test.go +++ b/metadata/service/exporter/configurable/exporter_test.go @@ -65,11 +65,11 @@ func TestConfigurableExporter(t *testing.T) { t.Run("configurableExporter", func(t *testing.T) { registryURL, _ := common.NewURL("service-discovery://localhost:12345") subURL, _ := common.NewURL("dubbo://localhost:20003") - registryURL.SubURL = &subURL + registryURL.SubURL = subURL assert.Equal(t, false, exported.IsExported()) - assert.NoError(t, exported.Export(®istryURL)) + assert.NoError(t, exported.Export(registryURL)) assert.Equal(t, true, exported.IsExported()) - assert.Regexp(t, "dubbo://:20003/MetadataService*", exported.GetExportedURLs()[0].String()) + assert.Regexp(t, "dubbo://:20003/org.apache.dubbo.metadata.MetadataService*", exported.GetExportedURLs()[0].String()) exported.Unexport() assert.Equal(t, false, exported.IsExported()) }) diff --git a/metadata/service/inmemory/metadata_service_proxy_factory.go b/metadata/service/inmemory/metadata_service_proxy_factory.go index 1f8eeaa55f4a0240746508fee2ff088e3a653ca5..becd804f6741e27dda418a4ac9ce976755c634c4 100644 --- a/metadata/service/inmemory/metadata_service_proxy_factory.go +++ b/metadata/service/inmemory/metadata_service_proxy_factory.go @@ -50,7 +50,7 @@ func createProxy(ins registry.ServiceInstance) service.MetadataService { u := urls[0] p := extension.GetProtocol(u.Protocol) - invoker := p.Refer(*u) + invoker := p.Refer(u) return &MetadataServiceProxy{ invkr: invoker, } diff --git a/metadata/service/inmemory/metadata_service_proxy_factory_test.go b/metadata/service/inmemory/metadata_service_proxy_factory_test.go index 96020e1eb762442f946ccf8b368d6ebe9429d05e..f5e519cd5d2f83b85eaccf636e6771c18c74c277 100644 --- a/metadata/service/inmemory/metadata_service_proxy_factory_test.go +++ b/metadata/service/inmemory/metadata_service_proxy_factory_test.go @@ -66,11 +66,11 @@ func TestCreateProxy(t *testing.T) { type mockProtocol struct { } -func (m mockProtocol) Export(invoker protocol.Invoker) protocol.Exporter { +func (m mockProtocol) Export(protocol.Invoker) protocol.Exporter { panic("implement me") } -func (m mockProtocol) Refer(url common.URL) protocol.Invoker { +func (m mockProtocol) Refer(*common.URL) protocol.Invoker { return &mockInvoker{} } @@ -81,7 +81,7 @@ func (m mockProtocol) Destroy() { type mockInvoker struct { } -func (m *mockInvoker) GetUrl() common.URL { +func (m *mockInvoker) GetUrl() *common.URL { panic("implement me") } diff --git a/metadata/service/inmemory/service.go b/metadata/service/inmemory/service.go index 8269e691f1794fd9ac4b6091c157539e39ad7072..8da78c34207c37a7f7e3f475502ac43e7d88b6fd 100644 --- a/metadata/service/inmemory/service.go +++ b/metadata/service/inmemory/service.go @@ -22,7 +22,6 @@ import ( ) import ( - cm "github.com/Workiva/go-datastructures/common" "github.com/Workiva/go-datastructures/slice/skip" ) @@ -75,23 +74,6 @@ func NewMetadataService() (service.MetadataService, error) { return metadataServiceInstance, nil } -// Comparator is defined as Comparator for skip list to compare the URL -type Comparator common.URL - -// Compare is defined as Comparator for skip list to compare the URL -func (c Comparator) Compare(comp cm.Comparator) int { - a := common.URL(c).String() - b := common.URL(comp.(Comparator)).String() - switch { - case a > b: - return 1 - case a < b: - return -1 - default: - return 0 - } -} - // addURL will add URL in memory func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { var ( @@ -101,7 +83,7 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { logger.Debug(url.ServiceKey()) if urlSet, loaded = targetMap.LoadOrStore(url.ServiceKey(), skip.New(uint64(0))); loaded { mts.lock.RLock() - wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + wantedUrl := urlSet.(*skip.SkipList).Get(url) if len(wantedUrl) > 0 && wantedUrl[0] != nil { mts.lock.RUnlock() return false @@ -110,12 +92,12 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { } mts.lock.Lock() // double chk - wantedUrl := urlSet.(*skip.SkipList).Get(Comparator(*url)) + wantedUrl := urlSet.(*skip.SkipList).Get(url) if len(wantedUrl) > 0 && wantedUrl[0] != nil { mts.lock.Unlock() return false } - urlSet.(*skip.SkipList).Insert(Comparator(*url)) + urlSet.(*skip.SkipList).Insert(url) mts.lock.Unlock() return true } @@ -124,7 +106,7 @@ func (mts *MetadataService) addURL(targetMap *sync.Map, url *common.URL) bool { func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) { if value, loaded := targetMap.Load(url.ServiceKey()); loaded { mts.lock.Lock() - value.(*skip.SkipList).Delete(Comparator(*url)) + value.(*skip.SkipList).Delete(url) mts.lock.Unlock() mts.lock.RLock() defer mts.lock.RUnlock() @@ -135,14 +117,14 @@ func (mts *MetadataService) removeURL(targetMap *sync.Map, url *common.URL) { } // getAllService can return all the exportedUrlString except for metadataService -func (mts *MetadataService) getAllService(services *sync.Map) []common.URL { +func (mts *MetadataService) getAllService(services *sync.Map) []*common.URL { // using skip list to dedup and sorting - res := make([]common.URL, 0) + var res []*common.URL services.Range(func(key, value interface{}) bool { urls := value.(*skip.SkipList) for i := uint64(0); i < urls.Len(); i++ { - url := common.URL(urls.ByPosition(i).(Comparator)) - if url.GetParam(constant.INTERFACE_KEY, url.Path) != constant.METADATA_SERVICE_NAME { + url := urls.ByPosition(i).(*common.URL) + if url.Service() != constant.METADATA_SERVICE_NAME { res = append(res, url) } } @@ -153,13 +135,13 @@ func (mts *MetadataService) getAllService(services *sync.Map) []common.URL { } // getSpecifiedService can return specified service url by serviceKey -func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) []common.URL { - res := make([]common.URL, 0) +func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey string, protocol string) []*common.URL { + var res []*common.URL serviceList, loaded := services.Load(serviceKey) if loaded { urls := serviceList.(*skip.SkipList) for i := uint64(0); i < urls.Len(); i++ { - url := common.URL(urls.ByPosition(i).(Comparator)) + url := urls.ByPosition(i).(*common.URL) if len(protocol) == 0 || protocol == constant.ANY_VALUE || url.Protocol == protocol || url.GetParam(constant.PROTOCOL_KEY, "") == protocol { res = append(res, url) } @@ -170,34 +152,34 @@ func (mts *MetadataService) getSpecifiedService(services *sync.Map, serviceKey s } // ExportURL can store the in memory -func (mts *MetadataService) ExportURL(url common.URL) (bool, error) { - return mts.addURL(mts.exportedServiceURLs, &url), nil +func (mts *MetadataService) ExportURL(url *common.URL) (bool, error) { + return mts.addURL(mts.exportedServiceURLs, url), nil } // UnexportURL can remove the url store in memory -func (mts *MetadataService) UnexportURL(url common.URL) error { - mts.removeURL(mts.exportedServiceURLs, &url) +func (mts *MetadataService) UnexportURL(url *common.URL) error { + mts.removeURL(mts.exportedServiceURLs, url) return nil } // SubscribeURL can store the in memory -func (mts *MetadataService) SubscribeURL(url common.URL) (bool, error) { - return mts.addURL(mts.subscribedServiceURLs, &url), nil +func (mts *MetadataService) SubscribeURL(url *common.URL) (bool, error) { + return mts.addURL(mts.subscribedServiceURLs, url), nil } // UnsubscribeURL can remove the url store in memory -func (mts *MetadataService) UnsubscribeURL(url common.URL) error { - mts.removeURL(mts.subscribedServiceURLs, &url) +func (mts *MetadataService) UnsubscribeURL(url *common.URL) error { + mts.removeURL(mts.subscribedServiceURLs, url) return nil } // PublishServiceDefinition: publish url's service metadata info, and write into memory -func (mts *MetadataService) PublishServiceDefinition(url common.URL) error { +func (mts *MetadataService) PublishServiceDefinition(url *common.URL) error { interfaceName := url.GetParam(constant.INTERFACE_KEY, "") isGeneric := url.GetParamBool(constant.GENERIC_KEY, false) if len(interfaceName) > 0 && !isGeneric { - service := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service())) - sd := definition.BuildServiceDefinition(*service, url) + tmpService := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey()) + sd := definition.BuildServiceDefinition(*tmpService, url) data, err := sd.ToBytes() if err != nil { logger.Errorf("publishProvider getServiceDescriptor error. providerUrl:%v , error:%v ", url, err) @@ -221,7 +203,7 @@ func (mts *MetadataService) GetExportedURLs(serviceInterface string, group strin } // GetSubscribedURLs get all subscribedUrl -func (mts *MetadataService) GetSubscribedURLs() ([]common.URL, error) { +func (mts *MetadataService) GetSubscribedURLs() ([]*common.URL, error) { return mts.getAllService(mts.subscribedServiceURLs), nil } @@ -239,7 +221,7 @@ func (mts *MetadataService) GetServiceDefinitionByServiceKey(serviceKey string) } // RefreshMetadata will always return true because it will be implement by remote service -func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { +func (mts *MetadataService) RefreshMetadata(string, string) (bool, error) { return true, nil } diff --git a/metadata/service/inmemory/service_proxy.go b/metadata/service/inmemory/service_proxy.go index e2b29686f49aeade5c61a46f464e6bf00165e70c..8b93aab9accfd8375ded971c27f898069e73e231 100644 --- a/metadata/service/inmemory/service_proxy.go +++ b/metadata/service/inmemory/service_proxy.go @@ -39,8 +39,8 @@ import ( // this is the stub, or proxy // for now, only GetExportedURLs need to be implemented type MetadataServiceProxy struct { - invkr protocol.Invoker - golangServer bool + invkr protocol.Invoker + //golangServer bool } func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { @@ -67,11 +67,7 @@ func (m *MetadataServiceProxy) GetExportedURLs(serviceInterface string, group st urlStrs := res.Result().(*[]interface{}) ret := make([]interface{}, 0, len(*urlStrs)) - - for _, s := range *urlStrs { - ret = append(ret, s) - } - return ret, nil + return append(ret, *urlStrs...), nil } func (m *MetadataServiceProxy) MethodMapper() map[string]string { @@ -88,34 +84,34 @@ func (m *MetadataServiceProxy) ServiceName() (string, error) { return "", nil } -func (m *MetadataServiceProxy) ExportURL(url common.URL) (bool, error) { +func (m *MetadataServiceProxy) ExportURL(url *common.URL) (bool, error) { logger.Error("you should never invoke this implementation") return false, nil } -func (m *MetadataServiceProxy) UnexportURL(url common.URL) error { +func (m *MetadataServiceProxy) UnexportURL(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } -func (m *MetadataServiceProxy) SubscribeURL(url common.URL) (bool, error) { +func (m *MetadataServiceProxy) SubscribeURL(url *common.URL) (bool, error) { logger.Error("you should never invoke this implementation") return false, nil } -func (m *MetadataServiceProxy) UnsubscribeURL(url common.URL) error { +func (m *MetadataServiceProxy) UnsubscribeURL(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } -func (m *MetadataServiceProxy) PublishServiceDefinition(url common.URL) error { +func (m *MetadataServiceProxy) PublishServiceDefinition(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } -func (m *MetadataServiceProxy) GetSubscribedURLs() ([]common.URL, error) { +func (m *MetadataServiceProxy) GetSubscribedURLs() ([]*common.URL, error) { logger.Error("you should never invoke this implementation") - return []common.URL{}, nil + return nil, nil } func (m *MetadataServiceProxy) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { diff --git a/metadata/service/inmemory/service_proxy_test.go b/metadata/service/inmemory/service_proxy_test.go index 0d75517e418133ffbf3804ec96f061dda09b9e5e..9278fd9d6cf1b4657d35028aa8c244fdbc16d106 100644 --- a/metadata/service/inmemory/service_proxy_test.go +++ b/metadata/service/inmemory/service_proxy_test.go @@ -48,19 +48,33 @@ func TestMetadataServiceProxy_GetExportedURLs(t *testing.T) { // when we implement them, adding UT func TestNewMetadataService(t *testing.T) { pxy := createPxy() - pxy.ServiceName() - pxy.PublishServiceDefinition(common.URL{}) - pxy.GetServiceDefinition(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) - pxy.Version() - pxy.GetSubscribedURLs() - pxy.UnsubscribeURL(common.URL{}) - pxy.GetServiceDefinitionByServiceKey("any") - pxy.ExportURL(common.URL{}) - pxy.SubscribeURL(common.URL{}) - pxy.MethodMapper() - pxy.UnexportURL(common.URL{}) - pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) - + _, err := pxy.ServiceName() + assert.Nil(t, err) + err = pxy.PublishServiceDefinition(&common.URL{}) + assert.Nil(t, err) + _, err = pxy.GetServiceDefinition(constant.ANY_VALUE, constant.ANY_VALUE, constant.ANY_VALUE) + assert.Nil(t, err) + _, err = pxy.Version() + assert.Nil(t, err) + _, err = pxy.GetSubscribedURLs() + assert.Nil(t, err) + err = pxy.UnsubscribeURL(&common.URL{}) + assert.Nil(t, err) + _, err = pxy.GetServiceDefinitionByServiceKey("any") + assert.Nil(t, err) + ok, err := pxy.ExportURL(&common.URL{}) + assert.False(t, ok) + assert.NoError(t, err) + ok, err = pxy.SubscribeURL(&common.URL{}) + assert.False(t, ok) + assert.NoError(t, err) + m := pxy.MethodMapper() + assert.True(t, len(m) == 0) + err = pxy.UnexportURL(&common.URL{}) + assert.NoError(t, err) + ok, err = pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) + assert.False(t, ok) + assert.NoError(t, err) } func createPxy() service.MetadataService { diff --git a/metadata/service/inmemory/service_test.go b/metadata/service/inmemory/service_test.go index 048c286fdf28fba6a15a86164df0789d421f0797..e50cd6208a4d86f8238cc68538493c309870c455 100644 --- a/metadata/service/inmemory/service_test.go +++ b/metadata/service/inmemory/service_test.go @@ -46,7 +46,9 @@ func TestMetadataService(t *testing.T) { "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", protocol, serviceName, group, version, beanName)) assert.NoError(t, err) - mts.ExportURL(u2) + ok, err := mts.ExportURL(u2) + assert.True(t, ok) + assert.NoError(t, err) u3, err := common.NewURL(fmt.Sprintf( "%v://127.0.0.1:20000/com.ikurento.user.UserProvider3?anyhost=true&"+ @@ -55,7 +57,9 @@ func TestMetadataService(t *testing.T) { "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", protocol, serviceName, group, version, beanName)) assert.NoError(t, err) - mts.ExportURL(u3) + ok, err = mts.ExportURL(u3) + assert.True(t, ok) + assert.NoError(t, err) u, err := common.NewURL(fmt.Sprintf( "%v://127.0.0.1:20000/com.ikurento.user.UserProvider1?anyhost=true&"+ @@ -64,32 +68,45 @@ func TestMetadataService(t *testing.T) { "owner=ZX&pid=1447&revision=0.0.1&side=provider&timeout=3000×tamp=1556509797245&group=%v&version=%v&bean.name=%v", protocol, serviceName, group, version, beanName)) assert.NoError(t, err) - mts.ExportURL(u) + ok, err = mts.ExportURL(u) + assert.True(t, ok) + assert.NoError(t, err) list, _ := mts.GetExportedURLs(serviceName, group, version, protocol) assert.Equal(t, 3, len(list)) - mts.SubscribeURL(u) + ok, err = mts.SubscribeURL(u) + assert.True(t, ok) + assert.NoError(t, err) - mts.SubscribeURL(u) - list2, _ := mts.GetSubscribedURLs() + ok, err = mts.SubscribeURL(u) + assert.False(t, ok) + assert.NoError(t, err) + list2, err := mts.GetSubscribedURLs() assert.Equal(t, 1, len(list2)) + assert.NoError(t, err) - mts.UnexportURL(u) + err = mts.UnexportURL(u) + assert.NoError(t, err) list3, _ := mts.GetExportedURLs(serviceName, group, version, protocol) assert.Equal(t, 2, len(list3)) - mts.UnsubscribeURL(u) + err = mts.UnsubscribeURL(u) + assert.NoError(t, err) list4, _ := mts.GetSubscribedURLs() assert.Equal(t, 0, len(list4)) userProvider := &definition.UserProvider{} - common.ServiceMap.Register(serviceName, protocol, userProvider) - mts.PublishServiceDefinition(u) + _, err = common.ServiceMap.Register(serviceName, protocol, group, version, userProvider) + assert.NoError(t, err) + err = mts.PublishServiceDefinition(u) + assert.NoError(t, err) expected := "{\"CanonicalName\":\"com.ikurento.user.UserProvider\",\"CodeSource\":\"\"," + "\"Methods\":[{\"Name\":\"GetUser\",\"ParameterTypes\":[\"slice\"],\"ReturnType\":\"ptr\"," + "\"Parameters\":null}],\"Types\":null}" - def1, _ := mts.GetServiceDefinition(serviceName, group, version) + def1, err := mts.GetServiceDefinition(serviceName, group, version) assert.Equal(t, expected, def1) + assert.NoError(t, err) serviceKey := definition.ServiceDescriperBuild(serviceName, group, version) - def2, _ := mts.GetServiceDefinitionByServiceKey(serviceKey) + def2, err := mts.GetServiceDefinitionByServiceKey(serviceKey) assert.Equal(t, expected, def2) + assert.NoError(t, err) } diff --git a/metadata/service/remote/service.go b/metadata/service/remote/service.go index ae83a69bef0af1614352c99c1e512a63770a0eff..e2a7a64d5311610b8d7c799d867f7f39c7f44fb8 100644 --- a/metadata/service/remote/service.go +++ b/metadata/service/remote/service.go @@ -89,47 +89,67 @@ func (mts *MetadataService) setInMemoryMetadataService(metadata *inmemory.Metada } // ExportURL will be implemented by in memory service -func (mts *MetadataService) ExportURL(url common.URL) (bool, error) { +func (mts *MetadataService) ExportURL(url *common.URL) (bool, error) { return mts.inMemoryMetadataService.ExportURL(url) } // UnexportURL remove @url's metadata -func (mts *MetadataService) UnexportURL(url common.URL) error { +func (mts *MetadataService) UnexportURL(url *common.URL) error { smi := identifier.NewServiceMetadataIdentifier(url) smi.Revision = mts.exportedRevision.Load() return mts.delegateReport.RemoveServiceMetadata(smi) } // SubscribeURL will be implemented by in memory service -func (mts *MetadataService) SubscribeURL(url common.URL) (bool, error) { +func (mts *MetadataService) SubscribeURL(url *common.URL) (bool, error) { return mts.inMemoryMetadataService.SubscribeURL(url) } // UnsubscribeURL will be implemented by in memory service -func (mts *MetadataService) UnsubscribeURL(url common.URL) error { - return mts.UnsubscribeURL(url) +func (mts *MetadataService) UnsubscribeURL(url *common.URL) error { + // TODO remove call local. + return nil + //return mts.UnsubscribeURL(url) } // PublishServiceDefinition will call remote metadata's StoreProviderMetadata to store url info and service definition -func (mts *MetadataService) PublishServiceDefinition(url common.URL) error { +func (mts *MetadataService) PublishServiceDefinition(url *common.URL) error { interfaceName := url.GetParam(constant.INTERFACE_KEY, "") isGeneric := url.GetParamBool(constant.GENERIC_KEY, false) - if len(interfaceName) > 0 && !isGeneric { - sv := common.ServiceMap.GetService(url.Protocol, url.GetParam(constant.BEAN_NAME_KEY, url.Service())) - sd := definition.BuildServiceDefinition(*sv, url) + if common.RoleType(common.PROVIDER).Role() == url.GetParam(constant.SIDE_KEY, "") { + if len(interfaceName) > 0 && !isGeneric { + sv := common.ServiceMap.GetServiceByServiceKey(url.Protocol, url.ServiceKey()) + sd := definition.BuildServiceDefinition(*sv, url) + id := &identifier.MetadataIdentifier{ + BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{ + ServiceInterface: interfaceName, + Version: url.GetParam(constant.VERSION_KEY, ""), + Group: url.GetParam(constant.GROUP_KEY, constant.DUBBO), + Side: url.GetParam(constant.SIDE_KEY, constant.PROVIDER_PROTOCOL), + }, + } + mts.delegateReport.StoreProviderMetadata(id, sd) + return nil + } + logger.Errorf("publishProvider interfaceName is empty . providerUrl:%v ", url) + } else { + params := make(map[string]string, len(url.GetParams())) + url.RangeParams(func(key, value string) bool { + params[key] = value + return true + }) id := &identifier.MetadataIdentifier{ BaseMetadataIdentifier: identifier.BaseMetadataIdentifier{ ServiceInterface: interfaceName, Version: url.GetParam(constant.VERSION_KEY, ""), - // Group: url.GetParam(constant.GROUP_KEY, constant.SERVICE_DISCOVERY_DEFAULT_GROUP), - Group: url.GetParam(constant.GROUP_KEY, constant.DUBBO), - Side: url.GetParam(constant.SIDE_KEY, "provider"), + Group: url.GetParam(constant.GROUP_KEY, constant.DUBBO), + Side: url.GetParam(constant.SIDE_KEY, "consumer"), }, } - mts.delegateReport.StoreProviderMetadata(id, sd) + mts.delegateReport.StoreConsumerMetadata(id, params) return nil } - logger.Errorf("publishProvider interfaceName is empty . providerUrl:%v ", url) + return nil } @@ -139,7 +159,7 @@ func (mts *MetadataService) GetExportedURLs(serviceInterface string, group strin } // GetSubscribedURLs will be implemented by in memory service -func (mts *MetadataService) GetSubscribedURLs() ([]common.URL, error) { +func (mts *MetadataService) GetSubscribedURLs() ([]*common.URL, error) { return mts.inMemoryMetadataService.GetSubscribedURLs() } @@ -186,7 +206,7 @@ func (mts *MetadataService) RefreshMetadata(exportedRevision string, subscribedR logger.Errorf("Error occur when execute remote.MetadataService.RefreshMetadata, error message is %v+", err) return false, err } - if urls != nil && len(urls) > 0 { + if len(urls) > 0 { id := &identifier.SubscriberMetadataIdentifier{ MetadataIdentifier: identifier.MetadataIdentifier{ Application: config.GetApplicationConfig().Name, diff --git a/metadata/service/remote/service_proxy.go b/metadata/service/remote/service_proxy.go index eaf7a02f4a0f3a8280835940bd8da720a0bde9f5..e0cd6e0783fe4572e0a69cf0694d70f74ba46b42 100644 --- a/metadata/service/remote/service_proxy.go +++ b/metadata/service/remote/service_proxy.go @@ -45,27 +45,27 @@ func (m *metadataServiceProxy) ServiceName() (string, error) { return m.serviceName, nil } -func (m *metadataServiceProxy) ExportURL(url common.URL) (bool, error) { +func (m *metadataServiceProxy) ExportURL(url *common.URL) (bool, error) { logger.Error("you should never invoke this implementation") return true, nil } -func (m *metadataServiceProxy) UnexportURL(url common.URL) error { +func (m *metadataServiceProxy) UnexportURL(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } -func (m *metadataServiceProxy) SubscribeURL(url common.URL) (bool, error) { +func (m *metadataServiceProxy) SubscribeURL(url *common.URL) (bool, error) { logger.Error("you should never invoke this implementation") return true, nil } -func (m *metadataServiceProxy) UnsubscribeURL(url common.URL) error { +func (m *metadataServiceProxy) UnsubscribeURL(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } -func (m *metadataServiceProxy) PublishServiceDefinition(url common.URL) error { +func (m *metadataServiceProxy) PublishServiceDefinition(url *common.URL) error { logger.Error("you should never invoke this implementation") return nil } @@ -85,7 +85,7 @@ func (m *metadataServiceProxy) GetExportedURLs(serviceInterface string, group st if err != nil { return []interface{}{}, nil } - res := make([]common.URL, 0, len(urls)) + var res []*common.URL for _, s := range urls { u, err := common.NewURL(s) if err != nil { @@ -101,9 +101,9 @@ func (m *metadataServiceProxy) MethodMapper() map[string]string { return map[string]string{} } -func (m *metadataServiceProxy) GetSubscribedURLs() ([]common.URL, error) { +func (m *metadataServiceProxy) GetSubscribedURLs() ([]*common.URL, error) { logger.Error("you should never invoke this implementation") - return []common.URL{}, nil + return nil, nil } func (m *metadataServiceProxy) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { @@ -135,7 +135,7 @@ func (m metadataServiceProxy) Version() (string, error) { func newMetadataServiceProxy(ins registry.ServiceInstance) service.MetadataService { revision := ins.GetMetadata()[constant.EXPORTED_SERVICES_REVISION_PROPERTY_NAME] if len(revision) == 0 { - revision = constant.DEFAULT_REVIESION + revision = constant.DEFAULT_REVISION } return &metadataServiceProxy{ @@ -146,7 +146,7 @@ func newMetadataServiceProxy(ins registry.ServiceInstance) service.MetadataServi } func parse(key string) []string { - arr := make([]string, 3, 3) + arr := make([]string, 3) tmp := strings.SplitN(key, "/", 2) if len(tmp) > 1 { arr[0] = tmp[0] diff --git a/metadata/service/remote/service_proxy_test.go b/metadata/service/remote/service_proxy_test.go index c284bb22123e731f3b8905f70508856bc767ace6..1899d02205e17f62637488e68630649e60cff061 100644 --- a/metadata/service/remote/service_proxy_test.go +++ b/metadata/service/remote/service_proxy_test.go @@ -54,18 +54,28 @@ func TestMetadataServiceProxy_GetServiceDefinition(t *testing.T) { // in fact, we don't use them func TestMetadataServiceProxy(t *testing.T) { pxy := createProxy() - pxy.ServiceName() - pxy.PublishServiceDefinition(common.URL{}) - pxy.Version() - pxy.GetSubscribedURLs() - pxy.UnsubscribeURL(common.URL{}) - pxy.GetServiceDefinitionByServiceKey("any") - pxy.ExportURL(common.URL{}) - pxy.SubscribeURL(common.URL{}) - pxy.MethodMapper() - pxy.UnexportURL(common.URL{}) - pxy.Reference() - pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) + _, err := pxy.ServiceName() + assert.NoError(t, err) + err = pxy.PublishServiceDefinition(&common.URL{}) + assert.NoError(t, err) + _, err = pxy.Version() + assert.NoError(t, err) + _, err = pxy.GetSubscribedURLs() + assert.NoError(t, err) + err = pxy.UnsubscribeURL(&common.URL{}) + assert.NoError(t, err) + _, err = pxy.GetServiceDefinitionByServiceKey("any") + assert.NoError(t, err) + _, err = pxy.ExportURL(&common.URL{}) + assert.NoError(t, err) + _, err = pxy.SubscribeURL(&common.URL{}) + assert.NoError(t, err) + _ = pxy.MethodMapper() + err = pxy.UnexportURL(&common.URL{}) + assert.NoError(t, err) + _ = pxy.Reference() + _, err = pxy.RefreshMetadata(constant.ANY_VALUE, constant.ANY_VALUE) + assert.NoError(t, err) } func createProxy() service.MetadataService { @@ -89,7 +99,7 @@ func prepareTest() { return &mockMetadataReportFactory{} }) u, _ := common.NewURL("mock://localhost") - instance.GetMetadataReportInstance(&u) + instance.GetMetadataReportInstance(u) } type mockMetadataReportFactory struct { @@ -110,7 +120,7 @@ func (m mockMetadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier panic("implement me") } -func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, common.URL) error { +func (m mockMetadataReport) SaveServiceMetadata(*identifier.ServiceMetadataIdentifier, *common.URL) error { return nil } diff --git a/metadata/service/remote/service_test.go b/metadata/service/remote/service_test.go index 734f0989ab06ef17caeef241cd067c678fb8b2ad..d6028152ccb275238491accc055e67d0b903bc55 100644 --- a/metadata/service/remote/service_test.go +++ b/metadata/service/remote/service_test.go @@ -39,7 +39,7 @@ import ( ) var ( - serviceMetadata = make(map[*identifier.ServiceMetadataIdentifier]common.URL, 4) + serviceMetadata = make(map[*identifier.ServiceMetadataIdentifier]*common.URL, 4) subscribedMetadata = make(map[*identifier.SubscriberMetadataIdentifier]string, 4) ) @@ -65,7 +65,7 @@ func (metadataReport) StoreConsumerMetadata(*identifier.MetadataIdentifier, stri return nil } -func (mr *metadataReport) SaveServiceMetadata(id *identifier.ServiceMetadataIdentifier, url common.URL) error { +func (mr *metadataReport) SaveServiceMetadata(id *identifier.ServiceMetadataIdentifier, url *common.URL) error { logger.Infof("SaveServiceMetadata , url is %v", url) serviceMetadata[id] = url return nil @@ -95,9 +95,9 @@ func (metadataReport) GetServiceDefinition(*identifier.MetadataIdentifier) (stri func TestMetadataService(t *testing.T) { extension.SetMetadataReportFactory("mock", getMetadataReportFactory) - u, err := common.NewURL(fmt.Sprintf("mock://127.0.0.1:20000/?sync.report=true")) + u, err := common.NewURL("mock://127.0.0.1:20000/?sync.report=true") assert.NoError(t, err) - instance.GetMetadataReportInstance(&u) + instance.GetMetadataReportInstance(u) mts, err := newMetadataService() assert.NoError(t, err) mts.(*MetadataService).setInMemoryMetadataService(mockInmemoryProc(t)) @@ -126,7 +126,7 @@ func mockInmemoryProc(t *testing.T) *inmemory.MetadataService { _, err = mts.SubscribeURL(u) assert.NoError(t, err) - _, err = common.ServiceMap.Register(serviceName, protocol, userProvider) + _, err = common.ServiceMap.Register(serviceName, protocol, group, version, userProvider) assert.NoError(t, err) err = mts.PublishServiceDefinition(u) assert.NoError(t, err) diff --git a/metadata/service/service.go b/metadata/service/service.go index f6509d0a72eb26e488dfb4fdeef5f4bbfd6b1bea..1d90f8a516831adcae20163a3620dd765459310d 100644 --- a/metadata/service/service.go +++ b/metadata/service/service.go @@ -34,15 +34,15 @@ type MetadataService interface { // ServiceName will get the service's name in meta service , which is application name ServiceName() (string, error) // ExportURL will store the exported url in metadata - ExportURL(url common.URL) (bool, error) + ExportURL(url *common.URL) (bool, error) // UnexportURL will delete the exported url in metadata - UnexportURL(url common.URL) error + UnexportURL(url *common.URL) error // SubscribeURL will store the subscribed url in metadata - SubscribeURL(url common.URL) (bool, error) + SubscribeURL(url *common.URL) (bool, error) // UnsubscribeURL will delete the subscribed url in metadata - UnsubscribeURL(url common.URL) error + UnsubscribeURL(url *common.URL) error // PublishServiceDefinition will generate the target url's code info - PublishServiceDefinition(url common.URL) error + PublishServiceDefinition(url *common.URL) error // GetExportedURLs will get the target exported url in metadata // the url should be unique // due to dubbo-go only support return array []interface{} in RPCService, so we should declare the return type as []interface{} @@ -53,7 +53,7 @@ type MetadataService interface { // GetExportedURLs will get the target subscribed url in metadata // the url should be unique - GetSubscribedURLs() ([]common.URL, error) + GetSubscribedURLs() ([]*common.URL, error) // GetServiceDefinition will get the target service info store in metadata GetServiceDefinition(interfaceName string, group string, version string) (string, error) // GetServiceDefinition will get the target service info store in metadata by service key @@ -122,7 +122,7 @@ func getExportedServicesRevision(serviceInstance registry.ServiceInstance) strin return metaData[constant.EXPORTED_SERVICES_REVISION_PROPERTY_NAME] } -func ConvertURLArrToIntfArr(urls []common.URL) []interface{} { +func ConvertURLArrToIntfArr(urls []*common.URL) []interface{} { if len(urls) == 0 { return []interface{}{} } diff --git a/metrics/prometheus/reporter.go b/metrics/prometheus/reporter.go index bd1e7986ca709a4e10dfcad04d2380931308d568..266c8206c5ea5b6dc07402943bcebdeb6dd7dea7 100644 --- a/metrics/prometheus/reporter.go +++ b/metrics/prometheus/reporter.go @@ -24,6 +24,7 @@ import ( "sync" "time" ) + import ( "github.com/prometheus/client_golang/prometheus" ) @@ -64,7 +65,6 @@ var ( // should initialize after loading configuration func init() { - extension.SetMetricReporter(reporterName, newPrometheusReporter) } @@ -72,12 +72,10 @@ func init() { // if you want to use this feature, you need to initialize your prometheus. // https://prometheus.io/docs/guides/go-application/ type PrometheusReporter struct { - // report the consumer-side's summary data consumerSummaryVec *prometheus.SummaryVec // report the provider-side's summary data providerSummaryVec *prometheus.SummaryVec - // report the provider-side's histogram data providerHistogramVec *prometheus.HistogramVec // report the consumer-side's histogram data @@ -130,13 +128,13 @@ func newHistogramVec(side string) *prometheus.HistogramVec { } // whether this url represents the application received the request as server -func isProvider(url common.URL) bool { +func isProvider(url *common.URL) bool { role := url.GetParam(constant.ROLE_KEY, "") return strings.EqualFold(role, strconv.Itoa(common.PROVIDER)) } // whether this url represents the application sent then request as client -func isConsumer(url common.URL) bool { +func isConsumer(url *common.URL) bool { role := url.GetParam(constant.ROLE_KEY, "") return strings.EqualFold(role, strconv.Itoa(common.CONSUMER)) } diff --git a/metrics/prometheus/reporter_test.go b/metrics/prometheus/reporter_test.go index eaba0e324ff078bdfb2fd4b146ac9ea60d429724..1b9853f4d20739269861aac680b1ec491ef552d6 100644 --- a/metrics/prometheus/reporter_test.go +++ b/metrics/prometheus/reporter_test.go @@ -26,6 +26,7 @@ import ( import ( "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/extension" diff --git a/metrics/reporter.go b/metrics/reporter.go index 9a7094fa62d9c0fa3e6ee0a8ef373f91c28d2c90..24d75aa4fe949737d7ca94cfd339a32af14a40be 100644 --- a/metrics/reporter.go +++ b/metrics/reporter.go @@ -21,6 +21,7 @@ import ( "context" "time" ) + import ( "github.com/apache/dubbo-go/protocol" ) diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go index 5e859a7fa2254ba0e4806bc60c037c47777bc641..21376c3145d7d620d7fc12f2bc67efcd65faee4b 100644 --- a/protocol/dubbo/dubbo_codec.go +++ b/protocol/dubbo/dubbo_codec.go @@ -105,7 +105,7 @@ func (c *DubboCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer, er return pkg.Marshal() } -// encode heartbeart request +// encode heartbeat request func (c *DubboCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { header := impl.DubboHeader{ Type: impl.PackageHeartbeat, @@ -177,10 +177,7 @@ func (c *DubboCodec) Decode(data []byte) (remoting.DecodeResult, int, error) { } func (c *DubboCodec) isRequest(data []byte) bool { - if data[2]&byte(0x80) == 0x00 { - return false - } - return true + return data[2]&byte(0x80) != 0x00 } // decode request @@ -238,7 +235,6 @@ func (c *DubboCodec) decodeRequest(data []byte) (*remoting.Request, int, error) 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) @@ -250,7 +246,7 @@ func (c *DubboCodec) decodeResponse(data []byte) (*remoting.Response, int, error return nil, 0, perrors.WithStack(err) } - response = &remoting.Response{ + response := &remoting.Response{ ID: pkg.Header.ID, //Version: pkg.Header., SerialID: pkg.Header.SerialID, diff --git a/protocol/dubbo/dubbo_exporter.go b/protocol/dubbo/dubbo_exporter.go index dd80937c5bdf6718c2047b102115d8c08afcd899..1873a63fe14f1e2bbb415d5a22c157d03f9ae6ee 100644 --- a/protocol/dubbo/dubbo_exporter.go +++ b/protocol/dubbo/dubbo_exporter.go @@ -42,10 +42,9 @@ func NewDubboExporter(key string, invoker protocol.Invoker, exporterMap *sync.Ma // Unexport unexport dubbo service exporter. func (de *DubboExporter) Unexport() { - serviceId := de.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := de.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") de.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(interfaceName, DUBBO, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, DUBBO, de.GetInvoker().GetUrl().ServiceKey()) if err != nil { logger.Errorf("[DubboExporter.Unexport] error: %v", err) } diff --git a/protocol/dubbo/dubbo_invoker.go b/protocol/dubbo/dubbo_invoker.go index bce33508bec92b706a1722c3bbc0ed0607e66643..bc85d73032cbbf01dabb3c951c051cac58f57e7c 100644 --- a/protocol/dubbo/dubbo_invoker.go +++ b/protocol/dubbo/dubbo_invoker.go @@ -22,13 +22,11 @@ import ( "strconv" "strings" "sync" - "sync/atomic" "time" ) import ( "github.com/opentracing/opentracing-go" - perrors "github.com/pkg/errors" ) import ( @@ -41,44 +39,52 @@ import ( "github.com/apache/dubbo-go/remoting" ) -var ( - // ErrNoReply - ErrNoReply = perrors.New("request need @response") - // ErrDestroyedInvoker - ErrDestroyedInvoker = perrors.New("request Destroyed invoker") -) - var ( attachmentKey = []string{constant.INTERFACE_KEY, constant.GROUP_KEY, constant.TOKEN_KEY, constant.TIMEOUT_KEY, constant.VERSION_KEY} ) -// DubboInvoker is implement of protocol.Invoker. A dubboInvoker refer to one service and ip. +// DubboInvoker is implement of protocol.Invoker. A dubboInvoker refers to one service and ip. type DubboInvoker struct { protocol.BaseInvoker // the exchange layer, it is focus on network communication. - client *remoting.ExchangeClient - quitOnce sync.Once + clientGuard *sync.RWMutex + 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 constructor -func NewDubboInvoker(url common.URL, client *remoting.ExchangeClient) *DubboInvoker { +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{ + di := &DubboInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), + clientGuard: &sync.RWMutex{}, client: client, - reqNum: 0, timeout: requestTimeout, } + + return di +} + +func (di *DubboInvoker) setClient(client *remoting.ExchangeClient) { + di.clientGuard.Lock() + defer di.clientGuard.Unlock() + + di.client = client +} + +func (di *DubboInvoker) getClient() *remoting.ExchangeClient { + di.clientGuard.RLock() + defer di.clientGuard.RUnlock() + + return di.client } // Invoke call remoting. @@ -87,15 +93,30 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati err error result protocol.RPCResult ) - if di.reqNum < 0 { + if !di.BaseInvoker.IsAvailable() { // Generally, the case will not happen, because the invoker has been removed // from the invoker list before destroy,so no new request will enter the destroyed invoker logger.Warnf("this dubboInvoker is destroyed") - result.Err = ErrDestroyedInvoker + result.Err = protocol.ErrDestroyedInvoker + return &result + } + + di.clientGuard.RLock() + defer di.clientGuard.RUnlock() + + if di.client == nil { + result.Err = protocol.ErrClientClosed + logger.Debugf("result.Err: %v", result.Err) + return &result + } + + if !di.BaseInvoker.IsAvailable() { + // Generally, the case will not happen, because the invoker has been removed + // from the invoker list before destroy,so no new request will enter the destroyed invoker + logger.Warnf("this dubboInvoker is destroying") + result.Err = protocol.ErrDestroyedInvoker return &result } - atomic.AddInt64(&(di.reqNum), 1) - defer atomic.AddInt64(&(di.reqNum), -1) inv := invocation.(*invocation_impl.RPCInvocation) // init param @@ -125,14 +146,13 @@ func (di *DubboInvoker) Invoke(ctx context.Context, invocation protocol.Invocati 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.AsyncRequest(&invocation, url, timeout, callBack, rest) } else { result.Err = di.client.Send(&invocation, url, timeout) } } else { if inv.Reply() == nil { - result.Err = ErrNoReply + result.Err = protocol.ErrNoReply } else { result.Err = di.client.Request(&invocation, url, timeout, rest) } @@ -161,24 +181,28 @@ func (di *DubboInvoker) getTimeout(invocation *invocation_impl.RPCInvocation) ti return di.timeout } +func (di *DubboInvoker) IsAvailable() bool { + client := di.getClient() + if client != nil { + return client.IsAvailable() + } + + return false +} + // Destroy destroy dubbo client invoker. func (di *DubboInvoker) Destroy() { di.quitOnce.Do(func() { - for { - if di.reqNum == 0 { - di.reqNum = -1 - logger.Infof("dubboInvoker is destroyed,url:{%s}", di.GetUrl().Key()) - di.BaseInvoker.Destroy() - if di.client != nil { - di.client.Close() - di.client = nil - } - break + di.BaseInvoker.Destroy() + client := di.getClient() + if client != nil { + activeNumber := client.DecreaseActiveNumber() + di.setClient(nil) + if activeNumber == 0 { + exchangeClientMap.Delete(di.GetUrl().Location) + client.Close() } - logger.Warnf("DubboInvoker is to be destroyed, wait {%v} req end,url:{%s}", di.reqNum, di.GetUrl().Key()) - time.Sleep(1 * time.Second) } - }) } diff --git a/protocol/dubbo/dubbo_invoker_test.go b/protocol/dubbo/dubbo_invoker_test.go index 4d32c29a222e8bb73486d664b2ed2e0038a4b3f5..fecb3b01ab286d4900cba4628cb80ea6d9920a9a 100644 --- a/protocol/dubbo/dubbo_invoker_test.go +++ b/protocol/dubbo/dubbo_invoker_test.go @@ -89,15 +89,15 @@ func TestDubboInvokerInvoke(t *testing.T) { // destroy lock.Lock() + defer lock.Unlock() proto.Destroy() - lock.Unlock() } -func InitTest(t *testing.T) (protocol.Protocol, common.URL) { +func InitTest(t *testing.T) (protocol.Protocol, *common.URL) { hessian.RegisterPOJO(&User{}) - methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{}) + 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) @@ -169,17 +169,16 @@ type ( } UserProvider struct { - user map[string]User + //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++ { + for i := 0; i < 800; i++ { // use chinese for test argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") - argBuf.WriteString("鍑婚紦鍏堕晽锛岃笂璺冪敤鍏点€傚湡鍥藉煄婕曪紝鎴戠嫭鍗楄銆備粠瀛欏瓙浠诧紝骞抽檲涓庡畫銆備笉鎴戜互褰掞紝蹇у績鏈夊俊銆傜埌灞呯埌澶勶紵鐖颁抚鍏堕┈锛熶簬浠ユ眰涔嬶紵浜庢灄涔嬩笅銆傛鐢熷闃旓紝涓庡瓙鎴愯銆傛墽瀛愪箣鎵嬶紝涓庡瓙鍋曡€併€備簬鍡熼様鍏紝涓嶆垜娲诲叜銆備簬鍡熸吹鍏紝涓嶆垜淇″叜銆�") } rsp.Id = argBuf.String() rsp.Name = argBuf.String() diff --git a/protocol/dubbo/dubbo_protocol.go b/protocol/dubbo/dubbo_protocol.go index 8dda52b6b9b79e5d20a730db52f9a376422ccd63..d6a71dc001a47644dc837f8b25419942f479ff02 100644 --- a/protocol/dubbo/dubbo_protocol.go +++ b/protocol/dubbo/dubbo_protocol.go @@ -90,7 +90,7 @@ func (dp *DubboProtocol) Export(invoker protocol.Invoker) protocol.Exporter { } // Refer create dubbo service reference. -func (dp *DubboProtocol) Refer(url common.URL) protocol.Invoker { +func (dp *DubboProtocol) Refer(url *common.URL) protocol.Invoker { exchangeClient := getExchangeClient(url) if exchangeClient == nil { logger.Warnf("can't dial the server: %+v", url.Location) @@ -115,7 +115,7 @@ func (dp *DubboProtocol) Destroy() { } } -func (dp *DubboProtocol) openServer(url common.URL) { +func (dp *DubboProtocol) openServer(url *common.URL) { _, ok := dp.serverMap[url.Location] if !ok { _, ok := dp.ExporterMap().Load(url.ServiceKey()) @@ -176,7 +176,7 @@ func doHandleRequest(rpcInvocation *invocation.RPCInvocation) protocol.RPCResult return result } -func getExchangeClient(url common.URL) *remoting.ExchangeClient { +func getExchangeClient(url *common.URL) *remoting.ExchangeClient { clientTmp, ok := exchangeClientMap.Load(url.Location) if !ok { var exchangeClientTmp *remoting.ExchangeClient @@ -215,20 +215,22 @@ func getExchangeClient(url common.URL) *remoting.ExchangeClient { if clientTmp == nil { return nil } - return clientTmp.(*remoting.ExchangeClient) + exchangeClient := clientTmp.(*remoting.ExchangeClient) + exchangeClient.IncreaseActiveNumber() + return 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()) + ctx := context.WithValue(context.Background(), constant.DubboCtxKey("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) + ctx = context.WithValue(ctx, constant.DubboCtxKey(constant.TRACING_REMOTE_SPAN_CTX), spanCtx) } return ctx } diff --git a/protocol/dubbo/hessian2/const.go b/protocol/dubbo/hessian2/const.go index 74a00b601db22397916aab215ccd33bc918d91e7..96e6eea8ea4cd3088e0d32157c013e93a93cad4e 100644 --- a/protocol/dubbo/hessian2/const.go +++ b/protocol/dubbo/hessian2/const.go @@ -28,11 +28,6 @@ import ( perrors "github.com/pkg/errors" ) -const ( - mask = byte(127) - flag = byte(128) -) - const ( // Zero : byte zero Zero = byte(0x00) @@ -80,14 +75,10 @@ const ( 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_VARIABLE = byte(0x55) + BC_LIST_FIXED = byte('V') + BC_LIST_VARIABLE_UNTYPED = byte(0x57) + BC_LIST_FIXED_UNTYPED = byte(0x58) BC_LIST_DIRECT = byte(0x70) BC_LIST_DIRECT_UNTYPED = byte(0x78) diff --git a/protocol/dubbo/hessian2/hessian_dubbo.go b/protocol/dubbo/hessian2/hessian_dubbo.go index 1afa4ec96eccbb8077852dfcc020e0eb05be3257..5ffebde54b951997057a1f161dbcf6aae1d4a7fd 100644 --- a/protocol/dubbo/hessian2/hessian_dubbo.go +++ b/protocol/dubbo/hessian2/hessian_dubbo.go @@ -99,7 +99,7 @@ func (h *HessianCodec) Write(service Service, header DubboHeader, body interface return packResponse(header, body) default: - return nil, perrors.Errorf("Unrecognised message type: %v", header.Type) + return nil, perrors.Errorf("Unrecognized message type: %v", header.Type) } // unreachable return nil, nil @@ -190,9 +190,9 @@ func (h *HessianCodec) ReadBody(rspObj interface{}) error { 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) + exception, exceptionErr := decoder.Decode() + if exceptionErr != nil { + return perrors.WithStack(exceptionErr) } rsp, ok := rspObj.(*DubboResponse) if !ok { diff --git a/protocol/dubbo/hessian2/hessian_dubbo_test.go b/protocol/dubbo/hessian2/hessian_dubbo_test.go index c3f19f04536484816e4b4f709f534dcbf4adb2b4..eaaf500738e6c77142b0c0cb267fb0d8e7ac1437 100644 --- a/protocol/dubbo/hessian2/hessian_dubbo_test.go +++ b/protocol/dubbo/hessian2/hessian_dubbo_test.go @@ -80,6 +80,7 @@ func doTestHessianEncodeHeader(t *testing.T, packageType PackageType, responseSt func doTestResponse(t *testing.T, packageType PackageType, responseStatus byte, body interface{}, decodedResponse *DubboResponse, assertFunc func()) { resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) + assert.NoError(t, err) codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) @@ -163,6 +164,7 @@ func TestResponse(t *testing.T) { func doTestRequest(t *testing.T, packageType PackageType, responseStatus byte, body interface{}) { resp, err := doTestHessianEncodeHeader(t, packageType, responseStatus, body) + assert.NoError(t, err) codecR := NewHessianCodec(bufio.NewReader(bytes.NewReader(resp))) @@ -223,7 +225,7 @@ func TestHessianCodec_ReadAttachments(t *testing.T) { type AttachTestObject struct { Id int32 - Name string `dubbo:name` + Name string `dubbo:"name"` } func (AttachTestObject) JavaClassName() string { diff --git a/protocol/dubbo/hessian2/hessian_request.go b/protocol/dubbo/hessian2/hessian_request.go index 4ebb4aa1be05d4d1941661fed452dda06cf55fa0..94aa34dee895bc8d441f710edecabe0326da1400 100644 --- a/protocol/dubbo/hessian2/hessian_request.go +++ b/protocol/dubbo/hessian2/hessian_request.go @@ -30,6 +30,10 @@ import ( perrors "github.com/pkg/errors" ) +import ( + "github.com/apache/dubbo-go/common/logger" +) + ///////////////////////////////////////// // dubbo ///////////////////////////////////////// @@ -39,7 +43,7 @@ func getArgType(v interface{}) string { return "V" } - switch v.(type) { + switch v := v.(type) { // Serialized tags for base types case nil: return "V" @@ -216,23 +220,31 @@ func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, // body ////////////////////////////////////////// if hb { - encoder.Encode(nil) + _ = 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) + if err = encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION); err != nil { + logger.Warnf("Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) = error: %v", err) + } + if err = encoder.Encode(service.Path); err != nil { + logger.Warnf("Encode(service.Path) = error: %v", err) + } + if err = encoder.Encode(service.Version); err != nil { + logger.Warnf("Encode(service.Version) = error: %v", err) + } + if err = encoder.Encode(service.Method); err != nil { + logger.Warnf("Encode(service.Method) = error: %v", err) + } // 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) + _ = encoder.Encode(types) for _, v := range args { - encoder.Encode(v) + _ = encoder.Encode(v) } request.Attachments[PATH_KEY] = service.Path @@ -247,7 +259,7 @@ func packRequest(service Service, header DubboHeader, req interface{}) ([]byte, request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond)) } - encoder.Encode(request.Attachments) + _ = encoder.Encode(request.Attachments) END: byteArray = encoder.Buffer() diff --git a/protocol/dubbo/hessian2/hessian_response.go b/protocol/dubbo/hessian2/hessian_response.go index 982960ed87e74b325687ac364c97a347efe6c38f..b95e1c2711e8922b280260035eaba1cc03f50aa4 100644 --- a/protocol/dubbo/hessian2/hessian_response.go +++ b/protocol/dubbo/hessian2/hessian_response.go @@ -18,6 +18,7 @@ package hessian2 import ( "encoding/binary" + "github.com/apache/dubbo-go/common/logger" "math" "reflect" "strconv" @@ -93,7 +94,9 @@ func packResponse(header DubboHeader, ret interface{}) ([]byte, error) { if header.ResponseStatus == Response_OK { if hb { - encoder.Encode(nil) + if err := encoder.Encode(nil); err != nil { + logger.Warnf("Encode(nil) = %v", err) + } } else { atta := isSupportResponseAttachment(response.Attachments[DUBBO_VERSION_KEY]) @@ -109,30 +112,30 @@ func packResponse(header DubboHeader, ret interface{}) ([]byte, error) { } if response.Exception != nil { // throw error - encoder.Encode(resWithException) + _ = encoder.Encode(resWithException) if t, ok := response.Exception.(java_exception.Throwabler); ok { - encoder.Encode(t) + _ = encoder.Encode(t) } else { - encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) + _ = encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) } } else { if response.RspObj == nil { - encoder.Encode(resNullValue) + _ = encoder.Encode(resNullValue) } else { - encoder.Encode(resValue) - encoder.Encode(response.RspObj) // result + _ = encoder.Encode(resValue) + _ = encoder.Encode(response.RspObj) // result } } if atta { - encoder.Encode(response.Attachments) // attachments + _ = encoder.Encode(response.Attachments) // attachments } } } else { if response.Exception != nil { // throw error - encoder.Encode(response.Exception.Error()) + _ = encoder.Encode(response.Exception.Error()) } else { - encoder.Encode(response.RspObj) + _ = encoder.Encode(response.RspObj) } } @@ -145,7 +148,6 @@ func packResponse(header DubboHeader, ret interface{}) ([]byte, error) { // byteArray{body length} binary.BigEndian.PutUint32(byteArray[12:], uint32(pkgLen-HEADER_LENGTH)) return byteArray, nil - } // hessian decode response body diff --git a/protocol/dubbo/impl/codec.go b/protocol/dubbo/impl/codec.go index c139f3547b2af5196a38be9bc83f49251bfcf043..6c9816f0ff9a4faee7750d16468fa65b9aa803f2 100644 --- a/protocol/dubbo/impl/codec.go +++ b/protocol/dubbo/impl/codec.go @@ -146,7 +146,7 @@ func (c *ProtocolCodec) Encode(p DubboPackage) ([]byte, error) { return packResponse(p, c.serializer) default: - return nil, perrors.Errorf("Unrecognised message type: %v", header.Type) + return nil, perrors.Errorf("Unrecognized message type: %v", header.Type) } } @@ -163,15 +163,12 @@ func (c *ProtocolCodec) Decode(p *DubboPackage) error { if p.IsResponseWithException() { logger.Infof("response with exception: %+v", p.Header) decoder := hessian.NewDecoder(body) + p.Body = &ResponsePayload{} 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)) + p.Body.(*ResponsePayload).Exception = perrors.Errorf("java exception:%s", exception.(string)) return nil } else if p.IsHeartBeat() { // heartbeat no need to unmarshal contents diff --git a/protocol/dubbo/impl/const.go b/protocol/dubbo/impl/const.go index 70d8bae6cad63d436f5d9f1ef69c397ee8a052f3..c9b92bad3b402f6f8656ca47c1a2954c7881d68b 100644 --- a/protocol/dubbo/impl/const.go +++ b/protocol/dubbo/impl/const.go @@ -28,11 +28,6 @@ const ( DUBBO = "dubbo" ) -const ( - mask = byte(127) - flag = byte(128) -) - const ( // Zero : byte zero Zero = byte(0x00) @@ -80,14 +75,10 @@ const ( 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_VARIABLE = byte(0x55) + BC_LIST_FIXED = byte('V') + BC_LIST_VARIABLE_UNTYPED = byte(0x57) + BC_LIST_FIXED_UNTYPED = byte(0x58) BC_LIST_DIRECT = byte(0x70) BC_LIST_DIRECT_UNTYPED = byte(0x78) diff --git a/protocol/dubbo/impl/hessian.go b/protocol/dubbo/impl/hessian.go index b686d5728d026fdb1e37180d5a9d24d32bf4b7bc..e355276c37f9d1babc12f7e3b694ec7a193aed80 100644 --- a/protocol/dubbo/impl/hessian.go +++ b/protocol/dubbo/impl/hessian.go @@ -37,8 +37,6 @@ import ( "github.com/apache/dubbo-go/common/logger" ) -type Object interface{} - type HessianSerializer struct { } @@ -65,7 +63,7 @@ func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { response := EnsureResponsePayload(p.Body) if header.ResponseStatus == Response_OK { if p.IsHeartBeat() { - encoder.Encode(nil) + _ = encoder.Encode(nil) } else { var version string if attachmentVersion, ok := response.Attachments[DUBBO_VERSION_KEY]; ok { @@ -85,30 +83,30 @@ func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { } if response.Exception != nil { // throw error - encoder.Encode(resWithException) + _ = encoder.Encode(resWithException) if t, ok := response.Exception.(java_exception.Throwabler); ok { - encoder.Encode(t) + _ = encoder.Encode(t) } else { - encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) + _ = encoder.Encode(java_exception.NewThrowable(response.Exception.Error())) } } else { if response.RspObj == nil { - encoder.Encode(resNullValue) + _ = encoder.Encode(resNullValue) } else { - encoder.Encode(resValue) - encoder.Encode(response.RspObj) // result + _ = encoder.Encode(resValue) + _ = encoder.Encode(response.RspObj) // result } } if atta { - encoder.Encode(response.Attachments) // attachments + _ = encoder.Encode(response.Attachments) // attachments } } } else { if response.Exception != nil { // throw error - encoder.Encode(response.Exception.Error()) + _ = encoder.Encode(response.Exception.Error()) } else { - encoder.Encode(response.RspObj) + _ = encoder.Encode(response.RspObj) } } bs := encoder.Buffer() @@ -120,10 +118,10 @@ func marshalResponse(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { 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) + _ = encoder.Encode(DEFAULT_DUBBO_PROTOCOL_VERSION) + _ = encoder.Encode(service.Path) + _ = encoder.Encode(service.Version) + _ = encoder.Encode(service.Method) args, ok := request.Params.([]interface{}) @@ -135,9 +133,9 @@ func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { if err != nil { return nil, perrors.Wrapf(err, " PackRequest(args:%+v)", args) } - encoder.Encode(types) + _ = encoder.Encode(types) for _, v := range args { - encoder.Encode(v) + _ = encoder.Encode(v) } request.Attachments[PATH_KEY] = service.Path @@ -152,9 +150,8 @@ func marshalRequest(encoder *hessian.Encoder, p DubboPackage) ([]byte, error) { request.Attachments[TIMEOUT_KEY] = strconv.Itoa(int(service.Timeout / time.Millisecond)) } - encoder.Encode(request.Attachments) + _ = encoder.Encode(request.Attachments) return encoder.Buffer(), nil - } var versionInt = make(map[string]int) @@ -385,7 +382,7 @@ func buildServerSidePackageBody(pkg *DubboPackage) { "dubboVersion": dubboVersion, "argsTypes": argsTypes, "args": args, - "service": common.ServiceMap.GetService(DUBBO, svc.Path), // path as a key + "service": common.ServiceMap.GetService(DUBBO, svc.Interface, svc.Group, svc.Version), // path as a key "attachments": attachments, }) } @@ -420,7 +417,7 @@ func getArgType(v interface{}) string { return "V" } - switch v.(type) { + switch v := v.(type) { // Serialized tags for base types case nil: return "V" @@ -474,7 +471,7 @@ func getArgType(v interface{}) string { return "java.lang.String" case []string: return "[Ljava.lang.String;" - case []Object: + case []hessian.Object: return "[Ljava.lang.Object;" case map[interface{}]interface{}: // return "java.util.HashMap" @@ -489,6 +486,10 @@ func getArgType(v interface{}) string { } switch t.Kind() { case reflect.Struct: + v, ok := v.(hessian.POJO) + if ok { + return v.JavaClassName() + } return "java.lang.Object" case reflect.Slice, reflect.Array: if t.Elem().Kind() == reflect.Struct { diff --git a/protocol/dubbo/opentracing.go b/protocol/dubbo/opentracing.go index 2dcd6a4d0d9f491ba6d51ea7a3ba96812a6f9e08..f45e6fdc71af6279c2fadadd0576cde629a535e1 100644 --- a/protocol/dubbo/opentracing.go +++ b/protocol/dubbo/opentracing.go @@ -35,14 +35,6 @@ func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvoca 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 { diff --git a/protocol/grpc/client.go b/protocol/grpc/client.go index a0ab0be80cc905115e675c1c4dea2b1c748f6c09..24ab125625465068e90f2b0d96033b6fc3ffd2f6 100644 --- a/protocol/grpc/client.go +++ b/protocol/grpc/client.go @@ -19,6 +19,7 @@ package grpc import ( "reflect" + "strconv" ) import ( @@ -89,14 +90,18 @@ type Client struct { } // NewClient creates a new gRPC client. -func NewClient(url common.URL) *Client { +func NewClient(url *common.URL) *Client { // if global trace instance was set , it means trace function enabled. If not , will return Nooptracer tracer := opentracing.GlobalTracer() - dailOpts := make([]grpc.DialOption, 0, 4) - dailOpts = append(dailOpts, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor( + dialOpts := make([]grpc.DialOption, 0, 4) + maxMessageSize, _ := strconv.Atoi(url.GetParam(constant.MESSAGE_SIZE_KEY, "4")) + dialOpts = append(dialOpts, grpc.WithInsecure(), grpc.WithBlock(), grpc.WithUnaryInterceptor( otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads())), - grpc.WithDefaultCallOptions(grpc.CallContentSubtype(clientConf.ContentSubType))) - conn, err := grpc.Dial(url.Location, dailOpts...) + grpc.WithDefaultCallOptions( + grpc.CallContentSubtype(clientConf.ContentSubType), + grpc.MaxCallRecvMsgSize(1024*1024*maxMessageSize), + grpc.MaxCallSendMsgSize(1024*1024*maxMessageSize))) + conn, err := grpc.Dial(url.Location, dialOpts...) if err != nil { panic(err) } @@ -112,7 +117,7 @@ func NewClient(url common.URL) *Client { } func getInvoker(impl interface{}, conn *grpc.ClientConn) interface{} { - in := []reflect.Value{} + var in []reflect.Value in = append(in, reflect.ValueOf(conn)) method := reflect.ValueOf(impl).MethodByName("GetDubboStub") res := method.Call(in) diff --git a/protocol/grpc/grpc_exporter.go b/protocol/grpc/grpc_exporter.go index 0dc764854d61576892800180041c53f0a7735c7c..5beb4fedb1aa0285288d5b2b52515d4f6b9498e8 100644 --- a/protocol/grpc/grpc_exporter.go +++ b/protocol/grpc/grpc_exporter.go @@ -42,10 +42,9 @@ func NewGrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map // Unexport and unregister gRPC service from registry and memory. func (gg *GrpcExporter) Unexport() { - serviceId := gg.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := gg.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") gg.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(interfaceName, GRPC, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, GRPC, gg.GetInvoker().GetUrl().ServiceKey()) if err != nil { logger.Errorf("[GrpcExporter.Unexport] error: %v", err) } diff --git a/protocol/grpc/grpc_invoker.go b/protocol/grpc/grpc_invoker.go index 737e8c40a063b07e56d7c90f7de04670461ce103..7b33c6704db7a365bb81a1ab5bef31f220378380 100644 --- a/protocol/grpc/grpc_invoker.go +++ b/protocol/grpc/grpc_invoker.go @@ -24,46 +24,88 @@ import ( ) import ( + hessian2 "github.com/apache/dubbo-go-hessian2" "github.com/pkg/errors" "google.golang.org/grpc/connectivity" ) import ( - hessian2 "github.com/apache/dubbo-go-hessian2" - "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/protocol" ) -var errNoReply = errors.New("request need @response") +var ( + errNoReply = errors.New("request need @response") +) // nolint type GrpcInvoker struct { protocol.BaseInvoker - quitOnce sync.Once - client *Client + quitOnce sync.Once + clientGuard *sync.RWMutex + client *Client } // NewGrpcInvoker returns a Grpc invoker instance -func NewGrpcInvoker(url common.URL, client *Client) *GrpcInvoker { +func NewGrpcInvoker(url *common.URL, client *Client) *GrpcInvoker { return &GrpcInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), + clientGuard: &sync.RWMutex{}, client: client, } } +func (gi *GrpcInvoker) setClient(client *Client) { + gi.clientGuard.Lock() + defer gi.clientGuard.Unlock() + + gi.client = client +} + +func (gi *GrpcInvoker) getClient() *Client { + gi.clientGuard.RLock() + defer gi.clientGuard.RUnlock() + + return gi.client +} + // Invoke is used to call service method by invocation func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { var ( result protocol.RPCResult ) + if !gi.BaseInvoker.IsAvailable() { + // Generally, the case will not happen, because the invoker has been removed + // from the invoker list before destroy,so no new request will enter the destroyed invoker + logger.Warnf("this grpcInvoker is destroyed") + result.Err = protocol.ErrDestroyedInvoker + return &result + } + + gi.clientGuard.RLock() + defer gi.clientGuard.RUnlock() + + if gi.client == nil { + result.Err = protocol.ErrClientClosed + return &result + } + + if !gi.BaseInvoker.IsAvailable() { + // Generally, the case will not happen, because the invoker has been removed + // from the invoker list before destroy,so no new request will enter the destroyed invoker + logger.Warnf("this grpcInvoker is destroying") + result.Err = protocol.ErrDestroyedInvoker + return &result + } + if invocation.Reply() == nil { result.Err = errNoReply } - in := []reflect.Value{} - in = append(in, reflect.ValueOf(context.Background())) + var in []reflect.Value + in = append(in, reflect.ValueOf(ctx)) in = append(in, invocation.ParameterValues()...) methodName := invocation.MethodName() @@ -83,21 +125,32 @@ func (gi *GrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocatio // IsAvailable get available status func (gi *GrpcInvoker) IsAvailable() bool { - return gi.BaseInvoker.IsAvailable() && gi.client.GetState() != connectivity.Shutdown + client := gi.getClient() + if client != nil { + return gi.BaseInvoker.IsAvailable() && client.GetState() != connectivity.Shutdown + } + + return false } // IsDestroyed get destroyed status func (gi *GrpcInvoker) IsDestroyed() bool { - return gi.BaseInvoker.IsDestroyed() && gi.client.GetState() == connectivity.Shutdown + client := gi.getClient() + if client != nil { + return gi.BaseInvoker.IsDestroyed() && client.GetState() == connectivity.Shutdown + } + + return false } // Destroy will destroy gRPC's invoker and client, so it is only called once func (gi *GrpcInvoker) Destroy() { gi.quitOnce.Do(func() { gi.BaseInvoker.Destroy() - - if gi.client != nil { - _ = gi.client.Close() + client := gi.getClient() + if client != nil { + gi.setClient(nil) + client.Close() } }) } diff --git a/protocol/grpc/grpc_protocol.go b/protocol/grpc/grpc_protocol.go index 68594a4b35921b6e3b1d59d404ed163025d57a81..3ad124532f389d6df5ef746eaca26158b8c42790 100644 --- a/protocol/grpc/grpc_protocol.go +++ b/protocol/grpc/grpc_protocol.go @@ -18,11 +18,13 @@ package grpc import ( + "strconv" "sync" ) 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/protocol" @@ -65,7 +67,7 @@ func (gp *GrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { return exporter } -func (gp *GrpcProtocol) openServer(url common.URL) { +func (gp *GrpcProtocol) openServer(url *common.URL) { _, ok := gp.serverMap[url.Location] if !ok { _, ok := gp.ExporterMap().Load(url.ServiceKey()) @@ -76,7 +78,9 @@ func (gp *GrpcProtocol) openServer(url common.URL) { gp.serverLock.Lock() _, ok = gp.serverMap[url.Location] if !ok { + grpcMessageSize, _ := strconv.Atoi(url.GetParam(constant.MESSAGE_SIZE_KEY, "4")) srv := NewServer() + srv.SetBufferSize(grpcMessageSize) gp.serverMap[url.Location] = srv srv.Start(url) } @@ -85,7 +89,7 @@ func (gp *GrpcProtocol) openServer(url common.URL) { } // Refer a remote gRPC service -func (gp *GrpcProtocol) Refer(url common.URL) protocol.Invoker { +func (gp *GrpcProtocol) Refer(url *common.URL) protocol.Invoker { invoker := NewGrpcInvoker(url, NewClient(url)) gp.SetInvokers(invoker) logger.Infof("Refer service: %s", url.String()) diff --git a/protocol/grpc/protoc-gen-dubbo/main.go b/protocol/grpc/protoc-gen-dubbo/main.go index fbcfa6f9d492afa8bc7848c733358b3cf7223e99..fe3e38dddd8bfc2a30ed89e6088516cad120ef79 100644 --- a/protocol/grpc/protoc-gen-dubbo/main.go +++ b/protocol/grpc/protoc-gen-dubbo/main.go @@ -43,7 +43,7 @@ func main() { g.Error(err, "reading input") } - if err := proto.Unmarshal(data, g.Request); err != nil { + if err = proto.Unmarshal(data, g.Request); err != nil { g.Error(err, "parsing input proto") } diff --git a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go index 1af4fafdc606783e937ede63f99e5a08f0b2419e..ba84f36b74016f1495fa7d616315deda7c54bbcc 100644 --- a/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go +++ b/protocol/grpc/protoc-gen-dubbo/plugin/dubbo/dubbo.go @@ -28,19 +28,11 @@ import ( "github.com/golang/protobuf/protoc-gen-go/generator" ) -// generatedCodeVersion indicates a version of the generated code. -// It is incremented whenever an incompatibility between the generated code and -// the grpc package is introduced; the generated code references -// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion). -const generatedCodeVersion = 4 - // Paths for packages used by code generated in this file, // relative to the import_prefix of the generator.Generator. const ( contextPkgPath = "context" grpcPkgPath = "google.golang.org/grpc" - codePkgPath = "google.golang.org/grpc/codes" - statusPkgPath = "google.golang.org/grpc/status" ) func init() { @@ -220,7 +212,23 @@ func (g *dubboGrpc) generateService(file *generator.FileDescriptor, service *pb. g.P("},") } g.P("},") - g.P("Streams: []", grpcPkg, ".StreamDesc{},") + g.P("Streams: []", grpcPkg, ".StreamDesc{") + for i, method := range service.Method { + if !method.GetClientStreaming() && !method.GetServerStreaming() { + continue + } + g.P("{") + g.P("StreamName: ", strconv.Quote(method.GetName()), ",") + g.P("Handler: ", handlerNames[i], ",") + if method.GetServerStreaming() { + g.P("ServerStreams: true,") + } + if method.GetClientStreaming() { + g.P("ClientStreams: true,") + } + g.P("},") + } + g.P("},") g.P("Metadata: \"", file.GetName(), "\",") g.P("}") g.P("}") @@ -241,18 +249,18 @@ func (g *dubboGrpc) generateClientSignature(servName string, method *pb.MethodDe respName := "out *" + g.typeName(method.GetOutputType()) if method.GetServerStreaming() || method.GetClientStreaming() { respName = servName + "_" + generator.CamelCase(origMethName) + "Client" + return fmt.Sprintf("%s func(ctx %s.Context%s) (%s, error)", methName, contextPkg, reqArg, respName) } return fmt.Sprintf("%s func(ctx %s.Context%s, %s) error", methName, contextPkg, reqArg, respName) } -func (g *dubboGrpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { -} +//func (g *dubboGrpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) { +//} func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method *pb.MethodDescriptorProto) string { methName := generator.CamelCase(method.GetName()) hname := fmt.Sprintf("_DUBBO_%s_%s_Handler", servName, methName) inType := g.typeName(method.GetInputType()) - outType := g.typeName(method.GetOutputType()) if !method.GetServerStreaming() && !method.GetClientStreaming() { g.P("func ", hname, "(srv interface{}, ctx ", contextPkg, ".Context, dec func(interface{}) error, interceptor ", grpcPkg, ".UnaryServerInterceptor) (interface{}, error) {") @@ -265,7 +273,7 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * g.P(`invo := invocation.NewRPCInvocation("`, methName, `", args, nil)`) g.P("if interceptor == nil {") - g.P("result := base.GetProxyImpl().Invoke(context.Background(), invo)") + g.P("result := base.GetProxyImpl().Invoke(ctx, invo)") g.P("return result.Result(), result.Error()") g.P("}") @@ -275,7 +283,7 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * g.P("}") g.P("handler := func(ctx ", contextPkg, ".Context, req interface{}) (interface{}, error) {") - g.P("result := base.GetProxyImpl().Invoke(context.Background(), invo)") + g.P("result := base.GetProxyImpl().Invoke(ctx, invo)") g.P("return result.Result(), result.Error()") g.P("}") @@ -286,6 +294,11 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * } streamType := unexport(servName) + methName + "Server" g.P("func ", hname, "(srv interface{}, stream ", grpcPkg, ".ServerStream) error {") + g.P("_, ok := srv.(dgrpc.DubboGrpcService)") + g.P(`invo := invocation.NewRPCInvocation("`, methName, `", nil, nil)`) + g.P("if !ok {") + g.P("fmt.Println(invo)") + g.P("}") if !method.GetClientStreaming() { g.P("m := new(", inType, ")") g.P("if err := stream.RecvMsg(m); err != nil { return err }") @@ -296,50 +309,5 @@ func (g *dubboGrpc) generateServerMethod(servName, fullServName string, method * g.P("}") g.P() - genSend := method.GetServerStreaming() - genSendAndClose := !method.GetServerStreaming() - genRecv := method.GetClientStreaming() - - // Stream auxiliary types and methods. - g.P("type ", servName, "_", methName, "Server interface {") - if genSend { - g.P("Send(*", outType, ") error") - } - if genSendAndClose { - g.P("SendAndClose(*", outType, ") error") - } - if genRecv { - g.P("Recv() (*", inType, ", error)") - } - g.P(grpcPkg, ".ServerStream") - g.P("}") - g.P() - - g.P("type ", streamType, " struct {") - g.P(grpcPkg, ".ServerStream") - g.P("}") - g.P() - - if genSend { - g.P("func (x *", streamType, ") Send(m *", outType, ") error {") - g.P("return x.ServerStream.SendMsg(m)") - g.P("}") - g.P() - } - if genSendAndClose { - g.P("func (x *", streamType, ") SendAndClose(m *", outType, ") error {") - g.P("return x.ServerStream.SendMsg(m)") - g.P("}") - g.P() - } - if genRecv { - g.P("func (x *", streamType, ") Recv() (*", inType, ", error) {") - g.P("m := new(", inType, ")") - g.P("if err := x.ServerStream.RecvMsg(m); err != nil { return nil, err }") - g.P("return m, nil") - g.P("}") - g.P() - } - return hname } diff --git a/protocol/grpc/server.go b/protocol/grpc/server.go index 2b7b1adddf573e3b84db32a11cfc286ff22a0276..e77e2babd0aba81cdcfe2288eeff69195decf9f4 100644 --- a/protocol/grpc/server.go +++ b/protocol/grpc/server.go @@ -40,6 +40,7 @@ import ( // Server is a gRPC server type Server struct { grpcServer *grpc.Server + bufferSize int } // NewServer creates a new server @@ -57,8 +58,12 @@ type DubboGrpcService interface { ServiceDesc() *grpc.ServiceDesc } +func (s *Server) SetBufferSize(n int) { + s.bufferSize = n +} + // Start gRPC server with @url -func (s *Server) Start(url common.URL) { +func (s *Server) Start(url *common.URL) { var ( addr string err error @@ -72,7 +77,9 @@ func (s *Server) Start(url common.URL) { // if global trace instance was set, then server tracer instance can be get. If not , will return Nooptracer tracer := opentracing.GlobalTracer() server := grpc.NewServer( - grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer))) + grpc.UnaryInterceptor(otgrpc.OpenTracingServerInterceptor(tracer)), + grpc.MaxRecvMsgSize(1024*1024*s.bufferSize), + grpc.MaxSendMsgSize(1024*1024*s.bufferSize)) key := url.GetParam(constant.BEAN_NAME_KEY, "") service := config.GetProviderService(key) diff --git a/protocol/invocation.go b/protocol/invocation.go index 452f619e2dd9a5835141d91d7adfed37bb9f6859..2ecc817cc91e08fb6be10a0348477943d05bb1e3 100644 --- a/protocol/invocation.go +++ b/protocol/invocation.go @@ -25,6 +25,8 @@ import ( type Invocation interface { // MethodName gets invocation method name. MethodName() string + // ParameterTypeNames gets invocation parameter type names. + ParameterTypeNames() []string // ParameterTypes gets invocation parameter types. ParameterTypes() []reflect.Type // ParameterValues gets invocation parameter values. diff --git a/protocol/invocation/rpcinvocation.go b/protocol/invocation/rpcinvocation.go index 4e806324bf7b236d80a932a92898ba117fb1638d..0f879c06515a645e358627a0596fc66ea7e9f17b 100644 --- a/protocol/invocation/rpcinvocation.go +++ b/protocol/invocation/rpcinvocation.go @@ -19,6 +19,7 @@ package invocation import ( "reflect" + "strings" "sync" ) @@ -35,8 +36,11 @@ import ( // todo: is it necessary to separate fields of consumer(provider) from RPCInvocation // nolint type RPCInvocation struct { - methodName string - parameterTypes []reflect.Type + methodName string + // Parameter Type Names. It is used to specify the parameterType + parameterTypeNames []string + parameterTypes []reflect.Type + parameterValues []reflect.Value arguments []interface{} reply interface{} @@ -80,6 +84,11 @@ func (r *RPCInvocation) ParameterTypes() []reflect.Type { return r.parameterTypes } +// ParameterTypeNames gets RPC invocation parameter types of string expression. +func (r *RPCInvocation) ParameterTypeNames() []string { + return r.parameterTypeNames +} + // ParameterValues gets RPC invocation parameter values. func (r *RPCInvocation) ParameterValues() []reflect.Value { return r.parameterValues @@ -189,7 +198,7 @@ func (r *RPCInvocation) SetCallBack(c interface{}) { } func (r *RPCInvocation) ServiceKey() string { - return common.ServiceKey(r.AttachmentsByKey(constant.INTERFACE_KEY, ""), + return common.ServiceKey(strings.TrimPrefix(r.AttachmentsByKey(constant.PATH_KEY, r.AttachmentsByKey(constant.INTERFACE_KEY, "")), "/"), r.AttachmentsByKey(constant.GROUP_KEY, ""), r.AttachmentsByKey(constant.VERSION_KEY, "")) } @@ -213,6 +222,18 @@ func WithParameterTypes(parameterTypes []reflect.Type) option { } } +// WithParameterTypeNames creates option with @parameterTypeNames. +func WithParameterTypeNames(parameterTypeNames []string) option { + return func(invo *RPCInvocation) { + if len(parameterTypeNames) == 0 { + return + } + parameterTypeNamesTmp := make([]string, len(parameterTypeNames)) + copy(parameterTypeNamesTmp, parameterTypeNames) + invo.parameterTypeNames = parameterTypeNamesTmp + } +} + // WithParameterValues creates option with @parameterValues func WithParameterValues(parameterValues []reflect.Value) option { return func(invo *RPCInvocation) { diff --git a/protocol/invocation/rpcinvocation_test.go b/protocol/invocation/rpcinvocation_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ef70e2cd5ea2ebc92449146689c212d9a652a465 --- /dev/null +++ b/protocol/invocation/rpcinvocation_test.go @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package invocation + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" +) + +func TestRPCInvocation_ServiceKey(t *testing.T) { + providerURL := "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245" + + sameInfPathConsumerURL := "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + + "side=provider&timeout=3000×tamp=1556509797245" + diffInfPathConsumerURL := "dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.UserProviderFoo&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" + + providerUrl, err := common.NewURL(providerURL) + assert.NoError(t, err) + + // invocation with same interface and path value + sameInfPathConsumerUrl, err := common.NewURL(sameInfPathConsumerURL) + assert.NoError(t, err) + invocation := NewRPCInvocationWithOptions(WithAttachments(map[string]interface{}{ + constant.INTERFACE_KEY: sameInfPathConsumerUrl.GetParam(constant.INTERFACE_KEY, ""), + constant.PATH_KEY: sameInfPathConsumerUrl.Path, + constant.GROUP_KEY: sameInfPathConsumerUrl.GetParam(constant.GROUP_KEY, ""), + constant.VERSION_KEY: sameInfPathConsumerUrl.GetParam(constant.VERSION_KEY, ""), + })) + assert.Equal(t, providerUrl.ServiceKey(), invocation.ServiceKey()) + + // invocation with different interface and path value + diffInfPathConsumerUrl, err := common.NewURL(diffInfPathConsumerURL) + assert.NoError(t, err) + invocation = NewRPCInvocationWithOptions(WithAttachments(map[string]interface{}{ + constant.INTERFACE_KEY: diffInfPathConsumerUrl.GetParam(constant.INTERFACE_KEY, ""), + constant.PATH_KEY: diffInfPathConsumerUrl.Path, + constant.GROUP_KEY: diffInfPathConsumerUrl.GetParam(constant.GROUP_KEY, ""), + constant.VERSION_KEY: diffInfPathConsumerUrl.GetParam(constant.VERSION_KEY, ""), + })) + assert.Equal(t, providerUrl.ServiceKey(), invocation.ServiceKey()) +} diff --git a/protocol/invoker.go b/protocol/invoker.go index 3ca370479cbe2255f26628b855b11b07396f1b6d..e799991686a954a3f8d84ee38b58c2be35c50789 100644 --- a/protocol/invoker.go +++ b/protocol/invoker.go @@ -21,12 +21,26 @@ import ( "context" ) +import ( + perrors "github.com/pkg/errors" + uatomic "go.uber.org/atomic" +) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" ) -// Invoker ... +var ( + // ErrClientClosed means client has clossed. + ErrClientClosed = perrors.New("remoting client has closed") + // ErrNoReply + ErrNoReply = perrors.New("request need @response") + // ErrDestroyedInvoker + ErrDestroyedInvoker = perrors.New("request Destroyed invoker") +) + +// Invoker the service invocation interface for the consumer //go:generate mockgen -source invoker.go -destination mock/mock_invoker.go -self_package github.com/apache/dubbo-go/protocol/mock --package mock Invoker // Extension - Invoker type Invoker interface { @@ -41,33 +55,35 @@ type Invoker interface { // BaseInvoker provides default invoker implement type BaseInvoker struct { - url common.URL - available bool - destroyed bool + url *common.URL + available uatomic.Bool + destroyed uatomic.Bool } // NewBaseInvoker creates a new BaseInvoker -func NewBaseInvoker(url common.URL) *BaseInvoker { - return &BaseInvoker{ - url: url, - available: true, - destroyed: false, +func NewBaseInvoker(url *common.URL) *BaseInvoker { + ivk := &BaseInvoker{ + url: url, } + ivk.available.Store(true) + ivk.destroyed.Store(false) + + return ivk } // GetUrl gets base invoker URL -func (bi *BaseInvoker) GetUrl() common.URL { +func (bi *BaseInvoker) GetUrl() *common.URL { return bi.url } // IsAvailable gets available flag func (bi *BaseInvoker) IsAvailable() bool { - return bi.available + return bi.available.Load() } // IsDestroyed gets destroyed flag func (bi *BaseInvoker) IsDestroyed() bool { - return bi.destroyed + return bi.destroyed.Load() } // Invoke provides default invoker implement @@ -77,7 +93,7 @@ func (bi *BaseInvoker) Invoke(context context.Context, invocation Invocation) Re // Destroy changes available and destroyed flag func (bi *BaseInvoker) Destroy() { - logger.Infof("Destroy invoker: %s", bi.GetUrl().String()) - bi.destroyed = true - bi.available = false + logger.Infof("Destroy invoker: %s", bi.GetUrl()) + bi.destroyed.Store(true) + bi.available.Store(false) } diff --git a/protocol/invoker_test.go b/protocol/invoker_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c2a587dc99a1be883de7431feb7e25d552ebce2b --- /dev/null +++ b/protocol/invoker_test.go @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package protocol + +import ( + "testing" +) + +import ( + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" +) + +func TestBaseInvoker(t *testing.T) { + url, err := common.NewURL("dubbo://localhost:9090") + assert.Nil(t, err) + + ivk := NewBaseInvoker(url) + assert.NotNil(t, ivk.GetUrl()) + assert.True(t, ivk.IsAvailable()) + assert.False(t, ivk.IsDestroyed()) + + ivk.Destroy() + assert.False(t, ivk.IsAvailable()) + assert.True(t, ivk.IsDestroyed()) +} diff --git a/protocol/jsonrpc/http.go b/protocol/jsonrpc/http.go index 2a2ddfeeeb52b865e96ccff69d2b39d8a671ed41..11051df8ac05010e9e07fe14d44768dda9342201 100644 --- a/protocol/jsonrpc/http.go +++ b/protocol/jsonrpc/http.go @@ -49,14 +49,14 @@ import ( // Request is HTTP protocol request type Request struct { - ID int64 - group string - protocol string - version string - service string - method string - args interface{} - contentType string + ID int64 + group string + protocol string + version string + service string + method string + args interface{} + //contentType string } // //////////////////////////////////////////// @@ -101,7 +101,7 @@ func NewHTTPClient(opt *HTTPOptions) *HTTPClient { } // NewRequest creates a new HTTP request with @service ,@method and @arguments. -func (c *HTTPClient) NewRequest(service common.URL, method string, args interface{}) *Request { +func (c *HTTPClient) NewRequest(service *common.URL, method string, args interface{}) *Request { return &Request{ ID: atomic.AddInt64(&c.ID, 1), @@ -115,7 +115,7 @@ func (c *HTTPClient) NewRequest(service common.URL, method string, args interfac } // Call makes a HTTP call with @ctx , @service ,@req and @rsp -func (c *HTTPClient) Call(ctx context.Context, service common.URL, req *Request, rsp interface{}) error { +func (c *HTTPClient) Call(ctx context.Context, service *common.URL, req *Request, rsp interface{}) error { // header httpHeader := http.Header{} httpHeader.Set("Content-Type", "application/json") @@ -181,15 +181,17 @@ func (c *HTTPClient) Do(addr, path string, httpHeader http.Header, body []byte) return nil, perrors.WithStack(err) } defer tcpConn.Close() - setNetConnTimeout := func(conn net.Conn, timeout time.Duration) { + setNetConnTimeout := func(conn net.Conn, timeout time.Duration) error { t := time.Time{} if timeout > time.Duration(0) { t = time.Now().Add(timeout) } - conn.SetDeadline(t) + return conn.SetDeadline(t) + } + if err = setNetConnTimeout(tcpConn, c.options.HTTPTimeout); err != nil { + return nil, err } - setNetConnTimeout(tcpConn, c.options.HTTPTimeout) if _, err = reqBuf.WriteTo(tcpConn); err != nil { return nil, perrors.WithStack(err) diff --git a/protocol/jsonrpc/http_test.go b/protocol/jsonrpc/http_test.go index 4a9645e828a3b092c938c62091fb400b05605b67..5ef40649b1fcb209f29d9bbe871e47b1fcda8a59 100644 --- a/protocol/jsonrpc/http_test.go +++ b/protocol/jsonrpc/http_test.go @@ -44,12 +44,12 @@ type ( } UserProvider struct { - user map[string]User + //user map[string]User } ) const ( - mockJsonCommonUrl = "jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + + mockJsonCommonUrl = "jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&" + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + @@ -58,7 +58,7 @@ const ( func TestHTTPClientCall(t *testing.T) { - methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", &UserProvider{}) + methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", "", "", &UserProvider{}) assert.NoError(t, err) assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods) diff --git a/protocol/jsonrpc/json.go b/protocol/jsonrpc/json.go index 506c4c953b1b1113b43669171efdeeaeb6fca10d..81ca512271baa19a103b42c059ba63fd14513377 100644 --- a/protocol/jsonrpc/json.go +++ b/protocol/jsonrpc/json.go @@ -48,13 +48,11 @@ type CodecData struct { const ( // Errors defined in the JSON-RPC spec. See // http://www.jsonrpc.org/specification#error_object. - CodeParseError = -32700 - CodeInvalidRequest = -32600 - CodeMethodNotFound = -32601 - CodeInvalidParams = -32602 - CodeInternalError = -32603 - codeServerErrorStart = -32099 - codeServerErrorEnd = -32000 + CodeParseError = -32700 + CodeInvalidRequest = -32600 + CodeMethodNotFound = -32601 + CodeInvalidParams = -32602 + CodeInternalError = -32603 ) // Error response Error diff --git a/protocol/jsonrpc/jsonrpc_exporter.go b/protocol/jsonrpc/jsonrpc_exporter.go index 6f75f6aeae9fb1a8d75ded5f558e0ddae84686a0..6b91d266a704d928bf1a308a79044055c9a64328 100644 --- a/protocol/jsonrpc/jsonrpc_exporter.go +++ b/protocol/jsonrpc/jsonrpc_exporter.go @@ -42,10 +42,9 @@ func NewJsonrpcExporter(key string, invoker protocol.Invoker, exporterMap *sync. // Unexport exported JSON RPC service. func (je *JsonrpcExporter) Unexport() { - serviceId := je.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := je.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") je.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, JSONRPC, je.GetInvoker().GetUrl().ServiceKey()) if err != nil { logger.Errorf("[JsonrpcExporter.Unexport] error: %v", err) } diff --git a/protocol/jsonrpc/jsonrpc_invoker.go b/protocol/jsonrpc/jsonrpc_invoker.go index d84b980216ade6e569e68af31fc90e1ea16b3056..357443f5d4efbe5159fb6c0e09d3dab51266dc73 100644 --- a/protocol/jsonrpc/jsonrpc_invoker.go +++ b/protocol/jsonrpc/jsonrpc_invoker.go @@ -36,7 +36,7 @@ type JsonrpcInvoker struct { } // NewJsonrpcInvoker creates JSON RPC invoker with @url and @client -func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { +func NewJsonrpcInvoker(url *common.URL, client *HTTPClient) *JsonrpcInvoker { return &JsonrpcInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), client: client, @@ -45,10 +45,7 @@ func NewJsonrpcInvoker(url common.URL, client *HTTPClient) *JsonrpcInvoker { // Invoke the JSON RPC invocation and return result. func (ji *JsonrpcInvoker) Invoke(ctx context.Context, invocation protocol.Invocation) protocol.Result { - - var ( - result protocol.RPCResult - ) + var result protocol.RPCResult inv := invocation.(*invocation_impl.RPCInvocation) url := ji.GetUrl() diff --git a/protocol/jsonrpc/jsonrpc_invoker_test.go b/protocol/jsonrpc/jsonrpc_invoker_test.go index d7124ca07c6ba6d79dc72e7fb6bd98cd4b3a97b2..12a57052eba6e3a2af1b7cfb15e6ab368a7da286 100644 --- a/protocol/jsonrpc/jsonrpc_invoker_test.go +++ b/protocol/jsonrpc/jsonrpc_invoker_test.go @@ -36,13 +36,13 @@ import ( func TestJsonrpcInvokerInvoke(t *testing.T) { - methods, err := common.ServiceMap.Register("UserProvider", "jsonrpc", &UserProvider{}) + methods, err := common.ServiceMap.Register("com.ikurento.user.UserProvider", "jsonrpc", "", "", &UserProvider{}) assert.NoError(t, err) assert.Equal(t, "GetUser,GetUser0,GetUser1,GetUser2,GetUser3,GetUser4", methods) // Export proto := GetProtocol() - url, err := common.NewURL("jsonrpc://127.0.0.1:20001/UserProvider?anyhost=true&" + + url, err := common.NewURL("jsonrpc://127.0.0.1:20001/com.ikurento.user.UserProvider?anyhost=true&" + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + diff --git a/protocol/jsonrpc/jsonrpc_protocol.go b/protocol/jsonrpc/jsonrpc_protocol.go index 1778d99d4078058dc43a630b89b9d256350da0f3..643bcde8cb41a3cb94e97b2d21df26894c574b28 100644 --- a/protocol/jsonrpc/jsonrpc_protocol.go +++ b/protocol/jsonrpc/jsonrpc_protocol.go @@ -75,7 +75,7 @@ func (jp *JsonrpcProtocol) Export(invoker protocol.Invoker) protocol.Exporter { } // Refer a remote JSON PRC service from registry -func (jp *JsonrpcProtocol) Refer(url common.URL) protocol.Invoker { +func (jp *JsonrpcProtocol) Refer(url *common.URL) protocol.Invoker { //default requestTimeout var requestTimeout = config.GetConsumerConfig().RequestTimeout @@ -106,7 +106,7 @@ func (jp *JsonrpcProtocol) Destroy() { } } -func (jp *JsonrpcProtocol) openServer(url common.URL) { +func (jp *JsonrpcProtocol) openServer(url *common.URL) { _, ok := jp.serverMap[url.Location] if !ok { _, loadOk := jp.ExporterMap().Load(strings.TrimPrefix(url.Path, "/")) diff --git a/protocol/jsonrpc/server.go b/protocol/jsonrpc/server.go index 9755a481fdba325887a1d16731cbbcb8e3943d8a..76901bff6e62748e103d8691f75aedcf68dd202a 100644 --- a/protocol/jsonrpc/server.go +++ b/protocol/jsonrpc/server.go @@ -92,7 +92,9 @@ func (s *Server) handlePkg(conn net.Conn) { t = time.Now().Add(timeout) } - conn.SetDeadline(t) + if err := conn.SetDeadline(t); err != nil { + logger.Error("connection.SetDeadline(t:%v) = error:%v", t, err) + } } sendErrorResp := func(header http.Header, body []byte) error { @@ -229,7 +231,7 @@ func accept(listener net.Listener, fn func(net.Conn)) error { } // Start JSON RPC server then ready for accept request. -func (s *Server) Start(url common.URL) { +func (s *Server) Start(url *common.URL) { listener, err := net.Listen("tcp", url.Location) if err != nil { logger.Errorf("jsonrpc server [%s] start failed: %v", url.Path, err) @@ -239,7 +241,9 @@ func (s *Server) Start(url common.URL) { s.wg.Add(1) go func() { - accept(listener, func(conn net.Conn) { s.handlePkg(conn) }) + if err := accept(listener, func(conn net.Conn) { s.handlePkg(conn) }); err != nil { + logger.Error("accept() = error:%v", err) + } s.wg.Done() }() diff --git a/protocol/mock/mock_invoker.go b/protocol/mock/mock_invoker.go index 0c88b47e36122dc1a9bc9345d7e93f62cd76f13b..8a0973bd128a74677e8549793b6792e67b26b443 100644 --- a/protocol/mock/mock_invoker.go +++ b/protocol/mock/mock_invoker.go @@ -59,9 +59,9 @@ func (m *MockInvoker) EXPECT() *MockInvokerMockRecorder { } // GetUrl mocks base method -func (m *MockInvoker) GetUrl() common.URL { +func (m *MockInvoker) GetUrl() *common.URL { ret := m.ctrl.Call(m, "GetUrl") - ret0, _ := ret[0].(common.URL) + ret0, _ := ret[0].(*common.URL) return ret0 } diff --git a/protocol/protocol.go b/protocol/protocol.go index 6bed5ec4c2be82edda7a642eb342381c53387460..d03e70f1ea4acbd1277d4b968be9109fdde187a9 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -32,7 +32,7 @@ type Protocol interface { // Export service for remote invocation Export(invoker Invoker) Exporter // Refer a remote service - Refer(url common.URL) Invoker + Refer(url *common.URL) Invoker // Destroy will destroy all invoker and exporter, so it only is called once. Destroy() } @@ -89,7 +89,7 @@ func (bp *BaseProtocol) Export(invoker Invoker) Exporter { } // Refer is default refer implement. -func (bp *BaseProtocol) Refer(url common.URL) Invoker { +func (bp *BaseProtocol) Refer(url *common.URL) Invoker { return NewBaseInvoker(url) } diff --git a/protocol/protocolwrapper/mock_protocol_filter.go b/protocol/protocolwrapper/mock_protocol_filter.go index 2e9ffed06f03d196c91f6679df21538612bac405..d1baba813416ff688c5f9ccccf05e276c55c2bba 100644 --- a/protocol/protocolwrapper/mock_protocol_filter.go +++ b/protocol/protocolwrapper/mock_protocol_filter.go @@ -39,11 +39,10 @@ func (pfw *mockProtocolFilter) Export(invoker protocol.Invoker) protocol.Exporte } // Refer a mock remote service -func (pfw *mockProtocolFilter) Refer(url common.URL) protocol.Invoker { +func (pfw *mockProtocolFilter) Refer(url *common.URL) protocol.Invoker { return protocol.NewBaseInvoker(url) } // Destroy will do nothing func (pfw *mockProtocolFilter) Destroy() { - return } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper.go b/protocol/protocolwrapper/protocol_filter_wrapper.go index 4b2702b99f9793d4e567de65bd9555fb20381b1d..448bd6f5a90ae0fe3732845d9ce9e2fa238e8e74 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper.go @@ -55,11 +55,15 @@ func (pfw *ProtocolFilterWrapper) Export(invoker protocol.Invoker) protocol.Expo } // Refer a remote service -func (pfw *ProtocolFilterWrapper) Refer(url common.URL) protocol.Invoker { +func (pfw *ProtocolFilterWrapper) Refer(url *common.URL) protocol.Invoker { if pfw.protocol == nil { pfw.protocol = extension.GetProtocol(url.Protocol) } - return buildInvokerChain(pfw.protocol.Refer(url), constant.REFERENCE_FILTER_KEY) + invoker := pfw.protocol.Refer(url) + if invoker == nil { + return nil + } + return buildInvokerChain(invoker, constant.REFERENCE_FILTER_KEY) } // Destroy will destroy all invoker and exporter. @@ -77,7 +81,7 @@ func buildInvokerChain(invoker protocol.Invoker, key string) protocol.Invoker { // The order of filters is from left to right, so loading from right to left next := invoker for i := len(filterNames) - 1; i >= 0; i-- { - flt := extension.GetFilter(filterNames[i]) + flt := extension.GetFilter(strings.TrimSpace(filterNames[i])) fi := &FilterInvoker{next: next, invoker: invoker, filter: flt} next = fi } @@ -101,7 +105,7 @@ type FilterInvoker struct { } // GetUrl is used to get url from FilterInvoker -func (fi *FilterInvoker) GetUrl() common.URL { +func (fi *FilterInvoker) GetUrl() *common.URL { return fi.invoker.GetUrl() } diff --git a/protocol/protocolwrapper/protocol_filter_wrapper_test.go b/protocol/protocolwrapper/protocol_filter_wrapper_test.go index b03ea7b9b66d926aff8851f88e1bd7434b254903..b37d066f03c8084d2d529217567c1ede940a5491 100644 --- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go +++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go @@ -42,8 +42,8 @@ func TestProtocolFilterWrapperExport(t *testing.T) { u := common.NewURLWithOptions( common.WithParams(url.Values{}), - common.WithParamsValue(constant.SERVICE_FILTER_KEY, "echo")) - exporter := filtProto.Export(protocol.NewBaseInvoker(*u)) + common.WithParamsValue(constant.SERVICE_FILTER_KEY, "echo ")) + exporter := filtProto.Export(protocol.NewBaseInvoker(u)) _, ok := exporter.GetInvoker().(*FilterInvoker) assert.True(t, ok) } @@ -54,8 +54,8 @@ func TestProtocolFilterWrapperRefer(t *testing.T) { u := common.NewURLWithOptions( common.WithParams(url.Values{}), - common.WithParamsValue(constant.REFERENCE_FILTER_KEY, "echo")) - invoker := filtProto.Refer(*u) + common.WithParamsValue(constant.REFERENCE_FILTER_KEY, " echo")) + invoker := filtProto.Refer(u) _, ok := invoker.(*FilterInvoker) assert.True(t, ok) } diff --git a/protocol/rest/config/reader/rest_config_reader.go b/protocol/rest/config/reader/rest_config_reader.go index 873af9924b5644158024b22c24aa9eebbf1bf187..2338790ea9926b1ddbf44fc476f092156d8fe2fa 100644 --- a/protocol/rest/config/reader/rest_config_reader.go +++ b/protocol/rest/config/reader/rest_config_reader.go @@ -62,7 +62,7 @@ func (cr *RestConfigReader) ReadConsumerConfig(reader *bytes.Buffer) error { for key, rc := range restConsumerConfig.RestServiceConfigsMap { rc.Client = getNotEmptyStr(rc.Client, restConsumerConfig.Client, constant.DEFAULT_REST_CLIENT) rc.RestMethodConfigsMap = initMethodConfigMap(rc, restConsumerConfig.Consumes, restConsumerConfig.Produces) - restConsumerServiceConfigMap[strings.TrimPrefix(key, "/")] = rc + restConsumerServiceConfigMap[key] = rc } config.SetRestConsumerServiceConfigMap(restConsumerServiceConfigMap) return nil @@ -79,7 +79,7 @@ func (cr *RestConfigReader) ReadProviderConfig(reader *bytes.Buffer) error { for key, rc := range restProviderConfig.RestServiceConfigsMap { rc.Server = getNotEmptyStr(rc.Server, restProviderConfig.Server, constant.DEFAULT_REST_SERVER) rc.RestMethodConfigsMap = initMethodConfigMap(rc, restProviderConfig.Consumes, restProviderConfig.Produces) - restProviderServiceConfigMap[strings.TrimPrefix(key, "/")] = rc + restProviderServiceConfigMap[key] = rc } config.SetRestProviderServiceConfigMap(restProviderServiceConfigMap) return nil diff --git a/protocol/rest/config/rest_config.go b/protocol/rest/config/rest_config.go index 4732dd8e4eae3ba874fdd8ed95380e6ace3ab66d..27c67db12d1477470550a2f75779b25113781a04 100644 --- a/protocol/rest/config/rest_config.go +++ b/protocol/rest/config/rest_config.go @@ -123,13 +123,13 @@ func (c *RestMethodConfig) UnmarshalYAML(unmarshal func(interface{}) error) erro } // nolint -func GetRestConsumerServiceConfig(path string) *RestServiceConfig { - return restConsumerServiceConfigMap[path] +func GetRestConsumerServiceConfig(id string) *RestServiceConfig { + return restConsumerServiceConfigMap[id] } // nolint -func GetRestProviderServiceConfig(path string) *RestServiceConfig { - return restProviderServiceConfigMap[path] +func GetRestProviderServiceConfig(id string) *RestServiceConfig { + return restProviderServiceConfigMap[id] } // nolint diff --git a/protocol/rest/rest_exporter.go b/protocol/rest/rest_exporter.go index 359506a842437ba15c121c29b50478ae775a8ed7..7a49a2063559c2f4e9b4975bc86ea708abbfb026 100644 --- a/protocol/rest/rest_exporter.go +++ b/protocol/rest/rest_exporter.go @@ -42,10 +42,9 @@ func NewRestExporter(key string, invoker protocol.Invoker, exporterMap *sync.Map // Unexport unexport the RestExporter func (re *RestExporter) Unexport() { - serviceId := re.GetInvoker().GetUrl().GetParam(constant.BEAN_NAME_KEY, "") interfaceName := re.GetInvoker().GetUrl().GetParam(constant.INTERFACE_KEY, "") re.BaseExporter.Unexport() - err := common.ServiceMap.UnRegister(interfaceName, REST, serviceId) + err := common.ServiceMap.UnRegister(interfaceName, REST, re.GetInvoker().GetUrl().ServiceKey()) if err != nil { logger.Errorf("[RestExporter.Unexport] error: %v", err) } diff --git a/protocol/rest/rest_invoker.go b/protocol/rest/rest_invoker.go index 691beeda4085f316075fe55b9328bc86d6328187..898890e41732d3c874c47cf3b88bba2e201d01d0 100644 --- a/protocol/rest/rest_invoker.go +++ b/protocol/rest/rest_invoker.go @@ -43,7 +43,7 @@ type RestInvoker struct { } // NewRestInvoker returns a RestInvoker -func NewRestInvoker(url common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker { +func NewRestInvoker(url *common.URL, client *client.RestClient, restMethodConfig map[string]*config.RestMethodConfig) *RestInvoker { return &RestInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), client: *client, diff --git a/protocol/rest/rest_invoker_test.go b/protocol/rest/rest_invoker_test.go index 9df97a211e9d90daa3206b94926ceeac42df4606..b6bc9806534979976d8c52cd66142a71f408fb06 100644 --- a/protocol/rest/rest_invoker_test.go +++ b/protocol/rest/rest_invoker_test.go @@ -44,7 +44,7 @@ const ( "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=192.168.56.1&methods=GetUser%2C&" + "module=dubbogo+user-info+server&org=ikurento.com&owner=ZX&pid=1447&revision=0.0.1&" + - "side=provider&timeout=3000×tamp=1556509797245" + "side=provider&timeout=3000×tamp=1556509797245&bean.name=com.ikurento.user.UserProvider" ) func TestRestInvokerInvoke(t *testing.T) { @@ -65,7 +65,7 @@ func TestRestInvokerInvoke(t *testing.T) { url, err := common.NewURL(mockRestCommonUrl) assert.NoError(t, err) - _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) + _, err = common.ServiceMap.Register(url.Service(), url.Protocol, "", "", &UserProvider{}) assert.NoError(t, err) con := config.ProviderConfig{} config.SetProviderConfig(con) @@ -210,6 +210,6 @@ func TestRestInvokerInvoke(t *testing.T) { assert.Error(t, res.Error(), "test error") assert.Equal(t, filterNum, 12) - err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider") + err = common.ServiceMap.UnRegister(url.Service(), url.Protocol, url.ServiceKey()) assert.NoError(t, err) } diff --git a/protocol/rest/rest_protocol.go b/protocol/rest/rest_protocol.go index 0cd26c240a7eb1b83a04ca2f57c332bbda994967..d19bd002fc4974c57f50a8595ee25debceb27921 100644 --- a/protocol/rest/rest_protocol.go +++ b/protocol/rest/rest_protocol.go @@ -18,7 +18,6 @@ package rest import ( - "strings" "sync" "time" ) @@ -72,7 +71,8 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter { url := invoker.GetUrl() serviceKey := url.ServiceKey() exporter := NewRestExporter(serviceKey, invoker, rp.ExporterMap()) - restServiceConfig := rest_config.GetRestProviderServiceConfig(strings.TrimPrefix(url.Path, "/")) + id := url.GetParam(constant.BEAN_NAME_KEY, "") + restServiceConfig := rest_config.GetRestProviderServiceConfig(id) if restServiceConfig == nil { logger.Errorf("%s service doesn't has provider config", url.Path) return nil @@ -86,7 +86,7 @@ func (rp *RestProtocol) Export(invoker protocol.Invoker) protocol.Exporter { } // Refer create rest service reference -func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker { +func (rp *RestProtocol) Refer(url *common.URL) protocol.Invoker { // create rest_invoker var requestTimeout = config.GetConsumerConfig().RequestTimeout requestTimeoutStr := url.GetParam(constant.TIMEOUT_KEY, config.GetConsumerConfig().Request_Timeout) @@ -94,7 +94,8 @@ func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker { if t, err := time.ParseDuration(requestTimeoutStr); err == nil { requestTimeout = t } - restServiceConfig := rest_config.GetRestConsumerServiceConfig(strings.TrimPrefix(url.Path, "/")) + id := url.GetParam(constant.BEAN_NAME_KEY, "") + restServiceConfig := rest_config.GetRestConsumerServiceConfig(id) if restServiceConfig == nil { logger.Errorf("%s service doesn't has consumer config", url.Path) return nil @@ -107,7 +108,7 @@ func (rp *RestProtocol) Refer(url common.URL) protocol.Invoker { } // nolint -func (rp *RestProtocol) getServer(url common.URL, serverType string) server.RestServer { +func (rp *RestProtocol) getServer(url *common.URL, serverType string) server.RestServer { restServer, ok := rp.serverMap[url.Location] if ok { return restServer @@ -149,8 +150,8 @@ func (rp *RestProtocol) getClient(restOptions client.RestOptions, clientType str func (rp *RestProtocol) Destroy() { // destroy rest_server rp.BaseProtocol.Destroy() - for key, server := range rp.serverMap { - server.Destroy() + for key, tmpServer := range rp.serverMap { + tmpServer.Destroy() delete(rp.serverMap, key) } for key := range rp.clientMap { diff --git a/protocol/rest/rest_protocol_test.go b/protocol/rest/rest_protocol_test.go index 9ff4e7df7fe41d7fd028bf476cc2192cf7cefcca..580fc61fd6cfda731184eed6f785b80abc9fccfe 100644 --- a/protocol/rest/rest_protocol_test.go +++ b/protocol/rest/rest_protocol_test.go @@ -72,7 +72,7 @@ func TestRestProtocolExport(t *testing.T) { proto := GetRestProtocol() url, err := common.NewURL(mockRestCommonUrl) assert.NoError(t, err) - _, err = common.ServiceMap.Register("UserProvider", url.Protocol, &UserProvider{}) + _, err = common.ServiceMap.Register(url.Service(), url.Protocol, "", "", &UserProvider{}) assert.NoError(t, err) con := config.ProviderConfig{} config.SetProviderConfig(con) @@ -120,8 +120,6 @@ func TestRestProtocolExport(t *testing.T) { proto.Destroy() _, ok = proto.(*RestProtocol).serverMap[url.Location] assert.False(t, ok) - err = common.ServiceMap.UnRegister("UserProvider", url.Protocol, "com.ikurento.user.UserProvider") - assert.NoError(t, err) } type UserProvider struct { diff --git a/protocol/rest/server/rest_server.go b/protocol/rest/server/rest_server.go index d9542bb8760cd2ddb7b839ebc6761dab75ae9c30..b0bf0e07bdd2c1351b1d9c1f55187515e3cc852c 100644 --- a/protocol/rest/server/rest_server.go +++ b/protocol/rest/server/rest_server.go @@ -23,7 +23,6 @@ import ( "net/http" "reflect" "strconv" - "strings" ) import ( @@ -43,7 +42,7 @@ const parseParameterErrorStr = "An error occurred while parsing parameters on th // RestServer user can implement this server interface type RestServer interface { // Start rest server - Start(url common.URL) + Start(url *common.URL) // Deploy a http api Deploy(restMethodConfig *rest_config.RestMethodConfig, routeFunc func(request RestServerRequest, response RestServerResponse)) // UnDeploy a http api @@ -90,7 +89,7 @@ func GetRouteFunc(invoker protocol.Invoker, methodConfig *rest_config.RestMethod err error args []interface{} ) - svc := common.ServiceMap.GetService(invoker.GetUrl().Protocol, strings.TrimPrefix(invoker.GetUrl().Path, "/")) + svc := common.ServiceMap.GetServiceByServiceKey(invoker.GetUrl().Protocol, invoker.GetUrl().ServiceKey()) // get method method := svc.Method()[methodConfig.MethodName] argsTypes := method.ArgsType() @@ -258,6 +257,7 @@ func assembleArgsFromQueryParams(methodConfig *rest_config.RestMethodConfig, arg kind := t.Kind() if kind == reflect.Ptr { t = t.Elem() + kind = t.Kind() } if kind == reflect.Slice { param = req.QueryParameters(v) @@ -298,6 +298,7 @@ func assembleArgsFromPathParams(methodConfig *rest_config.RestMethodConfig, args kind := t.Kind() if kind == reflect.Ptr { t = t.Elem() + kind = t.Kind() } if kind == reflect.Int { param, err = strconv.Atoi(req.PathParameter(v)) diff --git a/protocol/rest/server/server_impl/go_restful_server.go b/protocol/rest/server/server_impl/go_restful_server.go index 6fb9ee8daa7383580b9144ea25954f8ead974dcc..4481f44912d8f1caf6f5250e9c13e90e99537cff 100644 --- a/protocol/rest/server/server_impl/go_restful_server.go +++ b/protocol/rest/server/server_impl/go_restful_server.go @@ -59,7 +59,7 @@ func NewGoRestfulServer() server.RestServer { // Start go-restful server // It will add all go-restful filters -func (grs *GoRestfulServer) Start(url common.URL) { +func (grs *GoRestfulServer) Start(url *common.URL) { container := restful.NewContainer() for _, filter := range filterSlice { container.Filter(filter) @@ -99,8 +99,6 @@ func (grs *GoRestfulServer) Deploy(restMethodConfig *config.RestMethodConfig, ro // Delete a http api in go-restful server func (grs *GoRestfulServer) UnDeploy(restMethodConfig *config.RestMethodConfig) { - ws := new(restful.WebService) - ws.Path(restMethodConfig.Path) err := grs.ws.RemoveRoute(restMethodConfig.Path, restMethodConfig.MethodType) if err != nil { logger.Warnf("[Go restful] Remove web service error:%v", err) diff --git a/protocol/rpc_status.go b/protocol/rpc_status.go index 978534ea8b98ddd76f68619155ff90fe5e031ac1..8fc5ddd76e84a7de87ba8a9f09aa2adfa09e434b 100644 --- a/protocol/rpc_status.go +++ b/protocol/rpc_status.go @@ -23,15 +23,28 @@ import ( "time" ) +import ( + uberAtomic "go.uber.org/atomic" +) + import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/logger" ) var ( - methodStatistics sync.Map // url -> { methodName : RPCStatus} - serviceStatistic sync.Map // url -> RPCStatus + methodStatistics sync.Map // url -> { methodName : RPCStatus} + serviceStatistic sync.Map // url -> RPCStatus + invokerBlackList sync.Map // store unhealthy url blackList + blackListCacheDirty uberAtomic.Bool // store if the cache in chain is not refreshed by blacklist + blackListRefreshing int32 // store if the refresing method is processing ) +func init() { + blackListCacheDirty.Store(false) +} + // RPCStatus is URL statistics. type RPCStatus struct { active int32 @@ -97,7 +110,7 @@ func (rpc *RPCStatus) GetSuccessiveRequestFailureCount() int32 { } // GetURLStatus get URL RPC status. -func GetURLStatus(url common.URL) *RPCStatus { +func GetURLStatus(url *common.URL) *RPCStatus { rpcStatus, found := serviceStatistic.Load(url.Key()) if !found { rpcStatus, _ = serviceStatistic.LoadOrStore(url.Key(), &RPCStatus{}) @@ -106,7 +119,7 @@ func GetURLStatus(url common.URL) *RPCStatus { } // GetMethodStatus get method RPC status. -func GetMethodStatus(url common.URL, methodName string) *RPCStatus { +func GetMethodStatus(url *common.URL, methodName string) *RPCStatus { identifier := url.Key() methodMap, found := methodStatistics.Load(identifier) if !found { @@ -124,13 +137,13 @@ func GetMethodStatus(url common.URL, methodName string) *RPCStatus { } // BeginCount gets begin count. -func BeginCount(url common.URL, methodName string) { +func BeginCount(url *common.URL, methodName string) { beginCount0(GetURLStatus(url)) beginCount0(GetMethodStatus(url, methodName)) } // EndCount gets end count. -func EndCount(url common.URL, methodName string, elapsed int64, succeeded bool) { +func EndCount(url *common.URL, methodName string, elapsed int64, succeeded bool) { endCount0(GetURLStatus(url), elapsed, succeeded) endCount0(GetMethodStatus(url, methodName), elapsed, succeeded) } @@ -181,4 +194,82 @@ func CleanAllStatus() { return true } serviceStatistic.Range(delete2) + delete3 := func(key, _ interface{}) bool { + invokerBlackList.Delete(key) + return true + } + invokerBlackList.Range(delete3) +} + +// GetInvokerHealthyStatus get invoker's conn healthy status +func GetInvokerHealthyStatus(invoker Invoker) bool { + _, found := invokerBlackList.Load(invoker.GetUrl().Key()) + return !found +} + +// SetInvokerUnhealthyStatus add target invoker to black list +func SetInvokerUnhealthyStatus(invoker Invoker) { + invokerBlackList.Store(invoker.GetUrl().Key(), invoker) + logger.Info("Add invoker ip = ", invoker.GetUrl().Location, " to black list") + blackListCacheDirty.Store(true) +} + +// RemoveInvokerUnhealthyStatus remove unhealthy status of target invoker from blacklist +func RemoveInvokerUnhealthyStatus(invoker Invoker) { + invokerBlackList.Delete(invoker.GetUrl().Key()) + logger.Info("Remove invoker ip = ", invoker.GetUrl().Location, " from black list") + blackListCacheDirty.Store(true) +} + +// GetBlackListInvokers get at most size of blockSize invokers from black list +func GetBlackListInvokers(blockSize int) []Invoker { + resultIvks := make([]Invoker, 0, 16) + invokerBlackList.Range(func(k, v interface{}) bool { + resultIvks = append(resultIvks, v.(Invoker)) + return true + }) + if blockSize > len(resultIvks) { + return resultIvks + } + return resultIvks[:blockSize] +} + +// RemoveUrlKeyUnhealthyStatus called when event of provider unregister, delete from black list +func RemoveUrlKeyUnhealthyStatus(key string) { + invokerBlackList.Delete(key) + logger.Info("Remove invoker key = ", key, " from black list") + blackListCacheDirty.Store(true) +} + +func GetAndRefreshState() bool { + state := blackListCacheDirty.Load() + blackListCacheDirty.Store(false) + return state +} + +// TryRefreshBlackList start 3 gr to check at most block=16 invokers in black list +// if target invoker is available, then remove it from black list +func TryRefreshBlackList() { + if atomic.CompareAndSwapInt32(&blackListRefreshing, 0, 1) { + wg := sync.WaitGroup{} + defer func() { + atomic.CompareAndSwapInt32(&blackListRefreshing, 1, 0) + }() + + ivks := GetBlackListInvokers(constant.DEFAULT_BLACK_LIST_RECOVER_BLOCK) + logger.Debug("blackList len = ", len(ivks)) + + for i := 0; i < 3; i++ { + wg.Add(1) + go func(ivks []Invoker, i int) { + defer wg.Done() + for j, _ := range ivks { + if j%3-i == 0 && ivks[j].(Invoker).IsAvailable() { + RemoveInvokerUnhealthyStatus(ivks[i]) + } + } + }(ivks, i) + } + wg.Wait() + } } diff --git a/protocol/rpc_status_test.go b/protocol/rpc_status_test.go index cc12753cf25007ab6d1010836b203aee22d78ca9..6fd449ce77fada15b32cba936691b51d3c8218ed 100644 --- a/protocol/rpc_status_test.go +++ b/protocol/rpc_status_test.go @@ -79,7 +79,7 @@ func TestGetUrlStatus(t *testing.T) { assert.Equal(t, int32(0), status.total) } -func TestbeginCount0(t *testing.T) { +func TestBeginCount0(t *testing.T) { defer CleanAllStatus() url, _ := common.NewURL(mockCommonDubboUrl) @@ -142,7 +142,7 @@ func TestAll(t *testing.T) { } -func request(url common.URL, method string, elapsed int64, active, succeeded bool) { +func request(url *common.URL, method string, elapsed int64, active, succeeded bool) { BeginCount(url, method) if !active { EndCount(url, method, elapsed, succeeded) diff --git a/registry/base_configuration_listener.go b/registry/base_configuration_listener.go index 3b36510306680486ba9d269472450df8867b61b1..31e859eec279f08f4fd286bf021414bdb297a333 100644 --- a/registry/base_configuration_listener.go +++ b/registry/base_configuration_listener.go @@ -20,6 +20,7 @@ package registry import ( perrors "github.com/pkg/errors" ) + import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/config" @@ -42,7 +43,9 @@ func (bcl *BaseConfigurationListener) Configurators() []config_center.Configurat } // InitWith will init BaseConfigurationListener by @key+@Listener+@f -func (bcl *BaseConfigurationListener) InitWith(key string, listener config_center.ConfigurationListener, f func(url *common.URL) config_center.Configurator) { +func (bcl *BaseConfigurationListener) InitWith(key string, listener config_center.ConfigurationListener, + f func(url *common.URL) config_center.Configurator) { + bcl.dynamicConfiguration = config.GetEnvInstance().GetDynamicConfiguration() if bcl.dynamicConfiguration == nil { //set configurators to empty @@ -51,12 +54,15 @@ func (bcl *BaseConfigurationListener) InitWith(key string, listener config_cente } bcl.defaultConfiguratorFunc = f bcl.dynamicConfiguration.AddListener(key, listener) - if rawConfig, err := bcl.dynamicConfiguration.GetInternalProperty(key, config_center.WithGroup(constant.DUBBO)); err != nil { + if rawConfig, err := bcl.dynamicConfiguration.GetInternalProperty(key, + config_center.WithGroup(constant.DUBBO)); err != nil { //set configurators to empty bcl.configurators = []config_center.Configurator{} return } else if len(rawConfig) > 0 { - bcl.genConfiguratorFromRawRule(rawConfig) + if err := bcl.genConfiguratorFromRawRule(rawConfig); err != nil { + logger.Error("bcl.genConfiguratorFromRawRule(rawConfig:%v) = error:%v", rawConfig, err) + } } } diff --git a/registry/base_registry.go b/registry/base_registry.go index 797ffb2eaebefd691c4273c47b5fdfccd2c66cf9..df8c8a3250ebccb66e0ea797349d1a328dabb860 100644 --- a/registry/base_registry.go +++ b/registry/base_registry.go @@ -18,7 +18,6 @@ package registry import ( - "context" "fmt" "net/url" "os" @@ -29,7 +28,6 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" perrors "github.com/pkg/errors" ) @@ -53,7 +51,7 @@ var ( func init() { processID = fmt.Sprintf("%d", os.Getpid()) - localIP, _ = gxnet.GetLocalIP() + localIP = common.GetLocalIp() } type createPathFunc func(dubboPath string) error @@ -94,14 +92,14 @@ type FacadeBasedRegistry interface { // BaseRegistry is a common logic abstract for registry. It implement Registry interface. type BaseRegistry struct { - context context.Context + //context context.Context facadeBasedRegistry FacadeBasedRegistry *common.URL birth int64 // time of file birth, seconds since Epoch; 0 if unknown wg sync.WaitGroup // wg+done for zk restart done chan struct{} - cltLock sync.RWMutex //ctl lock is a lock for services map - services map[string]common.URL // service name + protocol -> service config, for store the service registered + cltLock sync.RWMutex //ctl lock is a lock for services map + services map[string]*common.URL // service name + protocol -> service config, for store the service registered } // InitBaseRegistry for init some local variables and set BaseRegistry's subclass to it @@ -109,14 +107,14 @@ func (r *BaseRegistry) InitBaseRegistry(url *common.URL, facadeRegistry FacadeBa r.URL = url r.birth = time.Now().UnixNano() r.done = make(chan struct{}) - r.services = make(map[string]common.URL) + r.services = make(map[string]*common.URL) r.facadeBasedRegistry = facadeRegistry return r } // GetUrl for get registry's url -func (r *BaseRegistry) GetUrl() common.URL { - return *r.URL +func (r *BaseRegistry) GetUrl() *common.URL { + return r.URL } // Destroy for graceful down @@ -133,11 +131,18 @@ func (r *BaseRegistry) Destroy() { } // Register implement interface registry to register -func (r *BaseRegistry) Register(conf common.URL) error { +func (r *BaseRegistry) Register(conf *common.URL) error { var ( ok bool err error ) + // if developer define registry port and ip, use it first. + if ipToRegistry := os.Getenv("DUBBO_IP_TO_REGISTRY"); ipToRegistry != "" { + conf.Ip = ipToRegistry + } + if portToRegistry := os.Getenv("DUBBO_PORT_TO_REGISTRY"); portToRegistry != "" { + conf.Port = portToRegistry + } role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) // Check if the service has been registered r.cltLock.Lock() @@ -161,11 +166,11 @@ func (r *BaseRegistry) Register(conf common.URL) error { } // UnRegister implement interface registry to unregister -func (r *BaseRegistry) UnRegister(conf common.URL) error { +func (r *BaseRegistry) UnRegister(conf *common.URL) error { var ( ok bool err error - oldURL common.URL + oldURL *common.URL ) func() { @@ -198,7 +203,7 @@ func (r *BaseRegistry) UnRegister(conf common.URL) error { } // service is for getting service path stored in url -func (r *BaseRegistry) service(c common.URL) string { +func (r *BaseRegistry) service(c *common.URL) string { return url.QueryEscape(c.Service()) } @@ -206,7 +211,7 @@ func (r *BaseRegistry) service(c common.URL) string { func (r *BaseRegistry) RestartCallBack() bool { // copy r.services - services := make([]common.URL, 0, len(r.services)) + services := make([]*common.URL, 0, len(r.services)) for _, confIf := range r.services { services = append(services, confIf) } @@ -231,16 +236,16 @@ func (r *BaseRegistry) RestartCallBack() bool { } // register for register url to registry, include init params -func (r *BaseRegistry) register(c common.URL) error { +func (r *BaseRegistry) register(c *common.URL) error { return r.processURL(c, r.facadeBasedRegistry.DoRegister, r.createPath) } // unregister for unregister url to registry, include init params -func (r *BaseRegistry) unregister(c common.URL) error { +func (r *BaseRegistry) unregister(c *common.URL) error { return r.processURL(c, r.facadeBasedRegistry.DoUnregister, nil) } -func (r *BaseRegistry) processURL(c common.URL, f func(string, string) error, cpf createPathFunc) error { +func (r *BaseRegistry) processURL(c *common.URL, f func(string, string) error, cpf createPathFunc) error { if f == nil { panic(" Must provide a `function(string, string) error` to process URL. ") } @@ -274,6 +279,10 @@ func (r *BaseRegistry) processURL(c common.URL, f func(string, string) error, cp default: return perrors.Errorf("@c{%v} type is not referencer or provider", c) } + if err != nil { + return perrors.WithMessagef(err, "@c{%v} registry fail", c) + } + encodedURL = url.QueryEscape(rawURL) dubboPath = strings.ReplaceAll(dubboPath, "$", "%24") err = f(dubboPath, encodedURL) @@ -292,7 +301,7 @@ func (r *BaseRegistry) createPath(dubboPath string) error { } // providerRegistry for provider role do -func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f createPathFunc) (string, string, error) { +func (r *BaseRegistry) providerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) { var ( dubboPath string rawURL string @@ -314,7 +323,7 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f creat // Dubbo java consumer to start looking for the provider url,because the category does not match, // the provider will not find, causing the consumer can not start, so we use consumers. - if len(c.Methods) == 0 { + if len(c.Methods) != 0 { params.Add(constant.METHODS_KEY, strings.Join(c.Methods, ",")) } logger.Debugf("provider url params:%#v", params) @@ -343,7 +352,7 @@ func (r *BaseRegistry) providerRegistry(c common.URL, params url.Values, f creat } // consumerRegistry for consumer role do -func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values, f createPathFunc) (string, string, error) { +func (r *BaseRegistry) consumerRegistry(c *common.URL, params url.Values, f createPathFunc) (string, string, error) { var ( dubboPath string rawURL string @@ -370,7 +379,8 @@ func (r *BaseRegistry) consumerRegistry(c common.URL, params url.Values, f creat } params.Add("protocol", c.Protocol) - rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, params.Encode()) + s, _ := url.QueryUnescape(params.Encode()) + rawURL = fmt.Sprintf("consumer://%s%s?%s", localIP, c.Path, s) dubboPath = fmt.Sprintf("/dubbo/%s/%s", r.service(c), (common.RoleType(common.CONSUMER)).String()) logger.Debugf("consumer path:%s, url:%s", dubboPath, rawURL) diff --git a/registry/consul/listener.go b/registry/consul/listener.go index b1598345032bddd7358c2263e5fd71b3d4e5545d..0d665afe5a8425bd91a02b74cfa451405974e21a 100644 --- a/registry/consul/listener.go +++ b/registry/consul/listener.go @@ -39,17 +39,17 @@ import ( // registry. type consulListener struct { // Registry url. - registryUrl common.URL + registryUrl *common.URL // Consumer url. - consumerUrl common.URL + consumerUrl *common.URL // Consul watcher. plan *watch.Plan // Most recent service urls return by // watcher. - urls []common.URL + urls []*common.URL // All service information changes will // be wrapped into ServiceEvent, and be @@ -78,7 +78,7 @@ type consulListener struct { wg sync.WaitGroup } -func newConsulListener(registryUrl common.URL, consumerUrl common.URL) (*consulListener, error) { +func newConsulListener(registryUrl *common.URL, consumerUrl *common.URL) (*consulListener, error) { params := make(map[string]interface{}, 8) params["type"] = "service" params["service"] = consumerUrl.Service() @@ -93,7 +93,7 @@ func newConsulListener(registryUrl common.URL, consumerUrl common.URL) (*consulL registryUrl: registryUrl, consumerUrl: consumerUrl, plan: plan, - urls: make([]common.URL, 0, 8), + urls: make([]*common.URL, 0, 8), eventCh: make(chan *registry.ServiceEvent, 32), errCh: make(chan error, 32), done: make(chan struct{}), @@ -142,7 +142,7 @@ func (l *consulListener) run() { func (l *consulListener) handler(idx uint64, raw interface{}) { var ( service *consul.ServiceEntry - url common.URL + url *common.URL ok bool err error ) @@ -153,7 +153,7 @@ func (l *consulListener) handler(idx uint64, raw interface{}) { l.errCh <- err return } - newUrls := make([]common.URL, 0, 8) + newUrls := make([]*common.URL, 0, 8) events := make([]*registry.ServiceEvent, 0, 8) for _, service = range services { diff --git a/registry/consul/registry.go b/registry/consul/registry.go index b92e335fdb69f82210d2977789902eb6123201b8..4d3718e58cd0f98b931ed525fe61f33713b6e71f 100644 --- a/registry/consul/registry.go +++ b/registry/consul/registry.go @@ -23,6 +23,7 @@ import ( ) import ( + getty "github.com/apache/dubbo-getty" consul "github.com/hashicorp/consul/api" perrors "github.com/pkg/errors" ) @@ -56,6 +57,10 @@ type consulRegistry struct { // Done field represents whether // consul registry is closed. done chan struct{} + + // registeredURLs field represents all URLs that have been registered + // will be unregistered when destroyed + registeredURLs []*common.URL } func newConsulRegistry(url *common.URL) (registry.Registry, error) { @@ -76,7 +81,7 @@ func newConsulRegistry(url *common.URL) (registry.Registry, error) { // Register register @url // it delegate the job to register() method -func (r *consulRegistry) Register(url common.URL) error { +func (r *consulRegistry) Register(url *common.URL) error { var err error role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) @@ -90,7 +95,8 @@ func (r *consulRegistry) Register(url common.URL) error { } // register actually register the @url -func (r *consulRegistry) register(url common.URL) error { +func (r *consulRegistry) register(url *common.URL) error { + r.registeredURLs = append(r.registeredURLs, url.Clone()) service, err := buildService(url) if err != nil { return err @@ -100,7 +106,7 @@ func (r *consulRegistry) register(url common.URL) error { // UnRegister unregister the @url // it delegate the job to unregister() method -func (r *consulRegistry) UnRegister(url common.URL) error { +func (r *consulRegistry) UnRegister(url *common.URL) error { var err error role, _ := strconv.Atoi(r.URL.GetParam(constant.ROLE_KEY, "")) @@ -114,7 +120,7 @@ func (r *consulRegistry) UnRegister(url common.URL) error { } // unregister actually unregister the @url -func (r *consulRegistry) unregister(url common.URL) error { +func (r *consulRegistry) unregister(url *common.URL) error { return r.client.Agent().ServiceDeregister(buildId(url)) } @@ -141,7 +147,7 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti return } - listener, err := r.getListener(*url) + listener, err := r.getListener(url) if err != nil { if !r.IsAvailable() { logger.Warnf("event listener game over.") @@ -166,14 +172,14 @@ func (r *consulRegistry) subscribe(url *common.URL, notifyListener registry.Noti } } -func (r *consulRegistry) getListener(url common.URL) (registry.Listener, error) { - listener, err := newConsulListener(*r.URL, url) +func (r *consulRegistry) getListener(url *common.URL) (registry.Listener, error) { + listener, err := newConsulListener(r.URL, url) return listener, err } // GetUrl get registry URL of consul registry center -func (r *consulRegistry) GetUrl() common.URL { - return *r.URL +func (r *consulRegistry) GetUrl() *common.URL { + return r.URL } // IsAvailable checks consul registry center whether is available @@ -188,25 +194,26 @@ func (r *consulRegistry) IsAvailable() bool { // Destroy consul registry center func (r *consulRegistry) Destroy() { - if r.URL != nil { - done := make(chan struct{}, 1) - go func() { - defer func() { - if e := recover(); e != nil { - logger.Errorf("consulRegistry destory with panic: %v", e) - } - done <- struct{}{} - }() - if err := r.UnRegister(*r.URL); err != nil { - logger.Errorf("consul registry unregister with err: %s", err.Error()) + done := make(chan struct{}, 1) + go func() { + defer func() { + if e := recover(); e != nil { + logger.Errorf("consulRegistry destroy with panic: %v", e) } + done <- struct{}{} }() - select { - case <-done: - logger.Infof("consulRegistry unregister done") - case <-time.After(registryDestroyDefaultTimeout): - logger.Errorf("consul unregister timeout") + for _, url := range r.registeredURLs { + if err := r.UnRegister(url); err != nil { + logger.Errorf("consul registry unregister with err: %s", err.Error()) + } } + }() + select { + case <-done: + logger.Infof("consulRegistry unregister done") + case <-getty.GetTimeWheel().After(registryDestroyDefaultTimeout): + logger.Errorf("consul unregister timeout") } + close(r.done) } diff --git a/registry/consul/service_discovery.go b/registry/consul/service_discovery.go index d8ab93f31ee6fdcf79aa869a35548e8192841fe4..fba142e04e4979b9c4fe123cc121111838ae2d4e 100644 --- a/registry/consul/service_discovery.go +++ b/registry/consul/service_discovery.go @@ -27,7 +27,7 @@ import ( import ( "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/page" + "github.com/dubbogo/gost/hash/page" consul "github.com/hashicorp/consul/api" "github.com/hashicorp/consul/api/watch" perrors "github.com/pkg/errors" @@ -252,7 +252,7 @@ func (csd *consulServiceDiscovery) GetServices() *gxset.HashSet { return res } - for service, _ := range services { + for service := range services { res.Add(service) } return res @@ -339,7 +339,7 @@ func (csd *consulServiceDiscovery) GetInstancesByPage(serviceName string, offset for i := offset; i < len(all) && i < offset+pageSize; i++ { res = append(res, all[i]) } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } func (csd *consulServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { @@ -358,7 +358,7 @@ func (csd *consulServiceDiscovery) GetHealthyInstancesByPage(serviceName string, } i++ } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } func (csd *consulServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { @@ -444,7 +444,7 @@ func (csd *consulServiceDiscovery) buildRegisterInstance(instance registry.Servi metadata = encodeConsulMetadata(metadata) metadata[enable] = strconv.FormatBool(instance.IsEnable()) // check - check := csd.buildCheck(instance) + check := csd.buildCheck() return &consul.AgentServiceRegistration{ ID: buildID(instance), @@ -456,12 +456,7 @@ func (csd *consulServiceDiscovery) buildRegisterInstance(instance registry.Servi }, 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 - } +func (csd *consulServiceDiscovery) buildCheck() consul.AgentServiceCheck { return consul.AgentServiceCheck{ TTL: strconv.FormatInt(csd.checkPassInterval/1000, 10) + "s", DeregisterCriticalServiceAfter: csd.deregisterCriticalServiceAfter, diff --git a/registry/consul/service_discovery_test.go b/registry/consul/service_discovery_test.go index ed7220f2de3140d4e97ea48acac1f53d29953208..3f97d841d56111746f396ba753142324ac4555d2 100644 --- a/registry/consul/service_discovery_test.go +++ b/registry/consul/service_discovery_test.go @@ -44,7 +44,6 @@ var ( consulCheckPassInterval = 17000 consulDeregisterCriticalServiceAfter = "20s" consulWatchTimeout = 60000 - registryURL = common.URL{} ) func TestConsulServiceDiscovery_newConsulServiceDiscovery(t *testing.T) { @@ -85,7 +84,10 @@ func TestConsulServiceDiscovery_Destroy(t *testing.T) { func TestConsulServiceDiscovery_CRUD(t *testing.T) { // start consul agent consulAgent := consul.NewConsulAgent(t, registryPort) - defer consulAgent.Shutdown() + defer func() { + err := consulAgent.Shutdown() + assert.NoError(t, err) + }() prepareData() var eventDispatcher = MockEventDispatcher{Notify: make(chan struct{}, 1)} @@ -139,7 +141,8 @@ func TestConsulServiceDiscovery_CRUD(t *testing.T) { assert.Equal(t, 1, len(page.GetData())) instanceResult = page.GetData()[0].(*registry.DefaultServiceInstance) - v, _ := instanceResult.Metadata["aaa"] + v, ok := instanceResult.Metadata["aaa"] + assert.True(t, ok) assert.Equal(t, "bbb", v) // test dispatcher event @@ -173,7 +176,7 @@ func prepareData() { } } -func prepareService() (registry.ServiceInstance, common.URL) { +func prepareService() (registry.ServiceInstance, *common.URL) { id := "id" registryUrl, _ := common.NewURL(protocol + "://" + providerHost + ":" + strconv.Itoa(providerPort) + "/" + service + "?anyhost=true&" + @@ -200,19 +203,19 @@ type MockEventDispatcher struct { } // AddEventListener do nothing -func (m *MockEventDispatcher) AddEventListener(listener observer.EventListener) { +func (m *MockEventDispatcher) AddEventListener(observer.EventListener) { } // AddEventListeners do nothing -func (m *MockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) { +func (m *MockEventDispatcher) AddEventListeners([]observer.EventListener) { } // RemoveEventListener do nothing -func (m *MockEventDispatcher) RemoveEventListener(listener observer.EventListener) { +func (m *MockEventDispatcher) RemoveEventListener(observer.EventListener) { } // RemoveEventListeners do nothing -func (m *MockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) { +func (m *MockEventDispatcher) RemoveEventListeners([]observer.EventListener) { } // GetAllEventListeners return empty list diff --git a/registry/consul/utils.go b/registry/consul/utils.go index 05ac5e76e292a2b574bd5e661bbbcca4f419fc22..76f5d2dd3378f67d300d10b4c5ecc72934ef7715 100644 --- a/registry/consul/utils.go +++ b/registry/consul/utils.go @@ -25,7 +25,6 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" consul "github.com/hashicorp/consul/api" perrors "github.com/pkg/errors" ) @@ -34,12 +33,12 @@ import ( "github.com/apache/dubbo-go/common" ) -func buildId(url common.URL) string { +func buildId(url *common.URL) string { t := md5.Sum([]byte(url.String())) return hex.EncodeToString(t[:]) } -func buildService(url common.URL) (*consul.AgentServiceRegistration, error) { +func buildService(url *common.URL) (*consul.AgentServiceRegistration, error) { var err error // id @@ -47,7 +46,7 @@ func buildService(url common.URL) (*consul.AgentServiceRegistration, error) { // address if url.Ip == "" { - url.Ip, _ = gxnet.GetLocalIP() + url.Ip = common.GetLocalIp() } // port @@ -94,21 +93,21 @@ func buildService(url common.URL) (*consul.AgentServiceRegistration, error) { return service, nil } -func retrieveURL(service *consul.ServiceEntry) (common.URL, error) { +func retrieveURL(service *consul.ServiceEntry) (*common.URL, error) { url, ok := service.Service.Meta["url"] if !ok { - return common.URL{}, perrors.New("retrieve url fails with no url key in service meta") + return nil, perrors.New("retrieve url fails with no url key in service meta") } url1, err := common.NewURL(url) if err != nil { - return common.URL{}, perrors.WithStack(err) + return nil, perrors.WithStack(err) } return url1, nil } -func in(url common.URL, urls []common.URL) bool { +func in(url *common.URL, urls []*common.URL) bool { for _, url1 := range urls { - if url.URLEqual(url1) { + if common.IsEquals(url, url1) { return true } } diff --git a/registry/consul/utils_test.go b/registry/consul/utils_test.go index 0e5bffe457b9e3317ff056c51e4f5a9633a429e6..b7e2929cec13c839a8ebaa9cb8a3000ab2cafef3 100644 --- a/registry/consul/utils_test.go +++ b/registry/consul/utils_test.go @@ -19,6 +19,8 @@ package consul import ( "fmt" + "github.com/apache/dubbo-go/common/logger" + "github.com/stretchr/testify/assert" "net" "net/url" "strconv" @@ -63,8 +65,8 @@ func newConsumerRegistryUrl(host string, port int) *common.URL { ) } -func newProviderUrl(host string, port int, service string, protocol string) common.URL { - return *common.NewURLWithOptions( +func newProviderUrl(host string, port int, service string, protocol string) *common.URL { + return common.NewURLWithOptions( common.WithIp(host), common.WithPort(strconv.Itoa(port)), common.WithPath(service), @@ -72,8 +74,8 @@ func newProviderUrl(host string, port int, service string, protocol string) comm ) } -func newConsumerUrl(host string, port int, service string, protocol string) common.URL { - return *common.NewURLWithOptions( +func newConsumerUrl(host string, port int, service string, protocol string) *common.URL { + return common.NewURLWithOptions( common.WithIp(host), common.WithPort(strconv.Itoa(port)), common.WithPath(service), @@ -113,15 +115,24 @@ func (server *testServer) serve() { if err != nil { continue } - conn.Write([]byte("Hello World")) - conn.Close() + _, err = conn.Write([]byte("Hello World")) + if err != nil { + logger.Warnf("conn.Write() = error: %v", err) + } + err = conn.Close() + if err != nil { + logger.Warnf("conn.Close() = error: %v", err) + } } } } func (server *testServer) close() { close(server.done) - server.listener.Close() + if err := server.listener.Close(); err != nil { + fmt.Printf("server.listener.Close() = error:%v\n", err) + } + server.wg.Wait() } @@ -130,8 +141,8 @@ type consulRegistryTestSuite struct { providerRegistry registry.Registry consumerRegistry *consulRegistry listener registry.Listener - providerUrl common.URL - consumerUrl common.URL + providerUrl *common.URL + consumerUrl *common.URL } func newConsulRegistryTestSuite(t *testing.T) *consulRegistryTestSuite { @@ -148,7 +159,10 @@ func (suite *consulRegistryTestSuite) close() { // register -> subscribe -> unregister func test1(t *testing.T) { consulAgent := consul.NewConsulAgent(t, registryPort) - defer consulAgent.Shutdown() + defer func() { + err := consulAgent.Shutdown() + assert.NoError(t, err) + }() server := newServer(providerHost, providerPort) defer server.close() @@ -169,7 +183,10 @@ func test1(t *testing.T) { // subscribe -> register -> unregister func test2(t *testing.T) { consulAgent := consul.NewConsulAgent(t, registryPort) - defer consulAgent.Shutdown() + defer func() { + err := consulAgent.Shutdown() + assert.NoError(t, err) + }() server := newServer(providerHost, providerPort) defer server.close() diff --git a/registry/directory/directory.go b/registry/directory/directory.go index 8871a2a26e406145679e2911256ffb633fbb7f8c..8c1524087221c235a762a291d5653b88fa4f15b6 100644 --- a/registry/directory/directory.go +++ b/registry/directory/directory.go @@ -19,17 +19,19 @@ package directory import ( "fmt" + "net/url" + "os" "sync" ) import ( perrors "github.com/pkg/errors" - "go.uber.org/atomic" ) import ( "github.com/apache/dubbo-go/cluster" "github.com/apache/dubbo-go/cluster/directory" + "github.com/apache/dubbo-go/cluster/router/chain" "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" "github.com/apache/dubbo-go/common/extension" @@ -56,12 +58,14 @@ type RegistryDirectory struct { serviceType string registry registry.Registry cacheInvokersMap *sync.Map // use sync.map + consumerURL *common.URL cacheOriginUrl *common.URL configurators []config_center.Configurator consumerConfigurationListener *consumerConfigurationListener referenceConfigurationListener *referenceConfigurationListener - serviceKey string - forbidden atomic.Bool + //serviceKey string + //forbidden atomic.Bool + registerLock sync.Mutex // this lock if for register } // NewRegistryDirectory will create a new RegistryDirectory @@ -69,6 +73,7 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster. if url.SubURL == nil { return nil, perrors.Errorf("url is invalid, suburl can not be nil") } + logger.Debugf("new RegistryDirectory for service :%s.", url.Key()) dir := &RegistryDirectory{ BaseDirectory: directory.NewBaseDirectory(url), cacheInvokers: []protocol.Invoker{}, @@ -76,6 +81,15 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster. serviceType: url.SubURL.Service(), registry: registry, } + + dir.consumerURL = dir.getConsumerUrl(url.SubURL) + + if routerChain, err := chain.NewRouterChain(dir.consumerURL); err == nil { + dir.BaseDirectory.SetRouterChain(routerChain) + } else { + logger.Warnf("fail to create router chain with url: %s, err is: %v", url.SubURL, err) + } + dir.consumerConfigurationListener = newConsumerConfigurationListener(dir) go dir.subscribe(url.SubURL) @@ -84,69 +98,132 @@ func NewRegistryDirectory(url *common.URL, registry registry.Registry) (cluster. // subscribe from registry func (dir *RegistryDirectory) subscribe(url *common.URL) { + logger.Debugf("subscribe service :%s for RegistryDirectory.", url.Key()) dir.consumerConfigurationListener.addNotifyListener(dir) dir.referenceConfigurationListener = newReferenceConfigurationListener(dir, url) - dir.registry.Subscribe(url, dir) + if err := dir.registry.Subscribe(url, dir); err != nil { + logger.Error("registry.Subscribe(url:%v, dir:%v) = error:%v", url, dir, err) + } } // Notify monitor changes from registry,and update the cacheServices -func (dir *RegistryDirectory) Notify(events ...*registry.ServiceEvent) { - go dir.refreshInvokers(events...) +func (dir *RegistryDirectory) Notify(event *registry.ServiceEvent) { + if event == nil { + return + } + go dir.refreshInvokers(event) +} + +// NotifyAll notify the events that are complete Service Event List. +// After notify the address, the callback func will be invoked. +func (dir *RegistryDirectory) NotifyAll(events []*registry.ServiceEvent, callback func()) { + go dir.refreshAllInvokers(events, callback) +} + +// refreshInvokers refreshes service's events. +func (dir *RegistryDirectory) refreshInvokers(event *registry.ServiceEvent) { + if event != nil { + logger.Debugf("refresh invokers with %+v", event) + } else { + logger.Debug("refresh invokers with nil") + } + + var oldInvoker protocol.Invoker + if event != nil { + oldInvoker, _ = dir.cacheInvokerByEvent(event) + } + dir.setNewInvokers() + if oldInvoker != nil { + oldInvoker.Destroy() + } } -// 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 +// refreshAllInvokers the argument is the complete list of the service events, we can safely assume any cached invoker +// not in the incoming list can be removed. The Action of serviceEvent should be EventTypeUpdate. +func (dir *RegistryDirectory) refreshAllInvokers(events []*registry.ServiceEvent, callback func()) { + var ( + oldInvokers []protocol.Invoker + addEvents []*registry.ServiceEvent + ) + dir.overrideUrl(dir.GetDirectoryUrl()) + referenceUrl := dir.GetDirectoryUrl().SubURL - // in batch mode, it is safe to remove since we have the complete list of events. - if len(events) > 1 { + // loop the events to check the Action should be EventTypeUpdate. + for _, event := range events { + if event.Action != remoting.EventTypeUpdate { + panic("Your implements of register center is wrong, " + + "please check the Action of ServiceEvent should be EventTypeUpdate") + } + // Originally it will Merge URL many times, now we just execute once. + // MergeUrl is executed once and put the result into Event. After this, the key will get from Event.Key(). + newUrl := dir.convertUrl(event) + newUrl = common.MergeUrl(newUrl, referenceUrl) + dir.overrideUrl(newUrl) + event.Update(newUrl) + } + // After notify all addresses, do some callback. + defer callback() + func() { + // this lock is work at batch update of InvokeCache + dir.registerLock.Lock() + defer dir.registerLock.Unlock() + // get need clear invokers from original invoker list dir.cacheInvokersMap.Range(func(k, v interface{}) bool { if !dir.eventMatched(k.(string), events) { + // delete unused invoker from cache if invoker := dir.uncacheInvokerWithKey(k.(string)); invoker != nil { oldInvokers = append(oldInvokers, invoker) } } return true }) - } - - for _, event := range events { - logger.Debugf("registry update, result{%s}", event) - if oldInvoker, _ := dir.cacheInvokerByEvent(event); oldInvoker != nil { - oldInvokers = append(oldInvokers, oldInvoker) + // get need add invokers from events + for _, event := range events { + // Get the key from Event.Key() + if _, ok := dir.cacheInvokersMap.Load(event.Key()); !ok { + addEvents = append(addEvents, event) + } } - } - - if len(events) > 0 { - dir.setNewInvokers() - } - - // After dir.cacheInvokers is updated,destroy the oldInvoker - // Ensure that no request will enter the oldInvoker + // loop the updateEvents + for _, event := range addEvents { + logger.Debugf("registry update, result{%s}", event) + if event.Service != nil { + logger.Infof("selector add service url{%s}", event.Service.String()) + } + if event != nil && event.Service != nil && constant.ROUTER_PROTOCOL == event.Service.Protocol { + dir.configRouters() + } + if oldInvoker, _ := dir.doCacheInvoker(event.Service); oldInvoker != nil { + oldInvokers = append(oldInvokers, oldInvoker) + } + } + }() + dir.setNewInvokers() + // destroy unused invokers for _, invoker := range oldInvokers { - invoker.Destroy() + go 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 { + if dir.invokerCacheKey(event) == key { return true } } return false } -// invokerCacheKey generates the key in the cache for a given URL. -func (dir *RegistryDirectory) invokerCacheKey(url *common.URL) string { +// invokerCacheKey generates the key in the cache for a given ServiceEvent. +func (dir *RegistryDirectory) invokerCacheKey(event *registry.ServiceEvent) string { + // If the url is merged, then return Event.Key() directly. + if event.Updated() { + return event.Key() + } referenceUrl := dir.GetDirectoryUrl().SubURL - newUrl := common.MergeUrl(url, referenceUrl) + newUrl := common.MergeUrl(event.Service, referenceUrl) + event.Update(newUrl) return newUrl.Key() } @@ -167,8 +244,9 @@ func (dir *RegistryDirectory) cacheInvokerByEvent(event *registry.ServiceEvent) 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() + if u != nil && constant.ROUTER_PROTOCOL == u.Protocol { + dir.configRouters() + } return dir.cacheInvoker(u), nil case remoting.EventTypeDel: logger.Infof("selector delete service url{%s}", event.Service) @@ -194,7 +272,7 @@ func (dir *RegistryDirectory) configRouters() { // convertUrl processes override:// and router:// func (dir *RegistryDirectory) convertUrl(res *registry.ServiceEvent) *common.URL { - ret := &res.Service + 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)) @@ -221,11 +299,7 @@ func (dir *RegistryDirectory) toGroupInvokers() []protocol.Invoker { for _, invoker := range newInvokersList { group := invoker.GetUrl().GetParam(constant.GROUP_KEY, "") - if _, ok := groupInvokersMap[group]; ok { - groupInvokersMap[group] = append(groupInvokersMap[group], invoker) - } else { - groupInvokersMap[group] = []protocol.Invoker{invoker} - } + groupInvokersMap[group] = append(groupInvokersMap[group], invoker) } groupInvokersList := make([]protocol.Invoker, 0, len(groupInvokersMap)) if len(groupInvokersMap) == 1 { @@ -256,6 +330,7 @@ func (dir *RegistryDirectory) uncacheInvoker(url *common.URL) protocol.Invoker { func (dir *RegistryDirectory) uncacheInvokerWithKey(key string) protocol.Invoker { logger.Debugf("service will be deleted in cache invokers: invokers key is %s!", key) + protocol.RemoveUrlKeyUnhealthyStatus(key) if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); ok { dir.cacheInvokersMap.Delete(key) return cacheInvoker.(protocol.Invoker) @@ -281,30 +356,38 @@ func (dir *RegistryDirectory) cacheInvoker(url *common.URL) protocol.Invoker { if url.Protocol == referenceUrl.Protocol || referenceUrl.Protocol == "" { newUrl := common.MergeUrl(url, referenceUrl) dir.overrideUrl(newUrl) - if cacheInvoker, ok := dir.cacheInvokersMap.Load(newUrl.Key()); !ok { - logger.Debugf("service will be added in cache invokers: invokers url is %s!", newUrl) - newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(*newUrl) - if newInvoker != nil { - 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 { - dir.cacheInvokersMap.Store(newUrl.Key(), newInvoker) - return cacheInvoker.(protocol.Invoker) - } + if v, ok := dir.doCacheInvoker(newUrl); ok { + return v } } return nil } +func (dir *RegistryDirectory) doCacheInvoker(newUrl *common.URL) (protocol.Invoker, bool) { + key := newUrl.Key() + if cacheInvoker, ok := dir.cacheInvokersMap.Load(key); !ok { + logger.Debugf("service will be added in cache invokers: invokers url is %s!", newUrl) + newInvoker := extension.GetProtocol(protocolwrapper.FILTER).Refer(newUrl) + if newInvoker != nil { + dir.cacheInvokersMap.Store(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.GetCompareURLEqualFunc()(newUrl, cacheInvoker.(protocol.Invoker).GetUrl()) { + return nil, true + } + + 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 { + dir.cacheInvokersMap.Store(key, newInvoker) + return cacheInvoker.(protocol.Invoker), true + } + } + return nil, false +} + // List selected protocol invokers from the directory func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.Invoker { invokers := dir.cacheInvokers @@ -313,7 +396,7 @@ func (dir *RegistryDirectory) List(invocation protocol.Invocation) []protocol.In if routerChain == nil { return invokers } - return routerChain.Route(invokers, dir.cacheOriginUrl, invocation) + return routerChain.Route(dir.consumerURL, invocation) } // IsAvailable whether the directory is available @@ -349,6 +432,24 @@ func (dir *RegistryDirectory) overrideUrl(targetUrl *common.URL) { doOverrideUrl(dir.referenceConfigurationListener.Configurators(), targetUrl) } +func (dir *RegistryDirectory) getConsumerUrl(c *common.URL) *common.URL { + processID := fmt.Sprintf("%d", os.Getpid()) + localIP := common.GetLocalIp() + + params := url.Values{} + c.RangeParams(func(key, value string) bool { + params.Add(key, value) + return true + }) + + params.Add("pid", processID) + params.Add("ip", localIP) + params.Add("protocol", c.Protocol) + + return common.NewURLWithOptions(common.WithProtocol("consumer"), common.WithIp(localIP), common.WithPath(c.Path), + common.WithParams(params)) +} + func doOverrideUrl(configurators []config_center.Configurator, targetUrl *common.URL) { for _, v := range configurators { v.Configure(targetUrl) @@ -375,7 +476,7 @@ func newReferenceConfigurationListener(dir *RegistryDirectory, url *common.URL) func (l *referenceConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) // FIXME: this doesn't trigger dir.overrideUrl() - l.directory.refreshInvokers() + l.directory.refreshInvokers(nil) } type consumerConfigurationListener struct { @@ -402,5 +503,5 @@ func (l *consumerConfigurationListener) addNotifyListener(listener registry.Noti func (l *consumerConfigurationListener) Process(event *config_center.ConfigChangeEvent) { l.BaseConfigurationListener.Process(event) // FIXME: this doesn't trigger dir.overrideUrl() - l.directory.refreshInvokers() + l.directory.refreshInvokers(nil) } diff --git a/registry/directory/directory_test.go b/registry/directory/directory_test.go index f2b2f8edd2d46950d2e74733b1d869e0de282ec0..b5d81eb0da0b7ae635b8a54e3002a9380a69a2fd 100644 --- a/registry/directory/directory_test.go +++ b/registry/directory/directory_test.go @@ -72,7 +72,7 @@ func TestSubscribe(t *testing.T) { func TestSubscribe_InvalidUrl(t *testing.T) { url, _ := common.NewURL("mock://127.0.0.1:1111") mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) - _, err := NewRegistryDirectory(&url, mockRegistry) + _, err := NewRegistryDirectory(url, mockRegistry) assert.Error(t, err) } @@ -83,9 +83,9 @@ func TestSubscribe_Group(t *testing.T) { regurl, _ := common.NewURL("mock://127.0.0.1:1111") suburl, _ := common.NewURL("dubbo://127.0.0.1:20000") suburl.SetParam(constant.CLUSTER_KEY, "mock") - regurl.SubURL = &suburl + regurl.SubURL = suburl mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) - dir, _ := NewRegistryDirectory(®url, mockRegistry) + dir, _ := NewRegistryDirectory(regurl, mockRegistry) go dir.(*RegistryDirectory).subscribe(common.NewURLWithOptions(common.WithPath("testservice"))) //for group1 @@ -93,7 +93,7 @@ func TestSubscribe_Group(t *testing.T) { urlmap.Set(constant.GROUP_KEY, "group1") urlmap.Set(constant.CLUSTER_KEY, "failover") //to test merge url for i := 0; i < 3; i++ { - mockRegistry.(*registry.MockRegistry).MockEvent(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: *common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"), + mockRegistry.(*registry.MockRegistry).MockEvent(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"), common.WithParams(urlmap))}) } //for group2 @@ -101,7 +101,7 @@ func TestSubscribe_Group(t *testing.T) { urlmap2.Set(constant.GROUP_KEY, "group2") urlmap2.Set(constant.CLUSTER_KEY, "failover") //to test merge url for i := 0; i < 3; i++ { - mockRegistry.(*registry.MockRegistry).MockEvent(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: *common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"), + mockRegistry.(*registry.MockRegistry).MockEvent(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: common.NewURLWithOptions(common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"), common.WithParams(urlmap2))}) } @@ -124,7 +124,7 @@ func Test_Destroy(t *testing.T) { func Test_List(t *testing.T) { registryDirectory, _ := normalRegistryDir() - time.Sleep(4e9) + time.Sleep(6e9) assert.Len(t, registryDirectory.List(&invocation.RPCInvocation{}), 3) assert.Equal(t, true, registryDirectory.IsAvailable()) @@ -192,6 +192,34 @@ func Test_toGroupInvokers(t *testing.T) { assert.Len(t, groupInvokers, 2) } +func Test_RefreshUrl(t *testing.T) { + registryDirectory, mockRegistry := normalRegistryDir() + providerUrl, _ := common.NewURL("dubbo://0.0.0.0:20011/org.apache.dubbo-go.mockService", + common.WithParamsValue(constant.CLUSTER_KEY, "mock1"), + common.WithParamsValue(constant.GROUP_KEY, "group"), + common.WithParamsValue(constant.VERSION_KEY, "1.0.0")) + providerUrl2, _ := common.NewURL("dubbo://0.0.0.0:20012/org.apache.dubbo-go.mockService", + common.WithParamsValue(constant.CLUSTER_KEY, "mock1"), + common.WithParamsValue(constant.GROUP_KEY, "group"), + common.WithParamsValue(constant.VERSION_KEY, "1.0.0")) + time.Sleep(1e9) + assert.Len(t, registryDirectory.cacheInvokers, 3) + mockRegistry.MockEvent(®istry.ServiceEvent{Action: remoting.EventTypeAdd, Service: providerUrl}) + time.Sleep(1e9) + assert.Len(t, registryDirectory.cacheInvokers, 4) + mockRegistry.MockEvents([]*registry.ServiceEvent{®istry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl}}) + time.Sleep(1e9) + assert.Len(t, registryDirectory.cacheInvokers, 1) + mockRegistry.MockEvents([]*registry.ServiceEvent{®istry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl}, + ®istry.ServiceEvent{Action: remoting.EventTypeUpdate, Service: providerUrl2}}) + time.Sleep(1e9) + assert.Len(t, registryDirectory.cacheInvokers, 2) + // clear all address + mockRegistry.MockEvents([]*registry.ServiceEvent{}) + time.Sleep(1e9) + assert.Len(t, registryDirectory.cacheInvokers, 0) +} + func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockRegistry) { extension.SetProtocol(protocolwrapper.FILTER, protocolwrapper.NewMockProtocolFilter) @@ -202,17 +230,17 @@ func normalRegistryDir(noMockEvent ...bool) (*RegistryDirectory, *registry.MockR common.WithParamsValue(constant.GROUP_KEY, "group"), common.WithParamsValue(constant.VERSION_KEY, "1.0.0"), ) - url.SubURL = &suburl + url.SubURL = suburl mockRegistry, _ := registry.NewMockRegistry(&common.URL{}) - dir, _ := NewRegistryDirectory(&url, mockRegistry) + dir, _ := NewRegistryDirectory(url, mockRegistry) - go dir.(*RegistryDirectory).subscribe(&suburl) + go dir.(*RegistryDirectory).subscribe(suburl) if len(noMockEvent) == 0 { for i := 0; i < 3; i++ { mockRegistry.(*registry.MockRegistry).MockEvent( ®istry.ServiceEvent{ Action: remoting.EventTypeAdd, - Service: *common.NewURLWithOptions( + Service: common.NewURLWithOptions( common.WithPath("TEST"+strconv.FormatInt(int64(i), 10)), common.WithProtocol("dubbo"), ), diff --git a/registry/etcdv3/listener.go b/registry/etcdv3/listener.go index 436b6eca5b0dfb8514cbc21a47032490f7b1c21f..f900495f97d3acfd9a2ae232264910b791961bc8 100644 --- a/registry/etcdv3/listener.go +++ b/registry/etcdv3/listener.go @@ -19,6 +19,7 @@ package etcdv3 import ( "strings" + "sync" ) import ( @@ -64,7 +65,7 @@ func (l *dataListener) DataChange(eventType remoting.Event) bool { } for _, v := range l.interestedURL { - if serviceURL.URLEqual(*v) { + if serviceURL.URLEqual(v) { l.listener.Process( &config_center.ConfigChangeEvent{ Key: eventType.Path, @@ -79,8 +80,9 @@ func (l *dataListener) DataChange(eventType remoting.Event) bool { } type configurationListener struct { - registry *etcdV3Registry - events chan *config_center.ConfigChangeEvent + registry *etcdV3Registry + events chan *config_center.ConfigChangeEvent + closeOnce sync.Once } // NewConfigurationListener for listening the event of etcdv3. @@ -113,12 +115,14 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { } continue } - return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil + return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil } } } // Close etcd registry center func (l *configurationListener) Close() { - l.registry.WaitGroup().Done() + l.closeOnce.Do(func() { + l.registry.WaitGroup().Done() + }) } diff --git a/registry/etcdv3/listener_test.go b/registry/etcdv3/listener_test.go index 1cf06d17dcd4c92afc5221dab404e9d180c03e6c..ff7f63f614c6c43564dc10e412a833449b3bea8a 100644 --- a/registry/etcdv3/listener_test.go +++ b/registry/etcdv3/listener_test.go @@ -80,7 +80,7 @@ func (suite *RegistryTestSuite) TestDataChange() { listener := NewRegistryDataListener(&MockDataListener{}) url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") - listener.AddInterestedURL(&url) + listener.AddInterestedURL(url) if !listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"}) { t.Fatal("data change not ok") } diff --git a/registry/etcdv3/registry.go b/registry/etcdv3/registry.go index f3cc379bd8e94b15b678f0ac1d5ed5b6c917da6a..7ccf32661c75ed4cd27fddd1fc020ded56066f37 100644 --- a/registry/etcdv3/registry.go +++ b/registry/etcdv3/registry.go @@ -51,7 +51,7 @@ type etcdV3Registry struct { registry.BaseRegistry cltLock sync.Mutex client *etcdv3.Client - listenerLock sync.Mutex + listenerLock sync.RWMutex listener *etcdv3.EventListener dataListener *dataListener configListener *configurationListener @@ -150,14 +150,9 @@ func (r *etcdV3Registry) CreatePath(k string) error { // DoSubscribe actually subscribe the provider URL func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) { - - var ( - configListener *configurationListener - ) - - r.listenerLock.Lock() - configListener = r.configListener - r.listenerLock.Unlock() + r.listenerLock.RLock() + configListener := r.configListener + r.listenerLock.RUnlock() if r.listener == nil { r.cltLock.Lock() client := r.client @@ -165,12 +160,8 @@ func (r *etcdV3Registry) DoSubscribe(svc *common.URL) (registry.Listener, error) if client == nil { return nil, perrors.New("etcd client broken") } - - // new client & listener - listener := etcdv3.NewEventListener(r.client) - r.listenerLock.Lock() - r.listener = listener + r.listener = etcdv3.NewEventListener(r.client) // new client & listener r.listenerLock.Unlock() } diff --git a/registry/etcdv3/registry_test.go b/registry/etcdv3/registry_test.go index 164fe9ca61793614e3d9d2f77f49cdfaebdd7317..d94eff656dbc4f46e4b34c8c50b20396b335c073 100644 --- a/registry/etcdv3/registry_test.go +++ b/registry/etcdv3/registry_test.go @@ -39,7 +39,7 @@ func initRegistry(t *testing.T) *etcdV3Registry { t.Fatal(err) } - reg, err := newETCDV3Registry(®url) + reg, err := newETCDV3Registry(regurl) if err != nil { t.Fatal(err) } @@ -86,7 +86,7 @@ func (suite *RegistryTestSuite) TestSubscribe() { err = reg2.Register(url) assert.NoError(t, err) - listener, err := reg2.DoSubscribe(&url) + listener, err := reg2.DoSubscribe(url) if err != nil { t.Fatal(err) } @@ -104,7 +104,7 @@ func (suite *RegistryTestSuite) TestConsumerDestroy() { url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) reg := initRegistry(t) - _, err := reg.DoSubscribe(&url) + _, err := reg.DoSubscribe(url) if err != nil { t.Fatal(err) } diff --git a/registry/etcdv3/service_discovery.go b/registry/etcdv3/service_discovery.go index e8d4aea9a42634896c3c30e5c6b527a935179873..ca6016e49b4e174e5843b3c08beea7b559bbfb9b 100644 --- a/registry/etcdv3/service_discovery.go +++ b/registry/etcdv3/service_discovery.go @@ -26,7 +26,7 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" "github.com/hashicorp/vault/sdk/helper/jsonutil" perrors "github.com/pkg/errors" ) @@ -108,8 +108,11 @@ func (e *etcdV3ServiceDiscovery) Update(instance registry.ServiceInstance) error if nil != e.client { ins, err := jsonutil.EncodeJSON(instance) - if nil == err { - e.client.RegisterTemp(path, string(ins)) + if err == nil { + if err = e.client.RegisterTemp(path, string(ins)); err != nil { + logger.Warnf("etcdV3ServiceDiscovery.client.RegisterTemp(path:%v, instance:%v) = error:%v", + path, string(ins), err) + } e.services.Add(instance.GetServiceName()) } } @@ -162,7 +165,7 @@ func (e *etcdV3ServiceDiscovery) GetInstances(serviceName string) []registry.Ser logger.Infof("could not getChildrenKVList the err is:%v", err) } - return make([]registry.ServiceInstance, 0, 0) + return make([]registry.ServiceInstance, 0) } // GetInstancesByPage will return a page containing instances of ServiceInstance with the serviceName @@ -177,7 +180,7 @@ func (e *etcdV3ServiceDiscovery) GetInstancesByPage(serviceName string, offset i res = append(res, all[i]) } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // GetHealthyInstancesByPage will return a page containing instances of ServiceInstance. @@ -199,7 +202,7 @@ func (e *etcdV3ServiceDiscovery) GetHealthyInstancesByPage(serviceName string, o } i++ } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // Batch get all instances by the specified service names @@ -319,5 +322,5 @@ func newEtcdV3ServiceDiscovery(name string) (registry.ServiceDiscovery, error) { descriptor := fmt.Sprintf("etcd-service-discovery[%s]", remoteConfig.Address) - return &etcdV3ServiceDiscovery{descriptor, client, nil, gxset.NewSet(), make(map[string]*etcdv3.EventListener, 0)}, nil + return &etcdV3ServiceDiscovery{descriptor, client, nil, gxset.NewSet(), make(map[string]*etcdv3.EventListener)}, nil } diff --git a/registry/etcdv3/service_discovery_test.go b/registry/etcdv3/service_discovery_test.go index d8e3f1a2864150cc1f1e8996bc7c53e115dbef45..5609cf7be6831d93b78945fa7bf8959c321f0d1e 100644 --- a/registry/etcdv3/service_discovery_test.go +++ b/registry/etcdv3/service_discovery_test.go @@ -75,6 +75,6 @@ func Test_newEtcdV3ServiceDiscovery(t *testing.T) { func TestEtcdV3ServiceDiscovery_GetDefaultPageSize(t *testing.T) { setUp() - serviceDiscovry := &etcdV3ServiceDiscovery{} - assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize()) + serviceDiscovery := &etcdV3ServiceDiscovery{} + assert.Equal(t, registry.DefaultPageSize, serviceDiscovery.GetDefaultPageSize()) } diff --git a/registry/event.go b/registry/event.go index 39fb00c740ef2e70e2cd6768fa4a4bb3226f832c..2ebc375597aef67b7605073b852ce53f68cb58e4 100644 --- a/registry/event.go +++ b/registry/event.go @@ -40,12 +40,37 @@ func init() { // ServiceEvent includes create, update, delete event type ServiceEvent struct { Action remoting.EventType - Service common.URL + Service *common.URL + // store the key for Service.Key() + key string + // If the url is updated, such as Merged. + updated bool } // String return the description of event -func (e ServiceEvent) String() string { - return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}}", e.Action, e.Service) +func (e *ServiceEvent) String() string { + return fmt.Sprintf("ServiceEvent{Action{%s}, Path{%s}, Key{%s}}", e.Action, e.Service, e.key) +} + +// Update() update the url with the merged URL. Work with Updated() can reduce the process of some merging URL. +func (e *ServiceEvent) Update(url *common.URL) { + e.Service = url + e.updated = true +} + +// Updated() check if the url is updated. +// If the serviceEvent is updated, then it don't need merge url again. +func (e *ServiceEvent) Updated() bool { + return e.updated +} + +// Key() generate the key for service.Key(). It is cached once. +func (e *ServiceEvent) Key() string { + if len(e.key) > 0 { + return e.key + } + e.key = e.Service.Key() + return e.key } // ServiceInstancesChangedEvent represents service instances make some changing diff --git a/registry/event/event_publishing_service_deiscovery_test.go b/registry/event/event_publishing_service_deiscovery_test.go index 54752c03c0de598226270b27c8d7d0f3621d07d1..504f7b5faed374795bd534c9e97cf5b42fc94d4c 100644 --- a/registry/event/event_publishing_service_deiscovery_test.go +++ b/registry/event/event_publishing_service_deiscovery_test.go @@ -24,7 +24,7 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) diff --git a/registry/event/event_publishing_service_discovery.go b/registry/event/event_publishing_service_discovery.go index 3ee2f4a44946065cdf7489abc391df41f251d810..773eee6e83c6b3e24c293053b55b028d0e7b2e9a 100644 --- a/registry/event/event_publishing_service_discovery.go +++ b/registry/event/event_publishing_service_discovery.go @@ -19,7 +19,7 @@ package event import ( gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" ) import ( diff --git a/registry/event/metadata_service_url_params_customizer_test.go b/registry/event/metadata_service_url_params_customizer_test.go index 98ae2df883f590f4c3e4b379bb5a0fcbe46d946c..c041232b7836986db034da9c61acabc64050757e 100644 --- a/registry/event/metadata_service_url_params_customizer_test.go +++ b/registry/event/metadata_service_url_params_customizer_test.go @@ -70,27 +70,27 @@ func (m *mockMetadataService) ServiceName() (string, error) { panic("implement me") } -func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { +func (m *mockMetadataService) ExportURL(*common.URL) (bool, error) { panic("implement me") } -func (m *mockMetadataService) UnexportURL(url common.URL) error { +func (m *mockMetadataService) UnexportURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { +func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) { panic("implement me") } -func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { +func (m *mockMetadataService) UnsubscribeURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { +func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { +func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) { return m.urls, nil } @@ -98,8 +98,8 @@ func (m *mockMetadataService) MethodMapper() map[string]string { panic("implement me") } -func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { - res := make([]common.URL, 0, len(m.urls)) +func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) { + var res []*common.URL for _, ui := range m.urls { u, _ := common.NewURL(ui.(string)) res = append(res, u) @@ -107,15 +107,15 @@ func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { return res, nil } -func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { +func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) { panic("implement me") } -func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) { panic("implement me") } -func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { +func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) { panic("implement me") } diff --git a/registry/file/service_discovery.go b/registry/file/service_discovery.go index 59c5cf9a89140be537e4f08816e8a080a7f6a6fc..768a1c2a3d8a208d637e374632da530a43465a4e 100644 --- a/registry/file/service_discovery.go +++ b/registry/file/service_discovery.go @@ -28,7 +28,7 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" perrors "github.com/pkg/errors" ) @@ -69,8 +69,8 @@ func newFileSystemServiceDiscovery(name string) (registry.ServiceDiscovery, erro 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) + url.AddParamAvoidNil(file.ConfigCenterDirParamName, p) + c, err := fdcf.GetDynamicConfiguration(url) if err != nil { return nil, perrors.WithStack(err) } @@ -82,7 +82,9 @@ func newFileSystemServiceDiscovery(name string) (registry.ServiceDiscovery, erro } extension.AddCustomShutdownCallback(func() { - sd.Destroy() + if err := sd.Destroy(); err != nil { + logger.Warnf("sd.Destroy() = error:%v", err) + } }) for _, v := range sd.GetServices().Values() { @@ -210,7 +212,7 @@ func (fssd *fileSystemServiceDiscovery) GetInstances(serviceName string) []regis 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) + return make([]registry.ServiceInstance, 0) } res := make([]registry.ServiceInstance, 0, set.Size()) @@ -221,7 +223,7 @@ func (fssd *fileSystemServiceDiscovery) GetInstances(serviceName string) []regis 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) + return make([]registry.ServiceInstance, 0) } dsi := ®istry.DefaultServiceInstance{} @@ -230,7 +232,7 @@ func (fssd *fileSystemServiceDiscovery) GetInstances(serviceName string) []regis 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) + return make([]registry.ServiceInstance, 0) } res = append(res, dsi) diff --git a/registry/file/service_discovery_test.go b/registry/file/service_discovery_test.go index 0bffcae31d039a49d8cf696a6de2f6858c42ada2..0062eae32cc04fd58ae4398ac7a28aabc54892c1 100644 --- a/registry/file/service_discovery_test.go +++ b/registry/file/service_discovery_test.go @@ -44,7 +44,10 @@ func TestNewFileSystemServiceDiscoveryAndDestroy(t *testing.T) { serviceDiscovery, err := newFileSystemServiceDiscovery(testName) assert.NoError(t, err) assert.NotNil(t, serviceDiscovery) - defer serviceDiscovery.Destroy() + defer func() { + err = serviceDiscovery.Destroy() + assert.Nil(t, err) + }() } func TestCURDFileSystemServiceDiscovery(t *testing.T) { @@ -78,8 +81,11 @@ func TestCURDFileSystemServiceDiscovery(t *testing.T) { assert.NoError(t, err) err = serviceDiscovery.Register(r1) - - defer serviceDiscovery.Destroy() + assert.NoError(t, err) + defer func() { + err = serviceDiscovery.Destroy() + assert.NoError(t, err) + }() } func prepareData() { diff --git a/registry/kubernetes/listener.go b/registry/kubernetes/listener.go index 24c8d81614c7dd323e4f23ec7de5d28b24eecb70..e20b7c7e7d2f72349f7ec268bbb3b09f135dc6ed 100644 --- a/registry/kubernetes/listener.go +++ b/registry/kubernetes/listener.go @@ -65,7 +65,7 @@ func (l *dataListener) DataChange(eventType remoting.Event) bool { } for _, v := range l.interestedURL { - if serviceURL.URLEqual(*v) { + if serviceURL.URLEqual(v) { l.listener.Process( &config_center.ConfigChangeEvent{ Key: eventType.Path, @@ -114,7 +114,7 @@ func (l *configurationListener) Next() (*registry.ServiceEvent, error) { } continue } - return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil + return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil } } } diff --git a/registry/kubernetes/listener_test.go b/registry/kubernetes/listener_test.go index ccaaf80907f9eb3d4956758374f518c66fa613d5..ef6e8fb1fceed9aa84a0f9099b8c782ceb624910 100644 --- a/registry/kubernetes/listener_test.go +++ b/registry/kubernetes/listener_test.go @@ -31,142 +31,10 @@ import ( "github.com/apache/dubbo-go/remoting" ) -var clientPodJsonData = `{ - "apiVersion": "v1", - "kind": "Pod", - "metadata": { - "annotations": { - "dubbo.io/annotation": "W3siayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJibyIsInYiOiIifSx7ImsiOiIvZHViYm8vY29tLmlrdXJlbnRvLnVzZXIuVXNlclByb3ZpZGVyIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvcHJvdmlkZXJzIiwidiI6IiJ9LHsiayI6Ii9kdWJiby9jb20uaWt1cmVudG8udXNlci5Vc2VyUHJvdmlkZXIvY29uc3VtZXJzL2NvbnN1bWVyJTNBJTJGJTJGMTcyLjE3LjAuOCUyRlVzZXJQcm92aWRlciUzRmNhdGVnb3J5JTNEY29uc3VtZXJzJTI2ZHViYm8lM0RkdWJib2dvLWNvbnN1bWVyLTIuNi4wJTI2cHJvdG9jb2wlM0RkdWJibyIsInYiOiIifV0=" - }, - "creationTimestamp": "2020-03-13T03:38:57Z", - "labels": { - "dubbo.io/label": "dubbo.io-value" - }, - "name": "client", - "namespace": "default", - "resourceVersion": "2449700", - "selfLink": "/api/v1/namespaces/default/pods/client", - "uid": "3ec394f5-dcc6-49c3-8061-57b4b2b41344" - }, - "spec": { - "containers": [ - { - "env": [ - { - "name": "NAMESPACE", - "valueFrom": { - "fieldRef": { - "apiVersion": "v1", - "fieldPath": "metadata.namespace" - } - } - } - ], - "image": "registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client", - "imagePullPolicy": "Always", - "name": "client", - "resources": {}, - "terminationMessagePath": "/dev/termination-log", - "terminationMessagePolicy": "File", - "volumeMounts": [ - { - "mountPath": "/var/run/secrets/kubernetes.io/serviceaccount", - "name": "dubbo-sa-token-l2lzh", - "readOnly": true - } - ] - } - ], - "dnsPolicy": "ClusterFirst", - "enableServiceLinks": true, - "nodeName": "minikube", - "priority": 0, - "restartPolicy": "Never", - "schedulerName": "default-scheduler", - "securityContext": {}, - "serviceAccount": "dubbo-sa", - "serviceAccountName": "dubbo-sa", - "terminationGracePeriodSeconds": 30, - "tolerations": [ - { - "effect": "NoExecute", - "key": "node.kubernetes.io/not-ready", - "operator": "Exists", - "tolerationSeconds": 300 - }, - { - "effect": "NoExecute", - "key": "node.kubernetes.io/unreachable", - "operator": "Exists", - "tolerationSeconds": 300 - } - ], - "volumes": [ - { - "name": "dubbo-sa-token-l2lzh", - "secret": { - "defaultMode": 420, - "secretName": "dubbo-sa-token-l2lzh" - } - } - ] - }, - "status": { - "conditions": [ - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:38:57Z", - "status": "True", - "type": "Initialized" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:40:18Z", - "status": "True", - "type": "Ready" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:40:18Z", - "status": "True", - "type": "ContainersReady" - }, - { - "lastProbeTime": null, - "lastTransitionTime": "2020-03-13T03:38:57Z", - "status": "True", - "type": "PodScheduled" - } - ], - "containerStatuses": [ - { - "containerID": "docker://2870d6abc19ca7fe22ca635ebcfac5d48c6d5550a659bafd74fb48104f6dfe3c", - "image": "registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client:latest", - "imageID": "docker-pullable://registry.cn-hangzhou.aliyuncs.com/scottwang/dubbogo-client@sha256:1f075131f708a0d400339e81549d7c4d4ed917ab0b6bd38ef458dd06ad25a559", - "lastState": {}, - "name": "client", - "ready": true, - "restartCount": 0, - "state": { - "running": { - "startedAt": "2020-03-13T03:40:17Z" - } - } - } - ], - "hostIP": "10.0.2.15", - "phase": "Running", - "podIP": "172.17.0.8", - "qosClass": "BestEffort", - "startTime": "2020-03-13T03:38:57Z" - } -} -` - func Test_DataChange(t *testing.T) { listener := NewRegistryDataListener(&MockDataListener{}) url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") - listener.AddInterestedURL(&url) + listener.AddInterestedURL(url) int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"}) assert.Equal(t, true, int) } @@ -179,7 +47,7 @@ func TestDataChange(t *testing.T) { listener := NewRegistryDataListener(&MockDataListener{}) url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") - listener.AddInterestedURL(&url) + listener.AddInterestedURL(url) if !listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-2.6.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"}) { t.Fatal("data change not ok") } diff --git a/registry/kubernetes/registry.go b/registry/kubernetes/registry.go index 88895855689df42f2cd66ec158e6b8f2806ef6f0..a26478f2ee5e82cc7cbeaf7996a0febb49238e91 100644 --- a/registry/kubernetes/registry.go +++ b/registry/kubernetes/registry.go @@ -19,15 +19,13 @@ package kubernetes import ( "fmt" - "os" "path" "sync" "time" ) import ( - "github.com/apache/dubbo-getty" - "github.com/dubbogo/gost/net" + getty "github.com/apache/dubbo-getty" perrors "github.com/pkg/errors" v1 "k8s.io/api/core/v1" ) @@ -41,11 +39,6 @@ import ( "github.com/apache/dubbo-go/remoting/kubernetes" ) -var ( - processID = "" - localIP = "" -) - const ( Name = "kubernetes" ConnDelay = 3 @@ -53,8 +46,8 @@ const ( ) func init() { - processID = fmt.Sprintf("%d", os.Getpid()) - localIP, _ = gxnet.GetLocalIP() + //processID = fmt.Sprintf("%d", os.Getpid()) + //localIP = common.GetLocalIp() extension.SetRegistry(Name, newKubernetesRegistry) } @@ -209,7 +202,7 @@ func (r *kubernetesRegistry) HandleClientRestart() { failTimes int ) - defer r.WaitGroup() + defer r.WaitGroup().Done() LOOP: for { select { diff --git a/registry/kubernetes/registry_test.go b/registry/kubernetes/registry_test.go index 347dadcd2c462e3a1caf9829b051a665ec61e8e3..a816b035c593b444665d3f259b17af1d4e5d53eb 100644 --- a/registry/kubernetes/registry_test.go +++ b/registry/kubernetes/registry_test.go @@ -231,7 +231,7 @@ func getTestRegistry(t *testing.T) *kubernetesRegistry { if err != nil { t.Fatal(err) } - out, err := newMockKubernetesRegistry(®url, pl) + out, err := newMockKubernetesRegistry(regurl, pl) if err != nil { t.Fatal(err) } @@ -268,7 +268,7 @@ func TestSubscribe(t *testing.T) { t.Fatal(err) } - listener, err := r.DoSubscribe(&url) + listener, err := r.DoSubscribe(url) if err != nil { t.Fatal(err) } @@ -280,7 +280,7 @@ func TestSubscribe(t *testing.T) { defer wg.Done() registerErr := r.Register(url) if registerErr != nil { - t.Fatal(registerErr) + t.Error(registerErr) } }() @@ -301,7 +301,7 @@ func TestConsumerDestroy(t *testing.T) { common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - _, err := r.DoSubscribe(&url) + _, err := r.DoSubscribe(url) if err != nil { t.Fatal(err) } @@ -336,7 +336,7 @@ func TestNewRegistry(t *testing.T) { if err != nil { t.Fatal(err) } - _, err = newKubernetesRegistry(®Url) + _, err = newKubernetesRegistry(regUrl) if err == nil { t.Fatal("not in cluster, should be a err") } diff --git a/registry/mock_registry.go b/registry/mock_registry.go index 10561d0f49e995c94c93fa0463fc0b0421ff6e20..6287bb0d168f18f166618b7c6505b2f8b012be04 100644 --- a/registry/mock_registry.go +++ b/registry/mock_registry.go @@ -18,6 +18,7 @@ package registry import ( + "fmt" "time" ) @@ -32,14 +33,16 @@ import ( // MockRegistry is used as mock registry type MockRegistry struct { - listener *listener - destroyed *atomic.Bool + listener *listener + destroyed *atomic.Bool + allAddress chan []*ServiceEvent } // NewMockRegistry creates a mock registry func NewMockRegistry(url *common.URL) (Registry, error) { registry := &MockRegistry{ - destroyed: atomic.NewBool(false), + destroyed: atomic.NewBool(false), + allAddress: make(chan []*ServiceEvent), } listener := &listener{count: 0, registry: registry, listenChan: make(chan *ServiceEvent)} registry.listener = listener @@ -47,12 +50,12 @@ func NewMockRegistry(url *common.URL) (Registry, error) { } // Register is used as a mock registry -func (*MockRegistry) Register(url common.URL) error { +func (*MockRegistry) Register(url *common.URL) error { return nil } // nolint -func (r *MockRegistry) UnRegister(conf common.URL) error { +func (r *MockRegistry) UnRegister(conf *common.URL) error { return nil } @@ -68,8 +71,8 @@ func (r *MockRegistry) IsAvailable() bool { } // nolint -func (r *MockRegistry) GetUrl() common.URL { - return common.URL{} +func (r *MockRegistry) GetUrl() *common.URL { + return nil } func (r *MockRegistry) subscribe(*common.URL) (Listener, error) { @@ -80,22 +83,12 @@ func (r *MockRegistry) subscribe(*common.URL) (Listener, error) { func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) error { go func() { for { - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - time.Sleep(time.Duration(3) * time.Second) - return - } - - listener, err := r.subscribe(url) - if err != nil { - if !r.IsAvailable() { - logger.Warnf("event listener game over.") - return - } - time.Sleep(time.Duration(3) * time.Second) + t, listener := r.checkLoopSubscribe(url) + if t == 0 { continue + } else if t == -1 { + return } - for { serviceEvent, err := listener.Next() if err != nil { @@ -109,6 +102,26 @@ func (r *MockRegistry) Subscribe(url *common.URL, notifyListener NotifyListener) } } }() + go func() { + for { + t, _ := r.checkLoopSubscribe(url) + if t == 0 { + continue + } else if t == -1 { + return + } + + for { + select { + case e := <-r.allAddress: + notifyListener.NotifyAll(e, func() { + fmt.Print("notify all ok") + }) + break + } + } + } + }() return nil } @@ -124,10 +137,7 @@ type listener struct { } func (l *listener) Next() (*ServiceEvent, error) { - select { - case e := <-l.listenChan: - return e, nil - } + return <-l.listenChan, nil } func (*listener) Close() { @@ -138,3 +148,27 @@ func (*listener) Close() { func (r *MockRegistry) MockEvent(event *ServiceEvent) { r.listener.listenChan <- event } + +// nolint +func (r *MockRegistry) MockEvents(events []*ServiceEvent) { + r.allAddress <- events +} + +func (r *MockRegistry) checkLoopSubscribe(url *common.URL) (int, Listener) { + if !r.IsAvailable() { + logger.Warnf("event listener game over.") + time.Sleep(time.Duration(3) * time.Second) + return -1, nil + } + + listener, err := r.subscribe(url) + if err != nil { + if !r.IsAvailable() { + logger.Warnf("event listener game over.") + return -1, nil + } + time.Sleep(time.Duration(3) * time.Second) + return 0, nil + } + return 1, listener +} diff --git a/registry/nacos/listener.go b/registry/nacos/listener.go index cf6a73d38fbefc1ecb6a80e4911bb0d8fb13a6f6..d2b574648fa2fea5664fdf35b457aa99f14e323f 100644 --- a/registry/nacos/listener.go +++ b/registry/nacos/listener.go @@ -43,7 +43,7 @@ import ( type nacosListener struct { namingClient naming_client.INamingClient - listenUrl common.URL + listenUrl *common.URL events chan *config_center.ConfigChangeEvent instanceMap map[string]model.Instance cacheLock sync.Mutex @@ -52,7 +52,7 @@ type nacosListener struct { } // NewRegistryDataListener creates a data listener for nacos -func NewNacosListener(url common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) { +func NewNacosListener(url *common.URL, namingClient naming_client.INamingClient) (*nacosListener, error) { listener := &nacosListener{ namingClient: namingClient, listenUrl: url, events: make(chan *config_center.ConfigChangeEvent, 32), @@ -115,14 +115,14 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error) logger.Errorf("nacos subscribe callback error:%s , subscribe:%+v ", err.Error(), nl.subscribeParam) return } - nl.cacheLock.Lock() - defer nl.cacheLock.Unlock() + addInstances := make([]model.Instance, 0, len(services)) delInstances := make([]model.Instance, 0, len(services)) updateInstances := make([]model.Instance, 0, len(services)) - newInstanceMap := make(map[string]model.Instance, len(services)) + nl.cacheLock.Lock() + defer nl.cacheLock.Unlock() for i := range services { if !services[i].Enable || !services[i].Valid { // instance is not available,so ignore it @@ -154,25 +154,25 @@ func (nl *nacosListener) Callback(services []model.SubscribeService, err error) for i := range addInstances { newUrl := generateUrl(addInstances[i]) if newUrl != nil { - nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeAdd}) + nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeAdd}) } } for i := range delInstances { newUrl := generateUrl(delInstances[i]) if newUrl != nil { - nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeDel}) + nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeDel}) } } for i := range updateInstances { newUrl := generateUrl(updateInstances[i]) if newUrl != nil { - nl.process(&config_center.ConfigChangeEvent{Value: *newUrl, ConfigType: remoting.EventTypeUpdate}) + nl.process(&config_center.ConfigChangeEvent{Value: newUrl, ConfigType: remoting.EventTypeUpdate}) } } } -func getSubscribeName(url common.URL) string { +func getSubscribeName(url *common.URL) string { var buffer bytes.Buffer buffer.Write([]byte(common.DubboNodes[common.PROVIDER])) @@ -188,7 +188,9 @@ func (nl *nacosListener) startListen() error { } serviceName := getSubscribeName(nl.listenUrl) nl.subscribeParam = &vo.SubscribeParam{ServiceName: serviceName, SubscribeCallback: nl.Callback} - go nl.namingClient.Subscribe(nl.subscribeParam) + go func() { + _ = nl.namingClient.Subscribe(nl.subscribeParam) + }() return nil } @@ -210,7 +212,7 @@ func (nl *nacosListener) Next() (*registry.ServiceEvent, error) { case e := <-nl.events: logger.Debugf("got nacos event %s", e) - return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil + return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil } } } diff --git a/registry/nacos/registry.go b/registry/nacos/registry.go index 411090820c7682ab9c3b5576ea8ad5207c2c899f..e9a4bd317ef5482ff98b4f1a9d3920d5b86aad42 100644 --- a/registry/nacos/registry.go +++ b/registry/nacos/registry.go @@ -26,7 +26,6 @@ import ( ) import ( - gxnet "github.com/dubbogo/gost/net" "github.com/nacos-group/nacos-sdk-go/clients" "github.com/nacos-group/nacos-sdk-go/clients/naming_client" nacosConstant "github.com/nacos-group/nacos-sdk-go/common/constant" @@ -52,23 +51,23 @@ const ( ) func init() { - localIP, _ = gxnet.GetLocalIP() + localIP = common.GetLocalIp() extension.SetRegistry(constant.NACOS_KEY, newNacosRegistry) } type nacosRegistry struct { *common.URL namingClient naming_client.INamingClient - registryUrls []common.URL + registryUrls []*common.URL } -func getCategory(url common.URL) string { +func getCategory(url *common.URL) string { role, _ := strconv.Atoi(url.GetParam(constant.ROLE_KEY, strconv.Itoa(constant.NACOS_DEFAULT_ROLETYPE))) category := common.DubboNodes[role] return category } -func getServiceName(url common.URL) string { +func getServiceName(url *common.URL) string { var buffer bytes.Buffer buffer.Write([]byte(getCategory(url))) @@ -78,7 +77,7 @@ func getServiceName(url common.URL) string { return buffer.String() } -func appendParam(target *bytes.Buffer, url common.URL, key string) { +func appendParam(target *bytes.Buffer, url *common.URL, key string) { value := url.GetParam(key, "") if strings.TrimSpace(value) != "" { target.Write([]byte(constant.NACOS_SERVICE_NAME_SEPARATOR)) @@ -86,7 +85,7 @@ func appendParam(target *bytes.Buffer, url common.URL, key string) { } } -func createRegisterParam(url common.URL, serviceName string) vo.RegisterInstanceParam { +func createRegisterParam(url *common.URL, serviceName string) vo.RegisterInstanceParam { category := getCategory(url) params := make(map[string]string) @@ -119,7 +118,7 @@ func createRegisterParam(url common.URL, serviceName string) vo.RegisterInstance } // Register will register the service @url to its nacos registry center -func (nr *nacosRegistry) Register(url common.URL) error { +func (nr *nacosRegistry) Register(url *common.URL) error { serviceName := getServiceName(url) param := createRegisterParam(url, serviceName) isRegistry, err := nr.namingClient.RegisterInstance(param) @@ -133,7 +132,7 @@ func (nr *nacosRegistry) Register(url common.URL) error { return nil } -func createDeregisterParam(url common.URL, serviceName string) vo.DeregisterInstanceParam { +func createDeregisterParam(url *common.URL, serviceName string) vo.DeregisterInstanceParam { if len(url.Ip) == 0 { url.Ip = localIP } @@ -149,7 +148,7 @@ func createDeregisterParam(url common.URL, serviceName string) vo.DeregisterInst } } -func (nr *nacosRegistry) DeRegister(url common.URL) error { +func (nr *nacosRegistry) DeRegister(url *common.URL) error { serviceName := getServiceName(url) param := createDeregisterParam(url, serviceName) isDeRegistry, err := nr.namingClient.DeregisterInstance(param) @@ -163,16 +162,21 @@ func (nr *nacosRegistry) DeRegister(url common.URL) error { } // UnRegister -func (nr *nacosRegistry) UnRegister(conf common.URL) error { +func (nr *nacosRegistry) UnRegister(conf *common.URL) error { return perrors.New("UnRegister is not support in nacosRegistry") } func (nr *nacosRegistry) subscribe(conf *common.URL) (registry.Listener, error) { - return NewNacosListener(*conf, nr.namingClient) + return NewNacosListener(conf, nr.namingClient) } // subscribe from registry func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.NotifyListener) error { + role, _ := strconv.Atoi(nr.URL.GetParam(constant.ROLE_KEY, "")) + if role != common.CONSUMER { + return nil + } + for { if !nr.IsAvailable() { logger.Warnf("event listener game over.") @@ -203,7 +207,6 @@ func (nr *nacosRegistry) Subscribe(url *common.URL, notifyListener registry.Noti } } - return nil } // UnSubscribe : @@ -212,8 +215,8 @@ func (nr *nacosRegistry) UnSubscribe(url *common.URL, notifyListener registry.No } // GetUrl gets its registration URL -func (nr *nacosRegistry) GetUrl() common.URL { - return *nr.URL +func (nr *nacosRegistry) GetUrl() *common.URL { + return nr.URL } // IsAvailable determines nacos registry center whether it is available @@ -244,12 +247,12 @@ func newNacosRegistry(url *common.URL) (registry.Registry, error) { if err != nil { return &nacosRegistry{}, err } - registry := &nacosRegistry{ + tmpRegistry := &nacosRegistry{ URL: url, namingClient: client, - registryUrls: []common.URL{}, + registryUrls: []*common.URL{}, } - return registry, nil + return tmpRegistry, nil } // getNacosConfig will return the nacos config @@ -276,7 +279,7 @@ func getNacosConfig(url *common.URL) (map[string]interface{}, error) { Port: uint64(port), }) } - configMap["serverConfigs"] = serverConfigs + configMap[nacosConstant.KEY_SERVER_CONFIGS] = serverConfigs var clientConfig nacosConstant.ClientConfig timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) @@ -288,8 +291,17 @@ func getNacosConfig(url *common.URL) (map[string]interface{}, error) { clientConfig.CacheDir = url.GetParam(constant.NACOS_CACHE_DIR_KEY, "") clientConfig.LogDir = url.GetParam(constant.NACOS_LOG_DIR_KEY, "") clientConfig.Endpoint = url.GetParam(constant.NACOS_ENDPOINT, "") - clientConfig.NotLoadCacheAtStart = true - configMap["clientConfig"] = clientConfig + clientConfig.NamespaceId = url.GetParam(constant.NACOS_NAMESPACE_ID, "") + + //enable local cache when nacos can not connect. + notLoadCache, err := strconv.ParseBool(url.GetParam(constant.NACOS_NOT_LOAD_LOCAL_CACHE, "false")) + if err != nil { + logger.Errorf("ParseBool - error: %v", err) + notLoadCache = false + } + clientConfig.NotLoadCacheAtStart = notLoadCache + + configMap[nacosConstant.KEY_CLIENT_CONFIG] = clientConfig return configMap, nil } diff --git a/registry/nacos/registry_test.go b/registry/nacos/registry_test.go index d0311b200b27081c60bc97b2307a54774ca977bd..b82820577144dfe98492165372a925500c20abfb 100644 --- a/registry/nacos/registry_test.go +++ b/registry/nacos/registry_test.go @@ -19,9 +19,11 @@ package nacos import ( "encoding/json" + "net/http" "net/url" "strconv" "testing" + "time" ) import ( @@ -35,7 +37,14 @@ import ( ) func TestNacosRegistry_Register(t *testing.T) { - regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if !checkNacosServerAlive() { + return + } + regurlMap := url.Values{} + regurlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) + regurlMap.Set(constant.NACOS_NOT_LOAD_LOCAL_CACHE, "true") + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParams(regurlMap)) + urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -44,7 +53,7 @@ func TestNacosRegistry_Register(t *testing.T) { urlMap.Set(constant.CLUSTER_KEY, "mock") testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) - reg, err := newNacosRegistry(®url) + reg, err := newNacosRegistry(regurl) assert.Nil(t, err) if err != nil { t.Errorf("new nacos registry error:%s \n", err.Error()) @@ -64,7 +73,14 @@ func TestNacosRegistry_Register(t *testing.T) { } func TestNacosRegistry_Subscribe(t *testing.T) { - regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if !checkNacosServerAlive() { + return + } + regurlMap := url.Values{} + regurlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) + regurlMap.Set(constant.NACOS_NOT_LOAD_LOCAL_CACHE, "true") + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParams(regurlMap)) + urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -74,7 +90,7 @@ func TestNacosRegistry_Subscribe(t *testing.T) { urlMap.Set(constant.NACOS_PATH_KEY, "") testUrl, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) - reg, _ := newNacosRegistry(®url) + reg, _ := newNacosRegistry(regurl) err := reg.Register(testUrl) assert.Nil(t, err) if err != nil { @@ -83,8 +99,8 @@ func TestNacosRegistry_Subscribe(t *testing.T) { } regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) - reg2, _ := newNacosRegistry(®url) - listener, err := reg2.(*nacosRegistry).subscribe(&testUrl) + reg2, _ := newNacosRegistry(regurl) + listener, err := reg2.(*nacosRegistry).subscribe(testUrl) assert.Nil(t, err) if err != nil { t.Errorf("subscribe error:%s \n", err.Error()) @@ -102,7 +118,14 @@ func TestNacosRegistry_Subscribe(t *testing.T) { } func TestNacosRegistry_Subscribe_del(t *testing.T) { - regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + if !checkNacosServerAlive() { + return + } + regurlMap := url.Values{} + regurlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) + regurlMap.Set(constant.NACOS_NOT_LOAD_LOCAL_CACHE, "true") + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParams(regurlMap)) + urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -113,7 +136,7 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) url2, _ := common.NewURL("dubbo://127.0.0.2:20000/com.ikurento.user.UserProvider", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) - reg, _ := newNacosRegistry(®url) + reg, _ := newNacosRegistry(regurl) err := reg.Register(url1) assert.Nil(t, err) if err != nil { @@ -128,8 +151,8 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { } regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) - reg2, _ := newNacosRegistry(®url) - listener, err := reg2.(*nacosRegistry).subscribe(&url1) + reg2, _ := newNacosRegistry(regurl) + listener, err := reg2.(*nacosRegistry).subscribe(url1) assert.Nil(t, err) if err != nil { t.Errorf("subscribe error:%s \n", err.Error()) @@ -156,7 +179,9 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { nacosReg := reg.(*nacosRegistry) //deregister instance to mock instance offline - nacosReg.namingClient.DeregisterInstance(vo.DeregisterInstanceParam{Ip: "127.0.0.2", Port: 20000, ServiceName: "providers:com.ikurento.user.UserProvider:2.0.0:guangzhou-idc"}) + _, err = nacosReg.namingClient.DeregisterInstance(vo.DeregisterInstanceParam{Ip: "127.0.0.2", Port: 20000, + ServiceName: "providers:com.ikurento.user.UserProvider:2.0.0:guangzhou-idc"}) + assert.NoError(t, err) serviceEvent3, _ := listener.Next() assert.NoError(t, err) @@ -168,7 +193,11 @@ func TestNacosRegistry_Subscribe_del(t *testing.T) { } func TestNacosListener_Close(t *testing.T) { - regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regurlMap := url.Values{} + regurlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) + regurlMap.Set(constant.NACOS_NOT_LOAD_LOCAL_CACHE, "true") + regurl, _ := common.NewURL("registry://console.nacos.io:80", common.WithParams(regurlMap)) + urlMap := url.Values{} urlMap.Set(constant.GROUP_KEY, "guangzhou-idc") urlMap.Set(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER)) @@ -177,8 +206,8 @@ func TestNacosListener_Close(t *testing.T) { urlMap.Set(constant.CLUSTER_KEY, "mock") urlMap.Set(constant.NACOS_PATH_KEY, "") url1, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider2", common.WithParams(urlMap), common.WithMethods([]string{"GetUser", "AddUser"})) - reg, _ := newNacosRegistry(®url) - listener, err := reg.(*nacosRegistry).subscribe(&url1) + reg, _ := newNacosRegistry(regurl) + listener, err := reg.(*nacosRegistry).subscribe(url1) assert.Nil(t, err) if err != nil { t.Errorf("subscribe error:%s \n", err.Error()) @@ -188,3 +217,11 @@ func TestNacosListener_Close(t *testing.T) { _, err = listener.Next() assert.NotNil(t, err) } + +func checkNacosServerAlive() bool { + c := http.Client{Timeout: time.Second} + if _, err := c.Get("http://console.nacos.io/nacos/"); err != nil { + return false + } + return true +} diff --git a/registry/nacos/service_discovery.go b/registry/nacos/service_discovery.go index 0e5ad8e6990856aeb0dfdde72f9c3f7fdae3e985..4533e7bc6b1427f1713e8b61bf2f125c0d0bde99 100644 --- a/registry/nacos/service_discovery.go +++ b/registry/nacos/service_discovery.go @@ -24,7 +24,7 @@ import ( import ( "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/page" + "github.com/dubbogo/gost/hash/page" "github.com/nacos-group/nacos-sdk-go/clients/naming_client" "github.com/nacos-group/nacos-sdk-go/model" "github.com/nacos-group/nacos-sdk-go/vo" @@ -141,8 +141,9 @@ func (n *nacosServiceDiscovery) GetInstances(serviceName string) []registry.Serv GroupName: n.group, }) if err != nil { - logger.Errorf("Could not query the instances for service: " + serviceName + ", group: " + n.group) - return make([]registry.ServiceInstance, 0, 0) + logger.Errorf("Could not query the instances for service: %+v, group: %+v . It happened err %+v", + serviceName, n.group, err) + return make([]registry.ServiceInstance, 0) } res := make([]registry.ServiceInstance, 0, len(instances)) for _, ins := range instances { @@ -174,7 +175,7 @@ func (n *nacosServiceDiscovery) GetInstancesByPage(serviceName string, offset in for i := offset; i < len(all) && i < offset+pageSize; i++ { res = append(res, all[i]) } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // GetHealthyInstancesByPage will return the instance @@ -197,7 +198,7 @@ func (n *nacosServiceDiscovery) GetHealthyInstancesByPage(serviceName string, of } i++ } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // GetRequestInstances will return the instances diff --git a/registry/nacos/service_discovery_test.go b/registry/nacos/service_discovery_test.go index 119be0b3aad3a828470c8c72c775abaada9512c2..aa044ad04a4f5d982914535a2f7fec55beed304c 100644 --- a/registry/nacos/service_discovery_test.go +++ b/registry/nacos/service_discovery_test.go @@ -81,6 +81,9 @@ func TestNacosServiceDiscovery_Destroy(t *testing.T) { } func TestNacosServiceDiscovery_CRUD(t *testing.T) { + if !checkNacosServerAlive() { + return + } prepareData() extension.SetEventDispatcher("mock", func() observer.EventDispatcher { return &dispatcher.MockEventDispatcher{} @@ -148,7 +151,8 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) { assert.Equal(t, 1, len(page.GetData())) instance = page.GetData()[0].(*registry.DefaultServiceInstance) - v, _ := instance.Metadata["a"] + v, ok := instance.Metadata["a"] + assert.True(t, ok) assert.Equal(t, "b", v) // test dispatcher event @@ -162,8 +166,8 @@ func TestNacosServiceDiscovery_CRUD(t *testing.T) { func TestNacosServiceDiscovery_GetDefaultPageSize(t *testing.T) { prepareData() - serviceDiscovry, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, testName) - assert.Equal(t, registry.DefaultPageSize, serviceDiscovry.GetDefaultPageSize()) + serviceDiscovery, _ := extension.GetServiceDiscovery(constant.NACOS_KEY, testName) + assert.Equal(t, registry.DefaultPageSize, serviceDiscovery.GetDefaultPageSize()) } func prepareData() { diff --git a/registry/protocol/protocol.go b/registry/protocol/protocol.go index 69a31ef2f2897013cf67ed6ae320c7550c5fe912..4fcdf93c0fa36812b69daacbbc829ddac5d2e101 100644 --- a/registry/protocol/protocol.go +++ b/registry/protocol/protocol.go @@ -130,31 +130,30 @@ func (proto *registryProtocol) GetRegistries() []registry.Registry { } // Refer provider service from registry center -func (proto *registryProtocol) Refer(url common.URL) protocol.Invoker { +func (proto *registryProtocol) Refer(url *common.URL) protocol.Invoker { var registryUrl = url var serviceUrl = registryUrl.SubURL if registryUrl.Protocol == constant.REGISTRY_PROTOCOL { - protocol := registryUrl.GetParam(constant.REGISTRY_KEY, "") - registryUrl.Protocol = protocol + registryUrl.Protocol = registryUrl.GetParam(constant.REGISTRY_KEY, "") } var reg registry.Registry if regI, loaded := proto.registries.Load(registryUrl.Key()); !loaded { - reg = getRegistry(®istryUrl) + reg = getRegistry(registryUrl) proto.registries.Store(registryUrl.Key(), reg) } else { reg = regI.(registry.Registry) } // new registry directory for store service url from registry - directory, err := extension.GetDefaultRegistryDirectory(®istryUrl, reg) + directory, err := extension.GetDefaultRegistryDirectory(registryUrl, reg) if err != nil { logger.Errorf("consumer service %v create registry directory error, error message is %s, and will return nil invoker!", serviceUrl.String(), err.Error()) return nil } - err = reg.Register(*serviceUrl) + err = reg.Register(serviceUrl) if err != nil { logger.Errorf("consumer service %v register registry %v error, error message is %s", serviceUrl.String(), registryUrl.String(), err.Error()) @@ -194,7 +193,7 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte } registeredProviderUrl := getUrlToRegistry(providerUrl, registryUrl) - err := reg.Register(*registeredProviderUrl) + err := reg.Register(registeredProviderUrl) if err != nil { logger.Errorf("provider service %v register registry %v error, error message is %s", providerUrl.Key(), registryUrl.Key(), err.Error()) @@ -213,7 +212,11 @@ func (proto *registryProtocol) Export(invoker protocol.Invoker) protocol.Exporte logger.Infof("The exporter has not been cached, and will return a new exporter!") } - go reg.Subscribe(overriderUrl, overrideSubscribeListener) + go func() { + if err = reg.Subscribe(overriderUrl, overrideSubscribeListener); err != nil { + logger.Warnf("reg.subscribe(overriderUrl:%v) = error:%v", overriderUrl, err) + } + }() return cachedExporter.(protocol.Exporter) } @@ -241,15 +244,20 @@ func newOverrideSubscribeListener(overriderUrl *common.URL, invoker protocol.Inv } // Notify will be triggered when a service change notification is received. -func (nl *overrideSubscribeListener) Notify(events ...*registry.ServiceEvent) { +func (nl *overrideSubscribeListener) Notify(event *registry.ServiceEvent) { + if isMatched(event.Service, nl.url) && event.Action == remoting.EventTypeAdd { + nl.configurator = extension.GetDefaultConfigurator(event.Service) + nl.doOverrideIfNecessary() + } +} + +func (nl *overrideSubscribeListener) NotifyAll(events []*registry.ServiceEvent, callback func()) { + defer callback() 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() + for _, e := range events { + nl.Notify(e) } } @@ -275,9 +283,9 @@ func (nl *overrideSubscribeListener) doOverrideIfNecessary() { } if currentUrl.String() != providerUrl.String() { - newRegUrl := nl.originInvoker.GetUrl() - setProviderUrl(&newRegUrl, providerUrl) - nl.protocol.reExport(nl.originInvoker, &newRegUrl) + newRegUrl := nl.originInvoker.GetUrl().Clone() + setProviderUrl(newRegUrl, providerUrl) + nl.protocol.reExport(nl.originInvoker, newRegUrl) } } } @@ -369,10 +377,9 @@ func getRegistryUrl(invoker protocol.Invoker) *common.URL { url := invoker.GetUrl() // 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 + url.Protocol = url.GetParam(constant.REGISTRY_KEY, "") } - return &url + return url } func getProviderUrl(invoker protocol.Invoker) *common.URL { @@ -401,7 +408,7 @@ type wrappedInvoker struct { func newWrappedInvoker(invoker protocol.Invoker, url *common.URL) *wrappedInvoker { return &wrappedInvoker{ invoker: invoker, - BaseInvoker: *protocol.NewBaseInvoker(*url), + BaseInvoker: *protocol.NewBaseInvoker(url), } } diff --git a/registry/protocol/protocol_test.go b/registry/protocol/protocol_test.go index 2d6e0248fddb88bfa5ce19546fb7aed703b0fd3c..0fca55221d54c50645bf253c52ec10725f757db3 100644 --- a/registry/protocol/protocol_test.go +++ b/registry/protocol/protocol_test.go @@ -59,7 +59,7 @@ func referNormal(t *testing.T, regProtocol *registryProtocol) { common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) - url.SubURL = &suburl + url.SubURL = suburl invoker := regProtocol.Refer(url) assert.IsType(t, &protocol.BaseInvoker{}, invoker) @@ -84,7 +84,7 @@ func TestMultiRegRefer(t *testing.T) { common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) - url2.SubURL = &suburl2 + url2.SubURL = suburl2 regProtocol.Refer(url2) var count int @@ -105,7 +105,7 @@ func TestOneRegRefer(t *testing.T) { common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) - url2.SubURL = &suburl2 + url2.SubURL = suburl2 regProtocol.Refer(url2) var count int @@ -128,13 +128,13 @@ func exporterNormal(t *testing.T, regProtocol *registryProtocol) *common.URL { common.WithParamsValue(constant.VERSION_KEY, "1.0.0"), ) - url.SubURL = &suburl + url.SubURL = suburl invoker := protocol.NewBaseInvoker(url) exporter := regProtocol.Export(invoker) assert.IsType(t, &protocol.BaseExporter{}, exporter) assert.Equal(t, exporter.GetInvoker().GetUrl().String(), suburl.String()) - return &url + return url } func TestExporter(t *testing.T) { @@ -153,7 +153,7 @@ func TestMultiRegAndMultiProtoExporter(t *testing.T) { common.WithParamsValue(constant.CLUSTER_KEY, "mock"), ) - url2.SubURL = &suburl2 + url2.SubURL = suburl2 invoker2 := protocol.NewBaseInvoker(url2) regProtocol.Export(invoker2) @@ -184,7 +184,7 @@ func TestOneRegAndProtoExporter(t *testing.T) { common.WithParamsValue(constant.VERSION_KEY, "1.0.0"), ) - url2.SubURL = &suburl2 + url2.SubURL = suburl2 invoker2 := protocol.NewBaseInvoker(url2) regProtocol.Export(invoker2) @@ -253,7 +253,7 @@ func TestExportWithOverrideListener(t *testing.T) { func TestExportWithServiceConfig(t *testing.T) { extension.SetDefaultConfigurator(configurator.NewMockConfigurator) ccUrl, _ := common.NewURL("mock://127.0.0.1:1111") - dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl) + dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(ccUrl) common_cfg.GetEnvInstance().SetDynamicConfiguration(dc) regProtocol := newRegistryProtocol() url := exporterNormal(t, regProtocol) @@ -265,6 +265,7 @@ func TestExportWithServiceConfig(t *testing.T) { newUrl := url.SubURL.Clone() newUrl.SetParam(constant.CLUSTER_KEY, "mock1") + v2, _ := regProtocol.bounds.Load(getCacheKey(newUrl)) assert.NotNil(t, v2) } @@ -272,7 +273,7 @@ func TestExportWithServiceConfig(t *testing.T) { func TestExportWithApplicationConfig(t *testing.T) { extension.SetDefaultConfigurator(configurator.NewMockConfigurator) ccUrl, _ := common.NewURL("mock://127.0.0.1:1111") - dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(&ccUrl) + dc, _ := (&config_center.MockDynamicConfigurationFactory{}).GetDynamicConfiguration(ccUrl) common_cfg.GetEnvInstance().SetDynamicConfiguration(dc) regProtocol := newRegistryProtocol() url := exporterNormal(t, regProtocol) @@ -290,7 +291,7 @@ func TestExportWithApplicationConfig(t *testing.T) { func TestGetProviderUrlWithHideKey(t *testing.T) { url, _ := common.NewURL("dubbo://127.0.0.1:1111?a=a1&b=b1&.c=c1&.d=d1&e=e1&protocol=registry") - providerUrl := getUrlToRegistry(&url, &url) + providerUrl := getUrlToRegistry(url, url) assert.NotContains(t, providerUrl.GetParams(), ".c") assert.NotContains(t, providerUrl.GetParams(), ".d") assert.Contains(t, providerUrl.GetParams(), "a") diff --git a/registry/registry.go b/registry/registry.go index 2225d2c1fc6d465bfbf27cd9622d7296e4596d8f..439178390a2fcedba3bd1c9919d9a797f44e3a21 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -34,7 +34,7 @@ type Registry interface { // 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 + Register(url *common.URL) error // UnRegister is required to support the contract: // 1. If it is the persistent stored data of dynamic=false, the @@ -43,7 +43,7 @@ type Registry interface { // 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 - UnRegister(url common.URL) error + UnRegister(url *common.URL) error // Subscribe is required to support the contract: // When creating new registry extension, pls select one of the @@ -72,7 +72,12 @@ type NotifyListener interface { // 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) + Notify(*ServiceEvent) + // NotifyAll the events are complete Service Event List. + // The argument of events []*ServiceEvent is equal to urls []*URL, The Action of serviceEvent should be EventTypeUpdate. + // If your registry center can only get all urls but can't get individual event, you should use this one. + // After notify the address, the callback func will be invoked. + NotifyAll([]*ServiceEvent, func()) } // Listener Deprecated! diff --git a/registry/service_discovery.go b/registry/service_discovery.go index cb7a3c0182ff88995ab9dd6c920523225c3cb36c..5ab768351508ed289cbe9b17a75ea9b8e95fff72 100644 --- a/registry/service_discovery.go +++ b/registry/service_discovery.go @@ -23,7 +23,7 @@ import ( import ( gxset "github.com/dubbogo/gost/container/set" - gxpage "github.com/dubbogo/gost/page" + gxpage "github.com/dubbogo/gost/hash/page" ) const DefaultPageSize = 100 diff --git a/registry/service_instance.go b/registry/service_instance.go index dbb458284d48aa350f2d5d3408b187b437ac81cd..43a1640eead7be1774556f25c9b8f97a75588801 100644 --- a/registry/service_instance.go +++ b/registry/service_instance.go @@ -91,7 +91,7 @@ func (d *DefaultServiceInstance) IsHealthy() bool { // GetMetadata will return the metadata, it will never return nil func (d *DefaultServiceInstance) GetMetadata() map[string]string { if d.Metadata == nil { - d.Metadata = make(map[string]string, 0) + d.Metadata = make(map[string]string) } return d.Metadata } diff --git a/registry/servicediscovery/instance/random/random_service_instance_selector.go b/registry/servicediscovery/instance/random/random_service_instance_selector.go index 3f8f30dc8e9e91f9c75f8ff0611c98bb2f0c7b85..7e4e0eefbbe7f9ce39ee5da1e9f6a9d8ad61c737 100644 --- a/registry/servicediscovery/instance/random/random_service_instance_selector.go +++ b/registry/servicediscovery/instance/random/random_service_instance_selector.go @@ -41,7 +41,7 @@ func NewRandomServiceInstanceSelector() instance.ServiceInstanceSelector { return &RandomServiceInstanceSelector{} } -func (r *RandomServiceInstanceSelector) Select(url common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance { +func (r *RandomServiceInstanceSelector) Select(url *common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance { if len(serviceInstances) == 0 { return nil } diff --git a/registry/servicediscovery/instance/random/random_service_instance_selector_test.go b/registry/servicediscovery/instance/random/random_service_instance_selector_test.go index cddeb42c904131cdc6a62e5142de850410a3ec5a..c53c058be912ba6fd2a66e42883914fdf1d60eb1 100644 --- a/registry/servicediscovery/instance/random/random_service_instance_selector_test.go +++ b/registry/servicediscovery/instance/random/random_service_instance_selector_test.go @@ -52,5 +52,5 @@ func TestRandomServiceInstanceSelector_Select(t *testing.T) { Metadata: nil, }, } - assert.NotNil(t, selector.Select(common.URL{}, serviceInstances)) + assert.NotNil(t, selector.Select(&common.URL{}, serviceInstances)) } diff --git a/registry/servicediscovery/instance/service_instance_selector.go b/registry/servicediscovery/instance/service_instance_selector.go index 82fb3458be2838e9a5780e95be71aa89039b664f..5690ab6c9013d82b1a6ee87797dd7e424e5c07b6 100644 --- a/registry/servicediscovery/instance/service_instance_selector.go +++ b/registry/servicediscovery/instance/service_instance_selector.go @@ -24,5 +24,5 @@ import ( type ServiceInstanceSelector interface { //Select an instance of ServiceInstance by the specified ServiceInstance service instances - Select(url common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance + Select(url *common.URL, serviceInstances []registry.ServiceInstance) registry.ServiceInstance } diff --git a/registry/servicediscovery/service_discovery_registry.go b/registry/servicediscovery/service_discovery_registry.go index 4db2c5aad438784b1289b4473aa8d23c4f8d923f..c97a7f7b51d2344f1a3fc0c59582e089f1e63b62 100644 --- a/registry/servicediscovery/service_discovery_registry.go +++ b/registry/servicediscovery/service_discovery_registry.go @@ -26,7 +26,6 @@ import ( ) import ( - cm "github.com/Workiva/go-datastructures/common" gxset "github.com/dubbogo/gost/container/set" perrors "github.com/pkg/errors" "go.uber.org/atomic" @@ -70,7 +69,7 @@ type serviceDiscoveryRegistry struct { metaDataService service.MetadataService registeredListeners *gxset.HashSet subscribedURLsSynthesizers []synthesizer.SubscribedURLsSynthesizer - serviceRevisionExportedURLsCache map[string]map[string][]common.URL + serviceRevisionExportedURLsCache map[string]map[string][]*common.URL } func newServiceDiscoveryRegistry(url *common.URL) (registry.Registry, error) { @@ -94,13 +93,13 @@ func newServiceDiscoveryRegistry(url *common.URL) (registry.Registry, error) { subscribedServices: subscribedServices, subscribedURLsSynthesizers: subscribedURLsSynthesizers, registeredListeners: gxset.NewSet(), - serviceRevisionExportedURLsCache: make(map[string]map[string][]common.URL, 8), + serviceRevisionExportedURLsCache: make(map[string]map[string][]*common.URL, 8), serviceNameMapping: serviceNameMapping, metaDataService: metaDataService, }, nil } -func (s *serviceDiscoveryRegistry) UnRegister(url common.URL) error { +func (s *serviceDiscoveryRegistry) UnRegister(url *common.URL) error { if !shouldRegister(url) { return nil } @@ -108,10 +107,10 @@ func (s *serviceDiscoveryRegistry) UnRegister(url common.URL) error { } func (s *serviceDiscoveryRegistry) UnSubscribe(url *common.URL, listener registry.NotifyListener) error { - if !shouldSubscribe(*url) { + if !shouldSubscribe(url) { return nil } - return s.metaDataService.UnsubscribeURL(*url) + return s.metaDataService.UnsubscribeURL(url) } func creatServiceDiscovery(url *common.URL) (registry.ServiceDiscovery, error) { @@ -145,8 +144,8 @@ func (s *serviceDiscoveryRegistry) GetServiceDiscovery() registry.ServiceDiscove return s.serviceDiscovery } -func (s *serviceDiscoveryRegistry) GetUrl() common.URL { - return *s.url +func (s *serviceDiscoveryRegistry) GetUrl() *common.URL { + return s.url } func (s *serviceDiscoveryRegistry) IsAvailable() bool { @@ -161,7 +160,7 @@ func (s *serviceDiscoveryRegistry) Destroy() { } } -func (s *serviceDiscoveryRegistry) Register(url common.URL) error { +func (s *serviceDiscoveryRegistry) Register(url *common.URL) error { if !shouldRegister(url) { return nil } @@ -175,17 +174,13 @@ func (s *serviceDiscoveryRegistry) Register(url common.URL) error { logger.Warnf("The URL[%s] has been registry!", url.String()) } - err = s.metaDataService.PublishServiceDefinition(url) - if err != nil { - return perrors.WithMessage(err, "publish the service definition failed. ") - } return s.serviceNameMapping.Map(url.GetParam(constant.INTERFACE_KEY, ""), url.GetParam(constant.GROUP_KEY, ""), url.GetParam(constant.Version, ""), url.Protocol) } -func shouldRegister(url common.URL) bool { +func shouldRegister(url *common.URL) bool { side := url.GetParam(constant.SIDE_KEY, "") if side == constant.PROVIDER_PROTOCOL { return true @@ -195,14 +190,14 @@ func shouldRegister(url common.URL) bool { } func (s *serviceDiscoveryRegistry) Subscribe(url *common.URL, notify registry.NotifyListener) error { - if !shouldSubscribe(*url) { + if !shouldSubscribe(url) { return nil } - _, err := s.metaDataService.SubscribeURL(*url) + _, err := s.metaDataService.SubscribeURL(url) if err != nil { return perrors.WithMessage(err, "subscribe url error: "+url.String()) } - services := s.getServices(*url) + services := s.getServices(url) if services.Empty() { return perrors.Errorf("Should has at least one way to know which services this interface belongs to, "+ "subscription url:%s", url.String()) @@ -218,12 +213,12 @@ func (s *serviceDiscoveryRegistry) Subscribe(url *common.URL, notify registry.No serviceDiscoveryRegistry: s, }, } - s.registerServiceInstancesChangedListener(*url, listener) + s.registerServiceInstancesChangedListener(url, listener) } return nil } -func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url common.URL, listener *registry.ServiceInstancesChangedListener) { +func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url *common.URL, listener *registry.ServiceInstancesChangedListener) { listenerId := listener.ServiceName + ":" + getUrlKey(url) if !s.subscribedServices.Contains(listenerId) { err := s.serviceDiscovery.AddListener(listener) @@ -234,7 +229,7 @@ func (s *serviceDiscoveryRegistry) registerServiceInstancesChangedListener(url c } -func getUrlKey(url common.URL) string { +func getUrlKey(url *common.URL) string { var bf bytes.Buffer if len(url.Protocol) != 0 { bf.WriteString(url.Protocol) @@ -256,7 +251,7 @@ func getUrlKey(url common.URL) string { return bf.String() } -func appendParam(buffer bytes.Buffer, paramKey string, url common.URL) { +func appendParam(buffer bytes.Buffer, paramKey string, url *common.URL) { buffer.WriteString(paramKey) buffer.WriteString("=") buffer.WriteString(url.GetParam(paramKey, "")) @@ -268,8 +263,8 @@ func (s *serviceDiscoveryRegistry) subscribe(url *common.URL, notify registry.No logger.Warnf("here is no instance in service[name : %s]", serviceName) return } - var subscribedURLs []common.URL - subscribedURLs = append(subscribedURLs, s.getExportedUrls(*url, serviceInstances)...) + var subscribedURLs []*common.URL + subscribedURLs = append(subscribedURLs, s.getExportedUrls(url, serviceInstances)...) if len(subscribedURLs) == 0 { subscribedURLs = s.synthesizeSubscribedURLs(url, serviceInstances) } @@ -282,8 +277,8 @@ func (s *serviceDiscoveryRegistry) subscribe(url *common.URL, notify registry.No } -func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL { - var urls []common.URL +func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL { + var urls []*common.URL for _, syn := range s.subscribedURLsSynthesizers { if syn.Support(subscribedURL) { urls = append(urls, syn.Synthesize(subscribedURL, serviceInstances)...) @@ -292,11 +287,11 @@ func (s *serviceDiscoveryRegistry) synthesizeSubscribedURLs(subscribedURL *commo return urls } -func shouldSubscribe(url common.URL) bool { +func shouldSubscribe(url *common.URL) bool { return !shouldRegister(url) } -func (s *serviceDiscoveryRegistry) getServices(url common.URL) *gxset.HashSet { +func (s *serviceDiscoveryRegistry) getServices(url *common.URL) *gxset.HashSet { services := gxset.NewSet() serviceNames := url.GetParam(constant.PROVIDER_BY, "") if len(serviceNames) > 0 { @@ -311,7 +306,7 @@ func (s *serviceDiscoveryRegistry) getServices(url common.URL) *gxset.HashSet { return services } -func (s *serviceDiscoveryRegistry) findMappedServices(url common.URL) *gxset.HashSet { +func (s *serviceDiscoveryRegistry) findMappedServices(url *common.URL) *gxset.HashSet { serviceInterface := url.GetParam(constant.INTERFACE_KEY, url.Path) group := url.GetParam(constant.GROUP_KEY, "") version := url.GetParam(constant.VERSION_KEY, "") @@ -325,7 +320,7 @@ func (s *serviceDiscoveryRegistry) findMappedServices(url common.URL) *gxset.Has return serviceNames } -func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL common.URL, serviceInstances []registry.ServiceInstance) []common.URL { +func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL { var filterInstances []registry.ServiceInstance for _, s := range serviceInstances { if !s.IsEnable() || !s.IsHealthy() { @@ -340,32 +335,15 @@ func (s *serviceDiscoveryRegistry) getExportedUrls(subscribedURL common.URL, ser filterInstances = append(filterInstances, s) } if len(filterInstances) == 0 { - return []common.URL{} + return []*common.URL{} } s.prepareServiceRevisionExportedURLs(filterInstances) subscribedURLs := s.cloneExportedURLs(subscribedURL, filterInstances) return subscribedURLs } -// comparator is defined as Comparator for skip list to compare the URL -type comparator common.URL - -// Compare is defined as Comparator for skip list to compare the URL -func (c comparator) Compare(comp cm.Comparator) int { - a := common.URL(c).String() - b := common.URL(comp.(comparator)).String() - switch { - case a > b: - return 1 - case a < b: - return -1 - default: - return 0 - } -} - -func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registry.ServiceInstance) []common.URL { - var urls []common.URL +func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registry.ServiceInstance) []*common.URL { + var urls []*common.URL metadataStorageType := getExportedStoreType(serviceInstance) proxyFactory := extension.GetMetadataServiceProxyFactory(metadataStorageType) if proxyFactory == nil { @@ -381,7 +359,7 @@ func (s *serviceDiscoveryRegistry) getExportedUrlsByInst(serviceInstance registr return urls } - ret := make([]common.URL, 0, len(result)) + ret := make([]*common.URL, 0, len(result)) for _, ui := range result { u, err := common.NewURL(ui.(string)) @@ -464,23 +442,23 @@ func (s *serviceDiscoveryRegistry) selectServiceInstance(serviceInstances []regi logger.Errorf("get service instance selector cathe error:%s", err.Error()) return nil } - return selector.Select(*s.url, serviceInstances) + return selector.Select(s.url, serviceInstances) } -func (s *serviceDiscoveryRegistry) initRevisionExportedURLsByInst(serviceInstance registry.ServiceInstance) []common.URL { +func (s *serviceDiscoveryRegistry) initRevisionExportedURLsByInst(serviceInstance registry.ServiceInstance) []*common.URL { if serviceInstance == nil { - return []common.URL{} + return nil } serviceName := serviceInstance.GetServiceName() revision := getExportedServicesRevision(serviceInstance) revisionExportedURLsMap := s.serviceRevisionExportedURLsCache[serviceName] if revisionExportedURLsMap == nil { - revisionExportedURLsMap = make(map[string][]common.URL, 4) + revisionExportedURLsMap = make(map[string][]*common.URL, 4) s.serviceRevisionExportedURLsCache[serviceName] = revisionExportedURLsMap } revisionExportedURLs := revisionExportedURLsMap[revision] firstGet := false - if revisionExportedURLs == nil || len(revisionExportedURLs) == 0 { + if len(revisionExportedURLs) == 0 { if len(revisionExportedURLsMap) > 0 { // The case is that current ServiceInstance with the different revision logger.Warnf("The ServiceInstance[id: %s, host : %s , port : %s] has different revision : %s"+ @@ -521,11 +499,11 @@ func getExportedStoreType(serviceInstance registry.ServiceInstance) string { return result } -func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsances []registry.ServiceInstance) []common.URL { +func (s *serviceDiscoveryRegistry) cloneExportedURLs(url *common.URL, serviceInsances []registry.ServiceInstance) []*common.URL { if len(serviceInsances) == 0 { - return []common.URL{} + return []*common.URL{} } - var clonedExportedURLs []common.URL + var clonedExportedURLs []*common.URL removeParamSet := gxset.NewSet() removeParamSet.Add(constant.PID_KEY) removeParamSet.Add(constant.TIMESTAMP_KEY) @@ -540,7 +518,7 @@ func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsa } cloneUrl := u.CloneExceptParams(removeParamSet) - clonedExportedURLs = append(clonedExportedURLs, *cloneUrl) + clonedExportedURLs = append(clonedExportedURLs, cloneUrl) } } return clonedExportedURLs @@ -548,8 +526,8 @@ func (s *serviceDiscoveryRegistry) cloneExportedURLs(url common.URL, serviceInsa } type endpoint struct { - Port int `json:"port, omitempty"` - Protocol string `json:"protocol, omitempty"` + Port int `json:"port,omitempty"` + Protocol string `json:"protocol,omitempty"` } func getProtocolPort(serviceInstance registry.ServiceInstance, protocol string) int { @@ -571,38 +549,38 @@ func getProtocolPort(serviceInstance registry.ServiceInstance, protocol string) } return -1 } -func (s *serviceDiscoveryRegistry) getTemplateExportedURLs(url common.URL, serviceInstance registry.ServiceInstance) []common.URL { +func (s *serviceDiscoveryRegistry) getTemplateExportedURLs(url *common.URL, serviceInstance registry.ServiceInstance) []*common.URL { exportedURLs := s.getRevisionExportedURLs(serviceInstance) if len(exportedURLs) == 0 { - return []common.URL{} + return []*common.URL{} } return filterSubscribedURLs(url, exportedURLs) } -func (s *serviceDiscoveryRegistry) getRevisionExportedURLs(serviceInstance registry.ServiceInstance) []common.URL { +func (s *serviceDiscoveryRegistry) getRevisionExportedURLs(serviceInstance registry.ServiceInstance) []*common.URL { if serviceInstance == nil { - return []common.URL{} + return []*common.URL{} } serviceName := serviceInstance.GetServiceName() revision := getExportedServicesRevision(serviceInstance) s.lock.RLock() revisionExportedURLsMap, exist := s.serviceRevisionExportedURLsCache[serviceName] if !exist { - return []common.URL{} + return []*common.URL{} } exportedURLs, exist := revisionExportedURLsMap[revision] if !exist { - return []common.URL{} + return []*common.URL{} } s.lock.RUnlock() // Get a copy from source in order to prevent the caller trying to change the cached data - cloneExportedURLs := make([]common.URL, len(exportedURLs)) + cloneExportedURLs := make([]*common.URL, len(exportedURLs)) copy(cloneExportedURLs, exportedURLs) return cloneExportedURLs } -func filterSubscribedURLs(subscribedURL common.URL, exportedURLs []common.URL) []common.URL { - var filterExportedURLs []common.URL +func filterSubscribedURLs(subscribedURL *common.URL, exportedURLs []*common.URL) []*common.URL { + var filterExportedURLs []*common.URL for _, url := range exportedURLs { if url.GetParam(constant.INTERFACE_KEY, url.Path) != subscribedURL.GetParam(constant.INTERFACE_KEY, url.Path) { break diff --git a/registry/servicediscovery/service_discovery_registry_test.go b/registry/servicediscovery/service_discovery_registry_test.go index 53eb86507e635be32eb362519922f7042f945519..391d92c8ad19c871756ca7426531bb01a7d54876 100644 --- a/registry/servicediscovery/service_discovery_registry_test.go +++ b/registry/servicediscovery/service_discovery_registry_test.go @@ -23,7 +23,7 @@ import ( import ( "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/page" + "github.com/dubbogo/gost/hash/page" "github.com/stretchr/testify/assert" ) import ( @@ -76,28 +76,27 @@ func TestServiceDiscoveryRegistry_Register(t *testing.T) { "&service_discovery=mock" + "&methods=getAllServiceKeys,getServiceRestMetadata,getExportedURLs,getAllExportedURLs" + "&side=provider") - registry, err := newServiceDiscoveryRegistry(®istryURL) + registry, err := newServiceDiscoveryRegistry(registryURL) assert.Nil(t, err) assert.NotNil(t, registry) - registry.Register(url) + err = registry.Register(url) + assert.NoError(t, err) } type mockEventDispatcher struct { } -func (m *mockEventDispatcher) AddEventListener(listener observer.EventListener) { - +func (m *mockEventDispatcher) AddEventListener(observer.EventListener) { } -func (m *mockEventDispatcher) AddEventListeners(listenersSlice []observer.EventListener) { - +func (m *mockEventDispatcher) AddEventListeners([]observer.EventListener) { } -func (m *mockEventDispatcher) RemoveEventListener(listener observer.EventListener) { +func (m *mockEventDispatcher) RemoveEventListener(observer.EventListener) { panic("implement me") } -func (m *mockEventDispatcher) RemoveEventListeners(listenersSlice []observer.EventListener) { +func (m *mockEventDispatcher) RemoveEventListeners([]observer.EventListener) { panic("implement me") } @@ -109,17 +108,17 @@ func (m *mockEventDispatcher) RemoveAllEventListeners() { panic("implement me") } -func (m *mockEventDispatcher) Dispatch(event observer.Event) { +func (m *mockEventDispatcher) Dispatch(observer.Event) { } type mockServiceNameMapping struct { } -func (m *mockServiceNameMapping) Map(serviceInterface string, group string, version string, protocol string) error { +func (m *mockServiceNameMapping) Map(string, string, string, string) error { return nil } -func (m *mockServiceNameMapping) Get(serviceInterface string, group string, version string, protocol string) (*gxset.HashSet, error) { +func (m *mockServiceNameMapping) Get(string, string, string, string) (*gxset.HashSet, error) { panic("implement me") } @@ -134,15 +133,15 @@ func (m *mockServiceDiscovery) Destroy() error { panic("implement me") } -func (m *mockServiceDiscovery) Register(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Register(registry.ServiceInstance) error { return nil } -func (m *mockServiceDiscovery) Update(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Update(registry.ServiceInstance) error { panic("implement me") } -func (m *mockServiceDiscovery) Unregister(instance registry.ServiceInstance) error { +func (m *mockServiceDiscovery) Unregister(registry.ServiceInstance) error { panic("implement me") } @@ -154,35 +153,35 @@ func (m *mockServiceDiscovery) GetServices() *gxset.HashSet { panic("implement me") } -func (m *mockServiceDiscovery) GetInstances(serviceName string) []registry.ServiceInstance { +func (m *mockServiceDiscovery) GetInstances(string) []registry.ServiceInstance { panic("implement me") } -func (m *mockServiceDiscovery) GetInstancesByPage(serviceName string, offset int, pageSize int) gxpage.Pager { +func (m *mockServiceDiscovery) GetInstancesByPage(string, int, int) gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) GetHealthyInstancesByPage(serviceName string, offset int, pageSize int, healthy bool) gxpage.Pager { +func (m *mockServiceDiscovery) GetHealthyInstancesByPage(string, int, int, bool) gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) GetRequestInstances(serviceNames []string, offset int, requestedSize int) map[string]gxpage.Pager { +func (m *mockServiceDiscovery) GetRequestInstances([]string, int, int) map[string]gxpage.Pager { panic("implement me") } -func (m *mockServiceDiscovery) AddListener(listener *registry.ServiceInstancesChangedListener) error { +func (m *mockServiceDiscovery) AddListener(*registry.ServiceInstancesChangedListener) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEventByServiceName(serviceName string) error { +func (m *mockServiceDiscovery) DispatchEventByServiceName(string) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEventForInstances(serviceName string, instances []registry.ServiceInstance) error { +func (m *mockServiceDiscovery) DispatchEventForInstances(string, []registry.ServiceInstance) error { panic("implement me") } -func (m *mockServiceDiscovery) DispatchEvent(event *registry.ServiceInstancesChangedEvent) error { +func (m *mockServiceDiscovery) DispatchEvent(*registry.ServiceInstancesChangedEvent) error { panic("implement me") } @@ -197,27 +196,27 @@ func (m *mockMetadataService) ServiceName() (string, error) { panic("implement me") } -func (m *mockMetadataService) ExportURL(url common.URL) (bool, error) { +func (m *mockMetadataService) ExportURL(*common.URL) (bool, error) { return true, nil } -func (m *mockMetadataService) UnexportURL(url common.URL) error { +func (m *mockMetadataService) UnexportURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) SubscribeURL(url common.URL) (bool, error) { +func (m *mockMetadataService) SubscribeURL(*common.URL) (bool, error) { panic("implement me") } -func (m *mockMetadataService) UnsubscribeURL(url common.URL) error { +func (m *mockMetadataService) UnsubscribeURL(*common.URL) error { panic("implement me") } -func (m *mockMetadataService) PublishServiceDefinition(url common.URL) error { +func (m *mockMetadataService) PublishServiceDefinition(*common.URL) error { return nil } -func (m *mockMetadataService) GetExportedURLs(serviceInterface string, group string, version string, protocol string) ([]interface{}, error) { +func (m *mockMetadataService) GetExportedURLs(string, string, string, string) ([]interface{}, error) { panic("implement me") } @@ -225,19 +224,19 @@ func (m *mockMetadataService) MethodMapper() map[string]string { panic("implement me") } -func (m *mockMetadataService) GetSubscribedURLs() ([]common.URL, error) { +func (m *mockMetadataService) GetSubscribedURLs() ([]*common.URL, error) { panic("implement me") } -func (m *mockMetadataService) GetServiceDefinition(interfaceName string, group string, version string) (string, error) { +func (m *mockMetadataService) GetServiceDefinition(string, string, string) (string, error) { panic("implement me") } -func (m *mockMetadataService) GetServiceDefinitionByServiceKey(serviceKey string) (string, error) { +func (m *mockMetadataService) GetServiceDefinitionByServiceKey(string) (string, error) { panic("implement me") } -func (m *mockMetadataService) RefreshMetadata(exportedRevision string, subscribedRevision string) (bool, error) { +func (m *mockMetadataService) RefreshMetadata(string, string) (bool, error) { panic("implement me") } diff --git a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go index 086a26de58f8472e35e07a8a174fdee86afa82f2..c6b3aeaad09ebfa01d832a92478b7638a6cff432 100644 --- a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go +++ b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer.go @@ -38,14 +38,11 @@ type RestSubscribedURLsSynthesizer struct { } func (r RestSubscribedURLsSynthesizer) Support(subscribedURL *common.URL) bool { - if "rest" == subscribedURL.Protocol { - return true - } - return false + return "rest" == subscribedURL.Protocol } -func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL { - urls := make([]common.URL, len(serviceInstances), len(serviceInstances)) +func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL { + urls := make([]*common.URL, len(serviceInstances)) for i, s := range serviceInstances { splitHost := strings.Split(s.GetHost(), ":") u := common.NewURLWithOptions(common.WithProtocol(subscribedURL.Protocol), common.WithIp(splitHost[0]), @@ -55,7 +52,7 @@ func (r RestSubscribedURLsSynthesizer) Synthesize(subscribedURL *common.URL, ser common.WithParamsValue(constant.APPLICATION_KEY, s.GetServiceName()), common.WithParamsValue(constant.REGISTRY_KEY, "true"), ) - urls[i] = *u + urls[i] = u } return urls } diff --git a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go index b52cc2323d6f9ae1bca8cfd1a4c5217af5e25f12..1bb38c92e32942fb1a39bbaeb9617013459bcdfe 100644 --- a/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go +++ b/registry/servicediscovery/synthesizer/rest/rest_subscribed_urls_synthesizer_test.go @@ -56,7 +56,7 @@ func TestRestSubscribedURLsSynthesizer_Synthesize(t *testing.T) { }, } - var expectUrls []common.URL + var expectUrls []*common.URL u1 := common.NewURLWithOptions(common.WithProtocol("rest"), common.WithIp("127.0.0.1"), common.WithPort("80"), common.WithPath("org.apache.dubbo-go.mockService"), common.WithParams(url.Values{}), @@ -69,7 +69,7 @@ func TestRestSubscribedURLsSynthesizer_Synthesize(t *testing.T) { common.WithParamsValue(constant.SIDE_KEY, constant.PROVIDER_PROTOCOL), common.WithParamsValue(constant.APPLICATION_KEY, "test2"), common.WithParamsValue(constant.REGISTRY_KEY, "true")) - expectUrls = append(expectUrls, *u1, *u2) - result := syn.Synthesize(&subUrl, instances) + expectUrls = append(expectUrls, u1, u2) + result := syn.Synthesize(subUrl, instances) assert.Equal(t, expectUrls, result) } diff --git a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go index 415ca35fbad2fa335d687dc7a7718fa3a4b2b487..557c86ec127fe88411fdbe9dd83d43ff8cd18cec 100644 --- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go +++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go @@ -27,5 +27,5 @@ type SubscribedURLsSynthesizer interface { // Supports the synthesis of the subscribed url or not Support(subscribedURL *common.URL) bool // synthesize the subscribed url - Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []common.URL + Synthesize(subscribedURL *common.URL, serviceInstances []registry.ServiceInstance) []*common.URL } diff --git a/registry/zookeeper/listener.go b/registry/zookeeper/listener.go index ec82fa0309118fba4b5c21772d4dfd356f3b0c5c..5a7d14b28a1790cc040d2b51ff0eabad6e44844e 100644 --- a/registry/zookeeper/listener.go +++ b/registry/zookeeper/listener.go @@ -18,6 +18,7 @@ package zookeeper import ( + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "strings" "sync" ) @@ -32,7 +33,6 @@ import ( "github.com/apache/dubbo-go/config_center" "github.com/apache/dubbo-go/registry" "github.com/apache/dubbo-go/remoting" - zk "github.com/apache/dubbo-go/remoting/zookeeper" ) // RegistryDataListener contains all URL information subscribed by zookeeper registry @@ -56,12 +56,13 @@ func (l *RegistryDataListener) SubscribeURL(url *common.URL, listener config_cen l.subscribed[url.ServiceKey()] = listener } -// UnSubscribeURL is used to set a watch listener for url +// UnSubscribeURL is used to unset a watch listener for url func (l *RegistryDataListener) UnSubscribeURL(url *common.URL) config_center.ConfigurationListener { if l.closed { return nil } listener := l.subscribed[url.ServiceKey()] + listener.(*RegistryConfigurationListener).Close() delete(l.subscribed, url.ServiceKey()) return listener } @@ -104,6 +105,7 @@ func (l *RegistryDataListener) DataChange(eventType remoting.Event) bool { func (l *RegistryDataListener) Close() { l.mutex.Lock() defer l.mutex.Unlock() + l.closed = true for _, listener := range l.subscribed { listener.(*RegistryConfigurationListener).Close() } @@ -111,7 +113,7 @@ func (l *RegistryDataListener) Close() { // RegistryConfigurationListener represent the processor of zookeeper watcher type RegistryConfigurationListener struct { - client *zk.ZookeeperClient + client *gxzookeeper.ZookeeperClient registry *zkRegistry events chan *config_center.ConfigChangeEvent isClosed bool @@ -121,7 +123,7 @@ type RegistryConfigurationListener struct { } // NewRegistryConfigurationListener for listening the event of zk. -func NewRegistryConfigurationListener(client *zk.ZookeeperClient, reg *zkRegistry, conf *common.URL) *RegistryConfigurationListener { +func NewRegistryConfigurationListener(client *gxzookeeper.ZookeeperClient, reg *zkRegistry, conf *common.URL) *RegistryConfigurationListener { reg.WaitGroup().Add(1) return &RegistryConfigurationListener{ client: client, @@ -141,9 +143,6 @@ func (l *RegistryConfigurationListener) Process(configType *config_center.Config func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { for { select { - case <-l.client.Done(): - logger.Warnf("listener's zk client connection (address {%s}) is broken, so zk event listener exit now.", l.client.ZkAddrs) - return nil, perrors.New("zookeeper client stopped") case <-l.close: return nil, perrors.New("listener have been closed") case <-l.registry.Done(): @@ -155,10 +154,7 @@ func (l *RegistryConfigurationListener) Next() (*registry.ServiceEvent, error) { logger.Warnf("update @result{%s}. But its connection to registry is invalid", e.Value) continue } - //r.update(e.res) - //write to invoker - //r.outerEventCh <- e.res - return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(common.URL)}, nil + return ®istry.ServiceEvent{Action: e.ConfigType, Service: e.Value.(*common.URL)}, nil } } } diff --git a/registry/zookeeper/listener_test.go b/registry/zookeeper/listener_test.go index a0e9147a9e0ee8767efcf78d5e2aa536140f6a8b..20ec1cf69a65601e5dd8351083ce23edfb67d10c 100644 --- a/registry/zookeeper/listener_test.go +++ b/registry/zookeeper/listener_test.go @@ -34,7 +34,7 @@ import ( func Test_DataChange(t *testing.T) { listener := NewRegistryDataListener() url, _ := common.NewURL("jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100") - listener.SubscribeURL(&url, &MockConfigurationListener{}) + listener.SubscribeURL(url, &MockConfigurationListener{}) int := listener.DataChange(remoting.Event{Path: "/dubbo/com.ikurento.user.UserProvider/providers/jsonrpc%3A%2F%2F127.0.0.1%3A20001%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26app.version%3D0.0.1%26application%3DBDTService%26category%3Dproviders%26cluster%3Dfailover%26dubbo%3Ddubbo-provider-golang-1.3.0%26environment%3Ddev%26group%3D%26interface%3Dcom.ikurento.user.UserProvider%26ip%3D10.32.20.124%26loadbalance%3Drandom%26methods.GetUser.loadbalance%3Drandom%26methods.GetUser.retries%3D1%26methods.GetUser.weight%3D0%26module%3Ddubbogo%2Buser-info%2Bserver%26name%3DBDTService%26organization%3Dikurento.com%26owner%3DZX%26pid%3D74500%26retries%3D0%26service.filter%3Decho%26side%3Dprovider%26timestamp%3D1560155407%26version%3D%26warmup%3D100"}) assert.Equal(t, true, int) } diff --git a/registry/zookeeper/registry.go b/registry/zookeeper/registry.go index e8ee51beb70b5a08ec60b213c5342ef52972c59f..8b21aaed73c2fcd72d287ee38096a4466d629c41 100644 --- a/registry/zookeeper/registry.go +++ b/registry/zookeeper/registry.go @@ -27,6 +27,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" perrors "github.com/pkg/errors" ) @@ -54,7 +55,7 @@ func init() { type zkRegistry struct { registry.BaseRegistry - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient listenerLock sync.Mutex listener *zookeeper.ZkEventListener dataListener *RegistryDataListener @@ -73,7 +74,7 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { } r.InitBaseRegistry(url, r) - err = zookeeper.ValidateZookeeperClient(r, zookeeper.WithZkName(RegistryZkClient)) + err = zookeeper.ValidateZookeeperClient(r, RegistryZkClient) if err != nil { return nil, err } @@ -89,13 +90,13 @@ func newZkRegistry(url *common.URL) (registry.Registry, error) { // nolint type Options struct { - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient } // nolint type Option func(*Options) -func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestCluster, *zkRegistry, error) { +func newMockZkRegistry(url *common.URL, opts ...gxzookeeper.Option) (*zk.TestCluster, *zkRegistry, error) { var ( err error r *zkRegistry @@ -107,7 +108,7 @@ func newMockZkRegistry(url *common.URL, opts ...zookeeper.Option) (*zk.TestClust zkPath: make(map[string]int), } r.InitBaseRegistry(url, r) - c, r.client, _, err = zookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) + c, r.client, _, err = gxzookeeper.NewMockZookeeperClient("test", 15*time.Second, opts...) if err != nil { return nil, nil, err } @@ -127,15 +128,15 @@ func (r *zkRegistry) InitListeners() { oldDataListener := r.dataListener oldDataListener.mutex.Lock() defer oldDataListener.mutex.Unlock() - recoverd := r.dataListener.subscribed - if recoverd != nil && len(recoverd) > 0 { + r.dataListener.closed = true + recovered := r.dataListener.subscribed + if len(recovered) > 0 { // recover all subscribed url - for _, oldListener := range recoverd { + for _, oldListener := range recovered { var ( regConfigListener *RegistryConfigurationListener ok bool ) - if regConfigListener, ok = oldListener.(*RegistryConfigurationListener); ok { regConfigListener.Close() } @@ -183,12 +184,12 @@ func (r *zkRegistry) CloseAndNilClient() { } // nolint -func (r *zkRegistry) ZkClient() *zookeeper.ZookeeperClient { +func (r *zkRegistry) ZkClient() *gxzookeeper.ZookeeperClient { return r.client } // nolint -func (r *zkRegistry) SetZkClient(client *zookeeper.ZookeeperClient) { +func (r *zkRegistry) SetZkClient(client *gxzookeeper.ZookeeperClient) { r.client = client } @@ -212,6 +213,9 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { r.cltLock.Lock() defer r.cltLock.Unlock() + if r.client == nil { + return perrors.WithStack(perrors.New("zk client already been closed")) + } err = r.client.Create(root) if err != nil { logger.Errorf("zk.Create(root{%s}) = err{%v}", root, perrors.WithStack(err)) @@ -220,23 +224,22 @@ func (r *zkRegistry) registerTempZookeeperNode(root string, node string) error { // try to register the node zkPath, err = r.client.RegisterTemp(root, node) - if err != nil { - logger.Errorf("Register temp node(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) - if perrors.Cause(err) == zk.ErrNodeExists { - // should delete the old node - logger.Info("Register temp node failed, try to delete the old and recreate (root{%s}, node{%s}) , ignore!", root, node) - if err = r.client.Delete(zkPath); err == nil { - _, err = r.client.RegisterTemp(root, node) - } - if err != nil { - logger.Errorf("Recreate the temp node failed, (root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) - } + if err == nil { + return nil + } + + if perrors.Cause(err) == zk.ErrNodeExists { + if err = r.client.Delete(zkPath); err == nil { + _, err = r.client.RegisterTemp(root, node) + } + + if err == nil { + return nil } - return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node) } - logger.Debugf("Create a zookeeper node:%s", zkPath) - return nil + logger.Errorf("Register temp node(root{%s}, node{%s}) = error{%v}", root, node, perrors.WithStack(err)) + return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node) } func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListener, error) { @@ -248,8 +251,7 @@ func (r *zkRegistry) getListener(conf *common.URL) (*RegistryConfigurationListen dataListener.mutex.Lock() defer dataListener.mutex.Unlock() if r.dataListener.subscribed[conf.ServiceKey()] != nil { - - zkListener, _ := r.dataListener.subscribed[conf.ServiceKey()].(*RegistryConfigurationListener) + zkListener, _ = r.dataListener.subscribed[conf.ServiceKey()].(*RegistryConfigurationListener) if zkListener != nil { r.listenerLock.Lock() defer r.listenerLock.Unlock() @@ -292,12 +294,10 @@ func (r *zkRegistry) getCloseListener(conf *common.URL) (*RegistryConfigurationL r.dataListener.mutex.Lock() configurationListener := r.dataListener.subscribed[conf.ServiceKey()] if configurationListener != nil { - - zkListener, _ := configurationListener.(*RegistryConfigurationListener) - if zkListener != nil { - if zkListener.isClosed { - return nil, perrors.New("configListener already been closed") - } + zkListener, _ = configurationListener.(*RegistryConfigurationListener) + if zkListener != nil && zkListener.isClosed { + r.dataListener.mutex.Unlock() + return nil, perrors.New("configListener already been closed") } } diff --git a/registry/zookeeper/registry_test.go b/registry/zookeeper/registry_test.go index d915fc2ce10359f0dd1970daf019746ce066f511..9e52dd70c3ba17cbfbc075ac75ef7b07e0901525 100644 --- a/registry/zookeeper/registry_test.go +++ b/registry/zookeeper/registry_test.go @@ -24,22 +24,25 @@ import ( ) import ( + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/constant" - "github.com/apache/dubbo-go/remoting/zookeeper" ) func Test_Register(t *testing.T) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, _ := newMockZkRegistry(®url) - defer ts.Stop() - err := reg.Register(url) + ts, reg, err := newMockZkRegistry(regURL) + assert.NoError(t, err) + defer func() { + _ = ts.Stop() + }() + err = reg.Register(url) children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers") assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock%26.*.serviceid%3Dsoa.mock", children) assert.NoError(t, err) @@ -47,17 +50,21 @@ func Test_Register(t *testing.T) { func Test_UnRegister(t *testing.T) { // register - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithParamsValue("serviceid", "soa.mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, _ := newMockZkRegistry(®url) - defer ts.Stop() + ts, reg, _ := newMockZkRegistry(regURL) + defer func() { + _ = ts.Stop() + }() err := reg.Register(url) + assert.NoError(t, err) children, _ := reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers") assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock%26.*.serviceid%3Dsoa.mock", children) assert.NoError(t, err) err = reg.UnRegister(url) + assert.NoError(t, err) children, err = reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers") assert.Equal(t, 0, len(children)) assert.Error(t, err) @@ -67,13 +74,12 @@ func Test_UnRegister(t *testing.T) { children, _ = reg.client.GetChildren("/dubbo/com.ikurento.user.UserProvider/providers") assert.Regexp(t, ".*dubbo%3A%2F%2F127.0.0.1%3A20000%2Fcom.ikurento.user.UserProvider%3Fanyhost%3Dtrue%26cluster%3Dmock%26.*.serviceid%3Dsoa.mock", children) assert.NoError(t, err) - } func Test_Subscribe(t *testing.T) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, _ := newMockZkRegistry(®url) + ts, reg, _ := newMockZkRegistry(regURL) //provider register err := reg.Register(url) @@ -84,11 +90,12 @@ func Test_Subscribe(t *testing.T) { } //consumer register - regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) - _, reg2, _ := newMockZkRegistry(®url, zookeeper.WithTestCluster(ts)) + regURL.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) + _, reg2, _ := newMockZkRegistry(regURL, gxzookeeper.WithTestCluster(ts)) - reg2.Register(url) - listener, _ := reg2.DoSubscribe(&url) + err = reg2.Register(url) + assert.Nil(t, err) + listener, _ := reg2.DoSubscribe(url) serviceEvent, _ := listener.Next() assert.NoError(t, err) @@ -96,13 +103,15 @@ func Test_Subscribe(t *testing.T) { return } assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent.String()) - defer ts.Stop() + defer func() { + _ = ts.Stop() + }() } func Test_UnSubscribe(t *testing.T) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, _ := newMockZkRegistry(®url) + ts, reg, _ := newMockZkRegistry(regURL) //provider register err := reg.Register(url) @@ -113,11 +122,12 @@ func Test_UnSubscribe(t *testing.T) { } //consumer register - regurl.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) - _, reg2, _ := newMockZkRegistry(®url, zookeeper.WithTestCluster(ts)) + regURL.SetParam(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER)) + _, reg2, _ := newMockZkRegistry(regURL, gxzookeeper.WithTestCluster(ts)) - reg2.Register(url) - listener, _ := reg2.DoSubscribe(&url) + err = reg2.Register(url) + assert.Nil(t, err) + listener, _ := reg2.DoSubscribe(url) serviceEvent, _ := listener.Next() assert.NoError(t, err) @@ -126,41 +136,47 @@ func Test_UnSubscribe(t *testing.T) { } assert.Regexp(t, ".*ServiceEvent{Action{add}.*", serviceEvent.String()) - reg2.UnSubscribe(&url, nil) + err = reg2.UnSubscribe(url, nil) + assert.Nil(t, err) assert.Nil(t, reg2.listener) - - defer ts.Stop() + defer func() { + _ = ts.Stop() + }() } -func Test_ConsumerDestory(t *testing.T) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))) +func Test_ConsumerDestroy(t *testing.T) { + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.CONSUMER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, err := newMockZkRegistry(®url) - defer ts.Stop() + ts, reg, err := newMockZkRegistry(regURL) + defer func() { + _ = ts.Stop() + }() assert.NoError(t, err) err = reg.Register(url) assert.NoError(t, err) - _, err = reg.DoSubscribe(&url) + _, err = reg.DoSubscribe(url) assert.NoError(t, err) //listener.Close() time.Sleep(1e9) reg.Destroy() assert.Equal(t, false, reg.IsAvailable()) - } -func Test_ProviderDestory(t *testing.T) { - regurl, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) +func Test_ProviderDestroy(t *testing.T) { + regURL, _ := common.NewURL("registry://127.0.0.1:1111", common.WithParamsValue(constant.ROLE_KEY, strconv.Itoa(common.PROVIDER))) url, _ := common.NewURL("dubbo://127.0.0.1:20000/com.ikurento.user.UserProvider", common.WithParamsValue(constant.CLUSTER_KEY, "mock"), common.WithMethods([]string{"GetUser", "AddUser"})) - ts, reg, err := newMockZkRegistry(®url) - defer ts.Stop() + ts, reg, err := newMockZkRegistry(regURL) + defer func() { + _ = ts.Stop() + }() assert.NoError(t, err) - reg.Register(url) + err = reg.Register(url) + assert.Nil(t, err) //listener.Close() time.Sleep(1e9) diff --git a/registry/zookeeper/service_discovery.go b/registry/zookeeper/service_discovery.go index 5ad83ef90947afc0a5ca75af5009e8b55b4f6627..06fe0f7f24437fbd4b7e0568c4d88c9b4e7cbe31 100644 --- a/registry/zookeeper/service_discovery.go +++ b/registry/zookeeper/service_discovery.go @@ -26,8 +26,9 @@ import ( ) import ( - "github.com/dubbogo/gost/container/set" - "github.com/dubbogo/gost/page" + gxset "github.com/dubbogo/gost/container/set" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" + gxpage "github.com/dubbogo/gost/hash/page" perrors "github.com/pkg/errors" ) @@ -60,9 +61,9 @@ func init() { } type zookeeperServiceDiscovery struct { - client *zookeeper.ZookeeperClient - csd *curator_discovery.ServiceDiscovery - listener *zookeeper.ZkEventListener + client *gxzookeeper.ZookeeperClient + csd *curator_discovery.ServiceDiscovery + //listener *zookeeper.ZkEventListener url *common.URL wg sync.WaitGroup cltLock sync.Mutex @@ -107,22 +108,23 @@ func newZookeeperServiceDiscovery(name string) (registry.ServiceDiscovery, error url: url, rootPath: rootPath, } - err := zookeeper.ValidateZookeeperClient(zksd, zookeeper.WithZkName(ServiceDiscoveryZkClient)) + err := zookeeper.ValidateZookeeperClient(zksd, ServiceDiscoveryZkClient) if err != nil { return nil, err } + zksd.WaitGroup().Add(1) //zk client start successful, then wg +1 go zookeeper.HandleClientRestart(zksd) zksd.csd = curator_discovery.NewServiceDiscovery(zksd.client, rootPath) return zksd, nil } // nolint -func (zksd *zookeeperServiceDiscovery) ZkClient() *zookeeper.ZookeeperClient { +func (zksd *zookeeperServiceDiscovery) ZkClient() *gxzookeeper.ZookeeperClient { return zksd.client } // nolint -func (zksd *zookeeperServiceDiscovery) SetZkClient(client *zookeeper.ZookeeperClient) { +func (zksd *zookeeperServiceDiscovery) SetZkClient(client *gxzookeeper.ZookeeperClient) { zksd.client = client } @@ -154,8 +156,8 @@ func (zksd *zookeeperServiceDiscovery) RestartCallBack() bool { } // nolint -func (zksd *zookeeperServiceDiscovery) GetUrl() common.URL { - return *zksd.url +func (zksd *zookeeperServiceDiscovery) GetUrl() *common.URL { + return zksd.url } // nolint @@ -214,7 +216,7 @@ func (zksd *zookeeperServiceDiscovery) GetInstances(serviceName string) []regist if err != nil { logger.Errorf("[zkServiceDiscovery] Could not query the instances for service{%s}, error = err{%v} ", serviceName, err) - return make([]registry.ServiceInstance, 0, 0) + return make([]registry.ServiceInstance, 0) } iss := make([]registry.ServiceInstance, 0, len(criss)) for _, cris := range criss { @@ -231,7 +233,7 @@ func (zksd *zookeeperServiceDiscovery) GetInstancesByPage(serviceName string, of for i := offset; i < len(all) && i < offset+pageSize; i++ { res = append(res, all[i]) } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // GetHealthyInstancesByPage will return the instance @@ -254,7 +256,7 @@ func (zksd *zookeeperServiceDiscovery) GetHealthyInstancesByPage(serviceName str } i++ } - return gxpage.New(offset, pageSize, res, len(all)) + return gxpage.NewPage(offset, pageSize, res, len(all)) } // GetRequestInstances will return the instances diff --git a/registry/zookeeper/service_discovery_test.go b/registry/zookeeper/service_discovery_test.go index ea3c7ddd48adc0adc4162d8306d28283575f694a..a73ecc99652d0b234f07036f2cfe3c2bb2aad580 100644 --- a/registry/zookeeper/service_discovery_test.go +++ b/registry/zookeeper/service_discovery_test.go @@ -37,11 +37,14 @@ import ( var testName = "test" +var tc *zk.TestCluster + func prepareData(t *testing.T) *zk.TestCluster { - ts, err := zk.StartTestCluster(1, nil, nil) + var err error + tc, err = zk.StartTestCluster(1, nil, nil) assert.NoError(t, err) - assert.NotNil(t, ts.Servers[0]) - address := "127.0.0.1:" + strconv.Itoa(ts.Servers[0].Port) + assert.NotNil(t, tc.Servers[0]) + address := "127.0.0.1:" + strconv.Itoa(tc.Servers[0].Port) config.GetBaseConfig().ServiceDiscoveries[testName] = &config.ServiceDiscoveryConfig{ Protocol: "zookeeper", @@ -52,7 +55,7 @@ func prepareData(t *testing.T) *zk.TestCluster { Address: address, TimeoutStr: "10s", } - return ts + return tc } func TestNewZookeeperServiceDiscovery(t *testing.T) { @@ -74,11 +77,12 @@ func TestNewZookeeperServiceDiscovery(t *testing.T) { } func TestCURDZookeeperServiceDiscovery(t *testing.T) { - ts := prepareData(t) - defer ts.Stop() + prepareData(t) sd, err := newZookeeperServiceDiscovery(testName) assert.Nil(t, err) - defer sd.Destroy() + defer func() { + _ = sd.Destroy() + }() md := make(map[string]string) md["t1"] = "test1" err = sd.Register(®istry.DefaultServiceInstance{ @@ -139,11 +143,14 @@ func TestCURDZookeeperServiceDiscovery(t *testing.T) { } func TestAddListenerZookeeperServiceDiscovery(t *testing.T) { - ts := prepareData(t) - defer ts.Stop() + defer func() { + _ = tc.Stop() + }() sd, err := newZookeeperServiceDiscovery(testName) assert.Nil(t, err) - defer sd.Destroy() + defer func() { + _ = sd.Destroy() + }() err = sd.Register(®istry.DefaultServiceInstance{ Id: "testId", @@ -154,8 +161,6 @@ func TestAddListenerZookeeperServiceDiscovery(t *testing.T) { Healthy: true, Metadata: nil, }) - assert.Nil(t, err) - assert.Nil(t, err) wg := &sync.WaitGroup{} wg.Add(1) @@ -170,7 +175,7 @@ func TestAddListenerZookeeperServiceDiscovery(t *testing.T) { extension.SetAndInitGlobalDispatcher("direct") extension.GetGlobalDispatcher().AddEventListener(sicl) err = sd.AddListener(sicl) - assert.Nil(t, err) + assert.NoError(t, err) err = sd.Update(®istry.DefaultServiceInstance{ Id: "testId", @@ -181,6 +186,7 @@ func TestAddListenerZookeeperServiceDiscovery(t *testing.T) { Healthy: true, Metadata: nil, }) + assert.NoError(t, err) tn.wg.Wait() } diff --git a/remoting/consul/test_agent.go b/remoting/consul/test_agent.go index f6ba336a95667fdf1d9b305e1844408c2501190a..2c5c13f4ac578658383798ea8982a7d85b1d6dd7 100644 --- a/remoting/consul/test_agent.go +++ b/remoting/consul/test_agent.go @@ -30,7 +30,7 @@ import ( // an embedded consul server. func NewConsulAgent(t *testing.T, port int) *agent.TestAgent { hcl := ` - ports { + ports { http = ` + strconv.Itoa(port) + ` } ` diff --git a/remoting/etcdv3/client.go b/remoting/etcdv3/client.go index ebd454242d49ee82c81fe1a1fae1a19980c238a4..34ee31bd94afcc48776bfc81d73bbd5c87a2b7f8 100644 --- a/remoting/etcdv3/client.go +++ b/remoting/etcdv3/client.go @@ -416,7 +416,9 @@ func (c *Client) keepAliveKV(k string, v string) error { keepAlive, err := c.rawClient.KeepAlive(c.ctx, lease.ID) if err != nil || keepAlive == nil { - c.rawClient.Revoke(c.ctx, lease.ID) + if _, revokeErr := c.rawClient.Revoke(c.ctx, lease.ID); revokeErr != nil { + logger.Warnf("rawClient.Revoke() = error:%v", revokeErr) + } if err != nil { return perrors.WithMessage(err, "keep alive lease") } else { diff --git a/remoting/etcdv3/client_test.go b/remoting/etcdv3/client_test.go index 3de266f42ffbc69a1e2ba4662a9a9fff1d831cd4..787c24d45fba17584dbdd4cb6485cf31ad0e5d2c 100644 --- a/remoting/etcdv3/client_test.go +++ b/remoting/etcdv3/client_test.go @@ -108,7 +108,6 @@ func (suite *ClientTestSuite) SetupSuite() { } suite.etcd = e - return } // stop etcd server @@ -133,9 +132,9 @@ func (suite *ClientTestSuite) setUpClient() *Client { // set up a client for suite func (suite *ClientTestSuite) SetupTest() { c := suite.setUpClient() - c.CleanKV() + err := c.CleanKV() + suite.Nil(err) suite.client = c - return } func (suite *ClientTestSuite) TestClientClose() { @@ -298,7 +297,7 @@ func (suite *ClientTestSuite) TestClientWatch() { wc, err := c.watch(prefix) if err != nil { - t.Fatal(err) + t.Error(err) } events := make([]mvccpb.Event, 0) @@ -358,7 +357,7 @@ func (suite *ClientTestSuite) TestClientRegisterTemp() { completePath := path.Join("scott", "wang") wc, err := observeC.watch(completePath) if err != nil { - t.Fatal(err) + t.Error(err) } events := make([]mvccpb.Event, 0) diff --git a/remoting/etcdv3/listener.go b/remoting/etcdv3/listener.go index 4f80a89dfb713036a5d4d812bc7a2d5551f42284..fd6f9585979fabeb86d7a74d5817b6992b6a0ad4 100644 --- a/remoting/etcdv3/listener.go +++ b/remoting/etcdv3/listener.go @@ -36,7 +36,7 @@ import ( // nolint type EventListener struct { client *Client - keyMapLock sync.Mutex + keyMapLock sync.RWMutex keyMap map[string]struct{} wg sync.WaitGroup } @@ -129,8 +129,6 @@ func (l *EventListener) handleEvents(event *clientv3.Event, listeners ...remotin default: return false } - - panic("unreachable") } // ListenServiceNodeEventWithPrefix listens on a set of key with spec prefix @@ -183,9 +181,9 @@ func timeSecondDuration(sec int) time.Duration { // --------> listenServiceNodeEvent func (l *EventListener) ListenServiceEvent(key string, listener remoting.DataListener) { - l.keyMapLock.Lock() + l.keyMapLock.RLock() _, ok := l.keyMap[key] - l.keyMapLock.Unlock() + l.keyMapLock.RUnlock() if ok { logger.Warnf("etcdv3 key %s has already been listened.", key) return diff --git a/remoting/exchange.go b/remoting/exchange.go index 848d9cbbcc23b0f565c45b646cf443be3f811efc..ad136a7c5ea4946e8467c69cfbf43b275df8ceef 100644 --- a/remoting/exchange.go +++ b/remoting/exchange.go @@ -17,6 +17,7 @@ package remoting import ( + "sync" "time" ) @@ -26,13 +27,19 @@ import ( import ( "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/logger" ) var ( // generate request ID for global use sequence atomic.Int64 + + // store requestID and response + pendingResponses = new(sync.Map) ) +type SequenceType int64 + func init() { // init request ID sequence.Store(0) @@ -90,6 +97,23 @@ func (response *Response) IsHeartbeat() bool { return response.Event && response.Result == nil } +func (response *Response) Handle() { + 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 + close(pendingResponse.Done) + } else { + pendingResponse.Callback(pendingResponse.GetCallResponse()) + } +} + type Options struct { // connect timeout ConnectTimeout time.Duration @@ -105,7 +129,7 @@ type AsyncCallbackResponse struct { Reply interface{} } -// the client sends requst to server, there is one pendingResponse at client side to wait the response from server +// the client sends request to server, there is one pendingResponse at client side to wait the response from server type PendingResponse struct { seq int64 Err error @@ -142,3 +166,28 @@ func (r PendingResponse) GetCallResponse() common.CallbackResponse { Reply: r.response, } } + +// 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_client.go b/remoting/exchange_client.go index 75ae945836e3f274773b167783625744241a04cb..f84cef1fa819bea244c4c526d6506acbcc726c74 100644 --- a/remoting/exchange_client.go +++ b/remoting/exchange_client.go @@ -18,35 +18,31 @@ package remoting import ( "errors" - "sync" "time" ) +import ( + uatomic "go.uber.org/atomic" +) + 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 + Connect(url *common.URL) error // close Close() // send request to server. Request(request *Request, timeout time.Duration, response *PendingResponse) error + // check if the client is still available + IsAvailable() bool } // This is abstraction level. it is like facade. @@ -59,15 +55,12 @@ type ExchangeClient struct { client Client // the tag for init. init bool -} - -// handle the message from server -type ResponseHandler interface { - Handler(response *Response) + // the number of service using the exchangeClient + activeNum uatomic.Uint32 } // create ExchangeClient -func NewExchangeClient(url common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient { +func NewExchangeClient(url *common.URL, client Client, connectTimeout time.Duration, lazyInit bool) *ExchangeClient { exchangeClient := &ExchangeClient{ ConnectTimeout: connectTimeout, address: url.Location, @@ -79,12 +72,11 @@ func NewExchangeClient(url common.URL, client Client, connectTimeout time.Durati return nil } } - - client.SetResponseHandler(exchangeClient) + exchangeClient.IncreaseActiveNumber() return exchangeClient } -func (cl *ExchangeClient) doInit(url common.URL) error { +func (cl *ExchangeClient) doInit(url *common.URL) error { if cl.init { return nil } @@ -101,8 +93,23 @@ func (cl *ExchangeClient) doInit(url common.URL) error { return nil } +// increase number of service using client +func (client *ExchangeClient) IncreaseActiveNumber() uint32 { + return client.activeNum.Add(1) +} + +// decrease number of service using client +func (client *ExchangeClient) DecreaseActiveNumber() uint32 { + return client.activeNum.Sub(1) +} + +// get number of service using client +func (client *ExchangeClient) GetActiveNumber() uint32 { + return client.activeNum.Load() +} + // two way request -func (client *ExchangeClient) Request(invocation *protocol.Invocation, url common.URL, timeout time.Duration, +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 @@ -132,7 +139,7 @@ func (client *ExchangeClient) Request(invocation *protocol.Invocation, url commo } // async two way request -func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url common.URL, timeout time.Duration, +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 @@ -158,7 +165,7 @@ func (client *ExchangeClient) AsyncRequest(invocation *protocol.Invocation, url } // oneway request -func (client *ExchangeClient) Send(invocation *protocol.Invocation, url common.URL, timeout time.Duration) error { +func (client *ExchangeClient) Send(invocation *protocol.Invocation, url *common.URL, timeout time.Duration) error { if er := client.doInit(url); er != nil { return er } @@ -184,46 +191,7 @@ func (client *ExchangeClient) Close() { client.init = false } -// 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 +// IsAvailable to check if the underlying network client is available yet. +func (client *ExchangeClient) IsAvailable() bool { + return client.client.IsAvailable() } diff --git a/remoting/exchange_server.go b/remoting/exchange_server.go index a31e994d7411df2da94c210a1b3d4b24aae9fcba..a8d7c73f305a42e0402861817e5dbe2abfcfdd01 100644 --- a/remoting/exchange_server.go +++ b/remoting/exchange_server.go @@ -32,11 +32,11 @@ type Server interface { // This is abstraction level. it is like facade. type ExchangeServer struct { Server Server - Url common.URL + Url *common.URL } // Create ExchangeServer -func NewExchangeServer(url common.URL, server Server) *ExchangeServer { +func NewExchangeServer(url *common.URL, server Server) *ExchangeServer { exchangServer := &ExchangeServer{ Server: server, Url: url, diff --git a/remoting/getty/config.go b/remoting/getty/config.go index dcf59d0821b90c052f736d1976400a1efe7d3445..b6aa08206a1497b2bac5904eef7b86bde11a61ac 100644 --- a/remoting/getty/config.go +++ b/remoting/getty/config.go @@ -54,6 +54,14 @@ type ( ServerConfig struct { SSLEnabled bool + // heartbeat + HeartbeatPeriod string `default:"60s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"` + heartbeatPeriod time.Duration + + // heartbeat timeout + HeartbeatTimeout string `default:"5s" yaml:"heartbeat_timeout" json:"heartbeat_timeout,omitempty"` + heartbeatTimeout time.Duration + // session SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` sessionTimeout time.Duration @@ -76,9 +84,13 @@ type ( ConnectionNum int `default:"16" yaml:"connection_number" json:"connection_number,omitempty"` // heartbeat - HeartbeatPeriod string `default:"15s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"` + HeartbeatPeriod string `default:"60s" yaml:"heartbeat_period" json:"heartbeat_period,omitempty"` heartbeatPeriod time.Duration + // heartbeat timeout + HeartbeatTimeout string `default:"5s" yaml:"heartbeat_timeout" json:"heartbeat_timeout,omitempty"` + heartbeatTimeout time.Duration + // session SessionTimeout string `default:"60s" yaml:"session_timeout" json:"session_timeout,omitempty"` sessionTimeout time.Duration @@ -188,6 +200,12 @@ func (c *ClientConfig) CheckValidity() error { c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan)) } + if len(c.HeartbeatTimeout) == 0 { + c.heartbeatTimeout = 60 * time.Second + } else if c.heartbeatTimeout, err = time.ParseDuration(c.HeartbeatTimeout); err != nil { + return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatTimeout{%#v})", c.HeartbeatTimeout) + } + if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) } @@ -199,6 +217,23 @@ func (c *ClientConfig) CheckValidity() error { func (c *ServerConfig) CheckValidity() error { var err error + if len(c.HeartbeatPeriod) == 0 { + c.heartbeatPeriod = 60 * time.Second + } else if c.heartbeatPeriod, err = time.ParseDuration(c.HeartbeatPeriod); err != nil { + return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatPeroid{%#v})", c.HeartbeatPeriod) + } + + if c.heartbeatPeriod >= time.Duration(config.MaxWheelTimeSpan) { + return perrors.WithMessagef(err, "heartbeat_period %s should be less than %s", + c.HeartbeatPeriod, time.Duration(config.MaxWheelTimeSpan)) + } + + if len(c.HeartbeatTimeout) == 0 { + c.heartbeatTimeout = 60 * time.Second + } else if c.heartbeatTimeout, err = time.ParseDuration(c.HeartbeatTimeout); err != nil { + return perrors.WithMessagef(err, "time.ParseDuration(HeartbeatTimeout{%#v})", c.HeartbeatTimeout) + } + if c.sessionTimeout, err = time.ParseDuration(c.SessionTimeout); err != nil { return perrors.WithMessagef(err, "time.ParseDuration(SessionTimeout{%#v})", c.SessionTimeout) } diff --git a/remoting/getty/dubbo_codec_for_test.go b/remoting/getty/dubbo_codec_for_test.go index b91fc9f4ccf69299870f9daf3707521d913cd4c0..9afc18a9aaf477588a2fbf051096494b6f887fa2 100644 --- a/remoting/getty/dubbo_codec_for_test.go +++ b/remoting/getty/dubbo_codec_for_test.go @@ -55,15 +55,15 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer if !ok { return nil, perrors.Errorf("encode request failed for parameter type :%+v", request) } - invocation := *invoc + tmpInvocation := 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))) + svc.Path = tmpInvocation.AttachmentsByKey(constant.PATH_KEY, "") + svc.Interface = tmpInvocation.AttachmentsByKey(constant.INTERFACE_KEY, "") + svc.Version = tmpInvocation.AttachmentsByKey(constant.VERSION_KEY, "") + svc.Group = tmpInvocation.AttachmentsByKey(constant.GROUP_KEY, "") + svc.Method = tmpInvocation.MethodName() + timeout, err := strconv.Atoi(tmpInvocation.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) @@ -71,7 +71,7 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer svc.Timeout = time.Duration(timeout) header := impl.DubboHeader{} - serialization := invocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) + serialization := tmpInvocation.AttachmentsByKey(constant.SERIALIZATION_KEY, constant.HESSIAN2_SERIALIZATION) if serialization == constant.PROTOBUF_SERIALIZATION { header.SerialID = constant.S_Proto } else { @@ -87,7 +87,7 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer pkg := &impl.DubboPackage{ Header: header, Service: svc, - Body: impl.NewRequestPayload(invocation.Arguments(), invocation.Attachments()), + Body: impl.NewRequestPayload(tmpInvocation.Arguments(), tmpInvocation.Attachments()), Err: nil, Codec: impl.NewDubboCodec(nil), } @@ -99,7 +99,7 @@ func (c *DubboTestCodec) EncodeRequest(request *remoting.Request) (*bytes.Buffer return pkg.Marshal() } -// encode heartbeart request +// encode heartbeat request func (c *DubboTestCodec) encodeHeartbeartReqeust(request *remoting.Request) (*bytes.Buffer, error) { header := impl.DubboHeader{ Type: impl.PackageHeartbeat, @@ -171,10 +171,7 @@ func (c *DubboTestCodec) Decode(data []byte) (remoting.DecodeResult, int, error) } func (c *DubboTestCodec) isRequest(data []byte) bool { - if data[2]&byte(0x80) == 0x00 { - return false - } - return true + return data[2]&byte(0x80) != 0x00 } // decode request @@ -230,7 +227,6 @@ func (c *DubboTestCodec) decodeRequest(data []byte) (*remoting.Request, int, err 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) @@ -240,7 +236,7 @@ func (c *DubboTestCodec) decodeResponse(data []byte) (*remoting.Response, int, e } return nil, 0, perrors.WithStack(err) } - response = &remoting.Response{ + response := &remoting.Response{ ID: pkg.Header.ID, //Version: pkg.Header., SerialID: pkg.Header.SerialID, diff --git a/remoting/getty/getty_client.go b/remoting/getty/getty_client.go index 6af3971f5c1f9ff5b0fafbc00ae2ba3f44eb34b5..774f999e8a5ae61edc383bbf904c90aae15293bb 100644 --- a/remoting/getty/getty_client.go +++ b/remoting/getty/getty_client.go @@ -19,6 +19,7 @@ package getty import ( "math/rand" + "sync" "time" ) @@ -38,14 +39,12 @@ import ( ) 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") + errClientReadTimeout = perrors.New("maybe the client read timeout or fail to decode tcp stream in Writer.Write") clientConf *ClientConfig - clientGrpool *gxsync.TaskPool + clientGrpool gxsync.GenericTaskPool ) // it is init client for single protocol. @@ -101,16 +100,13 @@ func SetClientConf(c ClientConfig) { } func setClientGrpool() { - if clientConf.GrPoolSize > 1 { - clientGrpool = gxsync.NewTaskPool(gxsync.WithTaskPoolTaskPoolSize(clientConf.GrPoolSize), gxsync.WithTaskPoolTaskQueueLength(clientConf.QueueLen), - gxsync.WithTaskPoolTaskQueueNumber(clientConf.QueueNumber)) - } + clientGrpool = gxsync.NewTaskPoolSimple(clientConf.GrPoolSize) } // Options : param config type Options struct { // connect timeout - // remove request timeout, it will be calulate for every request + // remove request timeout, it will be calculate for every request ConnectTimeout time.Duration // request timeout RequestTimeout time.Duration @@ -118,13 +114,13 @@ type Options struct { // 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 + addr string + opts Options + conf ClientConfig + mux sync.RWMutex + pool *gettyRPCClientPool + codec remoting.Codec + ExchangeClient *remoting.ExchangeClient } // create client @@ -146,12 +142,9 @@ func NewClient(opt Options) *Client { 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 { +func (c *Client) Connect(url *common.URL) error { initClient(url.Protocol) c.conf = *clientConf // new client @@ -170,10 +163,13 @@ func (c *Client) Connect(url common.URL) error { // close network connection func (c *Client) Close() { - if c.pool != nil { - c.pool.close() - } + c.mux.Lock() + p := c.pool c.pool = nil + c.mux.Unlock() + if p != nil { + p.close() + } } // send request @@ -185,8 +181,15 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, respo if session == nil { return errSessionNotExist } - - if err = c.transfer(session, request, timeout); err != nil { + var ( + totalLen int + sendLen int + ) + if totalLen, sendLen, err = c.transfer(session, request, timeout); err != nil { + if sendLen != 0 && totalLen != sendLen { + logger.Warnf("start to close the session at request because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) + go c.Close() + } return perrors.WithStack(err) } @@ -204,7 +207,20 @@ func (c *Client) Request(request *remoting.Request, timeout time.Duration, respo return perrors.WithStack(err) } +// isAvailable returns true if the connection is available, or it can be re-established. +func (c *Client) IsAvailable() bool { + client, _, err := c.selectSession(c.addr) + return err == nil && + // defensive check + client != nil +} + func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, error) { + c.mux.RLock() + defer c.mux.RUnlock() + if c.pool == nil { + return nil, nil, perrors.New("client pool have been closed") + } rpcClient, err := c.pool.getGettyRpcClient(addr) if err != nil { return nil, nil, perrors.WithStack(err) @@ -212,16 +228,7 @@ func (c *Client) selectSession(addr string) (*gettyRPCClient, getty.Session, 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) +func (c *Client) transfer(session getty.Session, request *remoting.Request, timeout time.Duration) (int, int, error) { + totalLen, sendLen, err := session.WritePkg(request, timeout) + return totalLen, sendLen, perrors.WithStack(err) } diff --git a/remoting/getty/getty_client_test.go b/remoting/getty/getty_client_test.go index 41ca3108a8a8d578c6aaaf374dc9f5fa6300a8b0..c32e0c23f4b3705c3400a96a8bb1247c7e3eae40 100644 --- a/remoting/getty/getty_client_test.go +++ b/remoting/getty/getty_client_test.go @@ -51,7 +51,7 @@ func TestRunSuite(t *testing.T) { svr.Stop() } -func testRequestOneWay(t *testing.T, svr *Server, url common.URL, client *Client) { +func testRequestOneWay(t *testing.T, svr *Server, url *common.URL, client *Client) { request := remoting.NewRequest("2.0.2") up := &UserProvider{} @@ -80,19 +80,20 @@ func setAttachment(invocation *invocation.RPCInvocation, attachments map[string] } } -func getClient(url common.URL) *Client { +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) + if err := client.Connect(url); err != nil { + return nil + } return client } -func testClient_Call(t *testing.T, svr *Server, url common.URL, c *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) @@ -212,7 +213,9 @@ func testGetUser3(t *testing.T, c *Client) { request := remoting.NewRequest("2.0.2") invocation := createInvocation("GetUser3", nil, nil, []interface{}{}, []reflect.Value{}) - attachment := map[string]string{INTERFACE_KEY: "com.ikurento.user.UserProvider"} + attachment := map[string]string{ + INTERFACE_KEY: "com.ikurento.user.UserProvider", + } setAttachment(invocation, attachment) request.Data = invocation request.Event = false @@ -309,7 +312,7 @@ func testGetUser61(t *testing.T, c *Client) { assert.Equal(t, User{Id: "1", Name: ""}, *user) } -func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Client) { +func testClient_AsyncCall(t *testing.T, svr *Server, url *common.URL, client *Client) { user := &User{} lock := sync.Mutex{} request := remoting.NewRequest("2.0.2") @@ -337,12 +340,12 @@ func testClient_AsyncCall(t *testing.T, svr *Server, url common.URL, client *Cli time.Sleep(1 * time.Second) } -func InitTest(t *testing.T) (*Server, common.URL) { +func InitTest(t *testing.T) (*Server, *common.URL) { hessian.RegisterPOJO(&User{}) remoting.RegistryCodec("dubbo", &DubboTestCodec{}) - methods, err := common.ServiceMap.Register("", "dubbo", &UserProvider{}) + 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) @@ -388,14 +391,16 @@ func InitTest(t *testing.T) (*Server, common.URL) { }}) assert.NoError(t, srvConf.CheckValidity()) - url, err := common.NewURL("dubbo://127.0.0.1:20060/UserProvider?anyhost=true&" + + url, err := common.NewURL("dubbo://127.0.0.1:20060/com.ikurento.user.UserProvider?anyhost=true&" + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + "environment=dev&interface=com.ikurento.user.UserProvider&ip=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") + assert.NoError(t, err) // init server userProvider := &UserProvider{} - common.ServiceMap.Register("", url.Protocol, userProvider) + _, err = common.ServiceMap.Register("", url.Protocol, "", "0.0.1", userProvider) + assert.NoError(t, err) invoker := &proxy_factory.ProxyInvoker{ BaseInvoker: *protocol.NewBaseInvoker(url), } @@ -428,7 +433,7 @@ type ( } UserProvider struct { - user map[string]User + //user map[string]User } ) diff --git a/remoting/getty/getty_server.go b/remoting/getty/getty_server.go index 7c8fa29a622b4b746801d1d2ce878f8b353c1a76..4930a6ad0ee0dbcde50a16e8df6b6245fb87a617 100644 --- a/remoting/getty/getty_server.go +++ b/remoting/getty/getty_server.go @@ -41,8 +41,7 @@ import ( ) var ( - srvConf *ServerConfig - srvGrpool *gxsync.TaskPool + srvConf *ServerConfig ) func initServer(protocol string) { @@ -76,7 +75,6 @@ func initServer(protocol string) { if err := srvConf.CheckValidity(); err != nil { panic(err) } - SetServerGrpool() } // SetServerConfig set dubbo server config. @@ -87,7 +85,6 @@ func SetServerConfig(s ServerConfig) { logger.Warnf("[ServerConfig CheckValidity] error: %v", err) return } - SetServerGrpool() } // GetServerConfig get getty server config. @@ -95,17 +92,6 @@ func GetServerConfig() ServerConfig { return *srvConf } -// 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), - ) - } -} - // Server define getty server type Server struct { conf ServerConfig @@ -117,7 +103,7 @@ type Server struct { } // NewServer create a new Server -func NewServer(url common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { +func NewServer(url *common.URL, handlers func(*invocation.RPCInvocation) protocol.RPCResult) *Server { //init initServer(url.Protocol) @@ -151,16 +137,14 @@ func (s *Server) newSession(session getty.Session) error { session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) 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.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6)) session.SetWaitTime(conf.GettySessionParam.waitTimeout) logger.Debugf("server accepts new session:%s\n", session.Stat()) - session.SetTaskPool(srvGrpool) return nil } - if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { + if _, ok = session.Conn().(*net.TCPConn); !ok { panic(fmt.Sprintf("%s, session.conn{%#v} is not tcp connection\n", session.Stat(), session.Conn())) } @@ -192,13 +176,11 @@ func (s *Server) newSession(session getty.Session) error { session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) 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.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6)) session.SetWaitTime(conf.GettySessionParam.waitTimeout) logger.Debugf("server accepts new session: %s", session.Stat()) - session.SetTaskPool(srvGrpool) return nil } @@ -210,17 +192,15 @@ func (s *Server) Start() { ) addr = s.addr + serverOpts := []getty.ServerOption{getty.WithLocalAddress(addr)} if s.conf.SSLEnabled { - tcpServer = getty.NewTCPServer( - getty.WithLocalAddress(addr), - getty.WithServerSslEnabled(s.conf.SSLEnabled), - getty.WithServerTlsConfigBuilder(config.GetServerTlsConfigBuilder()), - ) - } else { - tcpServer = getty.NewTCPServer( - getty.WithLocalAddress(addr), - ) + serverOpts = append(serverOpts, getty.WithServerSslEnabled(s.conf.SSLEnabled), + getty.WithServerTlsConfigBuilder(config.GetServerTlsConfigBuilder())) } + + serverOpts = append(serverOpts, getty.WithServerTaskPool(gxsync.NewTaskPoolSimple(s.conf.GrPoolSize))) + + tcpServer = getty.NewTCPServer(serverOpts...) tcpServer.RunEventLoop(s.newSession) logger.Debugf("s bind addr{%s} ok!", s.addr) s.tcpServer = tcpServer diff --git a/remoting/getty/listener.go b/remoting/getty/listener.go index 196aa20a087e7562f88865d6bb40e6d8feac1502..5443d371021c1235601a30a2e0011eeb9545713c 100644 --- a/remoting/getty/listener.go +++ b/remoting/getty/listener.go @@ -44,7 +44,8 @@ const ( ) var ( - errTooManySessions = perrors.New("too many sessions") + errTooManySessions = perrors.New("too many sessions") + errHeartbeatReadTimeout = perrors.New("heartbeat read timeout") ) type rpcSession struct { @@ -66,7 +67,8 @@ func (s *rpcSession) GetReqNum() int32 { // nolint type RpcClientHandler struct { - conn *gettyRPCClient + conn *gettyRPCClient + timeoutTimes int } // nolint @@ -99,7 +101,7 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { logger.Errorf("illegal package") return } - // get heartbeart request from server + // get heartbeat request from server if result.IsRequest { req := result.Result.(*remoting.Request) if req.Event { @@ -109,25 +111,21 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { resp.Event = req.Event resp.SerialID = req.SerialID resp.Version = "2.0.2" - reply(session, resp, hessian.PackageHeartbeat) + reply(session, resp) return } - logger.Errorf("illegal request but not heartbeart. {%#v}", req) + logger.Errorf("illegal request but not heartbeat. {%#v}", req) return } - + h.timeoutTimes = 0 p := result.Result.(*remoting.Response) - // get heartbeart + // get heartbeat 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) + p.Handle() return } @@ -135,12 +133,12 @@ func (h *RpcClientHandler) OnMessage(session getty.Session, pkg interface{}) { h.conn.updateSession(session) - h.conn.pool.rpcClient.responseHandler.Handler(p) + p.Handle() } // 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) + rs, err := h.conn.getClientRpcSession(session) if err != nil { logger.Errorf("client.getClientSession(session{%s}) = error{%v}", session.Stat(), perrors.WithStack(err)) @@ -148,12 +146,27 @@ func (h *RpcClientHandler) OnCron(session getty.Session) { } if h.conn.pool.rpcClient.conf.sessionTimeout.Nanoseconds() < time.Since(session.GetActive()).Nanoseconds() { logger.Warnf("session{%s} timeout{%s}, reqNum{%d}", - session.Stat(), time.Since(session.GetActive()).String(), rpcSession.reqNum) + session.Stat(), time.Since(session.GetActive()).String(), rs.reqNum) h.conn.removeSession(session) // -> h.conn.close() -> h.conn.pool.remove(h.conn) return } - h.conn.pool.rpcClient.heartbeat(session) + heartbeatCallBack := func(err error) { + if err != nil { + logger.Warnf("failed to send heartbeat, error{%v}", err) + if h.timeoutTimes >= 3 { + h.conn.removeSession(session) + return + } + h.timeoutTimes++ + return + } + h.timeoutTimes = 0 + } + + if err := heartbeat(session, h.conn.pool.rpcClient.conf.heartbeatTimeout, heartbeatCallBack); err != nil { + logger.Warnf("failed to send heartbeat, error{%v}", err) + } } // ////////////////////////////////////////// @@ -167,6 +180,7 @@ type RpcServerHandler struct { sessionMap map[getty.Session]*rpcSession rwlock sync.RWMutex server *Server + timeoutTimes int } // nolint @@ -223,13 +237,22 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { } h.rwlock.Unlock() - decodeResult, ok := pkg.(remoting.DecodeResult) - if !ok { + decodeResult, drOK := pkg.(remoting.DecodeResult) + if !drOK { logger.Errorf("illegal package{%#v}", pkg) return } if !decodeResult.IsRequest { - logger.Errorf("illegal package for it is response type. {%#v}", pkg) + res := decodeResult.Result.(*remoting.Response) + if res.Event { + logger.Debugf("get rpc heartbeat response{%#v}", res) + if res.Error != nil { + logger.Errorf("rpc heartbeat response{error: %#v}", res.Error) + } + res.Handle() + return + } + logger.Errorf("illegal package but not heartbeat. {%#v}", pkg) return } req := decodeResult.Result.(*remoting.Request) @@ -243,7 +266,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { // heartbeat if req.Event { logger.Debugf("get rpc heartbeat request{%#v}", resp) - reply(session, resp, hessian.PackageHeartbeat) + reply(session, resp) return } @@ -264,7 +287,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { if !req.TwoWay { return } - reply(session, resp, hessian.PackageResponse) + reply(session, resp) } }() @@ -272,7 +295,6 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { 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() @@ -283,7 +305,7 @@ func (h *RpcServerHandler) OnMessage(session getty.Session, pkg interface{}) { return } resp.Result = result - reply(session, resp, hessian.PackageResponse) + reply(session, resp) } // OnCron check the session health periodic. if the session's sessionTimeout has reached, just close the session @@ -310,10 +332,60 @@ func (h *RpcServerHandler) OnCron(session getty.Session) { h.rwlock.Unlock() session.Close() } + + heartbeatCallBack := func(err error) { + if err != nil { + logger.Warnf("failed to send heartbeat, error{%v}", err) + if h.timeoutTimes >= 3 { + h.rwlock.Lock() + delete(h.sessionMap, session) + h.rwlock.Unlock() + session.Close() + return + } + h.timeoutTimes++ + return + } + h.timeoutTimes = 0 + } + + if err := heartbeat(session, h.server.conf.heartbeatTimeout, heartbeatCallBack); err != nil { + logger.Warnf("failed to send heartbeat, error{%v}", err) + } } -func reply(session getty.Session, resp *remoting.Response, tp hessian.PackageType) { - if err := session.WritePkg(resp, WritePkg_Timeout); err != nil { +func reply(session getty.Session, resp *remoting.Response) { + if totalLen, sendLen, err := session.WritePkg(resp, WritePkg_Timeout); err != nil { + if sendLen != 0 && totalLen != sendLen { + logger.Warnf("start to close the session at replying because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) + go session.Close() + } logger.Errorf("WritePkg error: %#v, %#v", perrors.WithStack(err), resp) } } + +func heartbeat(session getty.Session, timeout time.Duration, callBack func(err error)) error { + req := remoting.NewRequest("2.0.2") + req.TwoWay = true + req.Event = true + resp := remoting.NewPendingResponse(req.ID) + remoting.AddPendingResponse(resp) + totalLen, sendLen, err := session.WritePkg(req, 3*time.Second) + if sendLen != 0 && totalLen != sendLen { + logger.Warnf("start to close the session at heartbeat because %d of %d bytes data is sent success. err:%+v", sendLen, totalLen, err) + go session.Close() + } + + go func() { + var err1 error + select { + case <-getty.GetTimeWheel().After(timeout): + err1 = errHeartbeatReadTimeout + case <-resp.Done: + err1 = resp.Err + } + callBack(err1) + }() + + return perrors.WithStack(err) +} diff --git a/remoting/getty/listener_test.go b/remoting/getty/listener_test.go index 7e7ac5fed440a02188057d520a944b48c8bf7b64..2700ed8cd8e03f0a37c2d978cd03932ef8d5f8cb 100644 --- a/remoting/getty/listener_test.go +++ b/remoting/getty/listener_test.go @@ -23,14 +23,14 @@ import ( ) 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 @@ -47,7 +47,7 @@ func TestRebuildCtx(t *testing.T) { assert.Nil(t, ctx.Value(constant.TRACING_REMOTE_SPAN_CTX)) span, ctx := opentracing.StartSpanFromContext(ctx, "Test-Client") - + assert.NotNil(t, ctx) err := injectTraceCtx(span, inv) assert.NoError(t, err) @@ -63,7 +63,7 @@ func TestRebuildCtx(t *testing.T) { // 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()) + ctx := context.WithValue(context.Background(), constant.DubboCtxKey("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, diff --git a/remoting/getty/opentracing.go b/remoting/getty/opentracing.go index 7db733cbe919f2bef46cfc477bda836dc2da0d45..4ba4fde9d880dee8437f8bb3dfdfec910f96fdcc 100644 --- a/remoting/getty/opentracing.go +++ b/remoting/getty/opentracing.go @@ -35,14 +35,6 @@ func injectTraceCtx(currentSpan opentracing.Span, inv *invocation_impl.RPCInvoca 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 { diff --git a/remoting/getty/pool.go b/remoting/getty/pool.go index a07243219ca161e0472e548c45066b015fb424a2..2b1cdfe2f49bfee0d5291f2eaf4183db71b8aa31 100644 --- a/remoting/getty/pool.go +++ b/remoting/getty/pool.go @@ -60,40 +60,46 @@ func newGettyRPCClientConn(pool *gettyRPCClientPool, addr string) (*gettyRPCClie sslEnabled bool ) sslEnabled = pool.sslEnabled + clientOpts := []getty.ClientOption{ + getty.WithServerAddress(addr), + getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)), + getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval), + } if sslEnabled { - gettyClient = getty.NewTCPClient( - getty.WithServerAddress(addr), - getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)), - getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval), - getty.WithClientSslEnabled(pool.sslEnabled), - getty.WithClientTlsConfigBuilder(config.GetClientTlsConfigBuilder()), - ) - } else { - gettyClient = getty.NewTCPClient( - getty.WithServerAddress(addr), - getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)), - getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval), - ) + clientOpts = append(clientOpts, getty.WithClientSslEnabled(pool.sslEnabled), getty.WithClientTlsConfigBuilder(config.GetClientTlsConfigBuilder())) + } + + if clientGrpool != nil { + clientOpts = append(clientOpts, getty.WithClientTaskPool(clientGrpool)) } + + gettyClient = getty.NewTCPClient(clientOpts...) c := &gettyRPCClient{ addr: addr, pool: pool, gettyClient: gettyClient, } go c.gettyClient.RunEventLoop(c.newSession) + idx := 1 - times := int(pool.rpcClient.opts.ConnectTimeout / 1e6) + start := time.Now() + connectTimeout := pool.rpcClient.opts.ConnectTimeout for { idx++ if c.isAvailable() { break } - if idx > times { + if time.Since(start) > connectTimeout { c.gettyClient.Close() - return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %f seconds", addr, float32(times)/1000)) + return nil, perrors.New(fmt.Sprintf("failed to create client connection to %s in %s", addr, connectTimeout)) } - time.Sleep(1e6) + + interval := time.Millisecond * time.Duration(idx) + if interval > time.Duration(100e6) { + interval = 100e6 // 100 ms + } + time.Sleep(interval) } logger.Debug("client init ok") c.updateActive(time.Now().Unix()) @@ -129,40 +135,44 @@ func (c *gettyRPCClient) newSession(session getty.Session) error { session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) session.SetPkgHandler(NewRpcClientPackageHandler(c.pool.rpcClient)) session.SetEventListener(NewRpcClientHandler(c)) - session.SetWQLen(conf.GettySessionParam.PkgWQSize) session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout) session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout) session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6)) session.SetWaitTime(conf.GettySessionParam.waitTimeout) logger.Debugf("client new session:%s\n", session.Stat()) - session.SetTaskPool(clientGrpool) return nil } if tcpConn, ok = session.Conn().(*net.TCPConn); !ok { 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 err := tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay); err != nil { + logger.Error("tcpConn.SetNoDelay() = error:%v", err) + } + if err := tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive); err != nil { + logger.Error("tcpConn.SetKeepAlive() = error:%v", err) + } if conf.GettySessionParam.TcpKeepAlive { - tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod) + if err := tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod); err != nil { + logger.Error("tcpConn.SetKeepAlivePeriod() = error:%v", err) + } + } + if err := tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize); err != nil { + logger.Error("tcpConn.SetReadBuffer() = error:%v", err) + } + if err := tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize); err != nil { + logger.Error("tcpConn.SetWriteBuffer() = error:%v", err) } - tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize) - tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize) session.SetName(conf.GettySessionParam.SessionName) session.SetMaxMsgLen(conf.GettySessionParam.MaxMsgLen) session.SetPkgHandler(NewRpcClientPackageHandler(c.pool.rpcClient)) session.SetEventListener(NewRpcClientHandler(c)) - session.SetWQLen(conf.GettySessionParam.PkgWQSize) session.SetReadTimeout(conf.GettySessionParam.tcpReadTimeout) session.SetWriteTimeout(conf.GettySessionParam.tcpWriteTimeout) session.SetCronPeriod((int)(conf.heartbeatPeriod.Nanoseconds() / 1e6)) session.SetWaitTime(conf.GettySessionParam.waitTimeout) logger.Debugf("client new session:%s\n", session.Stat()) - - session.SetTaskPool(clientGrpool) - return nil } @@ -253,33 +263,29 @@ func (c *gettyRPCClient) updateSession(session getty.Session) { func (c *gettyRPCClient) getClientRpcSession(session getty.Session) (rpcSession, error) { var ( - err error - rpcSession rpcSession + err error + rs rpcSession ) c.lock.RLock() defer c.lock.RUnlock() if c.sessions == nil { - return rpcSession, errClientClosed + return rs, errClientClosed } err = errSessionNotExist for _, s := range c.sessions { if s.session == session { - rpcSession = *s + rs = *s err = nil break } } - return rpcSession, perrors.WithStack(err) + return rs, perrors.WithStack(err) } func (c *gettyRPCClient) isAvailable() bool { - if c.selectSession() == nil { - return false - } - - return true + return c.selectSession() != nil } func (c *gettyRPCClient) close() error { @@ -297,9 +303,7 @@ func (c *gettyRPCClient) close() error { c.gettyClient = nil sessions = make([]*rpcSession, 0, len(c.sessions)) - for _, s := range c.sessions { - sessions = append(sessions, s) - } + sessions = append(sessions, c.sessions...) c.sessions = c.sessions[:0] }() @@ -352,16 +356,16 @@ func (p *gettyRPCClientPool) close() { } func (p *gettyRPCClientPool) getGettyRpcClient(addr string) (*gettyRPCClient, error) { - conn, err := p.get() - if err == nil && conn == nil { + conn, connErr := p.get() + if connErr == nil && conn == nil { // create new conn - rpcClientConn, err := newGettyRPCClientConn(p, addr) - if err == nil { + rpcClientConn, rpcErr := newGettyRPCClientConn(p, addr) + if rpcErr == nil { p.put(rpcClientConn) } - return rpcClientConn, perrors.WithStack(err) + return rpcClientConn, perrors.WithStack(rpcErr) } - return conn, perrors.WithStack(err) + return conn, perrors.WithStack(connErr) } func (p *gettyRPCClientPool) get() (*gettyRPCClient, error) { diff --git a/remoting/getty/readwriter.go b/remoting/getty/readwriter.go index 66c33a6db63f714e686382e199e19efd693e1dda..61062dfe506bdd68cab9be922ac085312e3a52d2 100644 --- a/remoting/getty/readwriter.go +++ b/remoting/getty/readwriter.go @@ -18,6 +18,7 @@ package getty import ( + "errors" "reflect" ) @@ -52,8 +53,7 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface 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 { + if errors.Is(err, hessian.ErrHeaderNotEnough) || errors.Is(err, hessian.ErrBodyNotEnough) { return nil, 0, nil } @@ -68,18 +68,27 @@ func (p *RpcClientPackageHandler) Read(ss getty.Session, data []byte) (interface // 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") + if ok { + 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 } - 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) + res, ok := pkg.(*remoting.Response) + if ok { + buf, err := (p.client.codec).EncodeResponse(res) + if err != nil { + logger.Warnf("binary.Write(res{%#v}) = err{%#v}", req, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + return buf.Bytes(), nil } - return buf.Bytes(), nil + logger.Errorf("illegal pkg:%+v\n", pkg) + return nil, perrors.New("invalid rpc request") } //////////////////////////////////////////// @@ -105,7 +114,7 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface req, length, err := (p.server.codec).Decode(data) //resp,len, err := (*p.).DecodeResponse(buf) if err != nil { - if err == hessian.ErrHeaderNotEnough || err == hessian.ErrBodyNotEnough { + if errors.Is(err, hessian.ErrHeaderNotEnough) || errors.Is(err, hessian.ErrBodyNotEnough) { return nil, 0, nil } @@ -120,16 +129,26 @@ func (p *RpcServerPackageHandler) Read(ss getty.Session, data []byte) (interface // 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") + if ok { + 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 } - 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) + req, ok := pkg.(*remoting.Request) + if ok { + buf, err := (p.server.codec).EncodeRequest(req) + if err != nil { + logger.Warnf("binary.Write(req{%#v}) = err{%#v}", res, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + return buf.Bytes(), nil } - return buf.Bytes(), nil + logger.Errorf("illegal pkg:%+v\n, it is %+v", pkg, reflect.TypeOf(pkg)) + return nil, perrors.New("invalid rpc response") + } diff --git a/remoting/getty/readwriter_test.go b/remoting/getty/readwriter_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c54c385b0e1310bdd9a5884b9f5cb6cd6b4c475a --- /dev/null +++ b/remoting/getty/readwriter_test.go @@ -0,0 +1,173 @@ +/* + * 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 ( + "context" + "reflect" + "testing" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/stretchr/testify/assert" +) + +import ( + "github.com/apache/dubbo-go/common" + "github.com/apache/dubbo-go/common/constant" + "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "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 TestTCPPackageHandle(t *testing.T) { + svr, url := getServer(t) + client := getClient(url) + testDecodeTCPPackage(t, svr, client) + svr.Stop() +} + +func testDecodeTCPPackage(t *testing.T, svr *Server, client *Client) { + request := remoting.NewRequest("2.0.2") + ap := &AdminProvider{} + rpcInvocation := createInvocation("GetAdmin", nil, nil, []interface{}{[]interface{}{"1", "username"}}, + []reflect.Value{reflect.ValueOf([]interface{}{"1", "username"}), reflect.ValueOf(ap)}) + attachment := map[string]string{ + constant.INTERFACE_KEY: "com.ikurento.user.AdminProvider", + constant.PATH_KEY: "AdminProvider", + constant.VERSION_KEY: "1.0.0", + } + setAttachment(rpcInvocation, attachment) + request.Data = rpcInvocation + request.Event = false + request.TwoWay = false + + pkgWriteHandler := NewRpcClientPackageHandler(client) + pkgBytes, err := pkgWriteHandler.Write(nil, request) + assert.NoError(t, err) + pkgReadHandler := NewRpcServerPackageHandler(svr) + _, pkgLen, err := pkgReadHandler.Read(nil, pkgBytes) + assert.NoError(t, err) + assert.Equal(t, pkgLen, len(pkgBytes)) + + // simulate incomplete tcp package + incompletePkgLen := len(pkgBytes) - 10 + assert.True(t, incompletePkgLen >= impl.HEADER_LENGTH, "header buffer too short") + incompletePkg := pkgBytes[0 : incompletePkgLen-1] + pkg, pkgLen, err := pkgReadHandler.Read(nil, incompletePkg) + assert.NoError(t, err) + assert.Equal(t, pkg, nil) + assert.Equal(t, pkgLen, 0) +} + +func getServer(t *testing.T) (*Server, *common.URL) { + hessian.RegisterPOJO(&User{}) + remoting.RegistryCodec("dubbo", &DubboTestCodec{}) + + methods, err := common.ServiceMap.Register("com.ikurento.user.AdminProvider", "dubbo", "", "", &AdminProvider{}) + assert.NoError(t, err) + assert.Equal(t, "GetAdmin", 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:20061/com.ikurento.user.AdminProvider?anyhost=true&" + + "application=BDTService&category=providers&default.timeout=10000&dubbo=dubbo-provider-golang-1.0.0&" + + "environment=dev&interface=com.ikurento.user.AdminProvider&ip=127.0.0.1&methods=GetAdmin%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=AdminProvider") + assert.NoError(t, err) + // init server + adminProvider := &AdminProvider{} + _, err = common.ServiceMap.Register("com.ikurento.user.AdminProvider", url.Protocol, "", "0.0.1", adminProvider) + assert.NoError(t, err) + 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 +} + +type AdminProvider struct { +} + +func (a *AdminProvider) GetAdmin(ctx context.Context, req []interface{}, rsp *User) error { + rsp.Id = req[0].(string) + rsp.Name = req[1].(string) + return nil +} + +func (a *AdminProvider) Reference() string { + return "AdminProvider" +} diff --git a/remoting/kubernetes/client.go b/remoting/kubernetes/client.go index 0a0548959a3e6d839321d03a627bb6aba66d8474..ce6bcccea50643cd49f3f99fcf4b86e73309b28c 100644 --- a/remoting/kubernetes/client.go +++ b/remoting/kubernetes/client.go @@ -47,17 +47,17 @@ type Client struct { } // newClient returns Client instance for registry -func newClient(url common.URL) (*Client, error) { - - ctx, cancel := context.WithCancel(context.Background()) - +func newClient(url *common.URL) (*Client, error) { // read type r, err := strconv.Atoi(url.GetParams().Get(constant.ROLE_KEY)) if err != nil { return nil, perrors.WithMessage(err, "atoi role") } + ctx, cancel := context.WithCancel(context.Background()) + controller, err := newDubboRegistryController(ctx, common.RoleType(r), GetInClusterKubernetesClient) if err != nil { + cancel() return nil, perrors.WithMessage(err, "new dubbo-registry controller") } @@ -186,12 +186,12 @@ func ValidateClient(container clientFacade) error { // NewMockClient exports for registry package test func NewMockClient(podList *v1.PodList) (*Client, error) { - ctx, cancel := context.WithCancel(context.Background()) controller, err := newDubboRegistryController(ctx, common.CONSUMER, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(podList), nil }) if err != nil { + cancel() return nil, perrors.WithMessage(err, "new dubbo-registry controller") } diff --git a/remoting/kubernetes/client_test.go b/remoting/kubernetes/client_test.go index d6c5a2e88057459c0a87cd9a607e9c10970b07b2..9cc421225ea15c250c3ea1196432cb65635144a2 100644 --- a/remoting/kubernetes/client_test.go +++ b/remoting/kubernetes/client_test.go @@ -317,7 +317,8 @@ func TestClientGetChildrenKVList(t *testing.T) { wc, done, err := client.WatchWithPrefix(prefix) if err != nil { - t.Fatal(err) + t.Error(err) + return } wg.Done() @@ -365,7 +366,7 @@ func TestClientGetChildrenKVList(t *testing.T) { // start get all children kList, vList, err := client.GetChildren(prefix) if err != nil { - t.Fatal(err) + t.Error(err) } for i := 0; i < len(kList); i++ { @@ -392,7 +393,7 @@ func TestClientWatchPrefix(t *testing.T) { wc, done, err := client.WatchWithPrefix(prefix) if err != nil { - t.Fatal(err) + t.Error(err) } wg.Done() @@ -435,7 +436,7 @@ func TestClientWatch(t *testing.T) { wc, done, err := client.Watch(prefix) if err != nil { - t.Fatal(err) + t.Error(err) } wg.Done() diff --git a/remoting/kubernetes/facade_test.go b/remoting/kubernetes/facade_test.go index 65c5d715a38bd0862245255e0276ff5e959de3a3..a6c6c02b515b6e1ebc815bb5b388ae68db68621d 100644 --- a/remoting/kubernetes/facade_test.go +++ b/remoting/kubernetes/facade_test.go @@ -19,7 +19,6 @@ package kubernetes import ( "strconv" - "sync" "testing" ) @@ -30,9 +29,9 @@ import ( type mockFacade struct { *common.URL - client *Client - cltLock sync.Mutex - done chan struct{} + client *Client + //cltLock sync.Mutex + //done chan struct{} } func (r *mockFacade) Client() *Client { @@ -43,8 +42,8 @@ func (r *mockFacade) SetClient(client *Client) { r.client = client } -func (r *mockFacade) GetUrl() common.URL { - return *r.URL +func (r *mockFacade) GetUrl() *common.URL { + return r.URL } func (r *mockFacade) Destroy() { @@ -68,7 +67,7 @@ func Test_Facade(t *testing.T) { mockClient := getTestClient(t) m := &mockFacade{ - URL: ®Url, + URL: regUrl, client: mockClient, } diff --git a/remoting/kubernetes/registry_controller.go b/remoting/kubernetes/registry_controller.go index 20be0d72ec1ce4c379f44d3218ffbd0cfd3d2a63..f66163d3c68768e99a84fbca0110e099985429be 100644 --- a/remoting/kubernetes/registry_controller.go +++ b/remoting/kubernetes/registry_controller.go @@ -442,7 +442,7 @@ func (c *dubboRegistryController) initCurrentPod() error { return perrors.WithMessage(err, "get patch") } - currentPod, err = c.patchCurrentPod(p) + _, err = c.patchCurrentPod(p) if err != nil { return perrors.WithMessage(err, "patch to current pod") } diff --git a/remoting/kubernetes/watch.go b/remoting/kubernetes/watch.go index 07eeb09b4dd4627fdd3b18ee4d59356911b3a9b1..7bb5ef1754836b481089d67b9cf34437567ffa9a 100644 --- a/remoting/kubernetes/watch.go +++ b/remoting/kubernetes/watch.go @@ -116,21 +116,17 @@ type watcherSetImpl struct { // closeWatchers // when the watcher-set was closed func (s *watcherSetImpl) closeWatchers() { - - select { - case <-s.ctx.Done(): - - // parent ctx be canceled, close the watch-set's watchers - s.lock.Lock() - watchers := s.watchers - s.lock.Unlock() - - for _, w := range watchers { - // stop data stream - // close(w.ch) - // stop watcher - w.stop() - } + <-s.ctx.Done() + // parent ctx be canceled, close the watch-set's watchers + s.lock.Lock() + watchers := s.watchers + s.lock.Unlock() + + for _, w := range watchers { + // stop data stream + // close(w.ch) + // stop watcher + w.stop() } } diff --git a/remoting/kubernetes/watch_test.go b/remoting/kubernetes/watch_test.go index 8889103be212381c07ffff3c3d4399f41aeee564..efefcc5dc9db629aa0f109a8da4f4227c18504fa 100644 --- a/remoting/kubernetes/watch_test.go +++ b/remoting/kubernetes/watch_test.go @@ -42,7 +42,8 @@ func TestWatchSet(t *testing.T) { defer wg.Done() w, err := s.Watch("key-1", false) if err != nil { - t.Fatal(err) + t.Error(err) + return } for { select { @@ -64,7 +65,8 @@ func TestWatchSet(t *testing.T) { defer wg.Done() w, err := s.Watch("key", true) if err != nil { - t.Fatal(err) + t.Error(err) + return } for { @@ -86,7 +88,8 @@ func TestWatchSet(t *testing.T) { Key: "key-" + strconv.Itoa(i), Value: strconv.Itoa(i), }); err != nil { - t.Fatal(err) + t.Error(err) + return } }(i) } diff --git a/remoting/nacos/builder_test.go b/remoting/nacos/builder_test.go index 61d13ef26f9f1d17173bbeb11468f9babdade2f5..48199bea45b85a3929b174af21791e599284c57c 100644 --- a/remoting/nacos/builder_test.go +++ b/remoting/nacos/builder_test.go @@ -34,11 +34,13 @@ func TestNewNacosClient(t *testing.T) { client, err := NewNacosClient(rc) // address is nil + assert.Nil(t, client) assert.NotNil(t, err) rc.Address = "console.nacos.io:80:123" client, err = NewNacosClient(rc) // invalid address + assert.Nil(t, client) assert.NotNil(t, err) rc.Address = "console.nacos.io:80" diff --git a/remoting/zookeeper/client.go b/remoting/zookeeper/client.go index fbd90762eb34f361a38486ef2d8f5f10699a96f7..5d39666ebd422f695c9654b7c4381b677ae45572 100644 --- a/remoting/zookeeper/client.go +++ b/remoting/zookeeper/client.go @@ -18,14 +18,12 @@ package zookeeper import ( - "path" "strings" - "sync" "time" ) import ( - "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" perrors "github.com/pkg/errors" ) @@ -38,7 +36,7 @@ const ( // ConnDelay connection delay interval ConnDelay = 3 // MaxFailTimes max fail times - MaxFailTimes = 15 + MaxFailTimes = 3 ) var ( @@ -47,79 +45,8 @@ var ( errNilNode = perrors.Errorf("node does not exist") ) -// ZookeeperClient represents zookeeper client Configuration -type ZookeeperClient struct { - name string - ZkAddrs []string - sync.RWMutex // for conn - Conn *zk.Conn - Timeout time.Duration - exit chan struct{} - Wait sync.WaitGroup - - eventRegistry map[string][]*chan struct{} - eventRegistryLock sync.RWMutex -} - -// nolint -func StateToString(state zk.State) string { - switch state { - case zk.StateDisconnected: - return "zookeeper disconnected" - case zk.StateConnecting: - return "zookeeper connecting" - case zk.StateAuthFailed: - return "zookeeper auth failed" - case zk.StateConnectedReadOnly: - return "zookeeper connect readonly" - case zk.StateSaslAuthenticated: - return "zookeeper sasl authenticated" - case zk.StateExpired: - return "zookeeper connection expired" - case zk.StateConnected: - return "zookeeper connected" - case zk.StateHasSession: - return "zookeeper has session" - case zk.StateUnknown: - return "zookeeper unknown state" - case zk.State(zk.EventNodeDeleted): - return "zookeeper node deleted" - case zk.State(zk.EventNodeDataChanged): - return "zookeeper node data changed" - default: - return state.String() - } -} - -// nolint -type Options struct { - zkName string - client *ZookeeperClient - - ts *zk.TestCluster -} - -// Option will define a function of handling Options -type Option func(*Options) - -// WithZkName sets zk client name -func WithZkName(name string) Option { - return func(opt *Options) { - opt.zkName = name - } -} - // ValidateZookeeperClient validates client and sets options -func ValidateZookeeperClient(container ZkClientFacade, opts ...Option) error { - var ( - err error - ) - options := &Options{} - for _, opt := range opts { - opt(options) - } - connected := false - +func ValidateZookeeperClient(container ZkClientFacade, zkName string) error { lock := container.ZkClientLock() url := container.GetUrl() @@ -128,516 +55,20 @@ func ValidateZookeeperClient(container ZkClientFacade, opts ...Option) error { if container.ZkClient() == nil { // in dubbo, every registry only connect one node, so this is []string{r.Address} - timeout, err := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) - if err != nil { - logger.Errorf("timeout config %v is invalid ,err is %v", - url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), err.Error()) - return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) + timeout, paramErr := time.ParseDuration(url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT)) + if paramErr != nil { + logger.Errorf("timeout config %v is invalid, err is %v", + url.GetParam(constant.REGISTRY_TIMEOUT_KEY, constant.DEFAULT_REG_TIMEOUT), paramErr.Error()) + return perrors.WithMessagef(paramErr, "newZookeeperClient(address:%+v)", url.Location) } zkAddresses := strings.Split(url.Location, ",") - newClient, err := NewZookeeperClient(options.zkName, zkAddresses, timeout) - if err != nil { + newClient, cltErr := gxzookeeper.NewZookeeperClient(zkName, zkAddresses, true, gxzookeeper.WithZkTimeOut(timeout)) + if cltErr != nil { logger.Warnf("newZookeeperClient(name{%s}, zk address{%v}, timeout{%d}) = error{%v}", - options.zkName, url.Location, timeout.String(), err) - return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.Location) + zkName, url.Location, timeout.String(), cltErr) + return perrors.WithMessagef(cltErr, "newZookeeperClient(address:%+v)", url.Location) } container.SetZkClient(newClient) - connected = true - } - - if container.ZkClient().Conn == nil { - var event <-chan zk.Event - container.ZkClient().Conn, event, err = zk.Connect(container.ZkClient().ZkAddrs, container.ZkClient().Timeout) - if err == nil { - container.ZkClient().Wait.Add(1) - connected = true - go container.ZkClient().HandleZkEvent(event) - } - } - - if connected { - logger.Infof("Connect to zookeeper successfully, name{%s}, zk address{%v}", options.zkName, url.Location) - container.WaitGroup().Add(1) // zk client start successful, then registry wg +1 - } - - return perrors.WithMessagef(err, "newZookeeperClient(address:%+v)", url.PrimitiveURL) -} - -// nolint -func NewZookeeperClient(name string, zkAddrs []string, timeout time.Duration) (*ZookeeperClient, error) { - var ( - err error - event <-chan zk.Event - z *ZookeeperClient - ) - - z = &ZookeeperClient{ - name: name, - ZkAddrs: zkAddrs, - Timeout: timeout, - exit: make(chan struct{}), - eventRegistry: make(map[string][]*chan struct{}), - } - // connect to zookeeper - z.Conn, event, err = zk.Connect(zkAddrs, timeout) - if err != nil { - return nil, perrors.WithMessagef(err, "zk.Connect(zkAddrs:%+v)", zkAddrs) - } - - z.Wait.Add(1) - go z.HandleZkEvent(event) - - return z, nil -} - -// WithTestCluster sets test cluster for zk client -func WithTestCluster(ts *zk.TestCluster) Option { - return func(opt *Options) { - opt.ts = ts - } -} - -// NewMockZookeeperClient returns a mock client instance -func NewMockZookeeperClient(name string, timeout time.Duration, opts ...Option) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event, error) { - var ( - err error - event <-chan zk.Event - z *ZookeeperClient - ts *zk.TestCluster - ) - - z = &ZookeeperClient{ - name: name, - ZkAddrs: []string{}, - Timeout: timeout, - exit: make(chan struct{}), - eventRegistry: make(map[string][]*chan struct{}), - } - - options := &Options{} - for _, opt := range opts { - opt(options) - } - - // connect to zookeeper - if options.ts != nil { - ts = options.ts - } else { - ts, err = zk.StartTestCluster(1, nil, nil) - if err != nil { - return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect") - } - } - - z.Conn, event, err = ts.ConnectWithOptions(timeout) - if err != nil { - return nil, nil, nil, perrors.WithMessagef(err, "zk.Connect") - } - - return ts, z, event, nil -} - -// HandleZkEvent handles zookeeper events -func (z *ZookeeperClient) HandleZkEvent(session <-chan zk.Event) { - var ( - state int - event zk.Event - ) - - defer func() { - z.Wait.Done() - logger.Infof("zk{path:%v, name:%s} connection goroutine game over.", z.ZkAddrs, z.name) - }() - - for { - select { - case <-z.exit: - return - case event = <-session: - logger.Infof("client{%s} get a zookeeper event{type:%s, server:%s, path:%s, state:%d-%s, err:%v}", - z.name, event.Type, event.Server, event.Path, event.State, StateToString(event.State), event.Err) - switch (int)(event.State) { - case (int)(zk.StateDisconnected): - logger.Warnf("zk{addr:%s} state is StateDisconnected, so close the zk client{name:%s}.", z.ZkAddrs, z.name) - z.stop() - z.Lock() - conn := z.Conn - z.Conn = nil - z.Unlock() - if conn != nil { - conn.Close() - } - return - case (int)(zk.EventNodeDataChanged), (int)(zk.EventNodeChildrenChanged): - logger.Infof("zkClient{%s} get zk node changed event{path:%s}", z.name, event.Path) - z.eventRegistryLock.RLock() - for p, a := range z.eventRegistry { - if strings.HasPrefix(p, event.Path) { - logger.Infof("send event{state:zk.EventNodeDataChange, Path:%s} notify event to path{%s} related listener", - event.Path, p) - for _, e := range a { - *e <- struct{}{} - } - } - } - z.eventRegistryLock.RUnlock() - case (int)(zk.StateConnecting), (int)(zk.StateConnected), (int)(zk.StateHasSession): - if state == (int)(zk.StateHasSession) { - continue - } - z.eventRegistryLock.RLock() - if a, ok := z.eventRegistry[event.Path]; ok && 0 < len(a) { - for _, e := range a { - *e <- struct{}{} - } - } - z.eventRegistryLock.RUnlock() - } - state = (int)(event.State) - } - } -} - -// RegisterEvent registers zookeeper events -func (z *ZookeeperClient) RegisterEvent(zkPath string, event *chan struct{}) { - if zkPath == "" || event == nil { - return - } - - z.eventRegistryLock.Lock() - defer z.eventRegistryLock.Unlock() - a := z.eventRegistry[zkPath] - a = append(a, event) - z.eventRegistry[zkPath] = a - logger.Debugf("zkClient{%s} register event{path:%s, ptr:%p}", z.name, zkPath, event) -} - -// UnregisterEvent unregisters zookeeper events -func (z *ZookeeperClient) UnregisterEvent(zkPath string, event *chan struct{}) { - if zkPath == "" { - return - } - - z.eventRegistryLock.Lock() - defer z.eventRegistryLock.Unlock() - infoList, ok := z.eventRegistry[zkPath] - if !ok { - return - } - for i, e := range infoList { - if e == event { - infoList = append(infoList[:i], infoList[i+1:]...) - logger.Infof("zkClient{%s} unregister event{path:%s, event:%p}", z.name, zkPath, event) - } - } - logger.Debugf("after zkClient{%s} unregister event{path:%s, event:%p}, array length %d", - z.name, zkPath, event, len(infoList)) - if len(infoList) == 0 { - delete(z.eventRegistry, zkPath) - } else { - z.eventRegistry[zkPath] = infoList - } -} - -// nolint -func (z *ZookeeperClient) Done() <-chan struct{} { - return z.exit -} - -func (z *ZookeeperClient) stop() bool { - select { - case <-z.exit: - return true - default: - close(z.exit) - } - - return false -} - -// ZkConnValid validates zookeeper connection -func (z *ZookeeperClient) ZkConnValid() bool { - select { - case <-z.exit: - return false - default: - } - - z.RLock() - defer z.RUnlock() - return z.Conn != nil -} - -// nolint -func (z *ZookeeperClient) Close() { - if z == nil { - return - } - - z.stop() - z.Wait.Wait() - z.Lock() - conn := z.Conn - z.Conn = nil - z.Unlock() - if conn != nil { - logger.Infof("zkClient Conn{name:%s, zk addr:%d} exit now.", z.name, conn.SessionID()) - conn.Close() } - - logger.Infof("zkClient{name:%s, zk addr:%s} exit now.", z.name, z.ZkAddrs) -} - -// Create will create the node recursively, which means that if the parent node is absent, -// it will create parent node first. -// And the value for the basePath is "" -func (z *ZookeeperClient) Create(basePath string) error { - return z.CreateWithValue(basePath, []byte("")) -} - -// CreateWithValue will create the node recursively, which means that if the parent node is absent, -// it will create parent node first. -func (z *ZookeeperClient) CreateWithValue(basePath string, value []byte) error { - var ( - err error - tmpPath string - ) - - logger.Debugf("zookeeperClient.Create(basePath{%s})", basePath) - conn := z.getConn() - err = errNilZkClientConn - if conn == nil { - return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath) - } - - for _, str := range strings.Split(basePath, "/")[1:] { - tmpPath = path.Join(tmpPath, "/", str) - _, err = conn.Create(tmpPath, value, 0, zk.WorldACL(zk.PermAll)) - - if err != nil { - if err == zk.ErrNodeExists { - logger.Debugf("zk.create(\"%s\") exists", tmpPath) - } else { - logger.Errorf("zk.create(\"%s\") error(%v)", tmpPath, perrors.WithStack(err)) - return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath) - } - } - } - - return nil -} - -// CreateTempWithValue will create the node recursively, which means that if the parent node is absent, -// it will create parent node first锛宎nd set value in last child path -// If the path exist, it will update data -func (z *ZookeeperClient) CreateTempWithValue(basePath string, value []byte) error { - var ( - err error - tmpPath string - ) - - logger.Debugf("zookeeperClient.Create(basePath{%s})", basePath) - conn := z.getConn() - err = errNilZkClientConn - if conn == nil { - return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath) - } - - pathSlice := strings.Split(basePath, "/")[1:] - length := len(pathSlice) - for i, str := range pathSlice { - tmpPath = path.Join(tmpPath, "/", str) - // last child need be ephemeral - if i == length-1 { - _, err = conn.Create(tmpPath, value, zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) - if err == zk.ErrNodeExists { - return err - } - } else { - _, err = conn.Create(tmpPath, []byte{}, 0, zk.WorldACL(zk.PermAll)) - } - if err != nil { - if err == zk.ErrNodeExists { - logger.Debugf("zk.create(\"%s\") exists", tmpPath) - } else { - logger.Errorf("zk.create(\"%s\") error(%v)", tmpPath, perrors.WithStack(err)) - return perrors.WithMessagef(err, "zk.Create(path:%s)", basePath) - } - } - } - return nil } - -// nolint -func (z *ZookeeperClient) Delete(basePath string) error { - err := errNilZkClientConn - conn := z.getConn() - if conn != nil { - err = conn.Delete(basePath, -1) - } - - return perrors.WithMessagef(err, "Delete(basePath:%s)", basePath) -} - -// RegisterTemp registers temporary node by @basePath and @node -func (z *ZookeeperClient) RegisterTemp(basePath string, node string) (string, error) { - var ( - err error - zkPath string - tmpPath string - ) - - err = errNilZkClientConn - zkPath = path.Join(basePath) + "/" + node - conn := z.getConn() - if conn != nil { - tmpPath, err = conn.Create(zkPath, []byte(""), zk.FlagEphemeral, zk.WorldACL(zk.PermAll)) - } - - if err != nil { - logger.Warnf("conn.Create(\"%s\", zk.FlagEphemeral) = error(%v)", zkPath, perrors.WithStack(err)) - return zkPath, perrors.WithStack(err) - } - logger.Debugf("zkClient{%s} create a temp zookeeper node:%s", z.name, tmpPath) - - return tmpPath, nil -} - -// RegisterTempSeq register temporary sequence node by @basePath and @data -func (z *ZookeeperClient) RegisterTempSeq(basePath string, data []byte) (string, error) { - var ( - err error - tmpPath string - ) - - err = errNilZkClientConn - conn := z.getConn() - if conn != nil { - tmpPath, err = conn.Create( - path.Join(basePath)+"/", - data, - zk.FlagEphemeral|zk.FlagSequence, - zk.WorldACL(zk.PermAll), - ) - } - - logger.Debugf("zookeeperClient.RegisterTempSeq(basePath{%s}) = tempPath{%s}", basePath, tmpPath) - if err != nil && err != zk.ErrNodeExists { - logger.Errorf("zkClient{%s} conn.Create(\"%s\", \"%s\", zk.FlagEphemeral|zk.FlagSequence) error(%v)", - z.name, basePath, string(data), err) - return "", perrors.WithStack(err) - } - logger.Debugf("zkClient{%s} create a temp zookeeper node:%s", z.name, tmpPath) - - return tmpPath, nil -} - -// GetChildrenW gets children watch by @path -func (z *ZookeeperClient) GetChildrenW(path string) ([]string, <-chan zk.Event, error) { - var ( - err error - children []string - stat *zk.Stat - watcher *zk.Watcher - ) - - err = errNilZkClientConn - conn := z.getConn() - if conn != nil { - children, stat, watcher, err = conn.ChildrenW(path) - } - - if err != nil { - if err == zk.ErrNoChildrenForEphemerals { - return nil, nil, errNilChildren - } - if err == zk.ErrNoNode { - return nil, nil, errNilNode - } - logger.Errorf("zk.ChildrenW(path{%s}) = error(%v)", path, err) - return nil, nil, perrors.WithMessagef(err, "zk.ChildrenW(path:%s)", path) - } - if stat == nil { - return nil, nil, perrors.Errorf("path{%s} get stat is nil", path) - } - if len(children) == 0 { - return nil, nil, errNilChildren - } - - return children, watcher.EvtCh, nil -} - -// GetChildren gets children by @path -func (z *ZookeeperClient) GetChildren(path string) ([]string, error) { - var ( - err error - children []string - stat *zk.Stat - ) - - err = errNilZkClientConn - conn := z.getConn() - if conn != nil { - children, stat, err = conn.Children(path) - } - - if err != nil { - if err == zk.ErrNoNode { - return nil, perrors.Errorf("path{%s} has none children", path) - } - logger.Errorf("zk.Children(path{%s}) = error(%v)", path, perrors.WithStack(err)) - return nil, perrors.WithMessagef(err, "zk.Children(path:%s)", path) - } - if stat == nil { - return nil, perrors.Errorf("path{%s} has none children", path) - } - if len(children) == 0 { - return nil, errNilChildren - } - - return children, nil -} - -// ExistW to judge watch whether it exists or not by @zkPath -func (z *ZookeeperClient) ExistW(zkPath string) (<-chan zk.Event, error) { - var ( - exist bool - err error - watcher *zk.Watcher - ) - - err = errNilZkClientConn - conn := z.getConn() - if conn != nil { - exist, _, watcher, err = conn.ExistsW(zkPath) - } - - if err != nil { - logger.Warnf("zkClient{%s}.ExistsW(path{%s}) = error{%v}.", z.name, zkPath, perrors.WithStack(err)) - return nil, perrors.WithMessagef(err, "zk.ExistsW(path:%s)", zkPath) - } - if !exist { - logger.Warnf("zkClient{%s}'s App zk path{%s} does not exist.", z.name, zkPath) - return nil, perrors.Errorf("zkClient{%s} App zk path{%s} does not exist.", z.name, zkPath) - } - - return watcher.EvtCh, nil -} - -// GetContent gets content by @zkPath -func (z *ZookeeperClient) GetContent(zkPath string) ([]byte, *zk.Stat, error) { - return z.Conn.Get(zkPath) -} - -// nolint -func (z *ZookeeperClient) SetContent(zkPath string, content []byte, version int32) (*zk.Stat, error) { - return z.Conn.Set(zkPath, content, version) -} - -// getConn gets zookeeper connection safely -func (z *ZookeeperClient) getConn() *zk.Conn { - z.RLock() - defer z.RUnlock() - return z.Conn -} diff --git a/remoting/zookeeper/client_test.go b/remoting/zookeeper/client_test.go deleted file mode 100644 index 34741700ca2a9d86ee5321b0b19ed64b2b1a25a8..0000000000000000000000000000000000000000 --- a/remoting/zookeeper/client_test.go +++ /dev/null @@ -1,151 +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 zookeeper - -import ( - "testing" - "time" -) - -import ( - "github.com/dubbogo/go-zookeeper/zk" - "github.com/stretchr/testify/assert" -) - -import ( - "github.com/apache/dubbo-go/common/logger" -) - -func verifyEventStateOrder(t *testing.T, c <-chan zk.Event, expectedStates []zk.State, source string) { - for _, state := range expectedStates { - for { - event, ok := <-c - if !ok { - t.Fatalf("unexpected channel close for %s", source) - } - logger.Debug(event) - if event.Type != zk.EventSession { - continue - } - - if event.State != state { - t.Fatalf("mismatched state order from %s, expected %v, received %v", source, state, event.State) - } - break - } - } -} - -//func Test_newZookeeperClient(t *testing.T) { -// ts, err := zk.StartTestCluster(1, nil, nil) -// if err != nil { -// t.Fatal(err) -// } -// defer ts.Stop() -// -// callbackChan := make(chan zk.Event) -// f := func(event zk.Event) { -// callbackChan <- event -// } -// -// zook, eventChan, err := ts.ConnectWithOptions(15*time.Second, zk.WithEventCallback(f)) -// if err != nil { -// t.Fatalf("Connect returned error: %+v", err) -// } -// -// states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} -// verifyEventStateOrder(t, callbackChan, states, "callback") -// verifyEventStateOrder(t, eventChan, states, "event channel") -// -// zook.Close() -// verifyEventStateOrder(t, callbackChan, []zk.State{zk.StateDisconnected}, "callback") -// verifyEventStateOrder(t, eventChan, []zk.State{zk.StateDisconnected}, "event channel") -// -//} - -func Test_newMockZookeeperClient(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) - assert.NoError(t, err) - defer ts.Stop() - states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} - verifyEventStateOrder(t, event, states, "event channel") - - z.Close() - verifyEventStateOrder(t, event, []zk.State{zk.StateDisconnected}, "event channel") -} - -func TestCreate(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) - assert.NoError(t, err) - defer ts.Stop() - err = z.Create("test1/test2/test3/test4") - assert.NoError(t, err) - - states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} - verifyEventStateOrder(t, event, states, "event channel") -} - -func TestCreateDelete(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) - assert.NoError(t, err) - defer ts.Stop() - - states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} - verifyEventStateOrder(t, event, states, "event channel") - err = z.Create("/test1/test2/test3/test4") - assert.NoError(t, err) - err = z.Delete("/test1/test2/test3/test4") - assert.NoError(t, err) - // verifyEventOrder(t, event, []zk.EventType{zk.EventNodeCreated}, "event channel") -} - -func TestRegisterTemp(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) - assert.NoError(t, err) - defer ts.Stop() - err = z.Create("/test1/test2/test3") - assert.NoError(t, err) - - tmpath, err := z.RegisterTemp("/test1/test2/test3", "test4") - assert.NoError(t, err) - assert.Equal(t, "/test1/test2/test3/test4", tmpath) - states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} - verifyEventStateOrder(t, event, states, "event channel") -} - -func TestRegisterTempSeq(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) - assert.NoError(t, err) - defer ts.Stop() - err = z.Create("/test1/test2/test3") - assert.NoError(t, err) - tmpath, err := z.RegisterTempSeq("/test1/test2/test3", []byte("test")) - assert.NoError(t, err) - assert.Equal(t, "/test1/test2/test3/0000000000", tmpath) - states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} - verifyEventStateOrder(t, event, states, "event channel") -} - -func Test_UnregisterEvent(t *testing.T) { - client := &ZookeeperClient{} - client.eventRegistry = make(map[string][]*chan struct{}) - array := []*chan struct{}{} - array = append(array, new(chan struct{})) - client.eventRegistry["test"] = array - client.UnregisterEvent("test", new(chan struct{})) -} diff --git a/remoting/zookeeper/curator_discovery/service_discovery.go b/remoting/zookeeper/curator_discovery/service_discovery.go index acd43c0b92bd6220efc6527efc1748ed3021f7ac..ebe784c353ce2b7254baab1fcbe1d985e935be2f 100644 --- a/remoting/zookeeper/curator_discovery/service_discovery.go +++ b/remoting/zookeeper/curator_discovery/service_discovery.go @@ -26,6 +26,7 @@ import ( import ( "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" perrors "github.com/pkg/errors" ) @@ -46,7 +47,7 @@ type Entry struct { // https://github.com/apache/curator/blob/master/curator-x-discovery/src/main/java/org/apache/curator/x/discovery/ServiceDiscovery.java // It's not exactly the same as curator-x-discovery's service discovery type ServiceDiscovery struct { - client *zookeeper.ZookeeperClient + client *gxzookeeper.ZookeeperClient mutex *sync.Mutex basePath string services *sync.Map @@ -54,7 +55,7 @@ type ServiceDiscovery struct { } // NewServiceDiscovery the constructor of service discovery -func NewServiceDiscovery(client *zookeeper.ZookeeperClient, basePath string) *ServiceDiscovery { +func NewServiceDiscovery(client *gxzookeeper.ZookeeperClient, basePath string) *ServiceDiscovery { return &ServiceDiscovery{ client: client, mutex: &sync.Mutex{}, diff --git a/remoting/zookeeper/facade.go b/remoting/zookeeper/facade.go index 2a034390c016864f95303b12b6c56533771075f3..aeaa317ea35bea215d8573e9d3d20d945342d481 100644 --- a/remoting/zookeeper/facade.go +++ b/remoting/zookeeper/facade.go @@ -19,73 +19,37 @@ package zookeeper import ( "sync" + "time" ) import ( - "github.com/apache/dubbo-getty" - perrors "github.com/pkg/errors" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" ) - import ( "github.com/apache/dubbo-go/common" "github.com/apache/dubbo-go/common/logger" ) type ZkClientFacade interface { - ZkClient() *ZookeeperClient - SetZkClient(*ZookeeperClient) + ZkClient() *gxzookeeper.ZookeeperClient + SetZkClient(*gxzookeeper.ZookeeperClient) ZkClientLock() *sync.Mutex WaitGroup() *sync.WaitGroup // for wait group control, zk client listener & zk client container - Done() chan struct{} // for zk client control + Done() chan struct{} // for registry destroy RestartCallBack() bool - GetUrl() common.URL + GetUrl() *common.URL } // HandleClientRestart keeps the connection between client and server func HandleClientRestart(r ZkClientFacade) { - var ( - err error - - failTimes int - ) - -LOOP: + defer r.WaitGroup().Done() for { select { + case <-r.ZkClient().Reconnect(): + r.RestartCallBack() + time.Sleep(10 * time.Microsecond) case <-r.Done(): - r.WaitGroup().Done() // dec the wg when registry is closed - logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") - break LOOP - // re-register all services - case <-r.ZkClient().Done(): - r.ZkClientLock().Lock() - r.ZkClient().Close() - zkName := r.ZkClient().name - zkAddress := r.ZkClient().ZkAddrs - r.SetZkClient(nil) - r.ZkClientLock().Unlock() - r.WaitGroup().Done() // dec the wg when zk client is closed - - // Connect zk until success. - failTimes = 0 - for { - select { - case <-r.Done(): - r.WaitGroup().Done() // dec the wg when registry is closed - logger.Warnf("(ZkProviderRegistry)reconnectZkRegistry goroutine exit now...") - break LOOP - case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): // Prevent crazy reconnection zk. - } - err = ValidateZookeeperClient(r, WithZkName(zkName)) - logger.Infof("ZkProviderRegistry.validateZookeeperClient(zkAddr{%s}) = error{%#v}", - zkAddress, perrors.WithStack(err)) - if err == nil && r.RestartCallBack() { - break - } - failTimes++ - if MaxFailTimes <= failTimes { - failTimes = MaxFailTimes - } - } + logger.Warnf("receive registry destroy event, quit client restart handler") + return } } } diff --git a/remoting/zookeeper/facade_test.go b/remoting/zookeeper/facade_test.go index 1cd8f064bb15a2ac48b0d62154309b27c55ab946..6463e574535db70f17e6652330230e26d5e13ba5 100644 --- a/remoting/zookeeper/facade_test.go +++ b/remoting/zookeeper/facade_test.go @@ -22,23 +22,45 @@ import ( "testing" "time" ) + import ( "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/common" ) type mockFacade struct { - client *ZookeeperClient + client *gxzookeeper.ZookeeperClient cltLock sync.Mutex wg sync.WaitGroup URL *common.URL done chan struct{} } -func newMockFacade(client *ZookeeperClient, url *common.URL) ZkClientFacade { +func verifyEventStateOrder(t *testing.T, c <-chan zk.Event, expectedStates []zk.State, source string) { + for _, state := range expectedStates { + for { + event, ok := <-c + if !ok { + t.Fatalf("unexpected channel close for %s", source) + } + if event.Type != zk.EventSession { + continue + } + + if event.State != state { + t.Fatalf("mismatched state order from %s, expected %v, received %v", source, state, event.State) + } + break + } + } +} + +func newMockFacade(client *gxzookeeper.ZookeeperClient, url *common.URL) ZkClientFacade { mock := &mockFacade{ client: client, URL: url, @@ -48,11 +70,11 @@ func newMockFacade(client *ZookeeperClient, url *common.URL) ZkClientFacade { return mock } -func (r *mockFacade) ZkClient() *ZookeeperClient { +func (r *mockFacade) ZkClient() *gxzookeeper.ZookeeperClient { return r.client } -func (r *mockFacade) SetZkClient(client *ZookeeperClient) { +func (r *mockFacade) SetZkClient(client *gxzookeeper.ZookeeperClient) { r.client = client } @@ -68,8 +90,8 @@ func (r *mockFacade) Done() chan struct{} { return r.done } -func (r *mockFacade) GetUrl() common.URL { - return *r.URL +func (r *mockFacade) GetUrl() *common.URL { + return r.URL } func (r *mockFacade) Destroy() { @@ -86,15 +108,18 @@ func (r *mockFacade) IsAvailable() bool { } func Test_Facade(t *testing.T) { - ts, z, event, err := NewMockZookeeperClient("test", 15*time.Second) + ts, z, event, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) - defer ts.Stop() + defer func() { + if err := ts.Stop(); err != nil { + t.Errorf("tc.Stop() = error: %v", err) + } + }() url, _ := common.NewURL("mock://127.0.0.1") - mock := newMockFacade(z, &url) + mock := newMockFacade(z, url) go HandleClientRestart(mock) states := []zk.State{zk.StateConnecting, zk.StateConnected, zk.StateHasSession} verifyEventStateOrder(t, event, states, "event channel") z.Close() verifyEventStateOrder(t, event, []zk.State{zk.StateDisconnected}, "event channel") - //time.Sleep(2e9) } diff --git a/remoting/zookeeper/listener.go b/remoting/zookeeper/listener.go index b6c6d78106a5a97ec94e621d2e664185a8216656..c24a4fc1f9e6e97a0a09433dfe532a7e104435ce 100644 --- a/remoting/zookeeper/listener.go +++ b/remoting/zookeeper/listener.go @@ -25,8 +25,9 @@ import ( ) import ( - "github.com/apache/dubbo-getty" + getty "github.com/apache/dubbo-getty" "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" perrors "github.com/pkg/errors" ) @@ -43,22 +44,24 @@ var ( // nolint type ZkEventListener struct { - client *ZookeeperClient + client *gxzookeeper.ZookeeperClient pathMapLock sync.Mutex pathMap map[string]struct{} wg sync.WaitGroup + exit chan struct{} } // NewZkEventListener returns a EventListener instance -func NewZkEventListener(client *ZookeeperClient) *ZkEventListener { +func NewZkEventListener(client *gxzookeeper.ZookeeperClient) *ZkEventListener { return &ZkEventListener{ client: client, pathMap: make(map[string]struct{}), + exit: make(chan struct{}), } } // nolint -func (l *ZkEventListener) SetClient(client *ZookeeperClient) { +func (l *ZkEventListener) SetClient(client *gxzookeeper.ZookeeperClient) { l.client = client } @@ -69,8 +72,11 @@ func (l *ZkEventListener) ListenServiceNodeEvent(zkPath string, listener remotin go func(zkPath string, listener remoting.DataListener) { if l.listenServiceNodeEvent(zkPath, listener) { listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) + l.pathMapLock.Lock() + delete(l.pathMap, zkPath) + l.pathMapLock.Unlock() } - logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath) + logger.Warnf("ListenServiceNodeEvent->listenSelf(zk path{%s}) goroutine exit now", zkPath) }(zkPath, listener) } @@ -87,8 +93,8 @@ func (l *ZkEventListener) listenServiceNodeEvent(zkPath string, listener ...remo select { case zkEvent = <-keyEventCh: - 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) + logger.Warnf("get a zookeeper keyEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gxzookeeper.StateToString(zkEvent.State), zkEvent.Err) switch zkEvent.Type { case zk.EventNodeDataChanged: logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDataChanged}", zkPath) @@ -116,7 +122,7 @@ func (l *ZkEventListener) listenServiceNodeEvent(zkPath string, listener ...remo logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDeleted}", zkPath) return true } - case <-l.client.Done(): + case <-l.exit: return false } } @@ -135,9 +141,10 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li newChildren, err := l.client.GetChildren(zkPath) if err != nil { if err == errNilChildren { - content, _, err := l.client.Conn.Get(zkPath) - if err != nil { - logger.Errorf("Get new node path {%v} 's content error,message is {%v}", zkPath, perrors.WithStack(err)) + content, _, connErr := l.client.Conn.Get(zkPath) + if connErr != nil { + logger.Errorf("Get new node path {%v} 's content error,message is {%v}", + zkPath, perrors.WithStack(connErr)) } else { listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeUpdate, Content: string(content)}) } @@ -145,6 +152,7 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li } else { logger.Errorf("path{%s} child nodes changed, zk.Children() = error{%v}", zkPath, perrors.WithStack(err)) } + return } // a node was added -- listen the new node @@ -158,24 +166,27 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li newNode = path.Join(zkPath, n) logger.Infof("add zkNode{%s}", newNode) - content, _, err := l.client.Conn.Get(newNode) - if err != nil { - logger.Errorf("Get new node path {%v} 's content error,message is {%v}", newNode, perrors.WithStack(err)) + content, _, connErr := l.client.Conn.Get(newNode) + if connErr != nil { + logger.Errorf("Get new node path {%v} 's content error,message is {%v}", + newNode, perrors.WithStack(connErr)) } - if !listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeAdd, Content: string(content)}) { + if !listener.DataChange(remoting.Event{Path: newNode, Action: remoting.EventTypeAdd, Content: string(content)}) { continue } // listen l service node l.wg.Add(1) - go func(node string, zkPath string, listener remoting.DataListener) { - logger.Infof("delete zkNode{%s}", node) + go func(node string, listener remoting.DataListener) { if l.listenServiceNodeEvent(node, listener) { - logger.Infof("delete content{%s}", node) - listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) + logger.Warnf("delete zkNode{%s}", node) + listener.DataChange(remoting.Event{Path: node, Action: remoting.EventTypeDel}) + l.pathMapLock.Lock() + delete(l.pathMap, zkPath) + l.pathMapLock.Unlock() } - logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath) - }(newNode, zkPath, listener) + logger.Warnf("handleZkNodeEvent->listenSelf(zk path{%s}) goroutine exit now", node) + }(newNode, listener) } // old node was deleted @@ -186,12 +197,7 @@ func (l *ZkEventListener) handleZkNodeEvent(zkPath string, children []string, li } oldNode = path.Join(zkPath, n) - logger.Warnf("delete zkPath{%s}", oldNode) - - if err != nil { - logger.Errorf("NewURL(i{%s}) = error{%v}", n, perrors.WithStack(err)) - continue - } + logger.Warnf("delete oldNode{%s}", oldNode) listener.DataChange(remoting.Event{Path: oldNode, Action: remoting.EventTypeDel}) } } @@ -244,9 +250,9 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen case <-getty.GetTimeWheel().After(timeSecondDuration(failTimes * ConnDelay)): l.client.UnregisterEvent(zkPath, &event) continue - case <-l.client.Done(): + case <-l.exit: l.client.UnregisterEvent(zkPath, &event) - logger.Warnf("client.done(), listen(path{%s}) goroutine exit now...", zkPath) + logger.Warnf("listen(path{%s}) goroutine exit now...", zkPath) return case <-event: logger.Infof("get zk.EventNodeDataChange notify event") @@ -299,10 +305,13 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen logger.Infof("listen dubbo service key{%s}", dubboPath) l.wg.Add(1) go func(zkPath string, listener remoting.DataListener) { - if l.listenServiceNodeEvent(zkPath) { + if l.listenServiceNodeEvent(zkPath, listener) { listener.DataChange(remoting.Event{Path: zkPath, Action: remoting.EventTypeDel}) + l.pathMapLock.Lock() + delete(l.pathMap, zkPath) + l.pathMapLock.Unlock() } - logger.Warnf("listenSelf(zk path{%s}) goroutine exit now", zkPath) + logger.Warnf("listenDirEvent->listenSelf(zk path{%s}) goroutine exit now", zkPath) }(dubboPath, listener) // listen sub path recursive @@ -324,16 +333,16 @@ func (l *ZkEventListener) listenDirEvent(conf *common.URL, zkPath string, listen case <-ticker.C: 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) + logger.Warnf("get a zookeeper childEventCh{type:%s, server:%s, path:%s, state:%d-%s, err:%s}", + zkEvent.Type.String(), zkEvent.Server, zkEvent.Path, zkEvent.State, gxzookeeper.StateToString(zkEvent.State), zkEvent.Err) ticker.Stop() if zkEvent.Type != zk.EventNodeChildrenChanged { break WATCH } l.handleZkNodeEvent(zkEvent.Path, children, listener) break WATCH - case <-l.client.Done(): - logger.Warnf("client.done(), listen(path{%s}) goroutine exit now...", zkPath) + case <-l.exit: + logger.Warnf("listen(path{%s}) goroutine exit now...", zkPath) ticker.Stop() return } @@ -355,16 +364,16 @@ func (l *ZkEventListener) ListenServiceEvent(conf *common.URL, zkPath string, li l.wg.Add(1) go func(zkPath string, listener remoting.DataListener) { l.listenDirEvent(conf, zkPath, listener) - logger.Warnf("listenDirEvent(zkPath{%s}) goroutine exit now", zkPath) + logger.Warnf("ListenServiceEvent->listenDirEvent(zkPath{%s}) goroutine exit now", zkPath) }(zkPath, listener) } -func (l *ZkEventListener) valid() bool { - return l.client.ZkConnValid() -} +//func (l *ZkEventListener) valid() bool { +// return l.client.ZkConnValid() +//} // Close will let client listen exit func (l *ZkEventListener) Close() { - close(l.client.exit) + close(l.exit) l.wg.Wait() } diff --git a/remoting/zookeeper/listener_test.go b/remoting/zookeeper/listener_test.go index 37ef1b4b967d2f6708a4a099875ae90f273ae483..12786655a79335e32e4b65aadb6ba67d6ca09f22 100644 --- a/remoting/zookeeper/listener_test.go +++ b/remoting/zookeeper/listener_test.go @@ -23,10 +23,13 @@ import ( "testing" "time" ) + import ( "github.com/dubbogo/go-zookeeper/zk" + gxzookeeper "github.com/dubbogo/gost/database/kv/zk" "github.com/stretchr/testify/assert" ) + import ( "github.com/apache/dubbo-go/common/logger" "github.com/apache/dubbo-go/remoting" @@ -36,8 +39,8 @@ var ( dubboPropertiesPath = "/dubbo/dubbo.properties" ) -func initZkData(t *testing.T) (*zk.TestCluster, *ZookeeperClient, <-chan zk.Event) { - ts, client, event, err := NewMockZookeeperClient("test", 15*time.Second) +func initZkData(t *testing.T) (*zk.TestCluster, *gxzookeeper.ZookeeperClient, <-chan zk.Event) { + ts, client, event, err := gxzookeeper.NewMockZookeeperClient("test", 15*time.Second) assert.NoError(t, err) data := ` @@ -94,11 +97,15 @@ func TestListener(t *testing.T) { dubbo.service.com.ikurento.user.UserProvider.cluster=failover ` var wait sync.WaitGroup - ts, client, event := initZkData(t) - defer ts.Stop() + ts, client, _ := initZkData(t) + defer func() { + if err := ts.Stop(); err != nil { + t.Errorf("ts.Stop() = error: %v", err) + } + }() client.Wait.Add(1) wait.Add(1) - go client.HandleZkEvent(event) + go client.GetEventHandler().HandleZkEvent(client) listener := NewZkEventListener(client) dataListener := &mockDataListener{client: client, changedData: changedData, wait: &wait} listener.ListenServiceEvent(nil, "/dubbo", dataListener) @@ -107,12 +114,11 @@ func TestListener(t *testing.T) { assert.NoError(t, err) wait.Wait() assert.Equal(t, changedData, dataListener.eventList[1].Content) - } type mockDataListener struct { eventList []remoting.Event - client *ZookeeperClient + client *gxzookeeper.ZookeeperClient changedData string wait *sync.WaitGroup } @@ -122,8 +128,6 @@ func (m *mockDataListener) DataChange(eventType remoting.Event) bool { m.eventList = append(m.eventList, eventType) if eventType.Content == m.changedData { m.wait.Done() - m.client.Close() - } return true } diff --git a/test/integrate/dubbo/go-client/Dockerfile b/test/integrate/dubbo/go-client/Dockerfile index d48df36dc72d7e75f8c2c8c91d5acbb01e39757d..e18755862485ec57e8d25c760ab9075e67ed9c38 100644 --- a/test/integrate/dubbo/go-client/Dockerfile +++ b/test/integrate/dubbo/go-client/Dockerfile @@ -31,6 +31,8 @@ ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-client RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop -RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-client +ENV GO111MODULE on + +RUN go mod tidy && go install github.com/apache/dubbo-go/test/integrate/dubbo/go-client CMD go-client \ No newline at end of file diff --git a/test/integrate/dubbo/go-client/go.mod b/test/integrate/dubbo/go-client/go.mod index 162f32ba9b7a217e7ae8bcfea5aa5b91d76b383c..4708eb1f0f48c10acc254880ecb6dad3a03529f2 100644 --- a/test/integrate/dubbo/go-client/go.mod +++ b/test/integrate/dubbo/go-client/go.mod @@ -1,5 +1,3 @@ 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 deleted file mode 100644 index fc378395782f0f7e8032cdaed2ec4c4b0266c149..0000000000000000000000000000000000000000 --- a/test/integrate/dubbo/go-client/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -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/Dockerfile b/test/integrate/dubbo/go-server/Dockerfile index c2f2d63462d94df7624ac100023e8b8c24e23e11..f72648dbb8d8b33a0ee876c1c0e3657fdfbbdeea 100644 --- a/test/integrate/dubbo/go-server/Dockerfile +++ b/test/integrate/dubbo/go-server/Dockerfile @@ -30,6 +30,8 @@ ADD . /go/src/github.com/apache/dubbo-go/test/integrate/dubbo/go-server RUN test ${PR_ORIGIN_REPO} && echo "github.com/apache/dubbo-go will be replace to github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID}" || echo 'go get github.com/apache/dubbo-go@develop' RUN test ${PR_ORIGIN_REPO} && go mod edit -replace=github.com/apache/dubbo-go=github.com/${PR_ORIGIN_REPO}@${PR_ORIGIN_COMMITID} || go get -u github.com/apache/dubbo-go@develop -RUN go install github.com/apache/dubbo-go/test/integrate/dubbo/go-server +ENV GO111MODULE on + +RUN go mod tidy && go install github.com/apache/dubbo-go/test/integrate/dubbo/go-server CMD go-server diff --git a/test/integrate/dubbo/go-server/go.mod b/test/integrate/dubbo/go-server/go.mod index f9d950e0d1b9b7e56922120772c98699f7b3acc9..9e1162327de374fb131c2a0b89d1be3baa578a1b 100644 --- a/test/integrate/dubbo/go-server/go.mod +++ b/test/integrate/dubbo/go-server/go.mod @@ -1,5 +1,3 @@ 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 deleted file mode 100644 index fc378395782f0f7e8032cdaed2ec4c4b0266c149..0000000000000000000000000000000000000000 --- a/test/integrate/dubbo/go-server/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -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/tools/cli/.gitignore b/tools/cli/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..f38566b9981443d6b15bcd34c515e9331d3401d4 --- /dev/null +++ b/tools/cli/.gitignore @@ -0,0 +1,5 @@ +.idea/ + +# Binary +example/dubbo-go-cli + diff --git a/tools/cli/README.md b/tools/cli/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6bd1a7e0bb22920a53a583d7d0fa54613d3b98d0 --- /dev/null +++ b/tools/cli/README.md @@ -0,0 +1,14 @@ +# dubbo-go-cli + +### 1. Problem we solved. + +For the running dubbo-go server, we need a telnet-cli tool to test if the server works healthily. +The tool should support dubbo protocol, making it easy for you to define your own request pkg, get rsp struct of your server, and total costing time. + + +### 2. How to get cli-tool +run in dubbo-go/tools/cli \ +`$ sh build.sh`\ +and you can get dubbo-go-cli + +### 3. Quick start锛歔example](example/README.md) \ No newline at end of file diff --git a/tools/cli/README_CN.md b/tools/cli/README_CN.md new file mode 100644 index 0000000000000000000000000000000000000000..dfae26e85ffbec4dc8786bf7949e8b5d675aa4c4 --- /dev/null +++ b/tools/cli/README_CN.md @@ -0,0 +1,11 @@ +# dubbo-go-cli + +### 1. 瑙e喅闂 + +閽堝姝e湪杩愯鐨刣ubbo鏈嶅姟锛岄渶瑕佹嫢鏈変竴涓懡浠よ宸ュ叿鏉ラ拡瀵规湇鍔¤繘琛屾祴璇曘€� +璇ユ湇鍔¢渶瑕佹敮鎸乨ubbo鍗忚锛屾柟渚跨敤鎴疯繘琛岃嚜瀹氫箟浼犺緭鍖呬綋銆� + +### 2. cli宸ュ叿鑾峰彇鏂规硶 +`sh build.sh` + +### 3. 浣跨敤鏂规硶锛氳[example](example/README_CN.md) \ No newline at end of file diff --git a/tools/cli/build.sh b/tools/cli/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..f4a6e71da8ad3854648b2c0d3a58ae2c9040baaf --- /dev/null +++ b/tools/cli/build.sh @@ -0,0 +1,2 @@ +export GOPROXY="http://goproxy.io" +go build -o dubbo-go-cli \ No newline at end of file diff --git a/tools/cli/client/client.go b/tools/cli/client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..fd15939bba4ffd7e7bafbe374ed1a36ad588f1fa --- /dev/null +++ b/tools/cli/client/client.go @@ -0,0 +1,197 @@ +/* + * 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 client + +import ( + "log" + "net" + "strconv" + "sync" + "time" +) + +import ( + "go.uber.org/atomic" +) + +import ( + "github.com/apache/dubbo-go/tools/cli/common" + "github.com/apache/dubbo-go/tools/cli/protocol" + _ "github.com/apache/dubbo-go/tools/cli/protocol/dubbo" +) + +// defaultBufferSize is the tcp read default buffer size +const defaultBufferSize = 1024 * 1024 * 4 + +// TelnetClient maintain a connection to target +type TelnetClient struct { + tcpAddr string + responseTimeout time.Duration + protocolName string + requestList []*protocol.Request + conn *net.TCPConn + proto protocol.Protocol + + sequence atomic.Uint64 + pendingResponses *sync.Map + waitNum atomic.Uint64 +} + +// NewTelnetClient create a new tcp connection, and create default request +func NewTelnetClient(host string, port int, protocolName, interfaceID, version, group, method string, reqPkg interface{}, timeout int) (*TelnetClient, error) { + tcpAddr := net.JoinHostPort(host, strconv.Itoa(port)) + resolved := resolveTCPAddr(tcpAddr) + conn, err := net.DialTCP("tcp", nil, resolved) + if err != nil { + return nil, err + } + log.Printf("connected to %s:%d\n", host, port) + log.Printf("try calling interface:%s.%s\n", interfaceID, method) + log.Printf("with protocol:%s\n\n", protocolName) + proto := common.GetProtocol(protocolName) + + return &TelnetClient{ + tcpAddr: tcpAddr, + conn: conn, + responseTimeout: time.Duration(timeout) * time.Millisecond, //default timeout + protocolName: protocolName, + pendingResponses: &sync.Map{}, + proto: proto, + requestList: []*protocol.Request{ + { + InterfaceID: interfaceID, + Version: version, + Method: method, + Group: group, + Params: []interface{}{reqPkg}, + }, + }, + }, nil +} + +func resolveTCPAddr(addr string) *net.TCPAddr { + resolved, error := net.ResolveTCPAddr("tcp", addr) + if nil != error { + log.Fatalf("Error occured while resolving TCP address \"%v\": %v\n", addr, error) + } + + return resolved +} + +// ProcessRequests send all requests +func (t *TelnetClient) ProcessRequests(userPkg interface{}) { + for i, _ := range t.requestList { + t.processSingleRequest(t.requestList[i], userPkg) + } +} + +// addPendingResponse add a response @model to pending queue +// once the rsp got, the model will be used. +func (t *TelnetClient) addPendingResponse(model interface{}) uint64 { + seqId := t.sequence.Load() + t.pendingResponses.Store(seqId, model) + t.waitNum.Inc() + t.sequence.Inc() + return seqId +} + +// removePendingResponse delete item from pending queue by @seq +func (t *TelnetClient) removePendingResponse(seq uint64) { + if t.pendingResponses == nil { + return + } + if _, ok := t.pendingResponses.Load(seq); ok { + t.pendingResponses.Delete(seq) + t.waitNum.Dec() + } + return +} + +// processSingleRequest call one req. +func (t *TelnetClient) processSingleRequest(req *protocol.Request, userPkg interface{}) { + // proto create package procedure + req.ID = t.sequence.Load() + inputData, err := t.proto.Write(req) + if err != nil { + log.Fatalln("error: handler.Writer err = ", err) + } + startTime := time.Now() + + // init rsp Package and add to pending queue + seqId := t.addPendingResponse(userPkg) + defer t.removePendingResponse(seqId) + + requestDataChannel := make(chan []byte, 0) + responseDataChannel := make(chan []byte, 0) + + // start data transfer procedure + go t.readInputData(string(inputData), requestDataChannel) + go t.readServerData(t.conn, responseDataChannel) + + timeAfter := time.After(t.responseTimeout) + + for { + select { + case <-timeAfter: + log.Println("request timeout to:", t.tcpAddr) + return + case request := <-requestDataChannel: + if _, err := t.conn.Write(request); nil != err { + log.Fatalf("Error occured while writing to TCP socket: %v\n", err) + } + case response := <-responseDataChannel: + rspPkg, _, err := t.proto.Read(response, t.pendingResponses) + if err != nil { + log.Fatalln("Error with protocol Read(): ", err) + } + t.removePendingResponse(seqId) + log.Printf("After %dms , Got Rsp:", time.Now().Sub(startTime).Milliseconds()) + common.PrintInterface(rspPkg) + if t.waitNum.Sub(0) == 0 { + return + } + } + } +} + +func (t *TelnetClient) readInputData(inputData string, toSent chan<- []byte) { + toSent <- []byte(inputData) +} + +func (t *TelnetClient) readServerData(connection *net.TCPConn, received chan<- []byte) { + buffer := make([]byte, defaultBufferSize) + var err error + var n int + for nil == err { + n, err = connection.Read(buffer) + received <- buffer[:n] + } + + t.assertEOF(err) +} + +func (t *TelnetClient) assertEOF(error error) { + if "EOF" != error.Error() { + log.Fatalf("Error occured while operating on TCP socket: %v\n", error) + } +} + +// Destroy close the tcp conn +func (t *TelnetClient) Destroy() { + t.conn.Close() +} diff --git a/tools/cli/common/protocol.go b/tools/cli/common/protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..dd2454a1c7edde0911b69c0e4c581c5bd64f2297 --- /dev/null +++ b/tools/cli/common/protocol.go @@ -0,0 +1,39 @@ +/* + * 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 common + +import ( + "github.com/apache/dubbo-go/tools/cli/protocol" +) + +var ( + protocols = make(map[string]func() protocol.Protocol, 8) +) + +// SetProtocol sets the protocol extension with @name +func SetProtocol(name string, v func() protocol.Protocol) { + protocols[name] = v +} + +// GetProtocol finds the protocol extension with @name +func GetProtocol(name string) protocol.Protocol { + if protocols[name] == nil { + panic("protocol for " + name + " is not existing, make sure you have import the package.") + } + return protocols[name]() +} diff --git a/tools/cli/common/tool.go b/tools/cli/common/tool.go new file mode 100644 index 0000000000000000000000000000000000000000..122948c4b6f6e3e705d6404f746badd412566217 --- /dev/null +++ b/tools/cli/common/tool.go @@ -0,0 +1,39 @@ +/* + * 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 common + +import ( + "fmt" + "log" + "reflect" +) + +// PrintInterface print the interface by level +func PrintInterface(v interface{}) { + val := reflect.ValueOf(v).Elem() + typ := reflect.TypeOf(v) + log.Printf("%+v\n", v) + nums := val.NumField() + for i := 0; i < nums; i++ { + if typ.Elem().Field(i).Type.Kind() == reflect.Ptr { + log.Printf("%s: ", typ.Elem().Field(i).Name) + PrintInterface(val.Field(i).Interface()) + } + } + fmt.Println("") +} diff --git a/tools/cli/example/README.md b/tools/cli/example/README.md new file mode 100644 index 0000000000000000000000000000000000000000..bfc37e8f58cf4dbd6f141da0e0bbc11d27126566 --- /dev/null +++ b/tools/cli/example/README.md @@ -0,0 +1,136 @@ +# dubbo-go-cli example + +### 1. Start dubbo-go server +before we use dubbo-go-cli to send a request, we start a dubbo-go server firstly. + +example in file: server/main.go server/user.go + +example锛歶ser.go: +```go +func (u *UserProvider) GetUser(ctx context.Context, userStruct *CallUserStruct) (*User, error) { + fmt.Printf("=======================\nreq:%#v\n", userStruct) + rsp := User{"A002", "Alex Stocks", 18, userStruct.SubInfo} + fmt.Printf("=======================\nrsp:%#v\n", rsp) + return &rsp, nil +} + +``` +as example shows above, server start a function named GetUser, which has a param @CallUserStruct and return User struct. + +param @CallUserStruct defination: +```go +type CallUserStruct struct { + ID string + Male bool + SubInfo SubInfo // nesting sub struct +} +func (cs CallUserStruct) JavaClassName() string { + return "com.ikurento.user.CallUserStruct" +} + +type SubInfo struct { + SubID string + SubMale bool + SubAge int +} + +func (s SubInfo) JavaClassName() string { + return "com.ikurento.user.SubInfo" +} + +``` +User struct defination锛� +```go +type User struct { + Id string + Name string + Age int32 + SubInfo SubInfo // nesting sub struct, the same as above +} + +func (u *User) JavaClassName() string { + return "com.ikurento.user.User" +} +``` + +start dubbo-go server: + +`$ cd server `\ +`$ source builddev.sh`\ +`$ go run .` + +### 2. Define your request structs (encode and decode protocol) +You should define your request structs in json format. we appoint that key and val in json file must be string.\ +Key in json file defines your go struct's field name, such as "ID","Name".\ +Value in json file defines your go struct's field type and value, in format of "type@value". We support 'type' of 'string,int,bool,time', and use value to init the field, if value is empty, we init it by zero. +We appoint that each json struct must have key 'JavaClassName', and the value of it must corresponding to server end. + +example int userCall.json: +```json +{ + "ID": "string@A000", + "Male": "bool@true", + "SubInfo": { + "SubID": "string@A001", + "SubMale": "bool@false", + "SubAge": "int@18", + "JavaClassName":"string@com.ikurento.user.SubInfo" + }, + "JavaClassName": "string@com.ikurento.user.CallUserStruct" +} +``` +'userCall.json' defines param @CallUserStruct and it's substruct 'SubInfo', meanwhile it inits all fields. + + +'user.json' Similarly defines all field, but you are not need to set inital value for them. Remember that 'JavaClassName' field must corresponding to server end. +```go +{ + "ID": "string", + "Name": "string", + "Age": "int", + "JavaClassName": "string@com.ikurento.user.User", + "SubInfo": { + "SubID": "string", + "SubMale": "bool", + "SubAge": "int", + "JavaClassName":"string@com.ikurento.user.SubInfo" + } +} +``` + +### 3. Exec cli to send req. +`./dubbo-go-cli -h=localhost -p=20001 -proto=dubbo -i=com.ikurento.user.UserProvider -method=GetUser -sendObj="./userCall.json" -recvObj="./user.json"` + +cli-end output锛� +```log +2020/10/26 20:47:45 Created pkg: +2020/10/26 20:47:45 &{ID:A000 Male:true SubInfo:0xc00006ea20 JavaClassName:com.ikurento.user.CallUserStruct} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID:A001 SubMale:false SubAge:18 JavaClassName:com.ikurento.user.SubInfo} + + +2020/10/26 20:47:45 Created pkg: +2020/10/26 20:47:45 &{ID: Name: Age:0 JavaClassName:com.ikurento.user.User SubInfo:0xc00006ec90} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID: SubMale:false SubAge:0 JavaClassName:com.ikurento.user.SubInfo} + + +2020/10/26 20:47:45 connected to localhost:20001! +2020/10/26 20:47:45 try calling interface:com.ikurento.user.UserProvider.GetUser +2020/10/26 20:47:45 with protocol:dubbo + +2020/10/26 20:47:45 After 3ms , Got Rsp: +2020/10/26 20:47:45 &{ID:A002 Name:Alex Stocks Age:18 JavaClassName: SubInfo:0xc0001241b0} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID:A001 SubMale:false SubAge:18 JavaClassName:}``` +``` +By reading logs above, you can get req struct, cost time and rsp struct in details.\ +The nesting sub struct is supported. + +server-end output: +``` +======================= +req:&main.CallUserStruct{ID:"A000", Male:true, SubInfo:main.SubInfo{SubID:"A001", SubMale:false, SubAge:18}} +======================= +``` +It's showed that server-end got specific request. \ No newline at end of file diff --git a/tools/cli/example/README_CN.md b/tools/cli/example/README_CN.md new file mode 100644 index 0000000000000000000000000000000000000000..fd2135b5a70a8575f52720bbd5ac0e06f83bdef5 --- /dev/null +++ b/tools/cli/example/README_CN.md @@ -0,0 +1,132 @@ +# dubbo-go-cli 浣跨敤绀轰緥 + +### 1. 寮€鍚湇鍔$ +瑙� server/main.go server/user.go + +绀轰緥锛歶ser.go: +```go +func (u *UserProvider) GetUser(ctx context.Context, userStruct *CallUserStruct) (*User, error) { + fmt.Printf("=======================\nreq:%#v\n", userStruct) + rsp := User{"A002", "Alex Stocks", 18, userStruct.SubInfo} + fmt.Printf("=======================\nrsp:%#v\n", rsp) + return &rsp, nil +} + +``` +鏈嶅姟绔紑鍚竴涓湇鍔★紝鍚嶄负GetUser锛屼紶鍏ヤ竴涓狢allUserStruct鐨勫弬鏁帮紝杩斿洖涓€涓猆ser鍙傛暟\ +CallUserStruct鍙傛暟瀹氫箟锛� +```go +type CallUserStruct struct { + ID string + Male bool + SubInfo SubInfo // 宓屽瀛愮粨鏋� +} +func (cs CallUserStruct) JavaClassName() string { + return "com.ikurento.user.CallUserStruct" +} + +type SubInfo struct { + SubID string + SubMale bool + SubAge int +} + +func (s SubInfo) JavaClassName() string { + return "com.ikurento.user.SubInfo" +} + +``` +User缁撴瀯瀹氫箟锛� +```go +type User struct { + Id string + Name string + Age int32 + SubInfo SubInfo // 宓屽涓婅堪瀛愮粨鏋凷ubInfo +} + +func (u *User) JavaClassName() string { + return "com.ikurento.user.User" +} +``` + +寮€鍚湇鍔★細 + +`cd server `\ +`source builddev.sh`\ +`go run .` + +### 2. 瀹氫箟璇锋眰浣�(鎵撹В鍖呭崗璁�) + +璇锋眰浣撳畾涔変负json鏂囦欢锛岀害瀹氶敭鍊煎潎涓簊tring\ +閿搴攇o璇█struct瀛楁鍚嶄緥濡�"ID"銆�"Name" 锛屽€煎搴�"type@val"\ +鍏朵腑type鏀寔string int bool time锛寁al浣跨敤string 鏉ュ垵濮嬪寲锛屽鏋滃彧濉啓type鍒欏垵濮嬪寲涓洪浂鍊笺€� +绾﹀畾姣忎釜struct蹇呴』鏈塉avaClassName瀛楁锛屽姟蹇呬笌server绔弗鏍煎搴� + +瑙乽serCall.json: +```json +{ + "ID": "string@A000", + "Male": "bool@true", + "SubInfo": { + "SubID": "string@A001", + "SubMale": "bool@false", + "SubAge": "int@18", + "JavaClassName":"string@com.ikurento.user.SubInfo" + }, + "JavaClassName": "string@com.ikurento.user.CallUserStruct" +} +``` +userCall.json灏嗗弬鏁癈allUserStruct鐨勭粨鏋勫強瀛愮粨鏋凷ubInfo閮藉畾涔変簡鍑烘潵锛屽苟涓旂粰璇锋眰鍙傛暟璧嬪€笺€� + +user.json 鍚岀悊锛屼綔涓鸿繑鍥炲€间笉闇€瑕佽祴鍒濆鍊硷紝浣咼avaClassName瀛楁涓€瀹氫笌server绔弗鏍煎搴� +```go +{ + "ID": "string", + "Name": "string", + "Age": "int", + "JavaClassName": "string@com.ikurento.user.User", + "SubInfo": { + "SubID": "string", + "SubMale": "bool", + "SubAge": "int", + "JavaClassName":"string@com.ikurento.user.SubInfo" + } +} +``` + +### 3. 鎵ц璇锋眰 +`./dubbo-go-cli -h=localhost -p=20001 -proto=dubbo -i=com.ikurento.user.UserProvider -method=GetUser -sendObj="./userCall.json" -recvObj="./user.json"` + +cli绔墦鍗扮粨鏋滐細 +```log +2020/10/26 20:47:45 Created pkg: +2020/10/26 20:47:45 &{ID:A000 Male:true SubInfo:0xc00006ea20 JavaClassName:com.ikurento.user.CallUserStruct} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID:A001 SubMale:false SubAge:18 JavaClassName:com.ikurento.user.SubInfo} + + +2020/10/26 20:47:45 Created pkg: +2020/10/26 20:47:45 &{ID: Name: Age:0 JavaClassName:com.ikurento.user.User SubInfo:0xc00006ec90} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID: SubMale:false SubAge:0 JavaClassName:com.ikurento.user.SubInfo} + + +2020/10/26 20:47:45 connected to localhost:20001! +2020/10/26 20:47:45 try calling interface:com.ikurento.user.UserProvider.GetUser +2020/10/26 20:47:45 with protocol:dubbo + +2020/10/26 20:47:45 After 3ms , Got Rsp: +2020/10/26 20:47:45 &{ID:A002 Name:Alex Stocks Age:18 JavaClassName: SubInfo:0xc0001241b0} +2020/10/26 20:47:45 SubInfo: +2020/10/26 20:47:45 &{SubID:A001 SubMale:false SubAge:18 JavaClassName:}``` +``` +鍙湅鍒拌缁嗙殑璇锋眰浣撹祴鍊兼儏鍐碉紝浠ュ強杩斿洖缁撴灉鍜岃€楁椂銆傛敮鎸佸祵濂楃粨鏋� + +server绔墦鍗扮粨鏋� +``` +======================= +req:&main.CallUserStruct{ID:"A000", Male:true, SubInfo:main.SubInfo{SubID:"A001", SubMale:false, SubAge:18}} +======================= +``` +鍙鎺ユ敹鍒颁簡鏉ヨ嚜cli鐨勬暟鎹� \ No newline at end of file diff --git a/tools/cli/example/server/builddev.sh b/tools/cli/example/server/builddev.sh new file mode 100644 index 0000000000000000000000000000000000000000..45a06ea5b28208dbaf59d971b9d75a32c632eb7d --- /dev/null +++ b/tools/cli/example/server/builddev.sh @@ -0,0 +1,2 @@ +export APP_LOG_CONF_FILE="./config/log.yml" +export CONF_PROVIDER_FILE_PATH="./config/server.yml" \ No newline at end of file diff --git a/tools/cli/example/server/config/log.yml b/tools/cli/example/server/config/log.yml new file mode 100644 index 0000000000000000000000000000000000000000..59fa4279ad85272c4c49d532beaf23b74d00f58a --- /dev/null +++ b/tools/cli/example/server/config/log.yml @@ -0,0 +1,28 @@ + +level: "debug" +development: true +disableCaller: false +disableStacktrace: false +sampling: +encoding: "console" + +# encoder +encoderConfig: + messageKey: "message" + levelKey: "level" + timeKey: "time" + nameKey: "logger" + callerKey: "caller" + stacktraceKey: "stacktrace" + lineEnding: "" + levelEncoder: "capitalColor" + timeEncoder: "iso8601" + durationEncoder: "seconds" + callerEncoder: "short" + nameEncoder: "" + +outputPaths: + - "stderr" +errorOutputPaths: + - "stderr" +initialFields: diff --git a/tools/cli/example/server/config/server.yml b/tools/cli/example/server/config/server.yml new file mode 100644 index 0000000000000000000000000000000000000000..425eb1ca07bcd415bb7398fc3e6529eff5f9e7d6 --- /dev/null +++ b/tools/cli/example/server/config/server.yml @@ -0,0 +1,58 @@ +# dubbo server yaml configure file + + +# application config +application: + organization : "ikurento.com" + name : "BDTService" + module : "dubbogo user-info server" + version : "0.0.1" + owner : "ZX" + environment : "config" + +#registries : +# "demoConsul": +# protocol: "consul" +# timeout : "3s" +# address: "127.0.0.1:8500" +# simplified: true + +services: + "UserProvider": + # 鍙互鎸囧畾澶氫釜registry锛屼娇鐢ㄩ€楀彿闅斿紑;涓嶆寚瀹氶粯璁ゅ悜鎵€鏈夋敞鍐屼腑蹇冩敞鍐� +# registry: "demoConsul" + protocol : "dubbo" + # 鐩稿綋浜巇ubbo.xml涓殑interface + interface : "com.ikurento.user.UserProvider" + loadbalance: "random" + warmup: "100" + cluster: "failover" + methods: + - name: "GetUser" + retries: 1 + loadbalance: "random" + +protocols: + "dubbo": + name: "dubbo" + port: 20001 + + +protocol_conf: + dubbo: + session_number: 700 + session_timeout: "180s" + getty_session_param: + compress_encoding: false + tcp_no_delay: true + tcp_keep_alive: true + keep_alive_period: "120s" + tcp_r_buf_size: 262144 + tcp_w_buf_size: 65536 + pkg_rq_size: 1024 + pkg_wq_size: 512 + tcp_read_timeout: "1s" + tcp_write_timeout: "5s" + wait_timeout: "1s" + max_msg_len: 1024000 + session_name: "server" diff --git a/tools/cli/example/server/go.mod b/tools/cli/example/server/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..53763f2805f4b5e4bc37239f301893e18d58b7cf --- /dev/null +++ b/tools/cli/example/server/go.mod @@ -0,0 +1,8 @@ +module go-server + +go 1.13 + +require ( + github.com/apache/dubbo-go v1.5.4 + github.com/apache/dubbo-go-hessian2 v1.7.0 +) diff --git a/tools/cli/example/server/go.sum b/tools/cli/example/server/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..1f3cf1de845da8d69346bad04c0d4d597aaee789 --- /dev/null +++ b/tools/cli/example/server/go.sum @@ -0,0 +1,989 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +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/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/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/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/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/go.mod h1:ZG5p860J94/0kI9mNJVoIoLgXcirM2gF5i2kWloofxw= +github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= +github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= +github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= +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/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/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI= +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/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +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/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +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/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/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/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 h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alibaba/sentinel-golang v0.6.2 h1:1OjjpljJbNKWp9p5RJKxOqS1gHGZPUWPlCcokv5xYJs= +github.com/alibaba/sentinel-golang v0.6.2/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/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 v1.5.1 h1:9bcGOkek0+x+zqtvykKpuOwkoJfMef0zNxsyPbWOafY= +github.com/apache/dubbo-go v1.5.1/go.mod h1:lxwgtF+27mSFQsSrBLaVbdQpwCp+pBN/mHP4w4/N2Qc= +github.com/apache/dubbo-go v1.5.4 h1:kRVw2p6Fqk5PlJGHeb8IVFg5AuBJFPFvCvRm7/i3Qco= +github.com/apache/dubbo-go v1.5.4/go.mod h1:QIqjEvIbgmuk1mESsQSsTsFngHlYLGAmRN9gXN+YJq4= +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/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg= +github.com/armon/go-metrics v0.3.0 h1:B7AQgHi8QSEi4uHu7Sbsga+IJDU+CENgjxoo81vDUqU= +github.com/armon/go-metrics v0.3.0/go.mod h1:zXjbSimjXTd7vOpY8B0/2LpvNvDoXBuplAD+gJD3GYs= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +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/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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +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/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +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/census-instrumentation/opencensus-proto v0.2.1/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/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/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/go.mod h1:zASH/MVDgR6XZTbxvOnsZfffS+31vg6Ackf/wo1+AM0= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +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/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/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/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= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20170926055100-d3308649c661/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/digitalocean/godo v1.1.1/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/digitalocean/godo v1.10.0/go.mod h1:h6faOIcZ8lWIwNQ+DN7b3CgX4Kwby5T+nbpNqkUIozU= +github.com/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/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/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/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= +github.com/dubbogo/go-zookeeper v1.0.2/go.mod h1:fn6n2CAEer3novYgk9ULLwAjuV8/g4DdC2ENwRb6E+c= +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/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/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful/v3 v3.0.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.8.0/go.mod h1:GSSbY9P1neVhdY7G4wu+IK1rk/dqhiCC/4ExuWJZVuk= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/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/go.mod h1:36zfPVQyHxymz4cH7wlDmVwDrJuljRB60qkgn7rorfQ= +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/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/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-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= +github.com/go-co-op/gocron v0.1.1/go.mod h1:Y9PWlYqDChf2Nbgg7kfS+ZsXHDTZbMZYPEQ0MILqH+M= +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= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +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/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/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/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/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +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/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/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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/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/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= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +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/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +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/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/googleapis/gnostic v0.2.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= +github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +github.com/gophercloud/gophercloud v0.3.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8= +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/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +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/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/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +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/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/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.2/go.mod h1:ANbpTX1oAql27TZkKVeW8p1w8NTdnyzPe/0qqPCKohU= +github.com/hashicorp/go-checkpoint v0.0.0-20171009173528-1545e56e46de/go.mod h1:xIwEieBHERyEvaeKF/TcHh1Hu+lxPM+n2vT1+g9I4m4= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-connlimit v0.2.0/go.mod h1:OUj9FGL1tPIhl/2RCfzYHrIiWj+VVPGNyVPnUX8AqS0= +github.com/hashicorp/go-discover v0.0.0-20200501174627-ad1e96bde088/go.mod h1:vZu6Opqf49xX5lsFAu7iFNewkcVF1sn/wyapZh5ytlg= +github.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI= +github.com/hashicorp/go-hclog v0.8.0/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.9.1/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= +github.com/hashicorp/go-hclog v0.12.0 h1:d4QkX8FRTYaKaCZBoXYY8zJX2BXjWxurN/GA2tkrmZM= +github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.1.0 h1:vN9wG1D6KG6YHRTWr8512cxGOVgTMEfgEdSj/hr8MPc= +github.com/hashicorp/go-immutable-radix v1.1.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-memdb v1.0.3/go.mod h1:LWQ8R70vPrS4OEY9k28D2z8/Zzyu34NVzeRibGAzHO0= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror 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/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-plugin v1.0.1/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY= +github.com/hashicorp/go-raftchunking v0.6.1/go.mod h1:cGlg3JtDy7qy6c/3Bu660Mic1JF+7lWqIwCFSb08fX0= +github.com/hashicorp/go-raftchunking v0.6.3-0.20191002164813-7e9e8525653a/go.mod h1:xbXnmKqX9/+RhPkJ4zrEx4738HacP72aaUPlT2RZ4sU= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.5.4/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.6.2/go.mod h1:gEx6HMUGxYYhJScX7W1Il64m6cc2C1mDaW3NQ9sY1FY= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-rootcerts v1.0.1/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2-0.20191001231223-f32f5fe8d6a8/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk= +github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hil v0.0.0-20160711231837-1e86c6b523c5/go.mod h1:KHvg/R2/dPtaePb16oW4qIyzkMxXOL38xjRN64adsts= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/memberlist v0.2.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= +github.com/hashicorp/net-rpc-msgpackrpc v0.0.0-20151116020338-a14192a58a69/go.mod h1:/z+jUGRBlwVpUZfjute9jWaF6/HuhjuFQuL1YXzVD1Q= +github.com/hashicorp/raft v1.1.1/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft v1.1.2-0.20191002163536-9c6bd3e3eb17/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft v1.1.2/go.mod h1:vPAJM8Asw6u8LxC3eJCUZmRP/E4QmUGE1R7g7k8sG/8= +github.com/hashicorp/raft-boltdb v0.0.0-20171010151810-6e5ba93211ea/go.mod h1:pNv7Wc3ycL6F5oOWn+tPGo2gWD4a5X+yp/ntwdKLjRk= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hashicorp/serf v0.9.0/go.mod h1:YL0HO+FifKOW2u1ke99DGVu1zhcpZzNwrLIqBC7vbYU= +github.com/hashicorp/serf v0.9.2 h1:yJoyfZXo4Pk2p/M/viW+YLibBFiIbKoP79gu7kDAFP0= +github.com/hashicorp/serf v0.9.2/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= +github.com/hashicorp/vault/api v1.0.4/go.mod h1:gDcqh3WGcR1cpF5AJz/B1UFheUEneMoIospckxBxk6Q= +github.com/hashicorp/vault/api v1.0.5-0.20191108163347-bdd38fca2cff/go.mod h1:Uf8LaHyrYsgVgHzO2tMZKhqRGlL3UJ6XaSwW2EA1Iqo= +github.com/hashicorp/vault/sdk v0.1.13/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M= +github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU= +github.com/hashicorp/vault/sdk v0.1.14-0.20191112033314-390e96e22eb2/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU= +github.com/hashicorp/vic v1.5.1-0.20190403131502-bbfe86ec9443/go.mod h1:bEpDU35nTu0ey1EXjwNwPjI9xErAsoOCmcMb9GKvyxo= +github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/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/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +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/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/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/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/joyent/triton-go v0.0.0-20180628001255-830d2b111e62/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA= +github.com/joyent/triton-go v1.7.1-0.20200416154420-6801d15b779f/go.mod h1:KDSfL7qe5ZfQqvlDMkVjCztbmcpp/c8M77vhQP8ZPvk= +github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/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/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= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/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/go.mod h1:BLt8L9ld7wVsvEWQbuLrUZnCMnUmLZ+CGDzKtclrTlE= +github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f/go.mod h1:UGmTpUd3rjbtfIpwAPrcfmGf/Z1HS95TATB+m57TPB8= +github.com/lestrrat/go-strftime v0.0.0-20180220042222-ba3bf9c1d042/go.mod h1:TPpsiPUEh0zFL1Snz4crhMlBe60PYxRHr5oFF3rRYg0= +github.com/lib/pq 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/go.mod h1:ga11n3ivecUrPCHN0rANxKmfWBJVkOXfLMZinAbj2sY= +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/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/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +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/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +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/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= +github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.2.3 h1:f/MjBEBDLttYCGfRaKBbKSRVF5aV2O6fnBpzknuE3jU= +github.com/mitchellh/mapstructure v1.2.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.0.0/go.mod h1:k4XwG94++jLVsSiTxo7qdIfXA9pj9EAeo0QsNNJOLZ8= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/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/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/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk= +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/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/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/go.mod h1:otzZQXgoO96RTzDB/Hycg0qZcXZsWJGJRSXbmEIJ+4M= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.2.6+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +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/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/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/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +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/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/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= +github.com/sean-/conswriter v0.0.0-20180208195008-f5ae3917a627/go.mod h1:7zjs06qF79/FKAJpBvFx3P8Ww4UTIMAe+lpNXDHziac= +github.com/sean-/pager v0.0.0-20180208200047-666be9bf53b5/go.mod h1:BeybITEsBEg6qbIiqJ6/Bqeq25bCLbL7YFmpaFfJDuM= +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/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/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/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/softlayer/softlayer-go v0.0.0-20180806151055-260589d94c7d/go.mod h1:Cw4GTlQccdRGSEf6KiMju767x0NEHE0YIVPJSaXjlsw= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/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/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/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/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/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +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/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/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/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/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/go.mod h1:ag0XmE1r4iAgPd6PUnU9TJ0DMEjM1VKX1HUNqQJ2ywU= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +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/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/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/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/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= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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/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= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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/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/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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-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= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +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= +golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +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/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/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= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +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/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/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/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/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +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/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +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/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/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +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= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +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/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 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/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/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +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/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/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/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/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= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +istio.io/gogo-genproto v0.0.0-20190124151557-6d926a6e6feb/go.mod h1:eIDJ6jNk/IeJz6ODSksHl5Aiczy5JUq6vFhJWI5OtiI= +k8s.io/api v0.16.9/go.mod h1:Y7dZNHs1Xy0mSwSlzL9QShi6qkljnN41yR8oWCRTDe8= +k8s.io/apimachinery v0.16.9/go.mod h1:Xk2vD2TRRpuWYLQNM6lT9R7DSFZUYG03SarNkbGrnKE= +k8s.io/client-go v0.16.9/go.mod h1:ThjPlh7Kx+XoBFOCt775vx5J7atwY7F/zaFzTco5gL0= +k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= +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/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20190801114015-581e00157fb1/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew= +launchpad.net/gocheck v0.0.0-20140225173054-000000000087/go.mod h1:hj7XX3B/0A+80Vse0e+BUHsHMTEhd0O4cpUHr/e/BUM= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= diff --git a/tools/cli/example/server/main.go b/tools/cli/example/server/main.go new file mode 100644 index 0000000000000000000000000000000000000000..ed1ed5290e7d74b6a757ad13436f781a74273fe5 --- /dev/null +++ b/tools/cli/example/server/main.go @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + _ "github.com/apache/dubbo-go/cluster/cluster_impl" + _ "github.com/apache/dubbo-go/cluster/loadbalance" + "github.com/apache/dubbo-go/common/logger" + _ "github.com/apache/dubbo-go/common/proxy/proxy_factory" + "github.com/apache/dubbo-go/config" + _ "github.com/apache/dubbo-go/filter/filter_impl" + _ "github.com/apache/dubbo-go/protocol/dubbo" + _ "github.com/apache/dubbo-go/registry/consul" + _ "github.com/apache/dubbo-go/registry/protocol" +) + +var ( + survivalTimeout = int(3e9) +) + +// they are necessary: +// export CONF_PROVIDER_FILE_PATH="xxx" +// export APP_LOG_CONF_FILE="xxx" +func main() { + hessian.RegisterPOJO(&SubInfo{}) + hessian.RegisterPOJO(&User{}) + config.Load() + + initSignal() +} + +func initSignal() { + signals := make(chan os.Signal, 1) + // It is not possible to block SIGKILL or syscall.SIGSTOP + signal.Notify(signals, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT) + for { + sig := <-signals + logger.Infof("get signal %s", sig.String()) + switch sig { + case syscall.SIGHUP: + // reload() + default: + time.AfterFunc(time.Duration(survivalTimeout), func() { + logger.Warnf("app exit now by force...") + os.Exit(1) + }) + time.Sleep(time.Second) + // The program exits normally or timeout forcibly exits. + fmt.Println("provider app exit now...") + return + } + } +} diff --git a/tools/cli/example/server/user.go b/tools/cli/example/server/user.go new file mode 100644 index 0000000000000000000000000000000000000000..5835848dc9ad28fd2b14d2ee6fd0f4abfb5a8396 --- /dev/null +++ b/tools/cli/example/server/user.go @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "context" + "fmt" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + "github.com/apache/dubbo-go/config" +) + +func init() { + config.SetProviderService(new(UserProvider)) + // ------for hessian2------ + hessian.RegisterPOJO(&User{}) + hessian.RegisterPOJO(&CallUserStruct{}) +} + +type UserProvider struct { +} + +func (u *UserProvider) GetUser(ctx context.Context, userStruct *CallUserStruct) (*User, error) { + fmt.Printf("=======================\nreq:%#v\n", userStruct) + rsp := User{"A002", "Alex Stocks", 18, userStruct.SubInfo} + fmt.Printf("=======================\nrsp:%#v\n", rsp) + return &rsp, nil +} + +func (u *UserProvider) Reference() string { + return "UserProvider" +} + +type User struct { + Id string + Name string + Age int32 + SubInfo SubInfo +} + +func (u *User) JavaClassName() string { + return "com.ikurento.user.User" +} + +type CallUserStruct struct { + ID string + Male bool + SubInfo SubInfo +} + +type SubInfo struct { + SubID string + SubMale bool + SubAge int +} + +func (s SubInfo) JavaClassName() string { + return "com.ikurento.user.SubInfo" +} + +func (cs CallUserStruct) JavaClassName() string { + return "com.ikurento.user.CallUserStruct" +} diff --git a/tools/cli/example/test.sh b/tools/cli/example/test.sh new file mode 100644 index 0000000000000000000000000000000000000000..9a038a9594e6eff29f3a69c27cb970342b6629cf --- /dev/null +++ b/tools/cli/example/test.sh @@ -0,0 +1 @@ +./dubbo-go-cli -h=localhost -p=20001 -proto=dubbo -i=com.ikurento.user.UserProvider -method=GetUser -sendObj="./userCall.json" -recvObj="./user.json" \ No newline at end of file diff --git a/tools/cli/example/user.json b/tools/cli/example/user.json new file mode 100644 index 0000000000000000000000000000000000000000..1e6321993cda758e1511b3f08540b334e8e8027e --- /dev/null +++ b/tools/cli/example/user.json @@ -0,0 +1,12 @@ +{ + "ID": "string", + "Name": "string", + "Age": "int", + "JavaClassName": "string@com.ikurento.user.User", + "SubInfo": { + "SubID": "string", + "SubMale": "bool", + "SubAge": "int", + "JavaClassName":"string@com.ikurento.user.SubInfo" + } +} \ No newline at end of file diff --git a/tools/cli/example/userCall.json b/tools/cli/example/userCall.json new file mode 100644 index 0000000000000000000000000000000000000000..6ef703569dcb4c8274b99d562cb874d0f74eb064 --- /dev/null +++ b/tools/cli/example/userCall.json @@ -0,0 +1,11 @@ +{ + "ID": "string@A000", + "Male": "bool@true", + "SubInfo": { + "SubID": "string@A001", + "SubMale": "bool@false", + "SubAge": "int@18", + "JavaClassName":"string@com.ikurento.user.SubInfo" + }, + "JavaClassName": "string@com.ikurento.user.CallUserStruct" +} \ No newline at end of file diff --git a/tools/cli/go.mod b/tools/cli/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..559c7b0c1991a9b2f9c1d4fba3878811a0260493 --- /dev/null +++ b/tools/cli/go.mod @@ -0,0 +1,12 @@ +module github.com/apache/dubbo-go/tools/cli + +go 1.13 + +require ( + github.com/apache/dubbo-go-hessian2 v1.8.0 + github.com/dubbogo/gost v1.11.1 + github.com/pkg/errors v0.9.1 + go.uber.org/atomic v1.7.0 +) + +replace github.com/coreos/bbolt => go.etcd.io/bbolt v1.3.4 diff --git a/tools/cli/go.sum b/tools/cli/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..baaee26131ff65e0e93d08972ffa285f4a60b883 --- /dev/null +++ b/tools/cli/go.sum @@ -0,0 +1,482 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +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-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= +github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/apache/dubbo-go-hessian2 v1.8.0 h1:+GJQHxWd/WUw2p4hbfCal/zjKvGVb8yJZzOke8IEazc= +github.com/apache/dubbo-go-hessian2 v1.8.0/go.mod h1:7rEw9guWABQa6Aqb8HeZcsYPHsOS7XT1qtJvkmI6c5w= +github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= +github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= +github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= +github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coreos/etcd v3.3.25+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +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.11.1 h1:hpcjE/HsC7Ym0H4UF05/a8FD/s44lBIf4adh1vgsZfA= +github.com/dubbogo/gost v1.11.1/go.mod h1:n+lELlTuhrG4F7a+crC0tuVrdJ3q5t8ijGBsDCrWcI8= +github.com/dubbogo/jsonparser v1.0.1 h1:sAIr8gk+gkahkIm6CnUxh9wTCkbgwLEQ8dTXTnAXyzo= +github.com/dubbogo/jsonparser v1.0.1/go.mod h1:tYAtpctvSP/tWw4MeelsowSPgXQRVHHWbqL6ynps8jU= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +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/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= +github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= +github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +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.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +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/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +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/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +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/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 v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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.2.2/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= +github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= +github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +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/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= +github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= +github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= +github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.7/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.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg= +github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU= +github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k= +github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzEE/Zbp4w= +github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= +github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +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/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= +github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= +github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= +github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxSfWAKL3wpBW7V8scJMt8N8gnaMCS9E/cA= +github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= +github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= +github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= +github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= +github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU= +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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/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.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +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/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/shirou/gopsutil v3.20.11-0.20201116082039-2fb5da2f2449+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= +github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= +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.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +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/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +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/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +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.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/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-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= +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= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +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.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +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= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-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-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-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/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-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/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-20190425155659-357c62f0e4bb/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-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +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.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +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.3/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.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= diff --git a/tools/cli/json_register/json_register.go b/tools/cli/json_register/json_register.go new file mode 100644 index 0000000000000000000000000000000000000000..e1ad7870212b72ba4320153d637e77fcc4e83df9 --- /dev/null +++ b/tools/cli/json_register/json_register.go @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package json_register + +import ( + "fmt" + "log" + "reflect" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + jparser "github.com/dubbogo/gost/encoding/json" +) + +import ( + "github.com/apache/dubbo-go/tools/cli/common" +) + +// RegisterStructFromFile create the interface defined by @path file, and register it to hessian +// the interface defined must have field "JavaClassName" +func RegisterStructFromFile(path string) interface{} { + if path == "" { + return nil + } + pair, pkg, err := jparser.File2Interface(path) + log.Printf("Created pkg: \n") + common.PrintInterface(pkg) + if err != nil { + fmt.Println("error: json file parse failed :", err) + return nil + } + for _, v := range pair { + hessian.RegisterPOJOMapping(v.JavaClassName, v.Obj) + } + hessian.RegisterPOJOMapping(getJavaClassName(pkg), pkg) + return pkg +} + +func getJavaClassName(pkg interface{}) string { + val := reflect.ValueOf(pkg).Elem() + typ := reflect.TypeOf(pkg).Elem() + nums := val.NumField() + for i := 0; i < nums; i++ { + if typ.Field(i).Name == "JavaClassName" { + return val.Field(i).String() + } + } + fmt.Println("error: JavaClassName not found") + return "" +} diff --git a/tools/cli/main.go b/tools/cli/main.go new file mode 100644 index 0000000000000000000000000000000000000000..1f90f67293d2a6eedc27392ea647ee5c971fd309 --- /dev/null +++ b/tools/cli/main.go @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package main + +import ( + "flag" + "log" +) + +import ( + "github.com/apache/dubbo-go/tools/cli/client" + "github.com/apache/dubbo-go/tools/cli/json_register" +) + +var host string +var port int +var protocolName string +var InterfaceID string +var version string +var group string +var method string +var sendObjFilePath string +var recvObjFilePath string +var timeout int + +func init() { + flag.StringVar(&host, "h", "localhost", "target server host") + flag.IntVar(&port, "p", 8080, "target server port") + flag.StringVar(&protocolName, "proto", "dubbo", "transfer protocol") + flag.StringVar(&InterfaceID, "i", "com", "target service registered interface") + flag.StringVar(&version, "v", "", "target service version") + flag.StringVar(&group, "g", "", "target service group") + flag.StringVar(&method, "method", "", "target method") + flag.StringVar(&sendObjFilePath, "sendObj", "", "json file path to define transfer struct") + flag.StringVar(&recvObjFilePath, "recvObj", "", "json file path to define receive struct") + flag.IntVar(&timeout, "timeout", 3000, "request timeout (ms)") +} + +func checkParam() { + if method == "" { + log.Fatalln("-method value not fond") + } + if sendObjFilePath == "" { + log.Fatalln("-sendObj value not found") + } + if recvObjFilePath == "" { + log.Fatalln("-recObj value not found") + } +} + +func main() { + flag.Parse() + checkParam() + reqPkg := json_register.RegisterStructFromFile(sendObjFilePath) + recvPkg := json_register.RegisterStructFromFile(recvObjFilePath) + + t, err := client.NewTelnetClient(host, port, protocolName, InterfaceID, version, group, method, reqPkg, timeout) + if err != nil { + panic(err) + } + t.ProcessRequests(recvPkg) + t.Destroy() +} diff --git a/tools/cli/protocol/dubbo/codec.go b/tools/cli/protocol/dubbo/codec.go new file mode 100644 index 0000000000000000000000000000000000000000..f73bf8e5c0d89ffada3bdbaa339c0e76498c6eb7 --- /dev/null +++ b/tools/cli/protocol/dubbo/codec.go @@ -0,0 +1,125 @@ +/* + * 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" + e1 "errors" + "fmt" + "sync" +) + +import ( + perrors "github.com/pkg/errors" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" +) + +//SerialID serial ID +type SerialID byte + +const ( + // S_Dubbo protocol 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 +) + +//////////////////////////////////////////// +// protocol package +//////////////////////////////////////////// + +// SequenceType sequence type +type SequenceType int64 + +// nolint +type DubboPackage struct { + Header hessian.DubboHeader + Service hessian.Service + Body interface{} + Err error +} + +// Marshal encode hessian package. +// DubboPackage -> byte +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. +// byte -> DubboPackage +func (p *DubboPackage) Unmarshal(buf *bytes.Buffer, pendingRsp *sync.Map) error { + 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 p.Header.Type&hessian.PackageRequest != 0x00 { + p.Body = make([]interface{}, 7) + } else { + rspObj, ok := pendingRsp.Load(uint64(p.Header.ID)) + if !ok { + return e1.New(fmt.Sprintf("seq = %d not found", p.Header.ID)) + } + p.Body = &hessian.Response{RspObj: rspObj} + } + + // read body + err = codec.ReadBody(p.Body) + return perrors.WithStack(err) +} + +//////////////////////////////////////////// +// Response +//////////////////////////////////////////// +// Response is protocol 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, + } +} diff --git a/tools/cli/protocol/dubbo/dubbo_protocol.go b/tools/cli/protocol/dubbo/dubbo_protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..97a3ac7ea526ef43256dbfda237a95757eb38fc7 --- /dev/null +++ b/tools/cli/protocol/dubbo/dubbo_protocol.go @@ -0,0 +1,112 @@ +/* + * 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" + "log" + "sync" + "time" +) + +import ( + hessian "github.com/apache/dubbo-go-hessian2" + perrors "github.com/pkg/errors" +) + +import ( + "github.com/apache/dubbo-go/tools/cli/common" + "github.com/apache/dubbo-go/tools/cli/protocol" +) + +func init() { + common.SetProtocol("dubbo", NewRpcClientPackageHandler) +} + +// RpcClientPackageHandler handle package for client in getty. +type RpcClientPackageHandler struct { +} + +func NewRpcClientPackageHandler() protocol.Protocol { + return RpcClientPackageHandler{} +} + +// Read decode @data to DubboPackage. +func (p RpcClientPackageHandler) Read(data []byte, pendingRsp *sync.Map) (interface{}, int, error) { + pkg := &DubboPackage{} + + buf := bytes.NewBuffer(data) + err := pkg.Unmarshal(buf, pendingRsp) + if err != nil { + originErr := perrors.Cause(err) + if originErr == hessian.ErrHeaderNotEnough || originErr == hessian.ErrBodyNotEnough { + return nil, 0, nil + } + + 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) + } + dubboRsp, ok := pkg.Body.(*Response) + if !ok { + log.Println("error: dubbboRsp.Body assertion err:") + } + + return dubboRsp.Reply, hessian.HEADER_LENGTH + pkg.Header.BodyLen, nil +} + +// Write encode @clientReq to []byte +func (p RpcClientPackageHandler) Write(clientReq *protocol.Request) ([]byte, error) { + req, ok := parseReq2DubboPkg(clientReq) + if !ok { + log.Printf("illegal clientReq:%+v\n", clientReq) + return nil, perrors.New("invalid rpc request") + } + + buf, err := req.Marshal() + if err != nil { + log.Printf("binary.Write(req{%#v}) = err{%#v}\n", req, perrors.WithStack(err)) + return nil, perrors.WithStack(err) + } + + return buf.Bytes(), nil +} + +func parseReq2DubboPkg(req *protocol.Request) (*DubboPackage, bool) { + p := &DubboPackage{} + + p.Service.Interface = req.InterfaceID + p.Service.Version = req.Version + p.Service.Group = req.Group + p.Service.Method = req.Method + p.Service.Timeout = time.Second * 3 + p.Header.SerialID = byte(S_Dubbo) + p.Service.Path = req.InterfaceID + + atta := make(map[string]string) + atta["async"] = "false" + atta["interface"] = req.InterfaceID + p.Body = hessian.NewRequest(req.Params, atta) + p.Header.Type = hessian.PackageRequest_TwoWay + p.Header.ID = int64(req.ID) + + return p, true +} diff --git a/tools/cli/protocol/protocol.go b/tools/cli/protocol/protocol.go new file mode 100644 index 0000000000000000000000000000000000000000..136524b52262f934371777b5c652537e6ebd3c2e --- /dev/null +++ b/tools/cli/protocol/protocol.go @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package protocol + +import ( + "sync" +) + +type Protocol interface { + Read([]byte, *sync.Map) (interface{}, int, error) + Write(*Request) ([]byte, error) +} + +type Request struct { + ID uint64 + InterfaceID string // interface瀵诲潃id + Version string + Group string + Method string + Params interface{} + pojo interface{} +} diff --git a/tools/cli/test.sh b/tools/cli/test.sh new file mode 100644 index 0000000000000000000000000000000000000000..fa11833b52b3a332a5dcbf2ced456471bdd9e3fa --- /dev/null +++ b/tools/cli/test.sh @@ -0,0 +1 @@ +go run . -h=localhost -p=20001 -proto=dubbo -i=com.ikurento.user.UserProvider -method=GetUser -sendObj="./userCall.json" -recvObj="./user.json" \ No newline at end of file diff --git a/tools/cli/user.json b/tools/cli/user.json new file mode 100644 index 0000000000000000000000000000000000000000..1e6321993cda758e1511b3f08540b334e8e8027e --- /dev/null +++ b/tools/cli/user.json @@ -0,0 +1,12 @@ +{ + "ID": "string", + "Name": "string", + "Age": "int", + "JavaClassName": "string@com.ikurento.user.User", + "SubInfo": { + "SubID": "string", + "SubMale": "bool", + "SubAge": "int", + "JavaClassName":"string@com.ikurento.user.SubInfo" + } +} \ No newline at end of file diff --git a/tools/cli/userCall.json b/tools/cli/userCall.json new file mode 100644 index 0000000000000000000000000000000000000000..6ef703569dcb4c8274b99d562cb874d0f74eb064 --- /dev/null +++ b/tools/cli/userCall.json @@ -0,0 +1,11 @@ +{ + "ID": "string@A000", + "Male": "bool@true", + "SubInfo": { + "SubID": "string@A001", + "SubMale": "bool@false", + "SubAge": "int@18", + "JavaClassName":"string@com.ikurento.user.SubInfo" + }, + "JavaClassName": "string@com.ikurento.user.CallUserStruct" +} \ No newline at end of file