Quickly understand load balancing

Quickly understand load balancing

1, Introduction to load balancing

Learn more and pay attention code Zatan !

1.1. Challenges faced by large websites

Large websites have to face the challenges of huge number of users, high concurrency, massive data and so on. In order to improve the overall performance of the system, vertical expansion and horizontal expansion can be adopted.

Vertical expansion: in the early stage of website development, the server processing capacity can be improved from the perspective of single machine by increasing hardware processing capacity, such as CPU processing capacity, memory capacity, disk, etc. However, a single machine has a performance bottleneck. Once the bottleneck is touched, if you want to improve it, the cost and price will be very high. This obviously can not meet all the challenges of large-scale distributed systems (websites), such as large traffic, high concurrency, massive data and so on.

Horizontal expansion: share the traffic of large websites through clusters. The application servers (nodes) in the cluster are usually designed to be stateless. Users can request any node, and these nodes share the access pressure together. Horizontal expansion has two main points:

Application Cluster: deploy the same application to multiple machines to form a processing cluster, receive requests distributed by load balancing devices, process them, and return corresponding data.
Load balancing: distribute user access requests to nodes in the cluster through some algorithm.

1.2. What is load balancing

Load Balance (LB) is an essential component of high concurrency and high availability system. The goal is to try our best to distribute the network traffic evenly to multiple servers, so as to improve the overall response speed and availability of the system.

The main functions of load balancing are as follows:
High concurrency: load balancing adjusts the load through the algorithm and tries to evenly distribute the workload of each node in the application cluster, so as to improve the concurrent processing capacity (throughput) of the application cluster.

Scalability: add or reduce the number of servers, and then the distribution is controlled by load balancing. This makes the application cluster scalable.

High availability: the load balancer can monitor candidate servers. When the server is unavailable, it will automatically skip and distribute the request to the available servers. This makes the application cluster highly available.

Security protection: some load balancing software or hardware provide security functions, such as black-and-white list processing, firewall, anti DDos attack, etc.

2, Classification of load balancing

There are many technologies supporting load balancing, which can be classified through different dimensions.

2.1 carrier dimension classification

From the perspective of carriers supporting load balancing, load balancing can be divided into two categories: Hardware load balancing and software load balancing

2.1.1 hardware load balancing

Hardware load balancing is generally an independent load balancing server running on a custom processor, which is expensive and exclusive to local tyrants. The mainstream products of hardware load balancing are F5 and A10.

Advantages of hardware load balancing:

Powerful: support global load balancing and provide more comprehensive and complex load balancing algorithms.
Strong performance: Hardware load balancing runs on a special processor, so it has a large throughput and can support more than one million concurrency of a single machine.
DDos firewall has high security and other functions.

Disadvantages of hardware load balancing:

Expensive: the cost of purchasing and maintaining hardware load balancing is high.
Poor scalability: when the number of visits increases sharply, it cannot be dynamically expanded beyond the limit.

2.1.2 software load balancing

Software load balancing is widely used in both large and small companies.
Software load balancing realizes load balancing from the software level and can generally run on any standard physical device.

The mainstream products of software load balancing are: Nginx, HAProxy and LVS.

LVS can be used as a four layer load balancer. Its load balancing performance is better than Nginx.
HAProxy can be used as HTTP and TCP load balancer.
Nginx and HAProxy can be used as four or seven layer load balancers.

Advantages of software load balancing:

Good scalability: adapt to dynamic changes. You can dynamically expand beyond the initial capacity by adding software load balancing instances.
Low cost: software load balancing can run on any standard physical device, reducing the cost of purchase and operation and maintenance.

Disadvantages of software load balancing:

Slightly poor performance: compared with hardware load balancing, the performance of software load balancing is slightly lower.

2.2 classification of network communication

From the perspective of communication, software load balancing can be divided into four layers and seven layers.

  1. Seven layer load balancing: the request can be forwarded to a specific host according to the HTTP request header and URL information of the visiting user.

DNS redirection
HTTP redirection
Reverse proxy

  1. Four layer load balancing: forwarding requests based on IP address and port.

Modify IP address
Modify MAC address

2.2.1 DNS load balancing

DNS load balancing is generally used in Internet companies, and complex business systems are not suitable for use. Large websites generally use DNS load balancing as the first level load balancing means, and then use other methods internally to do the second level load balancing. DNS LOAD BALANCING belongs to seven layer load balancing.

DNS, the domain name resolution service, is the OSI layer 7 network protocol. DNS is designed as a distributed application with tree structure. From top to bottom, it is: root domain name server, primary domain name server, secondary domain name server,..., local domain name server. Obviously, if all data is stored in the root domain name server, the load and overhead of DNS query will be very huge.

Therefore, compared with the DNS hierarchy, DNS query is a reverse recursive process. The DNS client requests the local DNS server, the upper DNS server, the upper DNS server,..., and the root DNS server (also known as the authoritative DNS server) in turn. Once hit, it returns immediately. In order to reduce the number of queries, each level of DNS server will set DNS query cache.

The working principle of DNS load balancing is to return the IP addresses of different servers according to the load based on DNS query cache.

Advantages of DNS redirection:
Simple to use: the load balancing work is handed over to the DNS server, which saves the trouble of maintaining the load balancing server

Improve performance: it can support address based domain name resolution and resolve it to the server address closest to the user (similar to the principle of CDN), which can speed up access speed and improve performance;

Disadvantages of DNS redirection:
Poor availability: DNS resolution is a multi-level resolution. After adding / modifying DNS, the resolution time is long; In the process of parsing, users will fail to visit the website;

Low scalability: the control of DNS load balancing is in the domain name provider, so it is impossible to improve and expand it;

Poor maintainability: it cannot reflect the current running state of the server; Few supported algorithms; The difference between servers cannot be distinguished (the load cannot be judged according to the state of the system and service).

2.2.2 HTTP load balancing

HTTP load balancing is based on HTTP redirection. HTTP load balancing belongs to seven layer load balancing.

The principle of HTTP redirection is: calculate a real server address according to the user's HTTP request, write the server address into the HTTP redirection response, return it to the browser, and the browser can access it again.

Advantages of HTTP redirection: simple scheme.

Disadvantages of HTTP redirection:
The performance of each access to the server is poor and needs to be increased by two times.

Reduce search ranking: using reset backward, search engines will be regarded as SEO cheating.

If the load balancer goes down, the site cannot be accessed.
Because of its obvious shortcomings, this load balancing strategy is less applied in practice.

2.2.3 reverse proxy load balancing

Reverse Proxy means that the proxy server accepts the network request, then forwards the request to the server in the intranet, and returns the result obtained from the server in the intranet to the client of the network request. Reverse Proxy load balancing belongs to seven layer load balancing.

Mainstream products of reverse proxy service: Nginx and Apache.

What is the difference between forward proxy and reverse proxy?
Forward proxy: it occurs on the client and is initiated by the user. Wall climbing software is a typical forward proxy. The client actively accesses the proxy server to enable the proxy server to obtain the required external network data, and then forward it back to the client.

Reverse proxy: it occurs on the server side, and the user does not know the existence of the proxy.

How does reverse proxy achieve load balancing? Take Nginx as an example, as follows:

First, set the load balancing rules on the proxy server. Then, when receiving the client request, the reverse proxy server intercepts the specified domain name or IP request and distributes the request to the candidate server according to the load balancing algorithm. Secondly, if a candidate server goes down, the reverse proxy server will have fault-tolerant processing. For example, if the distribution request fails more than 3 times, it will distribute the request to other candidate servers.

Advantages of reverse proxy:

  1. Multiple load balancing algorithms: support multiple load balancing algorithms to meet the needs of different scenarios.

  2. Can monitor the server: Based on HTTP protocol, you can monitor the status of the forwarding server, such as system load, response time, availability, number of connections, traffic, etc., so as to adjust the load balancing strategy according to these data.

Disadvantages of reverse proxy:

  1. Additional forwarding overhead: the forwarding operation of the reverse proxy itself has performance overhead, which may include creating a connection, waiting for the connection response, analyzing the response results and so on.
  2. Increase system complexity: reverse agent is often used for horizontal expansion of distributed applications, but the reverse agent service has the following problems. In order to solve the following problems, it will add additional complexity and operation and maintenance cost to the whole system:
    If the reverse proxy service goes down, it cannot access the site, so it needs to have a high availability scheme. The common schemes are: active and standby mode (one active and one standby), dual active mode (mutual active and standby).

The reverse proxy service itself also has a performance bottleneck. With the increasing number of requests to be forwarded, a scalable scheme is needed.

2.2.4 IP load balancing

IP load balancing is to perform load balancing by modifying the request destination address at the network layer.

As shown in the figure above, the IP equalization process is roughly as follows:
The client requests 192.168.137.10, and the load balancing server receives the message.

The load balancing server selects a service node 192.168.0.1 according to the algorithm, and then changes the message request address to the IP of the node.

The real service node receives the request message and returns the response data to the load balancing server after processing.

The load balancing server changes the source address of the response data to the address of the load balancing server and returns it to the client.

IP load balancing completes data distribution in the kernel process, which has better slave processing performance than reverse proxy load balancing. However, since all requests and responses must pass through the load balancing server, the throughput of the cluster is limited by the bandwidth of the load balancing server.

2.2.5 data link layer load balancing

Data link layer load balancing refers to modifying mac address in data link layer of communication protocol for load balancing.

The best link layer load balancing open source product on Linux platform is LVS (Linux Virtual Server). LVS is a load balancing system based on netfilter framework in Linux kernel. netfilter is a kernel Linux firewall mechanism, which can set several checkpoints (hook functions) according to rules to perform relevant operations during the flow of data packets.

The working process of LVS is roughly as follows:
When users visit www.sina.com com. Cn, the user data passes through the layer by layer network, and finally enters the LVS server network card through the switch, and enters the kernel network layer.
After entering preouting, through route search, it is determined that the destination VIP is the local IP address, so the data packet enters the INPUT chain
IPVS works on the INPUT chain. It will judge whether the request is an IPVS service according to the accessed vip+port. If so, call the registered IPVS HOOK function to carry out IPVS related main processes, forcibly modify the relevant data of the data packet, and send the data packet to the POSTROUTING chain.
After receiving the data packet on POSTROUTING, the data packet is finally sent to the back-end server through routing according to the target IP address (back-end server).

There are three working modes in the open source LVS version, and the working principles of each mode are quite different. It is said that each mode has its own advantages and disadvantages and is suitable for different application scenarios. However, the ultimate essential function is to achieve balanced traffic scheduling and good scalability. It mainly includes three modes: DR mode, NAT mode and Tunnel mode.

3, Load balancing algorithm

The implementation of load balancer can be divided into two parts:
Select a server from the list of candidate servers according to the load balancing algorithm;

Send the request data to the server.

Load balancing algorithm is the core of load balancing service. There are many kinds of load balancing products, but the principles of various load balancing algorithms are common. There are many kinds of load balancing algorithms, which are applicable to different application scenarios. This paper only introduces the characteristics and principles of the most common load balancing algorithms: polling, random, minimum active number, source address hash and consistency hash.

Note: for the implementation of load balancing algorithm, it is recommended to read the official description of Dubbo load balancing algorithm. The explanation of the source code is very detailed and worthy of reference.

3.1 random

3.1.1 random algorithm

The Random algorithm randomly distributes requests to candidate servers.

The random algorithm is suitable for scenarios with the same server hardware. Those who have studied probability theory know that when the number of calls is small, the load may be uneven. The larger the number of calls, the more balanced the load is.

[example] implementation example of random algorithm

Load balancing interface
public interface LoadBalance<N extends Node> {
​
    N select(List<N> nodes, String ip);
​
}

Load balancing abstract class
public abstract class BaseLoadBalance<N extends Node> implements LoadBalance<N> {
​
    @Override
    public N select(List<N> nodes, String ip) {
        if (CollectionUtil.isEmpty(nodes)) {
            return null;
        }
​
        // If there is only one node in the nodes list, you can return directly without load balancing
        if (nodes.size() == 1) {
            return nodes.get(0);
        }
​
        return doSelect(nodes, ip);
    }
​
    protected abstract N doSelect(List<N> nodes, String ip);
​
}

Server node class
public class Node implements Comparable<Node> {
​
    protected String url;
​
    protected Integer weight;
​
    protected Integer active;
​
    // ...
}

Random algorithm implementation
public class RandomLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    private final Random random = new Random();
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
        // Select a node randomly in the list
        int index = random.nextInt(nodes.size());
        return nodes.get(index);
    }
​
}

3.1.2 weighted random algorithm

The Weighted Random algorithm adjusts the weight according to the probability on the basis of the random algorithm to distribute the load.

[[example] implementation example of weighted random algorithm
public class WeightRandomLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    private final Random random = ThreadLocalRandom.current();
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
​
        int length = nodes.size();
        AtomicInteger totalWeight = new AtomicInteger(0);
        for (N node : nodes) {
            Integer weight = node.getWeight();
            totalWeight.getAndAdd(weight);
        }
​
        if (totalWeight.get() > 0) {
            int offset = random.nextInt(totalWeight.get());
            for (N node : nodes) {
                // Let the random value offset subtract the weight value
                offset -= node.getWeight();
                if (offset < 0) {
                    // Return the corresponding Node
                    return node;
                }
            }
        }
​
        // Directly return one at random
        return nodes.get(random.nextInt(length));
    }
​
}

3.2 polling

3.2.1 polling algorithm

The strategy of Round Robin algorithm is to distribute requests to candidate servers in turn.

As shown in the figure below, the load balancer receives six requests from the client, and the requests of (1, 3, 5) will be sent to server 1, and the requests of (2, 4, 6) will be sent to server 2.

The algorithm is suitable for scenarios: the processing capacity of each server is similar, and the workload of each transaction is not different. If there is a big difference, the slow processing server may backlog requests and eventually cannot bear the excessive load.

[example] example of polling algorithm

Implementation of polling load balancing algorithm
public class RoundRobinLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    private final AtomicInteger position = new AtomicInteger(0);
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
        int length = nodes.size();
        // If the position value is already equal to the number of nodes, reset to 0
        position.compareAndSet(length, 0);
        N node = nodes.get(position.get());
        position.getAndIncrement();
        return node;
    }
​
}

3.2.2 weighted polling algorithm

The weighted round robin algorithm adds the weight attribute to adjust the number of requests from the forwarding server on the basis of the polling algorithm. Nodes with high performance and fast processing speed should set higher weights to give priority to distributing requests to nodes with higher weights when scoring.

As shown in the figure below, server A sets the weight to 5 and server B sets the weight to 1. When the load balancer receives 6 requests from the client, then (1, 2, 3, 4, 5) requests will be sent to server A and (6) requests will be sent to server B.

Learn more and pay attention code Zatan !

[example] implementation example of weighted polling algorithm

The following implementation is based on Dubbo The weighted polling algorithm is simplified.
public class WeightRoundRobinLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    /**
     * 60 second
     */
    private static final int RECYCLE_PERIOD = 60000;
​
    /**
     * Node hashcode Mapping to WeightedRoundRobin
     */
    private ConcurrentMap<Integer, WeightedRoundRobin> weightMap = new ConcurrentHashMap<>();
​
    /**
     * Atomic update lock
     */
    private AtomicBoolean updateLock = new AtomicBoolean();
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
​
        int totalWeight = 0;
        long maxCurrent = Long.MIN_VALUE;
​
        // Get current time
        long now = System.currentTimeMillis();
        N selectedNode = null;
        WeightedRoundRobin selectedWRR = null;
​
        // The following cycle mainly does the following things:
        //   1. Traverse the Node list to check whether the current Node has a corresponding WeightedRoundRobin. If not, create it
        //   2. Check whether the Node weight has changed. If so, update the weight field of WeightedRoundRobin
        //   3. Add its own weight to the current field, which is equivalent to current += weight
        //   4. Set the lastUpdate field, that is, lastUpdate = now
        //   5. Find the Node with the largest current and the WeightedRoundRobin corresponding to the Node,
        //      Temporarily stored for later use
        //   6. Calculate the weight sum
        for (N node : nodes) {
            int hashCode = node.hashCode();
            WeightedRoundRobin weightedRoundRobin = weightMap.get(hashCode);
            int weight = node.getWeight();
            if (weight < 0) {
                weight = 0;
            }
​
            // Check whether the current Node has a corresponding WeightedRoundRobin. If not, create it
            if (weightedRoundRobin == null) {
                weightedRoundRobin = new WeightedRoundRobin();
                // Set Node weight
                weightedRoundRobin.setWeight(weight);
                // Store the mapping relationship between url unique identifier identifyString and weightedRoundRobin
                weightMap.putIfAbsent(hashCode, weightedRoundRobin);
                weightedRoundRobin = weightMap.get(hashCode);
            }
            // The Node weight is not equal to the weight saved in WeightedRoundRobin, indicating that the weight has changed. Update it at this time
            if (weight != weightedRoundRobin.getWeight()) {
                weightedRoundRobin.setWeight(weight);
            }
​
            // Let current add its own weight, which is equivalent to current += weight
            long current = weightedRoundRobin.increaseCurrent();
            // Set lastUpdate to indicate that it has been updated recently
            weightedRoundRobin.setLastUpdate(now);
            // Find the largest current
            if (current > maxCurrent) {
                maxCurrent = current;
                // Assign the Node with the maximum current weight to selectedNode
                selectedNode = node;
                // Assign the weightedRoundRobin corresponding to Node to selectedWRR for later use
                selectedWRR = weightedRoundRobin;
            }
​
            // Calculate weight sum
            totalWeight += weight;
        }
​
        // Check the weightMap and filter out the nodes that have not been updated for a long time.
        // The node may hang up, and the node is not included in nodes, so the lastUpdate of the node cannot be updated for a long time.
        // If the duration of non update exceeds the threshold, it will be removed, and the default threshold is 60 seconds.
        if (!updateLock.get() && nodes.size() != weightMap.size()) {
            if (updateLock.compareAndSet(false, true)) {
                try {
                    // Traverse modification, that is, remove expired records
                    weightMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
                } finally {
                    updateLock.set(false);
                }
            }
        }
​
        if (selectedNode != null) {
            // Let current subtract the sum of weights, which is equivalent to current -= totalWeight
            selectedWRR.decreaseCurrent(totalWeight);
            // Returns the Node with the largest current
            return selectedNode;
        }
​
        // should not happen here
        return nodes.get(0);
    }
​
    protected static class WeightedRoundRobin {
​
        // Service provider weight
        private int weight;
        // Current weight
        private AtomicLong current = new AtomicLong(0);
        // Last update time
        private long lastUpdate;
​
        public long increaseCurrent() {
            // current = current + weight;
            return current.addAndGet(weight);
        }
​
        public long decreaseCurrent(int total) {
            // current = current - total;
            return current.addAndGet(-1 * total);
        }
​
        public int getWeight() {
            return weight;
        }
​
        public void setWeight(int weight) {
            this.weight = weight;
            // Initially, current = 0
            current.set(0);
        }
​
        public AtomicLong getCurrent() {
            return current;
        }
​
        public void setCurrent(AtomicLong current) {
            this.current = current;
        }
​
        public long getLastUpdate() {
            return lastUpdate;
        }
​
        public void setLastUpdate(long lastUpdate) {
            this.lastUpdate = lastUpdate;
        }
​
    }
​
}

3.3 minimum active number

The Least Active algorithm distributes the request to the candidate server with the least number of connections / requests (the server that currently processes the least requests).

Features: dynamically allocate according to the current number of requested connections of the candidate server.
Scenario: applicable to scenarios that are sensitive to system load or have a large difference in the duration of requesting connection.

Because the connection duration of each request is different, if a simple round robin or random algorithm is adopted, the current connection number of some servers may be too large, while the connection of other servers may be too small, resulting in the load is not really balanced. Although both polling and algorithms can adjust the load by weighting heavy attributes, the weighting method is difficult to deal with dynamic changes.

For example, in the following figure, (1, 3, 5) requests will be sent to server 1, but (1, 3) will be disconnected soon. At this time, only (5) requests to connect to server 1; (2, 4, 6) the request is sent to server 2, and only (2) is disconnected. When the system continues to run, server 2 will bear excessive load.

The minimum active number algorithm will record the number of connections being processed by each candidate node at the current time, and then select the node with the minimum number of connections. This strategy can dynamically and real-time reflect the current situation of the server, reasonably distribute the responsibility evenly, and is suitable for scenarios that are sensitive to the current system load.

For example, in the following figure, if the current number of connections of server 1 is the smallest, the new request 6 will be sent to server 1.

Weighted Least Connection assigns a weight to each server according to the performance of the server on the basis of the minimum active number, and then calculates the number of connections that each server can handle according to the weight.

Implementation points of minimum active number algorithm: the smaller the number of active calls, the higher the processing capacity of the service node, and more requests can be processed per unit time. Requests should be distributed to the service first. In the specific implementation, each service node corresponds to an active number active. Initially, the active number of all service providers is 0. Each time a request is received, the active number is increased by 1. After the request is completed, the active number is reduced by 1. After the service runs for a period of time, the service provider with good performance processes the request faster, so the active number decreases faster. At this time, such service provider can give priority to obtaining new service requests. This is the basic idea of the minimum active number load balancing algorithm.

[example] implementation of minimum active number algorithm

```handlebars
 The following implementation is based on Dubbo The minimum active number load balancing algorithm has made some changes.
public class LeastActiveLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    private final Random random = new Random();
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
        int length = nodes.size();
        // Minimum active number
        int leastActive = -1;
        // The number of service providers (hereinafter referred to as Node) with the same "minimum number of activities"
        int leastCount = 0;
        // leastIndexs is used to record the subscript information of nodes with the same "minimum active number" in the nodes list
        int[] leastIndexs = new int[length];
        int totalWeight = 0;
        // The Node weight value of the first minimum active number is used to compare with the weight of other nodes with the same minimum active number,
        // To check whether the weights of all nodes with the same minimum number of active nodes are equal
        int firstWeight = 0;
        boolean sameWeight = true;
​
        // Traverse the nodes list
        for (int i = 0; i < length; i++) {
            N node = nodes.get(i);
            // Find a smaller active number and start over
            if (leastActive == -1 || node.getActive() < leastActive) {
                // Update the minimum active number leastActive with the current active number
                leastActive = node.getActive();
                // Update leastCount to 1
                leastCount = 1;
                // Record the current subscript value into leastIndexs
                leastIndexs[0] = i;
                totalWeight = node.getWeight();
                firstWeight = node.getWeight();
                sameWeight = true;
​
                // The number of active nodes in the current Node Getactive() is the same as the minimum active number leastActive
            } else if (node.getActive() == leastActive) {
                // Record the subscript of the current Node in the nodes collection in leastindex
                leastIndexs[leastCount++] = i;
                // Cumulative weight
                totalWeight += node.getWeight();
                // Check whether the weight of the current Node is equal to the firstWeight,
                // If not, set sameWeight to false
                if (sameWeight && i > 0
                    && node.getWeight() != firstWeight) {
                    sameWeight = false;
                }
            }
        }
​
        // When only one Node has the minimum active number, you can directly return to the Node
        if (leastCount == 1) {
            return nodes.get(leastIndexs[0]);
        }
​
        // Multiple nodes have the same minimum active number, but their weights are different
        if (!sameWeight && totalWeight > 0) {
            // Randomly generate a number between [0, totalweight]
            int offsetWeight = random.nextInt(totalWeight);
            // Loop the random number minus the weight value of the Node with the minimum active number,
            // When offset is less than or equal to 0, the corresponding Node is returned
            for (int i = 0; i < leastCount; i++) {
                int leastIndex = leastIndexs[i];
                // Get the weight value and let the random number subtract the weight value
                offsetWeight -= nodes.get(leastIndex).getWeight();
                if (offsetWeight <= 0) {
                    return nodes.get(leastIndex);
                }
            }
        }
        // If the weight is the same or the weight is 0, a Node is returned randomly
        return nodes.get(leastIndexs[random.nextInt(leastCount)]);
    }
​
}

3.4 source address hash

The source address hash (IP Hash) algorithm obtains a value through hash calculation according to the source IP of the request. The value is used for modular operation in the list of candidate servers, and the result is the selected server.

It can ensure that the requests of clients with the same IP will be forwarded to the same server to realize session Sticky Session.
Features: ensure that specific users always request to the same server. If the server goes down, the session will be lost.

[example] implementation example of source address hash algorithm

public class IpHashLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    @Override
    protected N doSelect(List<N> nodes, String ip) {
        if (StrUtil.isBlank(ip)) {
            ip = "127.0.0.1";
        }
​
        int length = nodes.size();
        int index = hash(ip) % length;
        return nodes.get(index);
    }
​
    public int hash(String text) {
        return HashUtil.fnvHash(text);
    }
​
}

Learn more and pay attention code Zatan !

3.5 consistency hash

The goal of Consistent Hash algorithm is that the same request falls on the same server as much as possible.

Consistent hashing can solve the problem of stability. All storage nodes can be arranged on the Hash ring connected from head to tail. Each key will find the adjacent storage node clockwise after calculating the Hash. When a node joins or exits, it only affects the subsequent nodes clockwise adjacent to the node on the Hash ring.

1) The same request refers to: generally, when using consistent hash, you need to specify a key for hash calculation, which may be:
User ID

Requestor IP

Request service name, string composed of parameter list
2) As far as possible: the server may go online and offline, and the changes of a few servers should not affect most requests.

When a candidate server goes down, the requests originally sent to the server will be shared among other candidate servers based on the virtual node without causing drastic changes.

Advantages: adding and deleting nodes only affect the clockwise adjacent nodes in the hash ring, and have no impact on other nodes.

Disadvantages: adding or subtracting nodes will cause some data in the hash ring to fail to hit. When a small number of nodes are used, the changes of nodes will affect the data mapping in the hash ring in a wide range, which is not suitable for the distributed scheme of a small number of data nodes. Common consistent hash partitions need to double or subtract half of the nodes when increasing or decreasing nodes to ensure the balance of data and load.

Note: because of these shortcomings of consistent hash partition, some distributed systems use virtual slots to improve consistent hash, such as Dynamo system.

[[example] example of consistent hash algorithm
public class ConsistentHashLoadBalance<N extends Node> extends BaseLoadBalance<N> implements LoadBalance<N> {
​
    private final ConcurrentMap<String, ConsistentHashSelector<?>> selectors = new ConcurrentHashMap<>();
​
    @SuppressWarnings("unchecked")
    @Override
    protected N doSelect(List<N> nodes, String ip) {
        // The number of slices is set as 4 times of the number of nodes
        Integer replicaNum = nodes.size() * 4;
        // Get the original hashcode of nodes
        int identityHashCode = System.identityHashCode(nodes);
​
        // If nodes is a new List object, it means that the number of nodes has changed
        // The selector identityHashCode !=  Identityhashcode condition holds
        ConsistentHashSelector<N> selector = (ConsistentHashSelector<N>) selectors.get(ip);
        if (selector == null || selector.identityHashCode != identityHashCode) {
            // Create a new ConsistentHashSelector
            selectors.put(ip, new ConsistentHashSelector<>(nodes, identityHashCode, replicaNum));
            selector = (ConsistentHashSelector<N>) selectors.get(ip);
        }
        // Call the select method of ConsistentHashSelector to select the Node
        return selector.select(ip);
    }
​
    /**
     * Consistent hash selector
     */
    private static final class ConsistentHashSelector<N extends Node> {
​
        /**
         * Storage virtual node
         */
        private final TreeMap<Long, N> virtualNodes;
​
        private final int identityHashCode;
​
        /**
         * constructor 
         *
         * @param nodes            Node list
         * @param identityHashCode hashcode
         * @param replicaNum       Number of slices
         */
        ConsistentHashSelector(List<N> nodes, int identityHashCode, Integer replicaNum) {
            this.virtualNodes = new TreeMap<>();
            this.identityHashCode = identityHashCode;
            // Get the number of virtual nodes. The default is 100
            if (replicaNum == null) {
                replicaNum = 100;
            }
            for (N node : nodes) {
                for (int i = 0; i < replicaNum / 4; i++) {
                    // Perform md5 operation on the url to obtain a 16 byte array
                    byte[] digest = md5(node.getUrl());
                    // Four hash operations are performed on some bytes of digest to obtain four different long positive integers
                    for (int j = 0; j < 4; j++) {
                        // When h = 0, take 4 bytes with subscript 0 ~ 3 in digest for bit operation
                        // When h = 1, take 4 bytes with subscripts 4 ~ 7 in digest for bit operation
                        // The process is the same as above when h = 2 and H = 3
                        long m = hash(digest, j);
                        // Store the mapping relationship from hash to node in virtualNodes,
                        // virtualNodes needs to provide efficient query operations, so TreeMap is selected as the storage structure
                        virtualNodes.put(m, node);
                    }
                }
            }
        }
​
        public N select(String key) {
            // md5 operation on parameter key
            byte[] digest = md5(key);
            // Take the first four bytes of the digest array for hash operation, and then pass the hash value to the selectForKey method,
            // Find the right Node
            return selectForKey(hash(digest, 0));
        }
​
        private N selectForKey(long hash) {
            // Find the first node greater than or equal to the current hash
            Map.Entry<Long, N> entry = virtualNodes.ceilingEntry(hash);
            // If the hash is greater than the maximum position of the Node on the hash ring, then entry = null,
            // The header node of TreeMap needs to be assigned to entry
            if (entry == null) {
                entry = virtualNodes.firstEntry();
            }
            // Return to Node
            return entry.getValue();
        }
​
    }
​
    /**
     * Calculate hash value
     */
    public static 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;
    }
​
    /**
     * Calculate MD5 value
     */
    public static 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();
    }
​
}

The above example makes some simplification based on Dubbo's consistent hash load balancing algorithm.

Learn more and pay attention code Zatan !

Guess you like it

Mysql database optimization script

How HTTPS ensures data transmission security -- TLS protocol

Kafka high throughput, high performance core technology and best application scenario

Five minutes to build a real-time monitoring system based on Prometheus + Grafana
Learn more and pay attention code Zatan !

Welfare at the end of the article

Welcome to the official account message, free gift book, 1 single / person, first come first served!
EBook list:
Growth hackers: Secrets of startups' users and revenue growth
Man moon myth: one of the two bibles of Internet man
Dullness (Japan) by Junichi Watanabe: managing with dullness makes you healthier
In depth understanding of Nginx module development and architecture analysis version 2
Computational advertising: the history of Internet computable advertising
Stay inside: Chinese government and economic development
Learn more and pay attention code Zatan !

Learn more and pay attention code Zatan !

#Architecture | high availability | high performance | high concurrency | high error tolerance | HTTP|TLS | network | cloud native | interview#

Keywords: Operation & Maintenance Load Balance architecture

Added by FuriousIrishman on Thu, 24 Feb 2022 08:13:46 +0200