前言
本章初步了解一下Sentinel中的一些基础概念,主要包括:
- Resource:资源
- Context:上下文
- Sph:信号量
- Entry:Token/许可/入口---调用链
- Node:统计节点---树
- ProcessorSlot:插槽
- Rule:规则
注:基于Sentinel 1.8版本。
1、Resource
Resource是Sentinel对资源的一种抽象。
public abstract class ResourceWrapper {
// 资源名称
protected final String name;
// IN or OUT 是入口还是出口流量
protected final EntryType entryType;
// 资源类型
protected final int resourceType;
}
其中EntryType流量类型,分为入口流量和出口流量。
public enum EntryType {
IN("IN"),
OUT("OUT");
}
其中resourceType资源类型,目前分为四种。
public final class ResourceTypeConstants {
// 默认普通资源
public static final int COMMON = 0;
// http
public static final int COMMON_WEB = 1;
// RPC:Dubbo、Motan、Sofa-RPC
public static final int COMMON_RPC = 2;
// spring-cloud-gateway、zuul
public static final int COMMON_API_GATEWAY = 3;
}
Resource有两个实现类。
一个是StringResourceWrapper,用字符串表示一个资源。
public class StringResourceWrapper extends ResourceWrapper {
public StringResourceWrapper(String name, EntryType e) {
super(name, e, ResourceTypeConstants.COMMON);
}
public StringResourceWrapper(String name, EntryType e, int resType) {
super(name, e, resType);
}
}
另一类是MethodResourceWrapper,用方法表示一个资源,其底层仍然是解析Method的方法签名为字符串,用字符串作为资源标识。
public class MethodResourceWrapper extends ResourceWrapper {
private final transient Method method;
public MethodResourceWrapper(Method method, EntryType e) {
this(method, e, ResourceTypeConstants.COMMON);
}
public MethodResourceWrapper(Method method, EntryType e, int resType) {
super(MethodUtil.resolveMethodName(method), e, resType);
this.method = method;
}
}
2、Context
Context上下文,通过 ThreadLocal 传递,即每个线程持有一个Context。
public class Context {
// 上下文名称
private final String name;
// EntranceNode 入口节点 --- 树
private DefaultNode entranceNode;
// 当前Entry --- 链表
private Entry curEntry;
// 来源
private String origin = "";
}
Context中保存了两个重要的属性:
- entranceNode:EntranceNode入口节点,当创建Context时创建,可以统计整个Context的统计指标
- curEntry:当前上下文中,用户最后一个获得到的Entry
3、Sph
用户调用SphU(或SphO) #entry方法,获取资源许可。
如果抛出异常,代表获取失败,如果成功返回Entry实例代表获取资源成功。
public class SphU {
public static Entry entry(String name) throws BlockException {
return Env.sph.entry(name, EntryType.OUT, 1, OBJECTS0);
}
}
Sph信号量,是用户获取资源的入口。无论是SphO.entry还是SphU.entry,其底层都是通过Sph的实现类CtSph获取信号量。
public interface Sph extends SphResourceTypeSupport {
Entry entry(String name) throws BlockException;
Entry entry(Method method) throws BlockException;
// ... 省略其他方法
}
Sph保存在全局Env中。
public class Env {
public static final Sph sph = new CtSph();
static {
InitExecutor.doInit();
}
}
4、Entry
Entry,代表一个许可/入口,当Sph.entry获取资源成功时返回。
用户使用完资源后,需要通过Entry.exit归还许可,释放Sph信号量。
Entry抽象类主要包含:Node指标统计信息、Resource信息。
public abstract class Entry implements AutoCloseable {
// 进入时间
private final long createTimestamp;
// 退出时间
private long completeTimestamp;
// 当前上下文的统计信息(包含子Node)
private Node curNode;
// 来源方统计信息
private Node originNode;
// 资源
protected final ResourceWrapper resourceWrapper;
}
Entry的默认实现类是CtEntry。
- parent和child:将多个Entry串联成了链表,当多次调用Sph.entry时会形成一个调用链,当调用Entry.exit时,会将自己从链表中移除
- chain:ProcessorSlot,实际上是ProcessorSlotChain,代表了当前Entry需要经过的规则检查(如FlowRule限流规则检查)
- Context:上下文
class CtEntry extends Entry {
// 上一个入口Entry
protected Entry parent = null;
// 下一个Entry
protected Entry child = null;
// Slot插槽
protected ProcessorSlot chain;
// 上下文
protected Context context;
}
5、Node
Node负责指标统计,包含QPS、Thread、RT等指标。
public interface Node extends OccupySupport, DebugSupport {
long totalRequest();
long totalPass();
long totalSuccess();
long blockRequest();
long totalException();
double passQps();
double blockQps();
double totalQps();
double successQps();
double maxSuccessQps();
double exceptionQps();
double avgRt();
double minRt();
int curThreadNum();
double previousBlockQps();
double previousPassQps();
Map<Long, MetricNode> metrics();
List<MetricNode> rawMetricsInMin(Predicate<Long> timePredicate);
void addPassRequest(int count);
void addRtAndSuccess(long rt, int success);
void increaseBlockQps(int count);
void increaseExceptionQps(int count);
void increaseThreadNum();
void decreaseThreadNum();
void reset();
}
Node的实现类有四个
StatisticNode
StatisticNode主要负责做指标统计,所有的Node都继承了StatisticNode。
public class StatisticNode implements Node {
// 秒级滑动窗口,SAMPLE_COUNT=2,代表每500ms一个时间窗口
private transient volatile Metric rollingCounterInSecond = new ArrayMetric(SampleCountProperty.SAMPLE_COUNT,
IntervalProperty.INTERVAL);
// 分钟级滑动窗口,SAMPLE_COUNT=60,代表每1s一个时间窗口
private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000, false);
// 当前并发线程数量
private LongAdder curThreadNum = new LongAdder();
}
StatisticNode中包含一个ArrayMetric秒级滑动窗口和一个ArrayMetric分钟级滑动窗口,用于保存统计数据,但两者的用途不同。
比如统计被Sentinel拒绝的QPS,从秒级滑动窗口获取:
@Override
public double blockQps() {
return rollingCounterInSecond.block() / rollingCounterInSecond.getWindowIntervalInSec();
}
比如统计被Sentinel拒绝的请求总数,从分钟级滑动窗口获取:
@Override
public long blockRequest() {
return rollingCounterInMinute.block();
}
此外有个LongAddr负责统计并发线程数量:
@Override
public int curThreadNum() {
return (int)curThreadNum.sum();
}
@Override
public void increaseThreadNum() {
curThreadNum.increment();
}
@Override
public void decreaseThreadNum() {
curThreadNum.decrement();
}
DefaultNode
DefaultNode,普通链路节点。
统计维度:Context + Resource。
创建时机:多次调用SphU.entry,NodeSelectorSlot执行时创建DefaultNode(如果Context+Resource维度已经有这个Node了,将使用原来的Node实例),并加入Context上下文的Node链表中。
public class DefaultNode extends StatisticNode {
// 关联的资源
private ResourceWrapper id;
// 子节点
private volatile Set<Node> childList = new HashSet<>();
// 关联的ClusterNode
private ClusterNode clusterNode;
}
DefaultNode维护了一个Node集合,用于表示当前节点的子节点,通过这种方式,DefaultNode构成了一颗树。
DefaultNode重写了StaticNode更新统计数据的相关方法,如increaseBlockQps增加被拒绝的QPS时,额外调用了ClusterNode的increaseBlockQps方法。
@Override
public void increaseBlockQps(int count) {
super.increaseBlockQps(count);
this.clusterNode.increaseBlockQps(count);
}
EntranceNode
EntranceNode入口节点,是特殊的DefaultNode链路节点。
统计维度:Context。
创建时机:每个上下文Context,都会有一个EntranceNode与之关联,代表链路的入口。
EntranceNode继承了DefaultNode,是Node树的树根。
EntranceNode重写了所有统计方法,比如avgRt统计平均响应时间,根据所有子节点的统计数据,计算得到最终的平均响应时间。
public class EntranceNode extends DefaultNode {
public EntranceNode(ResourceWrapper id, ClusterNode clusterNode) {
super(id, clusterNode);
}
@Override
public double avgRt() {
double total = 0;
double totalQps = 0;
for (Node node : getChildList()) {
total += node.avgRt() * node.passQps();
totalQps += node.passQps();
}
return total / (totalQps == 0 ? 1 : totalQps);
}
}
ClusterNode
ClusterNode簇点,统计一个Resource的数据。
统计维度:Resource。
创建时机:多次调用Sph.entry获取不同资源,ClusterBuilderSlot执行时创建ClusterNode。如果这个Resource从来没有被entry调用过,则创建一个ClusterNode,否则沿用Resource对应的ClusterNode。
public class ClusterNode extends StatisticNode {
// 资源名称
private final String name;
// 资源类型
private final int resourceType;
// origin来源 - 统计数据
private Map<String, StatisticNode> originCountMap = new HashMap<>();
}
一个Resource对应一个ClusterNode,ClusterNode还根据Sph.entry时传入的origin不同(origin存储在上下文中),在originCountMap中保存了Resource下不同origin的统计数据StatisticNode。
6、ProcessorSlot
在执行Sph.entry和Entry.exit时,会经过一系列插槽(ProcessorSlot),每个插槽负责一部分功能(责任链)。
public interface ProcessorSlot<T> {
// Sph.entry时触发
void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,
Object... args) throws Throwable;
// 执行下一个Slot的entry方法
void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,
Object... args) throws Throwable;
// Entry.exit时触发
void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
// 执行下一个Slot的exit方法
void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}
目前总共有7个重要的Slot,按照Slot排列顺序如下(以文字为准,官方图片与代码顺序不一致):
- NodeSelectorSlot:构建资源(Resource)的路径(DefaultNode),用树的结构存储,即图中Tree Node Builder。
- ClusterBuilderSlot:构建ClusterNode,用于记录资源维度的统计信息,即图中Cluster Node Builder。
- StatisticSlot:使用Node记录指标信息,如RT、Pass/Block Count,为后续规则校验提供数据支撑。
- AuthoritySlot:授权规则校验
- SystemSlot:系统规则校验
- ParamFlowSlot:热点参数流控规则校验
- FlowSlot:流控规则校验
- DegradeSlot:降级规则校验
7、Rule
Rule,规则,定义了对于某个Resource的限制。
public interface Rule {
// 资源名称 - ResourceWrapper.name
String getResource();
}
AbstractRule,抽象规则。
除了提供Resource的getter&setter外,定义了limitApp字段,用于标识规则适用的来源系统。
目前limitApp分为default、other和用户指定,默认情况下为default。
public abstract class AbstractRule implements Rule {
// 资源名称 - ResourceWrapper.name
private String resource;
// 来源应用
private String limitApp;
@Override
public String getResource() {
return resource;
}
// ...
}
Sentinel支持5个规则定义分别对应5个规则校验ProcessorSlot:
- AuthorityRule:授权规则
- SystemRule:系统规则
- ParamFlowRule:热点参数流控规则
- FlowRule:流控规则
- DegradeRule:降级规则
总结
本章简单了解了Sentinel的7个概念
-
Resource:资源,根据EntryType流量类型,分为入口流量和出口流量。
-
Context:上下文,通过 ThreadLocal 传递,即每个线程持有一个Context。持有Node树的根节点EntranceNode,持有当前上下文中用户最后一个获得到的Entry。
-
Sph:信号量,是用户获取资源许可的入口。无论是SphO.entry还是SphU.entry,其底层都是通过Sph的实现类CtSph获取信号量。
-
Entry:Token/许可/入口,当Sph.entry获取资源成功时返回。实现类CtEntry用指针构成双向链表,形成一个调用链。
-
Node:统计节点,负责指标统计,包含QPS、Thread、RT等指标。共有四个实现类:
- StatisticNode:负责指标统计,所有的Node都继承了StatisticNode;
- DefaultNode:普通链路节点,统计Context * Resource维度的数据。用一个集合存储了当前Node的所有子Node,形成一棵树;
- EntranceNode:入口节点,继承了DefaultNode,是特殊的链路节点,统计Context维度的数据。每个Context都对应一个EntranceNode。
- ClusterNode:簇点,统计Resource维度的数据。
-
ProcessorSlot:插槽,Sph.entry和Entry.exit时,会经过一系列插槽(ProcessorSlot),每个插槽负责一部分功能(责任链)。
- NodeSelectorSlot:构建资源(Resource)的路径(DefaultNode)
- ClusterBuilderSlot:构建ClusterNode
- StatisticSlot:使用Node记录指标信息,如RT、Pass/Block Count,为后续规则校验提供数据支撑
- AuthoritySlot:授权规则校验
- SystemSlot:系统规则校验
- ParamFlowSlot:热点参数流控规则校验
- FlowSlot:流控规则校验
- DegradeSlot:降级规则校验
-
Rule:规则,Sentinel支持5个常用规则,分别对应5个规则校验ProcessorSlot
- AuthorityRule:授权规则
- SystemRule:系统规则
- ParamFlowRule:热点参数流控规则
- FlowRule:流控规则
- DegradeRule:降级规则