This is an automated email from the ASF dual-hosted git repository. liubao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-java-chassis.git
commit 5ad67e04fa299b1ee07f7783380885f76ced80b2 Author: liubao <[email protected]> AuthorDate: Sat Aug 25 17:50:52 2018 +0800 [SBC-870] change weights to Robin Weights and change algorithm --- .../servicecomb/loadbalance/LoadBalancer.java | 5 +- .../loadbalance/LoadbalanceHandler.java | 8 +- .../loadbalance/ServiceCombLoadBalancerStats.java | 11 +- .../loadbalance/ServiceCombServerStats.java | 19 +--- .../loadbalance/WeightedResponseTimeRuleExt.java | 76 +++++++++---- .../loadbalance/TestLoadBalanceHandler2.java | 16 +-- .../loadbalance/TestRoundRobinRuleExt.java | 76 +++++++++++++ .../TestServiceCombLoadBalancerStats.java | 18 +-- .../loadbalance/TestServiceCombServerStats.java | 22 ++-- .../TestWeightedResponseTimeRuleExt.java | 126 +++++++++++++++++++++ 10 files changed, 302 insertions(+), 75 deletions(-) diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java index 463ea19..bcc9fea 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadBalancer.java @@ -18,6 +18,7 @@ package org.apache.servicecomb.loadbalance; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils; @@ -29,6 +30,8 @@ import com.netflix.loadbalancer.LoadBalancerStats; * A load balancer with RuleExt and ServerListFilterExt */ public class LoadBalancer { + private static AtomicInteger id = new AtomicInteger(0); + private RuleExt rule; private LoadBalancerStats lbStats; @@ -40,7 +43,7 @@ public class LoadBalancer { public LoadBalancer(RuleExt rule, String microServiceName) { this.microServiceName = microServiceName; this.rule = rule; - this.lbStats = new LoadBalancerStats(null); + this.lbStats = new LoadBalancerStats(microServiceName + id.getAndDecrement()); // load new instances, because filters work on service information this.filters = SPIServiceUtils.loadSortedService(ServerListFilterExt.class); this.rule.setLoadBalancer(this); diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java index 30ee21b..95db713 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/LoadbalanceHandler.java @@ -205,10 +205,10 @@ public class LoadbalanceHandler implements Handler { chosenLB.getLoadBalancerStats().noteResponseTime(server, (System.currentTimeMillis() - time)); if (isFailedResponse(resp)) { chosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(server); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(server, System.currentTimeMillis() - time); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(server); } else { chosenLB.getLoadBalancerStats().incrementActiveRequestsCount(server); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, (System.currentTimeMillis() - time)); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); } asyncResp.handle(resp); }); @@ -315,13 +315,13 @@ public class LoadbalanceHandler implements Handler { ((Throwable) resp.getResult()).getMessage(), s); chosenLB.getLoadBalancerStats().incrementSuccessiveConnectionFailureCount(s); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(server, System.currentTimeMillis() - time); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(server); f.onError(resp.getResult()); } else { chosenLB.getLoadBalancerStats().incrementActiveRequestsCount(s); chosenLB.getLoadBalancerStats().noteResponseTime(s, (System.currentTimeMillis() - time)); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, (System.currentTimeMillis() - time)); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); f.onNext(resp); f.onCompleted(); } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombLoadBalancerStats.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombLoadBalancerStats.java index ac01152..ffde2d7 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombLoadBalancerStats.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombLoadBalancerStats.java @@ -78,17 +78,17 @@ public class ServiceCombLoadBalancerStats { } } - public void markSuccess(ServiceCombServer server, long execTime) { + public void markSuccess(ServiceCombServer server) { try { - serverStatsCache.get(server).markSuccess(execTime); + serverStatsCache.get(server).markSuccess(); } catch (ExecutionException e) { LOGGER.error("Not expected to happen, maybe a bug.", e); } } - public void markFailure(ServiceCombServer server, long execTime) { + public void markFailure(ServiceCombServer server) { try { - serverStatsCache.get(server).markFailure(execTime); + serverStatsCache.get(server).markFailure(); } catch (ExecutionException e) { LOGGER.error("Not expected to happen, maybe a bug.", e); } @@ -167,11 +167,10 @@ public class ServiceCombLoadBalancerStats { while (instances.hasNext()) { ServiceCombServer server = instances.next(); ServiceCombServerStats stats = allServers.get(server); - long startTime = System.currentTimeMillis(); if ((System.currentTimeMillis() - stats.getLastVisitTime() > timerIntervalInMilis) && !ping .ping(server.getInstance())) { LOGGER.info("ping mark server {} failure.", server.getInstance().getInstanceId()); - stats.markFailure(System.currentTimeMillis() - startTime); + stats.markFailure(); } } serverStatsCache.cleanUp(); diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServerStats.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServerStats.java index 03b458e..b33771b 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServerStats.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/ServiceCombServerStats.java @@ -45,47 +45,33 @@ public class ServiceCombServerStats { private boolean isolated = false; - private long averageResponseTime = 0; - - public long getAverageResponseTime() { - return averageResponseTime; - } - public void markIsolated(boolean isolated) { this.isolated = isolated; } - public void markSuccess(long execTime) { + public void markSuccess() { long time = System.currentTimeMillis(); ensureWindow(time); lastVisitTime = time; lastActiveTime = time; - updateAvgTime(execTime); totalRequests.incrementAndGet(); successRequests.incrementAndGet(); continuousFailureCount.set(0); } - public void markFailure(long execTime) { + public void markFailure() { long time = System.currentTimeMillis(); ensureWindow(time); lastVisitTime = time; // when isolated, do not update any failure statistics, or we can not recover from failure very quickly if (!isolated) { - updateAvgTime(execTime); totalRequests.incrementAndGet(); failedRequests.incrementAndGet(); continuousFailureCount.incrementAndGet(); } } - private void updateAvgTime(long execTime) { - // There maybe concurrent problems. But we do not care. - long total = totalRequests.get(); - averageResponseTime = (averageResponseTime * total + execTime) / (total + 1); - } - private void ensureWindow(long time) { if (time - lastWindow > TIME_WINDOW_IN_MILLISECONDS) { synchronized (lock) { @@ -95,7 +81,6 @@ public class ServiceCombServerStats { totalRequests.set(0); successRequests.set(0); failedRequests.set(0); - averageResponseTime = 0; } lastWindow = time; } diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java index 06aba4f..529c0a3 100644 --- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java +++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/WeightedResponseTimeRuleExt.java @@ -20,46 +20,84 @@ package org.apache.servicecomb.loadbalance; import java.util.ArrayList; import java.util.List; import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; import org.apache.servicecomb.core.Invocation; +import com.netflix.loadbalancer.ServerStats; + /** * Rule based on response time. */ public class WeightedResponseTimeRuleExt extends RoundRobinRuleExt { // 10ms - private static final int MIN_GAP = 10; + private static final double MIN_GAP = 10d; + + private static final int RANDOM_PERCENT = 10; private Random random = new Random(); + private LoadBalancer loadBalancer; + + private double totalWeightsCache = 0d; + + @Override + public void setLoadBalancer(LoadBalancer loadBalancer) { + this.loadBalancer = loadBalancer; + } + @Override public ServiceCombServer choose(List<ServiceCombServer> servers, Invocation invocation) { - List<AtomicInteger> stats = new ArrayList<>(servers.size()); - int totalWeights = 0; + List<Double> stats = calculateTotalWeights(servers); + + if (stats.size() > 0) { + double finalTotal = stats.get(stats.size() - 1); + List<Double> weights = new ArrayList<>(servers.size()); + for (int i = 0; i < stats.size() - 1; i++) { + weights.add(finalTotal - stats.get(i)); + } + double ran = random.nextDouble() * finalTotal * (servers.size() - 1); + for (int i = 0; i < weights.size(); i++) { + ran -= weights.get(i); + if (ran < 0) { + return servers.get(i); + } + } + return servers.get(servers.size() - 1); + } + return super.choose(servers, invocation); + } + + private List<Double> calculateTotalWeights(List<ServiceCombServer> servers) { + if (totalWeightsCache > MIN_GAP * servers.size()) { + return doCalculateTotalWeights(servers); + } + // 10% possibilities to use weighed response rule when the normal access is very fast. + if (random.nextInt(RANDOM_PERCENT) == 0) { + return doCalculateTotalWeights(servers); + } else { + return new ArrayList<>(); + } + } + + private List<Double> doCalculateTotalWeights(List<ServiceCombServer> servers) { + List<Double> stats = new ArrayList<>(servers.size() + 1); + double totalWeights = 0; boolean needRandom = false; for (ServiceCombServer server : servers) { - ServiceCombServerStats serverStats = ServiceCombLoadBalancerStats.INSTANCE.getServiceCombServerStats(server); - int avgTime = (int) serverStats.getAverageResponseTime(); + ServerStats serverStats = loadBalancer.getLoadBalancerStats().getSingleServerStat(server); + double avgTime = serverStats.getResponseTimeAvg(); if (!needRandom && avgTime > MIN_GAP) { needRandom = true; } totalWeights += avgTime; - stats.add(new AtomicInteger(avgTime)); + stats.add(avgTime); } - + stats.add(totalWeights); + totalWeightsCache = totalWeights; if (needRandom) { - int finalTotal = totalWeights; - stats.forEach(item -> item.set(finalTotal - item.get())); - int ran = random - .nextInt(Math.max(totalWeights * stats.size() - totalWeights, 1)); - for (int i = 0; i < stats.size(); i++) { - ran -= stats.get(i).get(); - if (ran < 0) { - return servers.get(i); - } - } + return stats; + } else { + return new ArrayList<>(); } - return super.choose(servers, invocation); } } diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceHandler2.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceHandler2.java index 3dbfa32..276e454 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceHandler2.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestLoadBalanceHandler2.java @@ -163,11 +163,11 @@ public class TestLoadBalanceHandler2 { server = (ServiceCombServer) loadBalancer.chooseServer(invocation); Assert.assertEquals(server.getEndpoint().getEndpoint(), "rest://localhost:9090"); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server, 0); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(server, 0); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(server); //if errorThresholdPercentage is 0,that means errorThresholdPercentage is not active. ArchaiusUtils.setProperty("servicecomb.loadbalance.isolation.errorThresholdPercentage", "0"); @@ -181,12 +181,12 @@ public class TestLoadBalanceHandler2 { loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = (ServiceCombServer) loadBalancer.chooseServer(invocation); Assert.assertEquals(server.getEndpoint().getEndpoint(), "rest://localhost:9091"); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server2, 0); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(server2); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = (ServiceCombServer) loadBalancer.chooseServer(invocation); Assert.assertEquals(server.getEndpoint().getEndpoint(), "rest://localhost:9090"); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(server2, 0); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(server2, 0); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(server2); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(server2); loadBalancer = handler.getOrCreateLoadBalancer(invocation); server = (ServiceCombServer) loadBalancer.chooseServer(invocation); Assert.assertEquals(server.getEndpoint().getEndpoint(), "rest://localhost:9091"); diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java new file mode 100644 index 0000000..d80f17c --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestRoundRobinRuleExt.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.loadbalance; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.Invocation; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestRoundRobinRuleExt { + @Test + public void testRoundRobin() { + RoundRobinRuleExt rule = new RoundRobinRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 2; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, 1); + } + + AtomicInteger server1 = new AtomicInteger(0); + AtomicInteger server2 = new AtomicInteger(0); + for (int i = 0; i < 2000; i++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + server1.incrementAndGet(); + } else { + server2.incrementAndGet(); + } + } + Assert.assertEquals(server1.get(), server2.get()); + } + + @Test + public void testBenchmarkRobin() { + // less than 0.001ms + RoundRobinRuleExt rule = new RoundRobinRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 100; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, 2); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assert.assertEquals(taken < 10 * 2, true); // 2 * times make slow machine happy + } +} diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombLoadBalancerStats.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombLoadBalancerStats.java index 23352c8..fb88452 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombLoadBalancerStats.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombLoadBalancerStats.java @@ -64,7 +64,7 @@ public class TestServiceCombLoadBalancerStats { ServiceCombServer serviceCombServer = new ServiceCombServer(transport, new CacheEndpoint("rest://localhost:8080", instance)); - serviceCombLoadBalancerStats.markSuccess(serviceCombServer, 0); + serviceCombLoadBalancerStats.markSuccess(serviceCombServer); ServiceCombServerStats stats = serviceCombLoadBalancerStats.getServiceCombServerStats(serviceCombServer); Assert.assertEquals(serviceCombLoadBalancerStats.getPingView().size(), 1); await().atMost(5, TimeUnit.SECONDS) @@ -83,16 +83,16 @@ public class TestServiceCombLoadBalancerStats { instance.setInstanceId("instance1"); ServiceCombServer serviceCombServer = new ServiceCombServer(transport, new CacheEndpoint("rest://localhost:8080", instance)); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer, 0); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer, 0); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer); Assert.assertEquals( ServiceCombLoadBalancerStats.INSTANCE.getServiceCombServerStats(serviceCombServer).getCountinuousFailureCount(), 2); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer, 0); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer); Assert.assertEquals( ServiceCombLoadBalancerStats.INSTANCE.getServiceCombServerStats(serviceCombServer).getCountinuousFailureCount(), 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer, 0); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer); Assert .assertEquals( ServiceCombLoadBalancerStats.INSTANCE.getServiceCombServerStats(serviceCombServer).getTotalRequests(), 4); @@ -119,10 +119,10 @@ public class TestServiceCombLoadBalancerStats { for (int i = 0; i < 10; i++) { new Thread() { public void run() { - ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer, 0); - ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer, 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer, 0); - ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer, 0); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer); + ServiceCombLoadBalancerStats.INSTANCE.markFailure(serviceCombServer); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer); + ServiceCombLoadBalancerStats.INSTANCE.markSuccess(serviceCombServer); latch.countDown(); } }.start(); diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServerStats.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServerStats.java index 3fda339..3010368 100644 --- a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServerStats.java +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestServiceCombServerStats.java @@ -31,12 +31,12 @@ public class TestServiceCombServerStats { public void testSimpleThread() { long time = System.currentTimeMillis(); ServiceCombServerStats stats = new ServiceCombServerStats(); - stats.markFailure(0); - stats.markFailure(0); + stats.markFailure(); + stats.markFailure(); Assert.assertEquals(stats.getCountinuousFailureCount(), 2); - stats.markSuccess(0); + stats.markSuccess(); Assert.assertEquals(stats.getCountinuousFailureCount(), 0); - stats.markSuccess(0); + stats.markSuccess(); Assert.assertEquals(stats.getTotalRequests(), 4); Assert.assertEquals(stats.getFailedRate(), 50); Assert.assertEquals(stats.getSuccessRate(), 50); @@ -52,10 +52,10 @@ public class TestServiceCombServerStats { for (int i = 0; i < 10; i++) { new Thread() { public void run() { - stats.markFailure(0); - stats.markFailure(0); - stats.markSuccess(0); - stats.markSuccess(0); + stats.markFailure(); + stats.markFailure(); + stats.markSuccess(); + stats.markSuccess(); latch.countDown(); } }.start(); @@ -78,8 +78,8 @@ public class TestServiceCombServerStats { }; ServiceCombServerStats stats = new ServiceCombServerStats(); Assert.assertEquals(stats.getLastVisitTime(), 1000); - stats.markSuccess(0); - stats.markFailure(0); + stats.markSuccess(); + stats.markFailure(); Assert.assertEquals(stats.getTotalRequests(), 2); Assert.assertEquals(stats.getFailedRate(), 50); Assert.assertEquals(stats.getSuccessRate(), 50); @@ -89,7 +89,7 @@ public class TestServiceCombServerStats { return 60000 + 2000; } }; - stats.markSuccess(0); + stats.markSuccess(); Assert.assertEquals(stats.getTotalRequests(), 1); Assert.assertEquals(stats.getFailedRate(), 0); Assert.assertEquals(stats.getSuccessRate(), 100); diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java new file mode 100644 index 0000000..54e1663 --- /dev/null +++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/TestWeightedResponseTimeRuleExt.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.servicecomb.loadbalance; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.apache.servicecomb.core.Invocation; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +public class TestWeightedResponseTimeRuleExt { + @Test + public void testRoundRobin() { + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 2; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, 1); + } + + AtomicInteger server1 = new AtomicInteger(0); + AtomicInteger server2 = new AtomicInteger(0); + for (int i = 0; i < 2000; i++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + server1.incrementAndGet(); + } else { + server2.incrementAndGet(); + } + } + Assert.assertEquals(server1.get(), server2.get()); + } + + @Test + public void testWeighed() { + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 2; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, 20 * Math.pow(4, i + 1)); + } + + AtomicInteger server1 = new AtomicInteger(0); + AtomicInteger server2 = new AtomicInteger(0); + for (int i = 0; i < 2000; i++) { + if (rule.choose(servers, invocation).toString().equals("server 0")) { + server1.incrementAndGet(); + } else { + server2.incrementAndGet(); + } + } + double percent = (double) server1.get() / (server2.get() + server1.get()); + System.out.println("percent" + percent); + Assert.assertEquals(0.70d < percent, percent < 0.90d); + } + + @Test + public void testBenchmark() { + // 100 instances will taken less than 0.1ms. Because we use weighed rule when response time more than 10ms, + // This only taken 1/1000 time. + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 100; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, i); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assert.assertEquals(taken < 1000 * 2, true); // 2 * times make slow machine happy + } + + @Test + public void testBenchmarkRobin() { + // 100 instances will taken less than 0.02ms. Not as good as RoundRobinRule, which taken less than 0.001ms + WeightedResponseTimeRuleExt rule = new WeightedResponseTimeRuleExt(); + LoadBalancer loadBalancer = new LoadBalancer(rule, "testService"); + List<ServiceCombServer> servers = new ArrayList<>(); + Invocation invocation = Mockito.mock(Invocation.class); + for (int i = 0; i < 100; i++) { + ServiceCombServer server = Mockito.mock(ServiceCombServer.class); + Mockito.when(server.toString()).thenReturn("server " + i); + servers.add(server); + loadBalancer.getLoadBalancerStats().noteResponseTime(server, 2); + } + long begin = System.currentTimeMillis(); + for (int i = 0; i < 10000; i++) { + rule.choose(servers, invocation); + } + long taken = System.currentTimeMillis() - begin; + System.out.println("taken " + taken); + Assert.assertEquals(taken < 200 * 2, true); // 2 * times make slow machine happy + } +}
