import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
interface HashFunction {
public int hash(String data);
}
class FNVHash1 implements HashFunction {
@Override
public int hash(final String data) {
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < data.length(); i++) {
hash = (hash ^ data.charAt(i)) * p;
}
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return Math.abs(hash);
}
}
class ConsistantHash {
private final HashFunction hashFunction;
private final int virtualNodeNum;
private final SortedMap<Integer, String> hashCircle = new TreeMap<>();
ConsistantHash(final List<String> nodes, final int virtualNodeNum, final HashFunction hashFunction) {
this.hashFunction = hashFunction;
this.virtualNodeNum = virtualNodeNum;
for (final String node : nodes) {
add(node);
}
}
public void add(final String node) {
hashCircle.put(hashFunction.hash(node), node);
for (int i = 0; i < virtualNodeNum; i++) {
hashCircle.put(hashFunction.hash(node + i), node);
}
}
public void remove(final String node) {
hashCircle.remove(hashFunction.hash(node));
for (int i = 0; i < virtualNodeNum; i++) {
hashCircle.remove(hashFunction.hash(node + i));
}
}
public String get(final String key) {
if (hashCircle.isEmpty()) {
return null;
}
int hash = hashFunction.hash(key);
if (!hashCircle.containsKey(hash)) {
final SortedMap<Integer, String> tailMap = hashCircle.tailMap(hash);
hash = tailMap.isEmpty() ? hashCircle.firstKey() : tailMap.firstKey();
}
return hashCircle.get(hash);
}
}
public class ConsistantHashTest {
public static void main(final String[] args) {
final int NODE_NUM = 10;
final int VIRTUAL_NODE_NUM = 200;
final int USER_KEY_NUM = 100_0000;
final List<String> nodes = new ArrayList<>();
for (int i = 0; i < NODE_NUM; i++) {
nodes.add("server-" + i);
}
final HashFunction fnvHash = new FNVHash1();
final ConsistantHash hashCircle = new ConsistantHash(nodes, VIRTUAL_NODE_NUM, fnvHash);
final List<String> keys = new ArrayList<>();
for (int i = 0; i < USER_KEY_NUM; i++) {
keys.add("user-key-" + i);
}
final SortedMap<String, Integer> result = new TreeMap<>();
for (final String key : keys) {
final String node = hashCircle.get(key);
result.put(node, result.getOrDefault(node, 0) + 1);
}
double sum = 0;
double standardDeviation = 0;
final int average = USER_KEY_NUM / NODE_NUM;
for (final Map.Entry<String, Integer> entry : result.entrySet()) {
final String node = entry.getKey();
final Integer numberOfKeysPerNode = entry.getValue();
System.out.println(node + " -> " + numberOfKeysPerNode);
sum += Math.pow( Math.abs(average - numberOfKeysPerNode), 2);
}
standardDeviation = Math.sqrt(sum / NODE_NUM);
System.out.println("Standard Deviation: " + standardDeviation);
}
}
评论 (2 条评论)