package org.fastcatsearch.cluster; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * id 마다 long값을 유지하여 round-robin방식으로 다음 노드를 선택해준다. * */ public class NodeLoadBalancer { protected static Logger logger = LoggerFactory.getLogger(NodeLoadBalancer.class); private Map<String, List<Node>> map; //컬렉션별로 seq를 유지하면 동일 노드에 여러 컬렉션을 한번에 검색하게 되므로, 이 방법은 사용하지 않는다. private AtomicLong rrSequence; //round robin 시퀀스. public NodeLoadBalancer() { map = new ConcurrentHashMap<String, List<Node>>(); rrSequence = new AtomicLong(); } public void update(String id, List<Node> list) { // if(!map.containsKey(id)) { // map.put(id, list); // } /* * id가 없을때에만 업데이트하도록하면 차후 업데이트가 되지 않는 문제가 발생한다. * 그러므로 무조건 put하도록 한다. * */ map.put(id, list); } /** * round-robin방식으로 노드를 선택한다. node.isActive()를 확인하여 false일 경우 모든 노드를 확인해본다. * active한 노드가 없을 경우 null을 리턴한다. * */ public Node getBalancedNode(String id) { long seq = rrSequence.getAndIncrement(); List<Node> list = map.get(id); Node node = null; if(list == null){ logger.error("cannot find node list for {}", id); return null; } int length = list.size(); //width만큼 돌면서 노드를 찾는다. for (int i = 0; i < length; i++) { int index = (int) (seq++ % length); node = list.get(index); // logger.info("{}]getAndIncrement seq[{}], list.size()[{}] index[{}] >> node[{}] isactive[{}]", tryCount - 1, seq, list.size(), index, node, node.isActive()); if (node.isActive()) { return node; } else { // logger.warn("#Node inactive! {}", node); } } logger.warn("#Fail to select node for {}", id); return node; } }