Author: FserSuN
Source: https://blog.csdn.net/Revivedsun/article/details/71022871
LoadBalance is responsible for selecting a specific Invoker from multiple invokers for this call to share the pressure. The LoadBalance structure in Dubbo is shown in the figure below.
com.alibaba.dubbo.rpc.cluster.LoadBalance Interface provides <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException; Through this method, nodes are selected.
com.alibaba.dubbo.rpc.cluster.loadbalance.AbstractLoadBalance Implements some common methods and defines abstract methods protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation); This method is implemented by a specific load balancing implementation class.
Consistent hash load balancing configuration
There are four specific load balancing implementation classes. They are random, rotation training, least active and consistent Hash Consistent hash load balancing configuration
Configuration such as:
<dubbo:service interface="..." loadbalance="consistenthash" /> Or: <dubbo:reference interface="..." loadbalance="consistenthash" /> Or: <dubbo:service interface="..."> <dubbo:method name="..." loadbalance="consistenthash"/> </dubbo:service> Or: <dubbo:reference interface="..."> <dubbo:method name="..." loadbalance="consistenthash"/> </dubbo:reference
Consistency Hash load balancing involves two main configuration parameters: hash.arguments and hash.nodes.
Hash.arguments: when a call is made, the key is generated according to the parameters of the calling method, and the calling node is selected through the consistency hash algorithm according to the key. For example, call the method invoke(String s1,String s2); if hash.arguments is 1 (the default), only the parameter 1 (s1) of invoke is taken to generate the hashCode.
hash.nodes: is the number of copies of the node.
Only the first parameter Hash is selected by default. If you want to modify it, please configure <dubbo:parameter key="hash.arguments" value="0,1" /> 160 virtual nodes are used by default. If you want to modify them, please configure <dubbo:parameter key="hash.nodes" value="320" />
Implementation analysis of consistency Hash in Dubbo
The consistency hash of dubbo is implemented by the ConsistentHashLoadBalance class.
ConsistentHashLoadBalance internally defines the ConsistentHashSelector class, and finally selects nodes through this class. The doSelect method implemented by ConsistentHashLoadBalance uses the ConsistentHashSelector object created to select nodes.
The implementation of doSelect is as follows. When the method is called, it is created if the selector does not exist. Then select the node through the select method of ConsistentHashSelector.
@SuppressWarnings("unchecked") @Override protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) { // Get call method name String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName(); // Generate call list hashCode int identityHashCode = System.identityHashCode(invokers); // Get the consistency hash selector by calling the method named key ConsistentHashSelector<T> selector = (ConsistentHashSelector<T>) selectors.get(key); // Create a new selector if it does not exist if (selector == null || selector.getIdentityHashCode() != identityHashCode) { // All virtual nodes are generated when ConsistentHashSelector is created selectors.put(key, new ConsistentHashSelector<T>(invokers, invocation.getMethodName(), identityHashCode)); // Get selector selector = (ConsistentHashSelector<T>) selectors.get(key); } // Select node return selector.select(invocation); }
ConsistentHashSelector creates replicaNumber virtual nodes inside the constructor and stores them in TreeMap. Then the key is generated according to the parameters of the calling method, and a node is selected in the TreeMap for calling.
private static final class ConsistentHashSelector<T> { private final TreeMap<Long, Invoker<T>> virtualInvokers; // Virtual node private final int replicaNumber; // Copy number private final int identityHashCode;// hashCode private final int[] argumentIndex; // Parameter index array public ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) { // Create TreeMap to save nodes this.virtualInvokers = new TreeMap<Long, Invoker<T>>(); // Generate call node HashCode this.identityHashCode = System.identityHashCode(invokers); // Get Url // dubbo://169.254.90.37:20880/service.DemoService?anyhost=true&application=srcAnalysisClient&check=false&dubbo=2.8.4&generic=false&interface=service.DemoService&loadbalance=consistenthash&methods=sayHello,retMap&pid=14648&sayHello.timeout=20000&side=consumer×tamp=1493522325563 URL url = invokers.get(0).getUrl(); // Get the configured number of nodes, if not set, use the default value of 160 this.replicaNumber = url.getMethodParameter(methodName, "hash.nodes", 160); // Get the parameter array index that needs to be hashed. The first parameter is hashed by default 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]); } // Create virtual node // Generate replicaNumber virtual nodes for each invoker and store them in TreeMap for (Invoker<T> invoker : invokers) { for (int i = 0; i < replicaNumber / 4; i++) { // According to md5 algorithm, a message summary is generated for every 4 nodes, which is 16 bytes and 128 bits long. byte[] digest = md5(invoker.getUrl().toFullString() + i); // Then, the 128 bits are divided into four parts, 0-31, 32-63, 64-95, 95-128, and four 32 bits are generated, which are stored in long. The high 32 bits of long are all 0 // As the key of the virtual node. for (int h = 0; h < 4; h++) { long m = hash(digest, h); virtualInvokers.put(m, invoker); } } } } public int getIdentityHashCode() { return identityHashCode; } // Select node public Invoker<T> select(Invocation invocation) { // Generate Key according to calling parameters String key = toKey(invocation.getArguments()); // Generate message summary based on this parameter byte[] digest = md5(key); //Call hash(digest, 0) to convert message summary to hashcode. Here, only 0-31 bits are taken to generate hashcode //Call the sekectForKey method to select the node. Invoker<T> invoker = sekectForKey(hash(digest, 0)); return invoker; } private String toKey(Object[] args) { StringBuilder buf = new StringBuilder(); // Because hash.arguments is not configured, only the first parameter of the method is taken as the key for (int i : argumentIndex) { if (i >= 0 && i < args.length) { buf.append(args[i]); } } return buf.toString(); } //Select nodes according to hashCode private Invoker<T> sekectForKey(long hash) { Invoker<T> invoker; Long key = hash; // If the HashCode is directly the same as the key of a virtual node, the node will be returned directly if (!virtualInvokers.containsKey(key)) { // If not, find the node corresponding to the last key. SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key); // If it exists, return, for example, the position of hashCode in [1] in the figure // If it does not exist, for example, the hashCode falls in the position of [2], then select the first node in the treeMap // Use the firstKey method of TreeMap to select the minimum upper bound. if (tailMap.isEmpty()) { key = virtualInvokers.firstKey(); } else { key = tailMap.firstKey(); } } invoker = virtualInvokers.get(key); return invoker; } 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[0 + 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 = null; try { bytes = value.getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new IllegalStateException(e.getMessage(), e); } md5.update(bytes); return md5.digest(); } }
In the above code, the hash(byte[] digest, int number) method is used to generate the hashCode. This function converts the generated result to a long class, because the generated result is a 32-digit number, which may result in a negative number if saved with int. The range of hashCode of the logic ring generated by consistency hash is between 0 - max value. Therefore, it is a positive integer, so you need to cast it to long type to avoid negative number.
The method of node selection is select. Finally, the node is selected through the sekectForKey method.
// Select node public Invoker<T> select(Invocation invocation) { // Generate Key according to calling parameters String key = toKey(invocation.getArguments()); // Generate message summary based on this parameter byte[] digest = md5(key); //Call hash(digest, 0) to convert message summary to hashcode. Here, only 0-31 bits are taken to generate hashcode //Call the sekectForKey method to select the node. Invoker<T> invoker = sekectForKey(hash(digest, 0)); return invoker; }
The implementation of the sekectForKey method is as follows.
private Invoker<T> sekectForKey(long hash) { Invoker<T> invoker; Long key = hash; // If the HashCode is directly the same as the key of a virtual node, the node will be returned directly if (!virtualInvokers.containsKey(key)) { // If not, find the node corresponding to the last key. SortedMap<Long, Invoker<T>> tailMap = virtualInvokers.tailMap(key); // If it exists, return, for example, the position of hashCode in [1] in the figure // If it does not exist, for example, the hashCode falls in the position of [2], then select the first node in the treeMap // Use the firstKey method of TreeMap to select the minimum upper bound. if (tailMap.isEmpty()) { key = virtualInvokers.firstKey(); } else { key = tailMap.firstKey(); } } invoker = virtualInvokers.get(key); return invoker; }
When selecting, if the hashcode is directly the same as the key of a virtual node, the node will be returned directly. For example, the hashcode falls on a node (represented by a circle). If not, find the node corresponding to the last key. For example, the key for selection falls to the position marked in Figure 1. Due to the use of TreeMap storage, the location where the key is located may not find the minimum upper bound, such as the location marked in Figure 2. Then you need to return the minimum value in the TreeMap (to form a logical ring structure, if it cannot be found, the node at the beginning will be returned).
If you like my article, you can pay attention to the personal subscription number. Welcome to leave a message and communicate at any time.