You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by li...@apache.org on 2023/12/08 07:11:53 UTC

(servicecomb-java-chassis) branch 2.8.x updated: [SCB-2841]zone-aware filter support configure group size ratio (#4108)

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

liubao pushed a commit to branch 2.8.x
in repository https://gitbox.apache.org/repos/asf/servicecomb-java-chassis.git


The following commit(s) were added to refs/heads/2.8.x by this push:
     new 9695b5baa [SCB-2841]zone-aware filter support configure group size ratio (#4108)
9695b5baa is described below

commit 9695b5baa1d0656071c9736d7636c7dcfb2ccdb8
Author: liubao68 <bi...@qq.com>
AuthorDate: Fri Dec 8 15:11:48 2023 +0800

    [SCB-2841]zone-aware filter support configure group size ratio (#4108)
---
 .../filterext/ZoneAwareDiscoveryFilter.java        |  26 ++-
 .../loadbalance/TestLoadBalanceHandler2.java       |  16 +-
 .../filterext/TestZoneAwareDiscoveryFilter.java    | 193 +++++++++++++++++++++
 3 files changed, 227 insertions(+), 8 deletions(-)

diff --git a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java
index d31597b5e..fd308c282 100644
--- a/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java
+++ b/handlers/handler-loadbalance/src/main/java/org/apache/servicecomb/loadbalance/filterext/ZoneAwareDiscoveryFilter.java
@@ -29,6 +29,7 @@ import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
 import com.netflix.config.DynamicPropertyFactory;
 
 public class ZoneAwareDiscoveryFilter implements ServerListFilterExt {
+  public static final String CONFIG_RATIO = "servicecomb.loadbalance.filter.zoneaware.ratio";
 
   @Override
   public int getOrder() {
@@ -42,6 +43,11 @@ public class ZoneAwareDiscoveryFilter implements ServerListFilterExt {
         .get();
   }
 
+  private int getRatio() {
+    return DynamicPropertyFactory.getInstance()
+        .getIntProperty(CONFIG_RATIO, 50).get();
+  }
+
   @Override
   public List<ServiceCombServer> getFilteredListOfServers(List<ServiceCombServer> servers,
       Invocation invocation) {
@@ -59,14 +65,30 @@ public class ZoneAwareDiscoveryFilter implements ServerListFilterExt {
         instancesNoMatch.add(server);
       }
     });
-    if (!instancesRegionAndAZMatch.isEmpty()) {
+
+    int ratio = getRatio();
+
+    if (hasEnoughMembers(servers.size(), instancesRegionAndAZMatch.size(), ratio)) {
       return instancesRegionAndAZMatch;
-    } else if (!instancesAZMatch.isEmpty()) {
+    } else {
+      instancesAZMatch.addAll(instancesRegionAndAZMatch);
+    }
+
+    if (hasEnoughMembers(servers.size(), instancesAZMatch.size(), ratio)) {
       return instancesAZMatch;
+    } else {
+      instancesNoMatch.addAll(instancesAZMatch);
     }
     return instancesNoMatch;
   }
 
+  private boolean hasEnoughMembers(int totalSize, int groupSize, int ratio) {
+    if (totalSize == 0 || groupSize == 0) {
+      return false;
+    }
+    return Math.floorDiv(groupSize * 100, totalSize) >= ratio;
+  }
+
   private boolean regionAndAZMatch(MicroserviceInstance myself, MicroserviceInstance target) {
     if (myself == null || myself.getDataCenterInfo() == null) {
       // when instance have no datacenter info, it will match all other datacenters
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 e2a353db4..2008445ae 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
@@ -17,6 +17,8 @@
 
 package org.apache.servicecomb.loadbalance;
 
+import static org.mockito.Mockito.when;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -40,6 +42,7 @@ import org.apache.servicecomb.foundation.common.event.EventManager;
 import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
 import org.apache.servicecomb.loadbalance.event.IsolationServerEvent;
 import org.apache.servicecomb.loadbalance.filter.ServerDiscoveryFilter;
+import org.apache.servicecomb.loadbalance.filterext.ZoneAwareDiscoveryFilter;
 import org.apache.servicecomb.localregistry.LocalRegistryStore;
 import org.apache.servicecomb.registry.DiscoveryManager;
 import org.apache.servicecomb.registry.api.registry.DataCenterInfo;
@@ -62,8 +65,6 @@ import com.google.common.eventbus.Subscribe;
 import mockit.Mock;
 import mockit.MockUp;
 
-import static org.mockito.Mockito.when;
-
 public class TestLoadBalanceHandler2 {
   private Holder<Long> mockTimeMillis;
 
@@ -85,7 +86,7 @@ public class TestLoadBalanceHandler2 {
 
   @Before
   public void setUp() {
-
+    ArchaiusUtils.setProperty(ZoneAwareDiscoveryFilter.CONFIG_RATIO, 0);
     // avoid mock
     ServiceCombLoadBalancerStats.INSTANCE.init();
     TestServiceCombServerStats.releaseTryingChance();
@@ -472,7 +473,8 @@ public class TestLoadBalanceHandler2 {
 
   @Test
   public void testZoneAwareAndIsolationFilterUsingMockedInvocationWorks() throws Exception {
-    Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+", (inv, aysnc) -> aysnc.success("OK"));
+    Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+",
+        (inv, aysnc) -> aysnc.success("OK"));
 
     InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class);
     TransportManager transportManager = Mockito.mock(TransportManager.class);
@@ -602,7 +604,8 @@ public class TestLoadBalanceHandler2 {
   public void testStatusFilterUsingMockedInvocationWorks() throws Exception {
     ArchaiusUtils.setProperty("servicecomb.loadbalance.filter.status.enabled", "false");
 
-    Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+", (inv, aysnc) -> aysnc.success("OK"));
+    Invocation invocation = new NonSwaggerInvocation("testApp", "testMicroserviceName", "0.0.0+",
+        (inv, aysnc) -> aysnc.success("OK"));
 
     InstanceCacheManager instanceCacheManager = Mockito.mock(InstanceCacheManager.class);
     TransportManager transportManager = Mockito.mock(TransportManager.class);
@@ -790,7 +793,8 @@ public class TestLoadBalanceHandler2 {
     } catch (Exception e) {
 
     }
-    Assertions.assertEquals("rest://127.0.0.1:8080?sslEnabled=true&protocol=http2", invocation.getEndpoint().getEndpoint());
+    Assertions.assertEquals("rest://127.0.0.1:8080?sslEnabled=true&protocol=http2",
+        invocation.getEndpoint().getEndpoint());
 
     // reset
     invocation.setEndpoint(null);
diff --git a/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filterext/TestZoneAwareDiscoveryFilter.java b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filterext/TestZoneAwareDiscoveryFilter.java
new file mode 100644
index 000000000..9d7e722ee
--- /dev/null
+++ b/handlers/handler-loadbalance/src/test/java/org/apache/servicecomb/loadbalance/filterext/TestZoneAwareDiscoveryFilter.java
@@ -0,0 +1,193 @@
+/*
+ * 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.filterext;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.servicecomb.config.ConfigUtil;
+import org.apache.servicecomb.core.Invocation;
+import org.apache.servicecomb.core.Transport;
+import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
+import org.apache.servicecomb.loadbalance.ServiceCombServer;
+import org.apache.servicecomb.registry.RegistrationManager;
+import org.apache.servicecomb.registry.api.registry.DataCenterInfo;
+import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
+import org.apache.servicecomb.registry.cache.CacheEndpoint;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class TestZoneAwareDiscoveryFilter {
+  @BeforeEach
+  public void setUp() {
+    ConfigUtil.createLocalConfig();
+  }
+
+  @AfterEach
+  public void tearDown() {
+    ArchaiusUtils.resetConfig();
+    RegistrationManager.renewInstance();
+  }
+
+  @Test
+  public void test_not_enough_instance() {
+    ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter();
+
+    // set up data
+    MicroserviceInstance myself = Mockito.mock(MicroserviceInstance.class);
+    RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class);
+    Mockito.when(registrationManager.getMicroserviceInstance()).thenReturn(myself);
+    RegistrationManager.setINSTANCE(registrationManager);
+    DataCenterInfo myDcInfo = new DataCenterInfo();
+    myDcInfo.setName("test");
+    myDcInfo.setRegion("test-Region");
+    myDcInfo.setAvailableZone("test-zone");
+    Mockito.when(myself.getDataCenterInfo()).thenReturn(myDcInfo);
+
+    MicroserviceInstance discoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> allMatchEndpoint = new ArrayList<>();
+    allMatchEndpoint.add("rest://localhost:9090");
+    Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint);
+    CacheEndpoint allmatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(allmatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9090");
+    Transport transport = Mockito.mock(Transport.class);
+    Mockito.when(allmatchCacheEndpoint.getInstance()).thenReturn(discoveryInstance);
+    ServiceCombServer allmatchInstance = new ServiceCombServer("test", transport, allmatchCacheEndpoint);
+    DataCenterInfo info = new DataCenterInfo();
+    info.setName("test");
+    info.setRegion("test-Region");
+    info.setAvailableZone("test-zone");
+    Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info);
+    Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance");
+
+    MicroserviceInstance regionMatchDiscoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> regionMatchEndpoint = new ArrayList<>();
+    regionMatchEndpoint.add("rest://localhost:9091");
+    Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint);
+    CacheEndpoint regionMatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(regionMatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9091");
+    Mockito.when(regionMatchCacheEndpoint.getInstance()).thenReturn(regionMatchDiscoveryInstance);
+    ServiceCombServer regionMatchInstance = new ServiceCombServer("test", transport, regionMatchCacheEndpoint);
+    DataCenterInfo regionMatchInfo = new DataCenterInfo();
+    regionMatchInfo.setName("test");
+    regionMatchInfo.setRegion("test-Region");
+    regionMatchInfo.setAvailableZone("test-zone2");
+    Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo);
+    Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance");
+
+    MicroserviceInstance noneMatchDiscoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> noMatchEndpoint = new ArrayList<>();
+    noMatchEndpoint.add("rest://localhost:9092");
+    Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint);
+    CacheEndpoint noneMatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(noneMatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9092");
+    Mockito.when(noneMatchCacheEndpoint.getInstance()).thenReturn(noneMatchDiscoveryInstance);
+    ServiceCombServer noneMatchInstance = new ServiceCombServer("test", transport, noneMatchCacheEndpoint);
+    DataCenterInfo noneMatchInfo = new DataCenterInfo();
+    noneMatchInfo.setName("test");
+    noneMatchInfo.setRegion("test-Region2");
+    noneMatchInfo.setAvailableZone("test-zone2");
+    Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo);
+    Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance");
+
+    // run test
+    Invocation invocation = Mockito.mock(Invocation.class);
+    List<ServiceCombServer> data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance);
+    List<ServiceCombServer> result = filter.getFilteredListOfServers(data, invocation);
+
+    // check result
+    Assertions.assertEquals(2, result.size());
+    Assertions.assertEquals("regionMatchInstance", result.get(0).getInstance().getInstanceId());
+    Assertions.assertEquals("allmatchInstance", result.get(1).getInstance().getInstanceId());
+  }
+
+  @Test
+  public void test_enough_instance() {
+    ArchaiusUtils.setProperty(ZoneAwareDiscoveryFilter.CONFIG_RATIO, 0);
+    ZoneAwareDiscoveryFilter filter = new ZoneAwareDiscoveryFilter();
+
+    // set up data
+    MicroserviceInstance myself = Mockito.mock(MicroserviceInstance.class);
+    RegistrationManager registrationManager = Mockito.mock(RegistrationManager.class);
+    Mockito.when(registrationManager.getMicroserviceInstance()).thenReturn(myself);
+    RegistrationManager.setINSTANCE(registrationManager);
+    DataCenterInfo myDcInfo = new DataCenterInfo();
+    myDcInfo.setName("test");
+    myDcInfo.setRegion("test-Region");
+    myDcInfo.setAvailableZone("test-zone");
+    Mockito.when(myself.getDataCenterInfo()).thenReturn(myDcInfo);
+
+    MicroserviceInstance discoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> allMatchEndpoint = new ArrayList<>();
+    allMatchEndpoint.add("rest://localhost:9090");
+    Mockito.when(discoveryInstance.getEndpoints()).thenReturn(allMatchEndpoint);
+    CacheEndpoint allmatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(allmatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9090");
+    Transport transport = Mockito.mock(Transport.class);
+    Mockito.when(allmatchCacheEndpoint.getInstance()).thenReturn(discoveryInstance);
+    ServiceCombServer allmatchInstance = new ServiceCombServer("test", transport, allmatchCacheEndpoint);
+    DataCenterInfo info = new DataCenterInfo();
+    info.setName("test");
+    info.setRegion("test-Region");
+    info.setAvailableZone("test-zone");
+    Mockito.when(discoveryInstance.getDataCenterInfo()).thenReturn(info);
+    Mockito.when(discoveryInstance.getInstanceId()).thenReturn("allmatchInstance");
+
+    MicroserviceInstance regionMatchDiscoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> regionMatchEndpoint = new ArrayList<>();
+    regionMatchEndpoint.add("rest://localhost:9091");
+    Mockito.when(regionMatchDiscoveryInstance.getEndpoints()).thenReturn(regionMatchEndpoint);
+    CacheEndpoint regionMatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(regionMatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9091");
+    Mockito.when(regionMatchCacheEndpoint.getInstance()).thenReturn(regionMatchDiscoveryInstance);
+    ServiceCombServer regionMatchInstance = new ServiceCombServer("test", transport, regionMatchCacheEndpoint);
+    DataCenterInfo regionMatchInfo = new DataCenterInfo();
+    regionMatchInfo.setName("test");
+    regionMatchInfo.setRegion("test-Region");
+    regionMatchInfo.setAvailableZone("test-zone2");
+    Mockito.when(regionMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(regionMatchInfo);
+    Mockito.when(regionMatchDiscoveryInstance.getInstanceId()).thenReturn("regionMatchInstance");
+
+    MicroserviceInstance noneMatchDiscoveryInstance = Mockito.mock(MicroserviceInstance.class);
+    List<String> noMatchEndpoint = new ArrayList<>();
+    noMatchEndpoint.add("rest://localhost:9092");
+    Mockito.when(noneMatchDiscoveryInstance.getEndpoints()).thenReturn(noMatchEndpoint);
+    CacheEndpoint noneMatchCacheEndpoint = Mockito.mock(CacheEndpoint.class);
+    Mockito.when(noneMatchCacheEndpoint.getEndpoint()).thenReturn("rest://localhost:9092");
+    Mockito.when(noneMatchCacheEndpoint.getInstance()).thenReturn(noneMatchDiscoveryInstance);
+    ServiceCombServer noneMatchInstance = new ServiceCombServer("test", transport, noneMatchCacheEndpoint);
+    DataCenterInfo noneMatchInfo = new DataCenterInfo();
+    noneMatchInfo.setName("test");
+    noneMatchInfo.setRegion("test-Region2");
+    noneMatchInfo.setAvailableZone("test-zone2");
+    Mockito.when(noneMatchDiscoveryInstance.getDataCenterInfo()).thenReturn(noneMatchInfo);
+    Mockito.when(noneMatchDiscoveryInstance.getInstanceId()).thenReturn("noneMatchInstance");
+
+    // run test
+    Invocation invocation = Mockito.mock(Invocation.class);
+    List<ServiceCombServer> data = Arrays.asList(allmatchInstance, regionMatchInstance, noneMatchInstance);
+    List<ServiceCombServer> result = filter.getFilteredListOfServers(data, invocation);
+
+    // check result
+    Assertions.assertEquals(1, result.size());
+    Assertions.assertEquals("allmatchInstance", result.get(0).getInstance().getInstanceId());
+  }
+}