这里写自定义目录标题
背景
提供服务给第三方,但是需要给的权限码进行限制,并且可动态配置,包括限流的接口,限流次数,限流日期,限流ip,限流时间
实现方式——sentinel
- 控制台(Dashboard):可以通过控制台进行限流,并通过配置实现(如果不通过配置,服务重启以后配置就没了),但是我当时测试了一下没成功,可能哪里没弄对,可参照官方文档(https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html)
- 核心库:使用提供的api进行限流控制(本文的处理方式中,提供服务的服务实例只有一个,在多个实例场景下应该会有问题)
官方文档:https://sentinelguard.io/zh-cn/docs/quick-start.html
使用规则——热点规则
选择热点规则是因为,需要实现几秒调用一次,流量规则不好使
官网说明:
热点参数规则(ParamFlowRule)类似于流量控制规则(FlowRule):
实现逻辑
1.将动态配置的限流数据存储到库中,并将规则放置到redis中,给一个热点规则修改的标识
redisService.setCacheObject("ParamFlowRuleFlag",true);
redisService.setCacheObject("ParamFlowRule",jsonString);
2.调用前进行限流处理
@Before("@annotation(requestLimit)")
public void doBefore(JoinPoint point, RequestLimiter requestLimit) throws Throwable {
//如果规则为空或者标识为已修改则对规则进行更新
if(CollUtil.isEmpty(ParamFlowRuleManager.getRules()) || (boolean)redisService.getCacheObject("ParamFlowRuleFlag")){
updateParamFlowRule();
}
//获取请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
//根据授权码进行限流
if(request.getParameterMap().get("authorization_code")!=null){
String authCode = request.getParameterMap().get("authorization_code")[0];
//获取授权码信息
DataService dataService = XXXX;
//未找到对应授权码
if(dataService == null){
throw new RequestLimiterException(DataServiceStatus.UNAUTHORIZED);
}
//是否符合限制信息,ip时间等
ipAndTimeAndOpenLimit(dataService, request, point, requestLimit);
try {
//一个授权码对应多个接口,所以限流resource命名为接口地址+授权码
entry = SphU.entry(request.getRequestURI() + authCode , EntryType.IN, 1, authCode );
}
// 如果被保护的资源被限流或者降级了,就会抛BlockException
catch (BlockException e) {
throw new RequestLimiterException(DataServiceStatus.FORBIDDEN);
} catch (IllegalArgumentException e2) {
Tracer.trace(e2);
} finally {
if (Objects.nonNull(entry)) {
// 退出entry
entry.exit(1,token);
}
ContextUtil.exit();
}
}
}
private void updateParamFlowRule() {
String rules = redisService.getCacheObject("ParamFlowRule");
if(StrUtil.isNotBlank(rules)){
JSONArray rulesArray = JSON.parseArray(rules);
List<ParamFlowRule> paramFlowRuleList = new ArrayList<>();
for (int i = 0; i < rulesArray.size(); i++) {
JSONObject jsonObject = (JSONObject) rulesArray.get(i);
ParamFlowRule paramFlowRule = rulesArray.getObject(i, ParamFlowRule.class);
//不单独写出来出问题了,具体什么问题有点忘记了
paramFlowRule.setLimitApp("default");
paramFlowRule.setParamIdx(jsonObject.getInteger("praramIdx"));
paramFlowRuleList.add(paramFlowRule);
}
ParamFlowRuleManager.loadRules(paramFlowRuleList);
}
redisService.setCacheObject("ParamFlowRuleFlag",false);
}
public static class ParamFlowRule {
@ApiModelProperty(value = "资源名称")
private String resource;
@ApiModelProperty(value = "单机阈值")
private double count;
@ApiModelProperty(value = "参数索引")
private Integer praramIdx;
@ApiModelProperty(value = "是否集群")
private boolean clusterMode;
@ApiModelProperty(value = "统计窗口")
private double durationInSec;
@ApiModelProperty(value = "参数外项")
private List<ParamFlowItem> paramFlowItemList;
}
public static class ParamFlowItem {
@ApiModelProperty(value = "参数值")
private String object;
@ApiModelProperty(value = "限流阈值")
private double count;
@ApiModelProperty(value = "参数类型")
private String classType;
}
注意
这个只是针对单个实例的服务,如果想在本文基础上稍加修改,可以考虑使用reids存储全局限流次数,但是不优雅