SpringCloud系列之负载均衡Ribbon·12-自定义IRule_一致性哈希 image.png

一致性哈希算法:

SpringCloud系列之负载均衡Ribbon·12-自定义IRule_一致性哈希_02 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,然后在环上查到下一个服务节点即可,这就完成了一致性哈希算法的负载均衡。