diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000000..7644b41f49
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,13 @@
+Copyright 1999-2011 Alibaba Group.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000..15c1e54d4d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 1999-2101 Alibaba Group.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/dubbo-cluster/pom.xml b/dubbo-cluster/pom.xml
new file mode 100644
index 0000000000..d8179ba862
--- /dev/null
+++ b/dubbo-cluster/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ com.alibaba
+ dubbo-parent
+ 2.0.7
+
+ dubbo-cluster
+ jar
+ Dubbo Cluster Module
+ The cluster module of dubbo project
+
+
+ com.alibaba
+ dubbo-rpc
+ ${project.parent.version}
+
+
+ org.apache.bsf
+ bsf-api
+ provided
+ true
+
+
+
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
new file mode 100644
index 0000000000..b39ecea890
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Cluster.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.support.FailoverCluster;
+
+/**
+ * Cluster. (SPI, Singleton, ThreadSafe)
+ *
+ * Cluster
+ * Fault-Tolerant
+ *
+ * @author william.liangf
+ */
+@Extension(FailoverCluster.NAME)
+public interface Cluster {
+
+ /**
+ * Merge the directory invokers to a virtual invoker.
+ *
+ * @param
+ * @param directory
+ * @return
+ * @throws RpcException
+ */
+ @Adaptive
+ Invoker merge(Directory directory) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
new file mode 100644
index 0000000000..10bacd8c07
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Directory.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Node;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * Directory. (SPI, Singleton, ThreadSafe)
+ *
+ * Directory Service
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#merge(Directory)
+ * @author william.liangf
+ */
+public interface Directory extends Node {
+
+ /**
+ * get service type.
+ *
+ * @return service type.
+ */
+ Class getInterface();
+
+ /**
+ * list invokers.
+ *
+ * @return invokers
+ */
+ List> list(Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
new file mode 100644
index 0000000000..603cfe7617
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/LoadBalance.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance;
+
+/**
+ * LoadBalance. (SPI, Singleton, ThreadSafe)
+ *
+ * Load-Balancing
+ *
+ * @see com.alibaba.dubbo.rpc.cluster.Cluster#merge(Directory)
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(RandomLoadBalance.NAME)
+public interface LoadBalance {
+
+ /**
+ * select one invoker in list.
+ *
+ * @param invokers invokers.
+ * @param invocation invocation.
+ * @return selected invoker.
+ */
+ Invoker select(List> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
new file mode 100644
index 0000000000..9480414e6b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/Router.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.RpcException;
+
+/**
+ * Router.
+ *
+ * @author chao.liuc
+ */
+public interface Router {
+
+ /**
+ * route.
+ *
+ * @param invokers
+ * @param invocation
+ * @return
+ * @throws RpcException
+ */
+ List> route(List> invokers, Invocation invocation) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
new file mode 100644
index 0000000000..0fe4949912
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/RouterFactory.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * RouterFactory. (SPI, Singleton, ThreadSafe)
+ *
+ * @author chao.liuc
+ */
+@Extension
+public interface RouterFactory {
+
+ /**
+ * Create router.
+ *
+ * @param url
+ * @return
+ */
+ @Adaptive("protocol")
+ Router getRouter(URL url);
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java
new file mode 100644
index 0000000000..d7dd7b0b8b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/directory/StaticDirectory.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.directory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractDirectory;
+
+/**
+ * StaticDirectory
+ *
+ * @author william.liangf
+ */
+public class StaticDirectory extends AbstractDirectory {
+
+ private final List> invokers;
+
+ private static final List> EMPTY_INVOKERLIST = new ArrayList>();
+
+ public StaticDirectory(List> invokers){
+ this(null, invokers, null);
+ }
+
+ public StaticDirectory(List> invokers, List routers){
+ this(null, invokers, routers);
+ }
+
+ public StaticDirectory(URL url, List> invokers) {
+ this(url, invokers, null);
+ }
+
+ public StaticDirectory(URL url, List> invokers, List routers) {
+ super(url == null && invokers != null && invokers.size() > 0 ? invokers.get(0).getUrl() : url, routers);
+ if (invokers == null || invokers.size() == 0)
+ throw new IllegalArgumentException("invokers == null");
+ this.invokers = invokers;
+ }
+
+ public Class getInterface() {
+ return invokers.get(0).getInterface();
+ }
+
+ public boolean isAvailable() {
+ for (Invoker invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void destroy() {
+ for (Invoker invoker : invokers) {
+ invoker.destroy();
+ }
+ invokers.clear();
+ }
+
+ @Override
+ protected List> doList(Invocation invocation) throws RpcException {
+
+ return invokers;
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
new file mode 100644
index 0000000000..ff62a6c3ff
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/AbstractLoadBalance.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AbstractLoadBalance
+ *
+ * @author william.liangf
+ */
+public abstract class AbstractLoadBalance implements LoadBalance {
+
+ public Invoker select(List> invokers, Invocation invocation) {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ return doSelect(invokers, invocation);
+ }
+
+ protected abstract Invoker doSelect(List> invokers, Invocation invocation);
+
+ protected int getWeight(Invoker> invoker, Invocation invocation) {
+ return invoker.getUrl().getMethodIntParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
new file mode 100644
index 0000000000..87c053b0f8
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LeastActiveLoadBalance.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcStatus;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * LeastActiveLoadBalance
+ *
+ * @author william.liangf
+ */
+@Extension(LeastActiveLoadBalance.NAME)
+public class LeastActiveLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "leastactive";
+
+ private final Random random = new Random();
+
+ protected Invoker doSelect(List> invokers, Invocation invocation) {
+ int length = invokers.size(); // 总个数
+ int leastActive = -1; // 最小的活跃数
+ int leastCount = 0; // 相同最小活跃数的个数
+ int[] leastIndexs = new int[length]; // 相同最小活跃数的下标
+ int totalWeight = 0; // 总权重
+ int firstWeight = 0; // 第一个权重,用于于计算是否相同
+ boolean sameWeight = true; // 是否所有权重相同
+ for (int i = 0; i < length; i++) {
+ Invoker invoker = invokers.get(i);
+ int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive(); // 活跃数
+ int weight = invoker.getUrl().getMethodIntParameter(invocation.getMethodName(), Constants.WEIGHT_KEY, Constants.DEFAULT_WEIGHT); // 权重
+ if (leastActive == -1 || active < leastActive) { // 发现更小的活跃数,重新开始
+ leastActive = active; // 记录最小活跃数
+ leastCount = 1; // 重新统计相同最小活跃数的个数
+ leastIndexs[0] = i; // 重新记录最小活跃数下标
+ totalWeight = weight; // 重新累计总权重
+ firstWeight = weight; // 记录第一个权重
+ sameWeight = true; // 还原权重相同标识
+ } else if (active == leastActive) { // 累计相同最小的活跃数
+ leastIndexs[leastCount ++] = i; // 累计相同最小活跃数下标
+ totalWeight += weight; // 累计总权重
+ // 判断所有权重是否一样
+ if (sameWeight && i > 0
+ && weight != firstWeight) {
+ sameWeight = false;
+ }
+ }
+ }
+ // assert(leastCount > 0)
+ if (leastCount == 1) {
+ // 如果只有一个最小则直接返回
+ return invokers.get(leastIndexs[0]);
+ }
+ if (! sameWeight && totalWeight > 0) {
+ // 如果权重不相同且权重大于0则按总权重数随机
+ int offsetWeight = random.nextInt(totalWeight);
+ // 并确定随机值落在哪个片断上
+ for (int i = 0; i < leastCount; i++) {
+ int leastIndex = leastIndexs[i];
+ offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
+ if (offsetWeight <= 0)
+ return invokers.get(leastIndex);
+ }
+ }
+ // 如果权重相同或权重为0则均等随机
+ return invokers.get(leastIndexs[random.nextInt(leastCount)]);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
new file mode 100644
index 0000000000..9e29dff6a9
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceAdptive.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Adaptive;
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * LoadBalanceAdptive
+ *
+ * @author ding.lid
+ * @author william.liangf
+ */
+@Adaptive
+public class LoadBalanceAdptive implements LoadBalance {
+ public Invoker select(List> invokers, Invocation invocation) throws RpcException {
+ if (invokers == null || invokers.size() == 0) {
+ return null;
+ }
+ URL url = invokers.get(0).getUrl();
+ String method = invocation.getMethodName();
+ String name;
+ if (method == null || method.length() == 0) {
+ name = url.getParameter(Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+ } else {
+ name = url.getMethodParameter(method, Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE);
+ }
+ LoadBalance loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(name);
+ return loadbalance.select(invokers, invocation);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
new file mode 100644
index 0000000000..a86c5f7210
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RandomLoadBalance.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.Random;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * random load balance.
+ *
+ * @author qianlei
+ * @author william.liangf
+ */
+@Extension(RandomLoadBalance.NAME)
+public class RandomLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "random";
+
+ private final Random random = new Random();
+
+ protected Invoker doSelect(List> invokers, Invocation invocation) {
+ int length = invokers.size(); // 总个数
+ int totalWeight = 0; // 总权重
+ boolean sameWeight = true; // 权重是否都一样
+ for (int i = 0; i < length; i++) {
+ // 获取权重
+ int weight = getWeight(invokers.get(i), invocation);
+ // 累计总权重
+ totalWeight += weight;
+ // 判断所有权重是否一样
+ if (sameWeight && i > 0
+ && weight != getWeight(invokers.get(i - 1), invocation)) {
+ sameWeight = false;
+ }
+ }
+ if (!sameWeight && totalWeight > 0) {
+ // 如果权重不相同且权重大于0则按总权重数随机
+ int offset = random.nextInt(totalWeight);
+ // 并确定随机值落在哪个片断上
+ for (int i = 0; i < length; i++) {
+ offset -= getWeight(invokers.get(i), invocation);
+ if (offset < 0) {
+ return invokers.get(i);
+ }
+ }
+ }
+ // 如果权重相同或权重为0则均等随机
+ return invokers.get(random.nextInt(length));
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
new file mode 100644
index 0000000000..a8aaec6841
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/loadbalance/RoundRobinLoadBalance.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Invocation;
+
+/**
+ * Round robin load balance.
+ *
+ * @author qian.lei
+ * @author william.liangf
+ */
+@Extension(RoundRobinLoadBalance.NAME)
+public class RoundRobinLoadBalance extends AbstractLoadBalance {
+ public static final String NAME = "roundrobin";
+
+ private final ConcurrentMap sequences = new ConcurrentHashMap();
+
+ protected Invoker doSelect(List> invokers, Invocation invocation) {
+ String key = invokers.get(0).getInterface().getName() + "." + invocation.getMethodName();// + System.identityHashCode(invokers);
+ AtomicPositiveInteger sequence = sequences.get(key);
+ if (sequence == null) {
+ sequences.putIfAbsent(key, new AtomicPositiveInteger());
+ sequence = sequences.get(key);
+ }
+ // 取模轮循
+ return invokers.get(sequence.getAndIncrement() % invokers.size());
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
new file mode 100644
index 0000000000..86e14b2a7f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/FileRouterFactory.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.utils.IOUtils;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+@Extension(FileRouterFactory.NAME)
+public class FileRouterFactory implements RouterFactory {
+
+ public static final String NAME = "file";
+
+ private RouterFactory routerFactory;
+
+ public void setRouterFactory(RouterFactory routerFactory) {
+ this.routerFactory = routerFactory;
+ }
+
+ public Router getRouter(URL url) {
+ try {
+ // File URL 转换成 其它Route URL,然后Load
+ // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=
+ String protocol = url.getParameter(RpcConstants.ROUTER_KEY, ScriptRouterFactory.NAME); // 将原类型转为协议
+ String type = null; // 使用文件后缀做为类型
+ String path = url.getPath();
+ if (path != null) {
+ int i = path.lastIndexOf('.');
+ if (i > 0) {
+ type = path.substring(i + 1);
+ }
+ }
+ String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));
+ URL script = url.setProtocol(protocol).addParameter(RpcConstants.TYPE_KEY, type).addParameterAndEncoded(RpcConstants.RULE_KEY, rule);
+
+ return routerFactory.getRouter(script);
+ } catch (IOException e) {
+ throw new IllegalStateException(e.getMessage(), e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
new file mode 100644
index 0000000000..8156b80604
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouter.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import org.jboss.netty.util.internal.ConcurrentHashMap;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcContext;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+/**
+ * ScriptRouter
+ *
+ * @author william.liangf
+ */
+public class ScriptRouter implements Router {
+
+ private static final Logger logger = LoggerFactory.getLogger(ScriptRouter.class);
+
+ private static final Map engines = new ConcurrentHashMap();
+
+ private final ScriptEngine engine;
+
+ private final String rule;
+
+ public ScriptRouter(URL url) {
+ String type = url.getParameter(RpcConstants.TYPE_KEY);
+ String rule = url.getParameterAndDecoded(RpcConstants.RULE_KEY);
+ if (type == null || type.length() == 0){
+ type = RpcConstants.DEFAULT_SCRIPT_TYPE_KEY;
+ }
+ if (rule == null || rule.length() == 0){
+ throw new IllegalStateException(new IllegalStateException("route rule can not be empty. rule:" + rule));
+ }
+ ScriptEngine engine = engines.get(type);
+ if (engine == null){
+ engine = new ScriptEngineManager().getEngineByName(type);
+ if (engine == null) {
+ throw new IllegalStateException(new IllegalStateException("Unsupported route rule type: " + type + ", rule: " + rule));
+ }
+ engines.put(type, engine);
+ }
+ this.engine = engine;
+ this.rule = rule;
+ }
+
+ @SuppressWarnings("unchecked")
+ public List> route(List> invokers, Invocation invocation) throws RpcException {
+ try {
+ List> invokersCopy = new ArrayList>(invokers);
+ Compilable compilable = (Compilable) engine;
+ Bindings bindings = engine.createBindings();
+ bindings.put("invokers", invokersCopy);
+ bindings.put("invocation", invocation);
+ bindings.put("context", RpcContext.getContext());
+ CompiledScript function = compilable.compile(rule);
+ Object obj = function.eval(bindings);
+ if (obj instanceof Invoker[]) {
+ invokersCopy = Arrays.asList((Invoker[]) obj);
+ } else if (obj instanceof Object[]) {
+ invokersCopy = new ArrayList>();
+ for (Object inv : (Object[]) obj) {
+ invokersCopy.add((Invoker)inv);
+ }
+ } else {
+ invokersCopy = (List>) obj;
+ }
+ return invokersCopy;
+ } catch (ScriptException e) {
+ //fail then ignore rule .invokers.
+ logger.error("route error , rule has been ignored .rule :"+ rule + ",invocation:" + invocation + ",url :"+(RpcContext.getContext().getInvoker() == null ? "" : RpcContext.getContext().getInvoker().getUrl()), e);
+ return invokers;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
new file mode 100644
index 0000000000..43edc39e34
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterFactory.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * ScriptRouterFactory
+ *
+ * Script Router Factory用到的URL形如:
+ *
+ * - script://registyAddress?type=js&rule=xxxx
+ *
- script:///path/to/routerfile.js?type=js&rule=xxxx
+ *
- script://D:\path\to\routerfile.js?type=js&rule=xxxx
+ *
- script://C:/path/to/routerfile.js?type=js&rule=xxxx
+ *
+ * URL的Host一段包含的是Script Router内容的来源,Registry、File etc
+ *
+ * @author william.liangf
+ */
+@Extension(ScriptRouterFactory.NAME)
+public class ScriptRouterFactory implements RouterFactory {
+
+ public static final String NAME = "script";
+
+ public Router getRouter(URL url) {
+ return new ScriptRouter(url);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
new file mode 100644
index 0000000000..6cf0b4669b
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvoker.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AbstractRpcRouter
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ */
+public abstract class AbstractClusterInvoker implements Invoker {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(AbstractClusterInvoker.class);
+ protected final Directory directory;
+
+ protected final boolean availablecheck;
+
+ private volatile boolean destroyed = false;
+
+ private volatile Invoker stickyInvoker = null;
+
+
+ public AbstractClusterInvoker(Directory directory) {
+ this(directory, directory.getUrl());
+ }
+ public AbstractClusterInvoker(Directory directory, URL url) {
+ if (directory == null)
+ throw new IllegalArgumentException("service directory == null");
+
+ this.directory = directory ;
+ this.availablecheck = url.getBooleanParameter(RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY, RpcConstants.DEFAULT_CLUSTER_AVAILABLE_CHECK) ;
+ }
+
+ public Class getInterface() {
+ return directory.getInterface();
+ }
+
+ public URL getUrl() {
+ return directory.getUrl();
+ }
+
+ public boolean isAvailable() {
+ Invoker invoker = stickyInvoker;
+ if (invoker != null) {
+ return invoker.isAvailable();
+ }
+ return directory.isAvailable();
+ }
+
+ public void destroy() {
+ directory.destroy();
+ destroyed = true;
+ }
+
+ /**
+ * 使用loadbalance选择invoker.
+ * a)先lb选择,如果在selected列表中 或者 不可用且做检验时,进入下一步(重选),否则直接返回
+ * b)重选验证规则:selected > available .保证重选出的结果尽量不在select中,并且是可用的
+ *
+ * @param availablecheck 如果设置true,在选择的时候先选invoker.available == true
+ * @param selected 已选过的invoker.注意:输入保证不重复
+ *
+ */
+ protected Invoker select(LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ String methodName = invocation == null ? "" : invocation.getMethodName();
+
+ boolean sticky = invokers.get(0).getUrl().getMethodBooleanParameter(methodName,RpcConstants.CLUSTER_STICKY_KEY, RpcConstants.DEFAULT_CLUSTER_STICKY) ;
+ {
+ //ignore overloaded method
+ if ( stickyInvoker != null && !invokers.contains(stickyInvoker) ){
+ stickyInvoker = null;
+ }
+ //ignore cucurrent problem
+ if (sticky && stickyInvoker != null && (selected == null || !selected.contains(stickyInvoker))){
+ if (availablecheck && stickyInvoker.isAvailable()){
+ return stickyInvoker;
+ }
+ }
+ }
+ Invoker invoker = doselect(loadbalance, invocation, invokers, selected);
+
+ if (sticky){
+ stickyInvoker = invoker;
+ }
+ return invoker;
+ }
+
+ private Invoker doselect(LoadBalance loadbalance, Invocation invocation, List> invokers, List> selected) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ return null;
+ if (invokers.size() == 1)
+ return invokers.get(0);
+ // 如果只有两个invoker,退化成轮循
+ if (invokers.size() == 2 && selected != null && selected.size() > 0) {
+ return selected.get(0) == invokers.get(0) ? invokers.get(1) : invokers.get(0);
+ }
+ Invoker invoker = loadbalance.select(invokers, invocation);
+
+ //如果 selected中包含(优先判断) 或者 不可用&&availablecheck=true 则重试.
+ if( (selected != null && selected.contains(invoker))
+ ||(!invoker.isAvailable() && getUrl()!=null && availablecheck)){
+ try{
+ Invoker rinvoker = reselect(loadbalance, invocation, invokers, selected, availablecheck);
+ if(rinvoker != null){
+ invoker = rinvoker;
+ }else{
+ //看下第一次选的位置,如果不是最后,选+1位置.
+ int index = invokers.indexOf(invoker);
+ try{
+ //最后在避免碰撞
+ invoker = index reselect(LoadBalance loadbalance,Invocation invocation,
+ List> invokers, List> selected ,boolean availablecheck)
+ throws RpcException {
+
+ //预先分配一个,这个列表是一定会用到的.
+ List> reselectInvokers = new ArrayList>(invokers.size()>1?(invokers.size()-1):invokers.size());
+
+ //先从非select中选
+ if( availablecheck ){ //选isAvailable 的非select
+ for(Invoker invoker : invokers){
+ if(invoker.isAvailable()){
+ if(selected ==null || !selected.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }else{ //选全部非select
+ for(Invoker invoker : invokers){
+ if(selected ==null || !selected.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }
+ //最后从select中选可用的.
+ {
+ if(selected != null){
+ for(Invoker invoker : selected){
+ if((invoker.isAvailable()) //优先选available
+ && !reselectInvokers.contains(invoker)){
+ reselectInvokers.add(invoker);
+ }
+ }
+ }
+ if(reselectInvokers.size()>0){
+ return loadbalance.select(reselectInvokers, invocation);
+ }
+ }
+ return null;
+ }
+
+ public Result invoke(final Invocation invocation) throws RpcException {
+
+ if(destroyed){
+ throw new RpcException("Rpc invoker for " + getInterface() + " on consumer " + NetUtils.getLocalHost()
+ + " use dubbo version " + Version.getVersion()
+ + " is not destroyed! Can not invoke any more.");
+ }
+
+ LoadBalance loadbalance;
+
+ List> invokers = directory.list(invocation);
+ if (invokers != null && invokers.size() > 0) {
+ loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl()
+ .getMethodParameter(invocation.getMethodName(),Constants.LOADBALANCE_KEY, Constants.DEFAULT_LOADBALANCE));
+ } else {
+ loadbalance = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(Constants.DEFAULT_LOADBALANCE);
+ }
+ return doInvoke(invocation, invokers, loadbalance);
+ }
+
+ @Override
+ public String toString() {
+ return getInterface() + " -> " + getUrl().toString();
+ }
+
+ protected abstract Result doInvoke(Invocation invocation, List> invokers,
+ LoadBalance loadbalance) throws RpcException;
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.java
new file mode 100644
index 0000000000..99d2f5b0a4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AbstractDirectory.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.Router;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+
+/**
+ * 增加router的Directory
+ * @author chao.liuc
+ */
+public abstract class AbstractDirectory implements Directory {
+
+ private final URL url ;
+ private Boolean destroyed = false;
+
+ private List routers = new ArrayList();
+
+ public AbstractDirectory(URL url) {
+ this(url, null);
+ }
+
+ public AbstractDirectory(URL url, List routers) {
+ if (url == null)
+ throw new IllegalArgumentException("url == null");
+ if (routers == null){
+ routers = new ArrayList();
+ }
+
+ this.url = url;
+ String routerkey = url.getParameter(Constants.ROUTER_KEY);
+ if (routerkey != null && routerkey.length()>0 ){
+ RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
+ routers.add(routerFactory.getRouter(url));
+ }
+ if (routers != null) {
+ setRouters(routers);
+ }
+ }
+
+ public List> list(Invocation invocation) throws RpcException {
+ List> invokers = doList(invocation);
+ for (Router router: routers){
+ invokers = router.route(invokers, invocation);
+ }
+ return invokers;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public List getRouters(){
+ return routers;
+ }
+
+ protected abstract List> doList(Invocation invocation) throws RpcException ;
+
+ protected void setRouters(List r){
+ routers = r;
+ }
+
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
new file mode 100644
index 0000000000..0ef2630136
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableCluster.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+@Extension(AvailableCluster.NAME)
+public class AvailableCluster implements Cluster {
+
+ public static final String NAME = "available";
+
+ public Invoker merge(Directory directory) throws RpcException {
+
+ return new AbstractClusterInvoker(directory) {
+ public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+ };
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
new file mode 100644
index 0000000000..d86fce38f3
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/AvailableClusterInvoker.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * AvailableCluster
+ *
+ * @author william.liangf
+ */
+public class AvailableClusterInvoker extends AbstractClusterInvoker {
+ public AvailableClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ for (Invoker invoker : invokers) {
+ if (invoker.isAvailable()) {
+ return invoker.invoke(invocation);
+ }
+ }
+ throw new RpcException("No provider available in " + invokers);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
new file mode 100644
index 0000000000..032ed6ff67
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ClusterUtils.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.URL;
+
+/**
+ * ClusterUtils
+ *
+ * @author william.liangf
+ */
+public class ClusterUtils {
+
+ public static URL mergeUrl(URL remoteUrl, Map localMap) {
+ Map map = new HashMap();
+ Map remoteMap = remoteUrl.getParameters();
+ if (remoteMap != null && remoteMap.size() > 0) {
+ map.putAll(remoteMap);
+ }
+ if (localMap != null && localMap.size() > 0) {
+ map.putAll(localMap);
+ }
+ if (remoteMap != null && remoteMap.size() > 0) {
+ // 版本号使用提供者的
+ String dubbo = remoteMap.get(Constants.DUBBO_VERSION_KEY);
+ if (dubbo != null && dubbo.length() > 0) {
+ map.put(Constants.DUBBO_VERSION_KEY, dubbo);
+ }
+ String version = remoteMap.get(Constants.VERSION_KEY);
+ if (version != null && version.length() > 0) {
+ map.put(Constants.VERSION_KEY, version);
+ }
+ String group = remoteMap.get(Constants.GROUP_KEY);
+ if (group != null && group.length() > 0) {
+ map.put(Constants.GROUP_KEY, group);
+ }
+ String methods = remoteMap.get(Constants.METHODS_KEY);
+ if (methods != null && methods.length() > 0) {
+ map.put(Constants.METHODS_KEY, methods);
+ }
+ // 合并filter和listener
+ String remoteFilter = remoteMap.get(Constants.REFERENCE_FILTER_KEY);
+ String localFilter = localMap.get(Constants.REFERENCE_FILTER_KEY);
+ if (remoteFilter != null && remoteFilter.length() > 0
+ && localFilter != null && localFilter.length() > 0) {
+ localMap.put(Constants.REFERENCE_FILTER_KEY, remoteFilter + "," + localFilter);
+ }
+ String remoteListener = remoteMap.get(Constants.INVOKER_LISTENER_KEY);
+ String localListener = localMap.get(Constants.INVOKER_LISTENER_KEY);
+ if (remoteListener != null && remoteListener.length() > 0
+ && localListener != null && localListener.length() > 0) {
+ localMap.put(Constants.INVOKER_LISTENER_KEY, remoteListener + "," + localListener);
+ }
+ }
+ return remoteUrl.addParameters(map);
+ }
+
+ private ClusterUtils() {}
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
new file mode 100644
index 0000000000..e16c06836e
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackCluster.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败自动恢复,后台记录失败请求,定时重发,通常用于消息通知操作。
+ *
+ * Failback
+ *
+ * @author william.liangf
+ */
+@Extension(FailbackCluster.NAME)
+public class FailbackCluster implements Cluster {
+ public final static String NAME = "failback";
+
+ public Invoker merge(Directory directory) throws RpcException {
+ return new FailbackClusterInvoker(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
new file mode 100644
index 0000000000..ae52ac74d4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailbackClusterInvoker.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * FailBackClusterInvoker.java
+ *
+ * @author tony.chenl
+ */
+public class FailbackClusterInvoker extends AbstractClusterInvoker {
+
+ public FailbackClusterInvoker(Directory directory){
+ super(directory);
+ }
+
+ private static final Logger logger = LoggerFactory.getLogger(FailbackClusterInvoker.class);
+
+ private static final long RETRY_FAILED_PERIOD = 5 * 1000;
+
+ private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2,
+ new NamedThreadFactory(
+ "failback-cluster-timer",
+ true));
+
+ private volatile ScheduledFuture> retryFuture;
+
+ private final ConcurrentMap> failed = new ConcurrentHashMap>();
+
+ private void addFailed(Invocation invocation, AbstractClusterInvoker> router) {
+ if (retryFuture == null) {
+ synchronized (this) {
+ if (retryFuture == null) {
+ retryFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+
+ public void run() {
+ // 收集统计信息
+ try {
+ retryFailed();
+ } catch (Throwable t) { // 防御性容错
+ logger.error("Unexpected error occur at collect statistic", t);
+ }
+ }
+ }, RETRY_FAILED_PERIOD, RETRY_FAILED_PERIOD, TimeUnit.MILLISECONDS);
+ }
+ }
+ }
+ failed.put(invocation, router);
+ }
+
+ void retryFailed() {
+ if (failed.size() == 0) {
+ return;
+ }
+ for (Map.Entry> entry : new HashMap>(
+ failed).entrySet()) {
+ Invocation invocation = entry.getKey();
+ Invoker> invoker = entry.getValue();
+ try {
+ invoker.invoke(invocation);
+ failed.remove(invocation);
+ } catch (Throwable e) {
+ logger.error("Failed retry to invoke " + invocation + ", waiting again.", e);
+ }
+ }
+ }
+
+ protected Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ Invoker invoker = select(loadbalance, invocation, invokers, null);
+ try {
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ logger.error("Failback to invoke " + invocation + ", wait for retry in background. Ignored exception: "
+ + e.getMessage() + ", ", e);
+ addFailed(invocation, this);
+ return new RpcResult(); // ignore
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
new file mode 100644
index 0000000000..3945f2be3a
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
+ * Fail-fast
+ *
+ * @author william.liangf
+ */
+@Extension(FailfastCluster.NAME)
+public class FailfastCluster implements Cluster {
+ public final static String NAME = "failfast";
+
+ public Invoker merge(Directory directory) throws RpcException {
+ return new FailfastClusterInvoker(directory);
+ }
+
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
new file mode 100644
index 0000000000..2c77b96662
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailfastClusterInvoker.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 快速失败,只发起一次调用,失败立即报错,通常用于非幂等性的写操作。
+ * Fail-fast
+ *
+ * @author william.liangf
+ * @author chao.liuc
+ *
+ */
+public class FailfastClusterInvoker extends AbstractClusterInvoker{
+ public FailfastClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+
+ Invoker invoker = select(loadbalance, invocation, invokers, null);
+ try {
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ throw new RpcException("Failfast invoke providers " + invoker.getUrl() + " " + loadbalance.getClass().getAnnotation(Extension.class).value() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
new file mode 100644
index 0000000000..d004412e1d
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverCluster.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。 Failover
+ *
+ * @author william.liangf
+ */
+@Extension(FailoverCluster.NAME)
+public class FailoverCluster implements Cluster {
+
+ public final static String NAME = "failover";
+
+ public Invoker merge(Directory directory) throws RpcException {
+
+ return new FailoverClusterInvoker(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
new file mode 100644
index 0000000000..48b4abe5ca
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailoverClusterInvoker.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.common.Version;
+import com.alibaba.dubbo.common.utils.NetUtils;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。
+ *
+ * Failover
+ * @author william.liangf
+ *
+ */
+public class FailoverClusterInvoker extends AbstractClusterInvoker{
+ public FailoverClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ if (invokers == null || invokers.size() == 0)
+ throw new RpcException("No provider available for service " + getInterface().getName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", Please check whether the service do exist or version is right firstly, and check the provider has started.");
+
+ int len = getUrl().getMethodIntParameter(invocation.getMethodName(), Constants.RETRIES_KEY, Constants.DEFAULT_RETRIES) + 1;
+ if (len <= 0)
+ len = 1;
+
+ // retry loop.
+ Throwable le = null; // last exception.
+ List> invoked = new ArrayList>(invokers.size()); // invoked invokers.
+ Set providers = new HashSet(len);
+ for (int i = 0; i < len; i++) {
+ //boolean pp = false; // is provider problem.
+ Invoker invoker = select(loadbalance, invocation, invokers, invoked);
+ invoked.add(invoker);
+ providers.add(invoker.getUrl());
+ try {
+ return invoker.invoke(invocation);
+ } catch (RpcException e) {
+ if (e.isBiz()) throw e;
+
+ le = e;
+ //pp = true;
+ } catch (Throwable e) // biz exception.
+ {
+ throw new RpcException(e.getMessage(), e);
+ } finally {
+ //if (pp) // if provider problem, fail over.
+ // inv.setWeight(0);
+ }
+ }
+ List urls = new ArrayList(invokers.size());
+ for(Invoker invoker : invokers){
+ if(invoker != null )
+ urls.add(invoker.getUrl());
+ }
+ throw new RpcException("Tried " + len + " times to invoke providers " + providers + " " + loadbalance.getClass().getAnnotation(Extension.class).value() + " select from all providers " + invokers + " for service " + getInterface().getName() + " method " + invocation.getMethodName() + " on consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + ", but no luck to perform the invocation. Last error is: " + (le != null ? le.getMessage() : ""), le);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
new file mode 100644
index 0000000000..2bf974bb0f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeCluster.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。 Fail-safe
+ *
+ * @author william.liangf
+ */
+@Extension(FailsafeCluster.NAME)
+public class FailsafeCluster implements Cluster {
+
+ public final static String NAME = "failsafe";
+
+ public Invoker merge(Directory directory) throws RpcException {
+ return new FailsafeClusterInvoker(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
new file mode 100644
index 0000000000..aa982984b4
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/FailsafeClusterInvoker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.List;
+
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 失败安全,出现异常时,直接忽略,通常用于写入审计日志等操作。
+ *
+ * Fail-safe
+ *
+ * @author william.liangf
+ */
+public class FailsafeClusterInvoker extends AbstractClusterInvoker{
+ private static final Logger logger = LoggerFactory.getLogger(FailsafeClusterInvoker.class);
+
+ public FailsafeClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ public Result doInvoke(Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ Invoker invoker = select(loadbalance, invocation, invokers, null);
+ try {
+ return invoker.invoke(invocation);
+ } catch (Throwable e) {
+ logger.error("Failsafe ignore exception: " + e.getMessage(), e);
+ return new RpcResult(); // ignore
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
new file mode 100644
index 0000000000..f57cda0f5f
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingCluster.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import com.alibaba.dubbo.common.Extension;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.cluster.Cluster;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * @author william.liangf
+ */
+@Extension(ForkingCluster.NAME)
+public class ForkingCluster implements Cluster {
+
+ public final static String NAME = "forking";
+
+ public Invoker merge(Directory directory) throws RpcException {
+ return new ForkingClusterInvoker(directory);
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
new file mode 100644
index 0000000000..50aa4236cd
--- /dev/null
+++ b/dubbo-cluster/src/main/java/com/alibaba/dubbo/rpc/cluster/support/ForkingClusterInvoker.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.support;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import com.alibaba.dubbo.common.Constants;
+import com.alibaba.dubbo.common.utils.NamedThreadFactory;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * 并行调用,只要一个成功即返回,通常用于实时性要求较高的操作,但需要浪费更多服务资源。
+ *
+ * @author william.liangf
+ */
+public class ForkingClusterInvoker extends AbstractClusterInvoker{
+
+ public ForkingClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ private final ExecutorService executor = Executors.newCachedThreadPool(new NamedThreadFactory("forking-cluster-timer", true));
+
+ public Result doInvoke(final Invocation invocation, List> invokers, LoadBalance loadbalance) throws RpcException {
+ final List> selected;
+ final int forks = getUrl().getIntParameter(Constants.FORKS_KEY, Constants.DEFAULT_FORKS);
+ final int timeout = getUrl().getIntParameter(Constants.TIMEOUT_KEY, Constants.DEFAULT_TIMEOUT);
+ if (forks <= 0 || forks >= invokers.size()) {
+ selected = invokers;
+ } else {
+ selected = new ArrayList>();
+ for (int i = 0; i < forks; i++) {
+ //在invoker列表(排除selected)后,如果没有选够,则存在重复循环问题.见select实现.
+ Invoker invoker = select(loadbalance, invocation, invokers, selected);
+ if(!selected.contains(invoker)){//防止重复添加invoker
+ selected.add(invoker);
+ }
+ }
+ }
+ final AtomicInteger count = new AtomicInteger();
+ final BlockingQueue ref = new LinkedBlockingQueue();
+ for (final Invoker invoker : selected) {
+ executor.execute(new Runnable() {
+ public void run() {
+ try {
+ Result result = invoker.invoke(invocation);
+ ref.offer(result);
+ } catch(Throwable e) {
+ int value = count.incrementAndGet();
+ if (value >= selected.size()) {
+ ref.offer(new RpcResult(e));
+ }
+ }
+ }
+ });
+ }
+ try {
+ return ref.poll(timeout, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RpcException("Failed to forking invoke provider " + selected + ", cause: " + e.getMessage(), e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
new file mode 100644
index 0000000000..f952821cab
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.Cluster
@@ -0,0 +1,6 @@
+com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
+com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
+com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
+com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
+com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
+com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
new file mode 100644
index 0000000000..6bf44c0d97
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.LoadBalance
@@ -0,0 +1,4 @@
+com.alibaba.dubbo.rpc.cluster.loadbalance.LoadBalanceAdptive
+com.alibaba.dubbo.rpc.cluster.loadbalance.RandomLoadBalance
+com.alibaba.dubbo.rpc.cluster.loadbalance.RoundRobinLoadBalance
+com.alibaba.dubbo.rpc.cluster.loadbalance.LeastActiveLoadBalance
\ No newline at end of file
diff --git a/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory
new file mode 100644
index 0000000000..a5021fb88b
--- /dev/null
+++ b/dubbo-cluster/src/main/resources/META-INF/services/com.alibaba.dubbo.rpc.cluster.RouterFactory
@@ -0,0 +1,2 @@
+com.alibaba.dubbo.rpc.cluster.router.FileRouterFactory
+com.alibaba.dubbo.rpc.cluster.router.ScriptRouterFactory
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
new file mode 100644
index 0000000000..05ebe88d84
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/StickyTest.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.easymock.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+@SuppressWarnings("unchecked")
+public class StickyTest {
+
+ List> invokers = new ArrayList>();
+
+
+ Invoker invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory dic ;
+ Result result = new RpcResult();
+ StickyClusterInvoker clusterinvoker = null;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ dic = EasyMock.createMock(Directory.class);
+ invocation = EasyMock.createMock(Invocation.class);
+
+ EasyMock.expect(dic.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(dic.list(invocation)).andReturn(invokers).anyTimes();
+ EasyMock.expect(dic.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(dic);
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+
+ clusterinvoker = new StickyClusterInvoker(dic);
+ }
+ URL url = URL.valueOf("test://test:11/test?"
+ +"&loadbalance=roundrobin"
+// +"&"+RpcConstants.CLUSTER_AVAILABLE_CHECK_KEY+"=true"
+ +"&"+RpcConstants.CLUSTER_STICKY_KEY+"=true"
+ );
+
+ int runs = 1;
+ @Test
+ public void testStickyNoCheck() {
+ int count = testSticky(null,false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testStickyForceCheck() {
+ int count = testSticky(null,true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+ @Test
+ public void testMethodStickyNoCheck() {
+ int count = testSticky("method1",false);
+ System.out.println(count);
+ Assert.assertTrue(count>0 && count <=runs);
+ }
+
+ @Test
+ public void testMethodStickyForceCheck() {
+ int count = testSticky("method1",true);
+ Assert.assertTrue(count == 0 || count == runs);
+ }
+
+ @Test
+ public void testMethodsSticky() {
+ for(int i = 0 ;i<100 ; i++){//多次调用看两个方法是否都选在同一个invoker
+ int count1 = testSticky("method1",true);
+ int count2 = testSticky("method2",true);
+ Assert.assertTrue(count1 == count2);
+ }
+ }
+
+ public int testSticky(String methodName, boolean check) {
+ if (methodName == null){
+ url = url.addParameter(RpcConstants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }else {
+ url = url.addParameter(methodName+"."+RpcConstants.CLUSTER_STICKY_KEY, String.valueOf(check));
+ }
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(StickyTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+
+ EasyMock.reset(invocation);
+ EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+ EasyMock.replay(invocation);
+
+ int count = 0;
+ for (int i = 0; i < runs; i++) {
+ Assert.assertEquals(null, clusterinvoker.invoke(invocation));
+ if(invoker1 == clusterinvoker.getSelectedInvoker()){
+ count ++;
+ }
+ }
+ return count;
+ }
+
+
+ static class StickyClusterInvoker extends AbstractClusterInvoker{
+ private Invoker selectedInvoker ;
+ public StickyClusterInvoker(Directory directory) {
+ super(directory);
+ }
+ public StickyClusterInvoker(Directory directory,URL url) {
+ super(directory, url);
+ }
+ @Override
+ protected Result doInvoke(Invocation invocation, List> invokers,
+ LoadBalance loadbalance) throws RpcException {
+ Invoker invoker = select(loadbalance, invocation, invokers, null);
+ selectedInvoker = invoker ;
+ return null;
+ }
+ public Invoker getSelectedInvoker() {
+ return selectedInvoker;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
new file mode 100644
index 0000000000..373e7d1de0
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoService.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * TestService
+ */
+
+public interface DemoService
+{
+ String sayHello(String name);
+
+ int plus(int a,int b);
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
new file mode 100644
index 0000000000..8f2902849c
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceLocal.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * TestService
+ */
+
+public class DemoServiceLocal implements DemoService {
+
+ public DemoServiceLocal(DemoService demoService){
+ }
+
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a + b;
+ }
+
+ public void ondisconnect(){
+
+ }
+
+ public void onconnect(){
+
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
new file mode 100644
index 0000000000..b1b7b4ab88
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceMock.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * MockService.java
+ * @author tony.chenl
+ */
+public class DemoServiceMock implements DemoService{
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a+b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
new file mode 100644
index 0000000000..c9bcb6cfb5
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/DemoServiceStub.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * TestService
+ */
+
+public class DemoServiceStub implements DemoService {
+
+ public DemoServiceStub(DemoService demoService){
+ }
+
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a + b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
new file mode 100644
index 0000000000..a8a2d3a0e6
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/filter/MockService.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.filter;
+
+/**
+ * MockService.java
+ * @author tony.chenl
+ */
+public class MockService implements DemoService{
+ public String sayHello(String name) {
+ return name;
+ }
+
+ public int plus(int a, int b) {
+ return a+b;
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
new file mode 100644
index 0000000000..7ca6ec1e9d
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/loadbalance/LoadBalanceTest.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.loadbalance;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.jboss.netty.util.internal.ConcurrentHashMap;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+
+/**
+ * RoundRobinLoadBalanceTest
+ * @author liuchao
+ *
+ */
+@SuppressWarnings({ "unchecked", "rawtypes" })
+public class LoadBalanceTest {
+ Invocation invocation ;
+ List> invokers = new ArrayList>();
+ Invoker invoker1 ;
+ Invoker invoker2 ;
+ Invoker invoker3 ;
+ Invoker invoker4 ;
+ Invoker invoker5 ;
+ /**
+ * @throws java.lang.Exception
+ */
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ /**
+ * @throws java.lang.Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+
+
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn("method1").anyTimes();
+
+ invoker1 = EasyMock.createMock(Invoker.class);
+ invoker2 = EasyMock.createMock(Invoker.class);
+ invoker3 = EasyMock.createMock(Invoker.class);
+ invoker4 = EasyMock.createMock(Invoker.class);
+ invoker5 = EasyMock.createMock(Invoker.class);
+
+ URL url = URL.valueOf("test://127.0.0.1/DemoService");
+
+ EasyMock.expect(invoker1.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker2.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker3.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker3.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker3.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker4.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker4.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker4.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.expect(invoker5.isAvailable()).andReturn(true).anyTimes();
+ EasyMock.expect(invoker5.getInterface()).andReturn(LoadBalanceTest.class).anyTimes();
+ EasyMock.expect(invoker5.getUrl()).andReturn(url).anyTimes();
+
+ EasyMock.replay(invocation,invoker1,invoker2,invoker3,invoker4,invoker5);
+
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ invokers.add(invoker4);
+ invokers.add(invoker5);
+ }
+
+ @Test
+ public void testRoundRobinLoadBalance_select() {
+ int runs = 10000;
+ Map counter = getInvokeCounter(runs,RoundRobinLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+ Assert.assertTrue("abs diff shoud < 1", Math.abs(count-runs/(0f+invokers.size())) <1f);
+ }
+ }
+ @Test
+ public void testRandomLoadBalance_select() {
+ int runs = 1000;
+ Map counter = getInvokeCounter(runs,RandomLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) counter = getInvokeCounter(runs,LeastActiveLoadBalance.NAME);
+ for (Invoker minvoker :counter.keySet() ){
+ Long count = counter.get(minvoker).get();
+// System.out.println(count);
+ Assert.assertTrue("abs diff shoud < avg", Math.abs(count-runs/(0f+invokers.size())) getInvokeCounter(int runs,String loadbalanceName) {
+ Map counter = new ConcurrentHashMap();
+ LoadBalance lb = ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+ for(Invoker invoker :invokers){
+ counter.put(invoker, new AtomicLong(0));
+ }
+ for(int i=0;i implements Invoker {
+ private boolean available = false;
+ private URL url ;
+
+ public MockInvoker() {
+ }
+ public MockInvoker(URL url) {
+ super();
+ this.url = url;
+ }
+ public MockInvoker(boolean available) {
+ this.available = available;
+ }
+
+ public Class getInterface() {
+ return null;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isAvailable() {
+ return available;
+ }
+
+ public Result invoke(Invocation invocation) throws RpcException {
+ return null;
+ }
+
+ public void destroy() {
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
new file mode 100644
index 0000000000..83adb88c53
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/ScriptRouterEngineTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router;
+
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.RpcConstants;
+import com.alibaba.dubbo.rpc.RpcInvocation;
+import com.alibaba.dubbo.rpc.cluster.Router;
+
+public class ScriptRouterEngineTest {
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ }
+
+ private URL SCRIPT_URL = URL.valueOf("script://javascript?type=javascript");
+
+ private URL getRouteUrl(String rule) {
+ return SCRIPT_URL.addParameterAndEncoded(RpcConstants.RULE_KEY, rule);
+ }
+
+ @Test
+ public void testRoute_ReturnAll(){
+ Router router = new ScriptRouterFactory().getRouter(getRouteUrl("function route(op1,op2){return op1} route(invokers)"));
+ List> invokers = new ArrayList>();
+ invokers.add(new MockInvoker());
+ invokers.add(new MockInvoker());
+ invokers.add(new MockInvoker());
+ List> fileredInvokers = router.route(invokers, new RpcInvocation());
+ Assert.assertEquals(invokers, fileredInvokers);
+ }
+
+ @Test
+ public void testRoute_PickInvokers(){
+ String rule = "var result = new java.util.ArrayList(invokers.size());" +
+ "for (i=0;i> invokers = new ArrayList>();
+ Invoker invoker1 = new MockInvoker(false) ;
+ Invoker invoker2 = new MockInvoker(true) ;
+ Invoker invoker3 = new MockInvoker(true) ;
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ invokers.add(invoker3);
+ List> fileredInvokers = router.route(invokers, new RpcInvocation());
+ Assert.assertEquals(2, fileredInvokers.size());
+ Assert.assertEquals(invoker2, fileredInvokers.get(0));
+ Assert.assertEquals(invoker3, fileredInvokers.get(1));
+ }
+ //TODO 异常场景测试。
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
new file mode 100644
index 0000000000..4f333d73eb
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/FileRouterEngineTest.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 1999-2011 Alibaba Group.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.dubbo.rpc.cluster.router.file;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import junit.framework.Assert;
+
+import org.easymock.EasyMock;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.alibaba.dubbo.common.ExtensionLoader;
+import com.alibaba.dubbo.common.URL;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+import com.alibaba.dubbo.rpc.Result;
+import com.alibaba.dubbo.rpc.RpcException;
+import com.alibaba.dubbo.rpc.RpcResult;
+import com.alibaba.dubbo.rpc.cluster.Directory;
+import com.alibaba.dubbo.rpc.cluster.LoadBalance;
+import com.alibaba.dubbo.rpc.cluster.RouterFactory;
+import com.alibaba.dubbo.rpc.cluster.directory.StaticDirectory;
+import com.alibaba.dubbo.rpc.cluster.support.AbstractClusterInvoker;
+
+/**
+ * @author chao.liuc
+ */
+@SuppressWarnings("unchecked")
+public class FileRouterEngineTest {
+ List> invokers = new ArrayList>();
+
+ Invoker invoker1 = EasyMock.createMock(Invoker.class);
+ Invoker invoker2 = EasyMock.createMock(Invoker.class);
+ Invocation invocation;
+ Directory dic;
+ Result result = new RpcResult();
+ private RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getAdaptiveExtension();
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ invokers.add(invoker1);
+ invokers.add(invoker2);
+ }
+
+ @Test
+ public void testRouteNotAvailable() {
+ URL url = initUrl("notAvailablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, false);
+
+ MockClusterInvoker sinvoker = new MockClusterInvoker(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteAvailable() {
+ URL url = initUrl("availablerule.javascript");
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url);
+
+ MockClusterInvoker sinvoker = new MockClusterInvoker(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+
+ @Test
+ public void testRouteByMethodName() {
+ URL url = initUrl("methodrule.javascript");
+ {
+ initInvocation("method1");
+ initDic(url);
+ initInvokers(url, true, true);
+
+ MockClusterInvoker sinvoker = new MockClusterInvoker(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker1, invoker);
+ }
+ }
+ {
+ initInvocation("method2");
+ initDic(url);
+ initInvokers(url, true, true);
+ MockClusterInvoker sinvoker = new MockClusterInvoker(
+ dic, url);
+ for (int i = 0; i < 100; i++) {
+ sinvoker.invoke(invocation);
+ Invoker invoker = sinvoker.getSelectedInvoker();
+ Assert.assertEquals(invoker2, invoker);
+ }
+ }
+ }
+
+ private URL initUrl(String filename) {
+ filename = FileRouterEngineTest.class.getResource(filename).toString();
+ URL url = URL.valueOf(filename.replaceAll("file:/", "file:///"));
+ return url;
+ }
+
+ private void initInvocation(String methodName) {
+ invocation = EasyMock.createMock(Invocation.class);
+ EasyMock.expect(invocation.getMethodName()).andReturn(methodName).anyTimes();
+ EasyMock.replay(invocation);
+ }
+
+ private void initInvokers(URL url) {
+ initInvokers(url, true, false);
+ }
+
+ private void initInvokers(URL url, boolean invoker1Status, boolean invoker2Status) {
+ EasyMock.reset(invoker1);
+ EasyMock.expect(invoker1.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker1.isAvailable()).andReturn(invoker1Status).anyTimes();
+ EasyMock.expect(invoker1.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker1.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker1);
+
+ EasyMock.reset(invoker2);
+ EasyMock.expect(invoker2.invoke(invocation)).andReturn(result).anyTimes();
+ EasyMock.expect(invoker2.isAvailable()).andReturn(invoker2Status).anyTimes();
+ EasyMock.expect(invoker2.getUrl()).andReturn(url).anyTimes();
+ EasyMock.expect(invoker2.getInterface()).andReturn(FileRouterEngineTest.class).anyTimes();
+ EasyMock.replay(invoker2);
+ }
+
+ private void initDic(URL url) {
+ dic = new StaticDirectory(url, invokers, Arrays.asList(routerFactory.getRouter(url)));
+ }
+
+ static class MockClusterInvoker extends AbstractClusterInvoker {
+ private Invoker selectedInvoker;
+
+ public MockClusterInvoker(Directory directory) {
+ super(directory);
+ }
+
+ public MockClusterInvoker(Directory directory, URL url) {
+ super(directory, url);
+ }
+
+ @Override
+ protected Result doInvoke(Invocation invocation, List> invokers,
+ LoadBalance loadbalance) throws RpcException {
+ Invoker invoker = select(loadbalance, invocation, invokers, null);
+ selectedInvoker = invoker;
+ return null;
+ }
+
+ public Invoker getSelectedInvoker() {
+ return selectedInvoker;
+ }
+ }
+}
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
new file mode 100644
index 0000000000..c76de32ee4
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/availablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i1 && invocation.getMethodName() .equals("method1")) {
+ result.add(invokers.get(0)) ;
+ } else {
+ result.add(invokers.get(1)) ;
+ }
+ return result;
+};
+route(invokers,invocation,context);
\ No newline at end of file
diff --git a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
new file mode 100644
index 0000000000..1fcdcaeb0d
--- /dev/null
+++ b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/router/file/notAvailablerule.javascript
@@ -0,0 +1,11 @@
+function route(invokers,invocation,context){
+ var result = new java.util.ArrayList(invokers.size());
+
+ for (i=0;i> invokers = new ArrayList>();
+ List> selectedInvokers = new ArrayList>();
+ AbstractClusterInvoker cluster;
+ AbstractClusterInvoker cluster_nocheck;
+ Directory