Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么即使在服务端没有上下线操作的时候一致性hash负载均衡算法每次都需要重新进行hash环的映射? #5424

Closed
thisiswanghy opened this issue Dec 4, 2019 · 1 comment

Comments

@thisiswanghy
Copy link
Contributor

Environment

  • Dubbo version: 2.7.4.1

为了更好的研究一致性hash负载均衡策略,我自定义了一个SPI。
whyConsistentHash=com.example.dubbo.loadbalance.provider.loadbalance.WhyConsistentHashLoadBalance

我加入了几行输出代码如下:

 @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        String methodName = RpcUtils.getMethodName(invocation);
        String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
        int identityHashCode = System.identityHashCode(invokers);
        WhyConsistentHashLoadBalance.ConsistentHashSelector<T> selector = (WhyConsistentHashLoadBalance.ConsistentHashSelector<T>) selectors.get(key);
        if (selector == null || selector.identityHashCode != identityHashCode) {
             //我加的
            System.out.println("是新的invoker :" + identityHashCode + ",原:" + (selector == null ? "null" : selector.identityHashCode));
            selectors.put(key, new WhyConsistentHashLoadBalance.ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
            selector = (WhyConsistentHashLoadBalance.ConsistentHashSelector<T>) selectors.get(key);
        }
       //我加的
        for (Map.Entry<Long, Invoker<T>> entry : selector.virtualInvokers.entrySet()) {
            System.out.println("key=" + entry.getKey() + ",value=" + entry.getValue());
        }
        return selector.select(invocation);
    }

同时为了演示方便,我调整了虚拟节点数为4:
<dubbo:parameter key="hash.nodes" value="4"></dubbo:parameter>

这样就是2个服务端,

启动两个服务端。这样就是2个服务端,一个服务端有4个虚拟节点,共计8个节点。

客户端调用如下:

    @Test
    public void testLeastActiveLoadBalance() {
        for (int i = 0; i < 5; i++) {
            demoService.sayHello("公众号why技术-" + i);
        }
    }

最后输出结果为:

是新的invoker :418299920,原:null
key=447653163,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=488050138,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=724509126,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=2033828652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=2496774652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=3155964929,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4068105014,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4223326152,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
是新的invoker :105393903,原:418299920
key=447653163,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=488050138,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=724509126,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=2033828652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=2496774652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=3155964929,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4068105014,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4223326152,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
是新的invoker :533698361,原:105393903
key=447653163,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=488050138,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=724509126,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=2033828652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=2496774652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=3155964929,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4068105014,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4223326152,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
是新的invoker :1531545666,原:533698361
key=447653163,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=488050138,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=724509126,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=2033828652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=2496774652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=3155964929,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4068105014,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4223326152,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
是新的invoker :481525476,原:1531545666
key=447653163,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=488050138,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=724509126,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=2033828652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=2496774652,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@67c119b7
key=3155964929,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4068105014,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed
key=4223326152,value=org.apache.dubbo.registry.integration.RegistryDirectory$InvokerDelegate@2ca5f1ed

在过程中并没有服务上下线的操作,为什么5次调用,每次都会重新进行hash环的映射。而且从输出可以看出,每次映射的结果是一样的。

我看了invokers在这五次调用的时候地址值确实不一样。但是我不明白为什么?求解答。

@thisiswanghy
Copy link
Contributor Author

自定义的负载均衡算法和Dubbo 2.7.4.1 版本的一样,除了加了几句输出语句:

public class WhyConsistentHashLoadBalance extends AbstractLoadBalance {
    public static final String NAME = "consistenthash";

    /**
     * Hash nodes name
     */
    public static final String HASH_NODES = "hash.nodes";

    /**
     * Hash arguments name
     */
    public static final String HASH_ARGUMENTS = "hash.arguments";

    private final ConcurrentMap<String, WhyConsistentHashLoadBalance.ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<String, WhyConsistentHashLoadBalance.ConsistentHashSelector<?>>();

    @SuppressWarnings("unchecked")
    @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        String methodName = RpcUtils.getMethodName(invocation);
        String key = invokers.get(0).getUrl().getServiceKey() + "." + methodName;
        int identityHashCode = System.identityHashCode(invokers);
        WhyConsistentHashLoadBalance.ConsistentHashSelector<T> selector = (WhyConsistentHashLoadBalance.ConsistentHashSelector<T>) selectors.get(key);
        if (selector == null || selector.identityHashCode != identityHashCode) {
            System.out.println("是新的invoker :" + identityHashCode + ",原:" + (selector == null ? "null" : selector.identityHashCode));
            selectors.put(key, new WhyConsistentHashLoadBalance.ConsistentHashSelector<T>(invokers, methodName, identityHashCode));
            selector = (WhyConsistentHashLoadBalance.ConsistentHashSelector<T>) selectors.get(key);
        }
        for (Map.Entry<Long, Invoker<T>> entry : selector.virtualInvokers.entrySet()) {
            System.out.println("key=" + entry.getKey() + ",value=" + entry.getValue());
        }
        return selector.select(invocation);
    }

    private static final class ConsistentHashSelector<T> {

        private final TreeMap<Long, Invoker<T>> virtualInvokers;

        private final int replicaNumber;

        private final int identityHashCode;

        private final int[] argumentIndex;

        ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
            this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
            this.identityHashCode = identityHashCode;
            URL url = invokers.get(0).getUrl();
            this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
            String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
            argumentIndex = new int[index.length];
            for (int i = 0; i < index.length; i++) {
                argumentIndex[i] = Integer.parseInt(index[i]);
            }
            for (Invoker<T> invoker : invokers) {
                String address = invoker.getUrl().getAddress();
                for (int i = 0; i < replicaNumber / 4; i++) {
                    byte[] digest = md5(address + i);
                    for (int h = 0; h < 4; h++) {
                        long m = hash(digest, h);
                        virtualInvokers.put(m, invoker);
                    }
                }
            }
        }

        public Invoker<T> select(Invocation invocation) {
            String key = toKey(invocation.getArguments());
            byte[] digest = md5(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) {
            Map.Entry<Long, Invoker<T>> 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();
        }

    }

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant