diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/CRCHashStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/CRCHashStrategy.java new file mode 100644 index 000000000..bc4d67797 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/CRCHashStrategy.java @@ -0,0 +1,71 @@ +package com.alipay.sofa.registry.client.consistenthash; + +import java.nio.charset.Charset; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class CRCHashStrategy implements HashStrategy { + + private static final int LOOKUP_TABLE[] = {0x0000, 0x1021, 0x2042, 0x3063, + 0x4084, 0x50A5, 0x60C6, 0x70E7, 0x8108, 0x9129, 0xA14A, 0xB16B, + 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF, 0x1231, 0x0210, 0x3273, 0x2252, + 0x52B5, 0x4294, 0x72F7, 0x62D6, 0x9339, 0x8318, 0xB37B, 0xA35A, + 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE, 0x2462, 0x3443, 0x0420, 0x1401, + 0x64E6, 0x74C7, 0x44A4, 0x5485, 0xA56A, 0xB54B, 0x8528, 0x9509, + 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D, 0x3653, 0x2672, 0x1611, 0x0630, + 0x76D7, 0x66F6, 0x5695, 0x46B4, 0xB75B, 0xA77A, 0x9719, 0x8738, + 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC, 0x48C4, 0x58E5, 0x6886, 0x78A7, + 0x0840, 0x1861, 0x2802, 0x3823, 0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, + 0x8948, 0x9969, 0xA90A, 0xB92B, 0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, + 0x1A71, 0x0A50, 0x3A33, 0x2A12, 0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, + 0x9B79, 0x8B58, 0xBB3B, 0xAB1A, 0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, + 0x2C22, 0x3C03, 0x0C60, 0x1C41, 0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, + 0xAD2A, 0xBD0B, 0x8D68, 0x9D49, 0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, + 0x3E13, 0x2E32, 0x1E51, 0x0E70, 0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, + 0xBF1B, 0xAF3A, 0x9F59, 0x8F78, 0x9188, 0x81A9, 0xB1CA, 0xA1EB, + 0xD10C, 0xC12D, 0xF14E, 0xE16F, 0x1080, 0x00A1, 0x30C2, 0x20E3, + 0x5004, 0x4025, 0x7046, 0x6067, 0x83B9, 0x9398, 0xA3FB, 0xB3DA, + 0xC33D, 0xD31C, 0xE37F, 0xF35E, 0x02B1, 0x1290, 0x22F3, 0x32D2, + 0x4235, 0x5214, 0x6277, 0x7256, 0xB5EA, 0xA5CB, 0x95A8, 0x8589, + 0xF56E, 0xE54F, 0xD52C, 0xC50D, 0x34E2, 0x24C3, 0x14A0, 0x0481, + 0x7466, 0x6447, 0x5424, 0x4405, 0xA7DB, 0xB7FA, 0x8799, 0x97B8, + 0xE75F, 0xF77E, 0xC71D, 0xD73C, 0x26D3, 0x36F2, 0x0691, 0x16B0, + 0x6657, 0x7676, 0x4615, 0x5634, 0xD94C, 0xC96D, 0xF90E, 0xE92F, + 0x99C8, 0x89E9, 0xB98A, 0xA9AB, 0x5844, 0x4865, 0x7806, 0x6827, + 0x18C0, 0x08E1, 0x3882, 0x28A3, 0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, + 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A, 0x4A75, 0x5A54, 0x6A37, 0x7A16, + 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92, 0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, + 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9, 0x7C26, 0x6C07, 0x5C64, 0x4C45, + 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1, 0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, + 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8, 0x6E17, 0x7E36, 0x4E55, 0x5E74, + 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0,}; + + /** + * Create a CRC16 checksum from the bytes. implementation is from + * mp911de/lettuce, modified with some more optimizations + * + * @param bytes + * @return CRC16 as integer value + */ + public static int getCRC16(byte[] bytes) { + int crc = 0x0000; + + for (byte b : bytes) { + crc = ((crc << 8) ^ LOOKUP_TABLE[((crc >>> 8) ^ (b & 0xFF)) & 0xFF]); + } + return crc & 0xFFFF; + } + + public static int getCRC16(String key) { + return getCRC16(key.getBytes(Charset.forName("UTF-8"))); + } + + @Override + public int getHashCode(String origin) { + // optimization with modulo operator with power of 2 + // equivalent to getCRC16(key) % 16384 + return getCRC16(origin) & (16384 - 1); + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancer.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancer.java new file mode 100644 index 000000000..07f57dea3 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancer.java @@ -0,0 +1,64 @@ +package com.alipay.sofa.registry.client.consistenthash; + + +import com.alipay.sofa.registry.client.remoting.ServerNode; + +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class ConsistentHashLoadBalancer implements LoadBalancer { + + private HashStrategy hashStrategy = new FnvHashStrategy(); + + private int virtualNodeSize = DEFAULT_VIRTUAL_NODE_SIZE; + private final static String VIRTUAL_NODE_SUFFIX = "&&"; + private final static int DEFAULT_VIRTUAL_NODE_SIZE=10; + + public ConsistentHashLoadBalancer() { + } + + public ConsistentHashLoadBalancer(int virtualNodeSize) { + this.virtualNodeSize = virtualNodeSize; + } + + public ConsistentHashLoadBalancer(HashStrategy hashStrategy) { + this.hashStrategy = hashStrategy; + } + + public ConsistentHashLoadBalancer(int virtualNodeSize, HashStrategy hashStrategy) { + this.virtualNodeSize = virtualNodeSize; + this.hashStrategy = hashStrategy; + } + + @Override + public ServerNode select(List servers, Invocation invocation) { + int invocationHashCode = hashStrategy.getHashCode(invocation.getHashKey()); + TreeMap ring = buildConsistentHashRing(servers); + ServerNode server = locate(ring, invocationHashCode); + return server; + } + + private ServerNode locate(TreeMap ring, int invocationHashCode) { + Map.Entry locateEntry = ring.ceilingEntry(invocationHashCode); + if (locateEntry == null) { + locateEntry = ring.firstEntry(); + } + return locateEntry.getValue(); + } + + private TreeMap buildConsistentHashRing(List servers) { + TreeMap virtualNodeRing = new TreeMap<>(); + for (ServerNode server : servers) { + for (int i = 0; i < virtualNodeSize; i++) { + virtualNodeRing.put(hashStrategy.getHashCode( + server.getUrl() + VIRTUAL_NODE_SUFFIX + i), server); + } + } + return virtualNodeRing; + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/FnvHashStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/FnvHashStrategy.java new file mode 100644 index 000000000..806cecee3 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/FnvHashStrategy.java @@ -0,0 +1,28 @@ +package com.alipay.sofa.registry.client.consistenthash; + +/** + * @author liqiuliang + * @create 2022-10-5 + * FNV1_32_HASH + */ + +public class FnvHashStrategy implements HashStrategy { + + private static final long FNV_32_INIT = 2166136261L; + private static final int FNV_32_PRIME = 16777619; + + @Override + public int getHashCode(String origin) { + final int p = FNV_32_PRIME; + int hash = (int) FNV_32_INIT; + for (int i = 0; i < origin.length(); i++) + hash = (hash ^ origin.charAt(i)) * p; + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + hash = Math.abs(hash); + return hash; + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/HashStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/HashStrategy.java new file mode 100644 index 000000000..4c886ec79 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/HashStrategy.java @@ -0,0 +1,9 @@ +package com.alipay.sofa.registry.client.consistenthash; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public interface HashStrategy { + int getHashCode(String origin); +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/Invocation.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/Invocation.java new file mode 100644 index 000000000..ce534bfef --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/Invocation.java @@ -0,0 +1,24 @@ +package com.alipay.sofa.registry.client.consistenthash; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class Invocation { + public Invocation() { + } + + public Invocation(String hashKey) { + this.hashKey = hashKey; + } + + private String hashKey; + + public String getHashKey() { + return hashKey; + } + + public void setHashKey(String hashKey) { + this.hashKey = hashKey; + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/JdkHashCodeStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/JdkHashCodeStrategy.java new file mode 100644 index 000000000..c42b323be --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/JdkHashCodeStrategy.java @@ -0,0 +1,14 @@ +package com.alipay.sofa.registry.client.consistenthash; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class JdkHashCodeStrategy implements HashStrategy { + + @Override + public int getHashCode(String origin) { + return origin.hashCode(); + } + +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaConsistentHashLoadBalancer.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaConsistentHashLoadBalancer.java new file mode 100644 index 000000000..dcd1e3234 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaConsistentHashLoadBalancer.java @@ -0,0 +1,92 @@ +package com.alipay.sofa.registry.client.consistenthash; + + +import com.alipay.sofa.registry.client.remoting.ServerNode; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class KetamaConsistentHashLoadBalancer implements LoadBalancer { + private static MessageDigest md5Digest; + + // 每一个物理节点的虚拟节点副本个数 + private int virtualNodeSize; + + private final static String VIRTUAL_NODE_SUFFIX = "-"; + + static { + try { + md5Digest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 not supported", e); + } + } + + public KetamaConsistentHashLoadBalancer() { + } + + public KetamaConsistentHashLoadBalancer(int virtualNodeSize) { + this.virtualNodeSize = virtualNodeSize; + } + + @Override + public ServerNode select(List servers, Invocation invocation) { + long invocationHashCode = getHashCode(invocation.getHashKey()); + TreeMap ring = buildConsistentHashRing(servers); + ServerNode server = locate(ring, invocationHashCode); + return server; + } + + private ServerNode locate(TreeMap ring, Long invocationHashCode) { + Map.Entry locateEntry = ring.ceilingEntry(invocationHashCode); + if (locateEntry == null) { + locateEntry = ring.firstEntry(); + } + return locateEntry.getValue(); + } + + private TreeMap buildConsistentHashRing(List servers) { + TreeMap virtualNodeRing = new TreeMap<>(); + for (ServerNode server : servers) { + for (int i = 0; i < virtualNodeSize / 4; i++) { + byte[] digest = computeMd5(server.getUrl() + VIRTUAL_NODE_SUFFIX + i); + for (int h = 0; h < 4; h++) { + Long k = ((long) (digest[3 + h * 4] & 0xFF) << 24) + | ((long) (digest[2 + h * 4] & 0xFF) << 16) + | ((long) (digest[1 + h * 4] & 0xFF) << 8) + | (digest[h * 4] & 0xFF); + virtualNodeRing.put(k, server); + + } + } + } + return virtualNodeRing; + } + + private long getHashCode(String origin) { + byte[] bKey = computeMd5(origin); + long rv = ((long) (bKey[3] & 0xFF) << 24) + | ((long) (bKey[2] & 0xFF) << 16) + | ((long) (bKey[1] & 0xFF) << 8) + | (bKey[0] & 0xFF); + return rv; + } + + private static byte[] computeMd5(String k) { + MessageDigest md5; + try { + md5 = (MessageDigest) md5Digest.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("clone of MD5 not supported", e); + } + md5.update(k.getBytes()); + return md5.digest(); + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaHashStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaHashStrategy.java new file mode 100644 index 000000000..9c61904bc --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/KetamaHashStrategy.java @@ -0,0 +1,44 @@ +package com.alipay.sofa.registry.client.consistenthash; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class KetamaHashStrategy implements HashStrategy { + private static MessageDigest md5Digest; + + static { + try { + md5Digest = MessageDigest.getInstance("MD5"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5 not supported", e); + } + } + + @Override + public int getHashCode(String origin) { + byte[] bKey = computeMd5(origin); + long rv = ((long) (bKey[3] & 0xFF) << 24) + | ((long) (bKey[2] & 0xFF) << 16) + | ((long) (bKey[1] & 0xFF) << 8) + | (bKey[0] & 0xFF); + return (int) (rv & 0xffffffffL); + } + + /** + * Get the md5 of the given key. + */ + public static byte[] computeMd5(String k) { + MessageDigest md5; + try { + md5 = (MessageDigest) md5Digest.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("clone of MD5 not supported", e); + } + md5.update(k.getBytes()); + return md5.digest(); + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/LoadBalancer.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/LoadBalancer.java new file mode 100644 index 000000000..fbef79536 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/LoadBalancer.java @@ -0,0 +1,14 @@ +package com.alipay.sofa.registry.client.consistenthash; + +import com.alipay.sofa.registry.client.remoting.ServerNode; + +import java.util.List; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public interface LoadBalancer { + + ServerNode select(List servers, Invocation invocation); +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/MurmurHashStrategy.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/MurmurHashStrategy.java new file mode 100644 index 000000000..1f0fa7925 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/MurmurHashStrategy.java @@ -0,0 +1,53 @@ +package com.alipay.sofa.registry.client.consistenthash; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class MurmurHashStrategy implements HashStrategy { + @Override + public int getHashCode(String origin) { + + ByteBuffer buf = ByteBuffer.wrap(origin.getBytes()); + int seed = 0x1234ABCD; + + ByteOrder byteOrder = buf.order(); + buf.order(ByteOrder.LITTLE_ENDIAN); + + long m = 0xc6a4a7935bd1e995L; + int r = 47; + + long h = seed ^ (buf.remaining() * m); + + long k; + while (buf.remaining() >= 8) { + k = buf.getLong(); + + k *= m; + k ^= k >>> r; + k *= m; + + h ^= k; + h *= m; + } + + if (buf.remaining() > 0) { + ByteBuffer finish = ByteBuffer.allocate(8).order( + ByteOrder.LITTLE_ENDIAN); + // for big-endian version, do this first: + // finish.position(8-buf.remaining()); + finish.put(buf).rewind(); + h ^= finish.getLong(); + h *= m; + } + h ^= h >>> r; + h *= m; + h ^= h >>> r; + + buf.order(byteOrder); + return (int) (h & 0xffffffffL); + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/StatisticsUtil.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/StatisticsUtil.java new file mode 100644 index 000000000..89a51068c --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/consistenthash/StatisticsUtil.java @@ -0,0 +1,37 @@ +package com.alipay.sofa.registry.client.consistenthash; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class StatisticsUtil { + + public static double variance(Long[] x) { + int m = x.length; + double sum = 0; + for (int i = 0; i < m; i++) { + sum += x[i]; + } + double dAve = sum / m; + double dVar = 0; + for (int i = 0; i < m; i++) { + dVar += (x[i] - dAve) * (x[i] - dAve); + } + return dVar / m; + } + + public static double standardDeviation(Long[] x) { + int m = x.length; + double sum = 0; + for (int i = 0; i < m; i++) { + sum += x[i]; + } + double dAve = sum / m; + double dVar = 0; + for (int i = 0; i < m; i++) { + dVar += (x[i] - dAve) * (x[i] - dAve); + } + return Math.sqrt(dVar / m); + } + +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/ClientConnection.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/ClientConnection.java index 7d4e5ea6e..f3a774a38 100644 --- a/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/ClientConnection.java +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/ClientConnection.java @@ -57,6 +57,10 @@ public class ClientConnection implements Client { private Map connectionEventProcessorMap; private RegistryClientConfig config; private Connection clientConnection; + + private Connection pubClientConnection; + + private Connection subClientConnection; private RegisterCache registerCache; private Worker worker; @@ -143,6 +147,9 @@ private boolean connect() { } if (null != connection && connection.isFine()) { + //todo 区分pub和sub +// pubClientConnection=connection; +// subClientConnection=connection; clientConnection = connection; return true; } @@ -166,11 +173,12 @@ public Object invokeSync(Object request) throws RemotingException, InterruptedEx return client.invokeSync(clientConnection, request, config.getInvokeTimeout()); } - private void recycle(Connection connection) { + private void recycle(Connection connection) throws InterruptedException { if (null == connection) { return; } - + Random random = new Random(); + Thread.sleep(random.nextInt(RECONNECTING_DELAY)); client.closeConnection(connection.getUrl()); } diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/PubConnection.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/PubConnection.java new file mode 100644 index 000000000..3d252cf1f --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/PubConnection.java @@ -0,0 +1,28 @@ +package com.alipay.sofa.registry.client.remoting; + +import com.alipay.remoting.Connection; +import com.alipay.remoting.ProtocolCode; +import com.alipay.remoting.Url; +import io.netty.channel.Channel; + +/** + * @author liqiuliang + * @create 2022-10-10 2:24 + */ +public class PubConnection extends Connection { + public PubConnection(Channel channel) { + super(channel); + } + + public PubConnection(Channel channel, Url url) { + super(channel, url); + } + + public PubConnection(Channel channel, ProtocolCode protocolCode, Url url) { + super(channel, protocolCode, url); + } + + public PubConnection(Channel channel, ProtocolCode protocolCode, byte version, Url url) { + super(channel, protocolCode, version, url); + } +} diff --git a/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/SubConnection.java b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/SubConnection.java new file mode 100644 index 000000000..298620773 --- /dev/null +++ b/client/impl/src/main/java/com/alipay/sofa/registry/client/remoting/SubConnection.java @@ -0,0 +1,28 @@ +package com.alipay.sofa.registry.client.remoting; + +import com.alipay.remoting.Connection; +import com.alipay.remoting.ProtocolCode; +import com.alipay.remoting.Url; +import io.netty.channel.Channel; + +/** + * @author liqiuliang + * @create 2022-10-10 2:26 + */ +public class SubConnection extends Connection { + public SubConnection(Channel channel) { + super(channel); + } + + public SubConnection(Channel channel, Url url) { + super(channel, url); + } + + public SubConnection(Channel channel, ProtocolCode protocolCode, Url url) { + super(channel, protocolCode, url); + } + + public SubConnection(Channel channel, ProtocolCode protocolCode, byte version, Url url) { + super(channel, protocolCode, version, url); + } +} diff --git a/client/impl/src/test/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancerTest.java b/client/impl/src/test/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancerTest.java new file mode 100644 index 000000000..074af0158 --- /dev/null +++ b/client/impl/src/test/java/com/alipay/sofa/registry/client/consistenthash/ConsistentHashLoadBalancerTest.java @@ -0,0 +1,189 @@ +package com.alipay.sofa.registry.client.consistenthash; + +import com.alipay.sofa.registry.client.provider.DefaultServerNode; +import com.alipay.sofa.registry.client.remoting.ServerNode; +import org.junit.Test; + +import java.util.*; + +/** + * @author liqiuliang + * @create 2022-10-5 + */ +public class ConsistentHashLoadBalancerTest { + @Test + public void testHits() { + List servers = new ArrayList<>(); + for (String ip : ips) { + servers.add(new DefaultServerNode(ip + ":8080", ip, 8080, null)); + } + LoadBalancer chloadBalance = new ConsistentHashLoadBalancer(100); + // Construct 10000 random requests + List invocations = new ArrayList<>(100); + for (int i = 0; i < 10000; i++) { + invocations.add(new Invocation(UUID.randomUUID().toString())); + } + // hit statistics + Map map = new HashMap(); + for (ServerNode server : servers) { + map.put(server, 0); + } + for (Invocation invocation : invocations) { + ServerNode selectedServer = chloadBalance.select(servers, invocation); + map.put(selectedServer, map.get(selectedServer)+1); + } + for (Map.Entry serverNodeIntegerEntry : map.entrySet()) { + System.out.println("serverNode: " + serverNodeIntegerEntry.getKey().getUrl() + " hits condition: " + + serverNodeIntegerEntry.getValue() / 10000.0); + } + } + + @Test + public void testDistribution() { + List servers = new ArrayList<>(); + for (String ip : ips) { + servers.add(new DefaultServerNode(ip + ":8080", ip, 8080, null)); + } + LoadBalancer chloadBalance = new ConsistentHashLoadBalancer(); + List invocations = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + invocations.add(new Invocation(UUID.randomUUID().toString())); + } + // statistical distribution + Map map = new HashMap<>(); + for (ServerNode server : servers) { + map.put(server, 0L); + } + for (Invocation invocation : invocations) { + ServerNode selectedServer = chloadBalance.select(servers, invocation); + map.put(selectedServer, map.get(selectedServer)+1); + } + System.out.println("方差" + StatisticsUtil.variance(map.values().toArray(new Long[]{}))); + System.out.println("标准差" + StatisticsUtil.standardDeviation(map.values().toArray(new Long[]{}))); + } + + @Test + public void testNodeAddAndRemove() { + List servers = new ArrayList<>(); + for (String ip : ips) { + servers.add(new DefaultServerNode(ip, null, 0, null)); + } + List serverChanged = servers.subList(0, 80); + LoadBalancer chloadBalance = new ConsistentHashLoadBalancer(); + List invocations = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + invocations.add(new Invocation(UUID.randomUUID().toString())); + } + int count = 0; + for (Invocation invocation : invocations) { + ServerNode origin = chloadBalance.select(servers, invocation); + ServerNode changed = chloadBalance.select(serverChanged, invocation); + if (origin.getUrl().equals(changed.getUrl())) count++; + } + System.out.println(count / 10000D); + } + + + static String[] ips = { + "11.10.172.215", + "11.10.176.96", + "11.14.65.34", + "11.14.64.205", + "11.14.65.67", + "11.134.247.206", + "11.10.173.47", + "11.14.65.117", + "11.14.69.32", + "11.10.173.46", + "11.14.65.170", + "11.14.65.159", + "11.14.65.172", + "11.14.65.171", + "11.13.137.50", + "11.13.129.20", + "11.14.65.60", + "11.13.167.124", + "11.13.137.175", + "11.14.65.17", + "11.14.65.79", + "11.14.65.179", + "11.13.129.114", + "11.13.129.123", + "11.14.64.107", + "11.14.65.177", + "11.14.64.254", + "11.14.65.63", + "11.13.137.48", + "11.14.64.235", + "11.14.65.155", + "11.13.129.121", + "11.14.65.142", + "11.14.69.45", + "11.10.173.57", + "11.10.173.54", + "11.10.185.203", + "11.10.176.102", + "11.179.205.41", + "11.179.206.58", + "11.179.206.227", + "11.179.205.71", + "11.10.176.100", + "11.179.206.42", + "11.10.176.140", + "11.10.173.115", + "11.10.173.82", + "11.10.185.105", + "11.10.176.134", + "11.179.206.27", + "11.179.206.190", + "11.15.246.86", + "11.15.92.53", + "11.15.214.36", + "11.15.180.34", + "11.14.67.4", + "11.13.111.15", + "11.8.239.196", + "11.10.147.202", + "11.10.174.220", + "11.17.110.6", + "11.14.68.78", + "11.17.110.108", + "11.17.110.107", + "11.21.132.41", + "11.17.98.170", + "11.13.166.82", + "11.17.97.234", + "11.14.69.38", + "11.27.62.112", + "11.27.78.248", + "11.27.146.130", + "11.27.122.51", + "11.27.134.108", + "11.27.127.67", + "11.27.134.107", + "11.23.58.112", + "11.23.90.169", + "11.24.58.112", + "11.24.50.24", + "11.23.120.8", + "11.26.228.195", + "11.26.240.203", + "11.27.19.252", + "11.23.91.19", + "11.17.110.52", + "11.27.61.119", + "11.27.85.228", + "11.224.244.121", + "11.226.220.49", + "11.27.0.108", + "11.8.17.104", + "11.11.68.168", + "11.14.65.133", + "11.134.247.244", + "11.10.192.114", + "11.10.192.115", + "11.10.192.116", + "11.10.192.117", + "11.10.192.118" + }; +}