场景
当前完成了一个商户端接入支付功能的sdk项目,在测试环境上排查问题时需要去捞日志的时候,发现日志基本上被提供给商户查询订单状态的接口日志打满,一来不方便捞日志,二来提醒了我可能需要对查询接口做限流处理,避免日后上生产被外部查询请求打垮。
方案选择
针对限流措施,有基于Redis的滑动窗口方案,适合作用于集群的场景。考虑在生产环境中,k8s有扩容策略会自动拉起额外的pod,选择基于内存的限流方案,对每个pod的流量单独限流。最终采用引入Sentinel的aop与spring集成,达到限流目的。
操作步骤
1.引入Sentinel的相关maven依赖
<!--sentinel流控组件核心-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
</dependency>
<!--sentinel流控组件-aop-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
</dependency>
<!--sentinel流控组件-热点参数-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-parameter-flow-control</artifactId>
</dependency>
2.与springboot集成
将SentinelResourceAspect注册到spring容器中,使Sentinel生效
@Bean
public SentinelResourceAspect sentinelResourceAspect(){
return new SentinelResourceAspect();
}
在需要进行限流的方法上,使用@SentinelResource注解对资源名、降级、熔断处理进行配置
@SentinelResource(value = "queryLimit",blockHandler = "handlerCall")
@GetMapping("/query")
public String query(@RequstParam String paramA,@RequstParam String paramB){
return "hello";
}
在同类中创建一个同返回值,同参数(额外增加一个BlockException类型参数接收限流触发的异常)的降级回调方法,方法名同blockHandler属性中一致
public String query(String paramA,String paramB,BlockException exception){
return "error";
}
3.实现自定义的流控规则
在完成上述操作后,还需要定义自己的流控规则才能真正实现接口的限流。Sentinel提供了几种规则,详情可见官网文档,这里不多说明。结合我的业务场景,需要对不同商户发起的调用进行分别限流,所以我采用了热点参数规则,将商户ID的当做参数,针对请求频繁的商户进行限流处理。
ParamFlowRule rule = new ParamFlowRule(resourceName)
.setParamIdx(0)
.setCount(5);
// 针对 int 类型的参数 PARAM_B,单独设置限流 QPS 阈值为 10,而不是全局的阈值 5.
ParamFlowItem item = new ParamFlowItem()
.setObject(String.valueOf(PARAM_B))
.setClassType(int.class.getName())
.setCount(10);
rule.setParamFlowItemList(Collections.singletonList(item));
ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
如上,即可完成对不同商户进行限流处理
后记
为了方便动态更新QPS的限流值,qps的限流阈值配置到了nacos,在nacos更新阈值后,需要将新限流规则覆盖旧的规则。会在后续的文章中单独记录。