Merge pull request #3 from apache/1.5
1.5
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 3eb1ec0..0c5d3e0 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 0000000..6ce0286
--- /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 0000000..f2e185c
--- /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 9ac051a..865b60e 100644
--- a/.github/workflows/github-actions.yml
+++ b/.github/workflows/github-actions.yml
@@ -19,49 +19,63 @@
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: 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: 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: 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: 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: gofmt
+ run: |
+ go fmt ./... && git status && [[ -z `git status -s` ]]
+ # diff -u <(echo -n) <(gofmt -d -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: 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: Coverage
- run: bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
+ - 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 8158b49..898962e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,3 +36,5 @@
# 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 0000000..6f214f4
--- /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 566c88e..0000000
--- 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 a2849f4..28ffeb8 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 @@
- [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 @@
- [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 0000000..2f6c9bd
--- /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 1120c20..e0f4af6 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 1eab98b..906c08f 100644
--- a/README.md
+++ b/README.md
@@ -1,13 +1,14 @@
# Apache Dubbo-go [中文](./README_CN.md) #
-[![Build Status](https://travis-ci.org/apache/dubbo-go.svg?branch=master)](https://travis-ci.org/apache/dubbo-go)
+[![Build Status](https://github.com/apache/dubbo-go/workflows/CI/badge.svg)](https://travis-ci.org/apache/dubbo-go)
[![codecov](https://codecov.io/gh/apache/dubbo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-go)
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc)
[![Go Report Card](https://goreportcard.com/badge/github.com/apache/dubbo-go)](https://goreportcard.com/report/github.com/apache/dubbo-go)
![license](https://img.shields.io/badge/license-Apache--2.0-green.svg)
---
-Apache Dubbo Go Implementation.
+
+Apache Dubbo Go Implementation to bridge the gap between java and go.
## License
@@ -16,23 +17,17 @@
## 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 @@
![dubbo go extend](./doc/pic/arch/dubbo-go-ext.png)
-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 @@
* 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 @@
- 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 @@
* [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 @@
## 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
-
-Mac/Linux
-```bash
-sh ./before_ut.sh
-```
-
-Windows
-```bash
-before_ut.bat
-```
-
### Run
-```bash
-go test ./...
-# coverage
-go test ./... -coverprofile=coverage.txt -covermode=atomic
+```bash
+make verify
+```
+
+### Verify license
+
+```bash
+make license
+```
+
+### Run unit test
+
+```bash
+make test
```
## Build
@@ -190,6 +180,30 @@
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 @@
## [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 @@
<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 88e34e6..060b5de 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -1,13 +1,14 @@
# Apache Dubbo-go [English](./README.md) #
-[![Build Status](https://travis-ci.org/apache/dubbo-go.svg?branch=master)](https://travis-ci.org/apache/dubbo-go)
+[![Build Status](https://github.com/apache/dubbo-go/workflows/CI/badge.svg)](https://travis-ci.org/apache/dubbo-go)
[![codecov](https://codecov.io/gh/apache/dubbo-go/branch/master/graph/badge.svg)](https://codecov.io/gh/apache/dubbo-go)
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/apache/dubbo-go?tab=doc)
[![Go Report Card](https://goreportcard.com/badge/github.com/apache/dubbo-go)](https://goreportcard.com/report/github.com/apache/dubbo-go)
![license](https://img.shields.io/badge/license-Apache--2.0-green.svg)
---
-Apache Dubbo Go 语言实现
+
+Apache Dubbo Go 语言实现,架起java和go之间的桥梁,与 gRPC/Spring Cloud 生态互联互通,带领Java生态享受云原生时代的技术红利。
## 证书 ##
@@ -15,24 +16,18 @@
## 发布日志 ##
+[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 @@
* 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 @@
* [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 @@
* [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 @@
## 文档
-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) 贡献代码或者文档,我们都热烈欢迎。具体请参考 [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 @@
## [User List](https://github.com/apache/dubbo-go/issues/2)
-若你正在使用 [apache/dubbo-go](github.com/apache/dubbo-go) 且认为其有用或者想对其做改进,请添列贵司信息于 [用户列表](https://github.com/apache/dubbo-go/issues/2),以便我们知晓。
+若你正在使用 [apache/dubbo-go](https://github.com/apache/dubbo-go) 且认为其有用或者想对其做改进,请添列贵司信息于 [用户列表](https://github.com/apache/dubbo-go/issues/2),以便我们知晓。
<div>
<table>
@@ -223,6 +253,52 @@
<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 7f5cf50..0000000
--- 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 b55e424..0000000
--- 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 8fa6e38..0000000
--- 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 ebd5767..1f41890 100644
--- a/cluster/cluster_impl/available_cluster.go
+++ b/cluster/cluster_impl/available_cluster.go
@@ -39,6 +39,6 @@
}
// 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 ced5b15..0d39bff 100644
--- a/cluster/cluster_impl/base_cluster_invoker.go
+++ b/cluster/cluster_impl/base_cluster_invoker.go
@@ -22,7 +22,6 @@
)
import (
- gxnet "github.com/dubbogo/gost/net"
perrors "github.com/pkg/errors"
"go.uber.org/atomic"
)
@@ -32,6 +31,7 @@
"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 (invoker *baseClusterInvoker) GetUrl() common.URL {
+func (invoker *baseClusterInvoker) GetUrl() *common.URL {
return invoker.directory.GetUrl()
}
@@ -72,7 +72,7 @@
//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 @@
//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) 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 @@
}
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 62f4804..5e0d133 100644
--- a/cluster/cluster_impl/failback_cluster_invoker.go
+++ b/cluster/cluster_impl/failback_cluster_invoker.go
@@ -77,8 +77,7 @@
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 @@
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 0edb81d..d36e16e 100644
--- a/cluster/cluster_impl/failback_cluster_test.go
+++ b/cluster/cluster_impl/failback_cluster_test.go
@@ -72,6 +72,8 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 77e8e9c..9ac06b8 100644
--- a/cluster/cluster_impl/failfast_cluster_test.go
+++ b/cluster/cluster_impl/failfast_cluster_test.go
@@ -53,6 +53,7 @@
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 @@
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 @@
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 4260a93..ca490e7 100644
--- a/cluster/cluster_impl/failover_cluster_invoker.go
+++ b/cluster/cluster_impl/failover_cluster_invoker.go
@@ -24,12 +24,12 @@
)
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 @@
result protocol.Result
invoked []protocol.Invoker
providers []string
+ ivk protocol.Invoker
)
invokers := invoker.directory.List(invocation)
@@ -75,7 +76,7 @@
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 @@
}
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 d3ac2c8..3ea6232 100644
--- a/cluster/cluster_impl/failover_cluster_test.go
+++ b/cluster/cluster_impl/failover_cluster_test.go
@@ -45,7 +45,7 @@
// nolint
type MockInvoker struct {
- url common.URL
+ url *common.URL
available bool
destroyed bool
@@ -53,7 +53,7 @@
}
// 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 @@
}
// 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 d9a716e..5e208bd 100644
--- a/cluster/cluster_impl/failsafe_cluster_test.go
+++ b/cluster/cluster_impl/failsafe_cluster_test.go
@@ -52,6 +52,7 @@
invokers := []protocol.Invoker{}
invokers = append(invokers, invoker)
+ invoker.EXPECT().IsAvailable().Return(true).AnyTimes()
invoker.EXPECT().GetUrl().Return(failbackUrl)
@@ -67,6 +68,8 @@
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 @@
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 1684448..3ffda58 100644
--- a/cluster/cluster_impl/forking_cluster_invoker.go
+++ b/cluster/cluster_impl/forking_cluster_invoker.go
@@ -56,7 +56,7 @@
}
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 0f52b04..050f831 100644
--- a/cluster/cluster_impl/zone_aware_cluster_invoker.go
+++ b/cluster/cluster_impl/zone_aware_cluster_invoker.go
@@ -43,7 +43,7 @@
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 20db1f2..309cd44 100644
--- a/cluster/directory/base_directory.go
+++ b/cluster/directory/base_directory.go
@@ -65,8 +65,8 @@
}
// 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 @@
routers := make([]router.PriorityRouter, 0, len(urls))
+ rc := dir.routerChain
+
for _, url := range urls {
routerKey := url.GetParam(constant.ROUTER_KEY, "")
@@ -94,7 +96,7 @@
}
}
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 @@
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 a2b62df..443f07d 100644
--- a/cluster/directory/base_directory_test.go
+++ b/cluster/directory/base_directory_test.go
@@ -24,11 +24,11 @@
)
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 @@
)
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 @@
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 @@
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 @@
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 @@
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 87f5135..d9695d4 100644
--- a/cluster/directory/static_directory.go
+++ b/cluster/directory/static_directory.go
@@ -34,15 +34,18 @@
// 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 @@
return invokers
}
dirUrl := dir.GetUrl()
- return routerChain.Route(invokers, &dirUrl, invocation)
+ return routerChain.Route(dirUrl, invocation)
}
// Destroy Destroy
@@ -88,10 +91,11 @@
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 27ce236..3d036b4 100644
--- a/cluster/loadbalance/consistent_hash.go
+++ b/cluster/loadbalance/consistent_hash.go
@@ -105,7 +105,7 @@
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 9f22d39..0fbb740 100644
--- a/cluster/loadbalance/consistent_hash_test.go
+++ b/cluster/loadbalance/consistent_hash_test.go
@@ -84,9 +84,9 @@
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 b94d7da..c24fdf0 100644
--- a/cluster/loadbalance/random_test.go
+++ b/cluster/loadbalance/random_test.go
@@ -83,6 +83,7 @@
}
selectedInvoker = append(selectedInvoker, s)
}
+ assert.Equal(t, 10000, len(selectedInvoker))
assert.Condition(t, func() bool {
// really is 0.9999999999999
@@ -114,6 +115,8 @@
}
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 e69de29..0000000
--- a/cluster/router/.gitkeep
+++ /dev/null
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 e3e84b8..cb33cf9 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 8746c1d..13cb1ff 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,33 +53,45 @@
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()
-
- for _, r := range rs {
- finalInvokers = r.Route(finalInvokers, url, invocation)
+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
}
+
+ bitmap := cache.bitmap
+ for _, r := range c.copyRouters() {
+ bitmap = r.Route(bitmap, cache, url, invocation)
+ }
+
+ indexes := bitmap.ToArray()
+ finalInvokers := make([]protocol.Invoker, len(indexes))
+ for i, index := range indexes {
+ finalInvokers[i] = cache.invokers[index]
+ }
+
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)
- }
- }
-}
-
// AddRouters Add routers to router chain
// New a array add builtinRouters which is not sorted in RouterChain and routers
// Sort the array
@@ -86,10 +104,119 @@
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 @@
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 @@
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 dec0389..d38b3be 100644
--- a/cluster/router/chain/chain_test.go
+++ b/cluster/router/chain/chain_test.go
@@ -25,6 +25,8 @@
)
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 @@
_ "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 @@
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 @@
_, 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 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 @@
_, 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 @@
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 @@
}
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 @@
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 @@
_, 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 @@
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 @@
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 @@
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 0000000..43cdfa5
--- /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 056e328..64b3914 100644
--- a/cluster/router/condition/app_router.go
+++ b/cluster/router/condition/app_router.go
@@ -34,14 +34,15 @@
// 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 cce96b1..86fdede 100644
--- a/cluster/router/condition/app_router_test.go
+++ b/cluster/router/condition/app_router_test.go
@@ -24,6 +24,7 @@
)
import (
+ gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
"github.com/stretchr/testify/assert"
)
@@ -35,7 +36,6 @@
"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 @@
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 @@
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 @@
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 @@
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 f8d3e13..2c1f035 100644
--- a/cluster/router/condition/factory.go
+++ b/cluster/router/condition/factory.go
@@ -37,8 +37,8 @@
}
// 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 @@
}
// 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 0f61b39..e08016d 100644
--- a/cluster/router/condition/factory_test.go
+++ b/cluster/router/condition/factory_test.go
@@ -26,12 +26,14 @@
)
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 @@
)
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 (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
return bi.url
}
@@ -73,20 +75,20 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 996db74..a97d9cb 100644
--- a/cluster/router/condition/file.go
+++ b/cluster/router/condition/file.go
@@ -39,7 +39,7 @@
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 @@
}
// 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 bd19a0d..9035e95 100644
--- a/cluster/router/condition/file_test.go
+++ b/cluster/router/condition/file_test.go
@@ -89,33 +89,41 @@
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 @@
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 7f4f14a..2e55b20 100644
--- a/cluster/router/condition/listenable_router.go
+++ b/cluster/router/condition/listenable_router.go
@@ -22,10 +22,12 @@
)
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 @@
url *common.URL
force bool
priority int64
+ notify chan struct{}
}
// RouterRule Get RouterRule instance from listenableRouter
@@ -54,7 +57,7 @@
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 @@
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 @@
return
}
l.generateConditions(routerRule)
+ go func() {
+ l.notify <- struct{}{}
+ }()
}
func (l *listenableRouter) generateConditions(rule *RouterRule) {
@@ -129,13 +136,13 @@
}
// 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 @@
}
// 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 751b5a7..d543ca3 100644
--- a/cluster/router/condition/router.go
+++ b/cluster/router/condition/router.go
@@ -23,20 +23,22 @@
)
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 @@
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 @@
enabled bool
WhenCondition map[string]MatchPair
ThenCondition map[string]MatchPair
+ notify chan struct{}
}
// NewConditionRouterWithRule Init condition router by raw rule
@@ -102,7 +112,7 @@
}
// 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 @@
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 @@
}
// 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 @@
}
// 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 @@
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 369b14f..192e528 100644
--- a/cluster/router/condition/router_rule_test.go
+++ b/cluster/router/condition/router_rule_test.go
@@ -80,5 +80,5 @@
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 c27a1d9..2895703 100644
--- a/cluster/router/condition/router_test.go
+++ b/cluster/router/condition/router_test.go
@@ -58,8 +58,13 @@
}
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 @@
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/chan.go b/cluster/router/conn_checker.go
similarity index 75%
copy from cluster/router/chan.go
copy to cluster/router/conn_checker.go
index e3e84b8..fda0ef8 100644
--- a/cluster/router/chan.go
+++ b/cluster/router/conn_checker.go
@@ -21,12 +21,8 @@
"github.com/apache/dubbo-go/protocol"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
-
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+// 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 0000000..97f049d
--- /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 0000000..fec7331
--- /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 0000000..9f05b06
--- /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 0000000..44d148e
--- /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 0000000..a7b19aa
--- /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 0000000..02f8fb4
--- /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 c522bdf..eb15e6f 100644
--- a/cluster/router/healthcheck/default_health_check.go
+++ b/cluster/router/healthcheck/default_health_check.go
@@ -110,8 +110,8 @@
// 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 5d35ae8..c32a607 100644
--- a/cluster/router/healthcheck/default_health_check_test.go
+++ b/cluster/router/healthcheck/default_health_check_test.go
@@ -43,7 +43,7 @@
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 @@
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 @@
}
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 @@
// 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 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 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 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 @@
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 40c9dd7..a9054c7 100644
--- a/cluster/router/healthcheck/factory.go
+++ b/cluster/router/healthcheck/factory.go
@@ -38,6 +38,6 @@
}
// 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 e80fd4c..1e73683 100644
--- a/cluster/router/healthcheck/factory_test.go
+++ b/cluster/router/healthcheck/factory_test.go
@@ -33,18 +33,18 @@
// 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 ee42e47..bd9543e 100644
--- a/cluster/router/healthcheck/health_check_route.go
+++ b/cluster/router/healthcheck/health_check_route.go
@@ -18,7 +18,12 @@
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 @@
)
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 @@
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 @@
}
// 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 d5862fb..f499c85 100644
--- a/cluster/router/healthcheck/health_check_route_test.go
+++ b/cluster/router/healthcheck/health_check_route_test.go
@@ -29,6 +29,9 @@
)
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 @@
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 @@
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 @@
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 @@
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 0000000..42e03e2
--- /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 0000000..a5a683b
--- /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 0000000..87eaaf7
--- /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 0000000..88535d3
--- /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 66603c1..1d71554 100644
--- a/cluster/router/router.go
+++ b/cluster/router/router.go
@@ -18,6 +18,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 @@
// 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 @@
// 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 @@
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 a5d989c..fd2c15c 100644
--- a/cluster/router/tag/factory.go
+++ b/cluster/router/tag/factory.go
@@ -37,8 +37,8 @@
// 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 ee19582..2a8eac2 100644
--- a/cluster/router/tag/factory_test.go
+++ b/cluster/router/tag/factory_test.go
@@ -39,7 +39,12 @@
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 433abcb..81c2510 100644
--- a/cluster/router/tag/file.go
+++ b/cluster/router/tag/file.go
@@ -24,10 +24,12 @@
)
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 @@
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 @@
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 @@
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 @@
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 c0a2d76..512d8f1 100644
--- a/cluster/router/tag/router_rule.go
+++ b/cluster/router/tag/router_rule.go
@@ -84,9 +84,9 @@
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 @@
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 fafed68..3d0393a 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 @@
"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 @@
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 @@
url: url,
enabled: url.GetParamBool(constant.RouterEnabled, true),
priority: url.GetParamInt(constant.RouterPriority, 0),
+ notify: notify,
}, nil
}
@@ -65,62 +98,79 @@
}
// 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)
}
-
- // 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, "")
- }
- }
-
- // if we are requesting for a Provider with a specific tag
- if len(tag) > 0 {
- return filterInvokersWithTag(invokers, url, invocation, *tagRouterRuleCopy, tag)
- }
-
- // 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
- }
- }
- // 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 filterInvoker(result, filter)
+ return c.routeWithStaticTag(invokers, cache, url, invocation)
}
+// 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
+ }
+
+ ret, _ := c.filterWithTag(invokers, cache, staticPrefix+tag)
+ if ret.IsEmpty() && !isForceUseTag(url, invocation) {
+ return invokers
+ }
+
+ 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)
+ }
+ 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 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 @@
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 @@
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 @@
}
}
-// 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
+
+ tagNameToAddresses := rule.getTagNameToAddresses()
+ for tag, addrs := range tagNameToAddresses {
+ pool[dynamicPrefix+tag] = addrsToBitmap(addrs, 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
- }
- // 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)
+ 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)
+ }
}
- filterTagIsEmpty := func(invoker protocol.Invoker) bool {
- return invoker.GetUrl().GetParam(constant.Tagkey, "") == ""
- }
- 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 @@
return false
}
-type filter func(protocol.Invoker) bool
-
-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
- }
+// 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
}
- res = append(res, invoker)
+
+ index := findIndexWithIp(addr, invokers)
+ if index != -1 {
+ ret.AddInt(index)
+ }
}
- 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 6b9a5e0..3b53d47 100644
--- a/cluster/router/tag/tag_router_test.go
+++ b/cluster/router/tag/tag_router_test.go
@@ -25,22 +25,25 @@
)
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 @@
)
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 (bi *MockInvoker) GetUrl() common.URL {
+func (bi *MockInvoker) GetUrl() *common.URL {
return bi.url
}
@@ -117,17 +119,27 @@
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 @@
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 @@
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 TestFilterInvoker(t *testing.T) {
+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 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 @@
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
+
+ url, _ := common.NewURL(tagRouterTestBeijingUrl)
+ notify := make(chan struct{})
+ go func() {
+ for range notify {
}
- 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
- }
- 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 @@
- 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 @@
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 @@
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 @@
- 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 0000000..8b4ee55
--- /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 44cdd1f..23baa70 100644
--- a/common/config/environment.go
+++ b/common/config/environment.go
@@ -34,8 +34,8 @@
// 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 @@
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 @@
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 183071b..ed3ca55 100644
--- a/common/config/environment_test.go
+++ b/common/config/environment_test.go
@@ -34,6 +34,29 @@
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 4165942..bbe022c 100644
--- a/common/constant/default.go
+++ b/common/constant/default.go
@@ -81,7 +81,7 @@
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 943338f..4b867d8 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 @@
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 @@
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 @@
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 @@
TAG_ROUTE_PROTOCOL = "tag"
PROVIDERS_CATEGORY = "providers"
ROUTER_KEY = "router"
+ EXPORT_KEY = "export"
)
const (
@@ -169,6 +174,7 @@
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 (
- 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 @@
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 @@
// 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 @@
// 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 @@
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 7302243..0403e3a 100644
--- a/common/constant/version.go
+++ b/common/constant/version.go
@@ -19,9 +19,9 @@
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 0000000..db126b7
--- /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 0000000..fbd9e34
--- /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 e35677d..08ddbc3 100644
--- a/common/extension/metadata_service.go
+++ b/common/extension/metadata_service.go
@@ -26,12 +26,15 @@
)
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 @@
"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/cluster/router/chan.go b/common/host_util.go
similarity index 69%
copy from cluster/router/chan.go
copy to common/host_util.go
index e3e84b8..9861c92 100644
--- a/cluster/router/chan.go
+++ b/common/host_util.go
@@ -15,18 +15,16 @@
* limitations under the License.
*/
-package router
+package common
-import (
- "github.com/apache/dubbo-go/protocol"
-)
+import gxnet "github.com/dubbogo/gost/net"
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+var localIp string
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+func GetLocalIp() string {
+ if len(localIp) != 0 {
+ return localIp
+ }
+ localIp, _ = gxnet.GetLocalIP()
+ return localIp
}
diff --git a/cluster/router/chan.go b/common/host_util_test.go
similarity index 70%
copy from cluster/router/chan.go
copy to common/host_util_test.go
index e3e84b8..ad96dde 100644
--- a/cluster/router/chan.go
+++ b/common/host_util_test.go
@@ -15,18 +15,16 @@
* limitations under the License.
*/
-package router
+package common
import (
- "github.com/apache/dubbo-go/protocol"
+ "testing"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+import (
+ "github.com/stretchr/testify/assert"
+)
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+func TestGetLocalIp(t *testing.T) {
+ assert.NotNil(t, GetLocalIp())
}
diff --git a/common/logger/logger.go b/common/logger/logger.go
index 63eda23..8519543 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 @@
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 @@
// 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 4febd78..b9c1f39 100644
--- a/common/node.go
+++ b/common/node.go
@@ -19,7 +19,7 @@
// 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 d51ce1c..fd34810 100644
--- a/common/proxy/proxy.go
+++ b/common/proxy/proxy.go
@@ -40,23 +40,47 @@
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 @@
// 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 @@
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 @@
}
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 @@
}
}
- 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 117428c..2e66c34 100644
--- a/common/proxy/proxy_factory.go
+++ b/common/proxy/proxy_factory.go
@@ -26,7 +26,7 @@
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 752f3ea..ff3d795 100644
--- a/common/proxy/proxy_factory/default.go
+++ b/common/proxy/proxy_factory/default.go
@@ -72,7 +72,7 @@
}
// 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 @@
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 @@
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 99d5c02..4002ab9 100644
--- a/common/proxy/proxy_factory/default_test.go
+++ b/common/proxy/proxy_factory/default_test.go
@@ -34,7 +34,7 @@
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 @@
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 c606615..c335bf6 100644
--- a/common/proxy/proxy_test.go
+++ b/common/proxy/proxy_test.go
@@ -19,12 +19,12 @@
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 @@
"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 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 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 @@
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 5ed4df6..30d72c1 100644
--- a/common/rpc_service.go
+++ b/common/rpc_service.go
@@ -106,10 +106,10 @@
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 @@
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 @@
}
// 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 @@
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 @@
}
// 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 @@
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 @@
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 7c4eb42..e8bd393 100644
--- a/common/rpc_service_test.go
+++ b/common/rpc_service_test.go
@@ -27,6 +27,10 @@
"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 @@
// 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 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 5a3e574..80b85fc 100644
--- a/common/url.go
+++ b/common/url.go
@@ -26,9 +26,11 @@
"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 @@
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 @@
}
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 @@
// 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 @@
}
// 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 @@
}
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 @@
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 @@
}
// 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 (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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
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 int64(r)
+ 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 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 int64(r)
+ 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 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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
// 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 @@
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 @@
}
// 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 @@
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 @@
// 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) 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 6845190..c645f1a 100644
--- a/common/url_test.go
+++ b/common/url_test.go
@@ -68,12 +68,17 @@
"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 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 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 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 @@
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 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 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 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 @@
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 @@
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 @@
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 5a271a2..19f4fe1 100644
--- a/common/yaml/yaml_test.go
+++ b/common/yaml/yaml_test.go
@@ -66,5 +66,5 @@
}
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 ef99664..6fe6c5d 100644
--- a/config/application_config.go
+++ b/config/application_config.go
@@ -48,8 +48,5 @@
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 336bb03..df1686a 100644
--- a/config/base_config.go
+++ b/config/base_config.go
@@ -33,10 +33,6 @@
"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 @@
// 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 (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 849a9c4..d3f50c8 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 @@
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 @@
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 @@
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 @@
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 @@
mockMap["dubbo.protocols.jsonrpc1.port"] = "20001"
config.GetEnvInstance().UpdateExternalConfigMap(mockMap)
+ config.GetEnvInstance().UpdateAppExternalConfigMap(map[string]string{})
father := &ProviderConfig{
BaseConfig: BaseConfig{
@@ -285,8 +289,7 @@
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 0000000..eac5a79
--- /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 0000000..fd8d561
--- /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 0fc4007..2489709 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 @@
"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 @@
//
// 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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 @@
// 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 c66e526..1fd27d5 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 (
- gxnet "github.com/dubbogo/gost/net"
perrors "github.com/pkg/errors"
)
@@ -38,6 +39,7 @@
"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 @@
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 @@
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 @@
}
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 @@
if url == nil {
return
}
- instance, err := createInstance(*url)
+ instance, err := createInstance(url)
if err != nil {
panic(err)
}
@@ -241,7 +270,7 @@
}
// 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 @@
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 @@
// 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 @@
break
}
}
- return &selectedUrl
+ return selectedUrl
}
func initRouter() {
@@ -410,9 +436,9 @@
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 461e607..5cda3b2 100644
--- a/config/config_loader_test.go
+++ b/config/config_loader_test.go
@@ -25,10 +25,9 @@
)
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 c808360..ca88fe3 100644
--- a/config/consumer_config.go
+++ b/config/consumer_config.go
@@ -43,9 +43,9 @@
// 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 @@
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 aa102f3..89ac2e3 100644
--- a/config/graceful_shutdown.go
+++ b/config/graceful_shutdown.go
@@ -51,6 +51,7 @@
* 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 @@
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 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 80eb531..43d2fc3 100644
--- a/config/graceful_shutdown_config_test.go
+++ b/config/graceful_shutdown_config_test.go
@@ -32,8 +32,8 @@
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 8e833dd..68a197d 100644
--- a/config/instance/metadata_report.go
+++ b/config/instance/metadata_report.go
@@ -29,7 +29,7 @@
var (
instance report.MetadataReport
- reportUrl common.URL
+ reportUrl *common.URL
once sync.Once
)
@@ -41,18 +41,18 @@
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 d489af0..110903a 100644
--- a/config/instance/metadata_report_test.go
+++ b/config/instance/metadata_report_test.go
@@ -38,7 +38,7 @@
return &mockMetadataReportFactory{}
})
u, _ := common.NewURL("mock://127.0.0.1")
- rpt := GetMetadataReportInstance(&u)
+ rpt := GetMetadataReportInstance(u)
assert.NotNil(t, rpt)
}
@@ -60,7 +60,7 @@
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/cluster/router/chan.go b/config/interfaces/config_post_processor.go
similarity index 63%
copy from cluster/router/chan.go
copy to config/interfaces/config_post_processor.go
index e3e84b8..53dd717 100644
--- a/cluster/router/chan.go
+++ b/config/interfaces/config_post_processor.go
@@ -15,18 +15,18 @@
* limitations under the License.
*/
-package router
+package interfaces
import (
- "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/common"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+// 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)
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+ // PostProcessServiceConfig customizes ServiceConfig's params.
+ PostProcessServiceConfig(*common.URL)
}
diff --git a/config/metadata_report_config.go b/config/metadata_report_config.go
index 6d319e5..24eba5e 100644
--- a/config/metadata_report_config.go
+++ b/config/metadata_report_config.go
@@ -84,7 +84,7 @@
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 @@
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 b64306f..db52940 100644
--- a/config/method_config.go
+++ b/config/method_config.go
@@ -57,8 +57,5 @@
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 fcb429b..4113aab 100644
--- a/config/provider_config.go
+++ b/config/provider_config.go
@@ -37,8 +37,8 @@
// 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 @@
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 cd10f89..f71c7ec 100644
--- a/config/reference_config.go
+++ b/config/reference_config.go
@@ -86,17 +86,13 @@
}
*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 @@
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 @@
}
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 @@
}
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 @@
// 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 @@
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 @@
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 a4345ad..0207e1f 100644
--- a/config/reference_config_test.go
+++ b/config/reference_config_test.go
@@ -334,7 +334,7 @@
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 @@
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 @@
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 89566c4..ed81a07 100644
--- a/config/registry_config.go
+++ b/config/registry_config.go
@@ -53,7 +53,7 @@
//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 @@
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 @@
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 6e5dedc..8bd2de1 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 @@
},
}
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 @@
},
}
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 0f0c3e5..61d4dce 100644
--- a/config/remote_config.go
+++ b/config/remote_config.go
@@ -63,9 +63,9 @@
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 ed42577..ea19b46 100644
--- a/config/router_config.go
+++ b/config/router_config.go
@@ -66,7 +66,7 @@
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 48632a1..e8523bd 100644
--- a/config/service_config.go
+++ b/config/service_config.go
@@ -73,10 +73,12 @@
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 @@
}
c.exported = atomic.NewBool(false)
c.unexported = atomic.NewBool(false)
+ c.export = true
return nil
}
@@ -111,6 +114,7 @@
id: id,
unexported: atomic.NewBool(false),
exported: atomic.NewBool(false),
+ export: true,
}
}
@@ -170,9 +174,10 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 4d4122e..aea0bde 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 @@
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 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 @@
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 1845650..eec4c86 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 74e89cc..1a1fda6 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 ac5328c..5b74f5e 100644
--- a/config_center/apollo/impl.go
+++ b/config_center/apollo/impl.go
@@ -109,11 +109,11 @@
}
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 @@
* 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 50c4e68..3b2cb16 100644
--- a/config_center/apollo/impl_test.go
+++ b/config_center/apollo/impl_test.go
@@ -126,24 +126,24 @@
}
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 @@
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 @@
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 ace5ed0..44d3255 100644
--- a/config_center/apollo/listener.go
+++ b/config_center/apollo/listener.go
@@ -36,7 +36,7 @@
// 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 ec4e606..7f5abb6 100644
--- a/config_center/configurator/override.go
+++ b/config_center/configurator/override.go
@@ -23,7 +23,6 @@
import (
gxset "github.com/dubbogo/gost/container/set"
- gxnet "github.com/dubbogo/gost/net"
)
import (
@@ -61,7 +60,7 @@
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 @@
// 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 8eccb50..4d2552d 100644
--- a/config_center/configurator/override_test.go
+++ b/config_center/configurator/override_test.go
@@ -37,51 +37,51 @@
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 cbf8e8c..69f8421 100644
--- a/config_center/dynamic_configuration.go
+++ b/config_center/dynamic_configuration.go
@@ -89,6 +89,6 @@
}
// 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 9d82540..6489a07 100644
--- a/config_center/file/impl.go
+++ b/config_center/file/impl.go
@@ -24,7 +24,6 @@
"os"
"os/exec"
"os/user"
- "path"
"path/filepath"
"runtime"
"strings"
@@ -41,11 +40,19 @@
"github.com/apache/dubbo-go/config_center/parser"
)
+var (
+ osType = runtime.GOOS
+)
+
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"
+ windowsOS = "windows"
+)
+
+const (
+ ParamNamePrefix = "dubbo.config-center."
+ ConfigCenterDirParamName = ParamNamePrefix + "dir"
+ ConfigCenterEncodingParamName = ParamNamePrefix + "encoding"
+ defaultConfigCenterEncoding = "UTF-8"
)
// FileSystemDynamicConfiguration
@@ -59,24 +66,14 @@
}
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
// 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 forceMkdirParent(fp string) error {
- pd := getParentDirectory(fp)
-
- return createDir(pd)
+ return createDir(getParentDirectory(fp))
}
func createDir(path string) error {
@@ -253,6 +245,7 @@
if l > len(runes) {
l = len(runes)
}
+
return string(runes[pos:l])
}
@@ -261,13 +254,13 @@
// 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 @@
}
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 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 5889295..556b6a3 100644
--- a/config_center/file/impl_test.go
+++ b/config_center/file/impl_test.go
@@ -42,7 +42,7 @@
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 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 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 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 acfe260..1e96b36 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 @@
"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 options struct {
nacosName string
- client *NacosClient
+ //client *NacosClient
}
// WithNacosName Set nacos name
@@ -122,7 +118,7 @@
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 @@
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 01319f3..2ce3e37 100644
--- a/config_center/nacos/client_test.go
+++ b/config_center/nacos/client_test.go
@@ -36,7 +36,7 @@
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 @@
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 @@
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 be94b9a..7c67930 100644
--- a/config_center/nacos/impl.go
+++ b/config_center/nacos/impl.go
@@ -186,8 +186,8 @@
}
// 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 88d200e..b7bd94b 100644
--- a/config_center/nacos/impl_test.go
+++ b/config_center/nacos/impl_test.go
@@ -40,7 +40,7 @@
// 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 @@
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 @@
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 @@
}
`
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 57df036..b0607e2 100644
--- a/config_center/nacos/listener.go
+++ b/config_center/nacos/listener.go
@@ -38,7 +38,6 @@
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 @@
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 f794221..b104d3d 100644
--- a/config_center/parser/configuration_parser.go
+++ b/config_center/parser/configuration_parser.go
@@ -144,14 +144,14 @@
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 @@
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 485abcb..e24e63f 100644
--- a/config_center/zookeeper/impl.go
+++ b/config_center/zookeeper/impl.go
@@ -24,6 +24,7 @@
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 @@
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 @@
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 @@
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 @@
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) 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 ecc3527..7f17153 100644
--- a/config_center/zookeeper/impl_test.go
+++ b/config_center/zookeeper/impl_test.go
@@ -50,7 +50,7 @@
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 @@
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 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 @@
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 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 @@
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 bc6eb6d..244451a 100644
--- a/config_center/zookeeper/listener.go
+++ b/config_center/zookeeper/listener.go
@@ -23,6 +23,7 @@
)
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) 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 0000000..a8c0754
--- /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 5130151..0da1281 100644
--- a/contributing.md
+++ b/contributing.md
@@ -40,7 +40,7 @@
>- 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 @@
// 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 3f677ff..a36e2b0 100644
--- a/doc/apache/apache-release-procedure-20200306.md
+++ b/doc/apache/apache-release-procedure-20200306.md
@@ -21,7 +21,7 @@
## 1. 发版准备
-发版文件需要签名,需要安装pgp工具.
+发版文件需要签名,需要安装pgp工具.
```bash
$ brew install gpg
@@ -167,7 +167,7 @@
发任何邮件都是有一定格式的,你加入社区邮件列表后,就会收到很多这样的邮件,多看看就知道了,具体邮件范本参考文章后面的邮件范本。
发完【VOTE】邮件,私下沟通群里面请大佬PMC投票。
-PMC投票会对你上传打包文件进行相关检查,
+PMC投票会对你上传打包文件进行相关检查,
详细可以了解孵化中的项目发布完整的检查项参考: https://cwiki.apache.org/confluence/display/INCUBATOR2/IncubatorReleaseChecklist
收到3个binding邮件且超过72小时后,就可以发 投票结果 [RESULT] [VOTE] 邮件了。
@@ -185,7 +185,7 @@
## 5. 发布版本
-当正式发布投票成功后,先发[Result]邮件,然后就准备 release package。
+当正式发布投票成功后,先发[Result]邮件,然后就准备 release package。
将之前在dev下发布的对应rc文件夹下的源码包、签名文件和hash文件拷贝到另一个目录 v1.4.0,
注意文件名字中不要rcxx (可以rename,但不要重新计算签名,hash可以重新计算,结果不会变)。
@@ -253,7 +253,7 @@
[ ] +1 approve
[ ] +0 no opinion
[ ] -1 disapprove with the reason
-
+
Thanks,
The Apache Dubbo-go Team
```
@@ -284,7 +284,7 @@
I checked the following items:
[v] Are release files in correct location? <-- 发布文件目录是否正确
-[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? <-- 签名、hash是否正确
[v] Do LICENSE and NOTICE files exists?
[v] Is the LICENSE and NOTICE text correct? <-- 协议文本是否正确
diff --git a/doc/pic/misc/dubbogo-dingding.png b/doc/pic/misc/dubbogo-dingding.png
new file mode 100644
index 0000000..dc27d4e
--- /dev/null
+++ b/doc/pic/misc/dubbogo-dingding.png
Binary files differ
diff --git a/doc/pic/misc/dubbogo-wechat.png b/doc/pic/misc/dubbogo-wechat.png
new file mode 100644
index 0000000..dd34357
--- /dev/null
+++ b/doc/pic/misc/dubbogo-wechat.png
Binary files differ
diff --git a/filter/access_key.go b/filter/access_key.go
index 4801d64..f38764b 100644
--- a/filter/access_key.go
+++ b/filter/access_key.go
@@ -26,7 +26,7 @@
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 804bf3b..f17c1a2 100644
--- a/filter/filter.go
+++ b/filter/filter.go
@@ -20,6 +20,7 @@
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 6eaf9cb..167b5ed 100644
--- a/filter/filter_impl/access_log_filter.go
+++ b/filter/filter_impl/access_log_filter.go
@@ -105,8 +105,12 @@
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 a3a6151..9c4b947 100644
--- a/filter/filter_impl/access_log_filter_test.go
+++ b/filter/filter_impl/access_log_filter_test.go
@@ -77,6 +77,6 @@
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 9837a49..2397503 100644
--- a/filter/filter_impl/active_filter_test.go
+++ b/filter/filter_impl/active_filter_test.go
@@ -37,7 +37,7 @@
)
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 @@
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 945cf3e..823db82 100644
--- a/filter/filter_impl/auth/consumer_sign.go
+++ b/filter/filter_impl/auth/consumer_sign.go
@@ -42,8 +42,8 @@
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 5b86fc1..7c7131c 100644
--- a/filter/filter_impl/auth/default_authenticator.go
+++ b/filter/filter_impl/auth/default_authenticator.go
@@ -69,7 +69,7 @@
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 8b0fb6b..d915b6a 100644
--- a/filter/filter_impl/auth/default_authenticator_test.go
+++ b/filter/filter_impl/auth/default_authenticator_test.go
@@ -39,7 +39,7 @@
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 @@
}{"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 @@
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 @@
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 @@
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 @@
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_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 d5f5db3..774fdb2 100644
--- a/filter/filter_impl/auth/provider_auth.go
+++ b/filter/filter_impl/auth/provider_auth.go
@@ -42,8 +42,8 @@
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 f6ebfcd..dc130b5 100644
--- a/filter/filter_impl/auth/provider_auth_test.go
+++ b/filter/filter_impl/auth/provider_auth_test.go
@@ -52,7 +52,7 @@
}
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 45170bb..4c12c76 100644
--- a/filter/filter_impl/auth/sign_util.go
+++ b/filter/filter_impl/auth/sign_util.go
@@ -26,6 +26,10 @@
"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 @@
// 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 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 b821a1a..f2ec81a 100644
--- a/filter/filter_impl/echo_filter_test.go
+++ b/filter/filter_impl/echo_filter_test.go
@@ -34,10 +34,10 @@
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 5fc309c..3561161 100644
--- a/filter/filter_impl/execute_limit_filter.go
+++ b/filter/filter_impl/execute_limit_filter.go
@@ -84,7 +84,7 @@
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 953f5e1..682f8fa 100644
--- a/filter/filter_impl/execute_limit_filter_test.go
+++ b/filter/filter_impl/execute_limit_filter_test.go
@@ -36,7 +36,7 @@
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 @@
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 @@
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 @@
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 36b4b13..cf307d0 100644
--- a/filter/filter_impl/generic_filter.go
+++ b/filter/filter_impl/generic_filter.go
@@ -122,15 +122,15 @@
}
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 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 40cf743..4203dd6 100644
--- a/filter/filter_impl/generic_filter_test.go
+++ b/filter/filter_impl/generic_filter_test.go
@@ -98,15 +98,17 @@
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 @@
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 3711e68..89b009b 100644
--- a/filter/filter_impl/generic_service_filter.go
+++ b/filter/filter_impl/generic_service_filter.go
@@ -20,7 +20,6 @@
import (
"context"
"reflect"
- "strings"
)
import (
@@ -75,7 +74,7 @@
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 6781971..c755a2d 100644
--- a/filter/filter_impl/generic_service_filter_test.go
+++ b/filter/filter_impl/generic_service_filter_test.go
@@ -96,7 +96,7 @@
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 @@
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 @@
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 220ef6f..b16956e 100644
--- a/filter/filter_impl/graceful_shutdown_filter_test.go
+++ b/filter/filter_impl/graceful_shutdown_filter_test.go
@@ -39,10 +39,8 @@
)
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 @@
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 @@
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 eebbae5..4973ce7 100644
--- a/filter/filter_impl/hystrix_filter_test.go
+++ b/filter/filter_impl/hystrix_filter_test.go
@@ -18,6 +18,7 @@
import (
"context"
+ "fmt"
"regexp"
"testing"
)
@@ -29,6 +30,8 @@
)
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 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 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 @@
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 @@
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 7722d29..b7b7b0e 100644
--- a/filter/filter_impl/seata_filter.go
+++ b/filter/filter_impl/seata_filter.go
@@ -23,6 +23,7 @@
)
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 @@
)
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 @@
// 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 45817e9..1705eba 100644
--- a/filter/filter_impl/seata_filter_test.go
+++ b/filter/filter_impl/seata_filter_test.go
@@ -50,7 +50,7 @@
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 86d6460..1de27ad 100644
--- a/filter/filter_impl/sentinel_filter.go
+++ b/filter/filter_impl/sentinel_filter.go
@@ -38,13 +38,15 @@
"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 @@
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) Fatalf(format string, v ...interface{}) {
- d.Logger.Errorf(format, v...)
+func (d DubboLoggerWrapper) DebugEnabled() bool {
+ return true
}
-func (d DubboLoggerWrapper) Panic(v ...interface{}) {
- d.Logger.Error(v...)
+func (d DubboLoggerWrapper) Info(msg string, keysAndValues ...interface{}) {
+ d.Logger.Info(logging.AssembleMsg(logging.GlobalCallerDepth, "INFO", msg, nil, keysAndValues))
}
-func (d DubboLoggerWrapper) Panicf(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) WarnEnabled() bool {
+ return true
+}
+
+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 @@
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 @@
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 e8d481b..c6b6d42 100644
--- a/filter/filter_impl/sentinel_filter_test.go
+++ b/filter/filter_impl/sentinel_filter_test.go
@@ -50,11 +50,11 @@
_, 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 cd1bba3..024ae2a 100644
--- a/filter/filter_impl/token_filter_test.go
+++ b/filter/filter_impl/token_filter_test.go
@@ -40,10 +40,10 @@
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 @@
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 @@
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 @@
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 cbbba19..bc78f64 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 @@
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 5761579..f0c2764 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 @@
// 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 @@
// 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 61f28e4..5baa70a 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 @@
"testing"
)
import (
- "github.com/apache/dubbo-go/filter"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
)
@@ -31,12 +30,13 @@
"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 @@
})
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 @@
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 @@
})
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 @@
})
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 b49084f..34c2790 100644
--- a/filter/filter_impl/tps/tps_limiter_mock.go
+++ b/filter/filter_impl/tps/tps_limiter_mock.go
@@ -58,7 +58,7 @@
}
// 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 da0fc48..55a3a55 100644
--- a/filter/filter_impl/tps_limit_filter_test.go
+++ b/filter/filter_impl/tps_limit_filter_test.go
@@ -44,10 +44,10 @@
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 @@
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 @@
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 b8058aa..dcdbe5b 100644
--- a/filter/filter_impl/tracing_filter.go
+++ b/filter/filter_impl/tracing_filter.go
@@ -83,7 +83,7 @@
}()
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 e159b74..bf96a7d 100644
--- a/filter/filter_impl/tracing_filter_test.go
+++ b/filter/filter_impl/tracing_filter_test.go
@@ -57,6 +57,6 @@
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 bff5476..5f2f458 100644
--- a/filter/handler/rejected_execution_handler_mock.go
+++ b/filter/handler/rejected_execution_handler_mock.go
@@ -58,7 +58,7 @@
}
// 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 52ac176..5242b5b 100644
--- a/filter/handler/rejected_execution_handler_only_log.go
+++ b/filter/handler/rejected_execution_handler_only_log.go
@@ -63,7 +63,7 @@
}
// 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 409f09f..7aa4aff 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 @@
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 3d1e1c1..73ac3d3 100644
--- a/filter/rejected_execution_handler.go
+++ b/filter/rejected_execution_handler.go
@@ -33,5 +33,5 @@
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 8385d7b..6f2466c 100644
--- a/filter/tps_limiter.go
+++ b/filter/tps_limiter.go
@@ -35,5 +35,5 @@
*/
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 317431f..4f79f3d 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 20d5345..b533e4a 100644
--- a/go.sum
+++ b/go.sum
@@ -62,11 +62,14 @@
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/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/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 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/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/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-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/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/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/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/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/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-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.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/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/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/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/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.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/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.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/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.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/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/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.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-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 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/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.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.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.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.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/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/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-/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/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/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/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 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/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/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/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/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/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-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-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-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-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-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.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-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/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/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.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 deccda7..e1bbc8c 100644
--- a/integrate_test.sh
+++ b/integrate_test.sh
@@ -26,12 +26,12 @@
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 @@
# 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 dbbc0c8..a032313 100644
--- a/metadata/definition/definition.go
+++ b/metadata/definition/definition.go
@@ -93,7 +93,7 @@
}
// 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 958f932..3891592 100644
--- a/metadata/definition/definition_test.go
+++ b/metadata/definition/definition_test.go
@@ -28,7 +28,6 @@
import (
"github.com/apache/dubbo-go/common"
- "github.com/apache/dubbo-go/common/constant"
)
func TestBuildServiceDefinition(t *testing.T) {
@@ -44,9 +43,9 @@
"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 b9e6596..3035cf3 100644
--- a/metadata/identifier/service_metadata_identifier.go
+++ b/metadata/identifier/service_metadata_identifier.go
@@ -32,7 +32,7 @@
// 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 2896b0f..af21704 100644
--- a/metadata/mapping/dynamic/service_name_mapping_test.go
+++ b/metadata/mapping/dynamic/service_name_mapping_test.go
@@ -39,6 +39,7 @@
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 eb2bdc2..e211f7f 100644
--- a/metadata/report/consul/report.go
+++ b/metadata/report/consul/report.go
@@ -61,7 +61,7 @@
}
// 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 e07a742..51e9317 100644
--- a/metadata/report/consul/report_test.go
+++ b/metadata/report/consul/report_test.go
@@ -100,7 +100,7 @@
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 @@
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 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 @@
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 cdd29ab..56a22de 100644
--- a/metadata/report/delegate/delegate_report.go
+++ b/metadata/report/delegate/delegate_report.go
@@ -94,7 +94,7 @@
// MetadataReport is a absolute delegate for MetadataReport
type MetadataReport struct {
- reportUrl common.URL
+ reportUrl *common.URL
syncReport bool
metadataReportRetry *metadataReportRetry
@@ -109,6 +109,10 @@
// 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 @@
}
// 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 @@
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 @@
}
// 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 @@
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 3dfca57..f60acf6 100644
--- a/metadata/report/delegate/delegate_report_test.go
+++ b/metadata/report/delegate/delegate_report_test.go
@@ -46,13 +46,10 @@
})
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 @@
})
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 @@
"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 097835c..1939b91 100644
--- a/metadata/report/etcd/report.go
+++ b/metadata/report/etcd/report.go
@@ -63,7 +63,7 @@
// 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 5dd8780..59d0975 100644
--- a/metadata/report/etcd/report_test.go
+++ b/metadata/report/etcd/report_test.go
@@ -60,7 +60,7 @@
t.Fatal(err)
}
metadataReportFactory := &etcdMetadataReportFactory{}
- metadataReport := metadataReportFactory.CreateMetadataReport(&url)
+ metadataReport := metadataReportFactory.CreateMetadataReport(url)
assert.NotNil(t, metadataReport)
e.Close()
}
@@ -72,7 +72,7 @@
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 @@
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 d69913b..42e9859 100644
--- a/metadata/report/nacos/report.go
+++ b/metadata/report/nacos/report.go
@@ -69,7 +69,7 @@
}
// 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 be01eb2..1b7075e 100644
--- a/metadata/report/nacos/report_test.go
+++ b/metadata/report/nacos/report_test.go
@@ -19,8 +19,10 @@
import (
"encoding/json"
+ "net/http"
"strconv"
"testing"
+ "time"
)
import (
@@ -36,6 +38,9 @@
)
func TestNacosMetadataReport_CRUD(t *testing.T) {
+ if !checkNacosServerAlive() {
+ return
+ }
rpt := newTestReport()
assert.NotNil(t, rpt)
@@ -111,6 +116,14 @@
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 62a9055..dcb4142 100644
--- a/metadata/report/report.go
+++ b/metadata/report/report.go
@@ -38,7 +38,7 @@
// 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 8f46bb0..472f50a 100644
--- a/metadata/report/zookeeper/report.go
+++ b/metadata/report/zookeeper/report.go
@@ -23,13 +23,16 @@
)
import (
+ gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
+)
+
+import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/constant"
"github.com/apache/dubbo-go/common/extension"
"github.com/apache/dubbo-go/metadata/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 @@
// zookeeperMetadataReport is the implementation of
// MetadataReport based on zookeeper.
type zookeeperMetadataReport struct {
- client *zookeeper.ZookeeperClient
+ client *gxzookeeper.ZookeeperClient
rootDir string
}
@@ -63,7 +66,7 @@
}
// 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 @@
// 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 a1e46e2..3f927b1 100644
--- a/metadata/report/zookeeper/report_test.go
+++ b/metadata/report/zookeeper/report_test.go
@@ -100,7 +100,7 @@
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 @@
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 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 @@
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 ceda255..7c2baa2 100644
--- a/metadata/service/exporter/configurable/exporter_test.go
+++ b/metadata/service/exporter/configurable/exporter_test.go
@@ -65,11 +65,11 @@
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 1f8eeaa..becd804 100644
--- a/metadata/service/inmemory/metadata_service_proxy_factory.go
+++ b/metadata/service/inmemory/metadata_service_proxy_factory.go
@@ -50,7 +50,7 @@
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 96020e1..f5e519c 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 @@
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 @@
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 8269e69..8da78c3 100644
--- a/metadata/service/inmemory/service.go
+++ b/metadata/service/inmemory/service.go
@@ -22,7 +22,6 @@
)
import (
- cm "github.com/Workiva/go-datastructures/common"
"github.com/Workiva/go-datastructures/slice/skip"
)
@@ -75,23 +74,6 @@
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 @@
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 @@
}
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) 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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
}
// 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 e2b2968..8b93aab 100644
--- a/metadata/service/inmemory/service_proxy.go
+++ b/metadata/service/inmemory/service_proxy.go
@@ -39,8 +39,8 @@
// 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 @@
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 @@
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 0d75517..9278fd9 100644
--- a/metadata/service/inmemory/service_proxy_test.go
+++ b/metadata/service/inmemory/service_proxy_test.go
@@ -48,19 +48,33 @@
// 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 048c286..e50cd62 100644
--- a/metadata/service/inmemory/service_test.go
+++ b/metadata/service/inmemory/service_test.go
@@ -46,7 +46,9 @@
"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 @@
"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 @@
"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 ae83a69..e2a7a64 100644
--- a/metadata/service/remote/service.go
+++ b/metadata/service/remote/service.go
@@ -89,47 +89,67 @@
}
// 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 @@
}
// 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 @@
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 eaf7a02..e0cd6e0 100644
--- a/metadata/service/remote/service_proxy.go
+++ b/metadata/service/remote/service_proxy.go
@@ -45,27 +45,27 @@
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 @@
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 @@
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 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 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 c284bb2..1899d02 100644
--- a/metadata/service/remote/service_proxy_test.go
+++ b/metadata/service/remote/service_proxy_test.go
@@ -54,18 +54,28 @@
// 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 @@
return &mockMetadataReportFactory{}
})
u, _ := common.NewURL("mock://localhost")
- instance.GetMetadataReportInstance(&u)
+ instance.GetMetadataReportInstance(u)
}
type mockMetadataReportFactory struct {
@@ -110,7 +120,7 @@
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 734f098..d602815 100644
--- a/metadata/service/remote/service_test.go
+++ b/metadata/service/remote/service_test.go
@@ -39,7 +39,7 @@
)
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 @@
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 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 @@
_, 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 f6509d0..1d90f8a 100644
--- a/metadata/service/service.go
+++ b/metadata/service/service.go
@@ -34,15 +34,15 @@
// 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 @@
// 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 @@
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 bd1e798..266c820 100644
--- a/metrics/prometheus/reporter.go
+++ b/metrics/prometheus/reporter.go
@@ -24,6 +24,7 @@
"sync"
"time"
)
+
import (
"github.com/prometheus/client_golang/prometheus"
)
@@ -64,7 +65,6 @@
// should initialize after loading configuration
func init() {
-
extension.SetMetricReporter(reporterName, newPrometheusReporter)
}
@@ -72,12 +72,10 @@
// 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 @@
}
// 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 eaba0e3..1b9853f 100644
--- a/metrics/prometheus/reporter_test.go
+++ b/metrics/prometheus/reporter_test.go
@@ -26,6 +26,7 @@
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 9a7094f..24d75aa 100644
--- a/metrics/reporter.go
+++ b/metrics/reporter.go
@@ -21,6 +21,7 @@
"context"
"time"
)
+
import (
"github.com/apache/dubbo-go/protocol"
)
diff --git a/protocol/dubbo/dubbo_codec.go b/protocol/dubbo/dubbo_codec.go
index 5e859a7..21376c3 100644
--- a/protocol/dubbo/dubbo_codec.go
+++ b/protocol/dubbo/dubbo_codec.go
@@ -105,7 +105,7 @@
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) 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) 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 @@
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 dd80937..1873a63 100644
--- a/protocol/dubbo/dubbo_exporter.go
+++ b/protocol/dubbo/dubbo_exporter.go
@@ -42,10 +42,9 @@
// 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 bce3350..bc85d73 100644
--- a/protocol/dubbo/dubbo_invoker.go
+++ b/protocol/dubbo/dubbo_invoker.go
@@ -22,13 +22,11 @@
"strconv"
"strings"
"sync"
- "sync/atomic"
"time"
)
import (
"github.com/opentracing/opentracing-go"
- perrors "github.com/pkg/errors"
)
import (
@@ -42,43 +40,51 @@
)
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 @@
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
}
- atomic.AddInt64(&(di.reqNum), 1)
- defer atomic.AddInt64(&(di.reqNum), -1)
+
+ 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
+ }
inv := invocation.(*invocation_impl.RPCInvocation)
// init param
@@ -125,14 +146,13 @@
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 @@
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 4d32c29..fecb3b0 100644
--- a/protocol/dubbo/dubbo_invoker_test.go
+++ b/protocol/dubbo/dubbo_invoker_test.go
@@ -89,15 +89,15 @@
// 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 @@
}
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 8dda52b..d6a71dc 100644
--- a/protocol/dubbo/dubbo_protocol.go
+++ b/protocol/dubbo/dubbo_protocol.go
@@ -90,7 +90,7 @@
}
// 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) 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 @@
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 @@
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 74a00b6..96e6eea 100644
--- a/protocol/dubbo/hessian2/const.go
+++ b/protocol/dubbo/hessian2/const.go
@@ -29,11 +29,6 @@
)
const (
- mask = byte(127)
- flag = byte(128)
-)
-
-const (
// Zero : byte zero
Zero = byte(0x00)
)
@@ -80,14 +75,10 @@
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 1afa4ec..5ffebde 100644
--- a/protocol/dubbo/hessian2/hessian_dubbo.go
+++ b/protocol/dubbo/hessian2/hessian_dubbo.go
@@ -99,7 +99,7 @@
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 @@
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 c3f19f0..eaaf500 100644
--- a/protocol/dubbo/hessian2/hessian_dubbo_test.go
+++ b/protocol/dubbo/hessian2/hessian_dubbo_test.go
@@ -80,6 +80,7 @@
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 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 @@
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 4ebb4aa..94aa34d 100644
--- a/protocol/dubbo/hessian2/hessian_request.go
+++ b/protocol/dubbo/hessian2/hessian_request.go
@@ -30,6 +30,10 @@
perrors "github.com/pkg/errors"
)
+import (
+ "github.com/apache/dubbo-go/common/logger"
+)
+
/////////////////////////////////////////
// dubbo
/////////////////////////////////////////
@@ -39,7 +43,7 @@
return "V"
}
- switch v.(type) {
+ switch v := v.(type) {
// Serialized tags for base types
case nil:
return "V"
@@ -216,23 +220,31 @@
// 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 @@
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 982960e..b95e1c2 100644
--- a/protocol/dubbo/hessian2/hessian_response.go
+++ b/protocol/dubbo/hessian2/hessian_response.go
@@ -18,6 +18,7 @@
import (
"encoding/binary"
+ "github.com/apache/dubbo-go/common/logger"
"math"
"reflect"
"strconv"
@@ -93,7 +94,9 @@
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 @@
}
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 @@
// 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 c139f35..6c9816f 100644
--- a/protocol/dubbo/impl/codec.go
+++ b/protocol/dubbo/impl/codec.go
@@ -146,7 +146,7 @@
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 @@
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 70d8bae..c9b92ba 100644
--- a/protocol/dubbo/impl/const.go
+++ b/protocol/dubbo/impl/const.go
@@ -29,11 +29,6 @@
)
const (
- mask = byte(127)
- flag = byte(128)
-)
-
-const (
// Zero : byte zero
Zero = byte(0x00)
)
@@ -80,14 +75,10 @@
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 b686d57..e355276 100644
--- a/protocol/dubbo/impl/hessian.go
+++ b/protocol/dubbo/impl/hessian.go
@@ -37,8 +37,6 @@
"github.com/apache/dubbo-go/common/logger"
)
-type Object interface{}
-
type HessianSerializer struct {
}
@@ -65,7 +63,7 @@
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 @@
}
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 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 @@
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 @@
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 @@
"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 @@
return "V"
}
- switch v.(type) {
+ switch v := v.(type) {
// Serialized tags for base types
case nil:
return "V"
@@ -474,7 +471,7 @@
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 @@
}
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 2dcd6a4..f45e6fd 100644
--- a/protocol/dubbo/opentracing.go
+++ b/protocol/dubbo/opentracing.go
@@ -35,14 +35,6 @@
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 a0ab0be..24ab125 100644
--- a/protocol/grpc/client.go
+++ b/protocol/grpc/client.go
@@ -19,6 +19,7 @@
import (
"reflect"
+ "strconv"
)
import (
@@ -89,14 +90,18 @@
}
// 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 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 0dc7648..5beb4fe 100644
--- a/protocol/grpc/grpc_exporter.go
+++ b/protocol/grpc/grpc_exporter.go
@@ -42,10 +42,9 @@
// 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 737e8c4..7b33c67 100644
--- a/protocol/grpc/grpc_invoker.go
+++ b/protocol/grpc/grpc_invoker.go
@@ -24,46 +24,88 @@
)
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 @@
// 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 68594a4..3ad1245 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 @@
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 @@
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 @@
}
// 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 fbcfa6f..fe3e38d 100644
--- a/protocol/grpc/protoc-gen-dubbo/main.go
+++ b/protocol/grpc/protoc-gen-dubbo/main.go
@@ -43,7 +43,7 @@
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 1af4faf..ba84f36 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 @@
"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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 2b7b1ad..e77e2ba 100644
--- a/protocol/grpc/server.go
+++ b/protocol/grpc/server.go
@@ -40,6 +40,7 @@
// Server is a gRPC server
type Server struct {
grpcServer *grpc.Server
+ bufferSize int
}
// NewServer creates a new server
@@ -57,8 +58,12 @@
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 @@
// 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 452f619..2ecc817 100644
--- a/protocol/invocation.go
+++ b/protocol/invocation.go
@@ -25,6 +25,8 @@
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 4e80632..0f879c0 100644
--- a/protocol/invocation/rpcinvocation.go
+++ b/protocol/invocation/rpcinvocation.go
@@ -19,6 +19,7 @@
import (
"reflect"
+ "strings"
"sync"
)
@@ -35,8 +36,11 @@
// 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 @@
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) 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 @@
}
}
+// 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 0000000..ef70e2c
--- /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 3ca3704..e799991 100644
--- a/protocol/invoker.go
+++ b/protocol/invoker.go
@@ -22,11 +22,25 @@
)
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 @@
// 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 @@
// 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/cluster/router/chan.go b/protocol/invoker_test.go
similarity index 63%
copy from cluster/router/chan.go
copy to protocol/invoker_test.go
index e3e84b8..c2a587d 100644
--- a/cluster/router/chan.go
+++ b/protocol/invoker_test.go
@@ -15,18 +15,30 @@
* limitations under the License.
*/
-package router
+package protocol
import (
- "github.com/apache/dubbo-go/protocol"
+ "testing"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+import (
+ "github.com/stretchr/testify/assert"
+)
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+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 2a2ddfe..11051df 100644
--- a/protocol/jsonrpc/http.go
+++ b/protocol/jsonrpc/http.go
@@ -49,14 +49,14 @@
// 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 @@
}
// 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 @@
}
// 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 @@
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)
}
- setNetConnTimeout(tcpConn, c.options.HTTPTimeout)
+ if err = setNetConnTimeout(tcpConn, c.options.HTTPTimeout); err != nil {
+ return nil, err
+ }
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 4a9645e..5ef4064 100644
--- a/protocol/jsonrpc/http_test.go
+++ b/protocol/jsonrpc/http_test.go
@@ -44,12 +44,12 @@
}
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 @@
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 506c4c9..81ca512 100644
--- a/protocol/jsonrpc/json.go
+++ b/protocol/jsonrpc/json.go
@@ -48,13 +48,11 @@
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 6f75f6a..6b91d26 100644
--- a/protocol/jsonrpc/jsonrpc_exporter.go
+++ b/protocol/jsonrpc/jsonrpc_exporter.go
@@ -42,10 +42,9 @@
// 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 d84b980..357443f 100644
--- a/protocol/jsonrpc/jsonrpc_invoker.go
+++ b/protocol/jsonrpc/jsonrpc_invoker.go
@@ -36,7 +36,7 @@
}
// 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 @@
// 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 d7124ca..12a5705 100644
--- a/protocol/jsonrpc/jsonrpc_invoker_test.go
+++ b/protocol/jsonrpc/jsonrpc_invoker_test.go
@@ -36,13 +36,13 @@
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 1778d99..643bcde 100644
--- a/protocol/jsonrpc/jsonrpc_protocol.go
+++ b/protocol/jsonrpc/jsonrpc_protocol.go
@@ -75,7 +75,7 @@
}
// 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) 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 9755a48..76901bf 100644
--- a/protocol/jsonrpc/server.go
+++ b/protocol/jsonrpc/server.go
@@ -92,7 +92,9 @@
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 @@
}
// 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 @@
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 0c88b47..8a0973b 100644
--- a/protocol/mock/mock_invoker.go
+++ b/protocol/mock/mock_invoker.go
@@ -59,9 +59,9 @@
}
// 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 6bed5ec..d03e70f 100644
--- a/protocol/protocol.go
+++ b/protocol/protocol.go
@@ -32,7 +32,7 @@
// 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 @@
}
// 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 2e9ffed..d1baba8 100644
--- a/protocol/protocolwrapper/mock_protocol_filter.go
+++ b/protocol/protocolwrapper/mock_protocol_filter.go
@@ -39,11 +39,10 @@
}
// 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 4b2702b..448bd6f 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper.go
@@ -55,11 +55,15 @@
}
// 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 @@
// 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 @@
}
// 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 b03ea7b..b37d066 100644
--- a/protocol/protocolwrapper/protocol_filter_wrapper_test.go
+++ b/protocol/protocolwrapper/protocol_filter_wrapper_test.go
@@ -42,8 +42,8 @@
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 @@
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 873af99..2338790 100644
--- a/protocol/rest/config/reader/rest_config_reader.go
+++ b/protocol/rest/config/reader/rest_config_reader.go
@@ -62,7 +62,7 @@
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 @@
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 4732dd8..27c67db 100644
--- a/protocol/rest/config/rest_config.go
+++ b/protocol/rest/config/rest_config.go
@@ -123,13 +123,13 @@
}
// 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 359506a..7a49a20 100644
--- a/protocol/rest/rest_exporter.go
+++ b/protocol/rest/rest_exporter.go
@@ -42,10 +42,9 @@
// 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 691beed..898890e 100644
--- a/protocol/rest/rest_invoker.go
+++ b/protocol/rest/rest_invoker.go
@@ -43,7 +43,7 @@
}
// 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 9df97a2..b6bc980 100644
--- a/protocol/rest/rest_invoker_test.go
+++ b/protocol/rest/rest_invoker_test.go
@@ -44,7 +44,7 @@
"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 @@
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 @@
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 0cd26c2..d19bd00 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 @@
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 @@
}
// 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 @@
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 @@
}
// 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) 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 9ff4e7d..580fc61 100644
--- a/protocol/rest/rest_protocol_test.go
+++ b/protocol/rest/rest_protocol_test.go
@@ -72,7 +72,7 @@
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 @@
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 d9542bb..b0bf0e0 100644
--- a/protocol/rest/server/rest_server.go
+++ b/protocol/rest/server/rest_server.go
@@ -23,7 +23,6 @@
"net/http"
"reflect"
"strconv"
- "strings"
)
import (
@@ -43,7 +42,7 @@
// 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 @@
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 @@
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 @@
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 6fb9ee8..4481f44 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 @@
// 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 @@
// 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 978534e..8fc5ddd 100644
--- a/protocol/rpc_status.go
+++ b/protocol/rpc_status.go
@@ -24,14 +24,27 @@
)
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 @@
}
// 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 @@
}
// 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 @@
}
// 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 @@
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 cc12753..6fd449c 100644
--- a/protocol/rpc_status_test.go
+++ b/protocol/rpc_status_test.go
@@ -79,7 +79,7 @@
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 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 3b36510..31e859e 100644
--- a/registry/base_configuration_listener.go
+++ b/registry/base_configuration_listener.go
@@ -20,6 +20,7 @@
import (
perrors "github.com/pkg/errors"
)
+
import (
"github.com/apache/dubbo-go/common"
"github.com/apache/dubbo-go/common/config"
@@ -42,7 +43,9 @@
}
// 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 @@
}
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 797ffb2..df8c8a3 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 (
- gxnet "github.com/dubbogo/gost/net"
perrors "github.com/pkg/errors"
)
@@ -53,7 +51,7 @@
func init() {
processID = fmt.Sprintf("%d", os.Getpid())
- localIP, _ = gxnet.GetLocalIP()
+ localIP = common.GetLocalIp()
}
type createPathFunc func(dubboPath string) error
@@ -94,14 +92,14 @@
// 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 @@
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 @@
}
// 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 @@
}
// 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 @@
}
// 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) 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 @@
}
// 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 @@
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 @@
}
// 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 @@
// 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 @@
}
// 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 @@
}
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 b159834..0d665af 100644
--- a/registry/consul/listener.go
+++ b/registry/consul/listener.go
@@ -39,17 +39,17 @@
// 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 @@
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 @@
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) handler(idx uint64, raw interface{}) {
var (
service *consul.ServiceEntry
- url common.URL
+ url *common.URL
ok bool
err error
)
@@ -153,7 +153,7 @@
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 b92e335..4d3718e 100644
--- a/registry/consul/registry.go
+++ b/registry/consul/registry.go
@@ -23,6 +23,7 @@
)
import (
+ getty "github.com/apache/dubbo-getty"
consul "github.com/hashicorp/consul/api"
perrors "github.com/pkg/errors"
)
@@ -56,6 +57,10 @@
// 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 @@
// 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 @@
}
// 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 @@
// 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 @@
}
// 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 @@
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) 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 @@
// 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 {
+ done := make(chan struct{}, 1)
+ go func() {
+ defer func() {
+ if e := recover(); e != nil {
+ logger.Errorf("consulRegistry destroy with panic: %v", e)
+ }
+ done <- struct{}{}
+ }()
+ 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 <-time.After(registryDestroyDefaultTimeout):
- logger.Errorf("consul unregister timeout")
}
+ }()
+ 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 d8ab93f..fba142e 100644
--- a/registry/consul/service_discovery.go
+++ b/registry/consul/service_discovery.go
@@ -27,7 +27,7 @@
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 @@
return res
}
- for service, _ := range services {
+ for service := range services {
res.Add(service)
}
return res
@@ -339,7 +339,7 @@
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 @@
}
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 @@
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 @@
}, 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 ed7220f..3f97d84 100644
--- a/registry/consul/service_discovery_test.go
+++ b/registry/consul/service_discovery_test.go
@@ -44,7 +44,6 @@
consulCheckPassInterval = 17000
consulDeregisterCriticalServiceAfter = "20s"
consulWatchTimeout = 60000
- registryURL = common.URL{}
)
func TestConsulServiceDiscovery_newConsulServiceDiscovery(t *testing.T) {
@@ -85,7 +84,10 @@
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 @@
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 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 @@
}
// 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 05ac5e7..76f5d2d 100644
--- a/registry/consul/utils.go
+++ b/registry/consul/utils.go
@@ -25,7 +25,6 @@
)
import (
- gxnet "github.com/dubbogo/gost/net"
consul "github.com/hashicorp/consul/api"
perrors "github.com/pkg/errors"
)
@@ -34,12 +33,12 @@
"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 @@
// address
if url.Ip == "" {
- url.Ip, _ = gxnet.GetLocalIP()
+ url.Ip = common.GetLocalIp()
}
// port
@@ -94,21 +93,21 @@
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 0e5bffe..b7e2929 100644
--- a/registry/consul/utils_test.go
+++ b/registry/consul/utils_test.go
@@ -19,6 +19,8 @@
import (
"fmt"
+ "github.com/apache/dubbo-go/common/logger"
+ "github.com/stretchr/testify/assert"
"net"
"net/url"
"strconv"
@@ -63,8 +65,8 @@
)
}
-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 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 @@
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 @@
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 @@
// 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 @@
// 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 8871a2a..8c15240 100644
--- a/registry/directory/directory.go
+++ b/registry/directory/directory.go
@@ -19,17 +19,19 @@
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 @@
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 @@
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 @@
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 @@
// 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)
}
-// 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
+// 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)
+}
- // in batch mode, it is safe to remove since we have the complete list of events.
- if len(events) > 1 {
+// 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()
+ }
+}
+
+// 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
+
+ // 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 @@
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 @@
// 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 @@
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) 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 @@
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 @@
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 @@
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 (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) 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 f2b2f8e..b5d81eb 100644
--- a/registry/directory/directory_test.go
+++ b/registry/directory/directory_test.go
@@ -72,7 +72,7 @@
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 @@
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 @@
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 @@
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_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 @@
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 @@
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 436b6ec..f900495 100644
--- a/registry/etcdv3/listener.go
+++ b/registry/etcdv3/listener.go
@@ -19,6 +19,7 @@
import (
"strings"
+ "sync"
)
import (
@@ -64,7 +65,7 @@
}
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 @@
}
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 @@
}
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 1cf06d1..ff7f63f 100644
--- a/registry/etcdv3/listener_test.go
+++ b/registry/etcdv3/listener_test.go
@@ -80,7 +80,7 @@
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 f3cc379..7ccf326 100644
--- a/registry/etcdv3/registry.go
+++ b/registry/etcdv3/registry.go
@@ -51,7 +51,7 @@
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 @@
// 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 @@
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 164fe9c..d94eff6 100644
--- a/registry/etcdv3/registry_test.go
+++ b/registry/etcdv3/registry_test.go
@@ -39,7 +39,7 @@
t.Fatal(err)
}
- reg, err := newETCDV3Registry(®url)
+ reg, err := newETCDV3Registry(regurl)
if err != nil {
t.Fatal(err)
}
@@ -86,7 +86,7 @@
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 @@
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 e8d4aea..ca6016e 100644
--- a/registry/etcdv3/service_discovery.go
+++ b/registry/etcdv3/service_discovery.go
@@ -26,7 +26,7 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 d8e3f1a..5609cf7 100644
--- a/registry/etcdv3/service_discovery_test.go
+++ b/registry/etcdv3/service_discovery_test.go
@@ -75,6 +75,6 @@
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 39fb00c..2ebc375 100644
--- a/registry/event.go
+++ b/registry/event.go
@@ -40,12 +40,37 @@
// 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 54752c0..504f7b5 100644
--- a/registry/event/event_publishing_service_deiscovery_test.go
+++ b/registry/event/event_publishing_service_deiscovery_test.go
@@ -24,7 +24,7 @@
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 3ee2f4a..773eee6 100644
--- a/registry/event/event_publishing_service_discovery.go
+++ b/registry/event/event_publishing_service_discovery.go
@@ -19,7 +19,7 @@
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 98ae2df..c041232 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 @@
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 @@
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 @@
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 59c5cf9..768a1c2 100644
--- a/registry/file/service_discovery.go
+++ b/registry/file/service_discovery.go
@@ -28,7 +28,7 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 0bffcae..0062eae 100644
--- a/registry/file/service_discovery_test.go
+++ b/registry/file/service_discovery_test.go
@@ -44,7 +44,10 @@
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 @@
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 24c8d81..e20b7c7 100644
--- a/registry/kubernetes/listener.go
+++ b/registry/kubernetes/listener.go
@@ -65,7 +65,7 @@
}
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 @@
}
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 ccaaf80..ef6e8fb 100644
--- a/registry/kubernetes/listener_test.go
+++ b/registry/kubernetes/listener_test.go
@@ -31,142 +31,10 @@
"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 @@
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 8889585..a26478f 100644
--- a/registry/kubernetes/registry.go
+++ b/registry/kubernetes/registry.go
@@ -19,15 +19,13 @@
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 @@
"github.com/apache/dubbo-go/remoting/kubernetes"
)
-var (
- processID = ""
- localIP = ""
-)
-
const (
Name = "kubernetes"
ConnDelay = 3
@@ -53,8 +46,8 @@
)
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 @@
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 347dadc..a816b03 100644
--- a/registry/kubernetes/registry_test.go
+++ b/registry/kubernetes/registry_test.go
@@ -231,7 +231,7 @@
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 @@
t.Fatal(err)
}
- listener, err := r.DoSubscribe(&url)
+ listener, err := r.DoSubscribe(url)
if err != nil {
t.Fatal(err)
}
@@ -280,7 +280,7 @@
defer wg.Done()
registerErr := r.Register(url)
if registerErr != nil {
- t.Fatal(registerErr)
+ t.Error(registerErr)
}
}()
@@ -301,7 +301,7 @@
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 @@
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 10561d0..6287bb0 100644
--- a/registry/mock_registry.go
+++ b/registry/mock_registry.go
@@ -18,6 +18,7 @@
package registry
import (
+ "fmt"
"time"
)
@@ -32,14 +33,16 @@
// 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 @@
}
// 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 @@
}
// 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(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)
+ t, listener := r.checkLoopSubscribe(url)
+ if t == 0 {
+ continue
+ } else if t == -1 {
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)
- continue
- }
-
for {
serviceEvent, err := listener.Next()
if err != nil {
@@ -109,6 +102,26 @@
}
}
}()
+ 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 @@
}
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 (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 cf6a73d..d2b5746 100644
--- a/registry/nacos/listener.go
+++ b/registry/nacos/listener.go
@@ -43,7 +43,7 @@
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 @@
}
// 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 @@
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 @@
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 @@
}
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 @@
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 4110908..e9a4bd3 100644
--- a/registry/nacos/registry.go
+++ b/registry/nacos/registry.go
@@ -26,7 +26,6 @@
)
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 @@
)
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 @@
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 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 @@
}
// 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 @@
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 (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 @@
}
// 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 @@
}
}
- return nil
}
// UnSubscribe :
@@ -212,8 +215,8 @@
}
// 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 @@
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 @@
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 @@
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 d0311b2..b828205 100644
--- a/registry/nacos/registry_test.go
+++ b/registry/nacos/registry_test.go
@@ -19,9 +19,11 @@
import (
"encoding/json"
+ "net/http"
"net/url"
"strconv"
"testing"
+ "time"
)
import (
@@ -35,7 +37,14 @@
)
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 @@
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_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 @@
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 @@
}
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_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 @@
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 @@
}
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 @@
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 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 @@
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 @@
_, 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 0e5ad8e..4533e7b 100644
--- a/registry/nacos/service_discovery.go
+++ b/registry/nacos/service_discovery.go
@@ -24,7 +24,7 @@
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 @@
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 @@
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 @@
}
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 119be0b..aa044ad 100644
--- a/registry/nacos/service_discovery_test.go
+++ b/registry/nacos/service_discovery_test.go
@@ -81,6 +81,9 @@
}
func TestNacosServiceDiscovery_CRUD(t *testing.T) {
+ if !checkNacosServerAlive() {
+ return
+ }
prepareData()
extension.SetEventDispatcher("mock", func() observer.EventDispatcher {
return &dispatcher.MockEventDispatcher{}
@@ -148,7 +151,8 @@
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_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 69a31ef..4fcdf93 100644
--- a/registry/protocol/protocol.go
+++ b/registry/protocol/protocol.go
@@ -130,31 +130,30 @@
}
// 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 @@
}
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 @@
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 @@
}
// 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 @@
}
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 @@
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 @@
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 2d6e024..0fca552 100644
--- a/registry/protocol/protocol_test.go
+++ b/registry/protocol/protocol_test.go
@@ -59,7 +59,7 @@
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 @@
common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
)
- url2.SubURL = &suburl2
+ url2.SubURL = suburl2
regProtocol.Refer(url2)
var count int
@@ -105,7 +105,7 @@
common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
)
- url2.SubURL = &suburl2
+ url2.SubURL = suburl2
regProtocol.Refer(url2)
var count int
@@ -128,13 +128,13 @@
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 @@
common.WithParamsValue(constant.CLUSTER_KEY, "mock"),
)
- url2.SubURL = &suburl2
+ url2.SubURL = suburl2
invoker2 := protocol.NewBaseInvoker(url2)
regProtocol.Export(invoker2)
@@ -184,7 +184,7 @@
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 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 @@
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 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 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 2225d2c..4391783 100644
--- a/registry/registry.go
+++ b/registry/registry.go
@@ -34,7 +34,7 @@
// 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 @@
// 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 @@
// 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 cb7a3c0..5ab7683 100644
--- a/registry/service_discovery.go
+++ b/registry/service_discovery.go
@@ -23,7 +23,7 @@
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 dbb4582..43a1640 100644
--- a/registry/service_instance.go
+++ b/registry/service_instance.go
@@ -91,7 +91,7 @@
// 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 3f8f30d..7e4e0ee 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 @@
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 cddeb42..c53c058 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 @@
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 82fb345..5690ab6 100644
--- a/registry/servicediscovery/instance/service_instance_selector.go
+++ b/registry/servicediscovery/instance/service_instance_selector.go
@@ -24,5 +24,5 @@
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 4db2c5a..c97a7f7 100644
--- a/registry/servicediscovery/service_discovery_registry.go
+++ b/registry/servicediscovery/service_discovery_registry.go
@@ -26,7 +26,6 @@
)
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 @@
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 @@
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) 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 @@
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) Register(url common.URL) error {
+func (s *serviceDiscoveryRegistry) Register(url *common.URL) error {
if !shouldRegister(url) {
return nil
}
@@ -175,17 +174,13 @@
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 (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 @@
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 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 @@
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 @@
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) 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
cloneUrl := u.CloneExceptParams(removeParamSet)
- clonedExportedURLs = append(clonedExportedURLs, *cloneUrl)
+ clonedExportedURLs = append(clonedExportedURLs, cloneUrl)
}
}
return clonedExportedURLs
@@ -548,8 +526,8 @@
}
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 @@
}
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 53eb865..391d92c 100644
--- a/registry/servicediscovery/service_discovery_registry_test.go
+++ b/registry/servicediscovery/service_discovery_registry_test.go
@@ -23,7 +23,7 @@
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 @@
"&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 @@
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 @@
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 @@
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 @@
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 @@
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 086a26d..c6b3aea 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 @@
}
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 @@
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 b52cc23..1bb38c9 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 @@
},
}
- 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 @@
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 415ca35..557c86e 100644
--- a/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
+++ b/registry/servicediscovery/synthesizer/subscribed_urls_synthesizer.go
@@ -27,5 +27,5 @@
// 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 ec82fa0..5a7d14b 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 @@
"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 @@
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) Close() {
l.mutex.Lock()
defer l.mutex.Unlock()
+ l.closed = true
for _, listener := range l.subscribed {
listener.(*RegistryConfigurationListener).Close()
}
@@ -111,7 +113,7 @@
// 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 @@
}
// 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) 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 @@
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 a0e9147..20ec1cf 100644
--- a/registry/zookeeper/listener_test.go
+++ b/registry/zookeeper/listener_test.go
@@ -34,7 +34,7 @@
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 e8ee51b..8b21aae 100644
--- a/registry/zookeeper/registry.go
+++ b/registry/zookeeper/registry.go
@@ -27,6 +27,7 @@
import (
"github.com/dubbogo/go-zookeeper/zk"
+ gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
perrors "github.com/pkg/errors"
)
@@ -54,7 +55,7 @@
type zkRegistry struct {
registry.BaseRegistry
- client *zookeeper.ZookeeperClient
+ client *gxzookeeper.ZookeeperClient
listenerLock sync.Mutex
listener *zookeeper.ZkEventListener
dataListener *RegistryDataListener
@@ -73,7 +74,7 @@
}
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 @@
// 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 @@
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 @@
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 @@
}
// 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 @@
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 @@
// 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))
- }
- }
- return perrors.WithMessagef(err, "RegisterTempNode(root{%s}, node{%s})", root, node)
+ if err == nil {
+ return nil
}
- logger.Debugf("Create a zookeeper node:%s", zkPath)
- 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
+ }
+ }
+
+ 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 @@
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 @@
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 d915fc2..9e52dd7 100644
--- a/registry/zookeeper/registry_test.go
+++ b/registry/zookeeper/registry_test.go
@@ -24,22 +24,25 @@
)
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_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 @@
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 @@
}
//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 @@
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 @@
}
//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 @@
}
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 5ad83ef..06fe0f7 100644
--- a/registry/zookeeper/service_discovery.go
+++ b/registry/zookeeper/service_discovery.go
@@ -26,8 +26,9 @@
)
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 @@
}
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 @@
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 @@
}
// nolint
-func (zksd *zookeeperServiceDiscovery) GetUrl() common.URL {
- return *zksd.url
+func (zksd *zookeeperServiceDiscovery) GetUrl() *common.URL {
+ return zksd.url
}
// nolint
@@ -214,7 +216,7 @@
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 @@
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 @@
}
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 ea3c7dd..a73ecc9 100644
--- a/registry/zookeeper/service_discovery_test.go
+++ b/registry/zookeeper/service_discovery_test.go
@@ -37,11 +37,14 @@
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 @@
Address: address,
TimeoutStr: "10s",
}
- return ts
+ return tc
}
func TestNewZookeeperServiceDiscovery(t *testing.T) {
@@ -74,11 +77,12 @@
}
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 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",
@@ -155,8 +162,6 @@
Metadata: nil,
})
assert.Nil(t, err)
-
- assert.Nil(t, err)
wg := &sync.WaitGroup{}
wg.Add(1)
tn := &testNotify{
@@ -170,7 +175,7 @@
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 @@
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 f6ba336..2c5c13f 100644
--- a/remoting/consul/test_agent.go
+++ b/remoting/consul/test_agent.go
@@ -30,7 +30,7 @@
// 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 ebd4542..34ee31b 100644
--- a/remoting/etcdv3/client.go
+++ b/remoting/etcdv3/client.go
@@ -416,7 +416,9 @@
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 3de266f..787c24d 100644
--- a/remoting/etcdv3/client_test.go
+++ b/remoting/etcdv3/client_test.go
@@ -108,7 +108,6 @@
}
suite.etcd = e
- return
}
// stop etcd server
@@ -133,9 +132,9 @@
// 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 @@
wc, err := c.watch(prefix)
if err != nil {
- t.Fatal(err)
+ t.Error(err)
}
events := make([]mvccpb.Event, 0)
@@ -358,7 +357,7 @@
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 4f80a89..fd6f958 100644
--- a/remoting/etcdv3/listener.go
+++ b/remoting/etcdv3/listener.go
@@ -36,7 +36,7 @@
// nolint
type EventListener struct {
client *Client
- keyMapLock sync.Mutex
+ keyMapLock sync.RWMutex
keyMap map[string]struct{}
wg sync.WaitGroup
}
@@ -129,8 +129,6 @@
default:
return false
}
-
- panic("unreachable")
}
// ListenServiceNodeEventWithPrefix listens on a set of key with spec prefix
@@ -183,9 +181,9 @@
// --------> 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 848d9cb..ad136a7 100644
--- a/remoting/exchange.go
+++ b/remoting/exchange.go
@@ -17,6 +17,7 @@
package remoting
import (
+ "sync"
"time"
)
@@ -26,13 +27,19 @@
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 @@
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 @@
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 @@
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 75ae945..f84cef1 100644
--- a/remoting/exchange_client.go
+++ b/remoting/exchange_client.go
@@ -18,35 +18,31 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
}
// 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 @@
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 a31e994..a8d7c73 100644
--- a/remoting/exchange_server.go
+++ b/remoting/exchange_server.go
@@ -32,11 +32,11 @@
// 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 dcf59d0..b6aa082 100644
--- a/remoting/getty/config.go
+++ b/remoting/getty/config.go
@@ -54,6 +54,14 @@
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 @@
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 @@
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 *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 b91fc9f..9afc18a 100644
--- a/remoting/getty/dubbo_codec_for_test.go
+++ b/remoting/getty/dubbo_codec_for_test.go
@@ -55,15 +55,15 @@
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 @@
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 @@
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 @@
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) 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) 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 @@
}
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 6af3971..774f999 100644
--- a/remoting/getty/getty_client.go
+++ b/remoting/getty/getty_client.go
@@ -19,6 +19,7 @@
import (
"math/rand"
+ "sync"
"time"
)
@@ -38,14 +39,12 @@
)
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 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 @@
// 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 (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 @@
// 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 @@
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 @@
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 @@
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 41ca310..c32e0c2 100644
--- a/remoting/getty/getty_client_test.go
+++ b/remoting/getty/getty_client_test.go
@@ -51,7 +51,7 @@
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 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 @@
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 @@
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 @@
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 @@
}})
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 @@
}
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 7c8fa29..4930a6a 100644
--- a/remoting/getty/getty_server.go
+++ b/remoting/getty/getty_server.go
@@ -41,8 +41,7 @@
)
var (
- srvConf *ServerConfig
- srvGrpool *gxsync.TaskPool
+ srvConf *ServerConfig
)
func initServer(protocol string) {
@@ -76,7 +75,6 @@
if err := srvConf.CheckValidity(); err != nil {
panic(err)
}
- SetServerGrpool()
}
// SetServerConfig set dubbo server config.
@@ -87,7 +85,6 @@
logger.Warnf("[ServerConfig CheckValidity] error: %v", err)
return
}
- SetServerGrpool()
}
// GetServerConfig get getty server config.
@@ -95,17 +92,6 @@
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 @@
}
// 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 @@
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 @@
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 @@
)
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 196aa20..5443d37 100644
--- a/remoting/getty/listener.go
+++ b/remoting/getty/listener.go
@@ -44,7 +44,8 @@
)
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 @@
// nolint
type RpcClientHandler struct {
- conn *gettyRPCClient
+ conn *gettyRPCClient
+ timeoutTimes int
}
// nolint
@@ -99,7 +101,7 @@
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 @@
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 @@
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 @@
}
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 @@
sessionMap map[getty.Session]*rpcSession
rwlock sync.RWMutex
server *Server
+ timeoutTimes int
}
// nolint
@@ -223,13 +237,22 @@
}
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 @@
// 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 @@
if !req.TwoWay {
return
}
- reply(session, resp, hessian.PackageResponse)
+ reply(session, resp)
}
}()
@@ -272,7 +295,6 @@
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 @@
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 @@
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 7e7ac5f..2700ed8 100644
--- a/remoting/getty/listener_test.go
+++ b/remoting/getty/listener_test.go
@@ -23,14 +23,14 @@
)
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 @@
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 @@
// 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 7db733c..4ba4fde 100644
--- a/remoting/getty/opentracing.go
+++ b/remoting/getty/opentracing.go
@@ -35,14 +35,6 @@
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 a072432..2b1cdfe 100644
--- a/remoting/getty/pool.go
+++ b/remoting/getty/pool.go
@@ -60,40 +60,46 @@
sslEnabled bool
)
sslEnabled = pool.sslEnabled
- 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 := []getty.ClientOption{
+ getty.WithServerAddress(addr),
+ getty.WithConnectionNumber((int)(pool.rpcClient.conf.ConnectionNum)),
+ getty.WithReconnectInterval(pool.rpcClient.conf.ReconnectInterval),
}
+ if sslEnabled {
+ 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 @@
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 conf.GettySessionParam.TcpKeepAlive {
- tcpConn.SetKeepAlivePeriod(conf.GettySessionParam.keepAlivePeriod)
+ if err := tcpConn.SetNoDelay(conf.GettySessionParam.TcpNoDelay); err != nil {
+ logger.Error("tcpConn.SetNoDelay() = error:%v", err)
}
- tcpConn.SetReadBuffer(conf.GettySessionParam.TcpRBufSize)
- tcpConn.SetWriteBuffer(conf.GettySessionParam.TcpWBufSize)
+ if err := tcpConn.SetKeepAlive(conf.GettySessionParam.TcpKeepAlive); err != nil {
+ logger.Error("tcpConn.SetKeepAlive() = error:%v", err)
+ }
+ if conf.GettySessionParam.TcpKeepAlive {
+ 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)
+ }
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) 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 @@
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) 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 66c33a6..61062df 100644
--- a/remoting/getty/readwriter.go
+++ b/remoting/getty/readwriter.go
@@ -18,6 +18,7 @@
package getty
import (
+ "errors"
"reflect"
)
@@ -52,8 +53,7 @@
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 @@
// 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 @@
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 @@
// 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 0000000..c54c385
--- /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 0a05489..ce6bccc 100644
--- a/remoting/kubernetes/client.go
+++ b/remoting/kubernetes/client.go
@@ -47,17 +47,17 @@
}
// 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 @@
// 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 d6c5a2e..9cc4212 100644
--- a/remoting/kubernetes/client_test.go
+++ b/remoting/kubernetes/client_test.go
@@ -317,7 +317,8 @@
wc, done, err := client.WatchWithPrefix(prefix)
if err != nil {
- t.Fatal(err)
+ t.Error(err)
+ return
}
wg.Done()
@@ -365,7 +366,7 @@
// 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 @@
wc, done, err := client.WatchWithPrefix(prefix)
if err != nil {
- t.Fatal(err)
+ t.Error(err)
}
wg.Done()
@@ -435,7 +436,7 @@
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 65c5d71..a6c6c02 100644
--- a/remoting/kubernetes/facade_test.go
+++ b/remoting/kubernetes/facade_test.go
@@ -19,7 +19,6 @@
import (
"strconv"
- "sync"
"testing"
)
@@ -30,9 +29,9 @@
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 @@
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 @@
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 20be0d7..f66163d 100644
--- a/remoting/kubernetes/registry_controller.go
+++ b/remoting/kubernetes/registry_controller.go
@@ -442,7 +442,7 @@
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 07eeb09..7bb5ef1 100644
--- a/remoting/kubernetes/watch.go
+++ b/remoting/kubernetes/watch.go
@@ -116,21 +116,17 @@
// closeWatchers
// when the watcher-set was closed
func (s *watcherSetImpl) closeWatchers() {
+ <-s.ctx.Done()
+ // parent ctx be canceled, close the watch-set's watchers
+ s.lock.Lock()
+ watchers := s.watchers
+ s.lock.Unlock()
- 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()
- }
+ 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 8889103..efefcc5 100644
--- a/remoting/kubernetes/watch_test.go
+++ b/remoting/kubernetes/watch_test.go
@@ -42,7 +42,8 @@
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 @@
defer wg.Done()
w, err := s.Watch("key", true)
if err != nil {
- t.Fatal(err)
+ t.Error(err)
+ return
}
for {
@@ -86,7 +88,8 @@
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 61d13ef..48199be 100644
--- a/remoting/nacos/builder_test.go
+++ b/remoting/nacos/builder_test.go
@@ -34,11 +34,13 @@
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 fbd9076..5d39666 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 @@
// ConnDelay connection delay interval
ConnDelay = 3
// MaxFailTimes max fail times
- MaxFailTimes = 15
+ MaxFailTimes = 3
)
var (
@@ -47,79 +45,8 @@
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 @@
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,and 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 3474170..0000000
--- 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 acd43c0..ebe784c 100644
--- a/remoting/zookeeper/curator_discovery/service_discovery.go
+++ b/remoting/zookeeper/curator_discovery/service_discovery.go
@@ -26,6 +26,7 @@
import (
"github.com/dubbogo/go-zookeeper/zk"
+ gxzookeeper "github.com/dubbogo/gost/database/kv/zk"
perrors "github.com/pkg/errors"
)
@@ -46,7 +47,7 @@
// 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 @@
}
// 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 2a03439..aeaa317 100644
--- a/remoting/zookeeper/facade.go
+++ b/remoting/zookeeper/facade.go
@@ -19,73 +19,37 @@
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 1cd8f06..6463e57 100644
--- a/remoting/zookeeper/facade_test.go
+++ b/remoting/zookeeper/facade_test.go
@@ -22,23 +22,45 @@
"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 @@
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 @@
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 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 b6c6d78..c24a4fc 100644
--- a/remoting/zookeeper/listener.go
+++ b/remoting/zookeeper/listener.go
@@ -25,8 +25,9 @@
)
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 @@
// 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 @@
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 @@
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 @@
logger.Warnf("zk.ExistW(key{%s}) = event{EventNodeDeleted}", zkPath)
return true
}
- case <-l.client.Done():
+ case <-l.exit:
return false
}
}
@@ -135,9 +141,10 @@
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 @@
} 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 37ef1b4..1278665 100644
--- a/remoting/zookeeper/listener_test.go
+++ b/remoting/zookeeper/listener_test.go
@@ -23,10 +23,13 @@
"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 @@
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 @@
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 @@
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 @@
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 d48df36..e187558 100644
--- a/test/integrate/dubbo/go-client/Dockerfile
+++ b/test/integrate/dubbo/go-client/Dockerfile
@@ -31,6 +31,8 @@
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 162f32b..4708eb1 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 fc37839..0000000
--- 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 c2f2d63..f72648d 100644
--- a/test/integrate/dubbo/go-server/Dockerfile
+++ b/test/integrate/dubbo/go-server/Dockerfile
@@ -30,6 +30,8 @@
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 f9d950e0..9e11623 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 fc37839..0000000
--- 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 0000000..f38566b
--- /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 0000000..6bd1a7e
--- /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 0000000..dfae26e
--- /dev/null
+++ b/tools/cli/README_CN.md
@@ -0,0 +1,11 @@
+# dubbo-go-cli
+
+### 1. 解决问题
+
+针对正在运行的dubbo服务,需要拥有一个命令行工具来针对服务进行测试。
+该服务需要支持dubbo协议,方便用户进行自定义传输包体。
+
+### 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 0000000..f4a6e71
--- /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 0000000..fd15939
--- /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/cluster/router/chan.go b/tools/cli/common/protocol.go
similarity index 60%
copy from cluster/router/chan.go
copy to tools/cli/common/protocol.go
index e3e84b8..dd2454a 100644
--- a/cluster/router/chan.go
+++ b/tools/cli/common/protocol.go
@@ -15,18 +15,25 @@
* limitations under the License.
*/
-package router
+package common
import (
- "github.com/apache/dubbo-go/protocol"
+ "github.com/apache/dubbo-go/tools/cli/protocol"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+var (
+ protocols = make(map[string]func() protocol.Protocol, 8)
+)
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+// 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/cluster/router/chan.go b/tools/cli/common/tool.go
similarity index 65%
copy from cluster/router/chan.go
copy to tools/cli/common/tool.go
index e3e84b8..122948c 100644
--- a/cluster/router/chan.go
+++ b/tools/cli/common/tool.go
@@ -15,18 +15,25 @@
* limitations under the License.
*/
-package router
+package common
import (
- "github.com/apache/dubbo-go/protocol"
+ "fmt"
+ "log"
+ "reflect"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
-
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+// 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 0000000..bfc37e8
--- /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:user.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 0000000..fd2135b
--- /dev/null
+++ b/tools/cli/example/README_CN.md
@@ -0,0 +1,132 @@
+# dubbo-go-cli 使用示例
+
+### 1. 开启服务端
+见 server/main.go server/user.go
+
+示例:user.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,传入一个CallUserStruct的参数,返回一个User参数\
+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 // 嵌套上述子结构SubInfo
+}
+
+func (u *User) JavaClassName() string {
+ return "com.ikurento.user.User"
+}
+```
+
+开启服务:
+
+`cd server `\
+`source builddev.sh`\
+`go run .`
+
+### 2. 定义请求体(打解包协议)
+
+请求体定义为json文件,约定键值均为string\
+键对应go语言struct字段名例如"ID"、"Name" ,值对应"type@val"\
+其中type支持string int bool time,val使用string 来初始化,如果只填写type则初始化为零值。
+约定每个struct必须有JavaClassName字段,务必与server端严格对应
+
+见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将参数CallUserStruct的结构及子结构SubInfo都定义了出来,并且给请求参数赋值。
+
+user.json 同理,作为返回值不需要赋初始值,但JavaClassName字段一定与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 0000000..45a06ea
--- /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 0000000..59fa427
--- /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 0000000..425eb1c
--- /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"
+ # 相当于dubbo.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 0000000..53763f2
--- /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 0000000..1f3cf1d
--- /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 0000000..ed1ed52
--- /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 0000000..5835848
--- /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 0000000..9a038a9
--- /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 0000000..1e63219
--- /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 0000000..6ef7035
--- /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 0000000..559c7b0
--- /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 0000000..baaee26
--- /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 0000000..e1ad787
--- /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 0000000..1f90f67
--- /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 0000000..f73bf8e
--- /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 0000000..97a3ac7
--- /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/cluster/router/chan.go b/tools/cli/protocol/protocol.go
similarity index 70%
copy from cluster/router/chan.go
copy to tools/cli/protocol/protocol.go
index e3e84b8..136524b 100644
--- a/cluster/router/chan.go
+++ b/tools/cli/protocol/protocol.go
@@ -15,18 +15,23 @@
* limitations under the License.
*/
-package router
+package protocol
import (
- "github.com/apache/dubbo-go/protocol"
+ "sync"
)
-// Chain
-type Chain interface {
- router
- // AddRouters Add routers
- AddRouters([]PriorityRouter)
+type Protocol interface {
+ Read([]byte, *sync.Map) (interface{}, int, error)
+ Write(*Request) ([]byte, error)
+}
- // SetInvokers notify router chain of the initial addresses from registry at the first time. Notify whenever addresses in registry change.
- SetInvokers(invokers []protocol.Invoker)
+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 0000000..fa11833
--- /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 0000000..1e63219
--- /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 0000000..6ef7035
--- /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