package com.taobao.tddl.atom.utils;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.exception.ExceptionUtils;
import com.taobao.tddl.atom.exception.AtomNotAvailableException;
import com.taobao.tddl.common.utils.TStringUtil;
/**
* 应用连接限制的主要逻辑实现。
*/
public class ConnRestrictor {
/**
* MAP 策略的应用连接限制, 精确的匹配 Key 和连接槽。
*/
private HashMap<String, ConnRestrictSlot> mapConnRestrict;
/**
* HASH 策略的应用连接限制, 用 Hash + 取模的方式匹配 Key 和连接槽。
*/
private ConnRestrictSlot[] hashConnRestrict;
/**
* 没有定义业务键 (null Key) 的连接限制槽。
*/
private ConnRestrictSlot nullKeyRestrictSlot;
/**
* 初始化应用连接限制的数据结构, 这些数据结构只会被初始化一次。
*/
public ConnRestrictor(String datasourceKey, List<ConnRestrictEntry> connRestrictEntries){
for (ConnRestrictEntry connRestrictEntry : connRestrictEntries) {
String[] slotKeys = connRestrictEntry.getKeys();
if (slotKeys.length == 1 && ConnRestrictEntry.isWildcard(slotKeys[0])) {
int maxHashSize = connRestrictEntry.getHashSize();
if (maxHashSize < 1) {
maxHashSize = 1;
}
if (maxHashSize > ConnRestrictEntry.MAX_HASH_RESTRICT_SLOT) {
maxHashSize = ConnRestrictEntry.MAX_HASH_RESTRICT_SLOT;
}
if (hashConnRestrict == null) {
// 每个 HASH 分片都用独立的槽
hashConnRestrict = new ConnRestrictSlot[maxHashSize];
for (int i = 0; i < maxHashSize; i++) {
hashConnRestrict[i] = new ConnRestrictSlot(datasourceKey, "*:" + i, connRestrictEntry);
}
}
} else if (slotKeys.length == 1 && ConnRestrictEntry.isNullKey(slotKeys[0])) {
if (nullKeyRestrictSlot == null) {
nullKeyRestrictSlot = new ConnRestrictSlot(datasourceKey, slotKeys[0], connRestrictEntry);
}
} else {
// 注意, 这里多个业务键同时关联到一个槽
ConnRestrictSlot connRestrictSlot = new ConnRestrictSlot(datasourceKey,
TStringUtil.join(slotKeys, '|'),
connRestrictEntry);
if (mapConnRestrict == null) {
mapConnRestrict = new HashMap<String, ConnRestrictSlot>();
}
for (String slotKey : slotKeys) {
if (ConnRestrictEntry.isNullKey(slotKey)) {
if (nullKeyRestrictSlot == null) {
nullKeyRestrictSlot = connRestrictSlot;
}
} else if (!ConnRestrictEntry.isWildcard(slotKey)) {
if (!mapConnRestrict.containsKey(slotKey)) {
mapConnRestrict.put(slotKey, connRestrictSlot);
}
}
}
}
}
}
/**
* 从数据结构中查找应用连接限制的槽。
*/
public ConnRestrictSlot findSlot(Object connKey) {
if (connKey != null) {
ConnRestrictSlot connRestrictSlot = null;
if (mapConnRestrict != null) {
// 首先精确匹配
connRestrictSlot = mapConnRestrict.get(String.valueOf(connKey));
}
if (connRestrictSlot == null) {
if (hashConnRestrict != null) {
// 如果没有精确指定, 则用 HASH 方式
final int hash = Math.abs(connKey.hashCode() % hashConnRestrict.length);
connRestrictSlot = hashConnRestrict[hash];
}
}
return connRestrictSlot;
}
// 没有定义业务键, 走 null Key 槽
return nullKeyRestrictSlot;
}
/**
* 应用连接限制的入口函数。
*/
public ConnRestrictSlot doRestrict(final int timeoutInMillis) throws SQLException {
final Object connKey = AtomDataSourceHelper.getConnRestrictKey();
ConnRestrictSlot connRestrictSlot = findSlot(connKey);
try {
// 如果没有匹配的槽, 忽略限制
if (connRestrictSlot != null) {
if (!connRestrictSlot.allocateConnection(timeoutInMillis)) {
// 阻塞超时
throw new AtomNotAvailableException("No connection available for '" + connKey
+ "' within configured blocking timeout (" + timeoutInMillis
+ "[ms])");
}
}
} catch (InterruptedException e) {
throw new AtomNotAvailableException("Allocate connection for '" + connKey
+ "' interrupted within configured blocking timeout ("
+ timeoutInMillis + "[ms]) , caused by "
+ ExceptionUtils.getFullStackTrace(e));
} catch (RuntimeException e) {
throw new AtomNotAvailableException("Allocate connection for '" + connKey
+ "' failed: unexpected exception "
+ ExceptionUtils.getFullStackTrace(e));
}
return connRestrictSlot;
}
}