hyperf原生rate-limit不支持热点参数限流,必须用sentinel实现;但php版sentinel默认未注册paramflowslot,导致@sentinelresource注解对热点规则无效。

Hyperf 原生 hyperf/rate-limit 不支持热点参数限流,必须用 Sentinel 实现;但 PHP 版 Sentinel(alibaba/sentinel-php)默认不内置热点规则支持,需手动启用 sentinel-parameter-flow-control 扩展逻辑并注册 ParamFlowSlot。
为什么 @SentinelResource 注解对热点参数无效
常见现象:加了 @SentinelResource(value="createOrder", blockHandler="handleHotspot"),但在 Sentinel Dashboard 里新增热点规则后完全不触发,日志无 ParamFlowException,监控里也看不到参数维度统计。
根本原因在于:alibaba/sentinel-php 默认只加载基础 FlowSlot、DegradeSlot,而热点参数限流依赖独立的 ParamFlowSlot,它不会随核心包自动注册。不显式启用,所有 paramFlowRule 都被跳过。
实操建议:
- 确认已安装扩展包:
composer require alibaba/sentinel-php(注意不是 Java 版本) - 在
config/autoload/sentinel.php中补全slot配置项:'slots' => [\Alibaba\Sentinel\Slot\ParamFlowSlot::class] - 确保
ParamFlowSlot在SentinelContext初始化时被注入——通常需在WorkerStartCallback中调用Sentinel::addSlot(new ParamFlowSlot()) - 检查
ParameterMetricStorage是否初始化成功,否则参数指标无法采集(错误日志常含no parameter metric storage found)
如何定义一个按 user_id 限流的热点规则
热点规则不是靠注解自动绑定参数,而是由 Sentinel 在运行时解析方法签名 + 实际入参,再匹配规则。你必须显式声明「哪个参数是热点」,且该参数必须出现在方法签名中(不能从 Request 对象里动态提取)。
示例场景:用户下单接口 create(int $userId, array $orderData),要求单个 $userId 每秒最多请求 5 次。
实操步骤:
- 方法签名必须含
$userId参数(类型要明确,避免mixed) - Dashboard 中新增热点规则时,「参数索引」填
0(对应第一个参数),「单机阈值」填5,「统计窗口时长」保持默认1000ms - 规则生效前,需在代码中触发一次完整调用(如用 cURL 请求一次
/orders?user_id=123),让 Sentinel 捕获到该参数并初始化ParameterMetric - 若想支持多参数组合(如
userId + skuId),需自定义ParamParser并替换默认实现,否则只认单个索引
错误示范:public function create(RequestInterface $request) { $userId = $request->input('user_id'); ... } → 此时 $userId 不在方法签名中,Sentinel 无法识别为热点参数。
maxQueueingTimeMs 和 coldFactor 的坑别踩
这两个参数常被混用或误配,导致规则看似生效,实际行为反直觉。
maxQueueingTimeMs 只在 CONTROL_BEHAVIOR_QUEUEING 下有效,且单位是毫秒;设为 0 表示禁用排队,设为 null 或未设置会直接报错 Invalid rule: queueing time not set。
coldFactor 是预热模式专用参数,默认值 3,但它只影响「初始阈值 = 阈值 ÷ coldFactor」的计算,和排队等待完全无关。有人把 queueing 规则里也配 coldFactor,结果毫无作用。
关键区别:
- 用排队等待:必须设
maxQueueingTimeMs ≤ 500(否则用户早已放弃,服务还在等) - 用预热启动:必须配
warmUpPeriodSec ≥ 10,且thresholdCount要明显大于冷启阶段能承受的 QPS(比如设 100,coldFactor=3 → 初始仅 33 QPS) - 二者不可共存:同一资源不能同时开启
QUEUEING和WARM_UP,Sentinel 会静默忽略后者
Dashboard 规则不生效的三个硬性前提
即使配置看起来都对,规则仍可能不触发。以下三点缺一不可:
-
app_name在config/autoload/sentinel.php中必须与 Sentinel Dashboard 里「应用管理」注册的名称**完全一致**(大小写、空格、下划线均敏感) - 每个 worker 进程必须独立完成
Sentinel::init(),且在WorkerStartCallback中执行,不能只在 main 进程初始化 - 心跳上报必须成功:检查日志是否有
Heartbeat report success,失败常见原因是网络不通或dashboard地址末尾少了:8080(默认端口不能省略)
最容易被忽略的是:Hyperf 多 worker 场景下,如果只在 Command 中初始化 Sentinel,worker 进程里 @SentinelResource 注解会静默失效——因为没上下文,也不会抛异常,只会当普通方法执行。











