主要hash分布还是比较均匀,和Cassandra的hash一致(DHT),主要Cassandra有Ring概念
主要实现类
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
public class ConsistentHash {
private final TreeMap<Long, InetAddress> virtualInvokers;
public ConsistentHash(List<InetAddress> invokers) {
this.virtualInvokers = new TreeMap<>();
int replicaNumber = 180;
for (InetAddress address : invokers) {
String addr = address.getHostAddress();
for (int i = 0; i < replicaNumber; i++) {
byte[] digest = md5(addr + i);
for (int h = 0; h < 4; h++) {
long m = hash(digest, h);
virtualInvokers.put(m, address);
}
}
}
}
public InetAddress select(String key) {
byte[] digest = md5(key);
return selectForKey(hash(digest, 0));
}
private InetAddress selectForKey(long hash) {
Map.Entry<Long, InetAddress> entry = virtualInvokers.ceilingEntry(hash);
if (entry == null) {
entry = virtualInvokers.firstEntry();
}
return entry.getValue();
}
private long hash(byte[] digest, int number) {
return (((long) (digest[3 + number * 4] & 0xFF) << 24)
| ((long) (digest[2 + number * 4] & 0xFF) << 16)
| ((long) (digest[1 + number * 4] & 0xFF) << 8)
| (digest[number * 4] & 0xFF))
& 0xFFFFFFFFL;
}
private byte[] md5(String value) {
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e.getMessage(), e);
}
md5.reset();
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
md5.update(bytes);
return md5.digest();
}
}
单元测试类
@Slf4j
public class ConsistentHashTests {
@Test
public void run() {
String item = "HelloWorld";
List<InetAddress> addresses = Lists.newArrayList();
Map<String, AtomicInteger> map = Maps.newHashMap();
IntStream.range(1, 5).forEach(i -> {
try {
InetAddress address = InetAddress.getByName("127.0.0." + i);
addresses.add(address);
map.put(address.getHostAddress(), new AtomicInteger(0));
} catch (UnknownHostException e) {
e.printStackTrace();
}
});
ConsistentHash custom = new ConsistentHash(addresses);
IntStream.range(0, 1000000).forEach(i -> {
InetAddress address = custom.select(item + i);
map.get(address.getHostAddress()).incrementAndGet();
});
log.info("===>Address: {}", map);
}
}
执行结果
===>Address: {127.0.0.4=249761, 127.0.0.3=257643, 127.0.0.2=252279, 127.0.0.1=240317}
总体看hash分布还是比较均匀