Skip to content
Snippets Groups Projects
Commit 59b55bd4 authored by xianlezheng's avatar xianlezheng Committed by GitHub
Browse files

Merge pull request #4 from flycash/2.7.5-bk

2.7.5 bk
parents 0c87c6d1 0920e1c6
No related branches found
No related tags found
No related merge requests found
Showing
with 279 additions and 188 deletions
notifications:
commits: commits@dubbo.apache.org
issues: notifications@dubbo.apache.org
pullrequests: notifications@dubbo.apache.org
jira_options: link label link label
language: go dist: trusty
sudo: required
# define the dependence env
language: go
os: os:
- linux - linux
- osx
go: go:
- "1.13" - "1.13"
services:
- docker
env: env:
- GO111MODULE=on - GO111MODULE=on
install: true install: true
# define ci-stage
script: script:
# license-check
- echo 'start license check'
- go fmt ./... && [[ -z `git status -s` ]] - 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 - chmod u+x before_ut.sh && ./before_ut.sh
- go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic - go mod vendor && go test ./... -coverprofile=coverage.txt -covermode=atomic
# integrate-test
- chmod +x integrate_test.sh && ./integrate_test.sh
after_success: after_success:
- bash <(curl -s https://codecov.io/bash) - bash <(curl -s https://codecov.io/bash)
notifications: notifications:
webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f webhooks: https://oapi.dingtalk.com/robot/send?access_token=f5d6237f2c79db584e75604f7f88db1ce1673c8c0e98451217b28fde791e1d4f
\ No newline at end of file
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
- [Context support](https://github.com/apache/dubbo-go/pull/330) - [Context support](https://github.com/apache/dubbo-go/pull/330)
- [Opentracing & transfer context end to end for jsonrpc protocol](https://github.com/apache/dubbo-go/pull/335) - [Opentracing & transfer context end to end for jsonrpc protocol](https://github.com/apache/dubbo-go/pull/335)
- [Opentracing & transfer context end to end for dubbo protocol](https://github.com/apache/dubbo-go/pull/344) - [Opentracing & transfer context end to end for dubbo protocol](https://github.com/apache/dubbo-go/pull/344)
- [Grpc tracing for client and server](https://github.com/apache/dubbo-go/pull/397)
- [Nacos config center](https://github.com/apache/dubbo-go/pull/357) - [Nacos config center](https://github.com/apache/dubbo-go/pull/357)
- [Prometheus support](https://github.com/apache/dubbo-go/pull/342) - [Prometheus support](https://github.com/apache/dubbo-go/pull/342)
- [Support sign and auth for request](https://github.com/apache/dubbo-go/pull/323) - [Support sign and auth for request](https://github.com/apache/dubbo-go/pull/323)
......
...@@ -16,6 +16,8 @@ Apache License, Version 2.0 ...@@ -16,6 +16,8 @@ Apache License, Version 2.0
## Release note ## ## Release note ##
[v1.4.0 - Mar 17, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.4.0)
[v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0) [v1.3.0 - Mar 1, 2020](https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
[v1.2.0 - Nov 15, 2019](https://github.com/apache/dubbo-go/releases/tag/v1.2.0) [v1.2.0 - Nov 15, 2019](https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
...@@ -28,7 +30,7 @@ Apache License, Version 2.0 ...@@ -28,7 +30,7 @@ Apache License, Version 2.0
Both extension module and layered project architecture is according to Apache Dubbo (including protocol layer, registry layer, cluster layer, config layer and so on), the advantage of this arch is as following: you can implement these layered interfaces in your own way, override the default implementation of dubbo-go by calling 'extension.SetXXX' of extension, complete your special needs without modifying the source code. At the same time, you are welcome to contribute implementation of useful extension to the community. Both extension module and layered project architecture is according to Apache Dubbo (including protocol layer, registry layer, cluster layer, config layer and so on), the advantage of this arch is as following: you can implement these layered interfaces in your own way, override the default implementation of dubbo-go by calling 'extension.SetXXX' of extension, complete your special needs without modifying the source code. At the same time, you are welcome to contribute implementation of useful extension to the community.
![frame design](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png) ![dubbo go extend](./doc/pic/arch/dubbo-go-ext.png)
If you wanna know more about dubbo-go, please visit this reference [Project Architeture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design) If you wanna know more about dubbo-go, please visit this reference [Project Architeture design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design)
...@@ -56,6 +58,7 @@ Finished List: ...@@ -56,6 +58,7 @@ Finished List:
- Router - Router
* [Condition router](https://github.com/apache/dubbo-go/pull/294) * [Condition router](https://github.com/apache/dubbo-go/pull/294)
* [Health check router](https://github.com/apache/dubbo-go/pull/389)
- Registry - Registry
* ZooKeeper * ZooKeeper
...@@ -91,13 +94,21 @@ Finished List: ...@@ -91,13 +94,21 @@ Finished List:
* [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237) * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)
* [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246) * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
* [GenericServiceFilter](https://github.com/apache/dubbo-go/pull/291) * [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)
- Invoke - Invoke
* [generic invoke](https://github.com/apache/dubbo-go/pull/122) * [generic invoke](https://github.com/apache/dubbo-go/pull/122)
- Monitor - Monitor
* Opentracing API * Opentracing API
* Prometheus * [Prometheus](https://github.com/apache/dubbo-go/pull/342)
- Tracing
* [For jsonrpc](https://github.com/apache/dubbo-go/pull/335)
* [For dubbo](https://github.com/apache/dubbo-go/pull/344)
* [For grpc](https://github.com/apache/dubbo-go/pull/397)
- Others: - Others:
* start check * start check
......
...@@ -15,6 +15,8 @@ Apache License, Version 2.0 ...@@ -15,6 +15,8 @@ Apache License, Version 2.0
## 发布日志 ## ## 发布日志 ##
[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) [v1.3.0 - 2020年3月1日](https://github.com/apache/dubbo-go/releases/tag/v1.3.0)
[v1.2.0 - 2019年11月15日](https://github.com/apache/dubbo-go/releases/tag/v1.2.0) [v1.2.0 - 2019年11月15日](https://github.com/apache/dubbo-go/releases/tag/v1.2.0)
...@@ -27,7 +29,7 @@ Apache License, Version 2.0 ...@@ -27,7 +29,7 @@ Apache License, Version 2.0
基于dubbo的extension模块和分层的代码设计(包括 protocol layer, registry layer, cluster layer, config 等等)。我们的目标是:你可以对这些分层接口进行新的实现,并通过调用 extension 模块的“ extension.SetXXX ”方法来覆盖 dubbo-go [同 go-for-apache-dubbo ]的默认实现,以完成自己的特殊需求而无需修改源代码。同时,欢迎你为社区贡献有用的拓展实现。 基于dubbo的extension模块和分层的代码设计(包括 protocol layer, registry layer, cluster layer, config 等等)。我们的目标是:你可以对这些分层接口进行新的实现,并通过调用 extension 模块的“ extension.SetXXX ”方法来覆盖 dubbo-go [同 go-for-apache-dubbo ]的默认实现,以完成自己的特殊需求而无需修改源代码。同时,欢迎你为社区贡献有用的拓展实现。
![框架设计](https://raw.githubusercontent.com/wiki/dubbo/dubbo-go/dubbo-go%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1.png) ![dubbo go extend](./doc/pic/arch/dubbo-go-ext.png)
关于详细设计请阅读 [code layered design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design) 关于详细设计请阅读 [code layered design](https://github.com/apache/dubbo-go/wiki/dubbo-go-V1.0-design)
...@@ -55,6 +57,7 @@ Apache License, Version 2.0 ...@@ -55,6 +57,7 @@ Apache License, Version 2.0
- 路由器 - 路由器
* [Condition router](https://github.com/apache/dubbo-go/pull/294) * [Condition router](https://github.com/apache/dubbo-go/pull/294)
* [Health check router](https://github.com/apache/dubbo-go/pull/389)
- 注册中心 - 注册中心
* ZooKeeper * ZooKeeper
...@@ -89,13 +92,22 @@ Apache License, Version 2.0 ...@@ -89,13 +92,22 @@ Apache License, Version 2.0
* [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214) * [AccessLogFilter](https://github.com/apache/dubbo-go/pull/214)
* [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237) * [TpsLimitFilter](https://github.com/apache/dubbo-go/pull/237)
* [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246) * [ExecuteLimitFilter](https://github.com/apache/dubbo-go/pull/246)
* [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) * [泛化调用](https://github.com/apache/dubbo-go/pull/122)
- 监控 - 监控
* Opentracing API * Opentracing API
* Prometheus * [Prometheus](https://github.com/apache/dubbo-go/pull/342)
- Tracing
* [For jsonrpc](https://github.com/apache/dubbo-go/pull/335)
* [For dubbo](https://github.com/apache/dubbo-go/pull/344)
* [For grpc](https://github.com/apache/dubbo-go/pull/397)
- 其他功能支持: - 其他功能支持:
* 启动时检查 * 启动时检查
......
...@@ -20,7 +20,7 @@ set zkJarPath=remoting/zookeeper/zookeeper-4unittest/contrib/fatjar ...@@ -20,7 +20,7 @@ set zkJarPath=remoting/zookeeper/zookeeper-4unittest/contrib/fatjar
set zkJar=%zkJarPath%/%zkJarName% set zkJar=%zkJarPath%/%zkJarName%
if not exist "%zkJar%" ( if not exist "%zkJar%" (
md %zkJarPath% md "%zkJarPath%"
curl -L %remoteJarUrl% -o %zkJar% curl -L %remoteJarUrl% -o %zkJar%
) )
......
#
# 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}
...@@ -21,7 +21,8 @@ import ( ...@@ -21,7 +21,8 @@ import (
"github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol"
) )
// Cluster ... // Cluster
// Extension - Cluster
type Cluster interface { type Cluster interface {
Join(Directory) protocol.Invoker Join(Directory) protocol.Invoker
} }
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
...@@ -31,7 +31,9 @@ func init() { ...@@ -31,7 +31,9 @@ func init() {
extension.SetCluster(available, NewAvailableCluster) extension.SetCluster(available, NewAvailableCluster)
} }
// NewAvailableCluster ... // NewAvailableCluster returns a cluster instance
//
// Obtain available service providers
func NewAvailableCluster() cluster.Cluster { func NewAvailableCluster() cluster.Cluster {
return &availableCluster{} return &availableCluster{}
} }
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
...@@ -35,7 +35,7 @@ type availableClusterInvoker struct { ...@@ -35,7 +35,7 @@ type availableClusterInvoker struct {
baseClusterInvoker baseClusterInvoker
} }
// NewAvailableClusterInvoker ... // NewAvailableClusterInvoker returns a cluster invoker instance
func NewAvailableClusterInvoker(directory cluster.Directory) protocol.Invoker { func NewAvailableClusterInvoker(directory cluster.Directory) protocol.Invoker {
return &availableClusterInvoker{ return &availableClusterInvoker{
baseClusterInvoker: newBaseClusterInvoker(directory), baseClusterInvoker: newBaseClusterInvoker(directory),
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
import ( import (
"context" "context"
"fmt"
"strings" "strings"
"testing" "testing"
) )
...@@ -32,6 +33,7 @@ import ( ...@@ -32,6 +33,7 @@ import (
"github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/directory"
"github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/cluster/loadbalance"
"github.com/apache/dubbo-go/common" "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/extension"
"github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/protocol/invocation"
...@@ -39,10 +41,11 @@ import ( ...@@ -39,10 +41,11 @@ import (
) )
var ( var (
availableUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") availableUrl, _ = common.NewURL(fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider",
constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
) )
func registerAvailable(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { func registerAvailable(invoker *mock.MockInvoker) protocol.Invoker {
extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
availableCluster := NewAvailableCluster() availableCluster := NewAvailableCluster()
...@@ -60,7 +63,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) { ...@@ -60,7 +63,7 @@ func TestAvailableClusterInvokerSuccess(t *testing.T) {
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerAvailable(t, invoker) clusterInvoker := registerAvailable(invoker)
mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} mockResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
invoker.EXPECT().IsAvailable().Return(true) invoker.EXPECT().IsAvailable().Return(true)
...@@ -76,7 +79,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) { ...@@ -76,7 +79,7 @@ func TestAvailableClusterInvokerNoAvail(t *testing.T) {
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerAvailable(t, invoker) clusterInvoker := registerAvailable(invoker)
invoker.EXPECT().IsAvailable().Return(false) invoker.EXPECT().IsAvailable().Return(false)
......
...@@ -87,8 +87,11 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error { ...@@ -87,8 +87,11 @@ func (invoker *baseClusterInvoker) checkWhetherDestroyed() error {
} }
func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
var selectedInvoker protocol.Invoker var selectedInvoker protocol.Invoker
if len(invokers) <= 0 {
return selectedInvoker
}
url := invokers[0].GetUrl() url := invokers[0].GetUrl()
sticky := url.GetParamBool(constant.STICKY_KEY, false) sticky := url.GetParamBool(constant.STICKY_KEY, false)
//Get the service method sticky config if have //Get the service method sticky config if have
...@@ -98,19 +101,17 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p ...@@ -98,19 +101,17 @@ func (invoker *baseClusterInvoker) doSelect(lb cluster.LoadBalance, invocation p
invoker.stickyInvoker = nil invoker.stickyInvoker = nil
} }
if sticky && invoker.stickyInvoker != nil && (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) { if sticky && invoker.availablecheck &&
if invoker.availablecheck && invoker.stickyInvoker.IsAvailable() { invoker.stickyInvoker != nil && invoker.stickyInvoker.IsAvailable() &&
return invoker.stickyInvoker (invoked == nil || !isInvoked(invoker.stickyInvoker, invoked)) {
} return invoker.stickyInvoker
} }
selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked) selectedInvoker = invoker.doSelectInvoker(lb, invocation, invokers, invoked)
if sticky { if sticky {
invoker.stickyInvoker = selectedInvoker invoker.stickyInvoker = selectedInvoker
} }
return selectedInvoker return selectedInvoker
} }
func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker { func (invoker *baseClusterInvoker) doSelectInvoker(lb cluster.LoadBalance, invocation protocol.Invocation, invokers []protocol.Invoker, invoked []protocol.Invoker) protocol.Invoker {
......
...@@ -33,7 +33,7 @@ import ( ...@@ -33,7 +33,7 @@ import (
"github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/protocol/invocation"
) )
func Test_StickyNormal(t *testing.T) { func TestStickyNormal(t *testing.T) {
invokers := []protocol.Invoker{} invokers := []protocol.Invoker{}
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
...@@ -43,12 +43,15 @@ func Test_StickyNormal(t *testing.T) { ...@@ -43,12 +43,15 @@ func Test_StickyNormal(t *testing.T) {
base := &baseClusterInvoker{} base := &baseClusterInvoker{}
base.availablecheck = true base.availablecheck = true
invoked := []protocol.Invoker{} invoked := []protocol.Invoker{}
result := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked)
result1 := base.doSelect(loadbalance.NewRandomLoadBalance(), invocation.NewRPCInvocation("getUser", nil, nil), invokers, invoked) tmpRandomBalance := loadbalance.NewRandomLoadBalance()
tmpInvocation := invocation.NewRPCInvocation("getUser", nil, nil)
result := base.doSelect(tmpRandomBalance, tmpInvocation, invokers, invoked)
result1 := base.doSelect(tmpRandomBalance, tmpInvocation, invokers, invoked)
assert.Equal(t, result, result1) assert.Equal(t, result, result1)
} }
func Test_StickyNormalWhenError(t *testing.T) { func TestStickyNormalWhenError(t *testing.T) {
invokers := []protocol.Invoker{} invokers := []protocol.Invoker{}
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i)) url, _ := common.NewURL(fmt.Sprintf("dubbo://192.168.1.%v:20000/com.ikurento.user.UserProvider", i))
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
...@@ -31,7 +31,10 @@ func init() { ...@@ -31,7 +31,10 @@ func init() {
extension.SetCluster(broadcast, NewBroadcastCluster) extension.SetCluster(broadcast, NewBroadcastCluster)
} }
// NewBroadcastCluster ... // NewBroadcastCluster returns a broadcast cluster instance.
//
// Calling all providers' broadcast one by one. All errors will be reported.
// It is usually used to notify all providers to update local resource information such as caches or logs.
func NewBroadcastCluster() cluster.Cluster { func NewBroadcastCluster() cluster.Cluster {
return &broadcastCluster{} return &broadcastCluster{}
} }
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"testing" "testing"
) )
...@@ -32,6 +33,7 @@ import ( ...@@ -32,6 +33,7 @@ import (
"github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/directory"
"github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/cluster/loadbalance"
"github.com/apache/dubbo-go/common" "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/extension"
"github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/protocol/invocation"
...@@ -39,10 +41,11 @@ import ( ...@@ -39,10 +41,11 @@ import (
) )
var ( var (
broadcastUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") broadcastUrl, _ = common.NewURL(
fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
) )
func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol.Invoker { func registerBroadcast(mockInvokers ...*mock.MockInvoker) protocol.Invoker {
extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
invokers := []protocol.Invoker{} invokers := []protocol.Invoker{}
...@@ -59,7 +62,7 @@ func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol ...@@ -59,7 +62,7 @@ func registerBroadcast(t *testing.T, mockInvokers ...*mock.MockInvoker) protocol
return clusterInvoker return clusterInvoker
} }
func Test_BroadcastInvokeSuccess(t *testing.T) { func TestBroadcastInvokeSuccess(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
...@@ -72,13 +75,13 @@ func Test_BroadcastInvokeSuccess(t *testing.T) { ...@@ -72,13 +75,13 @@ func Test_BroadcastInvokeSuccess(t *testing.T) {
invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
} }
clusterInvoker := registerBroadcast(t, invokers...) clusterInvoker := registerBroadcast(invokers...)
result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
assert.Equal(t, mockResult, result) assert.Equal(t, mockResult, result)
} }
func Test_BroadcastInvokeFailed(t *testing.T) { func TestBroadcastInvokeFailed(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
...@@ -102,7 +105,7 @@ func Test_BroadcastInvokeFailed(t *testing.T) { ...@@ -102,7 +105,7 @@ func Test_BroadcastInvokeFailed(t *testing.T) {
invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult) invoker.EXPECT().Invoke(gomock.Any()).Return(mockResult)
} }
clusterInvoker := registerBroadcast(t, invokers...) clusterInvoker := registerBroadcast(invokers...)
result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{}) result := clusterInvoker.Invoke(context.Background(), &invocation.RPCInvocation{})
assert.Equal(t, mockFailedResult.Err, result.Error()) assert.Equal(t, mockFailedResult.Err, result.Error())
......
...@@ -31,7 +31,10 @@ func init() { ...@@ -31,7 +31,10 @@ func init() {
extension.SetCluster(failback, NewFailbackCluster) extension.SetCluster(failback, NewFailbackCluster)
} }
// NewFailbackCluster ... // NewFailbackCluster returns a failback cluster instance
//
// Failure automatically restored, failed to record the background request,
// regular retransmission. Usually used for message notification operations.
func NewFailbackCluster() cluster.Cluster { func NewFailbackCluster() cluster.Cluster {
return &failbackCluster{} return &failbackCluster{}
} }
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
import ( import (
"context" "context"
"fmt"
"sync" "sync"
"testing" "testing"
"time" "time"
...@@ -34,6 +35,7 @@ import ( ...@@ -34,6 +35,7 @@ import (
"github.com/apache/dubbo-go/cluster/directory" "github.com/apache/dubbo-go/cluster/directory"
"github.com/apache/dubbo-go/cluster/loadbalance" "github.com/apache/dubbo-go/cluster/loadbalance"
"github.com/apache/dubbo-go/common" "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/extension"
"github.com/apache/dubbo-go/protocol" "github.com/apache/dubbo-go/protocol"
"github.com/apache/dubbo-go/protocol/invocation" "github.com/apache/dubbo-go/protocol/invocation"
...@@ -41,11 +43,12 @@ import ( ...@@ -41,11 +43,12 @@ import (
) )
var ( var (
failbackUrl, _ = common.NewURL("dubbo://192.168.1.1:20000/com.ikurento.user.UserProvider") failbackUrl, _ = common.NewURL(
fmt.Sprintf("dubbo://%s:%d/com.ikurento.user.UserProvider", constant.LOCAL_HOST_VALUE, constant.DEFAULT_PORT))
) )
// registerFailback register failbackCluster to cluster extension. // registerFailback register failbackCluster to cluster extension.
func registerFailback(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker { func registerFailback(invoker *mock.MockInvoker) protocol.Invoker {
extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance) extension.SetLoadbalance("random", loadbalance.NewRandomLoadBalance)
failbackCluster := NewFailbackCluster() failbackCluster := NewFailbackCluster()
...@@ -60,12 +63,12 @@ func registerFailback(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker ...@@ -60,12 +63,12 @@ func registerFailback(t *testing.T, invoker *mock.MockInvoker) protocol.Invoker
} }
// success firstly, failback should return origin invoke result. // success firstly, failback should return origin invoke result.
func Test_FailbackSuceess(t *testing.T) { func TestFailbackSuceess(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker)
invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
...@@ -77,12 +80,12 @@ func Test_FailbackSuceess(t *testing.T) { ...@@ -77,12 +80,12 @@ func Test_FailbackSuceess(t *testing.T) {
} }
// failed firstly, success later after one retry. // failed firstly, success later after one retry.
func Test_FailbackRetryOneSuccess(t *testing.T) { func TestFailbackRetryOneSuccess(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker)
invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
...@@ -95,7 +98,7 @@ func Test_FailbackRetryOneSuccess(t *testing.T) { ...@@ -95,7 +98,7 @@ func Test_FailbackRetryOneSuccess(t *testing.T) {
wg.Add(1) wg.Add(1)
now := time.Now() now := time.Now()
mockSuccResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}} mockSuccResult := &protocol.RPCResult{Rest: rest{tried: 0, success: true}}
invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result {
delta := time.Since(now).Nanoseconds() / int64(time.Second) delta := time.Since(now).Nanoseconds() / int64(time.Second)
assert.True(t, delta >= 5) assert.True(t, delta >= 5)
wg.Done() wg.Done()
...@@ -120,12 +123,12 @@ func Test_FailbackRetryOneSuccess(t *testing.T) { ...@@ -120,12 +123,12 @@ func Test_FailbackRetryOneSuccess(t *testing.T) {
} }
// failed firstly, and failed again after ech retry time. // failed firstly, and failed again after ech retry time.
func Test_FailbackRetryFailed(t *testing.T) { func TestFailbackRetryFailed(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker)
invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
...@@ -141,7 +144,7 @@ func Test_FailbackRetryFailed(t *testing.T) { ...@@ -141,7 +144,7 @@ func Test_FailbackRetryFailed(t *testing.T) {
// add retry call that eventually failed. // add retry call that eventually failed.
for i := 0; i < retries; i++ { for i := 0; i < retries; i++ {
j := i + 1 j := i + 1
invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result {
delta := time.Since(now).Nanoseconds() / int64(time.Second) delta := time.Since(now).Nanoseconds() / int64(time.Second)
assert.True(t, delta >= int64(5*j)) assert.True(t, delta >= int64(5*j))
wg.Done() wg.Done()
...@@ -166,12 +169,12 @@ func Test_FailbackRetryFailed(t *testing.T) { ...@@ -166,12 +169,12 @@ func Test_FailbackRetryFailed(t *testing.T) {
} }
// add 10 tasks but all failed firstly, and failed again with one retry. // add 10 tasks but all failed firstly, and failed again with one retry.
func Test_FailbackRetryFailed10Times(t *testing.T) { func TestFailbackRetryFailed10Times(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker)
clusterInvoker.maxRetries = 10 clusterInvoker.maxRetries = 10
invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
...@@ -184,7 +187,7 @@ func Test_FailbackRetryFailed10Times(t *testing.T) { ...@@ -184,7 +187,7 @@ func Test_FailbackRetryFailed10Times(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(10) wg.Add(10)
now := time.Now() now := time.Now()
invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(invocation protocol.Invocation) protocol.Result { invoker.EXPECT().Invoke(gomock.Any()).DoAndReturn(func(protocol.Invocation) protocol.Result {
delta := time.Since(now).Nanoseconds() / int64(time.Second) delta := time.Since(now).Nanoseconds() / int64(time.Second)
assert.True(t, delta >= 5) assert.True(t, delta >= 5)
wg.Done() wg.Done()
...@@ -208,12 +211,12 @@ func Test_FailbackRetryFailed10Times(t *testing.T) { ...@@ -208,12 +211,12 @@ func Test_FailbackRetryFailed10Times(t *testing.T) {
assert.Equal(t, int64(0), clusterInvoker.taskList.Len()) assert.Equal(t, int64(0), clusterInvoker.taskList.Len())
} }
func Test_FailbackOutOfLimit(t *testing.T) { func TestFailbackOutOfLimit(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
defer ctrl.Finish() defer ctrl.Finish()
invoker := mock.NewMockInvoker(ctrl) invoker := mock.NewMockInvoker(ctrl)
clusterInvoker := registerFailback(t, invoker).(*failbackClusterInvoker) clusterInvoker := registerFailback(invoker).(*failbackClusterInvoker)
clusterInvoker.failbackTasks = 1 clusterInvoker.failbackTasks = 1
invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes() invoker.EXPECT().GetUrl().Return(failbackUrl).AnyTimes()
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
...@@ -31,7 +31,10 @@ func init() { ...@@ -31,7 +31,10 @@ func init() {
extension.SetCluster(failfast, NewFailFastCluster) extension.SetCluster(failfast, NewFailFastCluster)
} }
// NewFailFastCluster ... // NewFailFastCluster returns a failfast cluster instance.
//
// Fast failure, only made a call, failure immediately error. Usually used for non-idempotent write operations,
// such as adding records.
func NewFailFastCluster() cluster.Cluster { func NewFailFastCluster() cluster.Cluster {
return &failfastCluster{} return &failfastCluster{}
} }
......
/* /*
Licensed to the Apache Software Foundation (ASF) under one or more * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with * contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership. * this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0 * 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 not use this file except in compliance with
the License. You may obtain a copy of the License at * the License. You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
limitations under the License. * limitations under the License.
*/ */
package cluster_impl package cluster_impl
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment