Sentinel介绍
略
https://sentinelguard.io/zh-cn/
https://github.com/alibaba/Sentinel
https://sentinelguard.io/zh-cn/docs/quick-start.html
https://github.com/alibaba/Sentinel/wiki/Sentinel-核心类解析
Sentinel定义的术语
Entry: 表示对某个资源的访问请求,通过 SphU.entry 方法进入资源,确保该资源受到 Sentinel 规则的保护
SphU:是Statistical Process Handling Utility,即统计过程处理工具
DefaultNode: 记录当前资源的指标信息、节点信息
entranceNode: 当前资源调用的入口节点
链路分析
这里以demo中的webnvc-adapter入口为例,为所有http接口添加AsyncHandlerInterceptor,如果在内部的链路中抛出了BlockException,则不会继续处理新的请求
链路的入口方法为SphU.entry,其中origin为标记的请求来源,可以自己配置,
其中ContextUtil.enter会初始化这次请求的上下文,存储在ThreadLocal中,同理在处理完请求拦截器的afterCompletion方法中会执行ContextUtil.exit()清除ThreadLocal和其他信息,
并且会在这个时候缓存DefaultNode,并且将将当前EntranceNode添加为Constants.ROOT的子节点
SphU.entry方法最终会进入entryWithPriority方法中,当出现异常会执行exit方法
通过SPI加载完ProcessorSlot会进行缓存下来,下一次资源使用的时候直接查询缓存,加载到链路中ProcessorSlot是指定的顺序,Sentinel中内置了几种Slot,默认按照加载顺序:
NodeSelectorSlot -> ClusterBuilderSlot -> LogSlot -> StatisticSlot -> AuthoritySlot -> SystemSlot -> FlowSlot -> DefaultCircuitBreakerSlot -> DegradeSlot
由于都继承了AbstractLinkedProcessorSlot,当调用fireEntry方法的时候都会调用next,所以是一个经典的责任链模式
NodeSelectorSlot
Node收集器卡槽,负责维护当前机器节点的资源调用路径,上下文共用NodeSelectorSlot内部缓存的map,并且维护当前context的Node树结构
以注释中的例子为例
ContextUtil.enter("entrance1", "appA");
Entry nodeA = SphU.entry("nodeA");
if (nodeA != null) {
nodeA.exit();
}
ContextUtil.exit();
当执行以上代码时,表示来源为appA,上下文名称为entracne1,然后在ContextUtil中会创建名称为entrance1的EntranceNode,并且会添加为ROOT的子节点,所以在内存中结构是下面这样:
machine-root
/
/
EntranceNode1
/
/
DefaultNode(nodeA)- - - - - -> ClusterNode(nodeA);
ClusterBuilderSlot
由于继承了StatisticNode,可以存储当前资源的统计信息,并赋值给上下文中的ClusterNode
在NodeSelectorSlot中缓存的key为content的name,但是在ClusterBuilderSlot中缓存的key为ResourceWrapper,这样就保证了资源与ClusterBuilderSlot的一对一关系
为了获取同一资源的总统计数据,所有具备相同资源名称的DefaultNode共享一个ClusterNode
LogSlot
用与单独记录异常信息日志,并记录在sentinel-block.log文件
StatisticSlot
用于记录不同维度指标监控信息,比如线程数、请求成功数
StatisticSlot与其他slot不同的是,可以看到首先执行fireEntry进行后续的调用,如果成功了或者抛出异常了才会记录一些指标
在increaseThreadNum方法中可以看到不仅当前node+1,同时也维护了clusternode,clusternode与defaultnode是一对多的关系,clustrenode与resource是一对一的关系,resouce与context也是一对多的关系
如果后续链路成功,threadnum+1,passCount+1
同时也会记录全局入站的信息,用于记录后续从系统层面进行控制所需要的指标,同时ProcessorSlotEntryCallback提供了扩展接口来额外记录信息
如果抛出BlockException,则会记录block qps
具体记录指标信息在StatisticNode中的LeapArray
AuthoritySlot
黑名单白名单控制,控制请求来源,dmeo中标记为请求头中含有“S-user”,可以自己定义,
SystemSlot
通过系统层面来控制资源的访问,比如配置的系统保护规则load1、qps、线程数,在Constants中维护的ENTRY_NODE计数
FlowSlot
根据预设的规则以及前面几个slot统计的信息,来进行流量控制,对应的流控规则
DefaultCircuitBreakerSlot
当某个资源在一段时间内频繁出现异常或响应时间过长,DefaultCircuitBreakerSlot会触发熔断机制,熔断状态分为开、关、半开
DegradeSlot
也是熔断卡槽,主要基于统计数据(如异常比例、响应时间、慢调用比例)触发熔断
客户端与服务端交互
Sentinel的数据交互主要依赖于sentinel-transport,根据不同的adapter来使用,webmvc demo引入的是sentinel-transport-simple-http
不同于其他的中间件借助spring.factories,也为了sentinel的通用,在core模块下创建CtSph时会执行客户端的初始化
首先会根据Sentinel的SPI加载InitFunc的接口,并根据Order指定加载的顺序,首次会默认加载MetricCallbackInit、CommandCenterInitFunc、HeartbeatSenderInitFunc,比较重要的是CommandCenterInitFunc,还是根据spi加载 CommandHandler,并且进行缓存,缓存的key为CommandMapping的name,后续会根据这个key来调用handle来调用客户端的一些方法
接着在start方法中初始化线程池,以及接着在ServerThread中启动单独的Socket,监听请求处理在HttpEventTask中处理,
当请求来到之后,这里逻辑相当于实现了简易版的Socket服务端处理,以此来实现databoard与客户端的信息交互,比如在控制台更改流控规则、授权规则等都需要将规则同步到当前机器,同步方法就是根据上述服务端同步给当前机器
比如查询簇点链路,首先会调用控制台的/machineResource.json接口,然后会请求当前机器的jsonTree接口
Socket监听到了之后根据初始化的缓存查找对应的CommandHandler,所以可以获取Root节点下的所有信息
上面是相当于推的模式,如果控制台新增了流控规则,需要同步到对应机器怎么办呢,保存完之后调用setRules接口,推送至客户端之后调用FlowRuleManager.loadRules(flowRules)更新本地的流控规则