本文转载自微信公众号「运维开发故事」,作者老郑。转载本文请联系运维开发故事公众号。
10余年的坡头网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。网络营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整坡头建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。成都创新互联从事“坡头网站设计”,“坡头网站推广”以来,每个客户项目都认真落实执行。
在前面两篇文章给大家介绍了 Sentinel 的功能和基本使用。现在我们继续来学习 Sentinel 控制台的基本使用,以及一些规则配置的说明。让大家能够在工作中使用 Sentinel 得心应手 (大部分理论和描述来源于官方文档和网络)。
在正文开始之前,我先说一下我的基本环境信息
Sentinel 提供一个轻量级的开源控制台,它提供机器发现以及健康情况管理、监控(单机和集群),规则管理和推送的功能。这里,我们将会详细讲述如何通过简单的步骤就可以使用这些功能。
Sentinel 控制台包含如下功能:
注意:Sentinel 控制台目前仅支持单机部署。Sentinel 控制台项目提供 Sentinel 功能全集示例,不作为开箱即用的生产环境控制台,若希望在生产环境使用需要自行定制和改造。
Alibaba 提供了企业版本的 Sentinel 我们可以在 aliyun.com 上面购买 AHAS Sentinel
如果我们正确的接入 Sentinel 之后我们可以在 Sentinel 控制台的 机器列表 菜单中来查看我们服务节点的健康情况
如果 Sentinel 接入不成功,可以查阅 Sentinel 官方文档或者 FAQ 来对应排查
1. 实时监控
同时,同一个服务下的所有机器的簇点信息会被汇总,并且秒级地展示在"实时监控"下。
注意: 实时监控仅存储 5 分钟以内的数据,如果需要持久化,需要通过调用实时监控接口来定制。
注意:请确保 Sentinel 控制台所在的机器时间与自己应用的机器时间保持一致,否则会导致拉不到实时的监控数据。
2. 簇点链路
簇点链路(单机调用链路)页面实时的去拉取指定客户端资源的运行情况。它一共提供两种展示模式:一种用树状结构展示资源的调用链路,另外一种则不区分调用链路展示资源的实时情况。
注意: 簇点链路监控是内存态的信息,它仅展示启动后调用过的资源。
注意:请确保 Sentinel 控制台所在的机器时间与自己应用的机器时间保持一致,否则会导致拉不到实时的监控数据。
3 流控规则
流量控制(flow control),其原理是监控应用流量的 QPS 或并发线程数等指标,当达到指定的阈值时对流量进行控制,以避免被瞬时的流量高峰冲垮,从而保障应用的高可用性。
FlowSlot 会根据预设的规则,结合 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 统计出来的实时信息进行流量控制。
限流的直接表现是在执行 Entry nodeA = SphU.entry(resourceName) 的时候抛出 FlowException 异常。FlowException 是 BlockException 的子类,您可以捕捉 BlockException 来自定义被限流之后的处理逻辑。
Sentinel 在触发规则保护时,返回的异常页面是一样的。不好区分是因为哪种规则导致的异常。所以需要自定义异常返回信息,明确是触发了哪种类型的规则。
- @Component
- public class SentinelBlockHandler implements BlockExceptionHandler {
- @Override
- public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
- BlockException e) throws Exception {
- CommonResult
result = new CommonResult<>(); - if (e instanceof FlowException) {
- result = CommonResult.error(101, "接口限流了");
- } else if (e instanceof DegradeException) {
- result = CommonResult.error(102, "服务降级了");
- } else if (e instanceof ParamFlowException) {
- result = CommonResult.error(103, "热点参数限流了");
- } else if (e instanceof SystemBlockException) {
- result = CommonResult.error(104, "系统规则(负载/...不满足要求)");
- } else if (e instanceof AuthorityException) {
- result = CommonResult.error(105, "授权规则不通过");
- }
- // http状态码
- httpServletResponse.setStatus(500);
- httpServletResponse.setCharacterEncoding("utf-8");
- httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
- httpServletResponse.setContentType("application/json;charset=utf-8");
- // spring mvc自带的json操作工具,叫jackson
- new ObjectMapper().writeValue(httpServletResponse.getWriter(), result);
- }
- }
效果如下:
- curl http://127.0.0.1:8088/getStockDetail
- {"code":1,"message":"this is a success message","data":{"id":1,"code":"STOCK==>1000"}}% curl http://127.0.0.1:8088/getStockDetail
- {"code":1,"message":"this is a success message","data":{"id":1,"code":"STOCK==>1000"}}% curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
线程数
并发数控制用于保护业务线程池不被慢调用耗尽。例如,当应用所依赖的下游应用由于某种原因导致服务不稳定、响应延迟增加,对于调用者来说,意味着吞吐量下降和更多的线程数占用,极端情况下甚至导致线程池耗尽。为应对太多线程占用的情况,业内有使用隔离的方案,比如通过不同业务逻辑使用不同线程池来隔离业务自身之间的资源争抢(线程池隔离)。这种隔离方案虽然隔离性比较好,但是代价就是线程数目太多,线程上下文切换的 overhead 比较大,特别是对低延时的调用有比较大的影响。Sentinel 并发控制不负责创建和管理线程池,而是简单统计当前请求上下文的线程数目(正在执行的调用数目),如果超出阈值,新的请求会被立即拒绝,效果类似于信号量隔离。并发数控制通常在调用端进行配置。
可以通过线程池模拟客户端调用, 也可以通过 Jmeter 模拟,触发流控的结果如下:
- ~ curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
调用关系包括调用方、被调用方;一个方法又可能会调用其它方法,形成一个调用链路的层次关系。
直接
当资源触发流控规则过后直接,抛出异常信息
- ~ curl http://127.0.0.1:8088/getStockDetail
- {"code":101,"message":"接口限流了","data":null}%
关联
当两个资源之间具有资源争抢或者依赖关系的时候,这两个资源便具有了关联。比如对数据库同一个字段的读操作和写操作存在争抢,读的速度过高会影响写的速度,写的速度过高会影响读的速度。如果放任读写操作争抢资源,则争抢本身带来的开销会降低整体的吞吐量。可使用关联限流来避免具有关联关系的资源之间过度的争抢,举例来说,read_db 和 write_db 这两个资源分别代表数据库读写,我们可以给 read_db 设置限流规则来达到写优先的目的:设置 strategy 为 RuleConstant.STRATEGY_RELATE 同时设置 refResource 为 write_db。这样当写库操作过于频繁时,读数据的请求会被限流。
如果配置流控规则为关联模式,那么当 /hello 接口超过阈值过后,就会对 /getStockDetail 接口触发流控规则。
链路
NodeSelectorSlot 中记录了资源之间的调用链路,这些资源通过调用关系,相互之间构成一棵调用树。这棵树的根节点是一个名字为 machine-root 的虚拟节点,调用链的入口都是这个虚节点的子节点。
一棵典型的调用树如下图所示:
- machine-root
- / \
- / \
- Entrance1 Entrance2
- / \
- / \
- DefaultNode(nodeA) DefaultNode(nodeA)
上图中来自入口 Entrance1 和 Entrance2 的请求都调用到了资源 NodeA,Sentinel 允许只根据某个入口的统计信息对资源限流。比如我们可以设置 strategy 为 RuleConstant.STRATEGY_CHAIN,同时设置 refResource 为 Entrance1 来表示只有从入口 Entrance1 的调用才会记录到 NodeA 的限流统计当中,而不关心经 Entrance2 到来的调用。
调用链的入口(上下文)是通过 API 方法 ContextUtil.enter(contextName) 定义的,其中 contextName 即对应调用链路入口名称。详情可以参考 ContextUtil 文档。]
测试会发现 链路 不会生效
从1.6.3版本开始,Sentinel Web filter默认收敛所有URL的入口context,因此链路限流不生效。1.7.0版本开始(对应SCA 2.1.1.RELEASE),我们在CommonFilter引入了WEB_CONTEXT_UNIFY这个init parameter,用于控制是否收敛context。将其配置为false即可根据不同的URL进行链路限流。参考:https://github.com/alibaba/sentinel/issues/1213
解决方案:
1.7.0 版本开始(对应Spring Cloud Alibaba的2.1.1.RELEASE) 需要新增依赖
- @Configuration
- public class FilterContextConfig {
- @Bean
- public FilterRegistrationBean sentinelFilterRegistration() {
- FilterRegistrationBean registration = new FilterRegistrationBean();
- registration.setFilter(new CommonFilter());
- registration.addUrlPatterns("/*");
- // 入口资源关闭聚合
- registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
- registration.setName("sentinelFilter");
- registration.setOrder(1);
- return registration;
- }
- }
然后我们再尝试触发流控规则, 对 /getStockDetail 进行访问,这里返回了FlowException
默认情况会返回
如果我们使用 OpenFeign 不添加 fallbackFactory 就会返回500 , 如果我们添加了就可以避免这个问题。
- // Controller
- @Autowired
- private StockFeign stockFeign;
- @GetMapping("/getStockDetail")
- public CommonResult
getStockDetail() { - CommonResult
result = stockFeign.getStockDetail(); - if (result.getCode() != 1) {
- return CommonResult.error(null, result.getCode(), result.getMessage());
- }
- return result;
- }
- // FeignClient
- @FeignClient(name = "stock-service")
- //, fallbackFactory = StockFeignFallbackFactory.class)
- public interface StockFeign {
- @GetMapping("/getStockDetail")
- CommonResult
getStockDetail(); - }
Sentinel 部分源码:
所以,我们在设置链路流控规则的时候一定要设置 fallbackFactory。不然无法处理 FlowExecption 异常信息,造成系统出错。对于个人而言,Sentinel 的链路规则比不是特别的好用,无特殊要求,不建议使用,或者选择Sentinel 的收费版本 AHAS
当 QPS、线程数超过某个阈值的时候,则采取措施进行流量控制。流量控制的效果包括以下几种:直接拒绝、Warm Up、匀速排队。
直接拒绝
直接拒绝(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)方式是默认的流量控制方式,当QPS超过任意规则的阈值后,新的请求就会被立即拒绝,拒绝方式为抛出FlowException。 这种方式适用于对系统处理能力确切已知的情况下,比如通过压测确定了系统的准确水位时。
Warm up
Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即预热/冷启动方式。当系统长期处于低水位的情况下,当流量突然增加时,直接把系统拉升到高水位可能瞬间把系统压垮。通过"冷启动",让通过的流量缓慢增加,在一定时间内逐渐增加到阈值上限,给冷系统一个预热的时间,避免冷系统被压垮。
通常冷启动的过程系统允许通过的 QPS 曲线如下图所示:
默认 coldFactor 为 3,即请求 QPS 从 threshold / 3 开始,经预热时长逐渐升至设定的 QPS 阈值。
规则设置如下图所示:
通过 Jmeter 请求过后,可以看到如下效果,完成流控
匀速排队
匀速排队(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)方式会严格控制请求通过的间隔时间,也即是让请求以均匀的速度通过,对应的是漏桶算法。
这种方式主要用于处理间隔性突发的流量,例如消息队列。想象一下这样的场景,在某一秒有大量的请求到来,而接下来的几秒则处于空闲状态,我们希望系统能够在接下来的空闲期间逐渐处理这些请求,而不是在第一秒直接拒绝多余的请求。
注意:匀速排队模式暂时不支持 QPS > 1000 的场景。
规则设置如下图所示:
然后我们通过 jmeter 请求过后可以看到如下效果:
流量控制以外,对调用链路中不稳定的资源进行熔断降级也是保障高可用的重要措施之一。一个服务常常会调用别的模块,可能是另外的一个远程服务、数据库,或者第三方 API 等。例如,支付的时候,可能需要远程调用银联提供的 API;查询某个商品的价格,可能需要进行数据库查询。然而,这个被依赖服务的稳定性是不能保证的。如果依赖的服务出现了不稳定的情况,请求的响应时间变长,那么调用服务的方法的响应时间也会变长,线程会产生堆积,最终可能耗尽业务自身的线程池,服务本身也变得不可用。
熔断降级策略
Sentinel 提供以下几种熔断策略:
我们可以在控制台配置:
jmeter 模拟请求
异常比例 (ERROR_RATIO):当单位统计时长(statIntervalMs)内请求数目大于设置的最小请求数目,并且异常的比例大于阈值,则接下来的熔断时长内请求会自动被熔断。经过熔断时长后熔断器会进入探测恢复状态(HALF-OPEN 状态),若接下来的一个请求成功完成(没有错误)则结束熔断,否则会再次被熔断。异常比率的阈值范围是 [0.0, 1.0],代表 0% - 100%。
我们可以在控制台配置:
我们可以在控制台配置:
熔断降级规则(DegradeRule)包含下面几个重要的属性:
Field |
说明 |
默认值 |
resource |
资源名,即规则的作用对象 |
|
grade |
熔断策略,支持慢调用比例/异常比例/异常数策略 |
慢调用比例 |
count |
慢调用比例模式下为慢调用临界 RT(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值 |
|
timeWindow |
熔断时长,单位为 s |
|
minRequestAmount |
熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入) |
5 |
statIntervalMs |
统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入) |
1000 ms |
slowRatioThreshold |
慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入) |
何为热点?热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
热点参数限流会统计传入参数中的热点参数,并根据配置的限流阈值与模式,对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制,仅对包含热点参数的资源调用生效。
Sentinel 利用 LRU 策略统计最近最常访问的热点参数,结合令牌桶算法来进行参数级别的流控。热点参数限流支持集群模式。
热点规则配置需要注意:
1. 首先资源必须是通过 @SentinelResource 申明
2. 参数类型必须是基础数据类型, 否则配置无效
热点规则配置如下图所示:
注意:资源名称要和 @SentinelResource 中的资源名称对应才能生效
控制器类的代码如下所示:
- @SentinelResource(value = "ResOrderGet",
- fallback = "fallback",
- fallbackClass = SentinelExceptionHandler.class,
- blockHandler = "blockHandler",
- blockHandlerClass = SentinelExceptionHandler.class
- )
- @GetMapping("/order/get/{id}")
- public CommonResult
getStockDetails(@PathVariable Integer id) { - StockModel stockModel = new StockModel();
- stockModel.setCode("STOCK==>1000");
- stockModel.setId(id);
- return CommonResult.success(stockModel);
- }
- // 异常处理类
- public class SentinelResourceExceptionHandler {
- //限流熔断业务逻辑
- public static CommonResult
blockHandler(@PathVariable Integer id) { - return CommonResult.error(null, -100, "系统错误 (限流熔断业务逻辑)");
- }
- //异常降级业务逻辑
- public static CommonResult
fallback(@PathVariable Integer id) { - return CommonResult.error(null, -100, "系统错误 (异常降级业务逻辑)");
- }
- }
返回异常信息:
很多时候,我们需要根据调用来源来判断该次请求是否允许放行,这时候可以使用 Sentinel 的来源访问控制(黑白名单控制)的功能。来源访问控制根据资源的请求来源(origin)限制资源是否通过,若配置白名单则只有请求来源位于白名单内时才可通过;若配置黑名单则请求来源位于黑名单时不通过,其余的请求通过。
调用方信息通过 ContextUtil.enter(resourceName, origin) 方法中的 origin 参数传入。
Sentinel提供了 RequestOriginParser 接口来处理访问来源,Sentinel保护的资源如果被访问,就会调用 RequestOriginParser解析访问来源。
- // 注意导包
- import com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser;
- import javax.servlet.http.HttpServletRequest;
- public class SentinelRequestOriginParser implements RequestOriginParser {
- @Override
- public String parseOrigin(HttpServletRequest request) {
- return request.getParameter("origin");
- }
- }
修改 Config 配置信息
- @Configuration
- public class FilterContextConfig {
- @Bean
- public FilterRegistrationBean sentinelFilterRegistration() {
- FilterRegistrationBean registration = new FilterRegistrationBean();
- registration.setFilter(new CommonFilter());
- registration.addUrlPatterns("/*");
- // 入口资源关闭聚合
- registration.addInitParameter(CommonFilter.WEB_CONTEXT_UNIFY, "false");
- registration.setName("sentinelFilter");
- registration.setOrder(1);
- // CommonFilter 的 BlockException 自定义处理逻辑
- WebCallbackManager.setUrlBlockHandler(new SentinelFlowHandler());
- //解决授权规则不生效的问题
- //com.alibaba.csp.sentinel.adapter.servlet.callback.RequestOriginParser
- WebCallbackManager.setRequestOriginParser(new SentinelRequestOriginParser());
- return registration;
- }
- }
规则配置
正常通过
异常不通过
系统保护规则是从应用级别的入口流量进行控制,从单台机器的 load、CPU 使用率、平均 RT、入口 QPS 和并发线程数等几个维度监控应用指标,让系统尽可能跑在最大吞吐量的同时保证系统整体的稳定性。
系统保护规则是应用整体维度的,而不是资源维度的,并且仅对入口流量生效。入口流量指的是进入应用的流量(EntryType.IN),比如 Web 服务或 Dubbo 服务端接收的请求,都属于入口流量。
系统规则支持以下的模式:
如下图所示
我们把系统处理请求的过程想象为一个水管,到来的请求是往这个水管灌水,当系统处理顺畅的时候,请求不需要排队,直接从水管中穿过,这个请求的RT是最短的;反之,当请求堆积的时候,那么处理请求的时间则会变为:排队时间 + 最短处理时间。
我们用 T 来表示(水管内部的水量),用RT来表示请求的处理时间,用P来表示进来的请求数,那么一个请求从进入水管道到从水管出来,这个水管会存在 P * RT 个请求。换一句话来说,当 T ≈ QPS * Avg(RT) 的时候,我们可以认为系统的处理能力和允许进入的请求个数达到了平衡,系统的负载不会进一步恶化。
接下来的问题是,水管的水位是可以达到了一个平衡点,但是这个平衡点只能保证水管的水位不再继续增高,但是还面临一个问题,就是在达到平衡点之前,这个水管里已经堆积了多少水。如果之前水管的水已经在一个量级了,那么这个时候系统允许通过的水量可能只能缓慢通过,RT会大,之前堆积在水管里的水会滞留;反之,如果之前的水管水位偏低,那么又会浪费了系统的处理能力。
然而,和 TCP BBR 的不一样的地方在于,还需要用一个系统负载的值(load1)来激发这套机制启动。
注:这种系统自适应算法对于低 load 的请求,它的效果是一个“兜底”的角色。对于不是应用本身造成的 load 高的情况(如其它进程导致的不稳定的情况),效果不明显。
配置页面
触发流控规则
为什么要使用集群流控呢?假设我们希望给某个用户限制调用某个 API 的总 QPS 为 50,但机器数可能很多(比如有 100 台)。这时候我们很自然地就想到,找一个 server 来专门来统计总的调用量,其它的实例都与这台 server 通信来判断是否可以调用。这就是最基础的集群流控的方式。
另外集群流控还可以解决流量不均匀导致总体限流效果不佳的问题。假设集群中有 10 台机器,我们给每台机器设置单机限流阈值为 10 QPS,理想情况下整个集群的限流阈值就为 100 QPS。不过实际情况下流量到每台机器可能会不均匀,会导致总量没有到的情况下某些机器就开始限流。因此仅靠单机维度去限制的话会无法精确地限制总体流量。而集群流控可以精确地控制整个集群的调用总量,结合单机限流兜底,可以更好地发挥流量控制的效果。
集群流控中共有两种身份:
Sentinel 控制台同时提供简单的规则管理以及推送的功能。规则推送分为 3 种模式,包括 "原始模式"、"Pull 模式" 和"Push 模式"。
这里先简单的介绍"原始模式"。
您可以在控制台通过接入端暴露的 HTTP API 来查询规则。
目前控制台的规则推送也是通过 规则查询更改 HTTP API 来更改规则。这也意味着这些规则仅在内存态生效,应用重启之后,该规则会丢失。
注:若通过控制台推送规则时出现 invalid type 或 empty type 的错误,请确保 transport 模块版本与 core 模块版本保持一致;若控制台版本 >= 1.7.1,请将接入端的相关依赖也升级至 1.7.1 及以上版本。
以上是原始模式。当了解了原始模式之后,我们非常鼓励您通过 动态规则 并结合各种外部存储来定制自己的规则源。我们推荐通过动态配置源的控制台来进行规则写入和推送,而不是通过 Sentinel 客户端直接写入到动态配置源中。在生产环境中,我们推荐 push 模式,具体可以参考:在生产环境使用 Sentinel。
注:若要使用集群流控功能,则必须对接动态规则源,否则无法正常使用。您也可以接入 AHAS Sentinel 快速接入全自动托管、高可用的集群流控能力。
Sentinel 同时还提供应用维度规则推送的示例页面(流控规则页面,前端路由为 /v2/flow),用户改造控制台对接配置中心后可直接通过 v2 页面推送规则至配置中心。Sentinel 抽取了通用接口用于向远程配置中心推送规则以及拉取规则:
DynamicRuleProvider : 拉取规则(应用维度)
DynamicRulePublisher : 推送规则(应用维度)
用户只需实现 DynamicRuleProvider 和 DynamicRulePublisher 接口,并在 v2 的 controller 中通过 @Qualifier 注解替换相应的 bean 即可实现应用维度推送。我们提供了 Nacos 和 Apollo 的示例,改造详情可参考 应用维度规则推送示例。
从 Sentinel 1.5.0 开始,控制台提供通用的鉴权接口 AuthService,用户可根据需求自行实现。
从 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登录功能,默认用户名和密码都是 sentinel。该鉴权能力非常基础,生产环境使用建议根据安全需要自行改造。
用户可以通过如下参数进行配置:
同样也可以直接在 Spring properties 文件中进行配置。
注意:部署多台控制台时,session 默认不会在各实例之间共享,这一块需要自行改造。
参考文档
https://github.com/alibaba/Sentinel/wiki/控制台
网页标题:Sentinel流控规则详解
当前路径:http://www.gawzjz.com/qtweb2/news48/9398.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联