Sentinel在消息网关的实践
前言
- Sentinel 是面向分布式服务架构的轻量级流量控制框架,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助您保护服务的稳定性。
- 想到流量控制我们就知道漏斗,令牌两种限流方式,及google RateLimite的限流组件。
- 想到熔断降级,SpringCould Hystrix大家玩微服务都应该知道。实践过没?深入研究过没?学习难度?
- 详细对比下Sentinel与Hystrix 特征
对比内容 | Sentinel | Hystrix |
---|---|---|
隔离策略 | 信号量隔离 | 线程池隔离/信号量隔离 |
熔断降级策略 | 基于响应时间或失败比率 | 基于失败比率 |
实时指标实现 | 滑动窗口 | 滑动窗口(基于 RxJava) |
规则配置 | 支持多种数据源 | 支持多种数据源 |
扩展性 | 多个扩展点 | 插件的形式 |
基于注解的支持 | 支持 | 支持 |
限流 | 基于 QPS,支持基于调用关系的限流 | 不支持 |
流量整形 | 支持慢启动、匀速器模式 | 不支持 |
系统负载保护 | 支持 | 不支持 |
控制台 | 开箱即用,可配置规则、查看秒级监控、机器发现等 | 不完善 |
常见框架的适配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |
学习难度 | 中国人开发,中文文档 | 外国思维 |
- 看完上面的简单介绍,请移步到官方文档 https://github.com/alibaba/Sentinel/wiki/介绍 同浏览一篇,再看下面实践。
场景应用
-
对同一个手机号,30秒内发送短信条数不超过10条,采用热点参数限流组件来实现
-
热点参数限流 热点即经常访问的数据。很多时候我们希望统计某个热点数据中访问频次最高的 Top K 数据,并对其访问进行限制。比如:
商品 ID 为参数,统计一段时间内最常购买的商品 ID 并进行限制 用户 ID 为参数,针对一段时间内频繁访问的用户 ID 进行限制
-
代码如下:
//对第一个参数设置限制
ParamFlowRule rule = new ParamFlowRule("mobileNo")
.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));
- 设置规则代码,对参数0的每个值的qps不超过5.
/**
* Here we prepare different parameters to validate flow control by parameters.
*/
private static final String[] params = new String[]{
"13632634458",
"15982099035",
// "15982099035",
// "15982099035",
// "15140553376",
// "15982099035",
// "18292800909",
// "18608037915",
// "13547882172",
// "13547882172",
// "18581853266",
// "18581853266",
// "18581853266",
// "15982099035",
// "15982099035",
// "18628201097",
// "13826281991",
// "13659282837",
// "13680234325",
// "18628201097",
// "18628201097",
// "13826281991",
// "18581853266",
// "13659282837",
// "18581853266",
// "18628201097",
// "18581853266",
// "18581853266",
// "13680234325",
// "18581853266",
// "18581853266",
};
@RequestMapping(value = "/getParam")
@ResponseBody
// @SentinelResource(value = "methodA", blockHandler = "exceptionHandler", fallback = "")
public String getTwoCache(HttpServletRequest request) {
Entry entry = null;
try {
String param = generateParam();
entry = SphU.entry("mobileNo", EntryType.IN, 1, param);
// Add pass for parameter.
log.info("抢购成功:" + param);
} catch (BlockException e1) {
log.info("熔断了" + e1.getMessage());
return "error";
} catch (Exception e2) {
// biz exception
} finally {
if (entry != null) {
entry.exit();
}
}
return "ok";
}
private String generateParam() {
int i = ThreadLocalRandom.current().nextInt(0, params.length);
return params[i];
}
- 通过sentinel-dashboard可以知道,只启用两个手机号,/getParam的qps应该为10 qps。如图:
- 通过图片可以看mobileNo的qps为10.
- 遗憾的是规则采用是1秒时间窗口内获取的qps,对于10秒内该请求数量,还是不够完美。可以自己设计。
滑动时间窗口
- 熔断降级采用的错误数,错误率,响应时间,限流的qps都是基于滑动时间窗口。
- 我们在流水式计算也经常遇到时间窗口这个概念,在报警系统我们也许选择一个窗口时间。背后原理是一致的滑动时间窗口
- 滑动时间窗口它有两个属性,一个是Window size,一个是Advance interval。Window size指定了窗口的大小,也即每次计算最小数据集的大小。而Advance interval定义输出的时间间隔。一个典型的应用场景是,每隔5秒钟输出一次过去1个小时内网站的PV或者UV
- 举例:监控系统定义最小时间WindowSize=30s,意思把单位时间30s数据值进行聚合。Advancenterval:为我们获取3分内的时间窗口数据算qps。滑动时间窗口意思已当时间点向前找Advancenterval时间内有效数据。
- 时间窗口在kafkaStream,Flink等广泛应用
自定义分布式滑动时间窗口
- 定义最小时间WindowSize=30s,采用redis hash数据结构实现窗口,key为窗口标识id,例如01-01 21:01:00.数据属性放入Hash结构。
- 利用redis HINCRBY命令为哈希表 key 中的域 field 的值加上增量 increment,很容为数据聚合。
- 我们的间隔时间为Advancenterval 可以考虑为1-5分钟,key采用LRU设置大于Advancenterval的失效时间自动过期。
- 一个分布式滑动时间窗口已done。
参考
- kafkaStream时间窗口详细介绍 http://www.jasongj.com/kafka/kafka_stream/
- 限流降级神器「sentinel」基于滑动时间窗口 https://mp.weixin.qq.com/s/B1_7Kb_CxeKEAv43kdCWOA
- Sentinel GitHub