From 3a7733e78ff9df515b2821f69a64c3871c1fe6d7 Mon Sep 17 00:00:00 2001 From: hein Date: Mon, 23 Dec 2024 18:07:26 +0800 Subject: [PATCH] optimize: Caching the ConsistentHashSelector to avoid remapping when select every time. --- .../ConsistentHashLoadBalance.java | 61 +++++++++++++++++-- .../loadbalance/LoadBalanceTest.java | 46 +++++++++++++- 2 files changed, 100 insertions(+), 7 deletions(-) diff --git a/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/loadbalance/ConsistentHashLoadBalance.java b/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/loadbalance/ConsistentHashLoadBalance.java index 1156bf00319..1d4e9fb1669 100644 --- a/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/loadbalance/ConsistentHashLoadBalance.java +++ b/discovery/seata-discovery-core/src/main/java/org/apache/seata/discovery/loadbalance/ConsistentHashLoadBalance.java @@ -19,9 +19,7 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.List; -import java.util.SortedMap; -import java.util.TreeMap; +import java.util.*; import org.apache.seata.common.loader.LoadLevel; import org.apache.seata.config.ConfigurationFactory; @@ -38,16 +36,67 @@ public class ConsistentHashLoadBalance implements LoadBalance { * The constant LOAD_BALANCE_CONSISTENT_HASH_VIRTUAL_NODES. */ public static final String LOAD_BALANCE_CONSISTENT_HASH_VIRTUAL_NODES = LoadBalanceFactory.LOAD_BALANCE_PREFIX - + "virtualNodes"; + + "virtualNodes"; /** * The constant VIRTUAL_NODES_NUM. */ private static final int VIRTUAL_NODES_NUM = ConfigurationFactory.getInstance().getInt( - LOAD_BALANCE_CONSISTENT_HASH_VIRTUAL_NODES, VIRTUAL_NODES_DEFAULT); + LOAD_BALANCE_CONSISTENT_HASH_VIRTUAL_NODES, VIRTUAL_NODES_DEFAULT); + /** + * The ConsistentHashSelectorWrapper that caches a {@link ConsistentHashSelector}. + */ + private volatile ConsistentHashSelectorWrapper selectorWrapper; + + @SuppressWarnings("unchecked") @Override public T select(List invokers, String xid) { - return new ConsistentHashSelector<>(invokers, VIRTUAL_NODES_NUM).select(xid); + if (selectorWrapper == null) { + synchronized (this) { + if (selectorWrapper == null) { + selectorWrapper = new ConsistentHashSelectorWrapper( + new ConsistentHashSelector<>(invokers, VIRTUAL_NODES_NUM), invokers); + } + } + } + return (T) selectorWrapper.getSelector(invokers).select(xid); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private static final class ConsistentHashSelectorWrapper { + + private volatile ConsistentHashSelector selector; + // only shared with read + private volatile Set invokers; + + public ConsistentHashSelectorWrapper(ConsistentHashSelector selector, List invokers) { + this.selector = selector; + this.invokers = new HashSet<>(invokers); + } + + public ConsistentHashSelector getSelector(List invokers) { + if (!equals(invokers)) { + synchronized (this) { + if (!equals(invokers)) { + selector = new ConsistentHashSelector(invokers, VIRTUAL_NODES_NUM); + this.invokers = new HashSet<>(invokers); + } + } + } + return selector; + } + + private boolean equals(List invokers) { + if (invokers.size() != this.invokers.size()) { + return false; + } + for (Object invoker : invokers) { + if (!this.invokers.contains(invoker)) { + return false; + } + } + return true; + } } private static final class ConsistentHashSelector { diff --git a/discovery/seata-discovery-core/src/test/java/org/apache/seata/discovery/loadbalance/LoadBalanceTest.java b/discovery/seata-discovery-core/src/test/java/org/apache/seata/discovery/loadbalance/LoadBalanceTest.java index 8ca741799ab..f26aa4f26c0 100644 --- a/discovery/seata-discovery-core/src/test/java/org/apache/seata/discovery/loadbalance/LoadBalanceTest.java +++ b/discovery/seata-discovery-core/src/test/java/org/apache/seata/discovery/loadbalance/LoadBalanceTest.java @@ -21,11 +21,14 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import java.lang.reflect.Field; import java.net.InetSocketAddress; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; @@ -85,7 +88,7 @@ public void testXIDLoadBalance_select(List addresses) throws Assertions.assertNotNull(inetSocketAddress); // test not found tc channel inetSocketAddress = loadBalance.select(addresses, "127.0.0.1:8199:123456"); - Assertions.assertNotEquals(inetSocketAddress.getPort(),8199); + Assertions.assertNotEquals(inetSocketAddress.getPort(), 8199); } /** @@ -108,6 +111,31 @@ public void testConsistentHashLoadBalance_select(List address Assertions.assertEquals(1, selected, "selected must be equal to 1"); } + /** + * Test cached consistent hash load balance select. + * + * @param addresses the addresses + */ + @ParameterizedTest + @MethodSource("addressProvider") + public void testCachedConsistentHashLoadBalance_select(List addresses) throws Exception { + ConsistentHashLoadBalance loadBalance = new ConsistentHashLoadBalance(); + + List addresses1 = new ArrayList<>(addresses); + loadBalance.select(addresses1, XID); + Object o1 = getConsistentHashSelectorByReflect(loadBalance); + List addresses2 = new ArrayList<>(addresses); + loadBalance.select(addresses2, XID); + Object o2 = getConsistentHashSelectorByReflect(loadBalance); + Assertions.assertEquals(o1, o2); + + List addresses3 = new ArrayList<>(addresses); + addresses3.remove(ThreadLocalRandom.current().nextInt(addresses.size())); + loadBalance.select(addresses3, XID); + Object o3 = getConsistentHashSelectorByReflect(loadBalance); + Assertions.assertNotEquals(o1, o3); + } + /** * Test least active load balance select. * @@ -166,6 +194,22 @@ public Map getSelectedCounter(int runs, List