微服务高并发概念与核心类:资源指标数据统计相关类
资源指标数据统计相关类
资源指标数据统计是 Sentinel 的核心功能,例如,限流和熔断降级都是依赖资源的实时指标数据统计实现的。为了能够提供丰富的流量控制功能,Sentinel 使用 Node 封装底层资源指标数据的统计,从多个不同维度为资源统计实时的指标数据,如区分不同调用来源、区分不同调用链。
ResourceWrapper
如果一个资源描述的是一个接口,则资源名称通常就是接口的 URL,如 GET:/api/demo。在 Sentinel 中,使用 ResourceWrapper 实例表示资源。ResourceWrapper 类源码如下:
name:资源名称,如 GET:/v1/demo。
entryType:流量类型,流入流量或流出流量。
resourceType:资源类型,如 Dubbo RPC、WebMvc 或 API Gateway。
EntryType 类是一个枚举类,EntryType 类源码如下:
IN 指流入流量,OUT 指流出流量。如果把 IN 和 OUT 简单理解为接收请求与发送请求,那么接收其他服务或前端发来的请求为流入流量;向其他服务发起的请求为流出流量。
Sentinel 目前支持的资源类型有以下几种:
COMMON:默认,可以是接口、一个方法、一段代码。
COMMON_WEB:Web 应用的接口。
COMMON_RPC:使用 Dubbo 框架实现的 RPC 接口。
COMMON_API_GATEWAY:API Gateway 网关接口。
COMMON_DB_SQL:数据库 SQL 操作。
虽然 Sentinel 在统计资源指标数据时将 ResourceWrapper 实例作为资源的 ID,但资源的名称才是资源的唯一标识,ResourceWrapper 实例只是额外记录资源类型和流量类型。
Node
Node 是一个接口,用于定义统计资源实时指标数据的方法,可以对外部屏蔽滑动窗口的存在。Node 接口的不同实现类被用在不同维度为资源统计实时指标数据。
Node 接口源码如下:
totalRequest:获取请求总数。
totalPass:获取被放行的请求总数。
totalSuccess:获取响应成功的请求总数,即被放行且未出现异常的请求总数。
blockRequest:获取被拒绝的请求总数。
totalException:获取发生异常的请求总数。
passQps:获取当前时间窗口被放行的请求总数。
blockQps:获取当前时间窗口被拒绝的请求总数。
totalQps:获取当前时间窗口的请求总数。
successQps:获取当前时间窗口响应成功的请求总数。
maxSuccessQps:获取一段时间内最大的 successQps,例如,若秒级滑动窗口的数组大小的默认配置为 2,则获取数组中 successQps 值最大的一个。
exceptionQps:获取当前时间窗口发生异常的请求总数。
avgRt:获取平均耗时。
minRt:获取最小耗时。
curThreadNum:获取当前并行占用的线程数。
previousBlockQps:获取前一个时间窗口的 blockQps。
previousPassQps:获取前一个时间窗口的 passQps。
addPassRequest:当前时间窗口被放行的请求总数+count。
addRtAndSuccess:当前时间窗口响应成功的请求总数+success 及总耗时+rt。
increaseBlockQps:当前时间窗口被拒绝的请求总数+1。
increaseExceptionQps:当前时间窗口发生异常的请求总数+1。
increaseThreadNum:并行占用线程数+1。
decreaseThreadNum:并行占用线程数-1。
Node 接口的几个实现类为 StatisticNode、DefaultNode、ClusterNode、EntranceNode,它们的关系如图所示:
StatisticNode
Statistic 是统计的意思,StatisticNode 类是 Node 接口的实现类,封装实现实时指标数据统计,其源码如下:
rollingCounterInSecond:秒级滑动窗口。
rollingCounterInMinute:分钟级滑动窗口。
curThreadNum:并行占用线程计数器。
一个 StatisticNode 实例包含一个秒级滑动窗口、一个分钟级滑动窗口及一个并行占用线程计数器。秒级滑动窗口用于统计实时的指标数据,分钟级滑动窗口用于保存最近一分钟内的历史指标数据,并行占用线程计数器用于统计实时占用的线程数。
需要注意的是,“分钟级滑动窗口用于保存最近一分钟内的历史指标数据”这句话并不完全正确,因为它统计的指标数据并不是从秒级滑动窗口得来的。
以 StatisticNode 类实现 Node 接口的 addRtAndSuccess 方法为例,addRtAndSuccess 方法中分别调用了两个滑动窗口的 addSuccess 方法和 addRT 方法,代码如下:
StatisticNode 实例的分钟级滑动窗口和秒级滑动窗口统计的指标数据分别有不同的用途,如下所示:
(1)当需要获取前一秒被拒绝的请求总数时,需要从分钟级滑动窗口中获取,代码如下:
(2)当需要获取当前一秒内已经被拒绝的请求总数时,需要从秒级滑动窗口中获取,代码如下:
(3)当需要获取当前一秒内的最小耗时时,需要从秒级滑动窗口中获取,代码如下:
从 StatisticNode 类定义的 curThreadNum 变量可以看出,StatisticNode 实例还负责统计资源并行占用的线程数,这个功能可用于实现信号量隔离,按资源所能并发占用的最大线程数实现限流。
除响应式编程外,每处理一个请求需要占用一个线程,那么可以在处理请求之前将 curThreadNum 自增 1,在处理完请求后将 curThreadNum 自减 1。当同时处理 N 个请求时,curThreadNum 的值就为 N。如果配置 Tomcat 处理请求的线程池大小为 N,则可以设置某接口允许并行占用的线程数小于 N,使该接口不能同时使用这 N 个线程,避免因为该接口响应慢将 N 个线程都阻塞而导致的应用无法处理其他请求的情况发生。
DefaultNode
从调用树的根节点出发,到每个叶子节点的路径都是一条调用链,而每条调用链中除根节点外,都可能有相同的入口节点。DefaultNode 实例用于统计同一资源、不同调用链入口的实时指标数据。
DefaultNode 类是 StatisticNode 类的子类,其构造方法要求传入资源 ID,表示该 DefaultNode 实例用于统计该资源的实时指标数据。DefaultNode 类的源码如下:
id:资源 ID,ResourceWrapper 实例。
childList:childList 是一个 Node 集合,用于存放子节点和构造调用树。
clusterNode:类型为 ClusterNode,引用当前资源全局唯一的 ClusterNode 实例。
一个资源可能有多个 DefaultNode 实例,而是否有多个 DefaultNode 实例取决于该资源是否被多个不同入口节点的调用链包含。Sentinel 这样做的目的是,实现可按不同调用链入口对资源采取不同的流量控制策略。
我们回顾一下 Sentinel 的基本使用方法,代码如下:
在本例中,doBusiness 方法就是被 Sentinel 保护的资源,资源名称为 demo,调用链入口名称为 myContextt。如果存在另一个入口名称为 myContextt2 的调用链也使用资源 demo,那么 Sentinel 就会对同一个资源 demo 创建两个 DefaultNode 实例。
ClusterNode
Sentinel 使用 ClusterNode 统计每个资源的全局指标数据,以及针对不同调用来源,分别统计资源的指标数据。
资源的全局指标数据统计不区分调用链入口,也不区分调用来源,一个资源有且仅有一个 ClusterNode 实例。
ClusterNode 类的源码如下:
name:资源名称。
resourceType:资源类型。
originCountMap:key 为调用来源,value 为 StatisticNode 实例。
ClusterNode 类没有使用 ResourceWrapper 实例表示资源,而是直接使用了资源名称,这可能是历史版本的原因,虽然 DefaultNode 类使用 ResourceWrapper 实例表示资源,但是在统计资源的实时指标数据时并不区分流量类型(IN/OUT)。
EntranceNode
EntranceNode 类是 DefaultNode 类的子类,调用链的入口节点及调用树的根节点的类型都是 EntranceNode。EntranceNode 类的源码如下:
在一个 Web 应用中,一个接口就是一个资源。Sentinel 通过 WebMvc 拦截器拦截每个接口的请求,为接口披上“保护伞”,并给接口设置相同的入口名称,即 sentinel_spring_web_context,代码如下:
Sentinel 会创建一个名称为 sentinel_spring_web_context 的入口节点,类型为 EntranceNode。一个 Web 应用可能有多个接口,但它们的入口节点都是 sentinel_spring_web_context,而入口节点的 childList 就用于存储每个接口的 DefaultNode 实例。
如果想统计一个 Web 应用的所有接口(不一定是所有接口,没有被调用过的接口不会创建对应的 DefaultNode 实例)的总 QPS,那么只需要调用入口节点(EntranceNode 实例)的 totalQps 方法就能获取。
EntranceNode 类的 totalQps 方法源码如下:
调用链入口节点、资源的 DefaultNode 节点、资源的 ClusterNode 节点与滑动窗口的关系如图所示:
评论