package com.alibaba.doris.admin.service.common.node;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.alibaba.doris.admin.core.AdminServiceLocator;
import com.alibaba.doris.admin.dataobject.PhysicalNodeDO;
import com.alibaba.doris.admin.service.AdminNodeService;
import com.alibaba.doris.admin.service.common.Managerable;
import com.alibaba.doris.client.net.Connection;
import com.alibaba.doris.client.net.ConnectionFactory;
import com.alibaba.doris.common.StoreNode;
import com.alibaba.doris.common.StoreNodeSequenceEnum;
/**
* 集群所有节点的管理器,定时从DB载入全部Node<br>
* 可以看作Node在内存的只读缓存
*
* @author frank
*/
public class NodesManager implements Managerable {
private static final Log log = LogFactory
.getLog(NodesManager.class);
private Map<String, PhysicalNodeDO> nodeMap = new ConcurrentHashMap<String, PhysicalNodeDO>();
private Map<String, StoreNode> storeNodesMap = new ConcurrentHashMap<String, StoreNode>();
private static NodesManager instance = new NodesManager();
private AdminNodeService nodeService = AdminServiceLocator
.getAdminNodeService();
private Map<String, Connection> nodeConnections = new HashMap<String, Connection>();
private Thread reloadThread = null;
private List<PhysicalNodeDO> nodeList = null;
private Object newAndCacheConnectionLock = new Object();
private NodesManager() {
reloadThread = new NodeReloadThread(this);
reLoadNodes();
}
public static NodesManager getInstance() {
return instance;
}
public Collection<PhysicalNodeDO> getAllNodeList() {
List<PhysicalNodeDO> cloneNodes = new ArrayList<PhysicalNodeDO>();
cloneNodes.addAll(nodeList);
return cloneNodes;
}
public PhysicalNodeDO getNode(String physicalId) {
return nodeMap.get(physicalId);
}
public List<String> getNodePhysicalIdListBySequence(StoreNodeSequenceEnum sequence) {
List<String> physicalIdList = new ArrayList<String>();
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == sequence.getValue()) {
physicalIdList.add(node.getPhysicalId());
}
}
return physicalIdList;
}
public List<PhysicalNodeDO> getNodeListBySequence(StoreNodeSequenceEnum sequence) {
List<PhysicalNodeDO> tempList = new ArrayList<PhysicalNodeDO>();
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == sequence.getValue()) {
tempList.add(node);
}
}
return tempList;
}
/**
* 获得一个序列的最大逻辑id,如果序列为空,返回-1
*
* @param sequence
* @return
*/
public int getLargestLogicId(StoreNodeSequenceEnum sequence) {
int largestId = -1;
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == sequence.getValue()) {
if (node.getLogicalId() > largestId) {
largestId = node.getLogicalId();
}
}
}
return largestId;
}
/**
* 所有临时节点。
*
* @return
*/
public List<PhysicalNodeDO> getAllTempNodeList() {
List<PhysicalNodeDO> physicalIdList = new ArrayList<PhysicalNodeDO>();
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == StoreNodeSequenceEnum.TEMP_SEQUENCE.getValue()) {
physicalIdList.add(node);
}
}
return physicalIdList;
}
/**
* 获得一个永久失效节点的备用节点
*
* @param physicalNodeId
* @return
*/
public PhysicalNodeDO getStandbyNodeId(String failPhysicalNodeId) {
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == StoreNodeSequenceEnum.STANDBY_SEQUENCE.getValue()) {
int sequence = NodesManager.getInstance().getNode(failPhysicalNodeId).getSerialId();
if (nodeService.checkLegalMigrateByPhysicalId(node.getPhysicalId(),
StoreNodeSequenceEnum.getTypeByValue(sequence))) {
return node;
}
}
}
return null;
}
/**
* 根据序列号和逻辑id查找节点
*
* @param sequence
* @param logicId
* @return
*/
public PhysicalNodeDO getNode(int sequence, int logicId) {
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == sequence && node.getLogicalId() == logicId) {
return node;
}
}
return null;
}
public String getLogFormatNodeId(String physicalId) {
PhysicalNodeDO node = getNode(physicalId);
if (node == null) {
return null;
}
return node.getSerialId() + "." + node.getLogicalId();
}
public int getSequenceLenght(int sequence) {
int length = 0;
for (PhysicalNodeDO node : getAllNodeList()) {
if (node.getSerialId() == sequence) {
length++;
}
}
return length;
}
/**
* 从数据库载入所有节点
*/
public synchronized void reLoadNodes() {
List<PhysicalNodeDO> nodeList = nodeService.queryAllPhysicalNodes();
this.nodeList = nodeList;
this.nodeMap = l2m(nodeList);
this.storeNodesMap = refreshStoreNodes(nodeList);
}
/**
* 用输入的参数载入节点
*
* @param nodeMap
*/
public synchronized void reLoadNodes(List<PhysicalNodeDO> nodeList) {
this.nodeList = nodeList;
this.nodeMap = l2m(nodeList);
this.storeNodesMap = refreshStoreNodes(nodeList);
}
private Map<String, StoreNode> refreshStoreNodes(List<PhysicalNodeDO> nodeList) {
Map<String, StoreNode> tempNodeMap = new ConcurrentHashMap<String, StoreNode>();
for (PhysicalNodeDO pNode : nodeList) {
StoreNode sn = NodeHelper.buildStoreNode(pNode);
tempNodeMap.put(pNode.getPhysicalId(), sn);
}
return tempNodeMap;
}
private Map<String, PhysicalNodeDO> l2m(List<PhysicalNodeDO> nodeList) {
Map<String, PhysicalNodeDO> tempNodeMap = new ConcurrentHashMap<String, PhysicalNodeDO>();
if (nodeList == null) {
if (log.isDebugEnabled()) {
log.debug("no nodes found in DB...");
}
return tempNodeMap;
}
if (log.isDebugEnabled()) {
log.debug("the nodes' number from db:" + nodeList.size());
}
for (PhysicalNodeDO node : nodeList) {
tempNodeMap.put(node.getPhysicalId(), node);
}
return tempNodeMap;
}
public StoreNode getStoreNode(String physicalId) {
PhysicalNodeDO pNode = this.getNode(physicalId);
if (pNode == null) {
return null;
}
return storeNodesMap.get(pNode.getPhysicalId());
}
public StoreNode getStoreNode(PhysicalNodeDO pNode) {
return storeNodesMap.get(pNode.getPhysicalId());
}
/**
* One new connection will be established and cached when first time access,
* otherwise, the cached connection returns. If the cached connection is not
* connected, and one new connection will be established and cached.
*
* @param physicalId the physical node id for one data server node.
*/
public Connection getNodeConnection(String physicalId) {
StoreNode storeNode = this.getStoreNode(physicalId);
return getNodeConnection(storeNode, false);
}
/**
* <p>
* One new connection will be established and cached when first time access,
* otherwise, the cached connection returns. If the cached connection is not
* connected, and one new connection will be established and cached.
* </p>
* <p>
* Please note, the connection must be closed if not use again when
* <code>bNewConnection</code> is true. Recommand to set
* <code>bNewConnection</code> as false, then {@code NodesManager} will
* manager the connection;
* </p>
*
* @param physicalId the physical node id for one data server node.
* @param bNewConnection One new connection will be established always if
* <code></code> is true. Otherwise, The behavior is same as
* {@link #getNodeConnection(String)}.
* @see #getNodeConnection(String)
*/
public Connection getNodeConnection(String physicalId, boolean bNewConnection) {
StoreNode storeNode = this.getStoreNode(physicalId);
return getNodeConnection(storeNode, bNewConnection);
}
/**
* One new connection will be established and cached when first time access,
* otherwise, the cached connection returns. If the cached connection is not
* connected, and one new connection will be established and cached.
*
* @param node the store node instance.
*/
public Connection getNodeConnection(StoreNode node) {
return getNodeConnection(node, false);
}
/**
* <p>
* One new connection will be established and cached when first time access,
* otherwise, the cached connection returns. If the cached connection is not
* connected, and one new connection will be established and cached.
* </p>
* <p>
* Please note, the connection must be closed if not use again when
* <code>bNewConnection</code> is true. Recommand to set
* <code>bNewConnection</code> as false, then {@code NodesManager} will
* manager the connection;
* </p>
*
* @param physicalId the physical node id for one data server node.
* @param bNewConnection One new connection will be established always if
* <code></code> is true. Otherwise, The behavior is same as
* {@link #getNodeConnection(StoreNode)}
* @see #getNodeConnection(StoreNode)
*/
public Connection getNodeConnection(StoreNode storeNode, boolean bNewConnection) {
if (bNewConnection) {
return newAndOpenConnection(storeNode);
} else {
return getAndCacheConnection(storeNode);
}
}
private void cacheConnection(String physicalId, Connection conn) {
nodeConnections.put(physicalId, conn);
}
private Connection newAndOpenConnection(StoreNode storeNode) {
Connection conn;
InetSocketAddress inetSocketAddress = new InetSocketAddress(storeNode.getIp(), storeNode
.getPort());
conn = ConnectionFactory.getInstance().getConnection(inetSocketAddress);
conn.open();
return conn;
}
private Connection getAndCacheConnection(StoreNode storeNode) {
Connection conn = nodeConnections.get(storeNode.getPhId());
if (conn == null) {
synchronized (newAndCacheConnectionLock) {
conn = newAndOpenConnection(storeNode);
cacheConnection(storeNode.getPhId(), conn);
}
} else if (conn != null && !conn.isConnected()) {
conn.close();
if (log.isInfoEnabled()) {
log.info("old connection is closed:" + conn.toString());
}
// create new connection and cache it.
synchronized (newAndCacheConnectionLock) {
conn = newAndOpenConnection(storeNode);
cacheConnection(storeNode.getPhId(), conn);
}
if (log.isInfoEnabled()) {
log.info("new connection is closed:" + conn.toString());
}
}
return conn;
}
public void start() {
reloadThread.start();
}
public void stop() {
//关闭所有连接
for (Connection conn : nodeConnections.values()) {
conn.close();
}
//关闭reload线程
reloadThread.interrupt();
}
}