This is an automated email from the ASF dual-hosted git repository.

iluo pushed a commit to branch 2.6.x
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git


The following commit(s) were added to refs/heads/2.6.x by this push:
     new e4725cd  Smooth Round Robin selection (#2647)
e4725cd is described below

commit e4725cd687e52637e7c6a107e9dc6840d1fbac75
Author: jasonjoo2010 <hblz...@163.com>
AuthorDate: Fri Oct 26 11:35:17 2018 +0800

    Smooth Round Robin selection (#2647)
---
 .../cluster/loadbalance/RoundRobinLoadBalance.java | 166 ++++++++-----
 .../com/alibaba/dubbo/rpc/cluster/StickyTest.java  |   6 +-
 .../rpc/cluster/loadbalance/LoadBalanceTest.java   | 262 +++++++++++++++++----
 .../support/AbstractClusterInvokerTest.java        |  10 +-
 4 files changed, 333 insertions(+), 111 deletions(-)

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
index 9d4d026..fa03e9d 100644
--- 
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
@@ -17,87 +17,141 @@
 package com.alibaba.dubbo.rpc.cluster.loadbalance;
 
 import com.alibaba.dubbo.common.URL;
-import com.alibaba.dubbo.common.utils.AtomicPositiveInteger;
 import com.alibaba.dubbo.rpc.Invocation;
 import com.alibaba.dubbo.rpc.Invoker;
 
-import java.util.LinkedHashMap;
+import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
 
 /**
  * Round robin load balance.
+ * 
+ * Smoothly round robin's implementation @since 2.6.5 
+ * @author jason
  *
  */
 public class RoundRobinLoadBalance extends AbstractLoadBalance {
-
     public static final String NAME = "roundrobin";
+    
+    private static int RECYCLE_PERIOD = 60000;
+    
+    protected static class WeightedRoundRobin {
+        private int weight;
+        private AtomicLong current = new AtomicLong(0);
+        private long lastUpdate;
+        public int getWeight() {
+            return weight;
+        }
+        public void setWeight(int weight) {
+            this.weight = weight;
+            current.set(0);
+        }
+        public long increaseCurrent() {
+            return current.addAndGet(weight);
+        }
+        public void sel(int total) {
+            current.addAndGet(-1 * total);
+        }
+        public long getLastUpdate() {
+            return lastUpdate;
+        }
+        public void setLastUpdate(long lastUpdate) {
+            this.lastUpdate = lastUpdate;
+        }
+    }
 
-    private final ConcurrentMap<String, AtomicPositiveInteger> sequences = new 
ConcurrentHashMap<String, AtomicPositiveInteger>();
-
+    private ConcurrentMap<String, ConcurrentMap<String, WeightedRoundRobin>> 
methodWeightMap = new ConcurrentHashMap<String, ConcurrentMap<String, 
WeightedRoundRobin>>();
+    private AtomicBoolean updateLock = new AtomicBoolean();
+    
+    /**
+     * get invoker addr list cached for specified invocation
+     * <p>
+     * <b>for unit test only</b>
+     * 
+     * @param invokers
+     * @param invocation
+     * @return
+     */
+    protected <T> Collection<String> getInvokerAddrList(List<Invoker<T>> 
invokers, Invocation invocation) {
+        String key = invokers.get(0).getUrl().getServiceKey() + "." + 
invocation.getMethodName();
+        Map<String, WeightedRoundRobin> map = methodWeightMap.get(key);
+        if (map != null) {
+            return map.keySet();
+        }
+        return null;
+    }
+    
     @Override
     protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, 
Invocation invocation) {
         String key = invokers.get(0).getUrl().getServiceKey() + "." + 
invocation.getMethodName();
-        int length = invokers.size(); // Number of invokers
-        int maxWeight = 0; // The maximum weight
-        int minWeight = Integer.MAX_VALUE; // The minimum weight
-        final LinkedHashMap<Invoker<T>, IntegerWrapper> invokerToWeightMap = 
new LinkedHashMap<Invoker<T>, IntegerWrapper>();
-        int weightSum = 0;
-        for (int i = 0; i < length; i++) {
-            int weight = getWeight(invokers.get(i), invocation);
-            maxWeight = Math.max(maxWeight, weight); // Choose the maximum 
weight
-            minWeight = Math.min(minWeight, weight); // Choose the minimum 
weight
-            if (weight > 0) {
-                invokerToWeightMap.put(invokers.get(i), new 
IntegerWrapper(weight));
-                weightSum += weight;
-            }
+        ConcurrentMap<String, WeightedRoundRobin> map = 
methodWeightMap.get(key);
+        if (map == null) {
+            methodWeightMap.putIfAbsent(key, new ConcurrentHashMap<String, 
WeightedRoundRobin>());
+            map = methodWeightMap.get(key);
         }
-        AtomicPositiveInteger sequence = sequences.get(key);
-        if (sequence == null) {
-            sequences.putIfAbsent(key, new AtomicPositiveInteger());
-            sequence = sequences.get(key);
+        int totalWeight = 0;
+        long maxCurrent = Long.MIN_VALUE;
+        long now = System.currentTimeMillis();
+        Invoker<T> selectedInvoker = null;
+        WeightedRoundRobin selectedWRR = null;
+        for (Invoker<T> invoker : invokers) {
+            String identifyString = invoker.getUrl().toIdentityString();
+            WeightedRoundRobin weightedRoundRobin = map.get(identifyString);
+            int weight = getWeight(invoker, invocation);
+            if (weight < 0) {
+                weight = 0;
+            }
+            if (weightedRoundRobin == null) {
+                weightedRoundRobin = new WeightedRoundRobin();
+                weightedRoundRobin.setWeight(weight);
+                map.putIfAbsent(identifyString, weightedRoundRobin);
+                weightedRoundRobin = map.get(identifyString);
+            }
+            if (weight != weightedRoundRobin.getWeight()) {
+                //weight changed
+                weightedRoundRobin.setWeight(weight);
+            }
+            long cur = weightedRoundRobin.increaseCurrent();
+            weightedRoundRobin.setLastUpdate(now);
+            if (cur > maxCurrent) {
+                maxCurrent = cur;
+                selectedInvoker = invoker;
+                selectedWRR = weightedRoundRobin;
+            }
+            totalWeight += weight;
         }
-        int currentSequence = sequence.getAndIncrement();
-        if (maxWeight > 0 && minWeight < maxWeight) {
-            int mod = currentSequence % weightSum;
-            for (int i = 0; i < maxWeight; i++) {
-                for (Map.Entry<Invoker<T>, IntegerWrapper> each : 
invokerToWeightMap.entrySet()) {
-                    final Invoker<T> k = each.getKey();
-                    final IntegerWrapper v = each.getValue();
-                    if (mod == 0 && v.getValue() > 0) {
-                        return k;
-                    }
-                    if (v.getValue() > 0) {
-                        v.decrement();
-                        mod--;
+        if (!updateLock.get() && invokers.size() != map.size()) {
+            if (updateLock.compareAndSet(false, true)) {
+                try {
+                    // copy -> modify -> update reference
+                    ConcurrentMap<String, WeightedRoundRobin> newMap = new 
ConcurrentHashMap<String, WeightedRoundRobin>();
+                    newMap.putAll(map);
+                    Iterator<Entry<String, WeightedRoundRobin>> it = 
newMap.entrySet().iterator();
+                    while (it.hasNext()) {
+                        Entry<String, WeightedRoundRobin> item = it.next();
+                        if (now - item.getValue().getLastUpdate() > 
RECYCLE_PERIOD) {
+                            it.remove();
+                        }
                     }
+                    methodWeightMap.put(key, newMap);
+                } finally {
+                    updateLock.set(false);
                 }
             }
         }
-        // Round robin
-        return invokers.get(currentSequence % length);
-    }
-
-    private static final class IntegerWrapper {
-        private int value;
-
-        public IntegerWrapper(int value) {
-            this.value = value;
-        }
-
-        public int getValue() {
-            return value;
-        }
-
-        public void setValue(int value) {
-            this.value = value;
-        }
-
-        public void decrement() {
-            this.value--;
+        if (selectedInvoker != null) {
+            selectedWRR.sel(totalWeight);
+            return selectedInvoker;
         }
+        // should not happen here
+        return invokers.get(0);
     }
 
 }
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
index 0800038..b3a0225 100644
--- 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
@@ -114,12 +114,12 @@ public class StickyTest {
 
         given(invoker1.invoke(invocation)).willReturn(result);
         given(invoker1.isAvailable()).willReturn(true);
-        given(invoker1.getUrl()).willReturn(url);
+        given(invoker1.getUrl()).willReturn(url.setPort(1));
         given(invoker1.getInterface()).willReturn(StickyTest.class);
 
         given(invoker2.invoke(invocation)).willReturn(result);
         given(invoker2.isAvailable()).willReturn(true);
-        given(invoker2.getUrl()).willReturn(url);
+        given(invoker2.getUrl()).willReturn(url.setPort(2));
         given(invoker2.getInterface()).willReturn(StickyTest.class);
 
         invocation.setMethodName(methodName);
@@ -158,4 +158,4 @@ public class StickyTest {
             return selectedInvoker;
         }
     }
-}
\ 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
index bdede14..d231c63 100644
--- 
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
@@ -24,15 +24,21 @@ import com.alibaba.dubbo.rpc.Invoker;
 import com.alibaba.dubbo.rpc.RpcInvocation;
 import com.alibaba.dubbo.rpc.RpcStatus;
 import com.alibaba.dubbo.rpc.cluster.LoadBalance;
-import junit.framework.Assert;
+import com.alibaba.fastjson.JSON;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicLong;
 
 import static org.mockito.BDDMockito.given;
@@ -55,7 +61,7 @@ public class LoadBalanceTest {
     RpcStatus weightTestRpcStatus2;
     RpcStatus weightTestRpcStatus3;
     RpcInvocation weightTestInvocation;
-
+    
     /**
      * @throws java.lang.Exception
      */
@@ -110,7 +116,11 @@ public class LoadBalanceTest {
         invokers.add(invoker4);
         invokers.add(invoker5);
     }
-
+    
+    private AbstractLoadBalance getLoadBalance(String loadbalanceName) {
+        return (AbstractLoadBalance) 
ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+    }
+    
     @Test
     public void testRoundRobinLoadBalance_select() {
         int runs = 10000;
@@ -120,6 +130,120 @@ public class LoadBalanceTest {
             Assert.assertTrue("abs diff shoud < 1", Math.abs(count - runs / 
(0f + invokers.size())) < 1f);
         }
     }
+    
+    private void assertStrictWRRResult(int runs, Map<Invoker, InvokeResult> 
resultMap) {
+        for (InvokeResult invokeResult : resultMap.values()) {
+            // Because it's a strictly round robin, so the abs delta should be 
< 10 too
+            Assert.assertTrue("delta with expected count should < 10", 
+                    Math.abs(invokeResult.getExpected(runs) - 
invokeResult.getCount().get()) < 10);
+        }
+    }
+    
+    /**
+     * a multi-threaded test on weighted round robin
+     */
+    @Test
+    public void testRoundRobinLoadBalanceWithWeight() {
+        final Map<Invoker, InvokeResult> totalMap = new HashMap<Invoker, 
InvokeResult>();
+        final AtomicBoolean shouldBegin = new AtomicBoolean(false);
+        final int runs = 10000;
+        List<Thread> threads = new ArrayList<Thread>();
+        int threadNum = 10;
+        for (int i = 0; i < threadNum; i ++) {
+            threads.add(new Thread() {
+                @Override
+                public void run() {
+                    while (!shouldBegin.get()) {
+                        try {
+                            sleep(5);
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                    Map<Invoker, InvokeResult> resultMap = 
getWeightedInvokeResult(runs, RoundRobinLoadBalance.NAME);
+                    synchronized (totalMap) {
+                        for (Entry<Invoker, InvokeResult> entry : 
resultMap.entrySet()) {
+                            if (!totalMap.containsKey(entry.getKey())) {
+                                totalMap.put(entry.getKey(), entry.getValue());
+                            } else {
+                                
totalMap.get(entry.getKey()).getCount().addAndGet(entry.getValue().getCount().get());
+                            }
+                        }
+                    }
+                }
+            });
+        }
+        for (Thread thread : threads) {
+            thread.start();
+        }
+        // let's rock it!
+        shouldBegin.set(true);
+        for (Thread thread : threads) {
+            try {
+                thread.join();
+            } catch (InterruptedException e) {
+            }
+        }
+        assertStrictWRRResult(runs * threadNum, totalMap);
+    }
+    
+    @Test
+    public void testRoundRobinLoadBalanceWithWeightShouldNotRecycle() {
+        int runs = 10000;
+        //tmperately add a new invoker
+        weightInvokers.add(weightInvokerTmp);
+        try {
+            Map<Invoker, InvokeResult> resultMap = 
getWeightedInvokeResult(runs, RoundRobinLoadBalance.NAME);
+            assertStrictWRRResult(runs, resultMap);
+            RoundRobinLoadBalance lb = 
(RoundRobinLoadBalance)getLoadBalance(RoundRobinLoadBalance.NAME);
+            Assert.assertEquals(weightInvokers.size(), 
lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size());
+            
+            //remove the last invoker and retry
+            weightInvokers.remove(weightInvokerTmp);
+            resultMap = getWeightedInvokeResult(runs, 
RoundRobinLoadBalance.NAME);
+            assertStrictWRRResult(runs, resultMap);
+            Assert.assertNotEquals(weightInvokers.size(), 
lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size());
+        } finally {
+            weightInvokers.remove(weightInvokerTmp);
+        }
+    }
+    
+    @Test
+    public void testRoundRobinLoadBalanceWithWeightShouldRecycle() {
+        {
+            Field recycleTimeField = null;
+            try {
+                //change recycle time to 1 ms
+                recycleTimeField = 
RoundRobinLoadBalance.class.getDeclaredField("RECYCLE_PERIOD");
+                recycleTimeField.setAccessible(true);
+                recycleTimeField.setInt(RoundRobinLoadBalance.class, 10);
+            } catch (NoSuchFieldException e) {
+                Assert.assertTrue("getField failed", true);
+            } catch (SecurityException e) {
+                Assert.assertTrue("getField failed", true);
+            } catch (IllegalArgumentException e) {
+                Assert.assertTrue("getField failed", true);
+            } catch (IllegalAccessException e) {
+                Assert.assertTrue("getField failed", true);
+            }
+        }
+        int runs = 10000;
+        //temporarily add a new invoker
+        weightInvokers.add(weightInvokerTmp);
+        try {
+            Map<Invoker, InvokeResult> resultMap = 
getWeightedInvokeResult(runs, RoundRobinLoadBalance.NAME);
+            assertStrictWRRResult(runs, resultMap);
+            RoundRobinLoadBalance lb = 
(RoundRobinLoadBalance)getLoadBalance(RoundRobinLoadBalance.NAME);
+            Assert.assertEquals(weightInvokers.size(), 
lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size());
+            
+            //remove the tmp invoker and retry, should recycle its cache
+            weightInvokers.remove(weightInvokerTmp);
+            resultMap = getWeightedInvokeResult(runs, 
RoundRobinLoadBalance.NAME);
+            assertStrictWRRResult(runs, resultMap);
+            Assert.assertEquals(weightInvokers.size(), 
lb.getInvokerAddrList(weightInvokers, weightTestInvocation).size());
+        } finally {
+            weightInvokers.remove(weightInvokerTmp);
+        }
+    }
 
     @Test
     public void testSelectByWeightLeastActive() {
@@ -171,32 +295,6 @@ public class LoadBalanceTest {
     }
 
     @Test
-    public void testSelectByWeight() {
-        int sumInvoker1 = 0;
-        int sumInvoker2 = 0;
-        int sumInvoker3 = 0;
-        int loop = 10000;
-        RoundRobinLoadBalance lb = new RoundRobinLoadBalance();
-        for (int i = 0; i < loop; i++) {
-            Invoker selected = lb.select(weightInvokers, null, 
weightTestInvocation);
-            if (selected.getUrl().getProtocol().equals("test1")) {
-                sumInvoker1++;
-            }
-            if (selected.getUrl().getProtocol().equals("test2")) {
-                sumInvoker2++;
-            }
-            if (selected.getUrl().getProtocol().equals("test3")) {
-                sumInvoker3++;
-            }
-        }
-        // 1 : 9 : 6
-        System.out.println(sumInvoker1);
-        System.out.println(sumInvoker2);
-        System.out.println(sumInvoker3);
-        Assert.assertEquals("select failed!", sumInvoker1 + sumInvoker2 + 
sumInvoker3, loop);
-    }
-
-    @Test
     public void testRandomLoadBalance_select() {
         int runs = 1000;
         Map<Invoker, AtomicLong> counter = getInvokeCounter(runs, 
RandomLoadBalance.NAME);
@@ -234,15 +332,16 @@ public class LoadBalanceTest {
                     Math.abs(count - runs / (0f + invokers.size())) < runs / 
(0f + invokers.size()));
         }
     }
-
-    public Map<Invoker, AtomicLong> getInvokeCounter(int runs, String 
loadbalanceName) {
+    
+    private Map<Invoker, AtomicLong> getInvokeCounter(int runs, String 
loadbalanceName) {
         Map<Invoker, AtomicLong> counter = new ConcurrentHashMap<Invoker, 
AtomicLong>();
-        LoadBalance lb = 
ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(loadbalanceName);
+        LoadBalance lb = getLoadBalance(loadbalanceName);
         for (Invoker invoker : invokers) {
             counter.put(invoker, new AtomicLong(0));
         }
+        URL url = invokers.get(0).getUrl();
         for (int i = 0; i < runs; i++) {
-            Invoker sinvoker = lb.select(invokers, invokers.get(0).getUrl(), 
invocation);
+            Invoker sinvoker = lb.select(invokers, url, invocation);
             counter.get(sinvoker).incrementAndGet();
         }
         return counter;
@@ -279,46 +378,115 @@ public class LoadBalanceTest {
         Assert.assertEquals(100, AbstractLoadBalance
                 .calculateWarmupWeight(20 * 60 * 1000, 
Constants.DEFAULT_WARMUP, Constants.DEFAULT_WEIGHT));
     }
-
+    
     /*------------------------------------test invokers for 
weight---------------------------------------*/
 
     protected List<Invoker<LoadBalanceTest>> weightInvokers = new 
ArrayList<Invoker<LoadBalanceTest>>();
     protected Invoker<LoadBalanceTest> weightInvoker1;
     protected Invoker<LoadBalanceTest> weightInvoker2;
     protected Invoker<LoadBalanceTest> weightInvoker3;
+    protected Invoker<LoadBalanceTest> weightInvokerTmp;
 
     @Before
-    public void before() throws Exception {
+    public void setUpWeightInvokers() throws Exception {
         weightInvoker1 = mock(Invoker.class);
         weightInvoker2 = mock(Invoker.class);
         weightInvoker3 = mock(Invoker.class);
+        weightInvokerTmp = mock(Invoker.class);
+        
         weightTestInvocation = new RpcInvocation();
         weightTestInvocation.setMethodName("test");
-        URL url1 = URL.valueOf("test1://0:1/DemoService");
-        url1 = url1.addParameter(Constants.WEIGHT_KEY, 1);
-        url1 = url1.addParameter(weightTestInvocation.getMethodName() + "." + 
Constants.WEIGHT_KEY, 1);
-        url1 = url1.addParameter("active", 0);
-        URL url2 = URL.valueOf("test2://0:9/DemoService");
-        url2 = url2.addParameter(Constants.WEIGHT_KEY, 9);
-        url2 = url2.addParameter(weightTestInvocation.getMethodName() + "." + 
Constants.WEIGHT_KEY, 9);
-        url2 = url2.addParameter("active", 0);
-        URL url3 = URL.valueOf("test3://1:6/DemoService");
-        url3 = url3.addParameter(Constants.WEIGHT_KEY, 6);
-        url3 = url3.addParameter(weightTestInvocation.getMethodName() + "." + 
Constants.WEIGHT_KEY, 6);
-        url3 = url3.addParameter("active", 1);
+        
+        URL url1 = 
URL.valueOf("test1://127.0.0.1:11/DemoService?weight=11&active=0");
+        URL url2 = 
URL.valueOf("test2://127.0.0.1:12/DemoService?weight=97&active=0");
+        URL url3 = 
URL.valueOf("test3://127.0.0.1:13/DemoService?weight=67&active=1");
+        URL urlTmp = 
URL.valueOf("test4://127.0.0.1:9999/DemoService?weight=601&active=0");
+        
         given(weightInvoker1.isAvailable()).willReturn(true);
+        given(weightInvoker1.getInterface()).willReturn(LoadBalanceTest.class);
         given(weightInvoker1.getUrl()).willReturn(url1);
+        
         given(weightInvoker2.isAvailable()).willReturn(true);
+        given(weightInvoker2.getInterface()).willReturn(LoadBalanceTest.class);
         given(weightInvoker2.getUrl()).willReturn(url2);
+        
         given(weightInvoker3.isAvailable()).willReturn(true);
+        given(weightInvoker3.getInterface()).willReturn(LoadBalanceTest.class);
         given(weightInvoker3.getUrl()).willReturn(url3);
+        
+        given(weightInvokerTmp.isAvailable()).willReturn(true);
+        
given(weightInvokerTmp.getInterface()).willReturn(LoadBalanceTest.class);
+        given(weightInvokerTmp.getUrl()).willReturn(urlTmp);
+        
         weightInvokers.add(weightInvoker1);
         weightInvokers.add(weightInvoker2);
         weightInvokers.add(weightInvoker3);
+        
         weightTestRpcStatus1 = RpcStatus.getStatus(weightInvoker1.getUrl(), 
weightTestInvocation.getMethodName());
         weightTestRpcStatus2 = RpcStatus.getStatus(weightInvoker2.getUrl(), 
weightTestInvocation.getMethodName());
         weightTestRpcStatus3 = RpcStatus.getStatus(weightInvoker3.getUrl(), 
weightTestInvocation.getMethodName());
+        
         // weightTestRpcStatus3 active is 1
         RpcStatus.beginCount(weightInvoker3.getUrl(), 
weightTestInvocation.getMethodName());
     }
+    
+    private static class InvokeResult {
+        private AtomicLong count = new AtomicLong();
+        private int weight = 0;
+        private int totalWeight = 0;
+
+        public InvokeResult(int weight) {
+            this.weight = weight;
+        }
+
+        public AtomicLong getCount() {
+            return count;
+        }
+        
+        public int getWeight() {
+            return weight;
+        }
+        
+        public int getTotalWeight() {
+            return totalWeight;
+        }
+        
+        public void setTotalWeight(int totalWeight) {
+            this.totalWeight = totalWeight;
+        }
+        
+        public int getExpected(int runCount) {
+            return getWeight() * runCount / getTotalWeight();
+        }
+        
+        public float getDeltaPercentage(int runCount) {
+            int expected = getExpected(runCount);
+            return Math.abs((expected - getCount().get()) * 100.0f / expected);
+        }
+        
+        @Override
+        public String toString() {
+            return JSON.toJSONString(this);
+        }
+    }
+    
+    private Map<Invoker, InvokeResult> getWeightedInvokeResult(int runs, 
String loadbalanceName) {
+        Map<Invoker, InvokeResult> counter = new ConcurrentHashMap<Invoker, 
InvokeResult>();
+        AbstractLoadBalance lb = getLoadBalance(loadbalanceName);
+        int totalWeight = 0;
+        for (int i = 0; i < weightInvokers.size(); i ++) {
+            InvokeResult invokeResult = new 
InvokeResult(lb.getWeight(weightInvokers.get(i), weightTestInvocation));
+            counter.put(weightInvokers.get(i), invokeResult);
+            totalWeight += invokeResult.getWeight();
+        }
+        for (InvokeResult invokeResult : counter.values()) {
+            invokeResult.setTotalWeight(totalWeight);
+        }
+        for (int i = 0; i < runs; i++) {
+            Invoker sinvoker = lb.select(weightInvokers, 
weightInvokers.get(0).getUrl(), weightTestInvocation);
+            counter.get(sinvoker).getCount().incrementAndGet();
+        }
+        return counter;
+    }
+
 }
\ No newline at end of file
diff --git 
a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
index cac83e9..8883ed6 100644
--- 
a/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
+++ 
b/dubbo-cluster/src/test/java/com/alibaba/dubbo/rpc/cluster/support/AbstractClusterInvokerTest.java
@@ -91,23 +91,23 @@ public class AbstractClusterInvokerTest {
 
         given(invoker1.isAvailable()).willReturn(false);
         given(invoker1.getInterface()).willReturn(IHelloService.class);
-        given(invoker1.getUrl()).willReturn(turl.addParameter("name", 
"invoker1"));
+        
given(invoker1.getUrl()).willReturn(turl.setPort(1).addParameter("name", 
"invoker1"));
 
         given(invoker2.isAvailable()).willReturn(true);
         given(invoker2.getInterface()).willReturn(IHelloService.class);
-        given(invoker2.getUrl()).willReturn(turl.addParameter("name", 
"invoker2"));
+        
given(invoker2.getUrl()).willReturn(turl.setPort(2).addParameter("name", 
"invoker2"));
 
         given(invoker3.isAvailable()).willReturn(false);
         given(invoker3.getInterface()).willReturn(IHelloService.class);
-        given(invoker3.getUrl()).willReturn(turl.addParameter("name", 
"invoker3"));
+        
given(invoker3.getUrl()).willReturn(turl.setPort(3).addParameter("name", 
"invoker3"));
 
         given(invoker4.isAvailable()).willReturn(true);
         given(invoker4.getInterface()).willReturn(IHelloService.class);
-        given(invoker4.getUrl()).willReturn(turl.addParameter("name", 
"invoker4"));
+        
given(invoker4.getUrl()).willReturn(turl.setPort(4).addParameter("name", 
"invoker4"));
 
         given(invoker5.isAvailable()).willReturn(false);
         given(invoker5.getInterface()).willReturn(IHelloService.class);
-        given(invoker5.getUrl()).willReturn(turl.addParameter("name", 
"invoker5"));
+        
given(invoker5.getUrl()).willReturn(turl.setPort(5).addParameter("name", 
"invoker5"));
 
         given(mockedInvoker1.isAvailable()).willReturn(false);
         given(mockedInvoker1.getInterface()).willReturn(IHelloService.class);

Reply via email to