类型 | 算法名称 | 描述 | ||
---|---|---|---|---|
RandomLoadBalancer | 负载均衡随机算法 | |||
LocalPreferenceLoadBalancer | 本地优先随机算法 | |||
ConsistentHashLoadBalancer | 一致性hash算法 | |||
RoundRobinLoadBalancer | 轮询算法 | |||
WeightRoundRobinLoadBalancer | 轮询算法 |
public class RandomLoadBalancer extends AbstractLoadBalancer {
private final Random random = new Random();
public RandomLoadBalancer(ConsumerBootstrap consumerBootstrap) {
super(consumerBootstrap);
}
public ProviderInfo doSelect(SofaRequest invocation, List providerInfos) {
ProviderInfo providerInfo = null;
int size = providerInfos.size();
int totalWeight = 0;
boolean isWeightSame = true;
int offset;
int i;
for(offset = 0; offset < size; ++offset) {
i = this.getWeight((ProviderInfo)providerInfos.get(offset));
totalWeight += i;
if (isWeightSame && offset > 0 && i != this.getWeight((ProviderInfo)providerInfos.get(offset - 1))) {
isWeightSame = false;
}
}
if (totalWeight > 0 && !isWeightSame) {
offset = this.random.nextInt(totalWeight);
for(i = 0; i < size; ++i) {
offset -= this.getWeight((ProviderInfo)providerInfos.get(i));
if (offset < 0) {
providerInfo = (ProviderInfo)providerInfos.get(i);
break;
}
}
} else {
providerInfo = (ProviderInfo)providerInfos.get(this.random.nextInt(size));
}
return providerInfo;
}
}
@Extension("roundRobin")
public class RoundRobinLoadBalancer extends AbstractLoadBalancer {
private final ConcurrentMap sequences = new ConcurrentHashMap();
public RoundRobinLoadBalancer(ConsumerBootstrap consumerBootstrap) {
super(consumerBootstrap);
}
public ProviderInfo doSelect(SofaRequest request, List providerInfos) {
String key = this.getServiceKey(request);
int length = providerInfos.size();
PositiveAtomicCounter sequence = (PositiveAtomicCounter)this.sequences.get(key);
if (sequence == null) {
this.sequences.putIfAbsent(key, new PositiveAtomicCounter());
sequence = (PositiveAtomicCounter)this.sequences.get(key);
}
return (ProviderInfo)providerInfos.get(sequence.getAndIncrement() % length);
}
private String getServiceKey(SofaRequest request) {
StringBuilder builder = new StringBuilder();
builder.append(request.getTargetAppName()).append("#").append(request.getMethodName());
return builder.toString();
}
}
public class PositiveAtomicCounter {
private static final int MASK = 2147483647;
private final AtomicInteger atom = new AtomicInteger(0);
public PositiveAtomicCounter() {
}
public final int incrementAndGet() {
return this.atom.incrementAndGet() & 2147483647;
}
public final int getAndIncrement() {
return this.atom.getAndIncrement() & 2147483647;
}
public int get() {
return this.atom.get() & 2147483647;
}
}
@Extension("localPref")
public class LocalPreferenceLoadBalancer extends RandomLoadBalancer {
public LocalPreferenceLoadBalancer(ConsumerBootstrap consumerBootstrap) {
super(consumerBootstrap);
}
public ProviderInfo doSelect(SofaRequest invocation, List providerInfos) {
String localhost = SystemInfo.getLocalHost();
if (StringUtils.isEmpty(localhost)) {
return super.doSelect(invocation, providerInfos);
} else {
List localProviderInfo = new ArrayList();
Iterator i$ = providerInfos.iterator();
while(i$.hasNext()) {
ProviderInfo providerInfo = (ProviderInfo)i$.next();
if (localhost.equals(providerInfo.getHost())) {
localProviderInfo.add(providerInfo);
}
}
if (CommonUtils.isNotEmpty(localProviderInfo)) {
return super.doSelect(invocation, localProviderInfo);
} else {
return super.doSelect(invocation, providerInfos);
}
}
}
}
当然,万事不可能十全十美,一致性Hash算法比普通的余数Hash算法更具有伸缩性,但是同时其算法实现也更为复杂,本文就来研究一下,如何利用Java代码实现一致性Hash算法。在开始之前,先对一致性Hash算法中的几个核心问题进行一些探究。
算法的具体原理这里再次贴上:
先构造一个长度为2^32的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 2^32-1])将服务器节点放置在这个Hash环上,
然后根据数据的Key值计算得到其Hash值(其分布也为[0, 232-1]),
接着在Hash环上顺时针查找距离这个Key值的Hash值最近的服务器节点,完成Key到服务器的映射查找。
/**
* 一致性hash算法,同样的请求(第一参数)会打到同样的节点
*
* @author GengZhang
*/
@Extension("consistentHash")
public class ConsistentHashLoadBalancer extends AbstractLoadBalancer {
/**
* {interface#method : selector}
*/
private ConcurrentMap<String, Selector> selectorCache = new ConcurrentHashMap<String, Selector>();
/**
* 构造函数
*
* @param consumerBootstrap 服务消费者配置
*/
public ConsistentHashLoadBalancer(ConsumerBootstrap consumerBootstrap) {
super(consumerBootstrap);
}
@Override
public ProviderInfo doSelect(SofaRequest request, List<ProviderInfo> providerInfos) {
String interfaceId = request.getInterfaceName();
String method = request.getMethodName();
String key = interfaceId + "#" + method;
int hashcode = providerInfos.hashCode(); // 判断是否同样的服务列表
Selector selector = selectorCache.get(key);
if (selector == null // 原来没有
||
selector.getHashCode() != hashcode) { // 或者服务列表已经变化
selector = new Selector(interfaceId, method, providerInfos, hashcode);
selectorCache.put(key, selector);
}
return selector.select(request);
}
/**
* 选择器
*/
private static class Selector {
/**
* The Hashcode.
*/
private final int hashcode;
/**
* The Interface id.
*/
private final String interfaceId;
/**
* The Method name.
*/
private final String method;
/**
* 虚拟节点
*/
private final TreeMap<Long, ProviderInfo> virtualNodes;
/**
* Instantiates a new Selector.
*
* @param interfaceId the interface id
* @param method the method
* @param actualNodes the actual nodes
*/
public Selector(String interfaceId, String method, List<ProviderInfo> actualNodes) {
this(interfaceId, method, actualNodes, actualNodes.hashCode());
}
/**
* Instantiates a new Selector.
*
* @param interfaceId the interface id
* @param method the method
* @param actualNodes the actual nodes
* @param hashcode the hashcode
*/
public Selector(String interfaceId, String method, List<ProviderInfo> actualNodes, int hashcode) {
this.interfaceId = interfaceId;
this.method = method;
this.hashcode = hashcode;
// 创建虚拟节点环 (默认一个provider共创建128个虚拟节点,较多比较均匀)
this.virtualNodes = new TreeMap<Long, ProviderInfo>();
int num = 128;
for (ProviderInfo providerInfo : actualNodes) {
for (int i = 0; i < num / 4; i++) {
byte[] digest = HashUtils.messageDigest(providerInfo.getHost() + providerInfo.getPort() + i);
for (int h = 0; h < 4; h++) {
long m = HashUtils.hash(digest, h);
virtualNodes.put(m, providerInfo);
}
}
}
}
/**
* Select provider.
*
* @param request the request
* @return the provider
*/
public ProviderInfo select(SofaRequest request) {
String key = buildKeyOfHash(request.getMethodArgs());
byte[] digest = HashUtils.messageDigest(key);
return selectForKey(HashUtils.hash(digest, 0));
}
/**
* 获取第一参数作为hash的key
*
* @param args the args
* @return the string
*/
private String buildKeyOfHash(Object[] args) {
if (CommonUtils.isEmpty(args)) {
return StringUtils.EMPTY;
} else {
return StringUtils.toString(args[0]);
}
}
/**
* Select for key.
*
* @param hash the hash
* @return the provider
*/
private ProviderInfo selectForKey(long hash) {
Map.Entry<Long, ProviderInfo> entry = virtualNodes.ceilingEntry(hash);
if (entry == null) {
entry = virtualNodes.firstEntry();
}
return entry.getValue();
}
/**
* Gets hash code.
*
* @return the hash code
*/
public int getHashCode() {
return hashcode;
}
}
}