Sentinel根据设置的资源名称构建出相应的资源,同时还会根据设置的规则创建一系列的功能插槽,在访问资源前,这些功能插槽会进行相应数据统计与降级校验,以保证分布式场景下服务的稳定。
一,sentinel的特征图
二,sentinel基本概念
资源
资源是sentinel的关键概念,资源可以是java应用里的任何内容,例如,由应用程序提供的服务,或由应用程序调用的其它应用提供的服务,甚至可以是一段代码。只要通过 Sentinel API 定义的代码,就是资源,能够被 Sentinel 保护起来。大部分情况下,可以使用方法签名,URL,甚至服务名称作为资源名来标示资源。
规则
围绕资源的实时状态设定的规则,可以包括流量控制规则、熔断降级规则以及系统保护规则。所有规则可以动态实时调整。
三,sentinel功能与设计理念
流量控制
流量控制在网络传输中是一个常用的概念,它用于调整网络包的发送数据。然而,从系统稳定性角度考虑,在处理请求的速度上,也有非常多的讲究。任意时间到来的请求往往是随机不可控的,而系统的处理能力是有限的。我们需要根据系统的处理能力对流量进行控制。Sentinel 作为一个调配器,可以根据需要把随机的请求调整成合适的形状,如下图所示:
可以从下面几方面对流量进行调整:
- 资源的调用关系,例如资源的调用链路,资源和资源之间的关系;
- 运行指标,例如 QPS、线程池、系统负载等;
- 控制的效果,例如直接限流、冷启动、排队等。
Sentinel 的设计理念是让您自由选择控制的角度,并进行灵活组合,从而达到想要的效果。
熔断降级
除了流量控制以外,降低调用链路中的不稳定资源也是 Sentinel 的使命之一。由于调用关系的复杂性,如果调用链路中的某个资源出现了不稳定,最终会导致请求发生堆积。Sentinel 和 Hystrix 的原则是一致的: 当检测到调用链路中某个资源出现不稳定的表现,例如请求响应时间长或异常比例升高的时候,则对这个资源的调用进行限制,让请求快速失败,避免影响到其它的资源而导致级联故障。
熔断降级的设计理念
在限制的手段上,Sentinel 和 Hystrix 采取了完全不一样的方法。Hystrix 通过 线程池隔离 的方式,来对依赖(在 Sentinel 的概念中对应 资源)进行了隔离。这样做的好处是资源和资源之间做到了最彻底的隔离。缺点是除了增加了线程切换的成本(过多的线程池导致线程数目过多),还需要预先给各个资源做线程池大小的分配。
sentinel又是如何做的呢?
- 通过并发线程数进行限制 : 和资源池隔离的方法不同,Sentinel 通过限制资源并发线程的数量,来减少不稳定资源对其它资源的影响。这样不但没有线程切换的损耗,也不需要您预先分配线程池的大小。当某个资源出现不稳定的情况下,例如响应时间变长,对资源的直接影响就是会造成线程数的逐步堆积。当线程数在特定资源上堆积到一定的数量之后,对该资源的新请求就会被拒绝。堆积的线程完成任务后才开始继续接收请求。
- 通过响应时间对资源进行降级 : 除了对并发线程数进行控制以外,Sentinel 还可以通过响应时间来快速降级不稳定的资源。当依赖的资源出现响应时间过长后,所有对该资源的访问都会被直接拒绝,直到过了指定的时间窗口之后才重新恢复。
系统负载保护:
Sentinel 同时提供系统维度的自适应保护能力。防止雪崩,是系统防护中重要的一环。当系统负载较高的时候,如果还持续让请求进入,可能会导致系统崩溃,无法响应。在集群环境下,网络负载均衡会把本应这台机器承载的流量转发到其它的机器上去。如果这个时候其它的机器也处在一个边缘状态的时候,这个增加的流量就会导致这台机器也崩溃,最后导致整个集群不可用。针对这个情况,Sentinel 提供了对应的保护机制,让系统的入口流量和系统的负载达到一个平衡,保证系统在能力范围之内处理最多的请求。
四,Sentinel 中的 Node
Node用于持有实时统计的指标数据,Node接口定义了一个Node类所需要提供的各项指标数据统计的相关功能,为外部屏蔽滑动窗口的存在。提供记录请求被拒绝、请求被放行、请求处理异常、请求处理成功的方法,以及获取当前时间窗口统计的请求总数、平均耗时等方法。
public interface Node extends OccupySupport, DebugSupport {
long totalRequest(); // 获取总的请求数
long totalPass(); // 获取通过的请求总数
long totalSuccess(); // 获取成功的请求总数
long blockRequest(); // 获取被 Sentinel 拒绝的请求总数
long totalException(); // 获取异常总数
double passQps(); // 通过 QPS
double blockQps(); // 拒绝 QPS
double totalQps(); // 总 qps
double successQps(); // 成功 qps
double maxSuccessQps(); // 最大成功总数 QPS(例如秒级滑动窗口的数组大小默认配置为 2,则取数组中最大)
double exceptionQps(); // 异常 QPS
double avgRt(); // 平均耗时
double minRt(); // 最小耗时
int curThreadNum(); // 当前并发占用的线程数
double previousBlockQps(); // 前一个时间窗口的被拒绝 qps
double previousPassQps(); // 前一个时间窗口的通过 qps
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(); // 重置滑动窗口
}
它的几个实现类:DefaultNode、ClusterNode、EntranceNode、StatisticNode 的关系如下图所示。
五,Sentinel 中上的Context与Entry
Context
Context代表调用链路上下文,贯穿一次调用链路中的所有Entry,Context维持着入口节点、本次调用链路的节点、调用来源等信息。
Context通过ThreadLocal传递,只在调用链路的入口处创建。
Entry
在调用Context#getCurNode方法获取调用链路上当前访问到的资源的DefaultNode时,实际是从Context#curEntry获取的,Entry维护了当前资源的DefaultNode,以及调用来源的StatisticNode。
六,Sentinel 中的 ProcessorSlot
ProcessorSlot处理器插槽是Sentinel实现限流降级、熔断降级、系统自适应降级等功能的切入点。Sentinel提供的ProcessorSlot分两类:一类是辅助完成资源指标数据统计的切入点;一类是实现降级功能的切入点。
辅助资源指标数据统计的ProcessorSlot
- NodeSelectorSlot:为当前资源创建DefaultNode,负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
- ClusterBuilderSlot:用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
- StatisticSlot:最重要的类之一,用于记录、统计不同纬度的 runtime 指标监控信息。每次判断完请求是否放行后会根据判断结果进行相应指标数据的统计操作
实现降级功能的ProcessorSlot
- AuthoritySlot:实现黑白名单降级
- SystemSlot:实现系统自适应降级
- FlowSlot:实现限流降级
- DegradeSlot:实现熔断降级
使用示例
- 定义资源限流规则:
//初始化规则
private static void initFlowRules() {
List<FlowRule> rules = new ArrayList<>(); //限流规则的集合
FlowRule flowRule = new FlowRule();//限流规则
flowRule.setResource("ruleTest");//资源(可以是方法名称、接口)
//线程数(FLOW_GRADE_THREAD)与QPS (FLOW_GRADE_QPS)
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS); //限流的阈值的类型
flowRule.setCount(18);// QPS数
rules.add(flowRule);
FlowRuleManager.loadRules(rules);
}
启动测试类
public static void main(String[] args) { initFlowRules(); //初始化一个规则 while(true){ Entry entry=null; try{ entry= SphU.entry("ruleTest"); System.out.println("Hello Word"); }catch (BlockException e){//如果被限流了,那么会抛出这个异常 e.printStackTrace(); }finally { if(entry!=null){ entry.exit();// 释放 } } } }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。