ConsistentHashSelector ,是 ConsistentHashLoadBalance 的内部类,一致性哈希选择器,基于 Ketama 算法。
- /**
- * 虚拟节点与 Invoker 的映射关系
- */
- private final TreeMap<Long, Invoker<T>> virtualInvokers;
- /**
- * 每个Invoker 对应的虚拟节点数
- */
- private final int replicaNumber;
- /**
- * 定义哈希值
- */
- private final int identityHashCode;
- /**
- * 取值参数位置数组
- */
- private final int[] argumentIndex;
- 1: ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
- 2: this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
- 3: // 设置 identityHashCode
- 4: this.identityHashCode = identityHashCode;
- 5: URL url = invokers.get(0).getUrl();
- 6: // 初始化 replicaNumber
- 7: this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
- 8: // 初始化 argumentIndex
- 9: String[] index = Constants.COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, "hash.arguments", "0"));
- 10: argumentIndex = new int[index.length];
- 11: for (int i = 0; i < index.length; i++) {
- 12: argumentIndex[i] = Integer.parseInt(index[i]);
- 13: }
- 14: // 初始化 virtualInvokers
- 15: for (Invoker<T> invoker : invokers) {
- 16: String address = invoker.getUrl().getAddress();
- 17: // 每四个虚拟结点为一组,为什么这样?下面会说到
- 18: for (int i = 0; i < replicaNumber / 4; i++) {
- 19: // 这组虚拟结点得到惟一名称
- 20: byte[] digest = md5(address + i);
- 21: // Md5是一个16字节长度的数组,将16字节的数组每四个字节一组,分别对应一个虚拟结点,这就是为什么上面把虚拟结点四个划分一组的原因
- 22: for (int h = 0; h < 4; h++) {
- 23: // 对于每四个字节,组成一个long值数值,做为这个虚拟节点的在环中的惟一key
- 24: long m = hash(digest, h);
- 25: virtualInvokers.put(m, invoker);
- 26: }
- 27: }
- 28: }
- 29: }
- public Invoker<T> select(Invocation invocation) {
- // 基于方法参数,获得 KEY
- String key = toKey(invocation.getArguments());
- // 计算 MD5 值
- byte[] digest = md5(key);
- // 计算 KEY 值
- return selectForKey(hash(digest, 0));
- }
- private String toKey(Object[] args) {
- StringBuilder buf = new StringBuilder();
- for (int i : argumentIndex) {
- if (i >= 0 && i < args.length) {
- buf.append(args[i]);
- }
- }
- return buf.toString();
- }
- private Invoker<T> selectForKey(long hash) {
- // 得到大于当前 key 的那个子 Map ,然后从中取出第一个 key ,就是大于且离它最近的那个 key
- Map.Entry<Long, Invoker<T>> entry = virtualInvokers.tailMap(hash, true).firstEntry();
- // 不存在,则取 virtualInvokers 第一个
- if (entry == null) {
- entry = virtualInvokers.firstEntry();
- }
- // 存在,则返回
- return entry.getValue();
- }
(编辑:ASP站长网)
|