Consistent Hash Algorithm and Its Application in Dubbo Load Balancing

This article explains the principles of consistent hashing, its data‑skew issue, how virtual nodes mitigate that problem, and demonstrates the implementation and usage of ConsistentHashLoadBalance in Dubbo with detailed code analysis and practical load‑balancing examples.

JD Tech
JD Tech
JD Tech
Consistent Hash Algorithm and Its Application in Dubbo Load Balancing

The article introduces consistent hashing, describing its basic principle and the data‑skew problem that occurs when a small number of nodes handle most requests, then presents virtual nodes as a solution to distribute load more evenly.

It explains load balancing concepts, quoting Dubbo’s definition of LoadBalance and listing four built‑in algorithms (RandomLoadBalance, LeastActiveLoadBalance, ConsistentHashLoadBalance, RoundRobinLoadBalance), and discusses the drawbacks of simple hash‑based partitioning, especially the heavy data migration caused by scaling up or down.

Virtual nodes are introduced: each physical server can be represented by multiple virtual nodes (e.g., IP+port+index), which spreads requests across servers and reduces data skew. Diagrams illustrate how adding virtual nodes balances request distribution.

The article then focuses on the practical application of consistent hashing in Dubbo. It provides a step‑by‑step source‑code analysis of ConsistentHashLoadBalance, starting with the doSelect method that detects changes in the invoker list and creates a new ConsistentHashSelector when needed.

public class ConsistentHashLoadBalance extends AbstractLoadBalance {
    private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<>();
    @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);
        ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key);
        if (selector == null || selector.identityHashCode != identityHashCode) {
            selectors.put(key, new ConsistentHashSelector<>(invokers, methodName, identityHashCode));
            selector = (ConsistentHashSelector<T>) selectors.get(key);
        }
        return selector.select(invocation);
    }
    private static final class ConsistentHashSelector<T> { ... }
}

The constructor of ConsistentHashSelector builds a TreeMap<Long, Invoker<T>> of virtual nodes, using MD5 to generate multiple hash points per server and storing them for efficient lookup.

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<>();
        this.identityHashCode = identityHashCode;
        URL url = invokers.get(0).getUrl();
        this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160);
        String[] index = Constants.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);
                }
            }
        }
    }
    // select and other methods omitted for brevity
}

The select method hashes the invocation arguments, finds the appropriate virtual node in the ring via TreeMap.tailMap, and returns the corresponding real invoker, ensuring that identical request parameters are consistently routed to the same provider.

Finally, the article lists various other load‑balancing techniques (DNS, proxy, NAT, reverse proxy, hybrid) and provides references for further reading.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Dubbovirtual nodesConsistent Hashhash algorithm
JD Tech
Written by

JD Tech

Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.