/*
* Copyright 2009-2016 Weibo, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.weibo.api.motan.cluster.loadbalance;
import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.rpc.Referer;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.util.LoggerUtil;
import com.weibo.api.motan.util.NetUtils;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
/**
* "本地服务优先" 负载均衡
* <p>
* <pre>
* 1) 本地服务优先:
* 当referers里面包含本地暴露的服务时,并此服务为available的情况下,优先使用此服务。
* 当不存在本地暴露的服务时,默认使用低并发ActiveWeight负载均衡策略
*
* 2) 本地服务优先获取策略:
* 对referers根据ip顺序查找本地服务,多存在多个本地服务,获取Active最小的本地服务进行服务。
* 当不存在本地服务,但是存在远程RPC服务,则根据ActivWeight获取远程RPC服务
* 当两者都存在,所有本地服务都应优先于远程服务,本地RPC服务与远程RPC服务内部则根据ActiveWeight进行
*
* </pre>
*/
@SpiMeta(name = "localFirst")
public class LocalFirstLoadBalance<T> extends AbstractLoadBalance<T> {
public static final int MAX_REFERER_COUNT = 10;
public static long ipToLong(final String addr) {
final String[] addressBytes = addr.split("\\.");
int length = addressBytes.length;
if (length < 3) {
return 0;
}
long ip = 0;
try {
for (int i = 0; i < 4; i++) {
ip <<= 8;
ip |= Integer.parseInt(addressBytes[i]);
}
} catch (Exception e) {
LoggerUtil.warn("Warn ipToInt addr is wrong: addr=" + addr);
}
return ip;
}
@Override
protected Referer<T> doSelect(Request request) {
List<Referer<T>> referers = getReferers();
List<Referer<T>> localReferers = searchLocalReferer(referers, NetUtils.getLocalAddress().getHostAddress());
if (!localReferers.isEmpty()) {
referers = localReferers;
}
int refererSize = referers.size();
Referer<T> referer = null;
for (int i = 0; i < refererSize; i++) {
Referer<T> temp = referers.get(i % refererSize);
if (!temp.isAvailable()) {
continue;
}
if (referer == null) {
referer = temp;
} else {
if (compare(referer, temp) > 0) {
referer = temp;
}
}
}
return referer;
}
@Override
protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
List<Referer<T>> referers = getReferers();
List<Referer<T>> localReferers = searchLocalReferer(referers, NetUtils.getLocalAddress().getHostAddress());
if (!localReferers.isEmpty()) {
Collections.sort(localReferers, new LowActivePriorityComparator<T>());
refersHolder.addAll(localReferers);
}
int refererSize = referers.size();
int startIndex = ThreadLocalRandom.current().nextInt(refererSize);
int currentCursor = 0;
int currentAvailableCursor = 0;
List<Referer<T>> remoteReferers = new ArrayList<Referer<T>>();
while (currentAvailableCursor < MAX_REFERER_COUNT && currentCursor < refererSize) {
Referer<T> temp = referers.get((startIndex + currentCursor) % refererSize);
currentCursor++;
if (!temp.isAvailable() || localReferers.contains(temp)) {
continue;
}
currentAvailableCursor++;
remoteReferers.add(temp);
}
Collections.sort(remoteReferers, new LowActivePriorityComparator<T>());
refersHolder.addAll(remoteReferers);
}
private List<Referer<T>> searchLocalReferer(List<Referer<T>> referers, String localhost) {
List<Referer<T>> localReferers = new ArrayList<Referer<T>>();
long local = ipToLong(localhost);
for (Referer<T> referer : referers) {
long tmp = ipToLong(referer.getUrl().getHost());
if (local != 0 && local == tmp) {
if (referer.isAvailable()) {
localReferers.add(referer);
}
}
}
return localReferers;
}
private int compare(Referer<T> referer1, Referer<T> referer2) {
return referer1.activeRefererCount() - referer2.activeRefererCount();
}
static class LowActivePriorityComparator<T> implements Comparator<Referer<T>> {
@Override
public int compare(Referer<T> referer1, Referer<T> referer2) {
return referer1.activeRefererCount() - referer2.activeRefererCount();
}
}
}