首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

深度解析微服务高并发热点参数限流:热点参数限流功能的实现

热点参数限流

参数限流是指根据方法调用传递的参数实现限流,或者根据接口的请求参数限流,而热点参数限流是指对访问频繁的参数限流。

在电商场景中,每位顾客购买的商品不同,有主播带货的商品下单流量较大,而没有主播带货的商品相对来说下单流量较少。因为商品数量有限,每个下单请求都能成功是不太可能的,所以如果能够根据客户端请求传递的商品ID实现限流,将流量控制在商品的库存总量左右,并且使用QPS限流等进行兜底,即可将接口通过的有效流量最大化。

本篇内容主要包括以下几个方面:

热点参数限流功能的实现。

流量效果控制。

热点参数限流功能的实现

与围绕资源实现限流不同,热点参数限流是围绕资源的参数的不同取值来限流的,它不需要统计资源指标数据,而需要统计不同参数取值的指标数据。因此,热点参数限流功能不在Sentinel的核心模块中,而是被当作一个扩展功能放在Sentinel的扩展功能模块——sentinel-extension中,其子模块为sentinel-parameter-flow-control。

热点参数指标数据统计

热点参数限流不使用Node统计指标数据,而是使用ParameterMetric与ParameterMetricStorage统计指标数据。

ParameterMetric:用于实现类似于ClusterNode的统计功能。

ParameterMetricStorage:用于实现类似于EntranceNode的功能,管理和存储每个资源对应的ParameterMetric。

ParameterMetric有3个静态字段,源码如下:

ruleTimeCounters:用于实现匀速流量控制效果,key为参数限流规则(ParamFlowRule),value为参数不同取值对应的上次生产令牌的时间。

ruleTokenCounter:用于实现匀速流量控制效果,key为参数限流规则(ParamFlowRule),value为参数不同取值对应的当前令牌桶中的令牌数。

threadCountMap:key为参数索引,value为参数不同取值对应的当前并行占用的线程总数。

ParameterMetricStorage使用ConcurrentHashMap缓存每个资源对应的ParameterMetric,且只会为配置了参数限流规则的资源创建一个ParameterMetric实例,其部分源码如下:

ParameterMetricStorage的initParamMetricsFor方法用于为资源创建ParameterMetric实例并初始化。该方法在资源被访问时由ParamFlowSlot调用,并且该方法只在为资源配置了参数限流规则的情况下被调用。

热点参数限流的实现原理

既然是参数限流,那么肯定需要获取参数,而ProcessorSlot#entry方法的最后一个参数就是请求传递过来的参数,该参数通过SphU#entry方法一层层向下传递。

回顾Sentinel的使用案例,代码如下:

在此案例中,如果导入了热点参数限流模块,并且为资源GET:/hello配置了热点参数规则,则调用SphU#entry方法时传入的name就是热点参数,且name最终会被传递给热点参数限流模块的ProcessorSlot。

热点参数限流模块通过JavaSPI注册自定义的SlotChainBuilder,即注册HotParamSlotChainBuilder,将ParamFlowSlot放置在StatisticSlot的后面,这个ParamFlowSlot就是实现热点参数限流功能的处理器插槽,其部分源码如下:

如源码所示,在ParamFlowSlot#entry方法中,首先调用ParamFlowRuleManager#hasRules方法判断当前资源是否存在参数限流规则,如果存在,则调用ParamFlowSlot#checkFlow方法判断当前请求是否允许通过。ParamFlowSlot#checkFlow方法的源码如下:

checkFlow方法的最后一个参数是请求参数,也就是调用SphU#entry方法传递进来的参数,如果参数为null,则没有必要走后续逻辑。

调用ParamFlowRuleManager#getRulesOfResource方法获取为当前资源配置的所有参数限流规则。

遍历参数限流规则,首先调用ParameterMetricStorage#initParamMetricsFor方法判断是否需要为当前资源初始化创建ParameterMetric实例,然后调用ParamFlowChecker#passCheck方法判断当前请求是否可以被放行,如果需要拒绝请求,则抛出ParamFlowException。

在阅读ParamFlowChecker#passCheck方法的源码之前,我们需要先了解参数限流规则的配置,了解每个配置项的作用。

设置参数限流规则的类名为ParamFlowRule,其源码如下:

grade:限流规则的阈值类型,支持的类型与FlowRule相同。

count:阈值,支持的类型与FlowRule相同。

paramIdx:参数索引,ParamFlowChecker根据限流规则的参数索引获取参数的值,下标从0开始。例如,apiHello(String name)方法只有一个参数,索引为0对应name参数。

controlBehavior:流量控制效果,支持的类型与FlowRule相同,但只支持快速失败和匀速排队。

maxQueueingTimeMs:实现匀速排队流量控制效果的虚拟队列最大等待时间,超过该值的请求被抛弃,支持的类型与FlowRule相同。

durationInSec:统计指标数据的时间窗口大小,单位为秒。

burstCount:支持的突发流量总数。

假设需要对资源GET:/hello的name参数限流,当name取值为jackson时,QPS限流阈值为5,则代码如下:

以此为例,我们继续分析ParamFlowChecker#passCheck方法的源码。passCheck方法返回true表示放行,返回false表示拒绝。passCheck方法的源码如下:

若参数为空,或者规则配置的参数索引越界,或者参数索引对应的参数值为空,则放行请求。

若是集群限流模式,则调用passClusterCheck方法,否则调用passLocalCheck方法。

我们先不讨论集群限流情况,仅看单机本地限流情况。passLocalCheck方法的源码如下:

由于参数可能是基本数据类型,也可能是数组类型或引用类型,因此passLocalCheck方法分3种情况进行处理。这里只讨论其中一种情况,其他情况的处理方式与其类似。

以资源GET:/hello为例,其apiHello方法的name参数为String类型,因此会调用passSingleValueCheck方法。该方法的源码如下:

当规则配置的阈值类型为QPS时,根据限流效果调用passThrottleLocalCheck方法或passDefaultLocalCheck方法。

当规则配置的阈值类型为Threads时,获取当前资源的ParameterMetric实例,从而获取当前资源和参数取值对应的并行占用线程总数。如果并行占用线程总数加1大于限流阈值,则拒绝请求,否则放行请求。

并行占用线程总数是在哪里自增和自减的呢?这是由

ParamFlowStatisticEntryCallback与ParamFlowStatisticExitCallback这两个Callback实现的,这两个Callback分别在StatisticSlot的entry方法和exit方法中被回调执行。

本文给大家讲解的内容是深度解析微服务高并发热点参数限流:热点参数限流功能的实现

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230406A00LHM00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券