The pursuit of extreme to overcome the limit
First, case background 1.1 system introduction
First look at the system architecture, convenient to explain:
The function that the page shows to the user is that you can view some properties of any one machine (hereinafter referred to as System information).
The message flow is that the page initiates a request to view the system information of the specified machine to the background, and the background can query to which server is serving, according to the load Balancing algorithm (simple polling) which server is queried and sends the message to Kafka, Then all of the server consumes Kafka information, when the information that is found in the consumption request itself to query, the connection of the designated machine to query, and return the results back.
Server is a clustered architecture that may be dynamically increased or decreased.
As to why the architecture is so designed, it is not the focus, only that it is in line with the environment of the optimal architecture.
1.2 Experiencing problems
The problem is slow, especially slow, after the initial verification, the most time-consuming thing is the server connection machine, the basic is about 5s, which is unacceptable.
1.3 Preliminary optimization
Because the most time-consuming is the server connection machine, so decided to cache machine on the server side of the connection, after testing if the use of the connection cache query, then the time-consuming will be controlled within 1 seconds to meet the user's requirements, but there is a problem resulting, That is, according to the existing load balancing algorithm, if Server1 has cached the connection to the MACHINE1, but when querying again, the request will be sent to the next server, such as Server2, which leads to two problems, one is that the re-establishment of the connection takes a long time, two, Two servers simultaneously cache the connection to machine1, resulting in a wasted connection.
1.4 Continue to optimize
The first thought is the simplest is to query machine hash calculation, and in addition to the number of sever, so as to ensure that the query the same machine will require the same server to operate, to meet the initial requirements. However, because the server side is a cluster, the machine is likely to increase or decrease dynamically, if based on the hash calculation, the specified machines will be the specified server connection, such as:
Then an additional server is added, then the connection between server and machine will become as follows according to the current hash algorithm:
It can be found that four machine and server connection relationship has changed, which will lead to 4 connection initialization, and four connection waste, although the probability of server cluster change is very small, but every change will be half of the connection is voided, which is unacceptable, The ideal result of the thought was:
- When the machine is added, the original connection is part of the new machine, but it remains unchanged except for the disconnected connection.
- When the machine is reduced, the connection of the machine is reduced to the rest of the machine, but the original connection of the machine remains unchanged.
Simply put, the change is unavoidable , but the change is minimized . According to this idea, I think of the consistency hash, think this should be able to meet the requirements.
Second, the use of consistent hash to solve the problem
The definition of a consistent hash or introduction in the third section, now write a consistent hash of the Java solution. Only write example implementation code, the first and foremost is the choice of hash algorithm, according to the existing situation and the performance of the hash algorithm has been selected, the FNV hash algorithm, the following is its implementation:
Public Static intFnvhash (String key) {Final intp = 16777619; Longhash = (int) 2166136261L; for(inti = 0,n = Key.length (); I < n; i++) {Hash= (hash ^ key.charat (i)) *p; } Hash+ = Hash << 13; Hash^= Hash >> 7; Hash+ = Hash << 3; Hash^= Hash >> 17; Hash+ = Hash << 5; return((int) Hash & 0x7FFFFFFF);}
Then the server that can provide the service is preprocessed:
Public StaticConcurrentskiplistmap<integer, string>init () {//Create a sort map to facilitate subsequent calculationsConcurrentskiplistmap<integer,string> servers=NewConcurrentskiplistmap<>(); //get the server that can provide servicesList<string> serverurls=arrays.aslist ("192.168.2.1:8080", "192.168.2.2:8080", "192.168.2.3:8080"); //add server to map in turn for(String serverurl:serverurls) {servers.put (Fnvhash (ServerURL), ServerURL); //The following three are the three virtual nodes of the current server, the hash is differentServers.put (Fnvhash (serverurl+ "#1"), ServerURL); Servers.put (Fnvhash (ServerURL+ "#2"), ServerURL); Servers.put (Fnvhash (ServerURL+ "#3"), ServerURL); } returnservers;}
This code will be able to provide the server into a sort map, the key is its hash value, the value of the server's host and IP, next to each request to connect to the Machin compute which server to connect:
/** * @parammachine to be connected *@paramServers Server * available for service@return */Private StaticString Getserver (intMachine, Concurrentskiplistmap<integer, string>servers) { intleft=Integer.max_value; intright=Integer.max_value; intLeftdis=0; intRightdis=0; for(Entry<integer, string>Server:servers.entrySet ()) { intkey=Server.getkey (); if(key<Machine ) { Left=key; }Else{ Right=key; } if(right!=integer.max_value) { Break; } } if(left==integer.max_value) { Left=Servers.lastkey (); Leftdis=integer.max_value-left+Machine ; }Else{Leftdis=machine-Left ; } if(right==integer.max_value) { Right=Servers.firstkey (); Rightdis=integer.max_value-machine+Right ; }Else{Rightdis=right-Machine ; } returnServers.get (Rightdis<=leftdis?right:left);}
This method is calculated, the specific logic can be read in the next section to have a deeper understanding.
After the above three methods to solve the above requirements, after testing is also perfect, perhaps the algorithm still do not understand, perhaps the same hash algorithm does not know what, virtual node is what, but now should understand how the demand is produced, has met the requirements through what, Now the only thing to do is to understand the consistency hash, which is described below.
Three, the consistency hash introduction 3.1 Theory Introduction
Consistent hash of the introduction, from the Baidu Encyclopedia.
The consistent hashing algorithm, proposed by MIT in 1997, was designed to address hot spot issues in the Internet. Consistent hashing presents the 4 adaptive conditions that the hashing algorithm should meet in a dynamically changing cache environment:
Equalization (Balance):
Balance means that the result of the hash can be distributed to all buffers as much as possible, thus allowing all buffer space to be exploited. Many hashing algorithms can satisfy this condition.
Monotonicity (monotonicity):
Monotonicity means that if some content has been allocated to the appropriate buffers by hashing, and a new buffer is added to the system, the result of the hash should be to ensure that the original allocated content can be mapped to the new buffer without being mapped to the other buffers in the old buffer collection. (This translation information has a negative value when the buffer size changes when the consistency hash (consistent hashing) tries to protect the allocated content from being remapped to the new buffer. )
Dispersion (Spread):
In a distributed environment, the terminal may not see all of the buffers, but can see only a subset of them. The end result is that the same content is mapped to different buffers by different endpoints when the terminal wants the content to be mapped to buffering through a hashing process, because the buffer range seen by different terminals may be different, resulting in inconsistent results for the hash. This is obviously something that should be avoided because it causes the same content to be stored in different buffers, reducing the efficiency of the system's storage. The definition of dispersion is the severity of the above-mentioned situation. A good hashing algorithm should be able to avoid inconsistencies as far as possible, that is, to minimize dispersion.
Payload (load):
The load problem is actually looking at the dispersion problem from another perspective. Since different terminals may map the same content to different buffers, it is possible for a particular buffer to be mapped to different content by different users. As with dispersion, this situation should also be avoided, so a good hashing algorithm should be able to minimize the buffering load.
3.2 Design Implementation
The general consistency hash design implementation is as follows:
First of all the hash value should constitute a ring, just like the moment of the clock, that is, there is a clear hash maximum, the number of hash in the ring is generally 2 of the 32-time Square:
Map the server to the ring via hash calculations, and note that you can select unique attributes that differentiate the server from open, such as IP plus port:
Then all of the requests use a unique attribute to calculate the hash value, and then request to the nearest server:
If a new machine is added:
Requests that are next to the new machine are redirected to the new server, and if a machine hangs, the request to hang up the machine is reassigned to the nearest server:
With the illustration above, it should be possible to see the benefits of ring design, that is, whether the machine is added or reduced, the change is the request near the change machine, the requested map will not change to the existing node.
Iv. understanding of the consistency Hash 4.1 application scenarios
Consistent hash of the characteristics of the consistency of the hash to ensure that the minimum change, the comparison is applicable to stateful connection, if the connection is stateless, then there is no need to use this algorithm, polling or random is possible. Efficiency is higher than the consistency hash, eliminating the computational process of each request.
Choice of 4.2-ring hash quantity
There is no special requirement in nature, the principle of selection can consider the following points:
- The number of hashes is the best, because consider the future of the new server, as well as the addition of virtual nodes
- The maximum value of the hash quantity is within the range of int, and the int maximum is already large enough to increase the calculation and storage cost relative to the Int.
- Another reference point for the maximum value of the hash number is the maximum value of the selected hash algorithm
So the above example, the number of ring hash selected 2^32, just FNV hash algorithm of the maximum value is also it, FNV hash algorithm reference this.
4.3 The role of virtual nodes
Read the above code should know that when the server hash, the server will be created at the same time several virtual nodes, they also represent their server, has the following role:
- To prevent the hash duplication of the server, although the probability of hash duplication is small, but still can not be completely avoided, so by using multiple virtual nodes, you can avoid server hash duplication caused the server to be completely overwritten
- In favor of load balancing, if each server has only one node, then it is possible to distribute unevenly, this time through multiple virtual nodes, can increase the likelihood of uniform distribution, of course, this depends on the choice of hash algorithm
As for the number of virtual nodes, this does not require, the number of nodes, the better the load balance, but the greater the computational capacity, if the server cluster is considered volatile, each request will need to recalculate the server and its virtual node hash value, then the number of nodes is not too large, Otherwise, it is also a performance bottleneck.
Selection of 4.4 hash algorithm
Hash algorithm has many kinds, the above FNV hash can refer to, as for the other, consider the following points can be:
- Do not write your own hash algorithm, with the existing can, for the purpose of learning can be written, production environment with the existing hash algorithm
- Algorithm speed must be fast
- The same input value, to have the same output
- Hash value is enough hash, hash collision probability is low
Consider the above points can be, follow-up will be for the hash algorithm, write a blog.
4.5 Alternative to consistent hash
No consistent hash can, can meet the same needs, the answer is yes, that is to actively maintain a routing table. The basic things to do are:
- First, get the server that currently provides the service
- When a request arrives, first determine whether the current request has a corresponding server, if there is to the corresponding server, if none, select the least load of a server, coexistence records
- After the server hangs up, the new request goes back 2 steps
- When a new server joins, you can proactively load balance, or you can go back 2 steps
The pros and cons simply say:
Advantages:
-
- More balanced load, even with full equalization, because the uncertainty of not relying on hash
- The entire allocation process is artificially mastered, and when certain requests must be assigned to the specified server, the modifications are simpler
Disadvantages:
-
- Large number of coding needs rigorous testing
- Need to proactively maintain a routing table, storage is an issue to consider
- When the request volume is large, the routing table capacity will increase, you can consider to deposit in Redis
The above is my understanding of the consistent hash, as well as my application in the project, hoping to help people in need.
"Data structure and algorithm" consistency hash algorithm and Java practice