一致性哈希算法:
image.png在请求中我们可以使用一致性哈希算法来做负载均衡,如图中,来访的请求我们找一个特征量,可以是url或者参数之类的计算出一个int值,然后这个int值落在这个环上之后,我们需要使用顺时针或者逆时针的方式寻找下一个服务的节点。
public class MyRule extends AbstractLoadBalancerRule implements IRule{
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
public Server choose(Object key) {
HttpServletRequest request =
((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
String uri = request.getServletPath() + "?" + request.getQueryString();
return route(uri.hashCode(),getLoadBalancer().getAllServers());
}
public Server route(int hashId, List<Server> addressList){
if(CollectionUtils.isEmpty(addressList))return null;
final TreeMap<Long,Server> treeMap = new TreeMap<Long, Server>();
addressList.forEach(server->{
//虚化若干个服务节点,到环上
for(int i=0;i<8;i++){
long hash = hash(server.getId()+i);
treeMap.put(hash,server);
}
});
long hash = hash(String.valueOf(hashId));
SortedMap<Long,Server> last = treeMap.tailMap(hash);
//当requestURL的hash值大于任意一个服务器对应的hashKey,那么就取address中的第一个节点
if(last.isEmpty()){
treeMap.firstEntry().getValue();
}
return last.get(last.firstKey());
}
public long hash(String key){
MessageDigest md5;
try {
md5 = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
byte[] keyByte;
try {
keyByte = key.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
md5.update(keyByte);
byte[] digest = md5.digest();
long hashCode = ( (long)(digest[2] & 0xFF << 16) ) |
( (long)(digest[1] & 0xFF << 8) ) |
( (long)(digest[0] & 0xFF) );
return hashCode & 0xffffffff;
}
}
如上代码,我们在实现一个自定义的IRule的时候,其实继承一个实现了IRule的抽象类即可,然后重写choose这个方法,可以看出我们重写choose的方法中,使用的就是一致性哈希算法来做的,首先获取到所有的服务,然后给每一台服务增加8个节点,然后使用TreeMap构成了一个环,然后再根据请求的URL再计算出一个hashCode,然后在环上查到下一个服务节点即可,这就完成了一致性哈希算法的负载均衡。
网友评论