- sentinal是阿里开源的限流降级的一个基础组件,本系列主要是记录本人对sentinal源码的理解。
sentinal官网 -
节点关系图
image.png
一 Context
1.1 ContextUtil类属性
public class ContextUtil {
private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();
private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();
private static final ReentrantLock LOCK = new ReentrantLock();
private static final Context NULL_CONTEXT = new NullContext();
...
}
- 线程context上下文信息,使用
static ThreadLocal<Context> contextHolder
存储,每个线程一个 - contextNameNodeMap以context名为key存储conext的入口EntranceNode类型的节点,多个线程使用相同名字时,用的同一个节点
- 使用
ReentrantLock LOCK
可重入锁保证context入口节点的并发读写
1.2 静态初始化
static {
// Cache the entrance node for default context.
initDefaultContext();
}
private static void initDefaultContext() {
String defaultContextName = Constants.CONTEXT_DEFAULT_NAME;
EntranceNode node = new EntranceNode(new StringResourceWrapper(defaultContextName, EntryType.IN), null);
Constants.ROOT.addChild(node);
contextNameNodeMap.put(defaultContextName, node);
}
- 使用static在类加载时初始化
- 初始化默认的context入口节点,名字为sentinel_default_context,未指定context名称时统一使用该节点
- 设置为ROOT节点的子节点
1.3 ROOT节点初始化
public final class Constants {
public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.5.1");
public final static int MAX_CONTEXT_NAME_SIZE = 2000;
public final static int MAX_SLOT_CHAIN_SIZE = 6000;
public final static String ROOT_ID = "machine-root";
public final static String CONTEXT_DEFAULT_NAME = "sentinel_default_context";
public final static String TOTAL_IN_RESOURCE_NAME = "__total_inbound_traffic__";
public final static DefaultNode ROOT = new EntranceNode(new StringResourceWrapper(ROOT_ID, EntryType.IN),
Env.nodeBuilder.buildClusterNode());
/**
* Statistics for {@link SystemRule} checking.
*/
public final static ClusterNode ENTRY_NODE = new ClusterNode();
/**
* Response time that exceeds TIME_DROP_VALVE will be calculated as TIME_DROP_VALVE.
* Default value is 4900 ms
* It can be configured by property file or JVM parameter -Dcsp.sentinel.statistic.max.rt=xxx
* See {@link SentinelConfig#statisticMaxRt()}
*/
public static int TIME_DROP_VALVE = SentinelConfig.statisticMaxRt();
/**
* The global switch for Sentinel.
*/
public static volatile boolean ON = true;
}
- Constant类型提供一些常量数据
- 通知提供一些初始化操作,如初始化一个machine-root名字的根节点,是所有context入口节点的父节点
1.4 获取指定名称的context
- 接口函数,参数为context名称和调用来源origin
public static Context enter(String name, String origin) {
if (Constants.CONTEXT_DEFAULT_NAME.equals(name)) {
throw new ContextNameDefineException(
"The " + Constants.CONTEXT_DEFAULT_NAME + " can't be permit to defined!");
}
return trueEnter(name, origin);
}
1.4.1 初始化线程的context
- 线程变量存储,一个线程一个
- context node节点按名字唯一创建,多个线程的context可以共用node
- 限制context node的最大数量为2000
- 加锁,二次检查conext node是否存在,不存在则新建EntranceNode类型node
- 设置为ROOT节点的子节点,更新conext node存储map数据
- 初始化context
- 设置context的origin值,调用来源
- 保存线程变量Threadlocal,返回context
protected static Context trueEnter(String name, String origin) {
Context context = contextHolder.get();
if (context == null) {
Map<String, DefaultNode> localCacheNameMap = contextNameNodeMap;
DefaultNode node = localCacheNameMap.get(name);
if (node == null) {
if (localCacheNameMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
try {
LOCK.lock();
node = contextNameNodeMap.get(name);
if (node == null) {
if (contextNameNodeMap.size() > Constants.MAX_CONTEXT_NAME_SIZE) {
setNullContext();
return NULL_CONTEXT;
} else {
node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);
// Add entrance node.
Constants.ROOT.addChild(node);
Map<String, DefaultNode> newMap = new HashMap<>(contextNameNodeMap.size() + 1);
newMap.putAll(contextNameNodeMap);
newMap.put(name, node);
contextNameNodeMap = newMap;
}
}
} finally {
LOCK.unlock();
}
}
}
context = new Context(node, name);
context.setOrigin(origin);
contextHolder.set(context);
}
return context;
}
1.5 context结构
public class Context {
private final String name;//context名称
private DefaultNode entranceNode;//context名字对应的入口节点,相同名称共用一个节点
private Entry curEntry;//当前资源请求处理节点
private String origin = "";//资源请求来源
private final boolean async;//异步处理标记
}
二 资源请求入口
- 两种类型的入口
SphU 请求失败抛异常,则正常处理
SphO 请求成功返回true,请求失败返回false - 实际调用的都是Env.sph.enter()函数,请求资源失败会抛异常。
SphO是对异常进行catch,返回false。
网友评论