Ribbon's final practice - creating a custom consistent hash load balancing strategy

Custom IRule

In this article, we implement a custom load balancing strategy-consistent hashing through the IRule interface.
The principle of consistent hashing is not the focus of this article. If you don’t understand, you can read this article "The principles and principles of consistent hashing algorithms" Realization", the writing is quite good.
Consistent hashing is widely used not only in the field of load balancing, but also in many other fields.
Let’s not talk nonsense and get straight to work!

First, we create a class under the ribbon-consumer module and inherit AbstractLoadBalancerRule and implement the IRule interface:
Insert image description here

Then get the HttpServletRequest object from the context:

HttpServletRequest request = ((ServletRequestAttributes) 
                Objects.requireNonNull(RequestContextHolder
                        .getRequestAttributes()))
                .getRequest();

We obtain information as a summary through the request object. Here we use uri as the summary:

String uri = request.getServletPath() + "?" + request.getQueryString();

We define the route method to implement the logic of finding the server:

public Server route(int hashId, List<Server> addressList){
    
    
        if (CollectionUtils.isEmpty(addressList)) {
    
    
            return null;
        }
        TreeMap<Long, Server> address = new TreeMap<>();
        addressList.forEach(e->{
    
    
            //把每个server利用for循环计算出若干个(8个)个不同的hash值,分布在圆环当中。
            //这个方式并不是均匀的分布,这里只是配合一致性哈希算法的思想做一个简单的例子。
            for (int i = 0; i < 8; i++) {
    
    
                long hash = hash(e.getId() + i);
                address.put(hash, e);
            }
        });
        long hash = hash(String.valueOf(hashId));
        SortedMap<Long, Server> last = address.tailMap(hash);
        //当request Url的hash值大于任意一个服务器对应的hashKey,
        //取address中的第一个节点
        if (last.isEmpty()) {
    
    
            return address.firstEntry().getValue();
        }
        return last.get(last.firstKey());
    }

Here, we use TreeMap to store the virtualized service node list, and then use the tailMap feature to obtain the subsequent server. If last is empty, we return the first element, because when last is empty, it means that this is the last element. This It's equivalent to us making it form a closed loop.

We also need a method to return a hash value here:

	public long hash(String key) {
    
    
        MessageDigest md5;
        try {
    
    
            md5 = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e){
    
    
            throw new RuntimeException(e);
        }

        byte [] bytes = key.getBytes(StandardCharsets.UTF_8);
        md5.update(bytes);

        byte[] digest = md5.digest();

        long hashCode = (digest[2] & 0xFF << 16)
                | (digest[1] & 0xFF << 8)
                | (digest[0] & 0xFF);

        return hashCode & 0xffffffffL;
    }

The digest here is the digest we want to get, and then we generate hashCode based on this digest. This digest is 16 digits, so we just pick the first three. We first take the second digit and move it forward 16 digits. Shift one bit by 8 bits, and bit 0 does not need to be shifted.

At this point our consistent hashing algorithm has been written. Now let’s test it.
Let’s first add the parameterless construction:
Insert image description here
Then we specify Ribbon's load balancing strategy as the MyRule we defined through configuration:
Insert image description here
Then, we start three eureka-client and ribbon-consumer:
Insert image description here
Let’s test it through Postman:
Insert image description here

Here we can find from testing that our requests are always made to the node 30001.
Then we change the request parameters:
Insert image description here

You can see that this request fell on 30000.
Then we try to take the 30000 node offline:
Insert image description here
Use Postman to request again:
Insert image description here
You can see that no matter how you request, This time it has fallen on the 30002 node.
At this point, we have learned how to use IRule to implement a customized load balancing strategy, and can apply it to our actual projects.

Guess you like

Origin blog.csdn.net/qq_45455361/article/details/121429997